]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
Merge branch 'serge-next-2' of git://git.kernel.org/pub/scm/linux/kernel/git/sergeh...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 13 Jun 2014 14:39:39 +0000 (07:39 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 13 Jun 2014 14:39:39 +0000 (07:39 -0700)
Pull more security layer updates from Serge Hallyn:
 "A few more commits had previously failed to make it through
  security-next into linux-next but this week made it into linux-next.
  At least commit "ima: introduce ima_kernel_read()" was deemed critical
  by Mimi to make this merge window.

  This is a temporary tree just for this request.  Mimi has pointed me
  to some previous threads about keeping maintainer trees at the
  previous release, which I'll certainly do for anything long-term,
  after talking with James"

* 'serge-next-2' of git://git.kernel.org/pub/scm/linux/kernel/git/sergeh/linux-security:
  ima: introduce ima_kernel_read()
  evm: prohibit userspace writing 'security.evm' HMAC value
  ima: check inode integrity cache in violation check
  ima: prevent unnecessary policy checking
  evm: provide option to protect additional SMACK xattrs
  evm: replace HMAC version with attribute mask
  ima: prevent new digsig xattr from being replaced

1625 files changed:
.gitignore
Documentation/ABI/testing/sysfs-class-net
Documentation/ABI/testing/sysfs-class-net-cdc_ncm [new file with mode: 0644]
Documentation/ABI/testing/sysfs-class-net-queues [new file with mode: 0644]
Documentation/ABI/testing/sysfs-class-net-statistics [new file with mode: 0644]
Documentation/DocBook/80211.tmpl
Documentation/devicetree/bindings/net/amd-xgbe-phy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/amd-xgbe.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/broadcom-bcmgenet.txt
Documentation/devicetree/bindings/net/broadcom-systemport.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/can/xilinx_can.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/cpsw-phy-sel.txt
Documentation/devicetree/bindings/net/fixed-link.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/fsl-tsec-phy.txt
Documentation/devicetree/bindings/net/hisilicon-hix5hd2-gmac.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/ieee802154/at86rf230.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/micrel-ks8851.txt
Documentation/devicetree/bindings/net/micrel-ksz9021.txt [deleted file]
Documentation/devicetree/bindings/net/micrel-ksz90x1.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/nfc/pn544.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/nfc/st21nfca.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/nfc/trf7970a.txt
Documentation/devicetree/bindings/net/via-rhine.txt [new file with mode: 0644]
Documentation/driver-model/devres.txt
Documentation/kbuild/modules.txt
Documentation/kprobes.txt
Documentation/mutex-design.txt
Documentation/networking/bonding.txt
Documentation/networking/can.txt
Documentation/networking/cdc_mbim.txt [new file with mode: 0644]
Documentation/networking/filter.txt
MAINTAINERS
Makefile
arch/arm/boot/dts/am33xx.dtsi
arch/arm/boot/dts/am4372.dtsi
arch/arm/boot/dts/armada-xp-matrix.dts
arch/arm/boot/dts/vt8500.dtsi
arch/arm/boot/dts/wm8650.dtsi
arch/arm/boot/dts/wm8850.dtsi
arch/arm/kernel/perf_event.c
arch/arm/kernel/perf_event_cpu.c
arch/arm/kernel/topology.c
arch/arm/mach-tegra/board-paz00.c
arch/arm/net/bpf_jit_32.c
arch/blackfin/configs/BF526-EZBRD_defconfig
arch/blackfin/configs/BF527-EZKIT-V2_defconfig
arch/blackfin/configs/BF527-EZKIT_defconfig
arch/blackfin/configs/BF548-EZKIT_defconfig
arch/blackfin/configs/BF609-EZKIT_defconfig
arch/blackfin/configs/BlackStamp_defconfig
arch/blackfin/configs/H8606_defconfig
arch/blackfin/include/asm/dma.h
arch/blackfin/mach-bf533/boards/stamp.c
arch/mips/bcm47xx/sprom.c
arch/powerpc/Kconfig.debug
arch/powerpc/boot/Makefile
arch/powerpc/configs/chroma_defconfig [deleted file]
arch/powerpc/include/asm/cpm2.h
arch/powerpc/include/asm/eeh.h
arch/powerpc/include/asm/eeh_event.h
arch/powerpc/include/asm/mmu-book3e.h
arch/powerpc/include/asm/opal.h
arch/powerpc/include/asm/reg_a2.h
arch/powerpc/include/asm/switch_to.h
arch/powerpc/include/asm/wsp.h [deleted file]
arch/powerpc/include/uapi/asm/cputable.h
arch/powerpc/kernel/Makefile
arch/powerpc/kernel/cpu_setup_a2.S [deleted file]
arch/powerpc/kernel/cpu_setup_power.S
arch/powerpc/kernel/cputable.c
arch/powerpc/kernel/eeh.c
arch/powerpc/kernel/eeh_driver.c
arch/powerpc/kernel/eeh_event.c
arch/powerpc/kernel/eeh_pe.c
arch/powerpc/kernel/entry_64.S
arch/powerpc/kernel/exceptions-64e.S
arch/powerpc/kernel/exceptions-64s.S
arch/powerpc/kernel/head_40x.S
arch/powerpc/kernel/process.c
arch/powerpc/kernel/setup-common.c
arch/powerpc/kernel/smp.c
arch/powerpc/kernel/time.c
arch/powerpc/kernel/traps.c
arch/powerpc/kernel/udbg.c
arch/powerpc/kernel/udbg_16550.c
arch/powerpc/kvm/book3s_hv_ras.c
arch/powerpc/kvm/book3s_hv_rmhandlers.S
arch/powerpc/lib/sstep.c
arch/powerpc/net/bpf_jit_64.S
arch/powerpc/net/bpf_jit_comp.c
arch/powerpc/platforms/Kconfig
arch/powerpc/platforms/Kconfig.cputype
arch/powerpc/platforms/Makefile
arch/powerpc/platforms/cell/spufs/spufs.h
arch/powerpc/platforms/powernv/Kconfig
arch/powerpc/platforms/powernv/Makefile
arch/powerpc/platforms/powernv/eeh-ioda.c
arch/powerpc/platforms/powernv/opal-msglog.c
arch/powerpc/platforms/powernv/opal-sysparam.c
arch/powerpc/platforms/powernv/pci.c
arch/powerpc/platforms/powernv/setup.c
arch/powerpc/platforms/powernv/smp.c
arch/powerpc/platforms/pseries/Kconfig
arch/powerpc/platforms/wsp/Kconfig [deleted file]
arch/powerpc/platforms/wsp/Makefile [deleted file]
arch/powerpc/platforms/wsp/chroma.c [deleted file]
arch/powerpc/platforms/wsp/h8.c [deleted file]
arch/powerpc/platforms/wsp/ics.c [deleted file]
arch/powerpc/platforms/wsp/ics.h [deleted file]
arch/powerpc/platforms/wsp/msi.c [deleted file]
arch/powerpc/platforms/wsp/msi.h [deleted file]
arch/powerpc/platforms/wsp/opb_pic.c [deleted file]
arch/powerpc/platforms/wsp/psr2.c [deleted file]
arch/powerpc/platforms/wsp/scom_smp.c [deleted file]
arch/powerpc/platforms/wsp/scom_wsp.c [deleted file]
arch/powerpc/platforms/wsp/setup.c [deleted file]
arch/powerpc/platforms/wsp/smp.c [deleted file]
arch/powerpc/platforms/wsp/wsp.c [deleted file]
arch/powerpc/platforms/wsp/wsp.h [deleted file]
arch/powerpc/platforms/wsp/wsp_pci.c [deleted file]
arch/powerpc/platforms/wsp/wsp_pci.h [deleted file]
arch/powerpc/sysdev/fsl_soc.c
arch/powerpc/sysdev/xics/icp-native.c
arch/powerpc/xmon/nonstdio.c
arch/s390/net/bpf_jit_comp.c
arch/sparc/include/asm/checksum_32.h
arch/sparc/include/asm/checksum_64.h
arch/sparc/net/bpf_jit_comp.c
arch/um/Makefile
arch/x86/Kconfig
arch/x86/include/asm/asm.h
arch/x86/include/asm/checksum_64.h
arch/x86/include/asm/kprobes.h
arch/x86/include/asm/qrwlock.h [new file with mode: 0644]
arch/x86/include/asm/spinlock.h
arch/x86/include/asm/spinlock_types.h
arch/x86/include/asm/traps.h
arch/x86/include/asm/uprobes.h
arch/x86/kernel/alternative.c
arch/x86/kernel/apic/hw_nmi.c
arch/x86/kernel/apic/io_apic.c
arch/x86/kernel/cpu/common.c
arch/x86/kernel/cpu/mshyperv.c
arch/x86/kernel/cpu/perf_event.c
arch/x86/kernel/cpu/perf_event_amd_ibs.c
arch/x86/kernel/cpu/perf_event_intel_lbr.c
arch/x86/kernel/dumpstack.c
arch/x86/kernel/entry_32.S
arch/x86/kernel/entry_64.S
arch/x86/kernel/hw_breakpoint.c
arch/x86/kernel/i8259.c
arch/x86/kernel/irq.c
arch/x86/kernel/kprobes/core.c
arch/x86/kernel/kprobes/ftrace.c
arch/x86/kernel/kprobes/opt.c
arch/x86/kernel/kvm.c
arch/x86/kernel/nmi.c
arch/x86/kernel/paravirt.c
arch/x86/kernel/process_64.c
arch/x86/kernel/traps.c
arch/x86/kernel/uprobes.c
arch/x86/lib/thunk_32.S
arch/x86/lib/thunk_64.S
arch/x86/mm/fault.c
arch/x86/net/bpf_jit.S
arch/x86/net/bpf_jit_comp.c
drivers/atm/fore200e.c
drivers/atm/idt77252.c
drivers/block/rbd.c
drivers/bluetooth/ath3k.c
drivers/bluetooth/btmrvl_drv.h
drivers/bluetooth/btmrvl_main.c
drivers/bluetooth/btmrvl_sdio.c
drivers/bluetooth/btmrvl_sdio.h
drivers/bluetooth/btusb.c
drivers/bluetooth/hci_h4.c
drivers/clk/ti/clk-43xx.c
drivers/cpuidle/cpuidle-powernv.c
drivers/cpuidle/driver.c
drivers/crypto/Kconfig
drivers/hv/channel_mgmt.c
drivers/hv/hyperv_vmbus.h
drivers/hv/vmbus_drv.c
drivers/infiniband/hw/cxgb4/cm.c
drivers/infiniband/hw/cxgb4/cq.c
drivers/infiniband/hw/cxgb4/iw_cxgb4.h
drivers/infiniband/hw/cxgb4/provider.c
drivers/infiniband/hw/cxgb4/t4.h
drivers/infiniband/hw/cxgb4/t4fw_ri_api.h
drivers/infiniband/ulp/ipoib/ipoib_ethtool.c
drivers/infiniband/ulp/iser/iser_initiator.c
drivers/infiniband/ulp/isert/ib_isert.c
drivers/infiniband/ulp/isert/ib_isert.h
drivers/isdn/capi/Kconfig
drivers/isdn/capi/capi.c
drivers/isdn/capi/capidrv.c
drivers/isdn/capi/capiutil.c
drivers/isdn/hisax/hfc4s8s_l1.c
drivers/isdn/i4l/isdn_ppp.c
drivers/isdn/mISDN/l1oip_core.c
drivers/media/platform/Kconfig
drivers/media/platform/omap3isp/Makefile
drivers/media/platform/omap3isp/isp.c
drivers/media/platform/omap3isp/isp.h
drivers/media/platform/omap3isp/ispccdc.c
drivers/media/platform/omap3isp/ispccdc.h
drivers/media/platform/omap3isp/ispccp2.c
drivers/media/platform/omap3isp/ispcsi2.c
drivers/media/platform/omap3isp/isph3a_aewb.c
drivers/media/platform/omap3isp/isph3a_af.c
drivers/media/platform/omap3isp/isppreview.c
drivers/media/platform/omap3isp/ispqueue.c [deleted file]
drivers/media/platform/omap3isp/ispqueue.h [deleted file]
drivers/media/platform/omap3isp/ispresizer.c
drivers/media/platform/omap3isp/ispstat.c
drivers/media/platform/omap3isp/ispstat.h
drivers/media/platform/omap3isp/ispvideo.c
drivers/media/platform/omap3isp/ispvideo.h
drivers/media/v4l2-core/videobuf2-core.c
drivers/mmc/host/mmc_spi.c
drivers/net/bonding/bond_3ad.c
drivers/net/bonding/bond_alb.c
drivers/net/bonding/bond_alb.h
drivers/net/bonding/bond_debugfs.c
drivers/net/bonding/bond_main.c
drivers/net/bonding/bond_netlink.c
drivers/net/bonding/bond_options.c
drivers/net/bonding/bond_options.h
drivers/net/bonding/bond_procfs.c
drivers/net/bonding/bond_sysfs.c
drivers/net/bonding/bond_sysfs_slave.c
drivers/net/bonding/bonding.h
drivers/net/can/Kconfig
drivers/net/can/Makefile
drivers/net/can/c_can/c_can.c
drivers/net/can/c_can/c_can.h
drivers/net/can/c_can/c_can_pci.c
drivers/net/can/c_can/c_can_platform.c
drivers/net/can/mscan/Kconfig
drivers/net/can/rcar_can.c [new file with mode: 0644]
drivers/net/can/softing/softing_main.c
drivers/net/can/spi/Kconfig [new file with mode: 0644]
drivers/net/can/spi/Makefile [new file with mode: 0644]
drivers/net/can/spi/mcp251x.c [moved from drivers/net/can/mcp251x.c with 96% similarity]
drivers/net/can/usb/Kconfig
drivers/net/can/usb/Makefile
drivers/net/can/usb/gs_usb.c [new file with mode: 0644]
drivers/net/can/usb/kvaser_usb.c
drivers/net/can/xilinx_can.c [new file with mode: 0644]
drivers/net/dsa/mv88e6123_61_65.c
drivers/net/dsa/mv88e6131.c
drivers/net/dsa/mv88e6xxx.c
drivers/net/ethernet/3com/3c509.c
drivers/net/ethernet/3com/3c589_cs.c
drivers/net/ethernet/3com/typhoon.c
drivers/net/ethernet/8390/ax88796.c
drivers/net/ethernet/Kconfig
drivers/net/ethernet/Makefile
drivers/net/ethernet/adaptec/starfire.c
drivers/net/ethernet/alteon/acenic.c
drivers/net/ethernet/altera/altera_sgdma.c
drivers/net/ethernet/altera/altera_tse_ethtool.c
drivers/net/ethernet/amd/Kconfig
drivers/net/ethernet/amd/Makefile
drivers/net/ethernet/amd/amd8111e.c
drivers/net/ethernet/amd/ariadne.c
drivers/net/ethernet/amd/au1000_eth.c
drivers/net/ethernet/amd/hplance.c
drivers/net/ethernet/amd/mvme147.c
drivers/net/ethernet/amd/nmclan_cs.c
drivers/net/ethernet/amd/xgbe/Makefile [new file with mode: 0644]
drivers/net/ethernet/amd/xgbe/xgbe-common.h [new file with mode: 0644]
drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c [new file with mode: 0644]
drivers/net/ethernet/amd/xgbe/xgbe-desc.c [new file with mode: 0644]
drivers/net/ethernet/amd/xgbe/xgbe-dev.c [new file with mode: 0644]
drivers/net/ethernet/amd/xgbe/xgbe-drv.c [new file with mode: 0644]
drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c [new file with mode: 0644]
drivers/net/ethernet/amd/xgbe/xgbe-main.c [new file with mode: 0644]
drivers/net/ethernet/amd/xgbe/xgbe-mdio.c [new file with mode: 0644]
drivers/net/ethernet/amd/xgbe/xgbe.h [new file with mode: 0644]
drivers/net/ethernet/arc/emac_main.c
drivers/net/ethernet/atheros/alx/main.c
drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c
drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c
drivers/net/ethernet/atheros/atlx/atl1.c
drivers/net/ethernet/atheros/atlx/atl2.c
drivers/net/ethernet/broadcom/Kconfig
drivers/net/ethernet/broadcom/Makefile
drivers/net/ethernet/broadcom/b44.c
drivers/net/ethernet/broadcom/bcm63xx_enet.c
drivers/net/ethernet/broadcom/bcmsysport.c [new file with mode: 0644]
drivers/net/ethernet/broadcom/bcmsysport.h [new file with mode: 0644]
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_cmn.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_fw_file_hdr.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_init.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_init_ops.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h
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_stats.h
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/genet/bcmgenet.c
drivers/net/ethernet/broadcom/genet/bcmmii.c
drivers/net/ethernet/broadcom/tg3.c
drivers/net/ethernet/broadcom/tg3.h
drivers/net/ethernet/brocade/bna/bnad_ethtool.c
drivers/net/ethernet/calxeda/xgmac.c
drivers/net/ethernet/chelsio/cxgb/cxgb2.c
drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
drivers/net/ethernet/chelsio/cxgb4/sge.c
drivers/net/ethernet/chelsio/cxgb4/t4_hw.h
drivers/net/ethernet/chelsio/cxgb4/t4_msg.h
drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
drivers/net/ethernet/chelsio/cxgb4vf/sge.c
drivers/net/ethernet/cisco/enic/enic.h
drivers/net/ethernet/cisco/enic/enic_dev.c
drivers/net/ethernet/cisco/enic/enic_dev.h
drivers/net/ethernet/cisco/enic/enic_ethtool.c
drivers/net/ethernet/cisco/enic/enic_main.c
drivers/net/ethernet/cisco/enic/vnic_cq.h
drivers/net/ethernet/cisco/enic/vnic_dev.c
drivers/net/ethernet/cisco/enic/vnic_dev.h
drivers/net/ethernet/davicom/dm9000.c
drivers/net/ethernet/dec/tulip/tulip_core.c
drivers/net/ethernet/dec/tulip/uli526x.c
drivers/net/ethernet/dlink/dl2k.c
drivers/net/ethernet/dlink/sundance.c
drivers/net/ethernet/ec_bhf.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_hw.h
drivers/net/ethernet/emulex/benet/be_main.c
drivers/net/ethernet/ethoc.c
drivers/net/ethernet/faraday/ftgmac100.c
drivers/net/ethernet/faraday/ftmac100.c
drivers/net/ethernet/freescale/Kconfig
drivers/net/ethernet/freescale/fec.h
drivers/net/ethernet/freescale/fec_main.c
drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c
drivers/net/ethernet/freescale/gianfar.c
drivers/net/ethernet/freescale/ucc_geth.c
drivers/net/ethernet/freescale/ucc_geth_ethtool.c
drivers/net/ethernet/freescale/xgmac_mdio.c
drivers/net/ethernet/fujitsu/fmvj18x_cs.c
drivers/net/ethernet/hisilicon/Kconfig [new file with mode: 0644]
drivers/net/ethernet/hisilicon/Makefile [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hix5hd2_gmac.c [new file with mode: 0644]
drivers/net/ethernet/ibm/ehea/ehea_ethtool.c
drivers/net/ethernet/ibm/ehea/ehea_main.c
drivers/net/ethernet/ibm/ehea/ehea_qmr.c
drivers/net/ethernet/ibm/emac/core.c
drivers/net/ethernet/icplus/ipg.c
drivers/net/ethernet/intel/e100.c
drivers/net/ethernet/intel/e1000/e1000_ethtool.c
drivers/net/ethernet/intel/e1000/e1000_hw.c
drivers/net/ethernet/intel/e1000/e1000_main.c
drivers/net/ethernet/intel/e1000e/80003es2lan.c
drivers/net/ethernet/intel/e1000e/82571.c
drivers/net/ethernet/intel/e1000e/e1000.h
drivers/net/ethernet/intel/e1000e/ethtool.c
drivers/net/ethernet/intel/e1000e/hw.h
drivers/net/ethernet/intel/e1000e/ich8lan.c
drivers/net/ethernet/intel/e1000e/mac.c
drivers/net/ethernet/intel/e1000e/mac.h
drivers/net/ethernet/intel/e1000e/netdev.c
drivers/net/ethernet/intel/e1000e/nvm.c
drivers/net/ethernet/intel/e1000e/param.c
drivers/net/ethernet/intel/e1000e/phy.c
drivers/net/ethernet/intel/i40e/i40e.h
drivers/net/ethernet/intel/i40e/i40e_adminq.c
drivers/net/ethernet/intel/i40e/i40e_adminq.h
drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
drivers/net/ethernet/intel/i40e/i40e_common.c
drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c
drivers/net/ethernet/intel/i40e/i40e_debugfs.c
drivers/net/ethernet/intel/i40e/i40e_diag.c
drivers/net/ethernet/intel/i40e/i40e_ethtool.c
drivers/net/ethernet/intel/i40e/i40e_hmc.c
drivers/net/ethernet/intel/i40e/i40e_hmc.h
drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c
drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h
drivers/net/ethernet/intel/i40e/i40e_main.c
drivers/net/ethernet/intel/i40e/i40e_prototype.h
drivers/net/ethernet/intel/i40e/i40e_ptp.c
drivers/net/ethernet/intel/i40e/i40e_register.h
drivers/net/ethernet/intel/i40e/i40e_txrx.c
drivers/net/ethernet/intel/i40e/i40e_txrx.h
drivers/net/ethernet/intel/i40e/i40e_type.h
drivers/net/ethernet/intel/i40e/i40e_virtchnl.h
drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
drivers/net/ethernet/intel/i40evf/Makefile
drivers/net/ethernet/intel/i40evf/i40e_adminq.c
drivers/net/ethernet/intel/i40evf/i40e_adminq.h
drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h
drivers/net/ethernet/intel/i40evf/i40e_alloc.h
drivers/net/ethernet/intel/i40evf/i40e_common.c
drivers/net/ethernet/intel/i40evf/i40e_hmc.h
drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h
drivers/net/ethernet/intel/i40evf/i40e_osdep.h
drivers/net/ethernet/intel/i40evf/i40e_prototype.h
drivers/net/ethernet/intel/i40evf/i40e_register.h
drivers/net/ethernet/intel/i40evf/i40e_status.h
drivers/net/ethernet/intel/i40evf/i40e_txrx.c
drivers/net/ethernet/intel/i40evf/i40e_txrx.h
drivers/net/ethernet/intel/i40evf/i40e_type.h
drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h
drivers/net/ethernet/intel/i40evf/i40evf.h
drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c
drivers/net/ethernet/intel/i40evf/i40evf_main.c
drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
drivers/net/ethernet/intel/igb/e1000_82575.c
drivers/net/ethernet/intel/igb/e1000_82575.h
drivers/net/ethernet/intel/igb/e1000_defines.h
drivers/net/ethernet/intel/igb/e1000_hw.h
drivers/net/ethernet/intel/igb/e1000_i210.c
drivers/net/ethernet/intel/igb/e1000_i210.h
drivers/net/ethernet/intel/igb/e1000_mac.c
drivers/net/ethernet/intel/igb/e1000_mac.h
drivers/net/ethernet/intel/igb/e1000_mbx.c
drivers/net/ethernet/intel/igb/e1000_mbx.h
drivers/net/ethernet/intel/igb/e1000_nvm.c
drivers/net/ethernet/intel/igb/e1000_nvm.h
drivers/net/ethernet/intel/igb/e1000_phy.c
drivers/net/ethernet/intel/igb/e1000_phy.h
drivers/net/ethernet/intel/igb/e1000_regs.h
drivers/net/ethernet/intel/igb/igb.h
drivers/net/ethernet/intel/igb/igb_ethtool.c
drivers/net/ethernet/intel/igb/igb_hwmon.c
drivers/net/ethernet/intel/igb/igb_main.c
drivers/net/ethernet/intel/igb/igb_ptp.c
drivers/net/ethernet/intel/igbvf/ethtool.c
drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c
drivers/net/ethernet/intel/ixgbe/ixgbe.h
drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c
drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
drivers/net/ethernet/intel/ixgbe/ixgbe_common.h
drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c
drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82598.c
drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c
drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h
drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c
drivers/net/ethernet/intel/ixgbe/ixgbe_debugfs.c
drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h
drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.c
drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h
drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h
drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h
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_main.c
drivers/net/ethernet/marvell/mv643xx_eth.c
drivers/net/ethernet/marvell/mvmdio.c
drivers/net/ethernet/marvell/mvneta.c
drivers/net/ethernet/marvell/pxa168_eth.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_rx.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/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/port.c
drivers/net/ethernet/mellanox/mlx4/profile.c
drivers/net/ethernet/mellanox/mlx4/qp.c
drivers/net/ethernet/mellanox/mlx4/reset.c
drivers/net/ethernet/mellanox/mlx4/resource_tracker.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/mlx5_core.h
drivers/net/ethernet/mellanox/mlx5/core/mr.c
drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c
drivers/net/ethernet/mellanox/mlx5/core/qp.c
drivers/net/ethernet/micrel/ks8695net.c
drivers/net/ethernet/micrel/ks8851.c
drivers/net/ethernet/micrel/ksz884x.c
drivers/net/ethernet/microchip/enc28j60.c
drivers/net/ethernet/myricom/myri10ge/myri10ge.c
drivers/net/ethernet/natsemi/natsemi.c
drivers/net/ethernet/natsemi/ns83820.c
drivers/net/ethernet/neterion/s2io.c
drivers/net/ethernet/neterion/vxge/vxge-config.c
drivers/net/ethernet/neterion/vxge/vxge-ethtool.c
drivers/net/ethernet/neterion/vxge/vxge-main.c
drivers/net/ethernet/nvidia/forcedeth.c
drivers/net/ethernet/nxp/lpc_eth.c
drivers/net/ethernet/oki-semi/pch_gbe/Kconfig
drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c
drivers/net/ethernet/packetengines/hamachi.c
drivers/net/ethernet/packetengines/yellowfin.c
drivers/net/ethernet/qlogic/Kconfig
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_ctx.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.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.h
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_main.c
drivers/net/ethernet/realtek/r8169.c
drivers/net/ethernet/renesas/sh_eth.c
drivers/net/ethernet/renesas/sh_eth.h
drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c
drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
drivers/net/ethernet/samsung/sxgbe/sxgbe_reg.h
drivers/net/ethernet/sfc/efx.c
drivers/net/ethernet/sfc/ethtool.c
drivers/net/ethernet/sfc/io.h
drivers/net/ethernet/sfc/siena_sriov.c
drivers/net/ethernet/sfc/tx.c
drivers/net/ethernet/sis/sis190.c
drivers/net/ethernet/smsc/smc91c92_cs.c
drivers/net/ethernet/smsc/smsc911x.c
drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
drivers/net/ethernet/tehuti/tehuti.c
drivers/net/ethernet/ti/cpmac.c
drivers/net/ethernet/ti/cpsw-phy-sel.c
drivers/net/ethernet/ti/cpsw.c
drivers/net/ethernet/ti/cpts.c
drivers/net/ethernet/ti/davinci_cpdma.c
drivers/net/ethernet/ti/davinci_emac.c
drivers/net/ethernet/ti/davinci_mdio.c
drivers/net/ethernet/tile/tilegx.c
drivers/net/ethernet/toshiba/ps3_gelic_net.c
drivers/net/ethernet/via/Kconfig
drivers/net/ethernet/via/via-rhine.c
drivers/net/ethernet/xilinx/ll_temac_main.c
drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
drivers/net/ethernet/xilinx/xilinx_emaclite.c
drivers/net/hyperv/hyperv_net.h
drivers/net/hyperv/netvsc.c
drivers/net/hyperv/netvsc_drv.c
drivers/net/hyperv/rndis_filter.c
drivers/net/ieee802154/at86rf230.c
drivers/net/ieee802154/fakelb.c
drivers/net/ieee802154/mrf24j40.c
drivers/net/irda/Kconfig
drivers/net/irda/via-ircc.c
drivers/net/irda/w83977af_ir.c
drivers/net/macvlan.c
drivers/net/ntb_netdev.c
drivers/net/phy/Kconfig
drivers/net/phy/Makefile
drivers/net/phy/amd-xgbe-phy.c [new file with mode: 0644]
drivers/net/phy/at803x.c
drivers/net/phy/fixed.c
drivers/net/phy/mdio_bus.c
drivers/net/phy/micrel.c
drivers/net/phy/phy_device.c
drivers/net/phy/realtek.c
drivers/net/phy/smsc.c
drivers/net/phy/vitesse.c
drivers/net/ppp/ppp_generic.c
drivers/net/ppp/pptp.c
drivers/net/rionet.c
drivers/net/team/team.c
drivers/net/team/team_mode_loadbalance.c
drivers/net/tun.c
drivers/net/usb/catc.c
drivers/net/usb/cdc_mbim.c
drivers/net/usb/cdc_ncm.c
drivers/net/usb/hso.c
drivers/net/usb/huawei_cdc_ncm.c
drivers/net/usb/ipheth.c
drivers/net/usb/kaweth.c
drivers/net/usb/pegasus.c
drivers/net/usb/qmi_wwan.c
drivers/net/usb/r8152.c
drivers/net/usb/rtl8150.c
drivers/net/virtio_net.c
drivers/net/vmxnet3/vmxnet3_ethtool.c
drivers/net/vxlan.c
drivers/net/wan/farsync.c
drivers/net/wan/sdla.c
drivers/net/wimax/i2400m/control.c
drivers/net/wimax/i2400m/driver.c
drivers/net/wireless/at76c50x-usb.c
drivers/net/wireless/at76c50x-usb.h
drivers/net/wireless/ath/ar5523/ar5523.c
drivers/net/wireless/ath/ath10k/bmi.c
drivers/net/wireless/ath/ath10k/bmi.h
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/htc.c
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/pci.c
drivers/net/wireless/ath/ath10k/pci.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/phy.c
drivers/net/wireless/ath/ath6kl/Kconfig
drivers/net/wireless/ath/ath6kl/cfg80211.c
drivers/net/wireless/ath/ath6kl/core.c
drivers/net/wireless/ath/ath6kl/debug.c
drivers/net/wireless/ath/ath6kl/debug.h
drivers/net/wireless/ath/ath6kl/hif.c
drivers/net/wireless/ath/ath6kl/hif.h
drivers/net/wireless/ath/ath6kl/htc_mbox.c
drivers/net/wireless/ath/ath6kl/htc_pipe.c
drivers/net/wireless/ath/ath6kl/init.c
drivers/net/wireless/ath/ath6kl/main.c
drivers/net/wireless/ath/ath6kl/sdio.c
drivers/net/wireless/ath/ath6kl/target.h
drivers/net/wireless/ath/ath6kl/txrx.c
drivers/net/wireless/ath/ath6kl/usb.c
drivers/net/wireless/ath/ath6kl/wmi.c
drivers/net/wireless/ath/ath6kl/wmi.h
drivers/net/wireless/ath/ath9k/Makefile
drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h
drivers/net/wireless/ath/ath9k/ar9330_1p1_initvals.h
drivers/net/wireless/ath/ath9k/ar9330_1p2_initvals.h
drivers/net/wireless/ath/ath9k/ar9340_initvals.h
drivers/net/wireless/ath/ath9k/ar953x_initvals.h
drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/beacon.c
drivers/net/wireless/ath/ath9k/common-debug.c [new file with mode: 0644]
drivers/net/wireless/ath/ath9k/common-debug.h [new file with mode: 0644]
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.c
drivers/net/wireless/ath/ath9k/htc.h
drivers/net/wireless/ath/ath9k/htc_drv_debug.c
drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/init.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/pci.c
drivers/net/wireless/ath/ath9k/recv.c
drivers/net/wireless/ath/ath9k/reg.h
drivers/net/wireless/ath/carl9170/main.c
drivers/net/wireless/ath/carl9170/usb.c
drivers/net/wireless/ath/dfs_pattern_detector.c
drivers/net/wireless/ath/wcn36xx/smd.c
drivers/net/wireless/ath/wil6210/cfg80211.c
drivers/net/wireless/ath/wil6210/debugfs.c
drivers/net/wireless/ath/wil6210/interrupt.c
drivers/net/wireless/ath/wil6210/main.c
drivers/net/wireless/ath/wil6210/netdev.c
drivers/net/wireless/ath/wil6210/pcie_bus.c
drivers/net/wireless/ath/wil6210/rx_reorder.c
drivers/net/wireless/ath/wil6210/txrx.c
drivers/net/wireless/ath/wil6210/wil6210.h
drivers/net/wireless/ath/wil6210/wmi.c
drivers/net/wireless/ath/wil6210/wmi.h
drivers/net/wireless/b43/Kconfig
drivers/net/wireless/b43/b43.h
drivers/net/wireless/b43/bus.h
drivers/net/wireless/b43/main.c
drivers/net/wireless/b43/phy_common.c
drivers/net/wireless/b43/phy_common.h
drivers/net/wireless/b43/phy_g.c
drivers/net/wireless/b43/phy_n.c
drivers/net/wireless/b43/phy_n.h
drivers/net/wireless/b43/radio_2056.c
drivers/net/wireless/b43/tables_nphy.c
drivers/net/wireless/b43/tables_nphy.h
drivers/net/wireless/b43/wa.c
drivers/net/wireless/b43/xmit.c
drivers/net/wireless/brcm80211/brcmfmac/Makefile
drivers/net/wireless/brcm80211/brcmfmac/dhd.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
drivers/net/wireless/brcm80211/brcmfmac/firmware.c [new file with mode: 0644]
drivers/net/wireless/brcm80211/brcmfmac/firmware.h [moved from drivers/net/wireless/brcm80211/brcmfmac/nvram.h with 52% similarity]
drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
drivers/net/wireless/brcm80211/brcmfmac/nvram.c [deleted file]
drivers/net/wireless/brcm80211/brcmfmac/usb.c
drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
drivers/net/wireless/brcm80211/brcmsmac/main.c
drivers/net/wireless/brcm80211/brcmutil/d11.c
drivers/net/wireless/brcm80211/include/brcmu_d11.h
drivers/net/wireless/brcm80211/include/brcmu_wifi.h
drivers/net/wireless/cw1200/sta.c
drivers/net/wireless/cw1200/sta.h
drivers/net/wireless/hostap/hostap_main.c
drivers/net/wireless/iwlegacy/3945.c
drivers/net/wireless/iwlegacy/4965-mac.c
drivers/net/wireless/iwlegacy/common.c
drivers/net/wireless/iwlegacy/common.h
drivers/net/wireless/iwlwifi/Kconfig
drivers/net/wireless/iwlwifi/dvm/Makefile
drivers/net/wireless/iwlwifi/dvm/calib.c
drivers/net/wireless/iwlwifi/dvm/debugfs.c
drivers/net/wireless/iwlwifi/dvm/dev.h
drivers/net/wireless/iwlwifi/dvm/devices.c
drivers/net/wireless/iwlwifi/dvm/led.h
drivers/net/wireless/iwlwifi/dvm/lib.c
drivers/net/wireless/iwlwifi/dvm/mac80211.c
drivers/net/wireless/iwlwifi/dvm/main.c
drivers/net/wireless/iwlwifi/dvm/power.c
drivers/net/wireless/iwlwifi/dvm/rs.c
drivers/net/wireless/iwlwifi/dvm/rx.c
drivers/net/wireless/iwlwifi/dvm/rxon.c
drivers/net/wireless/iwlwifi/dvm/scan.c
drivers/net/wireless/iwlwifi/dvm/sta.c
drivers/net/wireless/iwlwifi/dvm/tt.c
drivers/net/wireless/iwlwifi/dvm/tx.c
drivers/net/wireless/iwlwifi/dvm/ucode.c
drivers/net/wireless/iwlwifi/iwl-1000.c
drivers/net/wireless/iwlwifi/iwl-2000.c
drivers/net/wireless/iwlwifi/iwl-5000.c
drivers/net/wireless/iwlwifi/iwl-6000.c
drivers/net/wireless/iwlwifi/iwl-7000.c
drivers/net/wireless/iwlwifi/iwl-8000.c
drivers/net/wireless/iwlwifi/iwl-agn-hw.h
drivers/net/wireless/iwlwifi/iwl-config.h
drivers/net/wireless/iwlwifi/iwl-debug.c
drivers/net/wireless/iwlwifi/iwl-debug.h
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h [moved from drivers/net/wireless/iwlwifi/mvm/fw-error-dump.h with 82% similarity]
drivers/net/wireless/iwlwifi/iwl-fw.h
drivers/net/wireless/iwlwifi/iwl-io.c
drivers/net/wireless/iwlwifi/iwl-io.h
drivers/net/wireless/iwlwifi/iwl-modparams.h
drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
drivers/net/wireless/iwlwifi/iwl-op-mode.h
drivers/net/wireless/iwlwifi/iwl-phy-db.c
drivers/net/wireless/iwlwifi/iwl-prph.h
drivers/net/wireless/iwlwifi/iwl-trans.h
drivers/net/wireless/iwlwifi/mvm/Makefile
drivers/net/wireless/iwlwifi/mvm/coex.c
drivers/net/wireless/iwlwifi/mvm/d3.c
drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c
drivers/net/wireless/iwlwifi/mvm/debugfs.c
drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h
drivers/net/wireless/iwlwifi/mvm/fw-api-d3.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-tx.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/phy-ctxt.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/sf.c
drivers/net/wireless/iwlwifi/mvm/sta.c
drivers/net/wireless/iwlwifi/mvm/sta.h
drivers/net/wireless/iwlwifi/mvm/time-event.c
drivers/net/wireless/iwlwifi/mvm/tt.c
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/internal.h
drivers/net/wireless/iwlwifi/pcie/rx.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/iwlwifi/pcie/tx.c
drivers/net/wireless/libertas/cfg.c
drivers/net/wireless/libertas/defs.h
drivers/net/wireless/libertas/rx.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mwifiex/11ac.c
drivers/net/wireless/mwifiex/11n.c
drivers/net/wireless/mwifiex/11n.h
drivers/net/wireless/mwifiex/11n_aggr.c
drivers/net/wireless/mwifiex/README
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/cmdevt.c
drivers/net/wireless/mwifiex/debugfs.c
drivers/net/wireless/mwifiex/decl.h
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/ioctl.h
drivers/net/wireless/mwifiex/main.c
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/pcie.c
drivers/net/wireless/mwifiex/scan.c
drivers/net/wireless/mwifiex/sdio.c
drivers/net/wireless/mwifiex/sdio.h
drivers/net/wireless/mwifiex/sta_cmd.c
drivers/net/wireless/mwifiex/sta_cmdresp.c
drivers/net/wireless/mwifiex/sta_event.c
drivers/net/wireless/mwifiex/sta_rx.c
drivers/net/wireless/mwifiex/sta_tx.c
drivers/net/wireless/mwifiex/tdls.c
drivers/net/wireless/mwifiex/uap_cmd.c
drivers/net/wireless/mwifiex/usb.c
drivers/net/wireless/mwifiex/util.c
drivers/net/wireless/mwifiex/wmm.c
drivers/net/wireless/mwifiex/wmm.h
drivers/net/wireless/orinoco/hw.c
drivers/net/wireless/orinoco/hw.h
drivers/net/wireless/orinoco/orinoco_usb.c
drivers/net/wireless/orinoco/wext.c
drivers/net/wireless/p54/main.c
drivers/net/wireless/ray_cs.c
drivers/net/wireless/rndis_wlan.c
drivers/net/wireless/rsi/rsi_91x_mac80211.c
drivers/net/wireless/rsi/rsi_91x_mgmt.c
drivers/net/wireless/rsi/rsi_common.h
drivers/net/wireless/rsi/rsi_mgmt.h
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2x00.h
drivers/net/wireless/rt2x00/rt2x00mac.c
drivers/net/wireless/rt2x00/rt2x00usb.c
drivers/net/wireless/rt2x00/rt61pci.c
drivers/net/wireless/rt2x00/rt73usb.c
drivers/net/wireless/rtl818x/rtl8180/Makefile
drivers/net/wireless/rtl818x/rtl8180/dev.c
drivers/net/wireless/rtl818x/rtl8187/dev.c
drivers/net/wireless/rtl818x/rtl818x.h
drivers/net/wireless/rtlwifi/core.c
drivers/net/wireless/rtlwifi/rtl8188ee/hw.c
drivers/net/wireless/rtlwifi/rtl8188ee/hw.h
drivers/net/wireless/rtlwifi/rtl8188ee/sw.c
drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
drivers/net/wireless/rtlwifi/rtl8192ce/hw.h
drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
drivers/net/wireless/rtlwifi/rtl8192se/hw.c
drivers/net/wireless/rtlwifi/rtl8192se/hw.h
drivers/net/wireless/rtlwifi/rtl8192se/sw.c
drivers/net/wireless/rtlwifi/rtl8723ae/hal_bt_coexist.c
drivers/net/wireless/rtlwifi/rtl8723ae/hw.c
drivers/net/wireless/rtlwifi/rtl8723ae/hw.h
drivers/net/wireless/rtlwifi/rtl8723ae/sw.c
drivers/net/wireless/rtlwifi/rtl8723be/hw.c
drivers/net/wireless/rtlwifi/rtl8723be/hw.h
drivers/net/wireless/rtlwifi/rtl8723be/sw.c
drivers/net/wireless/rtlwifi/rtl8723be/trx.c
drivers/net/wireless/rtlwifi/wifi.h
drivers/net/wireless/ti/wl1251/acx.c
drivers/net/wireless/ti/wl1251/cmd.c
drivers/net/wireless/ti/wl1251/event.c
drivers/net/wireless/ti/wl1251/main.c
drivers/net/wireless/ti/wl1251/spi.c
drivers/net/wireless/ti/wlcore/debugfs.h
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/sdio.c
drivers/net/wireless/ti/wlcore/spi.c
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/pn544/i2c.c
drivers/nfc/port100.c
drivers/nfc/st21nfca/Kconfig [new file with mode: 0644]
drivers/nfc/st21nfca/Makefile [new file with mode: 0644]
drivers/nfc/st21nfca/i2c.c [new file with mode: 0644]
drivers/nfc/st21nfca/st21nfca.c [new file with mode: 0644]
drivers/nfc/st21nfca/st21nfca.h [new file with mode: 0644]
drivers/nfc/trf7970a.c
drivers/of/of_mdio.c
drivers/ptp/ptp_clock.c
drivers/s390/net/claw.c
drivers/s390/net/ctcm_main.c
drivers/s390/net/ctcm_sysfs.c
drivers/s390/net/lcs.c
drivers/s390/net/qeth_core.h
drivers/s390/net/qeth_core_main.c
drivers/s390/net/qeth_core_sys.c
drivers/s390/net/qeth_l2_main.c
drivers/s390/net/qeth_l3_main.c
drivers/scsi/Kconfig
drivers/scsi/iscsi_tcp.c
drivers/scsi/libiscsi.c
drivers/scsi/qla2xxx/qla_target.c
drivers/scsi/qla2xxx/qla_target.h
drivers/scsi/qla2xxx/tcm_qla2xxx.c
drivers/scsi/qla2xxx/tcm_qla2xxx.h
drivers/scsi/virtio_scsi.c
drivers/staging/et131x/et131x.c
drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c
drivers/staging/media/omap4iss/iss_video.c
drivers/staging/netlogic/xlr_net.c
drivers/staging/octeon/ethernet.c
drivers/staging/rtl8192ee/core.c
drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c
drivers/staging/rtl8821ae/core.c
drivers/staging/wlan-ng/cfg80211.c
drivers/target/iscsi/iscsi_target.c
drivers/target/iscsi/iscsi_target_auth.c
drivers/target/iscsi/iscsi_target_auth.h
drivers/target/iscsi/iscsi_target_login.c
drivers/target/iscsi/iscsi_target_nego.c
drivers/target/iscsi/iscsi_target_parameters.c
drivers/target/iscsi/iscsi_target_tpg.c
drivers/target/iscsi/iscsi_target_tpg.h
drivers/target/loopback/tcm_loop.c
drivers/target/target_core_sbc.c
drivers/target/target_core_spc.c
drivers/target/target_core_transport.c
drivers/target/target_core_xcopy.c
drivers/target/tcm_fc/tfc_cmd.c
drivers/target/tcm_fc/tfc_io.c
drivers/usb/gadget/u_ether.c
drivers/vhost/scsi.c
firmware/Makefile
fs/ceph/acl.c
fs/ceph/addr.c
fs/ceph/caps.c
fs/ceph/export.c
fs/ceph/inode.c
fs/ceph/mds_client.c
fs/ceph/mds_client.h
fs/ceph/super.h
fs/exec.c
include/asm-generic/qrwlock.h [new file with mode: 0644]
include/asm-generic/qrwlock_types.h [new file with mode: 0644]
include/asm-generic/vmlinux.lds.h
include/linux/ath9k_platform.h
include/linux/can/core.h
include/linux/can/dev.h
include/linux/can/led.h
include/linux/can/platform/cc770.h
include/linux/can/platform/mcp251x.h
include/linux/can/platform/rcar_can.h [new file with mode: 0644]
include/linux/can/platform/sja1000.h
include/linux/can/platform/ti_hecc.h
include/linux/can/skb.h
include/linux/ceph/ceph_fs.h
include/linux/ceph/mon_client.h
include/linux/compiler.h
include/linux/cpumask.h
include/linux/crc7.h
include/linux/ethtool.h
include/linux/filter.h
include/linux/ieee80211.h
include/linux/if_bridge.h
include/linux/if_link.h
include/linux/if_macvlan.h
include/linux/if_vlan.h
include/linux/isdn/capiutil.h
include/linux/kprobes.h
include/linux/ktime.h
include/linux/kvm_host.h
include/linux/mlx4/device.h
include/linux/netdev_features.h
include/linux/netdevice.h
include/linux/netfilter/nfnetlink_acct.h
include/linux/netlink.h
include/linux/nl802154.h
include/linux/of_mdio.h
include/linux/perf_event.h
include/linux/phy.h
include/linux/phy_fixed.h
include/linux/platform_data/st21nfca.h [new file with mode: 0644]
include/linux/rfkill-gpio.h
include/linux/ring_buffer.h
include/linux/rwsem.h
include/linux/sched.h
include/linux/skbuff.h
include/linux/spi/at86rf230.h
include/linux/ssb/ssb.h
include/linux/tcp.h
include/linux/udp.h
include/linux/uprobes.h
include/linux/usb/cdc_ncm.h
include/linux/virtio_scsi.h
include/media/videobuf2-core.h
include/net/6lowpan.h
include/net/addrconf.h
include/net/af_ieee802154.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/mgmt.h
include/net/bluetooth/rfcomm.h
include/net/cfg80211.h
include/net/checksum.h
include/net/dsa.h
include/net/gre.h
include/net/ieee802154.h
include/net/ieee802154_netdev.h
include/net/inet_ecn.h
include/net/inet_hashtables.h
include/net/inet_sock.h
include/net/inetpeer.h
include/net/ip.h
include/net/ip6_checksum.h
include/net/ip6_route.h
include/net/ipv6.h
include/net/mac80211.h
include/net/net_namespace.h
include/net/netfilter/nf_nat.h
include/net/netfilter/nf_tables.h
include/net/netfilter/nft_meta.h [new file with mode: 0644]
include/net/netns/ipv4.h
include/net/netns/ipv6.h
include/net/nfc/digital.h
include/net/nfc/hci.h
include/net/nfc/nfc.h
include/net/pkt_cls.h
include/net/pkt_sched.h
include/net/protocol.h
include/net/regulatory.h
include/net/sch_generic.h
include/net/sctp/structs.h
include/net/secure_seq.h
include/net/snmp.h
include/net/sock.h
include/net/tcp.h
include/net/tso.h [new file with mode: 0644]
include/net/udp.h
include/net/vxlan.h
include/net/xfrm.h
include/scsi/scsi_cmnd.h
include/target/iscsi/iscsi_transport.h
include/target/target_core_backend.h
include/trace/events/sched.h
include/uapi/linux/audit.h
include/uapi/linux/can.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/capability.h
include/uapi/linux/ethtool.h
include/uapi/linux/filter.h
include/uapi/linux/if_fddi.h
include/uapi/linux/if_link.h
include/uapi/linux/if_tunnel.h
include/uapi/linux/l2tp.h
include/uapi/linux/neighbour.h
include/uapi/linux/netfilter/nf_tables.h
include/uapi/linux/netfilter/nfnetlink.h
include/uapi/linux/netfilter/nfnetlink_acct.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/tipc.h
include/uapi/linux/tipc_config.h
include/uapi/linux/udp.h
include/xen/interface/io/netif.h
kernel/Kconfig.locks
kernel/audit.c
kernel/events/core.c
kernel/events/uprobes.c
kernel/kprobes.c
kernel/locking/Makefile
kernel/locking/qrwlock.c [new file with mode: 0644]
kernel/locking/rwsem-xadd.c
kernel/locking/rwsem.c
kernel/notifier.c
kernel/sched/core.c
kernel/sched/deadline.c
kernel/sched/fair.c
kernel/sched/features.h
kernel/sched/idle.c
kernel/sched/rt.c
kernel/sched/sched.h
kernel/seccomp.c
kernel/sysctl.c
kernel/trace/ring_buffer.c
kernel/trace/trace.c
kernel/trace/trace.h
kernel/trace/trace_event_perf.c
kernel/trace/trace_kprobe.c
kernel/trace/trace_probe.c
kernel/trace/trace_probe.h
kernel/trace/trace_uprobe.c
lib/Kconfig.debug
lib/Makefile
lib/cpumask.c
lib/crc7.c
lib/test_bpf.c [new file with mode: 0644]
net/8021q/vlan_core.c
net/8021q/vlan_dev.c
net/appletalk/ddp.c
net/atm/svc.c
net/batman-adv/debugfs.c
net/batman-adv/distributed-arp-table.c
net/batman-adv/main.h
net/batman-adv/network-coding.c
net/batman-adv/soft-interface.c
net/batman-adv/sysfs.c
net/bluetooth/6lowpan.c
net/bluetooth/hci_conn.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/hci_sock.c
net/bluetooth/l2cap_core.c
net/bluetooth/l2cap_sock.c
net/bluetooth/lib.c
net/bluetooth/mgmt.c
net/bluetooth/rfcomm/core.c
net/bluetooth/rfcomm/tty.c
net/bluetooth/smp.c
net/bluetooth/smp.h
net/bridge/Makefile
net/bridge/br.c
net/bridge/br_device.c
net/bridge/br_fdb.c
net/bridge/br_if.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_notify.c [deleted file]
net/bridge/br_private.h
net/bridge/br_sysfs_br.c
net/bridge/br_sysfs_if.c
net/bridge/br_vlan.c
net/bridge/netfilter/Kconfig
net/bridge/netfilter/Makefile
net/bridge/netfilter/nft_meta_bridge.c [new file with mode: 0644]
net/can/af_can.c
net/can/af_can.h
net/can/proc.c
net/ceph/ceph_common.c
net/ceph/debugfs.c
net/ceph/mon_client.c
net/ceph/osd_client.c
net/core/Makefile
net/core/datagram.c
net/core/dev.c
net/core/dev_addr_lists.c
net/core/ethtool.c
net/core/filter.c
net/core/net_namespace.c
net/core/pktgen.c
net/core/ptp_classifier.c
net/core/rtnetlink.c
net/core/secure_seq.c
net/core/skbuff.c
net/core/sock.c
net/core/tso.c [new file with mode: 0644]
net/dccp/ipv4.c
net/dccp/proto.c
net/dccp/sysctl.c
net/dccp/timer.c
net/decnet/af_decnet.c
net/dns_resolver/dns_query.c
net/dsa/slave.c
net/ieee802154/6lowpan_rtnl.c
net/ieee802154/dgram.c
net/ieee802154/header_ops.c
net/ieee802154/ieee802154.h
net/ieee802154/netlink.c
net/ieee802154/nl-mac.c
net/ieee802154/nl_policy.c
net/ieee802154/reassembly.c
net/ipv4/af_inet.c
net/ipv4/datagram.c
net/ipv4/devinet.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_hashtables.c
net/ipv4/inetpeer.c
net/ipv4/ip_forward.c
net/ipv4/ip_gre.c
net/ipv4/ip_options.c
net/ipv4/ip_output.c
net/ipv4/ip_tunnel.c
net/ipv4/ip_tunnel_core.c
net/ipv4/ip_vti.c
net/ipv4/ipip.c
net/ipv4/ipmr.c
net/ipv4/netfilter/iptable_nat.c
net/ipv4/netfilter/nf_defrag_ipv4.c
net/ipv4/netfilter/nft_chain_nat_ipv4.c
net/ipv4/proc.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_metrics.c
net/ipv4/tcp_minisocks.c
net/ipv4/tcp_offload.c
net/ipv4/tcp_output.c
net/ipv4/tcp_scalable.c
net/ipv4/tcp_vegas.c
net/ipv4/tcp_veno.c
net/ipv4/tcp_yeah.c
net/ipv4/udp.c
net/ipv4/udp_offload.c
net/ipv4/udplite.c
net/ipv4/xfrm4_mode_tunnel.c
net/ipv4/xfrm4_output.c
net/ipv6/addrconf.c
net/ipv6/addrconf_core.c
net/ipv6/af_inet6.c
net/ipv6/icmp.c
net/ipv6/inet6_connection_sock.c
net/ipv6/ip6_checksum.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
net/ipv6/netfilter/ip6table_nat.c
net/ipv6/netfilter/nf_conntrack_reasm.c
net/ipv6/netfilter/nft_chain_nat_ipv6.c
net/ipv6/output_core.c
net/ipv6/ping.c
net/ipv6/proc.c
net/ipv6/raw.c
net/ipv6/route.c
net/ipv6/sit.c
net/ipv6/syncookies.c
net/ipv6/sysctl_net_ipv6.c
net/ipv6/tcp_ipv6.c
net/ipv6/udp.c
net/ipv6/udp_offload.c
net/ipv6/udplite.c
net/ipv6/xfrm6_output.c
net/ipx/af_ipx.c
net/ipx/ipx_route.c
net/iucv/af_iucv.c
net/key/af_key.c
net/l2tp/l2tp_core.c
net/l2tp/l2tp_core.h
net/l2tp/l2tp_ip.c
net/l2tp/l2tp_ip6.c
net/l2tp/l2tp_netlink.c
net/mac80211/Makefile
net/mac80211/aes_ccm.c
net/mac80211/cfg.c
net/mac80211/chan.c
net/mac80211/debugfs.c
net/mac80211/debugfs.h
net/mac80211/debugfs_netdev.c
net/mac80211/debugfs_netdev.h
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/main.c
net/mac80211/mesh.c
net/mac80211/mesh_hwmp.c
net/mac80211/mesh_pathtbl.c
net/mac80211/mesh_sync.c
net/mac80211/michael.h
net/mac80211/mlme.c
net/mac80211/rc80211_minstrel.c
net/mac80211/rc80211_minstrel_ht.c
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/sta_info.c
net/mac80211/status.c
net/mac80211/tdls.c [new file with mode: 0644]
net/mac80211/trace.h
net/mac80211/tx.c
net/mac80211/util.c
net/mac80211/wpa.c
net/mac802154/Kconfig
net/mac802154/Makefile
net/mac802154/llsec.c [new file with mode: 0644]
net/mac802154/llsec.h [new file with mode: 0644]
net/mac802154/mac802154.h
net/mac802154/mac_cmd.c
net/mac802154/mib.c
net/mac802154/monitor.c
net/mac802154/rx.c
net/mac802154/wpan.c
net/mpls/mpls_gso.c
net/netfilter/ipset/ip_set_core.c
net/netfilter/ipvs/ip_vs_core.c
net/netfilter/ipvs/ip_vs_xmit.c
net/netfilter/nf_nat_core.c
net/netfilter/nf_tables_api.c
net/netfilter/nfnetlink.c
net/netfilter/nfnetlink_acct.c
net/netfilter/nft_ct.c
net/netfilter/nft_hash.c
net/netfilter/nft_lookup.c
net/netfilter/nft_meta.c
net/netfilter/nft_rbtree.c
net/netfilter/xt_bpf.c
net/netfilter/xt_nfacct.c
net/netfilter/xt_recent.c
net/netlink/af_netlink.c
net/netlink/af_netlink.h
net/netlink/genetlink.c
net/nfc/digital.h
net/nfc/digital_core.c
net/nfc/digital_dep.c
net/nfc/digital_technology.c
net/nfc/hci/command.c
net/nfc/hci/core.c
net/nfc/llcp_commands.c
net/nfc/llcp_core.c
net/nfc/nci/core.c
net/nfc/nci/ntf.c
net/nfc/nfc.h
net/nfc/rawsock.c
net/openvswitch/actions.c
net/openvswitch/datapath.c
net/openvswitch/datapath.h
net/openvswitch/flow.c
net/openvswitch/flow.h
net/openvswitch/flow_netlink.c
net/openvswitch/flow_netlink.h
net/openvswitch/flow_table.c
net/openvswitch/flow_table.h
net/openvswitch/vport-gre.c
net/openvswitch/vport-internal_dev.c
net/openvswitch/vport-vxlan.c
net/openvswitch/vport.h
net/rds/ib_send.c
net/rds/iw_send.c
net/rds/iw_sysctl.c
net/rds/rdma_transport.c
net/rds/sysctl.c
net/rds/tcp_listen.c
net/rfkill/rfkill-gpio.c
net/sched/cls_api.c
net/sched/cls_basic.c
net/sched/cls_bpf.c
net/sched/cls_cgroup.c
net/sched/cls_flow.c
net/sched/cls_fw.c
net/sched/cls_route.c
net/sched/cls_rsvp.h
net/sched/cls_tcindex.c
net/sched/cls_u32.c
net/sched/sch_api.c
net/sched/sch_choke.c
net/sched/sch_drr.c
net/sched/sch_fq.c
net/sched/sch_fq_codel.c
net/sched/sch_hhf.c
net/sched/sch_netem.c
net/sched/sch_sfq.c
net/sctp/associola.c
net/sctp/endpointola.c
net/sctp/ipv6.c
net/sctp/output.c
net/sctp/proc.c
net/sctp/protocol.c
net/sctp/sm_make_chunk.c
net/sctp/socket.c
net/sctp/sysctl.c
net/sctp/transport.c
net/sctp/ulpqueue.c
net/sunrpc/socklib.c
net/sunrpc/xprtsock.c
net/tipc/Makefile
net/tipc/bcast.c
net/tipc/bcast.h
net/tipc/bearer.c
net/tipc/bearer.h
net/tipc/config.c
net/tipc/core.c
net/tipc/core.h
net/tipc/discover.c
net/tipc/discover.h
net/tipc/eth_media.c
net/tipc/handler.c [deleted file]
net/tipc/ib_media.c
net/tipc/link.c
net/tipc/link.h
net/tipc/msg.c
net/tipc/msg.h
net/tipc/name_distr.c
net/tipc/name_distr.h
net/tipc/name_table.c
net/tipc/net.c
net/tipc/net.h
net/tipc/node.c
net/tipc/node.h
net/tipc/node_subscr.c
net/tipc/node_subscr.h
net/tipc/port.c
net/tipc/port.h
net/tipc/socket.c
net/tipc/socket.h
net/unix/af_unix.c
net/wireless/Kconfig
net/wireless/ap.c
net/wireless/chan.c
net/wireless/core.c
net/wireless/core.h
net/wireless/ethtool.c
net/wireless/genregdb.awk
net/wireless/ibss.c
net/wireless/mesh.c
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/nl80211.h
net/wireless/rdev-ops.h
net/wireless/reg.c
net/wireless/reg.h
net/wireless/scan.c
net/wireless/sme.c
net/wireless/trace.h
net/wireless/util.c
net/wireless/wext-compat.c
net/wireless/wext-compat.h
net/wireless/wext-sme.c
net/xfrm/xfrm_output.c
net/xfrm/xfrm_policy.c
net/xfrm/xfrm_proc.c
net/xfrm/xfrm_state.c
net/xfrm/xfrm_user.c
scripts/Makefile
scripts/Makefile.asm-generic
scripts/Makefile.build
scripts/Makefile.extrawarn [new file with mode: 0644]
scripts/Makefile.fwinst
scripts/Makefile.host
scripts/Makefile.lib
scripts/basic/fixdep.c
scripts/checkstack.pl
scripts/coccinelle/misc/of_table.cocci [new file with mode: 0644]
scripts/coccinelle/misc/returnvar.cocci [new file with mode: 0644]
scripts/config
scripts/conmakehash.c
scripts/docproc.c
scripts/dtc/.gitignore
scripts/dtc/fstree.c
scripts/dtc/libfdt/fdt_empty_tree.c
scripts/dtc/treesource.c
scripts/headers.sh
scripts/kallsyms.c
scripts/kconfig/Makefile
scripts/kconfig/check.sh
scripts/kconfig/conf.c
scripts/kconfig/gconf.c
scripts/kconfig/lxdialog/checklist.c
scripts/kconfig/lxdialog/inputbox.c
scripts/kconfig/lxdialog/menubox.c
scripts/kconfig/lxdialog/util.c
scripts/kconfig/mconf.c
scripts/kconfig/menu.c
scripts/kconfig/nconf.c
scripts/kconfig/streamline_config.pl
scripts/kconfig/util.c
scripts/kconfig/zconf.l
scripts/kconfig/zconf.lex.c_shipped
scripts/kconfig/zconf.tab.c_shipped
scripts/kconfig/zconf.y
scripts/markup_oops.pl
scripts/mkcompile_h
scripts/mkmakefile
scripts/mksysmap
scripts/mod/.gitignore
scripts/mod/file2alias.c
scripts/mod/mk_elfconfig.c
scripts/mod/modpost.c
scripts/mod/sumversion.c
scripts/objdiff
scripts/package/Makefile
scripts/package/builddeb
scripts/package/buildtar
scripts/patch-kernel
scripts/pnmtologo.c
scripts/recordmcount.c
scripts/rt-tester/check-all.sh
scripts/rt-tester/rt-tester.py
scripts/selinux/install_policy.sh
scripts/show_delta
scripts/tags.sh
security/selinux/include/classmap.h
tools/lib/api/fs/fs.c
tools/net/bpf_exp.l
tools/net/bpf_exp.y
tools/net/bpf_jit_disasm.c
tools/perf/Documentation/perf-record.txt
tools/perf/Documentation/perf-report.txt
tools/perf/Documentation/perf-top.txt
tools/perf/Makefile.perf
tools/perf/builtin-annotate.c
tools/perf/builtin-diff.c
tools/perf/builtin-record.c
tools/perf/builtin-report.c
tools/perf/builtin-sched.c
tools/perf/builtin-top.c
tools/perf/config/Makefile
tools/perf/perf.c
tools/perf/tests/builtin-test.c
tools/perf/tests/hists_common.c
tools/perf/tests/hists_common.h
tools/perf/tests/hists_cumulate.c [new file with mode: 0644]
tools/perf/tests/hists_filter.c
tools/perf/tests/hists_link.c
tools/perf/tests/hists_output.c
tools/perf/tests/tests.h
tools/perf/ui/browser.c
tools/perf/ui/browsers/hists.c
tools/perf/ui/gtk/hists.c
tools/perf/ui/hist.c
tools/perf/ui/stdio/hist.c
tools/perf/util/callchain.c
tools/perf/util/callchain.h
tools/perf/util/hist.c
tools/perf/util/hist.h
tools/perf/util/sort.c
tools/perf/util/sort.h
tools/perf/util/symbol.c
tools/perf/util/symbol.h
tools/testing/selftests/net/Makefile
tools/testing/selftests/powerpc/Makefile
tools/testing/selftests/powerpc/harness.c
tools/testing/selftests/powerpc/pmu/Makefile
tools/testing/selftests/powerpc/pmu/ebb/Makefile [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/close_clears_pmcc_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/ebb.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/ebb.h [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/ebb_handler.S [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/ebb_on_child_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/ebb_on_willing_child_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/ebb_vs_cpu_event_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/event_attributes_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/fixed_instruction_loop.S [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/fork_cleanup_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/no_handler_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/pmae_handling_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/reg.h [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/reg_access_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/task_event_pinned_vs_ebb_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/task_event_vs_ebb_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/trace.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/ebb/trace.h [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/event.c
tools/testing/selftests/powerpc/pmu/event.h
tools/testing/selftests/powerpc/pmu/lib.c [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/lib.h [new file with mode: 0644]
tools/testing/selftests/powerpc/pmu/loop.S
tools/testing/selftests/powerpc/subunit.h
tools/testing/selftests/powerpc/tm/Makefile [new file with mode: 0644]
tools/testing/selftests/powerpc/tm/tm-resched-dscr.c [new file with mode: 0644]
tools/testing/selftests/powerpc/utils.h
virt/kvm/kvm_main.c

index 42fa0d5626a9560d74d16b2df5250b300543b67e..f4c0b091dcf4e6413cbe70f0ec345a5894e0885e 100644 (file)
@@ -22,7 +22,6 @@
 *.lst
 *.symtypes
 *.order
-modules.builtin
 *.elf
 *.bin
 *.gz
@@ -33,6 +32,8 @@ modules.builtin
 *.lzo
 *.patch
 *.gcno
+modules.builtin
+Module.symvers
 
 #
 # Top-level generic files
@@ -44,7 +45,6 @@ modules.builtin
 /vmlinuz
 /System.map
 /Module.markers
-/Module.symvers
 
 #
 # Debian directory (make deb-pkg)
index d922060e455d5647e06c4f1182306d42c514a604..416c5d59f52eaf02081b88901cb0f165713e7e6a 100644 (file)
@@ -169,6 +169,14 @@ Description:
                "unknown", "notpresent", "down", "lowerlayerdown", "testing",
                "dormant", "up".
 
+What:          /sys/class/net/<iface>/phys_port_id
+Date:          July 2013
+KernelVersion: 3.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the interface unique physical port identifier within
+               the NIC, as a string.
+
 What:          /sys/class/net/<iface>/speed
 Date:          October 2009
 KernelVersion: 2.6.33
diff --git a/Documentation/ABI/testing/sysfs-class-net-cdc_ncm b/Documentation/ABI/testing/sysfs-class-net-cdc_ncm
new file mode 100644 (file)
index 0000000..5cedf72
--- /dev/null
@@ -0,0 +1,149 @@
+What:          /sys/class/net/<iface>/cdc_ncm/min_tx_pkt
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               The driver will pad NCM Transfer Blocks (NTBs) longer
+               than this to tx_max, allowing the device to receive
+               tx_max sized frames with no terminating short
+               packet. NTBs shorter than this limit are transmitted
+               as-is, without any padding, and are terminated with a
+               short USB packet.
+
+               Padding to tx_max allows the driver to transmit NTBs
+               back-to-back without any interleaving short USB
+               packets.  This reduces the number of short packet
+               interrupts in the device, and represents a tradeoff
+               between USB bus bandwidth and device DMA optimization.
+
+               Set to 0 to pad all frames. Set greater than tx_max to
+               disable all padding.
+
+What:          /sys/class/net/<iface>/cdc_ncm/rx_max
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               The maximum NTB size for RX.  Cannot exceed the
+               maximum value supported by the device. Must allow at
+               least one max sized datagram plus headers.
+
+               The actual limits are device dependent.  See
+               dwNtbInMaxSize.
+
+               Note: Some devices will silently ignore changes to
+               this value, resulting in oversized NTBs and
+               corresponding framing errors.
+
+What:          /sys/class/net/<iface>/cdc_ncm/tx_max
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               The maximum NTB size for TX.  Cannot exceed the
+               maximum value supported by the device.  Must allow at
+               least one max sized datagram plus headers.
+
+               The actual limits are device dependent.  See
+               dwNtbOutMaxSize.
+
+What:          /sys/class/net/<iface>/cdc_ncm/tx_timer_usecs
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               Datagram aggregation timeout in Âµs. The driver will
+               wait up to 3 times this timeout for more datagrams to
+               aggregate before transmitting an NTB frame.
+
+               Valid range: 5 to 4000000
+
+               Set to 0 to disable aggregation.
+
+The following read-only attributes all represent fields of the
+structure defined in section 6.2.1 "GetNtbParameters" of "Universal
+Serial Bus Communications Class Subclass Specifications for Network
+Control Model Devices" (CDC NCM), Revision 1.0 (Errata 1), November
+24, 2010 from USB Implementers Forum, Inc.  The descriptions are
+quoted from table 6-3 of CDC NCM: "NTB Parameter Structure".
+
+What:          /sys/class/net/<iface>/cdc_ncm/bmNtbFormatsSupported
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               Bit 0: 16-bit NTB supported (set to 1)
+               Bit 1: 32-bit NTB supported
+               Bits 2 â€“ 15: reserved (reset to zero; must be ignored by host)
+
+What:          /sys/class/net/<iface>/cdc_ncm/dwNtbInMaxSize
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               IN NTB Maximum Size in bytes
+
+What:          /sys/class/net/<iface>/cdc_ncm/wNdpInDivisor
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               Divisor used for IN NTB Datagram payload alignment
+
+What:          /sys/class/net/<iface>/cdc_ncm/wNdpInPayloadRemainder
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               Remainder used to align input datagram payload within
+               the NTB: (Payload Offset) mod (wNdpInDivisor) =
+               wNdpInPayloadRemainder
+
+What:          /sys/class/net/<iface>/cdc_ncm/wNdpInAlignment
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               NDP alignment modulus for NTBs on the IN pipe. Shall
+               be a power of 2, and shall be at least 4.
+
+What:          /sys/class/net/<iface>/cdc_ncm/dwNtbOutMaxSize
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               OUT NTB Maximum Size
+
+What:          /sys/class/net/<iface>/cdc_ncm/wNdpOutDivisor
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               OUT NTB Datagram alignment modulus
+
+What:          /sys/class/net/<iface>/cdc_ncm/wNdpOutPayloadRemainder
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               Remainder used to align output datagram payload
+               offsets within the NTB: Padding, shall be transmitted
+               as zero by function, and ignored by host.  (Payload
+               Offset) mod (wNdpOutDivisor) = wNdpOutPayloadRemainder
+
+What:          /sys/class/net/<iface>/cdc_ncm/wNdpOutAlignment
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               NDP alignment modulus for use in NTBs on the OUT
+               pipe. Shall be a power of 2, and shall be at least 4.
+
+What:          /sys/class/net/<iface>/cdc_ncm/wNtbOutMaxDatagrams
+Date:          May 2014
+KernelVersion: 3.16
+Contact:       Bjørn Mork <bjorn@mork.no>
+Description:
+               Maximum number of datagrams that the host may pack
+               into a single OUT NTB. Zero means that the device
+               imposes no limit.
diff --git a/Documentation/ABI/testing/sysfs-class-net-queues b/Documentation/ABI/testing/sysfs-class-net-queues
new file mode 100644 (file)
index 0000000..5e9aeb9
--- /dev/null
@@ -0,0 +1,79 @@
+What:          /sys/class/<iface>/queues/rx-<queue>/rps_cpus
+Date:          March 2010
+KernelVersion: 2.6.35
+Contact:       netdev@vger.kernel.org
+Description:
+               Mask of the CPU(s) currently enabled to participate into the
+               Receive Packet Steering packet processing flow for this
+               network device queue. Possible values depend on the number
+               of available CPU(s) in the system.
+
+What:          /sys/class/<iface>/queues/rx-<queue>/rps_flow_cnt
+Date:          April 2010
+KernelVersion: 2.6.35
+Contact:       netdev@vger.kernel.org
+Description:
+               Number of Receive Packet Steering flows being currently
+               processed by this particular network device receive queue.
+
+What:          /sys/class/<iface>/queues/tx-<queue>/tx_timeout
+Date:          November 2011
+KernelVersion: 3.3
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of transmit timeout events seen by this
+               network interface transmit queue.
+
+What:          /sys/class/<iface>/queues/tx-<queue>/xps_cpus
+Date:          November 2010
+KernelVersion: 2.6.38
+Contact:       netdev@vger.kernel.org
+Description:
+               Mask of the CPU(s) currently enabled to participate into the
+               Transmit Packet Steering packet processing flow for this
+               network device transmit queue. Possible vaules depend on the
+               number of available CPU(s) in the system.
+
+What:          /sys/class/<iface>/queues/tx-<queue>/byte_queue_limits/hold_time
+Date:          November 2011
+KernelVersion: 3.3
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the hold time in milliseconds to measure the slack
+               of this particular network device transmit queue.
+               Default value is 1000.
+
+What:          /sys/class/<iface>/queues/tx-<queue>/byte_queue_limits/inflight
+Date:          November 2011
+KernelVersion: 3.3
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of bytes (objects) in flight on this
+               network device transmit queue.
+
+What:          /sys/class/<iface>/queues/tx-<queue>/byte_queue_limits/limit
+Date:          November 2011
+KernelVersion: 3.3
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the current limit of bytes allowed to be queued
+               on this network device transmit queue. This value is clamped
+               to be within the bounds defined by limit_max and limit_min.
+
+What:          /sys/class/<iface>/queues/tx-<queue>/byte_queue_limits/limit_max
+Date:          November 2011
+KernelVersion: 3.3
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the absolute maximum limit of bytes allowed to be
+               queued on this network device transmit queue. See
+               include/linux/dynamic_queue_limits.h for the default value.
+
+What:          /sys/class/<iface>/queues/tx-<queue>/byte_queue_limits/limit_min
+Date:          November 2011
+KernelVersion: 3.3
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the absolute minimum limit of bytes allowed to be
+               queued on this network device transmit queue. Default value is
+               0.
diff --git a/Documentation/ABI/testing/sysfs-class-net-statistics b/Documentation/ABI/testing/sysfs-class-net-statistics
new file mode 100644 (file)
index 0000000..397118d
--- /dev/null
@@ -0,0 +1,201 @@
+What:          /sys/class/<iface>/statistics/collisions
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of collisions seen by this network device.
+               This value might not be relevant with all MAC layers.
+
+What:          /sys/class/<iface>/statistics/multicast
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of multicast packets received by this
+               network device.
+
+What:          /sys/class/<iface>/statistics/rx_bytes
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of bytes received by this network device.
+               See the network driver for the exact meaning of when this
+               value is incremented.
+
+What:          /sys/class/<iface>/statistics/rx_compressed
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of compressed packets received by this
+               network device. This value might only be relevant for interfaces
+               that support packet compression (e.g: PPP).
+
+What:          /sys/class/<iface>/statistics/rx_crc_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of packets received with a CRC (FCS) error
+               by this network device. Note that the specific meaning might
+               depend on the MAC layer used by the interface.
+
+What:          /sys/class/<iface>/statistics/rx_dropped
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of packets received by the network device
+               but dropped, that are not forwarded to the upper layers for
+               packet processing. See the network driver for the exact
+               meaning of this value.
+
+What:          /sys/class/<iface>/statistics/rx_fifo_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of receive FIFO errors seen by this
+               network device. See the network driver for the exact
+               meaning of this value.
+
+What:          /sys/class/<iface>/statistics/rx_frame_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of received frames with error, such as
+               alignment errors. Note that the specific meaning depends on
+               on the MAC layer protocol used. See the network driver for
+               the exact meaning of this value.
+
+What:          /sys/class/<iface>/statistics/rx_length_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of received error packet with a length
+               error, oversized or undersized. See the network driver for the
+               exact meaning of this value.
+
+What:          /sys/class/<iface>/statistics/rx_missed_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of received packets that have been missed
+               due to lack of capacity in the receive side. See the network
+               driver for the exact meaning of this value.
+
+What:          /sys/class/<iface>/statistics/rx_over_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of received packets that are oversized
+               compared to what the network device is configured to accept
+               (e.g: larger than MTU). See the network driver for the exact
+               meaning of this value.
+
+What:          /sys/class/<iface>/statistics/rx_packets
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the total number of good packets received by this
+               network device.
+
+What:          /sys/class/<iface>/statistics/tx_aborted_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of packets that have been aborted
+               during transmission by a network device (e.g: because of
+               a medium collision). See the network driver for the exact
+               meaning of this value.
+
+What:          /sys/class/<iface>/statistics/tx_bytes
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of bytes transmitted by a network
+               device. See the network driver for the exact meaning of this
+               value, in particular whether this accounts for all successfully
+               transmitted packets or all packets that have been queued for
+               transmission.
+
+What:          /sys/class/<iface>/statistics/tx_carrier_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of packets that could not be transmitted
+               because of carrier errors (e.g: physical link down). See the
+               network driver for the exact meaning of this value.
+
+What:          /sys/class/<iface>/statistics/tx_compressed
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of transmitted compressed packets. Note
+               this might only be relevant for devices that support
+               compression (e.g: PPP).
+
+What:          /sys/class/<iface>/statistics/tx_dropped
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of packets dropped during transmission.
+               See the driver for the exact reasons as to why the packets were
+               dropped.
+
+What:          /sys/class/<iface>/statistics/tx_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of packets in error during transmission by
+               a network device. See the driver for the exact reasons as to
+               why the packets were dropped.
+
+What:          /sys/class/<iface>/statistics/tx_fifo_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of packets having caused a transmit
+               FIFO error. See the driver for the exact reasons as to why the
+               packets were dropped.
+
+What:          /sys/class/<iface>/statistics/tx_heartbeat_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of packets transmitted that have been
+               reported as heartbeat errors. See the driver for the exact
+               reasons as to why the packets were dropped.
+
+What:          /sys/class/<iface>/statistics/tx_packets
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of packets transmitted by a network
+               device. See the driver for whether this reports the number of all
+               attempted or successful transmissions.
+
+What:          /sys/class/<iface>/statistics/tx_window_errors
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates the number of packets not successfully transmitted
+               due to a window collision. The specific meaning depends on the
+               MAC layer used.  On Ethernet this is usually used to report
+               late collisions errors.
index 044b76436e8373ae601f9c60bd274754f3df6033..d9b9416c989fd81f0a9a338b59fc9093d96479a4 100644 (file)
 !Finclude/net/cfg80211.h wdev_priv
 !Finclude/net/cfg80211.h ieee80211_iface_limit
 !Finclude/net/cfg80211.h ieee80211_iface_combination
+!Finclude/net/cfg80211.h cfg80211_check_combinations
       </chapter>
       <chapter>
       <title>Actions and configuration</title>
diff --git a/Documentation/devicetree/bindings/net/amd-xgbe-phy.txt b/Documentation/devicetree/bindings/net/amd-xgbe-phy.txt
new file mode 100644 (file)
index 0000000..d01ed63
--- /dev/null
@@ -0,0 +1,17 @@
+* AMD 10GbE PHY driver (amd-xgbe-phy)
+
+Required properties:
+- compatible: Should be "amd,xgbe-phy-seattle-v1a" and
+  "ethernet-phy-ieee802.3-c45"
+- reg: Address and length of the register sets for the device
+   - SerDes Rx/Tx registers
+   - SerDes integration registers (1/2)
+   - SerDes integration registers (2/2)
+
+Example:
+       xgbe_phy@e1240800 {
+               compatible = "amd,xgbe-phy-seattle-v1a", "ethernet-phy-ieee802.3-c45";
+               reg = <0 0xe1240800 0 0x00400>,
+                     <0 0xe1250000 0 0x00060>,
+                     <0 0xe1250080 0 0x00004>;
+       };
diff --git a/Documentation/devicetree/bindings/net/amd-xgbe.txt b/Documentation/devicetree/bindings/net/amd-xgbe.txt
new file mode 100644 (file)
index 0000000..ea0c790
--- /dev/null
@@ -0,0 +1,34 @@
+* AMD 10GbE driver (amd-xgbe)
+
+Required properties:
+- compatible: Should be "amd,xgbe-seattle-v1a"
+- reg: Address and length of the register sets for the device
+   - MAC registers
+   - PCS registers
+- interrupt-parent: Should be the phandle for the interrupt controller
+  that services interrupts for this device
+- interrupts: Should contain the amd-xgbe interrupt
+- clocks: Should be the DMA clock for the amd-xgbe device (used for
+  calculating the correct Rx interrupt watchdog timer value on a DMA
+  channel for coalescing)
+- clock-names: Should be the name of the DMA clock, "dma_clk"
+- phy-handle: See ethernet.txt file in the same directory
+- phy-mode: See ethernet.txt file in the same directory
+
+Optional properties:
+- mac-address: mac address to be assigned to the device. Can be overridden
+  by UEFI.
+
+Example:
+       xgbe@e0700000 {
+               compatible = "amd,xgbe-seattle-v1a";
+               reg = <0 0xe0700000 0 0x80000>,
+                     <0 0xe0780000 0 0x80000>;
+               interrupt-parent = <&gic>;
+               interrupts = <0 325 4>;
+               clocks = <&xgbe_clk>;
+               clock-names = "dma_clk";
+               phy-handle = <&phy>;
+               phy-mode = "xgmii";
+               mac-address = [ 02 a1 a2 a3 a4 a5 ];
+       };
index f2febb94550e8868b32497001f3a6514feddddfb..451fef26b4dfaf05b6782099e54816d52a52baa8 100644 (file)
@@ -24,7 +24,7 @@ Optional properties:
 - fixed-link: When the GENET interface is connected to a MoCA hardware block or
   when operating in a RGMII to RGMII type of connection, or when the MDIO bus is
   voluntarily disabled, this property should be used to describe the "fixed link".
-  See Documentation/devicetree/bindings/net/fsl-tsec-phy.txt for information on
+  See Documentation/devicetree/bindings/net/fixed-link.txt for information on
   the property specifics
 
 Required child nodes:
diff --git a/Documentation/devicetree/bindings/net/broadcom-systemport.txt b/Documentation/devicetree/bindings/net/broadcom-systemport.txt
new file mode 100644 (file)
index 0000000..c183ea9
--- /dev/null
@@ -0,0 +1,29 @@
+* Broadcom BCM7xxx Ethernet Systemport Controller (SYSTEMPORT)
+
+Required properties:
+- compatible: should be one of "brcm,systemport-v1.00" or "brcm,systemport"
+- reg: address and length of the register set for the device.
+- interrupts: interrupts for the device, first cell must be for the the rx
+  interrupts, and the second cell should be for the transmit queues
+- local-mac-address: Ethernet MAC address (48 bits) of this adapter
+- phy-mode: Should be a string describing the PHY interface to the
+  Ethernet switch/PHY, see Documentation/devicetree/bindings/net/ethernet.txt
+- fixed-link: see Documentation/devicetree/bindings/net/fixed-link.txt for
+  the property specific details
+
+Optional properties:
+- systemport,num-tier2-arb: number of tier 2 arbiters, an integer
+- systemport,num-tier1-arb: number of tier 1 arbiters, an integer
+- systemport,num-txq: number of HW transmit queues, an integer
+- systemport,num-rxq: number of HW receive queues, an integer
+
+Example:
+ethernet@f04a0000 {
+       compatible = "brcm,systemport-v1.00";
+       reg = <0xf04a0000 0x4650>;
+       local-mac-address = [ 00 11 22 33 44 55 ];
+       fixed-link = <0 1 1000 0 0>;
+       phy-mode = "gmii";
+       interrupts = <0x0 0x16 0x0>,
+               <0x0 0x17 0x0>;
+};
diff --git a/Documentation/devicetree/bindings/net/can/xilinx_can.txt b/Documentation/devicetree/bindings/net/can/xilinx_can.txt
new file mode 100644 (file)
index 0000000..fe38847
--- /dev/null
@@ -0,0 +1,44 @@
+Xilinx Axi CAN/Zynq CANPS controller Device Tree Bindings
+---------------------------------------------------------
+
+Required properties:
+- compatible           : Should be "xlnx,zynq-can-1.0" for Zynq CAN
+                         controllers and "xlnx,axi-can-1.00.a" for Axi CAN
+                         controllers.
+- reg                  : Physical base address and size of the Axi CAN/Zynq
+                         CANPS registers map.
+- interrupts           : Property with a value describing the interrupt
+                         number.
+- interrupt-parent     : Must be core interrupt controller
+- clock-names          : List of input clock names - "can_clk", "pclk"
+                         (For CANPS), "can_clk" , "s_axi_aclk"(For AXI CAN)
+                         (See clock bindings for details).
+- clocks               : Clock phandles (see clock bindings for details).
+- tx-fifo-depth                : Can Tx fifo depth.
+- rx-fifo-depth                : Can Rx fifo depth.
+
+
+Example:
+
+For Zynq CANPS Dts file:
+       zynq_can_0: can@e0008000 {
+                       compatible = "xlnx,zynq-can-1.0";
+                       clocks = <&clkc 19>, <&clkc 36>;
+                       clock-names = "can_clk", "pclk";
+                       reg = <0xe0008000 0x1000>;
+                       interrupts = <0 28 4>;
+                       interrupt-parent = <&intc>;
+                       tx-fifo-depth = <0x40>;
+                       rx-fifo-depth = <0x40>;
+               };
+For Axi CAN Dts file:
+       axi_can_0: axi-can@40000000 {
+                       compatible = "xlnx,axi-can-1.00.a";
+                       clocks = <&clkc 0>, <&clkc 1>;
+                       clock-names = "can_clk","s_axi_aclk" ;
+                       reg = <0x40000000 0x10000>;
+                       interrupt-parent = <&intc>;
+                       interrupts = <0 59 1>;
+                       tx-fifo-depth = <0x40>;
+                       rx-fifo-depth = <0x40>;
+               };
index 7ff57a119f81f449cfc2afb5ef988183071a8aa8..764c0c79b43d391435b847614c8d6c7aa4f06653 100644 (file)
@@ -2,7 +2,9 @@ TI CPSW Phy mode Selection Device Tree Bindings
 -----------------------------------------------
 
 Required properties:
-- compatible           : Should be "ti,am3352-cpsw-phy-sel"
+- compatible           : Should be "ti,am3352-cpsw-phy-sel" for am335x platform and
+                         "ti,dra7xx-cpsw-phy-sel" for dra7xx platform
+                         "ti,am43xx-cpsw-phy-sel" for am43xx platform
 - reg                  : physical base address and size of the cpsw
                          registers map
 - reg-names            : names of the register map given in "reg" node
diff --git a/Documentation/devicetree/bindings/net/fixed-link.txt b/Documentation/devicetree/bindings/net/fixed-link.txt
new file mode 100644 (file)
index 0000000..82bf7e0
--- /dev/null
@@ -0,0 +1,42 @@
+Fixed link Device Tree binding
+------------------------------
+
+Some Ethernet MACs have a "fixed link", and are not connected to a
+normal MDIO-managed PHY device. For those situations, a Device Tree
+binding allows to describe a "fixed link".
+
+Such a fixed link situation is described by creating a 'fixed-link'
+sub-node of the Ethernet MAC device node, with the following
+properties:
+
+* 'speed' (integer, mandatory), to indicate the link speed. Accepted
+  values are 10, 100 and 1000
+* 'full-duplex' (boolean, optional), to indicate that full duplex is
+  used. When absent, half duplex is assumed.
+* 'pause' (boolean, optional), to indicate that pause should be
+  enabled.
+* 'asym-pause' (boolean, optional), to indicate that asym_pause should
+  be enabled.
+
+Old, deprecated 'fixed-link' binding:
+
+* A 'fixed-link' property in the Ethernet MAC node, with 5 cells, of the
+  form <a b c d e> with the following accepted values:
+  - a: emulated PHY ID, choose any but but unique to the all specified
+    fixed-links, from 0 to 31
+  - b: duplex configuration: 0 for half duplex, 1 for full duplex
+  - c: link speed in Mbits/sec, accepted values are: 10, 100 and 1000
+  - d: pause configuration: 0 for no pause, 1 for pause
+  - e: asymmetric pause configuration: 0 for no asymmetric pause, 1 for
+    asymmetric pause
+
+Example:
+
+ethernet@0 {
+       ...
+       fixed-link {
+             speed = <1000>;
+             full-duplex;
+       };
+       ...
+};
index 737cdef4f9036eb6069b9f536f137351dc42b137..be6ea8960f208c72b7d69e0286c56712661ccb2c 100644 (file)
@@ -42,10 +42,7 @@ Properties:
     interrupt.  For TSEC and eTSEC devices, the first interrupt is
     transmit, the second is receive, and the third is error.
   - phy-handle : See ethernet.txt file in the same directory.
-  - fixed-link : <a b c d e> where a is emulated phy id - choose any,
-    but unique to the all specified fixed-links, b is duplex - 0 half,
-    1 full, c is link speed - d#10/d#100/d#1000, d is pause - 0 no
-    pause, 1 pause, e is asym_pause - 0 no asym_pause, 1 asym_pause.
+  - fixed-link : See fixed-link.txt in the same directory.
   - phy-connection-type : See ethernet.txt file in the same directory.
     This property is only really needed if the connection is of type
     "rgmii-id", as all other connection types are detected by hardware.
diff --git a/Documentation/devicetree/bindings/net/hisilicon-hix5hd2-gmac.txt b/Documentation/devicetree/bindings/net/hisilicon-hix5hd2-gmac.txt
new file mode 100644 (file)
index 0000000..75d398b
--- /dev/null
@@ -0,0 +1,36 @@
+Hisilicon hix5hd2 gmac controller
+
+Required properties:
+- compatible: should be "hisilicon,hix5hd2-gmac".
+- reg: specifies base physical address(s) and size of the device registers.
+  The first region is the MAC register base and size.
+  The second region is external interface control register.
+- interrupts: should contain the MAC interrupt.
+- #address-cells: must be <1>.
+- #size-cells: must be <0>.
+- phy-mode: see ethernet.txt [1].
+- phy-handle: see ethernet.txt [1].
+- mac-address: see ethernet.txt [1].
+- clocks: clock phandle and specifier pair.
+
+- PHY subnode: inherits from phy binding [2]
+
+[1] Documentation/devicetree/bindings/net/ethernet.txt
+[2] Documentation/devicetree/bindings/net/phy.txt
+
+Example:
+       gmac0: ethernet@f9840000 {
+               compatible = "hisilicon,hix5hd2-gmac";
+               reg = <0xf9840000 0x1000>,<0xf984300c 0x4>;
+               interrupts = <0 71 4>;
+               #address-cells = <1>;
+               #size-cells = <0>;
+               phy-mode = "mii";
+               phy-handle = <&phy2>;
+               mac-address = [00 00 00 00 00 00];
+               clocks = <&clock HIX5HD2_MAC0_CLK>;
+
+               phy2: ethernet-phy@2 {
+                       reg = <2>;
+               };
+       };
diff --git a/Documentation/devicetree/bindings/net/ieee802154/at86rf230.txt b/Documentation/devicetree/bindings/net/ieee802154/at86rf230.txt
new file mode 100644 (file)
index 0000000..d3bbdde
--- /dev/null
@@ -0,0 +1,23 @@
+* AT86RF230 IEEE 802.15.4 *
+
+Required properties:
+  - compatible:                should be "atmel,at86rf230", "atmel,at86rf231",
+                       "atmel,at86rf233" or "atmel,at86rf212"
+  - spi-max-frequency: maximal bus speed, should be set to 7500000 depends
+                       sync or async operation mode
+  - reg:               the chipselect index
+  - interrupts:                the interrupt generated by the device
+
+Optional properties:
+  - reset-gpio:                GPIO spec for the rstn pin
+  - sleep-gpio:                GPIO spec for the slp_tr pin
+
+Example:
+
+       at86rf231@0 {
+               compatible = "atmel,at86rf231";
+               spi-max-frequency = <7500000>;
+               reg = <0>;
+               interrupts = <19 1>;
+               interrupt-parent = <&gpio3>;
+       };
index d54d0cc794871b29cbbbf9fbedd6242defa63677..bbdf9a7359a2ef021c3d6f780e2c14e7f3434740 100644 (file)
@@ -1,9 +1,18 @@
-Micrel KS8851 Ethernet mac
+Micrel KS8851 Ethernet mac (MLL)
 
 Required properties:
-- compatible = "micrel,ks8851-ml" of parallel interface
+- compatible = "micrel,ks8851-mll" of parallel interface
 - reg : 2 physical address and size of registers for data and command
 - interrupts : interrupt connection
 
+Micrel KS8851 Ethernet mac (SPI)
+
+Required properties:
+- compatible = "micrel,ks8851" or the deprecated "ks8851"
+- reg : chip select number
+- interrupts : interrupt connection
+
 Optional properties:
-- vdd-supply:  supply for Ethernet mac
+- vdd-supply: analog 3.3V supply for Ethernet mac
+- vdd-io-supply: digital 1.8V IO supply for Ethernet mac
+- reset-gpios: reset_n input pin
diff --git a/Documentation/devicetree/bindings/net/micrel-ksz9021.txt b/Documentation/devicetree/bindings/net/micrel-ksz9021.txt
deleted file mode 100644 (file)
index 997a63f..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-Micrel KSZ9021 Gigabit Ethernet PHY
-
-Some boards require special tuning values, particularly when it comes to
-clock delays.  You can specify clock delay values by adding
-micrel-specific properties to an Ethernet OF device node.
-
-All skew control options are specified in picoseconds.  The minimum
-value is 0, and the maximum value is 3000.
-
-Optional properties:
- - rxc-skew-ps : Skew control of RXC pad
- - rxdv-skew-ps : Skew control of RX CTL pad
- - txc-skew-ps : Skew control of TXC pad
- - txen-skew-ps : Skew control of TX_CTL pad
- - rxd0-skew-ps : Skew control of RX data 0 pad
- - rxd1-skew-ps : Skew control of RX data 1 pad
- - rxd2-skew-ps : Skew control of RX data 2 pad
- - rxd3-skew-ps : Skew control of RX data 3 pad
- - txd0-skew-ps : Skew control of TX data 0 pad
- - txd1-skew-ps : Skew control of TX data 1 pad
- - txd2-skew-ps : Skew control of TX data 2 pad
- - txd3-skew-ps : Skew control of TX data 3 pad
-
-Examples:
-
-       /* Attach to an Ethernet device with autodetected PHY */
-       &enet {
-               rxc-skew-ps = <3000>;
-               rxdv-skew-ps = <0>;
-               txc-skew-ps = <3000>;
-               txen-skew-ps = <0>;
-               status = "okay";
-       };
-
-       /* Attach to an explicitly-specified PHY */
-       mdio {
-               phy0: ethernet-phy@0 {
-                       rxc-skew-ps = <3000>;
-                       rxdv-skew-ps = <0>;
-                       txc-skew-ps = <3000>;
-                       txen-skew-ps = <0>;
-                       reg = <0>;
-               };
-       };
-       ethernet@70000 {
-               status = "okay";
-               phy = <&phy0>;
-               phy-mode = "rgmii-id";
-       };
diff --git a/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt b/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt
new file mode 100644 (file)
index 0000000..692076f
--- /dev/null
@@ -0,0 +1,83 @@
+Micrel KSZ9021/KSZ9031 Gigabit Ethernet PHY
+
+Some boards require special tuning values, particularly when it comes to
+clock delays. You can specify clock delay values by adding
+micrel-specific properties to an Ethernet OF device node.
+
+Note that these settings are applied after any phy-specific fixup from
+phy_fixup_list (see phy_init_hw() from drivers/net/phy/phy_device.c),
+and therefore may overwrite them.
+
+KSZ9021:
+
+  All skew control options are specified in picoseconds. The minimum
+  value is 0, the maximum value is 3000, and it is incremented by 200ps
+  steps.
+
+  Optional properties:
+
+    - rxc-skew-ps : Skew control of RXC pad
+    - rxdv-skew-ps : Skew control of RX CTL pad
+    - txc-skew-ps : Skew control of TXC pad
+    - txen-skew-ps : Skew control of TX CTL pad
+    - rxd0-skew-ps : Skew control of RX data 0 pad
+    - rxd1-skew-ps : Skew control of RX data 1 pad
+    - rxd2-skew-ps : Skew control of RX data 2 pad
+    - rxd3-skew-ps : Skew control of RX data 3 pad
+    - txd0-skew-ps : Skew control of TX data 0 pad
+    - txd1-skew-ps : Skew control of TX data 1 pad
+    - txd2-skew-ps : Skew control of TX data 2 pad
+    - txd3-skew-ps : Skew control of TX data 3 pad
+
+KSZ9031:
+
+  All skew control options are specified in picoseconds. The minimum
+  value is 0, and the maximum is property-dependent. The increment
+  step is 60ps.
+
+  Optional properties:
+
+    Maximum value of 1860:
+
+      - rxc-skew-ps : Skew control of RX clock pad
+      - txc-skew-ps : Skew control of TX clock pad
+
+    Maximum value of 900:
+
+      - rxdv-skew-ps : Skew control of RX CTL pad
+      - txen-skew-ps : Skew control of TX CTL pad
+      - rxd0-skew-ps : Skew control of RX data 0 pad
+      - rxd1-skew-ps : Skew control of RX data 1 pad
+      - rxd2-skew-ps : Skew control of RX data 2 pad
+      - rxd3-skew-ps : Skew control of RX data 3 pad
+      - txd0-skew-ps : Skew control of TX data 0 pad
+      - txd1-skew-ps : Skew control of TX data 1 pad
+      - txd2-skew-ps : Skew control of TX data 2 pad
+      - txd3-skew-ps : Skew control of TX data 3 pad
+
+Examples:
+
+       /* Attach to an Ethernet device with autodetected PHY */
+       &enet {
+               rxc-skew-ps = <3000>;
+               rxdv-skew-ps = <0>;
+               txc-skew-ps = <3000>;
+               txen-skew-ps = <0>;
+               status = "okay";
+       };
+
+       /* Attach to an explicitly-specified PHY */
+       mdio {
+               phy0: ethernet-phy@0 {
+                       rxc-skew-ps = <3000>;
+                       rxdv-skew-ps = <0>;
+                       txc-skew-ps = <3000>;
+                       txen-skew-ps = <0>;
+                       reg = <0>;
+               };
+       };
+       ethernet@70000 {
+               status = "okay";
+               phy = <&phy0>;
+               phy-mode = "rgmii-id";
+       };
diff --git a/Documentation/devicetree/bindings/net/nfc/pn544.txt b/Documentation/devicetree/bindings/net/nfc/pn544.txt
new file mode 100644 (file)
index 0000000..dab69f3
--- /dev/null
@@ -0,0 +1,35 @@
+* NXP Semiconductors PN544 NFC Controller
+
+Required properties:
+- compatible: Should be "nxp,pn544-i2c".
+- clock-frequency: I²C work frequency.
+- reg: address on the bus
+- interrupt-parent: phandle for the interrupt gpio controller
+- interrupts: GPIO interrupt to which the chip is connected
+- enable-gpios: Output GPIO pin used for enabling/disabling the PN544
+- firmware-gpios: Output GPIO pin used to enter firmware download mode
+
+Optional SoC Specific Properties:
+- pinctrl-names: Contains only one value - "default".
+- pintctrl-0: Specifies the pin control groups used for this controller.
+
+Example (for ARM-based BeagleBone with PN544 on I2C2):
+
+&i2c2 {
+
+       status = "okay";
+
+       pn544: pn544@28 {
+
+               compatible = "nxp,pn544-i2c";
+
+               reg = <0x28>;
+               clock-frequency = <400000>;
+
+               interrupt-parent = <&gpio1>;
+               interrupts = <17 GPIO_ACTIVE_HIGH>;
+
+               enable-gpios = <&gpio3 21 GPIO_ACTIVE_HIGH>;
+               firmware-gpios = <&gpio3 19 GPIO_ACTIVE_HIGH>;
+       };
+};
diff --git a/Documentation/devicetree/bindings/net/nfc/st21nfca.txt b/Documentation/devicetree/bindings/net/nfc/st21nfca.txt
new file mode 100644 (file)
index 0000000..e4faa2e
--- /dev/null
@@ -0,0 +1,33 @@
+* STMicroelectronics SAS. ST21NFCA NFC Controller
+
+Required properties:
+- compatible: Should be "st,st21nfca_i2c".
+- clock-frequency: I²C work frequency.
+- reg: address on the bus
+- interrupt-parent: phandle for the interrupt gpio controller
+- interrupts: GPIO interrupt to which the chip is connected
+- enable-gpios: Output GPIO pin used for enabling/disabling the ST21NFCA
+
+Optional SoC Specific Properties:
+- pinctrl-names: Contains only one value - "default".
+- pintctrl-0: Specifies the pin control groups used for this controller.
+
+Example (for ARM-based BeagleBoard xM with ST21NFCA on I2C2):
+
+&i2c2 {
+
+       status = "okay";
+
+       st21nfca: st21nfca@1 {
+
+               compatible = "st,st21nfca_i2c";
+
+               reg = <0x01>;
+               clock-frequency = <400000>;
+
+               interrupt-parent = <&gpio5>;
+               interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
+
+               enable-gpios = <&gpio5 29 GPIO_ACTIVE_HIGH>;
+       };
+};
index 8dd3ef7bc56b560b1b4eb29c25feaf9f5d1785cc..1e436133685f91f470ba7f63e302503422bd818e 100644 (file)
@@ -12,6 +12,7 @@ Required properties:
 Optional SoC Specific Properties:
 - pinctrl-names: Contains only one value - "default".
 - pintctrl-0: Specifies the pin control groups used for this controller.
+- autosuspend-delay: Specify autosuspend delay in milliseconds.
 
 Example (for ARM-based BeagleBone with TRF7970A on SPI1):
 
@@ -29,6 +30,7 @@ Example (for ARM-based BeagleBone with TRF7970A on SPI1):
                ti,enable-gpios = <&gpio2 2 GPIO_ACTIVE_LOW>,
                                  <&gpio2 5 GPIO_ACTIVE_LOW>;
                vin-supply = <&ldo3_reg>;
+               autosuspend-delay = <30000>;
                status = "okay";
        };
 };
diff --git a/Documentation/devicetree/bindings/net/via-rhine.txt b/Documentation/devicetree/bindings/net/via-rhine.txt
new file mode 100644 (file)
index 0000000..334eca2
--- /dev/null
@@ -0,0 +1,17 @@
+* VIA Rhine 10/100 Network Controller
+
+Required properties:
+- compatible : Should be "via,vt8500-rhine" for integrated
+       Rhine controllers found in VIA VT8500, WonderMedia WM8950
+       and similar. These are listed as 1106:3106 rev. 0x84 on the
+       virtual PCI bus under vendor-provided kernels
+- reg : Address and length of the io space
+- interrupts : Should contain the controller interrupt line
+
+Examples:
+
+ethernet@d8004000 {
+       compatible = "via,vt8500-rhine";
+       reg = <0xd8004000 0x100>;
+       interrupts = <10>;
+};
index 89472558011ef295a98667745aa313cf4e6187f1..1525e30483fda44188d22b22548dd5d3068fe8e6 100644 (file)
@@ -318,3 +318,8 @@ GPIO
   devm_gpiod_get_optional()
   devm_gpiod_get_index_optional()
   devm_gpiod_put()
+
+MDIO
+  devm_mdiobus_alloc()
+  devm_mdiobus_alloc_size()
+  devm_mdiobus_free()
index 69372fb98cf89e4971e1520cd335ff66f27f7a6f..3fb39e0116b4c8e42d40009357ed5cf13c1f2888 100644 (file)
@@ -470,7 +470,7 @@ build.
 
        Sometimes, an external module uses exported symbols from
        another external module. kbuild needs to have full knowledge of
-       all symbols to avoid spliitting out warnings about undefined
+       all symbols to avoid spitting out warnings about undefined
        symbols. Three solutions exist for this situation.
 
        NOTE: The method with a top-level kbuild file is recommended
index 0cfb00fd86ffd7834a395df7688879d42132fb0e..4bbeca8483ed339f7efd5b6314da77f9b4a99f0d 100644 (file)
@@ -22,8 +22,9 @@ Appendix B: The kprobes sysctl interface
 
 Kprobes enables you to dynamically break into any kernel routine and
 collect debugging and performance information non-disruptively. You
-can trap at almost any kernel code address, specifying a handler
+can trap at almost any kernel code address(*), specifying a handler
 routine to be invoked when the breakpoint is hit.
+(*: some parts of the kernel code can not be trapped, see 1.5 Blacklist)
 
 There are currently three types of probes: kprobes, jprobes, and
 kretprobes (also called return probes).  A kprobe can be inserted
@@ -273,6 +274,19 @@ using one of the following techniques:
  or
 - Execute 'sysctl -w debug.kprobes_optimization=n'
 
+1.5 Blacklist
+
+Kprobes can probe most of the kernel except itself. This means
+that there are some functions where kprobes cannot probe. Probing
+(trapping) such functions can cause a recursive trap (e.g. double
+fault) or the nested probe handler may never be called.
+Kprobes manages such functions as a blacklist.
+If you want to add a function into the blacklist, you just need
+to (1) include linux/kprobes.h and (2) use NOKPROBE_SYMBOL() macro
+to specify a blacklisted function.
+Kprobes checks the given probe address against the blacklist and
+rejects registering it, if the given address is in the blacklist.
+
 2. Architectures Supported
 
 Kprobes, jprobes, and return probes are implemented on the following
index 1dfe62c3641d5d9087388c0255f2a9775b520faa..ee231ed09ec6fd96bbc75f6b7a3a76272ffd9500 100644 (file)
 Generic Mutex Subsystem
 
 started by Ingo Molnar <mingo@redhat.com>
+updated by Davidlohr Bueso <davidlohr@hp.com>
 
-  "Why on earth do we need a new mutex subsystem, and what's wrong
-   with semaphores?"
+What are mutexes?
+-----------------
 
-firstly, there's nothing wrong with semaphores. But if the simpler
-mutex semantics are sufficient for your code, then there are a couple
-of advantages of mutexes:
+In the Linux kernel, mutexes refer to a particular locking primitive
+that enforces serialization on shared memory systems, and not only to
+the generic term referring to 'mutual exclusion' found in academia
+or similar theoretical text books. Mutexes are sleeping locks which
+behave similarly to binary semaphores, and were introduced in 2006[1]
+as an alternative to these. This new data structure provided a number
+of advantages, including simpler interfaces, and at that time smaller
+code (see Disadvantages).
 
- - 'struct mutex' is smaller on most architectures: E.g. on x86,
-   'struct semaphore' is 20 bytes, 'struct mutex' is 16 bytes.
-   A smaller structure size means less RAM footprint, and better
-   CPU-cache utilization.
+[1] http://lwn.net/Articles/164802/
 
- - tighter code. On x86 i get the following .text sizes when
-   switching all mutex-alike semaphores in the kernel to the mutex
-   subsystem:
+Implementation
+--------------
 
-        text    data     bss     dec     hex filename
-     3280380  868188  396860 4545428  455b94 vmlinux-semaphore
-     3255329  865296  396732 4517357  44eded vmlinux-mutex
+Mutexes are represented by 'struct mutex', defined in include/linux/mutex.h
+and implemented in kernel/locking/mutex.c. These locks use a three
+state atomic counter (->count) to represent the different possible
+transitions that can occur during the lifetime of a lock:
 
-   that's 25051 bytes of code saved, or a 0.76% win - off the hottest
-   codepaths of the kernel. (The .data savings are 2892 bytes, or 0.33%)
-   Smaller code means better icache footprint, which is one of the
-   major optimization goals in the Linux kernel currently.
+         1: unlocked
+         0: locked, no waiters
+   negative: locked, with potential waiters
 
- - the mutex subsystem is slightly faster and has better scalability for
-   contended workloads. On an 8-way x86 system, running a mutex-based
-   kernel and testing creat+unlink+close (of separate, per-task files)
-   in /tmp with 16 parallel tasks, the average number of ops/sec is:
+In its most basic form it also includes a wait-queue and a spinlock
+that serializes access to it. CONFIG_SMP systems can also include
+a pointer to the lock task owner (->owner) as well as a spinner MCS
+lock (->osq), both described below in (ii).
 
-    Semaphores:                        Mutexes:
+When acquiring a mutex, there are three possible paths that can be
+taken, depending on the state of the lock:
 
-    $ ./test-mutex V 16 10             $ ./test-mutex V 16 10
-    8 CPUs, running 16 tasks.          8 CPUs, running 16 tasks.
-    checking VFS performance.          checking VFS performance.
-    avg loops/sec:      34713          avg loops/sec:      84153
-    CPU utilization:    63%            CPU utilization:    22%
+(i) fastpath: tries to atomically acquire the lock by decrementing the
+    counter. If it was already taken by another task it goes to the next
+    possible path. This logic is architecture specific. On x86-64, the
+    locking fastpath is 2 instructions:
 
-   i.e. in this workload, the mutex based kernel was 2.4 times faster
-   than the semaphore based kernel, _and_ it also had 2.8 times less CPU
-   utilization. (In terms of 'ops per CPU cycle', the semaphore kernel
-   performed 551 ops/sec per 1% of CPU time used, while the mutex kernel
-   performed 3825 ops/sec per 1% of CPU time used - it was 6.9 times
-   more efficient.)
-
-   the scalability difference is visible even on a 2-way P4 HT box:
-
-    Semaphores:                        Mutexes:
-
-    $ ./test-mutex V 16 10             $ ./test-mutex V 16 10
-    4 CPUs, running 16 tasks.          8 CPUs, running 16 tasks.
-    checking VFS performance.          checking VFS performance.
-    avg loops/sec:      127659         avg loops/sec:      181082
-    CPU utilization:    100%           CPU utilization:    34%
-
-   (the straight performance advantage of mutexes is 41%, the per-cycle
-    efficiency of mutexes is 4.1 times better.)
-
- - there are no fastpath tradeoffs, the mutex fastpath is just as tight
-   as the semaphore fastpath. On x86, the locking fastpath is 2
-   instructions:
-
-    c0377ccb <mutex_lock>:
-    c0377ccb:       f0 ff 08                lock decl (%eax)
-    c0377cce:       78 0e                   js     c0377cde <.text..lock.mutex>
-    c0377cd0:       c3                      ret
+    0000000000000e10 <mutex_lock>:
+    e21:   f0 ff 0b                lock decl (%rbx)
+    e24:   79 08                   jns    e2e <mutex_lock+0x1e>
 
    the unlocking fastpath is equally tight:
 
-    c0377cd1 <mutex_unlock>:
-    c0377cd1:       f0 ff 00                lock incl (%eax)
-    c0377cd4:       7e 0f                   jle    c0377ce5 <.text..lock.mutex+0x7>
-    c0377cd6:       c3                      ret
-
- - 'struct mutex' semantics are well-defined and are enforced if
-   CONFIG_DEBUG_MUTEXES is turned on. Semaphores on the other hand have
-   virtually no debugging code or instrumentation. The mutex subsystem
-   checks and enforces the following rules:
-
-   * - only one task can hold the mutex at a time
-   * - only the owner can unlock the mutex
-   * - multiple unlocks are not permitted
-   * - recursive locking is not permitted
-   * - a mutex object must be initialized via the API
-   * - a mutex object must not be initialized via memset or copying
-   * - task may not exit with mutex held
-   * - memory areas where held locks reside must not be freed
-   * - held mutexes must not be reinitialized
-   * - mutexes may not be used in hardware or software interrupt
-   *   contexts such as tasklets and timers
-
-   furthermore, there are also convenience features in the debugging
-   code:
-
-   * - uses symbolic names of mutexes, whenever they are printed in debug output
-   * - point-of-acquire tracking, symbolic lookup of function names
-   * - list of all locks held in the system, printout of them
-   * - owner tracking
-   * - detects self-recursing locks and prints out all relevant info
-   * - detects multi-task circular deadlocks and prints out all affected
-   *   locks and tasks (and only those tasks)
+    0000000000000bc0 <mutex_unlock>:
+    bc8:   f0 ff 07                lock incl (%rdi)
+    bcb:   7f 0a                   jg     bd7 <mutex_unlock+0x17>
+
+
+(ii) midpath: aka optimistic spinning, tries to spin for acquisition
+     while the lock owner is running and there are no other tasks ready
+     to run that have higher priority (need_resched). The rationale is
+     that if the lock owner is running, it is likely to release the lock
+     soon. The mutex spinners are queued up using MCS lock so that only
+     one spinner can compete for the mutex.
+
+     The MCS lock (proposed by Mellor-Crummey and Scott) is a simple spinlock
+     with the desirable properties of being fair and with each cpu trying
+     to acquire the lock spinning on a local variable. It avoids expensive
+     cacheline bouncing that common test-and-set spinlock implementations
+     incur. An MCS-like lock is specially tailored for optimistic spinning
+     for sleeping lock implementation. An important feature of the customized
+     MCS lock is that it has the extra property that spinners are able to exit
+     the MCS spinlock queue when they need to reschedule. This further helps
+     avoid situations where MCS spinners that need to reschedule would continue
+     waiting to spin on mutex owner, only to go directly to slowpath upon
+     obtaining the MCS lock.
+
+
+(iii) slowpath: last resort, if the lock is still unable to be acquired,
+      the task is added to the wait-queue and sleeps until woken up by the
+      unlock path. Under normal circumstances it blocks as TASK_UNINTERRUPTIBLE.
+
+While formally kernel mutexes are sleepable locks, it is path (ii) that
+makes them more practically a hybrid type. By simply not interrupting a
+task and busy-waiting for a few cycles instead of immediately sleeping,
+the performance of this lock has been seen to significantly improve a
+number of workloads. Note that this technique is also used for rw-semaphores.
+
+Semantics
+---------
+
+The mutex subsystem checks and enforces the following rules:
+
+    - Only one task can hold the mutex at a time.
+    - Only the owner can unlock the mutex.
+    - Multiple unlocks are not permitted.
+    - Recursive locking/unlocking is not permitted.
+    - A mutex must only be initialized via the API (see below).
+    - A task may not exit with a mutex held.
+    - Memory areas where held locks reside must not be freed.
+    - Held mutexes must not be reinitialized.
+    - Mutexes may not be used in hardware or software interrupt
+      contexts such as tasklets and timers.
+
+These semantics are fully enforced when CONFIG DEBUG_MUTEXES is enabled.
+In addition, the mutex debugging code also implements a number of other
+features that make lock debugging easier and faster:
+
+    - Uses symbolic names of mutexes, whenever they are printed
+      in debug output.
+    - Point-of-acquire tracking, symbolic lookup of function names,
+      list of all locks held in the system, printout of them.
+    - Owner tracking.
+    - Detects self-recursing locks and prints out all relevant info.
+    - Detects multi-task circular deadlocks and prints out all affected
+      locks and tasks (and only those tasks).
+
+
+Interfaces
+----------
+Statically define the mutex:
+   DEFINE_MUTEX(name);
+
+Dynamically initialize the mutex:
+   mutex_init(mutex);
+
+Acquire the mutex, uninterruptible:
+   void mutex_lock(struct mutex *lock);
+   void mutex_lock_nested(struct mutex *lock, unsigned int subclass);
+   int  mutex_trylock(struct mutex *lock);
+
+Acquire the mutex, interruptible:
+   int mutex_lock_interruptible_nested(struct mutex *lock,
+                                      unsigned int subclass);
+   int mutex_lock_interruptible(struct mutex *lock);
+
+Acquire the mutex, interruptible, if dec to 0:
+   int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock);
+
+Unlock the mutex:
+   void mutex_unlock(struct mutex *lock);
+
+Test if the mutex is taken:
+   int mutex_is_locked(struct mutex *lock);
 
 Disadvantages
 -------------
 
-The stricter mutex API means you cannot use mutexes the same way you
-can use semaphores: e.g. they cannot be used from an interrupt context,
-nor can they be unlocked from a different context that which acquired
-it. [ I'm not aware of any other (e.g. performance) disadvantages from
-using mutexes at the moment, please let me know if you find any. ]
-
-Implementation of mutexes
--------------------------
-
-'struct mutex' is the new mutex type, defined in include/linux/mutex.h and
-implemented in kernel/locking/mutex.c. It is a counter-based mutex with a
-spinlock and a wait-list. The counter has 3 states: 1 for "unlocked", 0 for
-"locked" and negative numbers (usually -1) for "locked, potential waiters
-queued".
-
-the APIs of 'struct mutex' have been streamlined:
-
- DEFINE_MUTEX(name);
+Unlike its original design and purpose, 'struct mutex' is larger than
+most locks in the kernel. E.g: on x86-64 it is 40 bytes, almost twice
+as large as 'struct semaphore' (24 bytes) and 8 bytes shy of the
+'struct rw_semaphore' variant. Larger structure sizes mean more CPU
+cache and memory footprint.
 
- mutex_init(mutex);
+When to use mutexes
+-------------------
 
- void mutex_lock(struct mutex *lock);
- int  mutex_lock_interruptible(struct mutex *lock);
- int  mutex_trylock(struct mutex *lock);
- void mutex_unlock(struct mutex *lock);
- int  mutex_is_locked(struct mutex *lock);
- void mutex_lock_nested(struct mutex *lock, unsigned int subclass);
- int  mutex_lock_interruptible_nested(struct mutex *lock,
-                                      unsigned int subclass);
- int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock);
+Unless the strict semantics of mutexes are unsuitable and/or the critical
+region prevents the lock from being shared, always prefer them to any other
+locking primitive.
index a383c00392d03f2e316f7fe7dd954c7d8b71f274..9c723ecd00251534a0b011c4e0ffbd053242454e 100644 (file)
@@ -585,13 +585,19 @@ mode
        balance-tlb or 5
 
                Adaptive transmit load balancing: channel bonding that
-               does not require any special switch support.  The
-               outgoing traffic is distributed according to the
-               current load (computed relative to the speed) on each
-               slave.  Incoming traffic is received by the current
-               slave.  If the receiving slave fails, another slave
-               takes over the MAC address of the failed receiving
-               slave.
+               does not require any special switch support.
+
+               In tlb_dynamic_lb=1 mode; the outgoing traffic is
+               distributed according to the current load (computed
+               relative to the speed) on each slave.
+
+               In tlb_dynamic_lb=0 mode; the load balancing based on
+               current load is disabled and the load is distributed
+               only using the hash distribution.
+
+               Incoming traffic is received by the current slave.
+               If the receiving slave fails, another slave takes over
+               the MAC address of the failed receiving slave.
 
                Prerequisite:
 
@@ -736,6 +742,28 @@ primary_reselect
 
        This option was added for bonding version 3.6.0.
 
+tlb_dynamic_lb
+
+       Specifies if dynamic shuffling of flows is enabled in tlb
+       mode. The value has no effect on any other modes.
+
+       The default behavior of tlb mode is to shuffle active flows across
+       slaves based on the load in that interval. This gives nice lb
+       characteristics but can cause packet reordering. If re-ordering is
+       a concern use this variable to disable flow shuffling and rely on
+       load balancing provided solely by the hash distribution.
+       xmit-hash-policy can be used to select the appropriate hashing for
+       the setup.
+
+       The sysfs entry can be used to change the setting per bond device
+       and the initial value is derived from the module parameter. The
+       sysfs entry is allowed to be changed only if the bond device is
+       down.
+
+       The default value is "1" that enables flow shuffling while value "0"
+       disables it. This option was added in bonding driver 3.7.1
+
+
 updelay
 
        Specifies the time, in milliseconds, to wait before enabling a
@@ -769,7 +797,7 @@ use_carrier
 xmit_hash_policy
 
        Selects the transmit hash policy to use for slave selection in
-       balance-xor and 802.3ad modes.  Possible values are:
+       balance-xor, 802.3ad, and tlb modes.  Possible values are:
 
        layer2
 
index 4f7ae5261364ac4a2c09b0063e900e5c05a0fda3..2236d6dcb7dadb0b77fa889046eeb8c55bc6e06d 100644 (file)
@@ -469,6 +469,41 @@ solution for a couple of reasons:
   having this 'send only' use-case we may remove the receive list in the
   Kernel to save a little (really a very little!) CPU usage.
 
+  4.1.1.1 CAN filter usage optimisation
+
+  The CAN filters are processed in per-device filter lists at CAN frame
+  reception time. To reduce the number of checks that need to be performed
+  while walking through the filter lists the CAN core provides an optimized
+  filter handling when the filter subscription focusses on a single CAN ID.
+
+  For the possible 2048 SFF CAN identifiers the identifier is used as an index
+  to access the corresponding subscription list without any further checks.
+  For the 2^29 possible EFF CAN identifiers a 10 bit XOR folding is used as
+  hash function to retrieve the EFF table index.
+
+  To benefit from the optimized filters for single CAN identifiers the
+  CAN_SFF_MASK or CAN_EFF_MASK have to be set into can_filter.mask together
+  with set CAN_EFF_FLAG and CAN_RTR_FLAG bits. A set CAN_EFF_FLAG bit in the
+  can_filter.mask makes clear that it matters whether a SFF or EFF CAN ID is
+  subscribed. E.g. in the example from above
+
+    rfilter[0].can_id   = 0x123;
+    rfilter[0].can_mask = CAN_SFF_MASK;
+
+  both SFF frames with CAN ID 0x123 and EFF frames with 0xXXXXX123 can pass.
+
+  To filter for only 0x123 (SFF) and 0x12345678 (EFF) CAN identifiers the
+  filter has to be defined in this way to benefit from the optimized filters:
+
+    struct can_filter rfilter[2];
+
+    rfilter[0].can_id   = 0x123;
+    rfilter[0].can_mask = (CAN_EFF_FLAG | CAN_RTR_FLAG | CAN_SFF_MASK);
+    rfilter[1].can_id   = 0x12345678 | CAN_EFF_FLAG;
+    rfilter[1].can_mask = (CAN_EFF_FLAG | CAN_RTR_FLAG | CAN_EFF_MASK);
+
+    setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));
+
   4.1.2 RAW socket option CAN_RAW_ERR_FILTER
 
   As described in chapter 3.4 the CAN interface driver can generate so
diff --git a/Documentation/networking/cdc_mbim.txt b/Documentation/networking/cdc_mbim.txt
new file mode 100644 (file)
index 0000000..a15ea60
--- /dev/null
@@ -0,0 +1,339 @@
+     cdc_mbim - Driver for CDC MBIM Mobile Broadband modems
+    ========================================================
+
+The cdc_mbim driver supports USB devices conforming to the "Universal
+Serial Bus Communications Class Subclass Specification for Mobile
+Broadband Interface Model" [1], which is a further development of
+"Universal Serial Bus Communications Class Subclass Specifications for
+Network Control Model Devices" [2] optimized for Mobile Broadband
+devices, aka "3G/LTE modems".
+
+
+Command Line Parameters
+=======================
+
+The cdc_mbim driver has no parameters of its own.  But the probing
+behaviour for NCM 1.0 backwards compatible MBIM functions (an
+"NCM/MBIM function" as defined in section 3.2 of [1]) is affected
+by a cdc_ncm driver parameter:
+
+prefer_mbim
+-----------
+Type:          Boolean
+Valid Range:   N/Y (0-1)
+Default Value: Y (MBIM is preferred)
+
+This parameter sets the system policy for NCM/MBIM functions.  Such
+functions will be handled by either the cdc_ncm driver or the cdc_mbim
+driver depending on the prefer_mbim setting.  Setting prefer_mbim=N
+makes the cdc_mbim driver ignore these functions and lets the cdc_ncm
+driver handle them instead.
+
+The parameter is writable, and can be changed at any time. A manual
+unbind/bind is required to make the change effective for NCM/MBIM
+functions bound to the "wrong" driver
+
+
+Basic usage
+===========
+
+MBIM functions are inactive when unmanaged. The cdc_mbim driver only
+provides an userspace interface to the MBIM control channel, and will
+not participate in the management of the function. This implies that a
+userspace MBIM management application always is required to enable a
+MBIM function.
+
+Such userspace applications includes, but are not limited to:
+ - mbimcli (included with the libmbim [3] library), and
+ - ModemManager [4]
+
+Establishing a MBIM IP session reequires at least these actions by the
+management application:
+ - open the control channel
+ - configure network connection settings
+ - connect to network
+ - configure IP interface
+
+Management application development
+----------------------------------
+The driver <-> userspace interfaces are described below.  The MBIM
+control channel protocol is described in [1].
+
+
+MBIM control channel userspace ABI
+==================================
+
+/dev/cdc-wdmX character device
+------------------------------
+The driver creates a two-way pipe to the MBIM function control channel
+using the cdc-wdm driver as a subdriver.  The userspace end of the
+control channel pipe is a /dev/cdc-wdmX character device.
+
+The cdc_mbim driver does not process or police messages on the control
+channel.  The channel is fully delegated to the userspace management
+application.  It is therefore up to this application to ensure that it
+complies with all the control channel requirements in [1].
+
+The cdc-wdmX device is created as a child of the MBIM control
+interface USB device.  The character device associated with a specific
+MBIM function can be looked up using sysfs.  For example:
+
+ bjorn@nemi:~$ ls /sys/bus/usb/drivers/cdc_mbim/2-4:2.12/usbmisc
+ cdc-wdm0
+
+ bjorn@nemi:~$ grep . /sys/bus/usb/drivers/cdc_mbim/2-4:2.12/usbmisc/cdc-wdm0/dev
+ 180:0
+
+
+USB configuration descriptors
+-----------------------------
+The wMaxControlMessage field of the CDC MBIM functional descriptor
+limits the maximum control message size. The managament application is
+responsible for negotiating a control message size complying with the
+requirements in section 9.3.1 of [1], taking this descriptor field
+into consideration.
+
+The userspace application can access the CDC MBIM functional
+descriptor of a MBIM function using either of the two USB
+configuration descriptor kernel interfaces described in [6] or [7].
+
+See also the ioctl documentation below.
+
+
+Fragmentation
+-------------
+The userspace application is responsible for all control message
+fragmentation and defragmentaion, as described in section 9.5 of [1].
+
+
+/dev/cdc-wdmX write()
+---------------------
+The MBIM control messages from the management application *must not*
+exceed the negotiated control message size.
+
+
+/dev/cdc-wdmX read()
+--------------------
+The management application *must* accept control messages of up the
+negotiated control message size.
+
+
+/dev/cdc-wdmX ioctl()
+--------------------
+IOCTL_WDM_MAX_COMMAND: Get Maximum Command Size
+This ioctl returns the wMaxControlMessage field of the CDC MBIM
+functional descriptor for MBIM devices.  This is intended as a
+convenience, eliminating the need to parse the USB descriptors from
+userspace.
+
+       #include <stdio.h>
+       #include <fcntl.h>
+       #include <sys/ioctl.h>
+       #include <linux/types.h>
+       #include <linux/usb/cdc-wdm.h>
+       int main()
+       {
+               __u16 max;
+               int fd = open("/dev/cdc-wdm0", O_RDWR);
+               if (!ioctl(fd, IOCTL_WDM_MAX_COMMAND, &max))
+                       printf("wMaxControlMessage is %d\n", max);
+       }
+
+
+Custom device services
+----------------------
+The MBIM specification allows vendors to freely define additional
+services.  This is fully supported by the cdc_mbim driver.
+
+Support for new MBIM services, including vendor specified services, is
+implemented entirely in userspace, like the rest of the MBIM control
+protocol
+
+New services should be registered in the MBIM Registry [5].
+
+
+
+MBIM data channel userspace ABI
+===============================
+
+wwanY network device
+--------------------
+The cdc_mbim driver represents the MBIM data channel as a single
+network device of the "wwan" type. This network device is initially
+mapped to MBIM IP session 0.
+
+
+Multiplexed IP sessions (IPS)
+-----------------------------
+MBIM allows multiplexing up to 256 IP sessions over a single USB data
+channel.  The cdc_mbim driver models such IP sessions as 802.1q VLAN
+subdevices of the master wwanY device, mapping MBIM IP session Z to
+VLAN ID Z for all values of Z greater than 0.
+
+The device maximum Z is given in the MBIM_DEVICE_CAPS_INFO structure
+described in section 10.5.1 of [1].
+
+The userspace management application is responsible for adding new
+VLAN links prior to establishing MBIM IP sessions where the SessionId
+is greater than 0. These links can be added by using the normal VLAN
+kernel interfaces, either ioctl or netlink.
+
+For example, adding a link for a MBIM IP session with SessionId 3:
+
+  ip link add link wwan0 name wwan0.3 type vlan id 3
+
+The driver will automatically map the "wwan0.3" network device to MBIM
+IP session 3.
+
+
+Device Service Streams (DSS)
+----------------------------
+MBIM also allows up to 256 non-IP data streams to be multiplexed over
+the same shared USB data channel.  The cdc_mbim driver models these
+sessions as another set of 802.1q VLAN subdevices of the master wwanY
+device, mapping MBIM DSS session A to VLAN ID (256 + A) for all values
+of A.
+
+The device maximum A is given in the MBIM_DEVICE_SERVICES_INFO
+structure described in section 10.5.29 of [1].
+
+The DSS VLAN subdevices are used as a practical interface between the
+shared MBIM data channel and a MBIM DSS aware userspace application.
+It is not intended to be presented as-is to an end user. The
+assumption is that an userspace application initiating a DSS session
+also takes care of the necessary framing of the DSS data, presenting
+the stream to the end user in an appropriate way for the stream type.
+
+The network device ABI requires a dummy ethernet header for every DSS
+data frame being transported.  The contents of this header is
+arbitrary, with the following exceptions:
+ - TX frames using an IP protocol (0x0800 or 0x86dd) will be dropped
+ - RX frames will have the protocol field set to ETH_P_802_3 (but will
+   not be properly formatted 802.3 frames)
+ - RX frames will have the destination address set to the hardware
+   address of the master device
+
+The DSS supporting userspace management application is responsible for
+adding the dummy ethernet header on TX and stripping it on RX.
+
+This is a simple example using tools commonly available, exporting
+DssSessionId 5 as a pty character device pointed to by a /dev/nmea
+symlink:
+
+  ip link add link wwan0 name wwan0.dss5 type vlan id 261
+  ip link set dev wwan0.dss5 up
+  socat INTERFACE:wwan0.dss5,type=2 PTY:,echo=0,link=/dev/nmea
+
+This is only an example, most suitable for testing out a DSS
+service. Userspace applications supporting specific MBIM DSS services
+are expected to use the tools and programming interfaces required by
+that service.
+
+Note that adding VLAN links for DSS sessions is entirely optional.  A
+management application may instead choose to bind a packet socket
+directly to the master network device, using the received VLAN tags to
+map frames to the correct DSS session and adding 18 byte VLAN ethernet
+headers with the appropriate tag on TX.  In this case using a socket
+filter is recommended, matching only the DSS VLAN subset. This avoid
+unnecessary copying of unrelated IP session data to userspace.  For
+example:
+
+  static struct sock_filter dssfilter[] = {
+       /* use special negative offsets to get VLAN tag */
+       BPF_STMT(BPF_LD|BPF_B|BPF_ABS, SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT),
+       BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 1, 0, 6), /* true */
+
+       /* verify DSS VLAN range */
+       BPF_STMT(BPF_LD|BPF_H|BPF_ABS, SKF_AD_OFF + SKF_AD_VLAN_TAG),
+       BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 256, 0, 4),     /* 256 is first DSS VLAN */
+       BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 512, 3, 0),     /* 511 is last DSS VLAN */
+
+       /* verify ethertype */
+        BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 2 * ETH_ALEN),
+        BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ETH_P_802_3, 0, 1),
+
+        BPF_STMT(BPF_RET|BPF_K, (u_int)-1),    /* accept */
+        BPF_STMT(BPF_RET|BPF_K, 0),            /* ignore */
+  };
+
+
+
+Tagged IP session 0 VLAN
+------------------------
+As described above, MBIM IP session 0 is treated as special by the
+driver.  It is initially mapped to untagged frames on the wwanY
+network device.
+
+This mapping implies a few restrictions on multiplexed IPS and DSS
+sessions, which may not always be practical:
+ - no IPS or DSS session can use a frame size greater than the MTU on
+   IP session 0
+ - no IPS or DSS session can be in the up state unless the network
+   device representing IP session 0 also is up
+
+These problems can be avoided by optionally making the driver map IP
+session 0 to a VLAN subdevice, similar to all other IP sessions.  This
+behaviour is triggered by adding a VLAN link for the magic VLAN ID
+4094.  The driver will then immediately start mapping MBIM IP session
+0 to this VLAN, and will drop untagged frames on the master wwanY
+device.
+
+Tip: It might be less confusing to the end user to name this VLAN
+subdevice after the MBIM SessionID instead of the VLAN ID.  For
+example:
+
+  ip link add link wwan0 name wwan0.0 type vlan id 4094
+
+
+VLAN mapping
+------------
+
+Summarizing the cdc_mbim driver mapping described above, we have this
+relationship between VLAN tags on the wwanY network device and MBIM
+sessions on the shared USB data channel:
+
+  VLAN ID       MBIM type   MBIM SessionID           Notes
+  ---------------------------------------------------------
+  untagged      IPS         0                        a)
+  1 - 255       IPS         1 - 255 <VLANID>
+  256 - 511     DSS         0 - 255 <VLANID - 256>
+  512 - 4093                                         b)
+  4094          IPS         0                        c)
+
+    a) if no VLAN ID 4094 link exists, else dropped
+    b) unsupported VLAN range, unconditionally dropped
+    c) if a VLAN ID 4094 link exists, else dropped
+
+
+
+
+References
+==========
+
+[1] USB Implementers Forum, Inc. - "Universal Serial Bus
+      Communications Class Subclass Specification for Mobile Broadband
+      Interface Model", Revision 1.0 (Errata 1), May 1, 2013
+      - http://www.usb.org/developers/docs/devclass_docs/
+
+[2] USB Implementers Forum, Inc. - "Universal Serial Bus
+      Communications Class Subclass Specifications for Network Control
+      Model Devices", Revision 1.0 (Errata 1), November 24, 2010
+      - http://www.usb.org/developers/docs/devclass_docs/
+
+[3] libmbim - "a glib-based library for talking to WWAN modems and
+      devices which speak the Mobile Interface Broadband Model (MBIM)
+      protocol"
+      - http://www.freedesktop.org/wiki/Software/libmbim/
+
+[4] ModemManager - "a DBus-activated daemon which controls mobile
+      broadband (2G/3G/4G) devices and connections"
+      - http://www.freedesktop.org/wiki/Software/ModemManager/
+
+[5] "MBIM (Mobile Broadband Interface Model) Registry"
+       - http://compliance.usb.org/mbim/
+
+[6] "/proc/bus/usb filesystem output"
+       - Documentation/usb/proc_usb_info.txt
+
+[7] "/sys/bus/usb/devices/.../descriptors"
+       - Documentation/ABI/stable/sysfs-bus-usb
index e3ba753cb714949c4e26f898da31b6d0e6b03737..ee78eba78a9dd2ba5a25ace8f597c8c8f53c9de5 100644 (file)
@@ -281,6 +281,7 @@ Possible BPF extensions are shown in the following table:
   cpu                                   raw_smp_processor_id()
   vlan_tci                              vlan_tx_tag_get(skb)
   vlan_pr                               vlan_tx_tag_present(skb)
+  rand                                  prandom_u32()
 
 These extensions can also be prefixed with '#'.
 Examples for low-level BPF:
@@ -308,6 +309,18 @@ Examples for low-level BPF:
   ret #-1
   drop: ret #0
 
+** icmp random packet sampling, 1 in 4
+  ldh [12]
+  jne #0x800, drop
+  ldb [23]
+  jneq #1, drop
+  # get a random uint32 number
+  ld rand
+  mod #4
+  jneq #1, drop
+  ret #-1
+  drop: ret #0
+
 ** SECCOMP filter example:
 
   ld [4]                  /* offsetof(struct seccomp_data, arch) */
@@ -548,42 +561,43 @@ toolchain for developing and testing the kernel's JIT compiler.
 
 BPF kernel internals
 --------------------
-Internally, for the kernel interpreter, a different BPF instruction set
+Internally, for the kernel interpreter, a different instruction set
 format with similar underlying principles from BPF described in previous
 paragraphs is being used. However, the instruction set format is modelled
 closer to the underlying architecture to mimic native instruction sets, so
-that a better performance can be achieved (more details later).
+that a better performance can be achieved (more details later). This new
+ISA is called 'eBPF' or 'internal BPF' interchangeably. (Note: eBPF which
+originates from [e]xtended BPF is not the same as BPF extensions! While
+eBPF is an ISA, BPF extensions date back to classic BPF's 'overloading'
+of BPF_LD | BPF_{B,H,W} | BPF_ABS instruction.)
 
 It is designed to be JITed with one to one mapping, which can also open up
-the possibility for GCC/LLVM compilers to generate optimized BPF code through
-a BPF backend that performs almost as fast as natively compiled code.
+the possibility for GCC/LLVM compilers to generate optimized eBPF code through
+an eBPF backend that performs almost as fast as natively compiled code.
 
 The new instruction set was originally designed with the possible goal in
-mind to write programs in "restricted C" and compile into BPF with a optional
+mind to write programs in "restricted C" and compile into eBPF with a optional
 GCC/LLVM backend, so that it can just-in-time map to modern 64-bit CPUs with
-minimal performance overhead over two steps, that is, C -> BPF -> native code.
+minimal performance overhead over two steps, that is, C -> eBPF -> native code.
 
 Currently, the new format is being used for running user BPF programs, which
 includes seccomp BPF, classic socket filters, cls_bpf traffic classifier,
 team driver's classifier for its load-balancing mode, netfilter's xt_bpf
 extension, PTP dissector/classifier, and much more. They are all internally
 converted by the kernel into the new instruction set representation and run
-in the extended interpreter. For in-kernel handlers, this all works
-transparently by using sk_unattached_filter_create() for setting up the
-filter, resp. sk_unattached_filter_destroy() for destroying it. The macro
-SK_RUN_FILTER(filter, ctx) transparently invokes the right BPF function to
-run the filter. 'filter' is a pointer to struct sk_filter that we got from
-sk_unattached_filter_create(), and 'ctx' the given context (e.g. skb pointer).
-All constraints and restrictions from sk_chk_filter() apply before a
-conversion to the new layout is being done behind the scenes!
-
-Currently, for JITing, the user BPF format is being used and current BPF JIT
-compilers reused whenever possible. In other words, we do not (yet!) perform
-a JIT compilation in the new layout, however, future work will successively
-migrate traditional JIT compilers into the new instruction format as well, so
-that they will profit from the very same benefits. Thus, when speaking about
-JIT in the following, a JIT compiler (TBD) for the new instruction format is
-meant in this context.
+in the eBPF interpreter. For in-kernel handlers, this all works transparently
+by using sk_unattached_filter_create() for setting up the filter, resp.
+sk_unattached_filter_destroy() for destroying it. The macro
+SK_RUN_FILTER(filter, ctx) transparently invokes eBPF interpreter or JITed
+code to run the filter. 'filter' is a pointer to struct sk_filter that we
+got from sk_unattached_filter_create(), and 'ctx' the given context (e.g.
+skb pointer). All constraints and restrictions from sk_chk_filter() apply
+before a conversion to the new layout is being done behind the scenes!
+
+Currently, the classic BPF format is being used for JITing on most of the
+architectures. Only x86-64 performs JIT compilation from eBPF instruction set,
+however, future work will migrate other JIT compilers as well, so that they
+will profit from the very same benefits.
 
 Some core changes of the new internal format:
 
@@ -592,35 +606,35 @@ Some core changes of the new internal format:
   The old format had two registers A and X, and a hidden frame pointer. The
   new layout extends this to be 10 internal registers and a read-only frame
   pointer. Since 64-bit CPUs are passing arguments to functions via registers
-  the number of args from BPF program to in-kernel function is restricted
+  the number of args from eBPF program to in-kernel function is restricted
   to 5 and one register is used to accept return value from an in-kernel
   function. Natively, x86_64 passes first 6 arguments in registers, aarch64/
   sparcv9/mips64 have 7 - 8 registers for arguments; x86_64 has 6 callee saved
   registers, and aarch64/sparcv9/mips64 have 11 or more callee saved registers.
 
-  Therefore, BPF calling convention is defined as:
+  Therefore, eBPF calling convention is defined as:
 
-    * R0       - return value from in-kernel function
-    * R1 - R5  - arguments from BPF program to in-kernel function
+    * R0       - return value from in-kernel function, and exit value for eBPF program
+    * R1 - R5  - arguments from eBPF program to in-kernel function
     * R6 - R9  - callee saved registers that in-kernel function will preserve
     * R10      - read-only frame pointer to access stack
 
-  Thus, all BPF registers map one to one to HW registers on x86_64, aarch64,
-  etc, and BPF calling convention maps directly to ABIs used by the kernel on
+  Thus, all eBPF registers map one to one to HW registers on x86_64, aarch64,
+  etc, and eBPF calling convention maps directly to ABIs used by the kernel on
   64-bit architectures.
 
   On 32-bit architectures JIT may map programs that use only 32-bit arithmetic
   and may let more complex programs to be interpreted.
 
-  R0 - R5 are scratch registers and BPF program needs spill/fill them if
-  necessary across calls. Note that there is only one BPF program (== one BPF
-  main routine) and it cannot call other BPF functions, it can only call
-  predefined in-kernel functions, though.
+  R0 - R5 are scratch registers and eBPF program needs spill/fill them if
+  necessary across calls. Note that there is only one eBPF program (== one
+  eBPF main routine) and it cannot call other eBPF functions, it can only
+  call predefined in-kernel functions, though.
 
 - Register width increases from 32-bit to 64-bit:
 
   Still, the semantics of the original 32-bit ALU operations are preserved
-  via 32-bit subregisters. All BPF registers are 64-bit with 32-bit lower
+  via 32-bit subregisters. All eBPF registers are 64-bit with 32-bit lower
   subregisters that zero-extend into 64-bit if they are being written to.
   That behavior maps directly to x86_64 and arm64 subregister definition, but
   makes other JITs more difficult.
@@ -631,8 +645,8 @@ Some core changes of the new internal format:
 
   Operation is 64-bit, because on 64-bit architectures, pointers are also
   64-bit wide, and we want to pass 64-bit values in/out of kernel functions,
-  so 32-bit BPF registers would otherwise require to define register-pair
-  ABI, thus, there won't be able to use a direct BPF register to HW register
+  so 32-bit eBPF registers would otherwise require to define register-pair
+  ABI, thus, there won't be able to use a direct eBPF register to HW register
   mapping and JIT would need to do combine/split/move operations for every
   register in and out of the function, which is complex, bug prone and slow.
   Another reason is the use of atomic 64-bit counters.
@@ -646,14 +660,145 @@ Some core changes of the new internal format:
 - Introduces bpf_call insn and register passing convention for zero overhead
   calls from/to other kernel functions:
 
-  After a kernel function call, R1 - R5 are reset to unreadable and R0 has a
-  return type of the function. Since R6 - R9 are callee saved, their state is
-  preserved across the call.
-
-Also in the new design, BPF is limited to 4096 insns, which means that any
+  Before an in-kernel function call, the internal BPF program needs to
+  place function arguments into R1 to R5 registers to satisfy calling
+  convention, then the interpreter will take them from registers and pass
+  to in-kernel function. If R1 - R5 registers are mapped to CPU registers
+  that are used for argument passing on given architecture, the JIT compiler
+  doesn't need to emit extra moves. Function arguments will be in the correct
+  registers and BPF_CALL instruction will be JITed as single 'call' HW
+  instruction. This calling convention was picked to cover common call
+  situations without performance penalty.
+
+  After an in-kernel function call, R1 - R5 are reset to unreadable and R0 has
+  a return value of the function. Since R6 - R9 are callee saved, their state
+  is preserved across the call.
+
+  For example, consider three C functions:
+
+  u64 f1() { return (*_f2)(1); }
+  u64 f2(u64 a) { return f3(a + 1, a); }
+  u64 f3(u64 a, u64 b) { return a - b; }
+
+  GCC can compile f1, f3 into x86_64:
+
+  f1:
+    movl $1, %edi
+    movq _f2(%rip), %rax
+    jmp  *%rax
+  f3:
+    movq %rdi, %rax
+    subq %rsi, %rax
+    ret
+
+  Function f2 in eBPF may look like:
+
+  f2:
+    bpf_mov R2, R1
+    bpf_add R1, 1
+    bpf_call f3
+    bpf_exit
+
+  If f2 is JITed and the pointer stored to '_f2'. The calls f1 -> f2 -> f3 and
+  returns will be seamless. Without JIT, __sk_run_filter() interpreter needs to
+  be used to call into f2.
+
+  For practical reasons all eBPF programs have only one argument 'ctx' which is
+  already placed into R1 (e.g. on __sk_run_filter() startup) and the programs
+  can call kernel functions with up to 5 arguments. Calls with 6 or more arguments
+  are currently not supported, but these restrictions can be lifted if necessary
+  in the future.
+
+  On 64-bit architectures all register map to HW registers one to one. For
+  example, x86_64 JIT compiler can map them as ...
+
+    R0 - rax
+    R1 - rdi
+    R2 - rsi
+    R3 - rdx
+    R4 - rcx
+    R5 - r8
+    R6 - rbx
+    R7 - r13
+    R8 - r14
+    R9 - r15
+    R10 - rbp
+
+  ... since x86_64 ABI mandates rdi, rsi, rdx, rcx, r8, r9 for argument passing
+  and rbx, r12 - r15 are callee saved.
+
+  Then the following internal BPF pseudo-program:
+
+    bpf_mov R6, R1 /* save ctx */
+    bpf_mov R2, 2
+    bpf_mov R3, 3
+    bpf_mov R4, 4
+    bpf_mov R5, 5
+    bpf_call foo
+    bpf_mov R7, R0 /* save foo() return value */
+    bpf_mov R1, R6 /* restore ctx for next call */
+    bpf_mov R2, 6
+    bpf_mov R3, 7
+    bpf_mov R4, 8
+    bpf_mov R5, 9
+    bpf_call bar
+    bpf_add R0, R7
+    bpf_exit
+
+  After JIT to x86_64 may look like:
+
+    push %rbp
+    mov %rsp,%rbp
+    sub $0x228,%rsp
+    mov %rbx,-0x228(%rbp)
+    mov %r13,-0x220(%rbp)
+    mov %rdi,%rbx
+    mov $0x2,%esi
+    mov $0x3,%edx
+    mov $0x4,%ecx
+    mov $0x5,%r8d
+    callq foo
+    mov %rax,%r13
+    mov %rbx,%rdi
+    mov $0x2,%esi
+    mov $0x3,%edx
+    mov $0x4,%ecx
+    mov $0x5,%r8d
+    callq bar
+    add %r13,%rax
+    mov -0x228(%rbp),%rbx
+    mov -0x220(%rbp),%r13
+    leaveq
+    retq
+
+  Which is in this example equivalent in C to:
+
+    u64 bpf_filter(u64 ctx)
+    {
+        return foo(ctx, 2, 3, 4, 5) + bar(ctx, 6, 7, 8, 9);
+    }
+
+  In-kernel functions foo() and bar() with prototype: u64 (*)(u64 arg1, u64
+  arg2, u64 arg3, u64 arg4, u64 arg5); will receive arguments in proper
+  registers and place their return value into '%rax' which is R0 in eBPF.
+  Prologue and epilogue are emitted by JIT and are implicit in the
+  interpreter. R0-R5 are scratch registers, so eBPF program needs to preserve
+  them across the calls as defined by calling convention.
+
+  For example the following program is invalid:
+
+    bpf_mov R1, 1
+    bpf_call foo
+    bpf_mov R0, R1
+    bpf_exit
+
+  After the call the registers R1-R5 contain junk values and cannot be read.
+  In the future an eBPF verifier can be used to validate internal BPF programs.
+
+Also in the new design, eBPF is limited to 4096 insns, which means that any
 program will terminate quickly and will only call a fixed number of kernel
 functions. Original BPF and the new format are two operand instructions,
-which helps to do one-to-one mapping between BPF insn and x86 insn during JIT.
+which helps to do one-to-one mapping between eBPF insn and x86 insn during JIT.
 
 The input context pointer for invoking the interpreter function is generic,
 its content is defined by a specific use case. For seccomp register R1 points
@@ -661,7 +806,26 @@ to seccomp_data, for converted BPF filters R1 points to a skb.
 
 A program, that is translated internally consists of the following elements:
 
-  op:16, jt:8, jf:8, k:32    ==>    op:8, a_reg:4, x_reg:4, off:16, imm:32
+  op:16, jt:8, jf:8, k:32    ==>    op:8, dst_reg:4, src_reg:4, off:16, imm:32
+
+So far 87 internal BPF instructions were implemented. 8-bit 'op' opcode field
+has room for new instructions. Some of them may use 16/24/32 byte encoding. New
+instructions must be multiple of 8 bytes to preserve backward compatibility.
+
+Internal BPF is a general purpose RISC instruction set. Not every register and
+every instruction are used during translation from original BPF to new format.
+For example, socket filters are not using 'exclusive add' instruction, but
+tracing filters may do to maintain counters of events, for example. Register R9
+is not used by socket filters either, but more complex filters may be running
+out of registers and would have to resort to spill/fill to stack.
+
+Internal BPF can used as generic assembler for last step performance
+optimizations, socket filters and seccomp are using it as assembler. Tracing
+filters may use it as assembler to generate code from kernel. In kernel usage
+may not be bounded by security considerations, since generated internal BPF code
+may be optimizing internal code path and not being exposed to the user space.
+Safety of internal BPF can come from a verifier (TBD). In such use cases as
+described, it may be used as safe instruction set.
 
 Just like the original BPF, the new format runs within a controlled environment,
 is deterministic and the kernel can easily prove that. The safety of the program
@@ -670,6 +834,181 @@ loops and other CFG validation; second step starts from the first insn and
 descends all possible paths. It simulates execution of every insn and observes
 the state change of registers and stack.
 
+eBPF opcode encoding
+--------------------
+
+eBPF is reusing most of the opcode encoding from classic to simplify conversion
+of classic BPF to eBPF. For arithmetic and jump instructions the 8-bit 'code'
+field is divided into three parts:
+
+  +----------------+--------+--------------------+
+  |   4 bits       |  1 bit |   3 bits           |
+  | operation code | source | instruction class  |
+  +----------------+--------+--------------------+
+  (MSB)                                      (LSB)
+
+Three LSB bits store instruction class which is one of:
+
+  Classic BPF classes:    eBPF classes:
+
+  BPF_LD    0x00          BPF_LD    0x00
+  BPF_LDX   0x01          BPF_LDX   0x01
+  BPF_ST    0x02          BPF_ST    0x02
+  BPF_STX   0x03          BPF_STX   0x03
+  BPF_ALU   0x04          BPF_ALU   0x04
+  BPF_JMP   0x05          BPF_JMP   0x05
+  BPF_RET   0x06          [ class 6 unused, for future if needed ]
+  BPF_MISC  0x07          BPF_ALU64 0x07
+
+When BPF_CLASS(code) == BPF_ALU or BPF_JMP, 4th bit encodes source operand ...
+
+  BPF_K     0x00
+  BPF_X     0x08
+
+ * in classic BPF, this means:
+
+  BPF_SRC(code) == BPF_X - use register X as source operand
+  BPF_SRC(code) == BPF_K - use 32-bit immediate as source operand
+
+ * in eBPF, this means:
+
+  BPF_SRC(code) == BPF_X - use 'src_reg' register as source operand
+  BPF_SRC(code) == BPF_K - use 32-bit immediate as source operand
+
+... and four MSB bits store operation code.
+
+If BPF_CLASS(code) == BPF_ALU or BPF_ALU64 [ in eBPF ], BPF_OP(code) is one of:
+
+  BPF_ADD   0x00
+  BPF_SUB   0x10
+  BPF_MUL   0x20
+  BPF_DIV   0x30
+  BPF_OR    0x40
+  BPF_AND   0x50
+  BPF_LSH   0x60
+  BPF_RSH   0x70
+  BPF_NEG   0x80
+  BPF_MOD   0x90
+  BPF_XOR   0xa0
+  BPF_MOV   0xb0  /* eBPF only: mov reg to reg */
+  BPF_ARSH  0xc0  /* eBPF only: sign extending shift right */
+  BPF_END   0xd0  /* eBPF only: endianness conversion */
+
+If BPF_CLASS(code) == BPF_JMP, BPF_OP(code) is one of:
+
+  BPF_JA    0x00
+  BPF_JEQ   0x10
+  BPF_JGT   0x20
+  BPF_JGE   0x30
+  BPF_JSET  0x40
+  BPF_JNE   0x50  /* eBPF only: jump != */
+  BPF_JSGT  0x60  /* eBPF only: signed '>' */
+  BPF_JSGE  0x70  /* eBPF only: signed '>=' */
+  BPF_CALL  0x80  /* eBPF only: function call */
+  BPF_EXIT  0x90  /* eBPF only: function return */
+
+So BPF_ADD | BPF_X | BPF_ALU means 32-bit addition in both classic BPF
+and eBPF. There are only two registers in classic BPF, so it means A += X.
+In eBPF it means dst_reg = (u32) dst_reg + (u32) src_reg; similarly,
+BPF_XOR | BPF_K | BPF_ALU means A ^= imm32 in classic BPF and analogous
+src_reg = (u32) src_reg ^ (u32) imm32 in eBPF.
+
+Classic BPF is using BPF_MISC class to represent A = X and X = A moves.
+eBPF is using BPF_MOV | BPF_X | BPF_ALU code instead. Since there are no
+BPF_MISC operations in eBPF, the class 7 is used as BPF_ALU64 to mean
+exactly the same operations as BPF_ALU, but with 64-bit wide operands
+instead. So BPF_ADD | BPF_X | BPF_ALU64 means 64-bit addition, i.e.:
+dst_reg = dst_reg + src_reg
+
+Classic BPF wastes the whole BPF_RET class to represent a single 'ret'
+operation. Classic BPF_RET | BPF_K means copy imm32 into return register
+and perform function exit. eBPF is modeled to match CPU, so BPF_JMP | BPF_EXIT
+in eBPF means function exit only. The eBPF program needs to store return
+value into register R0 before doing a BPF_EXIT. Class 6 in eBPF is currently
+unused and reserved for future use.
+
+For load and store instructions the 8-bit 'code' field is divided as:
+
+  +--------+--------+-------------------+
+  | 3 bits | 2 bits |   3 bits          |
+  |  mode  |  size  | instruction class |
+  +--------+--------+-------------------+
+  (MSB)                             (LSB)
+
+Size modifier is one of ...
+
+  BPF_W   0x00    /* word */
+  BPF_H   0x08    /* half word */
+  BPF_B   0x10    /* byte */
+  BPF_DW  0x18    /* eBPF only, double word */
+
+... which encodes size of load/store operation:
+
+ B  - 1 byte
+ H  - 2 byte
+ W  - 4 byte
+ DW - 8 byte (eBPF only)
+
+Mode modifier is one of:
+
+  BPF_IMM  0x00  /* classic BPF only, reserved in eBPF */
+  BPF_ABS  0x20
+  BPF_IND  0x40
+  BPF_MEM  0x60
+  BPF_LEN  0x80  /* classic BPF only, reserved in eBPF */
+  BPF_MSH  0xa0  /* classic BPF only, reserved in eBPF */
+  BPF_XADD 0xc0  /* eBPF only, exclusive add */
+
+eBPF has two non-generic instructions: (BPF_ABS | <size> | BPF_LD) and
+(BPF_IND | <size> | BPF_LD) which are used to access packet data.
+
+They had to be carried over from classic to have strong performance of
+socket filters running in eBPF interpreter. These instructions can only
+be used when interpreter context is a pointer to 'struct sk_buff' and
+have seven implicit operands. Register R6 is an implicit input that must
+contain pointer to sk_buff. Register R0 is an implicit output which contains
+the data fetched from the packet. Registers R1-R5 are scratch registers
+and must not be used to store the data across BPF_ABS | BPF_LD or
+BPF_IND | BPF_LD instructions.
+
+These instructions have implicit program exit condition as well. When
+eBPF program is trying to access the data beyond the packet boundary,
+the interpreter will abort the execution of the program. JIT compilers
+therefore must preserve this property. src_reg and imm32 fields are
+explicit inputs to these instructions.
+
+For example:
+
+  BPF_IND | BPF_W | BPF_LD means:
+
+    R0 = ntohl(*(u32 *) (((struct sk_buff *) R6)->data + src_reg + imm32))
+    and R1 - R5 were scratched.
+
+Unlike classic BPF instruction set, eBPF has generic load/store operations:
+
+BPF_MEM | <size> | BPF_STX:  *(size *) (dst_reg + off) = src_reg
+BPF_MEM | <size> | BPF_ST:   *(size *) (dst_reg + off) = imm32
+BPF_MEM | <size> | BPF_LDX:  dst_reg = *(size *) (src_reg + off)
+BPF_XADD | BPF_W  | BPF_STX: lock xadd *(u32 *)(dst_reg + off16) += src_reg
+BPF_XADD | BPF_DW | BPF_STX: lock xadd *(u64 *)(dst_reg + off16) += src_reg
+
+Where size is one of: BPF_B or BPF_H or BPF_W or BPF_DW. Note that 1 and
+2 byte atomic increments are not supported.
+
+Testing
+-------
+
+Next to the BPF toolchain, the kernel also ships a test module that contains
+various test cases for classic and internal BPF that can be executed against
+the BPF interpreter and JIT compiler. It can be found in lib/test_bpf.c and
+enabled via Kconfig:
+
+  CONFIG_TEST_BPF=m
+
+After the module has been built and installed, the test suite can be executed
+via insmod or modprobe against 'test_bpf' module. Results of the test cases
+including timings in nsec can be found in the kernel log (dmesg).
+
 Misc
 ----
 
index c2297fab77c87996cfaab422471a8ff56a8e1cd3..055f95238d8823a70e18fc33e3c0a407a5a5d123 100644 (file)
@@ -604,6 +604,13 @@ L: amd64-microcode@amd64.org
 S:     Maintained
 F:     arch/x86/kernel/microcode_amd.c
 
+AMD XGBE DRIVER
+M:     Tom Lendacky <thomas.lendacky@amd.com>
+L:     netdev@vger.kernel.org
+S:     Supported
+F:     drivers/net/ethernet/amd/xgbe/
+F:     drivers/net/phy/amd-xgbe-phy.c
+
 AMS (Apple Motion Sensor) DRIVER
 M:     Michael Hanselmann <linux-kernel@hansmi.ch>
 S:     Supported
@@ -1894,7 +1901,7 @@ F:        drivers/net/ethernet/broadcom/bnx2.*
 F:     drivers/net/ethernet/broadcom/bnx2_*
 
 BROADCOM BNX2X 10 GIGABIT ETHERNET DRIVER
-M:     Ariel Elior <ariele@broadcom.com>
+M:     Ariel Elior <ariel.elior@qlogic.com>
 L:     netdev@vger.kernel.org
 S:     Supported
 F:     drivers/net/ethernet/broadcom/bnx2x/
@@ -1974,6 +1981,12 @@ S:       Maintained
 F:     drivers/bcma/
 F:     include/linux/bcma/
 
+BROADCOM SYSTEMPORT ETHERNET DRIVER
+M:     Florian Fainelli <f.fainelli@gmail.com>
+L:     netdev@vger.kernel.org
+S:     Supported
+F:     drivers/net/ethernet/broadcom/bcmsysport.*
+
 BROCADE BFA FC SCSI DRIVER
 M:     Anil Gurumurthy <anil.gurumurthy@qlogic.com>
 M:     Sudarsana Kalluru <sudarsana.kalluru@qlogic.com>
@@ -2230,9 +2243,8 @@ F:        drivers/platform/chrome/
 CISCO VIC ETHERNET NIC DRIVER
 M:     Christian Benvenuti <benve@cisco.com>
 M:     Sujith Sankar <ssujith@cisco.com>
-M:     Govindarajulu Varadarajan <govindarajulu90@gmail.com>
+M:     Govindarajulu Varadarajan <_govind@gmx.com>
 M:     Neel Patel <neepatel@cisco.com>
-M:     Nishank Trivedi <nistrive@cisco.com>
 S:     Supported
 F:     drivers/net/ethernet/cisco/enic/
 
@@ -6168,6 +6180,7 @@ F:        include/uapi/linux/netdevice.h
 F:     tools/net/
 F:     tools/testing/selftests/net/
 F:     lib/random32.c
+F:     lib/test_bpf.c
 
 NETWORKING [IPv4/IPv6]
 M:     "David S. Miller" <davem@davemloft.net>
index c761fb1abfb6e283ff1e08fb1f039f084f7648cd..7680d7c707301b7a30368c66e3116ef42fd026b1 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -105,10 +105,6 @@ ifeq ("$(origin O)", "command line")
   KBUILD_OUTPUT := $(O)
 endif
 
-ifeq ("$(origin W)", "command line")
-  export KBUILD_ENABLE_EXTRA_GCC_CHECKS := $(W)
-endif
-
 # That's our default target when none is given on the command line
 PHONY := _all
 _all:
@@ -153,8 +149,18 @@ else
 _all: modules
 endif
 
-srctree                := $(if $(KBUILD_SRC),$(KBUILD_SRC),$(CURDIR))
-objtree                := $(CURDIR)
+ifeq ($(KBUILD_SRC),)
+        # building in the source tree
+        srctree := .
+else
+        ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))
+                # building in a subdirectory of the source tree
+                srctree := ..
+        else
+                srctree := $(KBUILD_SRC)
+        endif
+endif
+objtree                := .
 src            := $(srctree)
 obj            := $(objtree)
 
@@ -166,7 +172,7 @@ export srctree objtree VPATH
 # SUBARCH tells the usermode build what the underlying arch is.  That is set
 # first, and if a usermode build is happening, the "ARCH=um" on the command
 # line overrides the setting of ARCH below.  If a native build is happening,
-# then ARCH is assigned, getting whatever value it gets normally, and 
+# then ARCH is assigned, getting whatever value it gets normally, and
 # SUBARCH is subsequently ignored.
 
 SUBARCH := $(shell uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ \
@@ -259,18 +265,18 @@ endif
 KBUILD_MODULES :=
 KBUILD_BUILTIN := 1
 
-#      If we have only "make modules", don't compile built-in objects.
-#      When we're building modules with modversions, we need to consider
-#      the built-in objects during the descend as well, in order to
-#      make sure the checksums are up to date before we record them.
+# If we have only "make modules", don't compile built-in objects.
+# When we're building modules with modversions, we need to consider
+# the built-in objects during the descend as well, in order to
+# make sure the checksums are up to date before we record them.
 
 ifeq ($(MAKECMDGOALS),modules)
   KBUILD_BUILTIN := $(if $(CONFIG_MODVERSIONS),1)
 endif
 
-#      If we have "make <whatever> modules", compile modules
-#      in addition to whatever we do anyway.
-#      Just "make" or "make all" shall build modules as well
+# If we have "make <whatever> modules", compile modules
+# in addition to whatever we do anyway.
+# Just "make" or "make all" shall build modules as well
 
 ifneq ($(filter all _all modules,$(MAKECMDGOALS)),)
   KBUILD_MODULES := 1
@@ -294,7 +300,7 @@ export KBUILD_CHECKSRC KBUILD_SRC KBUILD_EXTMOD
 #         cmd_cc_o_c       = $(CC) $(c_flags) -c -o $@ $<
 #
 # If $(quiet) is empty, the whole command will be printed.
-# If it is set to "quiet_", only the short version will be printed. 
+# If it is set to "quiet_", only the short version will be printed.
 # If it is set to "silent_", nothing will be printed at all, since
 # the variable $(silent_cmd_cc_o_c) doesn't exist.
 #
@@ -346,7 +352,6 @@ $(srctree)/scripts/Kbuild.include: ;
 include $(srctree)/scripts/Kbuild.include
 
 # Make variables (CC, etc...)
-
 AS             = $(CROSS_COMPILE)as
 LD             = $(CROSS_COMPILE)ld
 CC             = $(CROSS_COMPILE)gcc
@@ -395,8 +400,8 @@ KBUILD_CPPFLAGS := -D__KERNEL__
 KBUILD_CFLAGS   := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
                   -fno-strict-aliasing -fno-common \
                   -Werror-implicit-function-declaration \
-                  -Wno-format-security \
-                  $(call cc-option,-fno-delete-null-pointer-checks,)
+                  -Wno-format-security
+
 KBUILD_AFLAGS_KERNEL :=
 KBUILD_CFLAGS_KERNEL :=
 KBUILD_AFLAGS   := -D__ASSEMBLY__
@@ -504,8 +509,16 @@ ifeq ($(mixed-targets),1)
 # We're called with mixed targets (*config and build targets).
 # Handle them one by one.
 
-%:: FORCE
-       $(Q)$(MAKE) -C $(srctree) KBUILD_SRC= $@
+PHONY += $(MAKECMDGOALS) __build_one_by_one
+
+$(filter-out __build_one_by_one, $(MAKECMDGOALS)): __build_one_by_one
+       @:
+
+__build_one_by_one:
+       $(Q)set -e; \
+       for i in $(MAKECMDGOALS); do \
+               $(MAKE) -f $(srctree)/Makefile $$i; \
+       done
 
 else
 ifeq ($(config-targets),1)
@@ -520,11 +533,9 @@ include $(srctree)/arch/$(SRCARCH)/Makefile
 export KBUILD_DEFCONFIG KBUILD_KCONFIG
 
 config: scripts_basic outputmakefile FORCE
-       $(Q)mkdir -p include/linux include/config
        $(Q)$(MAKE) $(build)=scripts/kconfig $@
 
 %config: scripts_basic outputmakefile FORCE
-       $(Q)mkdir -p include/linux include/config
        $(Q)$(MAKE) $(build)=scripts/kconfig $@
 
 else
@@ -594,14 +605,16 @@ endif # $(dot-config)
 # Defaults to vmlinux, but the arch makefile usually adds further targets
 all: vmlinux
 
+include $(srctree)/arch/$(SRCARCH)/Makefile
+
+KBUILD_CFLAGS  += $(call cc-option,-fno-delete-null-pointer-checks,)
+
 ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
 KBUILD_CFLAGS  += -Os $(call cc-disable-warning,maybe-uninitialized,)
 else
 KBUILD_CFLAGS  += -O2
 endif
 
-include $(srctree)/arch/$(SRCARCH)/Makefile
-
 ifdef CONFIG_READABLE_ASM
 # Disable optimizations that make assembler listings hard to read.
 # reorder blocks reorders the control in the function
@@ -731,6 +744,8 @@ ifeq ($(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-goto.sh $(CC)), y)
        KBUILD_CFLAGS += -DCC_HAVE_ASM_GOTO
 endif
 
+include $(srctree)/scripts/Makefile.extrawarn
+
 # Add user supplied CPPFLAGS, AFLAGS and CFLAGS as the last assignments
 KBUILD_CPPFLAGS += $(KCPPFLAGS)
 KBUILD_AFLAGS += $(KAFLAGS)
@@ -775,10 +790,10 @@ MODLIB    = $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)
 export MODLIB
 
 #
-#  INSTALL_MOD_STRIP, if defined, will cause modules to be
-#  stripped after they are installed.  If INSTALL_MOD_STRIP is '1', then
-#  the default option --strip-debug will be used.  Otherwise,
-#  INSTALL_MOD_STRIP value will be used as the options to the strip command.
+# INSTALL_MOD_STRIP, if defined, will cause modules to be
+# stripped after they are installed.  If INSTALL_MOD_STRIP is '1', then
+# the default option --strip-debug will be used.  Otherwise,
+# INSTALL_MOD_STRIP value will be used as the options to the strip command.
 
 ifdef INSTALL_MOD_STRIP
 ifeq ($(INSTALL_MOD_STRIP),1)
@@ -863,7 +878,7 @@ ifdef CONFIG_BUILD_DOCSRC
 endif
        +$(call if_changed,link-vmlinux)
 
-# The actual objects are generated when descending, 
+# The actual objects are generated when descending,
 # make sure no implicit rule kicks in
 $(sort $(vmlinux-deps)): $(vmlinux-dirs) ;
 
@@ -1021,11 +1036,11 @@ ifdef CONFIG_MODULES
 
 all: modules
 
-#      Build modules
+# Build modules
 #
-#      A module can be listed more than once in obj-m resulting in
-#      duplicate lines in modules.order files.  Those are removed
-#      using awk while concatenating to the final file.
+# A module can be listed more than once in obj-m resulting in
+# duplicate lines in modules.order files.  Those are removed
+# using awk while concatenating to the final file.
 
 PHONY += modules
 modules: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),vmlinux) modules.builtin
@@ -1054,10 +1069,10 @@ _modinst_:
        @rm -rf $(MODLIB)/kernel
        @rm -f $(MODLIB)/source
        @mkdir -p $(MODLIB)/kernel
-       @ln -s $(srctree) $(MODLIB)/source
+       @ln -s `cd $(srctree) && /bin/pwd` $(MODLIB)/source
        @if [ ! $(objtree) -ef  $(MODLIB)/build ]; then \
                rm -f $(MODLIB)/build ; \
-               ln -s $(objtree) $(MODLIB)/build ; \
+               ln -s $(CURDIR) $(MODLIB)/build ; \
        fi
        @cp -f $(objtree)/modules.order $(MODLIB)/
        @cp -f $(objtree)/modules.builtin $(MODLIB)/
@@ -1104,7 +1119,7 @@ CLEAN_DIRS  += $(MODVERDIR)
 
 # Directories & files removed with 'make mrproper'
 MRPROPER_DIRS  += include/config usr/include include/generated          \
-                  arch/*/include/generated .tmp_objdiff
+                 arch/*/include/generated .tmp_objdiff
 MRPROPER_FILES += .config .config.old .version .old_version $(version_h) \
                  Module.symvers tags TAGS cscope* GPATH GTAGS GRTAGS GSYMS \
                  signing_key.priv signing_key.x509 x509.genkey         \
@@ -1478,7 +1493,7 @@ endif
        $(build)=$(build-dir) $(@:.ko=.o)
        $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost
 
-# FIXME Should go into a make.lib or something 
+# FIXME Should go into a make.lib or something
 # ===========================================================================
 
 quiet_cmd_rmdirs = $(if $(wildcard $(rm-dirs)),CLEAN   $(wildcard $(rm-dirs)))
index 9f53e824b037a75ab8055226a792cf2ccd154eb4..4a4e02d0ce9e408b16b3c1bb57222fad963a2766 100644 (file)
@@ -662,6 +662,8 @@ ehrpwm2: ehrpwm@48304200 {
                mac: ethernet@4a100000 {
                        compatible = "ti,cpsw";
                        ti,hwmods = "cpgmac0";
+                       clocks = <&cpsw_125mhz_gclk>, <&cpsw_cpts_rft_clk>;
+                       clock-names = "fck", "cpts";
                        cpdma_channels = <8>;
                        ale_entries = <1024>;
                        bd_ram_size = <0x2000>;
index db464d7eaca847217260319ed3b064f2834eac6d..49fa596222547d646c79a031b260097cf9a8bf8e 100644 (file)
@@ -490,6 +490,8 @@ GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH
                        #address-cells = <1>;
                        #size-cells = <1>;
                        ti,hwmods = "cpgmac0";
+                       clocks = <&cpsw_125mhz_gclk>, <&cpsw_cpts_rft_clk>;
+                       clock-names = "fck", "cpts";
                        status = "disabled";
                        cpdma_channels = <8>;
                        ale_entries = <1024>;
index 25674fe81f703d279dc154ff75d02d366b34cef6..7e291e2ef4b38dc81a39d404b4c8953b673604e7 100644 (file)
@@ -57,6 +57,10 @@ sata@a0000 {
                        ethernet@30000 {
                                status = "okay";
                                phy-mode = "sgmii";
+                               fixed-link {
+                                       speed = <1000>;
+                                       full-duplex;
+                               };
                        };
 
                        pcie-controller {
index 51d0e912c8f585b1acb51edc9f47fc4270a1a988..1929ad390d88feb0ec42bce29ad1b174823ed55b 100644 (file)
@@ -165,5 +165,11 @@ rtc@d8100000 {
                        reg = <0xd8100000 0x10000>;
                        interrupts = <48>;
                };
+
+               ethernet@d8004000 {
+                       compatible = "via,vt8500-rhine";
+                       reg = <0xd8004000 0x100>;
+                       interrupts = <10>;
+               };
        };
 };
index 7525982262ac9896285031462e45b23b4020d9c7..b1c59a766a13381a693d897eb3349db4ac9d3c16 100644 (file)
@@ -218,5 +218,11 @@ rtc@d8100000 {
                        reg = <0xd8100000 0x10000>;
                        interrupts = <48>;
                };
+
+               ethernet@d8004000 {
+                       compatible = "via,vt8500-rhine";
+                       reg = <0xd8004000 0x100>;
+                       interrupts = <10>;
+               };
        };
 };
index d98386dd2882500bd71ecf726d8ac9bb26b777a7..8fbccfbe75f33df7be79ea7be37c15b9f5bf2535 100644 (file)
@@ -298,5 +298,11 @@ sdhc@d800a000 {
                        bus-width = <4>;
                        sdon-inverted;
                };
+
+               ethernet@d8004000 {
+                       compatible = "via,vt8500-rhine";
+                       reg = <0xd8004000 0x100>;
+                       interrupts = <10>;
+                };
        };
 };
index a6bc431cde701037ca6146d6931a56aa96838cb1..4238bcba9d60fc0aaa697a2a83818556db7c66cf 100644 (file)
@@ -410,7 +410,7 @@ __hw_perf_event_init(struct perf_event *event)
         */
        hwc->config_base            |= (unsigned long)mapping;
 
-       if (!hwc->sample_period) {
+       if (!is_sampling_event(event)) {
                /*
                 * For non-sampling runs, limit the sample_period to half
                 * of the counter width. That way, the new counter value
index a71ae1523620afc4cd149c26092d16d69cd858d8..af9e35e8836f1f3de2d9a4aaeee0c9445ce23740 100644 (file)
@@ -126,8 +126,8 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
 
        irqs = min(pmu_device->num_resources, num_possible_cpus());
        if (irqs < 1) {
-               pr_err("no irqs for PMUs defined\n");
-               return -ENODEV;
+               printk_once("perf/ARM: No irqs for PMU defined, sampling events not supported\n");
+               return 0;
        }
 
        irq = platform_get_irq(pmu_device, 0);
@@ -191,6 +191,10 @@ static void cpu_pmu_init(struct arm_pmu *cpu_pmu)
        /* Ensure the PMU has sane values out of reset. */
        if (cpu_pmu->reset)
                on_each_cpu(cpu_pmu->reset, cpu_pmu, 1);
+
+       /* If no interrupts available, set the corresponding capability flag */
+       if (!platform_get_irq(cpu_pmu->plat_device, 0))
+               cpu_pmu->pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;
 }
 
 /*
index 3997c411c1403659123d188c1c0750c58f59af3f..9d853189028bb0c79ad72557b018c12d5416aa67 100644 (file)
 #include <asm/topology.h>
 
 /*
- * cpu power scale management
+ * cpu capacity scale management
  */
 
 /*
- * cpu power table
+ * cpu capacity table
  * This per cpu data structure describes the relative capacity of each core.
  * On a heteregenous system, cores don't have the same computation capacity
- * and we reflect that difference in the cpu_power field so the scheduler can
- * take this difference into account during load balance. A per cpu structure
- * is preferred because each CPU updates its own cpu_power field during the
- * load balance except for idle cores. One idle core is selected to run the
- * rebalance_domains for all idle cores and the cpu_power can be updated
- * during this sequence.
+ * and we reflect that difference in the cpu_capacity field so the scheduler
+ * can take this difference into account during load balance. A per cpu
+ * structure is preferred because each CPU updates its own cpu_capacity field
+ * during the load balance except for idle cores. One idle core is selected
+ * to run the rebalance_domains for all idle cores and the cpu_capacity can be
+ * updated during this sequence.
  */
 static DEFINE_PER_CPU(unsigned long, cpu_scale);
 
-unsigned long arch_scale_freq_power(struct sched_domain *sd, int cpu)
+unsigned long arch_scale_freq_capacity(struct sched_domain *sd, int cpu)
 {
        return per_cpu(cpu_scale, cpu);
 }
 
-static void set_power_scale(unsigned int cpu, unsigned long power)
+static void set_capacity_scale(unsigned int cpu, unsigned long capacity)
 {
-       per_cpu(cpu_scale, cpu) = power;
+       per_cpu(cpu_scale, cpu) = capacity;
 }
 
 #ifdef CONFIG_OF
@@ -62,11 +62,11 @@ struct cpu_efficiency {
  * Table of relative efficiency of each processors
  * The efficiency value must fit in 20bit and the final
  * cpu_scale value must be in the range
- *   0 < cpu_scale < 3*SCHED_POWER_SCALE/2
+ *   0 < cpu_scale < 3*SCHED_CAPACITY_SCALE/2
  * in order to return at most 1 when DIV_ROUND_CLOSEST
  * is used to compute the capacity of a CPU.
  * Processors that are not defined in the table,
- * use the default SCHED_POWER_SCALE value for cpu_scale.
+ * use the default SCHED_CAPACITY_SCALE value for cpu_scale.
  */
 static const struct cpu_efficiency table_efficiency[] = {
        {"arm,cortex-a15", 3891},
@@ -83,9 +83,9 @@ static unsigned long middle_capacity = 1;
  * Iterate all CPUs' descriptor in DT and compute the efficiency
  * (as per table_efficiency). Also calculate a middle efficiency
  * as close as possible to  (max{eff_i} - min{eff_i}) / 2
- * This is later used to scale the cpu_power field such that an
- * 'average' CPU is of middle power. Also see the comments near
- * table_efficiency[] and update_cpu_power().
+ * This is later used to scale the cpu_capacity field such that an
+ * 'average' CPU is of middle capacity. Also see the comments near
+ * table_efficiency[] and update_cpu_capacity().
  */
 static void __init parse_dt_topology(void)
 {
@@ -141,15 +141,15 @@ static void __init parse_dt_topology(void)
         * cpu_scale because all CPUs have the same capacity. Otherwise, we
         * compute a middle_capacity factor that will ensure that the capacity
         * of an 'average' CPU of the system will be as close as possible to
-        * SCHED_POWER_SCALE, which is the default value, but with the
+        * SCHED_CAPACITY_SCALE, which is the default value, but with the
         * constraint explained near table_efficiency[].
         */
        if (4*max_capacity < (3*(max_capacity + min_capacity)))
                middle_capacity = (min_capacity + max_capacity)
-                               >> (SCHED_POWER_SHIFT+1);
+                               >> (SCHED_CAPACITY_SHIFT+1);
        else
                middle_capacity = ((max_capacity / 3)
-                               >> (SCHED_POWER_SHIFT-1)) + 1;
+                               >> (SCHED_CAPACITY_SHIFT-1)) + 1;
 
 }
 
@@ -158,20 +158,20 @@ static void __init parse_dt_topology(void)
  * boot. The update of all CPUs is in O(n^2) for heteregeneous system but the
  * function returns directly for SMP system.
  */
-static void update_cpu_power(unsigned int cpu)
+static void update_cpu_capacity(unsigned int cpu)
 {
        if (!cpu_capacity(cpu))
                return;
 
-       set_power_scale(cpu, cpu_capacity(cpu) / middle_capacity);
+       set_capacity_scale(cpu, cpu_capacity(cpu) / middle_capacity);
 
-       printk(KERN_INFO "CPU%u: update cpu_power %lu\n",
-               cpu, arch_scale_freq_power(NULL, cpu));
+       printk(KERN_INFO "CPU%u: update cpu_capacity %lu\n",
+               cpu, arch_scale_freq_capacity(NULL, cpu));
 }
 
 #else
 static inline void parse_dt_topology(void) {}
-static inline void update_cpu_power(unsigned int cpuid) {}
+static inline void update_cpu_capacity(unsigned int cpuid) {}
 #endif
 
  /*
@@ -267,7 +267,7 @@ void store_cpu_topology(unsigned int cpuid)
 
        update_siblings_masks(cpuid);
 
-       update_cpu_power(cpuid);
+       update_cpu_capacity(cpuid);
 
        printk(KERN_INFO "CPU%u: thread %d, cpu %d, socket %d, mpidr %x\n",
                cpuid, cpu_topology[cpuid].thread_id,
@@ -297,7 +297,7 @@ void __init init_cpu_topology(void)
 {
        unsigned int cpu;
 
-       /* init core mask and power*/
+       /* init core mask and capacity */
        for_each_possible_cpu(cpu) {
                struct cputopo_arm *cpu_topo = &(cpu_topology[cpu]);
 
@@ -307,7 +307,7 @@ void __init init_cpu_topology(void)
                cpumask_clear(&cpu_topo->core_sibling);
                cpumask_clear(&cpu_topo->thread_sibling);
 
-               set_power_scale(cpu, SCHED_POWER_SCALE);
+               set_capacity_scale(cpu, SCHED_CAPACITY_SCALE);
        }
        smp_wmb();
 
index e4dec9fcb084afe014d8beb09706f946f7b8e734..9c6029ba526fff85005d8195c311138be7074a94 100644 (file)
@@ -23,9 +23,7 @@
 #include "board.h"
 
 static struct rfkill_gpio_platform_data wifi_rfkill_platform_data = {
-       .name           = "wifi_rfkill",
-       .reset_gpio     = 25, /* PD1 */
-       .shutdown_gpio  = 85, /* PK5 */
+       .name   = "wifi_rfkill",
        .type   = RFKILL_TYPE_WLAN,
 };
 
index 6f879c319a9dbe9fc406946565da49a0e99f5eb8..fb5503ce016f0fa693f765f0157a2379422202dd 100644 (file)
@@ -136,7 +136,7 @@ static u16 saved_regs(struct jit_ctx *ctx)
        u16 ret = 0;
 
        if ((ctx->skf->len > 1) ||
-           (ctx->skf->insns[0].code == BPF_S_RET_A))
+           (ctx->skf->insns[0].code == (BPF_RET | BPF_A)))
                ret |= 1 << r_A;
 
 #ifdef CONFIG_FRAME_POINTER
@@ -164,18 +164,10 @@ static inline int mem_words_used(struct jit_ctx *ctx)
 static inline bool is_load_to_a(u16 inst)
 {
        switch (inst) {
-       case BPF_S_LD_W_LEN:
-       case BPF_S_LD_W_ABS:
-       case BPF_S_LD_H_ABS:
-       case BPF_S_LD_B_ABS:
-       case BPF_S_ANC_CPU:
-       case BPF_S_ANC_IFINDEX:
-       case BPF_S_ANC_MARK:
-       case BPF_S_ANC_PROTOCOL:
-       case BPF_S_ANC_RXHASH:
-       case BPF_S_ANC_VLAN_TAG:
-       case BPF_S_ANC_VLAN_TAG_PRESENT:
-       case BPF_S_ANC_QUEUE:
+       case BPF_LD | BPF_W | BPF_LEN:
+       case BPF_LD | BPF_W | BPF_ABS:
+       case BPF_LD | BPF_H | BPF_ABS:
+       case BPF_LD | BPF_B | BPF_ABS:
                return true;
        default:
                return false;
@@ -215,7 +207,7 @@ static void build_prologue(struct jit_ctx *ctx)
                emit(ARM_MOV_I(r_X, 0), ctx);
 
        /* do not leak kernel data to userspace */
-       if ((first_inst != BPF_S_RET_K) && !(is_load_to_a(first_inst)))
+       if ((first_inst != (BPF_RET | BPF_K)) && !(is_load_to_a(first_inst)))
                emit(ARM_MOV_I(r_A, 0), ctx);
 
        /* stack space for the BPF_MEM words */
@@ -480,36 +472,39 @@ static int build_body(struct jit_ctx *ctx)
        u32 k;
 
        for (i = 0; i < prog->len; i++) {
+               u16 code;
+
                inst = &(prog->insns[i]);
                /* K as an immediate value operand */
                k = inst->k;
+               code = bpf_anc_helper(inst);
 
                /* compute offsets only in the fake pass */
                if (ctx->target == NULL)
                        ctx->offsets[i] = ctx->idx * 4;
 
-               switch (inst->code) {
-               case BPF_S_LD_IMM:
+               switch (code) {
+               case BPF_LD | BPF_IMM:
                        emit_mov_i(r_A, k, ctx);
                        break;
-               case BPF_S_LD_W_LEN:
+               case BPF_LD | BPF_W | BPF_LEN:
                        ctx->seen |= SEEN_SKB;
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4);
                        emit(ARM_LDR_I(r_A, r_skb,
                                       offsetof(struct sk_buff, len)), ctx);
                        break;
-               case BPF_S_LD_MEM:
+               case BPF_LD | BPF_MEM:
                        /* A = scratch[k] */
                        ctx->seen |= SEEN_MEM_WORD(k);
                        emit(ARM_LDR_I(r_A, ARM_SP, SCRATCH_OFF(k)), ctx);
                        break;
-               case BPF_S_LD_W_ABS:
+               case BPF_LD | BPF_W | BPF_ABS:
                        load_order = 2;
                        goto load;
-               case BPF_S_LD_H_ABS:
+               case BPF_LD | BPF_H | BPF_ABS:
                        load_order = 1;
                        goto load;
-               case BPF_S_LD_B_ABS:
+               case BPF_LD | BPF_B | BPF_ABS:
                        load_order = 0;
 load:
                        /* the interpreter will deal with the negative K */
@@ -552,31 +547,31 @@ static int build_body(struct jit_ctx *ctx)
                        emit_err_ret(ARM_COND_NE, ctx);
                        emit(ARM_MOV_R(r_A, ARM_R0), ctx);
                        break;
-               case BPF_S_LD_W_IND:
+               case BPF_LD | BPF_W | BPF_IND:
                        load_order = 2;
                        goto load_ind;
-               case BPF_S_LD_H_IND:
+               case BPF_LD | BPF_H | BPF_IND:
                        load_order = 1;
                        goto load_ind;
-               case BPF_S_LD_B_IND:
+               case BPF_LD | BPF_B | BPF_IND:
                        load_order = 0;
 load_ind:
                        OP_IMM3(ARM_ADD, r_off, r_X, k, ctx);
                        goto load_common;
-               case BPF_S_LDX_IMM:
+               case BPF_LDX | BPF_IMM:
                        ctx->seen |= SEEN_X;
                        emit_mov_i(r_X, k, ctx);
                        break;
-               case BPF_S_LDX_W_LEN:
+               case BPF_LDX | BPF_W | BPF_LEN:
                        ctx->seen |= SEEN_X | SEEN_SKB;
                        emit(ARM_LDR_I(r_X, r_skb,
                                       offsetof(struct sk_buff, len)), ctx);
                        break;
-               case BPF_S_LDX_MEM:
+               case BPF_LDX | BPF_MEM:
                        ctx->seen |= SEEN_X | SEEN_MEM_WORD(k);
                        emit(ARM_LDR_I(r_X, ARM_SP, SCRATCH_OFF(k)), ctx);
                        break;
-               case BPF_S_LDX_B_MSH:
+               case BPF_LDX | BPF_B | BPF_MSH:
                        /* x = ((*(frame + k)) & 0xf) << 2; */
                        ctx->seen |= SEEN_X | SEEN_DATA | SEEN_CALL;
                        /* the interpreter should deal with the negative K */
@@ -606,113 +601,113 @@ static int build_body(struct jit_ctx *ctx)
                        emit(ARM_AND_I(r_X, ARM_R0, 0x00f), ctx);
                        emit(ARM_LSL_I(r_X, r_X, 2), ctx);
                        break;
-               case BPF_S_ST:
+               case BPF_ST:
                        ctx->seen |= SEEN_MEM_WORD(k);
                        emit(ARM_STR_I(r_A, ARM_SP, SCRATCH_OFF(k)), ctx);
                        break;
-               case BPF_S_STX:
+               case BPF_STX:
                        update_on_xread(ctx);
                        ctx->seen |= SEEN_MEM_WORD(k);
                        emit(ARM_STR_I(r_X, ARM_SP, SCRATCH_OFF(k)), ctx);
                        break;
-               case BPF_S_ALU_ADD_K:
+               case BPF_ALU | BPF_ADD | BPF_K:
                        /* A += K */
                        OP_IMM3(ARM_ADD, r_A, r_A, k, ctx);
                        break;
-               case BPF_S_ALU_ADD_X:
+               case BPF_ALU | BPF_ADD | BPF_X:
                        update_on_xread(ctx);
                        emit(ARM_ADD_R(r_A, r_A, r_X), ctx);
                        break;
-               case BPF_S_ALU_SUB_K:
+               case BPF_ALU | BPF_SUB | BPF_K:
                        /* A -= K */
                        OP_IMM3(ARM_SUB, r_A, r_A, k, ctx);
                        break;
-               case BPF_S_ALU_SUB_X:
+               case BPF_ALU | BPF_SUB | BPF_X:
                        update_on_xread(ctx);
                        emit(ARM_SUB_R(r_A, r_A, r_X), ctx);
                        break;
-               case BPF_S_ALU_MUL_K:
+               case BPF_ALU | BPF_MUL | BPF_K:
                        /* A *= K */
                        emit_mov_i(r_scratch, k, ctx);
                        emit(ARM_MUL(r_A, r_A, r_scratch), ctx);
                        break;
-               case BPF_S_ALU_MUL_X:
+               case BPF_ALU | BPF_MUL | BPF_X:
                        update_on_xread(ctx);
                        emit(ARM_MUL(r_A, r_A, r_X), ctx);
                        break;
-               case BPF_S_ALU_DIV_K:
+               case BPF_ALU | BPF_DIV | BPF_K:
                        if (k == 1)
                                break;
                        emit_mov_i(r_scratch, k, ctx);
                        emit_udiv(r_A, r_A, r_scratch, ctx);
                        break;
-               case BPF_S_ALU_DIV_X:
+               case BPF_ALU | BPF_DIV | BPF_X:
                        update_on_xread(ctx);
                        emit(ARM_CMP_I(r_X, 0), ctx);
                        emit_err_ret(ARM_COND_EQ, ctx);
                        emit_udiv(r_A, r_A, r_X, ctx);
                        break;
-               case BPF_S_ALU_OR_K:
+               case BPF_ALU | BPF_OR | BPF_K:
                        /* A |= K */
                        OP_IMM3(ARM_ORR, r_A, r_A, k, ctx);
                        break;
-               case BPF_S_ALU_OR_X:
+               case BPF_ALU | BPF_OR | BPF_X:
                        update_on_xread(ctx);
                        emit(ARM_ORR_R(r_A, r_A, r_X), ctx);
                        break;
-               case BPF_S_ALU_XOR_K:
+               case BPF_ALU | BPF_XOR | BPF_K:
                        /* A ^= K; */
                        OP_IMM3(ARM_EOR, r_A, r_A, k, ctx);
                        break;
-               case BPF_S_ANC_ALU_XOR_X:
-               case BPF_S_ALU_XOR_X:
+               case BPF_ANC | SKF_AD_ALU_XOR_X:
+               case BPF_ALU | BPF_XOR | BPF_X:
                        /* A ^= X */
                        update_on_xread(ctx);
                        emit(ARM_EOR_R(r_A, r_A, r_X), ctx);
                        break;
-               case BPF_S_ALU_AND_K:
+               case BPF_ALU | BPF_AND | BPF_K:
                        /* A &= K */
                        OP_IMM3(ARM_AND, r_A, r_A, k, ctx);
                        break;
-               case BPF_S_ALU_AND_X:
+               case BPF_ALU | BPF_AND | BPF_X:
                        update_on_xread(ctx);
                        emit(ARM_AND_R(r_A, r_A, r_X), ctx);
                        break;
-               case BPF_S_ALU_LSH_K:
+               case BPF_ALU | BPF_LSH | BPF_K:
                        if (unlikely(k > 31))
                                return -1;
                        emit(ARM_LSL_I(r_A, r_A, k), ctx);
                        break;
-               case BPF_S_ALU_LSH_X:
+               case BPF_ALU | BPF_LSH | BPF_X:
                        update_on_xread(ctx);
                        emit(ARM_LSL_R(r_A, r_A, r_X), ctx);
                        break;
-               case BPF_S_ALU_RSH_K:
+               case BPF_ALU | BPF_RSH | BPF_K:
                        if (unlikely(k > 31))
                                return -1;
                        emit(ARM_LSR_I(r_A, r_A, k), ctx);
                        break;
-               case BPF_S_ALU_RSH_X:
+               case BPF_ALU | BPF_RSH | BPF_X:
                        update_on_xread(ctx);
                        emit(ARM_LSR_R(r_A, r_A, r_X), ctx);
                        break;
-               case BPF_S_ALU_NEG:
+               case BPF_ALU | BPF_NEG:
                        /* A = -A */
                        emit(ARM_RSB_I(r_A, r_A, 0), ctx);
                        break;
-               case BPF_S_JMP_JA:
+               case BPF_JMP | BPF_JA:
                        /* pc += K */
                        emit(ARM_B(b_imm(i + k + 1, ctx)), ctx);
                        break;
-               case BPF_S_JMP_JEQ_K:
+               case BPF_JMP | BPF_JEQ | BPF_K:
                        /* pc += (A == K) ? pc->jt : pc->jf */
                        condt  = ARM_COND_EQ;
                        goto cmp_imm;
-               case BPF_S_JMP_JGT_K:
+               case BPF_JMP | BPF_JGT | BPF_K:
                        /* pc += (A > K) ? pc->jt : pc->jf */
                        condt  = ARM_COND_HI;
                        goto cmp_imm;
-               case BPF_S_JMP_JGE_K:
+               case BPF_JMP | BPF_JGE | BPF_K:
                        /* pc += (A >= K) ? pc->jt : pc->jf */
                        condt  = ARM_COND_HS;
 cmp_imm:
@@ -731,22 +726,22 @@ static int build_body(struct jit_ctx *ctx)
                                _emit(condt ^ 1, ARM_B(b_imm(i + inst->jf + 1,
                                                             ctx)), ctx);
                        break;
-               case BPF_S_JMP_JEQ_X:
+               case BPF_JMP | BPF_JEQ | BPF_X:
                        /* pc += (A == X) ? pc->jt : pc->jf */
                        condt   = ARM_COND_EQ;
                        goto cmp_x;
-               case BPF_S_JMP_JGT_X:
+               case BPF_JMP | BPF_JGT | BPF_X:
                        /* pc += (A > X) ? pc->jt : pc->jf */
                        condt   = ARM_COND_HI;
                        goto cmp_x;
-               case BPF_S_JMP_JGE_X:
+               case BPF_JMP | BPF_JGE | BPF_X:
                        /* pc += (A >= X) ? pc->jt : pc->jf */
                        condt   = ARM_COND_CS;
 cmp_x:
                        update_on_xread(ctx);
                        emit(ARM_CMP_R(r_A, r_X), ctx);
                        goto cond_jump;
-               case BPF_S_JMP_JSET_K:
+               case BPF_JMP | BPF_JSET | BPF_K:
                        /* pc += (A & K) ? pc->jt : pc->jf */
                        condt  = ARM_COND_NE;
                        /* not set iff all zeroes iff Z==1 iff EQ */
@@ -759,16 +754,16 @@ static int build_body(struct jit_ctx *ctx)
                                emit(ARM_TST_I(r_A, imm12), ctx);
                        }
                        goto cond_jump;
-               case BPF_S_JMP_JSET_X:
+               case BPF_JMP | BPF_JSET | BPF_X:
                        /* pc += (A & X) ? pc->jt : pc->jf */
                        update_on_xread(ctx);
                        condt  = ARM_COND_NE;
                        emit(ARM_TST_R(r_A, r_X), ctx);
                        goto cond_jump;
-               case BPF_S_RET_A:
+               case BPF_RET | BPF_A:
                        emit(ARM_MOV_R(ARM_R0, r_A), ctx);
                        goto b_epilogue;
-               case BPF_S_RET_K:
+               case BPF_RET | BPF_K:
                        if ((k == 0) && (ctx->ret0_fp_idx < 0))
                                ctx->ret0_fp_idx = i;
                        emit_mov_i(ARM_R0, k, ctx);
@@ -776,17 +771,17 @@ static int build_body(struct jit_ctx *ctx)
                        if (i != ctx->skf->len - 1)
                                emit(ARM_B(b_imm(prog->len, ctx)), ctx);
                        break;
-               case BPF_S_MISC_TAX:
+               case BPF_MISC | BPF_TAX:
                        /* X = A */
                        ctx->seen |= SEEN_X;
                        emit(ARM_MOV_R(r_X, r_A), ctx);
                        break;
-               case BPF_S_MISC_TXA:
+               case BPF_MISC | BPF_TXA:
                        /* A = X */
                        update_on_xread(ctx);
                        emit(ARM_MOV_R(r_A, r_X), ctx);
                        break;
-               case BPF_S_ANC_PROTOCOL:
+               case BPF_ANC | SKF_AD_PROTOCOL:
                        /* A = ntohs(skb->protocol) */
                        ctx->seen |= SEEN_SKB;
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff,
@@ -795,7 +790,7 @@ static int build_body(struct jit_ctx *ctx)
                        emit(ARM_LDRH_I(r_scratch, r_skb, off), ctx);
                        emit_swap16(r_A, r_scratch, ctx);
                        break;
-               case BPF_S_ANC_CPU:
+               case BPF_ANC | SKF_AD_CPU:
                        /* r_scratch = current_thread_info() */
                        OP_IMM3(ARM_BIC, r_scratch, ARM_SP, THREAD_SIZE - 1, ctx);
                        /* A = current_thread_info()->cpu */
@@ -803,7 +798,7 @@ static int build_body(struct jit_ctx *ctx)
                        off = offsetof(struct thread_info, cpu);
                        emit(ARM_LDR_I(r_A, r_scratch, off), ctx);
                        break;
-               case BPF_S_ANC_IFINDEX:
+               case BPF_ANC | SKF_AD_IFINDEX:
                        /* A = skb->dev->ifindex */
                        ctx->seen |= SEEN_SKB;
                        off = offsetof(struct sk_buff, dev);
@@ -817,30 +812,30 @@ static int build_body(struct jit_ctx *ctx)
                        off = offsetof(struct net_device, ifindex);
                        emit(ARM_LDR_I(r_A, r_scratch, off), ctx);
                        break;
-               case BPF_S_ANC_MARK:
+               case BPF_ANC | SKF_AD_MARK:
                        ctx->seen |= SEEN_SKB;
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4);
                        off = offsetof(struct sk_buff, mark);
                        emit(ARM_LDR_I(r_A, r_skb, off), ctx);
                        break;
-               case BPF_S_ANC_RXHASH:
+               case BPF_ANC | SKF_AD_RXHASH:
                        ctx->seen |= SEEN_SKB;
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4);
                        off = offsetof(struct sk_buff, hash);
                        emit(ARM_LDR_I(r_A, r_skb, off), ctx);
                        break;
-               case BPF_S_ANC_VLAN_TAG:
-               case BPF_S_ANC_VLAN_TAG_PRESENT:
+               case BPF_ANC | SKF_AD_VLAN_TAG:
+               case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT:
                        ctx->seen |= SEEN_SKB;
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2);
                        off = offsetof(struct sk_buff, vlan_tci);
                        emit(ARM_LDRH_I(r_A, r_skb, off), ctx);
-                       if (inst->code == BPF_S_ANC_VLAN_TAG)
+                       if (code == (BPF_ANC | SKF_AD_VLAN_TAG))
                                OP_IMM3(ARM_AND, r_A, r_A, VLAN_VID_MASK, ctx);
                        else
                                OP_IMM3(ARM_AND, r_A, r_A, VLAN_TAG_PRESENT, ctx);
                        break;
-               case BPF_S_ANC_QUEUE:
+               case BPF_ANC | SKF_AD_QUEUE:
                        ctx->seen |= SEEN_SKB;
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff,
                                                  queue_mapping) != 2);
index 1759fad540178705f0ff77940655d47569f18b93..e66ba31ef84de15520ded3e787fd1b7b91af4425 100644 (file)
@@ -53,7 +53,6 @@ CONFIG_IP_PNP=y
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 # CONFIG_FW_LOADER is not set
 CONFIG_MTD=y
-CONFIG_MTD_CHAR=y
 CONFIG_MTD_BLOCK=y
 CONFIG_MTD_CFI=y
 CONFIG_MTD_CFI_INTELEXT=y
@@ -63,6 +62,7 @@ CONFIG_MTD_COMPLEX_MAPPINGS=y
 CONFIG_MTD_PHYSMAP=y
 CONFIG_MTD_M25P80=y
 CONFIG_MTD_NAND=m
+CONFIG_MTD_SPI_NOR=y
 CONFIG_BLK_DEV_RAM=y
 CONFIG_SCSI=y
 # CONFIG_SCSI_PROC_FS is not set
index 357729682c0064edf2d908093037506a9dd31669..0207c588c19f3cad7d91b4dff2b84555474034f9 100644 (file)
@@ -58,7 +58,6 @@ CONFIG_BFIN_SIR0=y
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 # CONFIG_FW_LOADER is not set
 CONFIG_MTD=y
-CONFIG_MTD_CHAR=m
 CONFIG_MTD_BLOCK=y
 CONFIG_MTD_JEDECPROBE=m
 CONFIG_MTD_RAM=y
@@ -66,6 +65,7 @@ CONFIG_MTD_ROM=m
 CONFIG_MTD_COMPLEX_MAPPINGS=y
 CONFIG_MTD_M25P80=y
 CONFIG_MTD_NAND=m
+CONFIG_MTD_SPI_NOR=y
 CONFIG_BLK_DEV_RAM=y
 CONFIG_SCSI=y
 # CONFIG_SCSI_PROC_FS is not set
index 2e73a5d33da88f294f12d035a2f974917e8d3845..99c131ba7d908b557d85ebb57a0d0e0b6cf409b7 100644 (file)
@@ -57,7 +57,6 @@ CONFIG_BFIN_SIR0=y
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 # CONFIG_FW_LOADER is not set
 CONFIG_MTD=y
-CONFIG_MTD_CHAR=m
 CONFIG_MTD_BLOCK=y
 CONFIG_MTD_JEDECPROBE=m
 CONFIG_MTD_RAM=y
@@ -65,6 +64,7 @@ CONFIG_MTD_ROM=m
 CONFIG_MTD_COMPLEX_MAPPINGS=y
 CONFIG_MTD_M25P80=y
 CONFIG_MTD_NAND=m
+CONFIG_MTD_SPI_NOR=y
 CONFIG_BLK_DEV_RAM=y
 CONFIG_SCSI=y
 # CONFIG_SCSI_PROC_FS is not set
index f0a2ddf5de468d0a96f1fbc716b3e37915c377f8..38cb17d218d46f04f1b677beb748438e37cf65ea 100644 (file)
@@ -64,7 +64,6 @@ CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 CONFIG_FW_LOADER=m
 CONFIG_MTD=y
 CONFIG_MTD_CMDLINE_PARTS=y
-CONFIG_MTD_CHAR=y
 CONFIG_MTD_BLOCK=y
 CONFIG_MTD_CFI=y
 CONFIG_MTD_CFI_INTELEXT=y
@@ -75,6 +74,7 @@ CONFIG_MTD_M25P80=y
 CONFIG_MTD_NAND=y
 CONFIG_MTD_NAND_BF5XX=y
 # CONFIG_MTD_NAND_BF5XX_HWECC is not set
+CONFIG_MTD_SPI_NOR=y
 CONFIG_BLK_DEV_RAM=y
 # CONFIG_SCSI_PROC_FS is not set
 CONFIG_BLK_DEV_SD=y
index 4ca39ab6b2bf98c12b260fef822473ae75820c2f..a7e9bfd84183d5b7c26924679f9bb33d88ac0118 100644 (file)
@@ -57,7 +57,6 @@ CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 CONFIG_FW_LOADER=m
 CONFIG_MTD=y
 CONFIG_MTD_CMDLINE_PARTS=y
-CONFIG_MTD_CHAR=y
 CONFIG_MTD_BLOCK=y
 CONFIG_MTD_CFI=y
 CONFIG_MTD_CFI_INTELEXT=y
@@ -65,6 +64,7 @@ CONFIG_MTD_CFI_STAA=y
 CONFIG_MTD_COMPLEX_MAPPINGS=y
 CONFIG_MTD_PHYSMAP=y
 CONFIG_MTD_M25P80=y
+CONFIG_MTD_SPI_NOR=y
 CONFIG_MTD_UBI=m
 CONFIG_SCSI=y
 CONFIG_BLK_DEV_SD=y
index 3853c473b443193dac482e2994c1d131328a80fe..f4a9200e1ab1526e44786e0b982168aef734bf57 100644 (file)
@@ -45,7 +45,6 @@ CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 # CONFIG_FW_LOADER is not set
 CONFIG_MTD=y
 CONFIG_MTD_CMDLINE_PARTS=y
-CONFIG_MTD_CHAR=m
 CONFIG_MTD_BLOCK=y
 CONFIG_MTD_CFI=m
 CONFIG_MTD_CFI_AMDSTD=m
@@ -53,7 +52,7 @@ CONFIG_MTD_RAM=y
 CONFIG_MTD_ROM=m
 CONFIG_MTD_COMPLEX_MAPPINGS=y
 CONFIG_MTD_M25P80=y
-# CONFIG_M25PXX_USE_FAST_READ is not set
+CONFIG_MTD_SPI_NOR=y
 CONFIG_BLK_DEV_LOOP=y
 CONFIG_BLK_DEV_NBD=y
 CONFIG_BLK_DEV_RAM=y
index f754e490bbfd00a570995da744290cfa0db1d894..0ff97d8d047a5f685e92305d93c51692c877097d 100644 (file)
@@ -36,13 +36,12 @@ CONFIG_IRTTY_SIR=m
 # CONFIG_WIRELESS is not set
 # CONFIG_FW_LOADER is not set
 CONFIG_MTD=y
-CONFIG_MTD_CHAR=y
 CONFIG_MTD_BLOCK=y
 CONFIG_MTD_RAM=y
 CONFIG_MTD_ROM=y
 CONFIG_MTD_COMPLEX_MAPPINGS=y
 CONFIG_MTD_M25P80=y
-# CONFIG_M25PXX_USE_FAST_READ is not set
+CONFIG_MTD_SPI_NOR=y
 CONFIG_BLK_DEV_RAM=y
 CONFIG_MISC_DEVICES=y
 CONFIG_EEPROM_AT25=y
index 8d1e4c2d2c36d4f2944c8e7ea4d40050da4b530a..40e9c2bbc6e37f26ef8030316556a16c05c0d776 100644 (file)
@@ -316,6 +316,8 @@ static inline void disable_dma(unsigned int channel)
 }
 static inline void enable_dma(unsigned int channel)
 {
+       dma_ch[channel].regs->curr_x_count = 0;
+       dma_ch[channel].regs->curr_y_count = 0;
        dma_ch[channel].regs->cfg |= DMAEN;
 }
 int set_dma_callback(unsigned int channel, irq_handler_t callback, void *data);
index d0989290f54cc663dca045455fdaf9cc7f461807..6f4bac969bf72e360a6476c55a9e7f667d2d21fb 100644 (file)
@@ -17,6 +17,7 @@
 #if IS_ENABLED(CONFIG_USB_ISP1362_HCD)
 #include <linux/usb/isp1362.h>
 #endif
+#include <linux/gpio.h>
 #include <linux/irq.h>
 #include <linux/i2c.h>
 #include <asm/dma.h>
index a8b5408dd3495f48a19a7ad35ddca3b5724d6005..da4cdb16844ebcca13adff525d5e486a4f9367a6 100644 (file)
@@ -168,6 +168,7 @@ static void nvram_read_alpha2(const char *prefix, const char *name,
 static void bcm47xx_fill_sprom_r1234589(struct ssb_sprom *sprom,
                                        const char *prefix, bool fallback)
 {
+       nvram_read_u16(prefix, NULL, "devid", &sprom->dev_id, 0, fallback);
        nvram_read_u8(prefix, NULL, "ledbh0", &sprom->gpio0, 0xff, fallback);
        nvram_read_u8(prefix, NULL, "ledbh1", &sprom->gpio1, 0xff, fallback);
        nvram_read_u8(prefix, NULL, "ledbh2", &sprom->gpio2, 0xff, fallback);
index 21c9f304e96c50b7945ae4571a3fdf0363d924ce..790352f937004fde97c2430c67d0823fc382d808 100644 (file)
@@ -235,11 +235,6 @@ config PPC_EARLY_DEBUG_USBGECKO
          Select this to enable early debugging for Nintendo GameCube/Wii
          consoles via an external USB Gecko adapter.
 
-config PPC_EARLY_DEBUG_WSP
-       bool "Early debugging via WSP's internal UART"
-       depends on PPC_WSP
-       select PPC_UDBG_16550
-
 config PPC_EARLY_DEBUG_PS3GELIC
        bool "Early debugging through the PS3 Ethernet port"
        depends on PPC_PS3
index 426dce7ae7c4974db360c15bd6b6c9257d6564d1..ccc25eddbcb8b2ff8404d66cecbaec1e450f5a51 100644 (file)
@@ -333,8 +333,8 @@ $(addprefix $(obj)/, $(initrd-y)): $(obj)/ramdisk.image.gz
 $(obj)/zImage.initrd.%: vmlinux $(wrapperbits)
        $(call if_changed,wrap,$*,,,$(obj)/ramdisk.image.gz)
 
-$(obj)/zImage.%: vmlinux $(wrapperbits)
-       $(call if_changed,wrap,$*)
+$(addprefix $(obj)/, $(sort $(filter zImage.%, $(image-y)))): vmlinux $(wrapperbits)
+       $(call if_changed,wrap,$(subst $(obj)/zImage.,,$@))
 
 # dtbImage% - a dtbImage is a zImage with an embedded device tree blob
 $(obj)/dtbImage.initrd.%: vmlinux $(wrapperbits) $(obj)/%.dtb
diff --git a/arch/powerpc/configs/chroma_defconfig b/arch/powerpc/configs/chroma_defconfig
deleted file mode 100644 (file)
index 4f35fc4..0000000
+++ /dev/null
@@ -1,307 +0,0 @@
-CONFIG_PPC64=y
-CONFIG_PPC_BOOK3E_64=y
-# CONFIG_VIRT_CPU_ACCOUNTING_NATIVE is not set
-CONFIG_SMP=y
-CONFIG_NR_CPUS=256
-CONFIG_EXPERIMENTAL=y
-CONFIG_SYSVIPC=y
-CONFIG_POSIX_MQUEUE=y
-CONFIG_BSD_PROCESS_ACCT=y
-CONFIG_TASKSTATS=y
-CONFIG_TASK_DELAY_ACCT=y
-CONFIG_TASK_XACCT=y
-CONFIG_TASK_IO_ACCOUNTING=y
-CONFIG_AUDIT=y
-CONFIG_AUDITSYSCALL=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_LOG_BUF_SHIFT=19
-CONFIG_CGROUPS=y
-CONFIG_CGROUP_DEVICE=y
-CONFIG_CPUSETS=y
-CONFIG_CGROUP_CPUACCT=y
-CONFIG_RESOURCE_COUNTERS=y
-CONFIG_CGROUP_MEMCG=y
-CONFIG_CGROUP_MEMCG_SWAP=y
-CONFIG_NAMESPACES=y
-CONFIG_RELAY=y
-CONFIG_BLK_DEV_INITRD=y
-CONFIG_INITRAMFS_SOURCE=""
-CONFIG_RD_BZIP2=y
-CONFIG_RD_LZMA=y
-CONFIG_INITRAMFS_COMPRESSION_GZIP=y
-CONFIG_KALLSYMS_ALL=y
-CONFIG_EMBEDDED=y
-CONFIG_PERF_EVENTS=y
-CONFIG_PROFILING=y
-CONFIG_OPROFILE=y
-CONFIG_KPROBES=y
-CONFIG_MODULES=y
-CONFIG_MODULE_FORCE_LOAD=y
-CONFIG_MODULE_UNLOAD=y
-CONFIG_MODULE_FORCE_UNLOAD=y
-CONFIG_MODVERSIONS=y
-CONFIG_MODULE_SRCVERSION_ALL=y
-CONFIG_SCOM_DEBUGFS=y
-CONFIG_PPC_A2_DD2=y
-CONFIG_KVM_GUEST=y
-CONFIG_NO_HZ=y
-CONFIG_HIGH_RES_TIMERS=y
-CONFIG_HZ_100=y
-# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
-CONFIG_BINFMT_MISC=y
-CONFIG_NUMA=y
-# CONFIG_MIGRATION is not set
-CONFIG_PPC_64K_PAGES=y
-CONFIG_SCHED_SMT=y
-CONFIG_CMDLINE_BOOL=y
-CONFIG_CMDLINE=""
-# CONFIG_SECCOMP is not set
-CONFIG_PCIEPORTBUS=y
-# CONFIG_PCIEASPM is not set
-CONFIG_PCI_MSI=y
-CONFIG_PACKET=y
-CONFIG_UNIX=y
-CONFIG_XFRM_USER=m
-CONFIG_XFRM_SUB_POLICY=y
-CONFIG_XFRM_STATISTICS=y
-CONFIG_NET_KEY=m
-CONFIG_NET_KEY_MIGRATE=y
-CONFIG_INET=y
-CONFIG_IP_MULTICAST=y
-CONFIG_IP_ADVANCED_ROUTER=y
-CONFIG_IP_ROUTE_MULTIPATH=y
-CONFIG_IP_ROUTE_VERBOSE=y
-CONFIG_IP_PNP=y
-CONFIG_IP_PNP_DHCP=y
-CONFIG_IP_PNP_BOOTP=y
-CONFIG_NET_IPIP=y
-CONFIG_IP_MROUTE=y
-CONFIG_IP_PIMSM_V1=y
-CONFIG_IP_PIMSM_V2=y
-CONFIG_SYN_COOKIES=y
-CONFIG_INET_AH=m
-CONFIG_INET_ESP=m
-CONFIG_INET_IPCOMP=m
-CONFIG_IPV6=y
-CONFIG_IPV6_PRIVACY=y
-CONFIG_IPV6_ROUTER_PREF=y
-CONFIG_IPV6_ROUTE_INFO=y
-CONFIG_IPV6_OPTIMISTIC_DAD=y
-CONFIG_INET6_AH=y
-CONFIG_INET6_ESP=y
-CONFIG_INET6_IPCOMP=y
-CONFIG_IPV6_MIP6=y
-CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION=y
-CONFIG_IPV6_TUNNEL=y
-CONFIG_IPV6_MULTIPLE_TABLES=y
-CONFIG_IPV6_SUBTREES=y
-CONFIG_IPV6_MROUTE=y
-CONFIG_IPV6_PIMSM_V2=y
-CONFIG_NETFILTER=y
-CONFIG_NF_CONNTRACK=m
-CONFIG_NF_CONNTRACK_EVENTS=y
-CONFIG_NF_CT_PROTO_UDPLITE=m
-CONFIG_NF_CONNTRACK_FTP=m
-CONFIG_NF_CONNTRACK_IRC=m
-CONFIG_NF_CONNTRACK_TFTP=m
-CONFIG_NF_CT_NETLINK=m
-CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
-CONFIG_NETFILTER_XT_TARGET_CONNMARK=m
-CONFIG_NETFILTER_XT_TARGET_MARK=m
-CONFIG_NETFILTER_XT_TARGET_NFLOG=m
-CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
-CONFIG_NETFILTER_XT_TARGET_TCPMSS=m
-CONFIG_NETFILTER_XT_MATCH_COMMENT=m
-CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m
-CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m
-CONFIG_NETFILTER_XT_MATCH_CONNMARK=m
-CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
-CONFIG_NETFILTER_XT_MATCH_DCCP=m
-CONFIG_NETFILTER_XT_MATCH_DSCP=m
-CONFIG_NETFILTER_XT_MATCH_ESP=m
-CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m
-CONFIG_NETFILTER_XT_MATCH_HELPER=m
-CONFIG_NETFILTER_XT_MATCH_IPRANGE=m
-CONFIG_NETFILTER_XT_MATCH_LENGTH=m
-CONFIG_NETFILTER_XT_MATCH_LIMIT=m
-CONFIG_NETFILTER_XT_MATCH_MAC=m
-CONFIG_NETFILTER_XT_MATCH_MARK=m
-CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
-CONFIG_NETFILTER_XT_MATCH_OWNER=m
-CONFIG_NETFILTER_XT_MATCH_POLICY=m
-CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
-CONFIG_NETFILTER_XT_MATCH_QUOTA=m
-CONFIG_NETFILTER_XT_MATCH_RATEEST=m
-CONFIG_NETFILTER_XT_MATCH_REALM=m
-CONFIG_NETFILTER_XT_MATCH_RECENT=m
-CONFIG_NETFILTER_XT_MATCH_SCTP=m
-CONFIG_NETFILTER_XT_MATCH_STATE=m
-CONFIG_NETFILTER_XT_MATCH_STATISTIC=m
-CONFIG_NETFILTER_XT_MATCH_STRING=m
-CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
-CONFIG_NETFILTER_XT_MATCH_TIME=m
-CONFIG_NETFILTER_XT_MATCH_U32=m
-CONFIG_NF_CONNTRACK_IPV4=m
-CONFIG_IP_NF_QUEUE=m
-CONFIG_IP_NF_IPTABLES=m
-CONFIG_IP_NF_MATCH_AH=m
-CONFIG_IP_NF_MATCH_ECN=m
-CONFIG_IP_NF_MATCH_TTL=m
-CONFIG_IP_NF_FILTER=m
-CONFIG_IP_NF_TARGET_REJECT=m
-CONFIG_IP_NF_TARGET_LOG=m
-CONFIG_IP_NF_TARGET_ULOG=m
-CONFIG_NF_NAT=m
-CONFIG_IP_NF_TARGET_MASQUERADE=m
-CONFIG_IP_NF_TARGET_NETMAP=m
-CONFIG_IP_NF_TARGET_REDIRECT=m
-CONFIG_NET_TCPPROBE=y
-# CONFIG_WIRELESS is not set
-CONFIG_NET_9P=y
-CONFIG_NET_9P_DEBUG=y
-CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
-CONFIG_DEVTMPFS=y
-CONFIG_MTD=y
-CONFIG_MTD_CHAR=y
-CONFIG_MTD_BLOCK=y
-CONFIG_MTD_CFI=y
-CONFIG_MTD_CFI_ADV_OPTIONS=y
-CONFIG_MTD_CFI_LE_BYTE_SWAP=y
-CONFIG_MTD_CFI_INTELEXT=y
-CONFIG_MTD_CFI_AMDSTD=y
-CONFIG_MTD_CFI_STAA=y
-CONFIG_MTD_PHYSMAP_OF=y
-CONFIG_PROC_DEVICETREE=y
-CONFIG_BLK_DEV_LOOP=y
-CONFIG_BLK_DEV_CRYPTOLOOP=y
-CONFIG_BLK_DEV_NBD=m
-CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_RAM_SIZE=65536
-CONFIG_CDROM_PKTCDVD=y
-CONFIG_MISC_DEVICES=y
-CONFIG_BLK_DEV_SD=y
-CONFIG_BLK_DEV_SR=y
-CONFIG_BLK_DEV_SR_VENDOR=y
-CONFIG_CHR_DEV_SG=y
-CONFIG_SCSI_MULTI_LUN=y
-CONFIG_SCSI_CONSTANTS=y
-CONFIG_SCSI_SPI_ATTRS=y
-CONFIG_SCSI_FC_ATTRS=y
-CONFIG_SCSI_ISCSI_ATTRS=m
-CONFIG_SCSI_SAS_ATTRS=m
-CONFIG_SCSI_SRP_ATTRS=y
-CONFIG_ATA=y
-CONFIG_SATA_AHCI=y
-CONFIG_SATA_SIL24=y
-CONFIG_SATA_MV=y
-CONFIG_SATA_SIL=y
-CONFIG_PATA_CMD64X=y
-CONFIG_PATA_MARVELL=y
-CONFIG_PATA_SIL680=y
-CONFIG_MD=y
-CONFIG_BLK_DEV_MD=y
-CONFIG_MD_LINEAR=y
-CONFIG_BLK_DEV_DM=y
-CONFIG_DM_CRYPT=y
-CONFIG_DM_SNAPSHOT=y
-CONFIG_DM_MIRROR=y
-CONFIG_DM_ZERO=y
-CONFIG_DM_UEVENT=y
-CONFIG_NETDEVICES=y
-CONFIG_TUN=y
-CONFIG_E1000E=y
-CONFIG_TIGON3=y
-# CONFIG_WLAN is not set
-# CONFIG_INPUT is not set
-# CONFIG_SERIO is not set
-# CONFIG_VT is not set
-CONFIG_DEVPTS_MULTIPLE_INSTANCES=y
-CONFIG_SERIAL_8250=y
-CONFIG_SERIAL_8250_CONSOLE=y
-CONFIG_HW_RANDOM=y
-CONFIG_RAW_DRIVER=y
-CONFIG_MAX_RAW_DEVS=1024
-# CONFIG_HWMON is not set
-# CONFIG_VGA_ARB is not set
-# CONFIG_USB_SUPPORT is not set
-CONFIG_EDAC=y
-CONFIG_EDAC_MM_EDAC=y
-CONFIG_RTC_CLASS=y
-CONFIG_RTC_DRV_DS1511=y
-CONFIG_RTC_DRV_DS1553=y
-CONFIG_EXT2_FS=y
-CONFIG_EXT2_FS_XATTR=y
-CONFIG_EXT2_FS_POSIX_ACL=y
-CONFIG_EXT2_FS_SECURITY=y
-CONFIG_EXT2_FS_XIP=y
-CONFIG_EXT3_FS=y
-# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
-CONFIG_EXT3_FS_POSIX_ACL=y
-CONFIG_EXT3_FS_SECURITY=y
-CONFIG_EXT4_FS=y
-# CONFIG_DNOTIFY is not set
-CONFIG_FUSE_FS=y
-CONFIG_ISO9660_FS=y
-CONFIG_JOLIET=y
-CONFIG_ZISOFS=y
-CONFIG_UDF_FS=m
-CONFIG_MSDOS_FS=y
-CONFIG_VFAT_FS=y
-CONFIG_PROC_KCORE=y
-CONFIG_TMPFS=y
-CONFIG_TMPFS_POSIX_ACL=y
-CONFIG_CONFIGFS_FS=m
-CONFIG_CRAMFS=y
-CONFIG_NFS_FS=y
-CONFIG_NFS_V3=y
-CONFIG_NFS_V3_ACL=y
-CONFIG_NFS_V4=y
-CONFIG_NFS_V4_1=y
-CONFIG_ROOT_NFS=y
-CONFIG_CIFS=y
-CONFIG_CIFS_WEAK_PW_HASH=y
-CONFIG_CIFS_XATTR=y
-CONFIG_CIFS_POSIX=y
-CONFIG_NLS_CODEPAGE_437=y
-CONFIG_NLS_ASCII=y
-CONFIG_NLS_ISO8859_1=y
-CONFIG_CRC_CCITT=m
-CONFIG_CRC_T10DIF=y
-CONFIG_LIBCRC32C=m
-CONFIG_PRINTK_TIME=y
-CONFIG_MAGIC_SYSRQ=y
-CONFIG_STRIP_ASM_SYMS=y
-CONFIG_DETECT_HUNG_TASK=y
-# CONFIG_SCHED_DEBUG is not set
-CONFIG_DEBUG_INFO=y
-CONFIG_FTRACE_SYSCALLS=y
-CONFIG_PPC_EMULATED_STATS=y
-CONFIG_XMON=y
-CONFIG_XMON_DEFAULT=y
-CONFIG_IRQ_DOMAIN_DEBUG=y
-CONFIG_PPC_EARLY_DEBUG=y
-CONFIG_KEYS_DEBUG_PROC_KEYS=y
-CONFIG_CRYPTO_NULL=m
-CONFIG_CRYPTO_TEST=m
-CONFIG_CRYPTO_CCM=m
-CONFIG_CRYPTO_GCM=m
-CONFIG_CRYPTO_PCBC=m
-CONFIG_CRYPTO_MICHAEL_MIC=m
-CONFIG_CRYPTO_SHA256=m
-CONFIG_CRYPTO_SHA512=m
-CONFIG_CRYPTO_TGR192=m
-CONFIG_CRYPTO_WP512=m
-CONFIG_CRYPTO_AES=m
-CONFIG_CRYPTO_ANUBIS=m
-CONFIG_CRYPTO_BLOWFISH=m
-CONFIG_CRYPTO_CAST5=m
-CONFIG_CRYPTO_CAST6=m
-CONFIG_CRYPTO_KHAZAD=m
-CONFIG_CRYPTO_SALSA20=m
-CONFIG_CRYPTO_SERPENT=m
-CONFIG_CRYPTO_TEA=m
-CONFIG_CRYPTO_TWOFISH=m
-CONFIG_CRYPTO_LZO=m
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
-CONFIG_VIRTUALIZATION=y
index f42e9baf3a4e743fddd9cf4bbc809f49aa1299d0..7c8608b096947000f7120f2a7ee729677962a094 100644 (file)
@@ -489,7 +489,6 @@ typedef struct scc_trans {
 #define FCC_GFMR_TCI           ((uint)0x20000000)
 #define FCC_GFMR_TRX           ((uint)0x10000000)
 #define FCC_GFMR_TTX           ((uint)0x08000000)
-#define FCC_GFMR_TTX           ((uint)0x08000000)
 #define FCC_GFMR_CDP           ((uint)0x04000000)
 #define FCC_GFMR_CTSP          ((uint)0x02000000)
 #define FCC_GFMR_CDS           ((uint)0x01000000)
index b76f58c124ca04c4673f1392c2f7a06960854142..fab7743c2640d565d5689f1cff06aa3dddbe0aa9 100644 (file)
@@ -254,6 +254,7 @@ void *eeh_pe_traverse(struct eeh_pe *root,
 void *eeh_pe_dev_traverse(struct eeh_pe *root,
                eeh_traverse_func fn, void *flag);
 void eeh_pe_restore_bars(struct eeh_pe *pe);
+const char *eeh_pe_loc_get(struct eeh_pe *pe);
 struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe);
 
 void *eeh_dev_init(struct device_node *dn, void *data);
index 89d5670b2eeb400ec659793fc3e960cc6d5894a3..1e551a2d6f8257f3fc78a73152ef65a3ebe24e52 100644 (file)
@@ -33,7 +33,7 @@ struct eeh_event {
 
 int eeh_event_init(void);
 int eeh_send_failure_event(struct eeh_pe *pe);
-void eeh_remove_event(struct eeh_pe *pe);
+void eeh_remove_event(struct eeh_pe *pe, bool force);
 void eeh_handle_event(struct eeh_pe *pe);
 
 #endif /* __KERNEL__ */
index 901dac6b6cb7f6bb6299c14b82158d5fc5cbcc39..d0918e09557f95e3d09f4ed41babb7d36f4e7f70 100644 (file)
@@ -223,10 +223,6 @@ typedef struct {
        unsigned int    id;
        unsigned int    active;
        unsigned long   vdso_base;
-#ifdef CONFIG_PPC_ICSWX
-       struct spinlock *cop_lockp;     /* guard cop related stuff */
-       unsigned long acop;             /* mask of enabled coprocessor types */
-#endif /* CONFIG_PPC_ICSWX */
 #ifdef CONFIG_PPC_MM_SLICES
        u64 low_slices_psize;   /* SLB page size encodings */
        u64 high_slices_psize;  /* 4 bits per slice for now */
index cb15cbb5160044233ac697d4ab0cce79fe4c06e4..460018889ba9b228c723855557ed338d1b5a6847 100644 (file)
@@ -599,9 +599,9 @@ enum {
 };
 
 struct OpalIoPhbErrorCommon {
-       uint32_t version;
-       uint32_t ioType;
-       uint32_t len;
+       __be32 version;
+       __be32 ioType;
+       __be32 len;
 };
 
 struct OpalIoP7IOCPhbErrorData {
@@ -666,64 +666,64 @@ struct OpalIoP7IOCPhbErrorData {
 struct OpalIoPhb3ErrorData {
        struct OpalIoPhbErrorCommon common;
 
-       uint32_t brdgCtl;
+       __be32 brdgCtl;
 
        /* PHB3 UTL regs */
-       uint32_t portStatusReg;
-       uint32_t rootCmplxStatus;
-       uint32_t busAgentStatus;
+       __be32 portStatusReg;
+       __be32 rootCmplxStatus;
+       __be32 busAgentStatus;
 
        /* PHB3 cfg regs */
-       uint32_t deviceStatus;
-       uint32_t slotStatus;
-       uint32_t linkStatus;
-       uint32_t devCmdStatus;
-       uint32_t devSecStatus;
+       __be32 deviceStatus;
+       __be32 slotStatus;
+       __be32 linkStatus;
+       __be32 devCmdStatus;
+       __be32 devSecStatus;
 
        /* cfg AER regs */
-       uint32_t rootErrorStatus;
-       uint32_t uncorrErrorStatus;
-       uint32_t corrErrorStatus;
-       uint32_t tlpHdr1;
-       uint32_t tlpHdr2;
-       uint32_t tlpHdr3;
-       uint32_t tlpHdr4;
-       uint32_t sourceId;
+       __be32 rootErrorStatus;
+       __be32 uncorrErrorStatus;
+       __be32 corrErrorStatus;
+       __be32 tlpHdr1;
+       __be32 tlpHdr2;
+       __be32 tlpHdr3;
+       __be32 tlpHdr4;
+       __be32 sourceId;
 
-       uint32_t rsv3;
+       __be32 rsv3;
 
        /* Record data about the call to allocate a buffer */
-       uint64_t errorClass;
-       uint64_t correlator;
+       __be64 errorClass;
+       __be64 correlator;
 
-       uint64_t nFir;                  /* 000 */
-       uint64_t nFirMask;              /* 003 */
-       uint64_t nFirWOF;               /* 008 */
+       __be64 nFir;                    /* 000 */
+       __be64 nFirMask;                /* 003 */
+       __be64 nFirWOF;         /* 008 */
 
        /* PHB3 MMIO Error Regs */
-       uint64_t phbPlssr;              /* 120 */
-       uint64_t phbCsr;                /* 110 */
-       uint64_t lemFir;                /* C00 */
-       uint64_t lemErrorMask;          /* C18 */
-       uint64_t lemWOF;                /* C40 */
-       uint64_t phbErrorStatus;        /* C80 */
-       uint64_t phbFirstErrorStatus;   /* C88 */
-       uint64_t phbErrorLog0;          /* CC0 */
-       uint64_t phbErrorLog1;          /* CC8 */
-       uint64_t mmioErrorStatus;       /* D00 */
-       uint64_t mmioFirstErrorStatus;  /* D08 */
-       uint64_t mmioErrorLog0;         /* D40 */
-       uint64_t mmioErrorLog1;         /* D48 */
-       uint64_t dma0ErrorStatus;       /* D80 */
-       uint64_t dma0FirstErrorStatus;  /* D88 */
-       uint64_t dma0ErrorLog0;         /* DC0 */
-       uint64_t dma0ErrorLog1;         /* DC8 */
-       uint64_t dma1ErrorStatus;       /* E00 */
-       uint64_t dma1FirstErrorStatus;  /* E08 */
-       uint64_t dma1ErrorLog0;         /* E40 */
-       uint64_t dma1ErrorLog1;         /* E48 */
-       uint64_t pestA[OPAL_PHB3_NUM_PEST_REGS];
-       uint64_t pestB[OPAL_PHB3_NUM_PEST_REGS];
+       __be64 phbPlssr;                /* 120 */
+       __be64 phbCsr;          /* 110 */
+       __be64 lemFir;          /* C00 */
+       __be64 lemErrorMask;            /* C18 */
+       __be64 lemWOF;          /* C40 */
+       __be64 phbErrorStatus;  /* C80 */
+       __be64 phbFirstErrorStatus;     /* C88 */
+       __be64 phbErrorLog0;            /* CC0 */
+       __be64 phbErrorLog1;            /* CC8 */
+       __be64 mmioErrorStatus; /* D00 */
+       __be64 mmioFirstErrorStatus;    /* D08 */
+       __be64 mmioErrorLog0;           /* D40 */
+       __be64 mmioErrorLog1;           /* D48 */
+       __be64 dma0ErrorStatus; /* D80 */
+       __be64 dma0FirstErrorStatus;    /* D88 */
+       __be64 dma0ErrorLog0;           /* DC0 */
+       __be64 dma0ErrorLog1;           /* DC8 */
+       __be64 dma1ErrorStatus; /* E00 */
+       __be64 dma1FirstErrorStatus;    /* E08 */
+       __be64 dma1ErrorLog0;           /* E40 */
+       __be64 dma1ErrorLog1;           /* E48 */
+       __be64 pestA[OPAL_PHB3_NUM_PEST_REGS];
+       __be64 pestB[OPAL_PHB3_NUM_PEST_REGS];
 };
 
 enum {
@@ -851,8 +851,8 @@ int64_t opal_pci_mask_pe_error(uint64_t phb_id, uint16_t pe_number, uint8_t erro
 int64_t opal_set_slot_led_status(uint64_t phb_id, uint64_t slot_id, uint8_t led_type, uint8_t led_action);
 int64_t opal_get_epow_status(__be64 *status);
 int64_t opal_set_system_attention_led(uint8_t led_action);
-int64_t opal_pci_next_error(uint64_t phb_id, uint64_t *first_frozen_pe,
-                           uint16_t *pci_error_type, uint16_t *severity);
+int64_t opal_pci_next_error(uint64_t phb_id, __be64 *first_frozen_pe,
+                           __be16 *pci_error_type, __be16 *severity);
 int64_t opal_pci_poll(uint64_t phb_id);
 int64_t opal_return_cpu(void);
 int64_t opal_reinit_cpus(uint64_t flags);
index 3d52a1132f3d30e2b007174e9cec71e7786499c0..3ba9c6f096fcfd38c517a0a2c7882994894268bb 100644 (file)
 #define TLB1_UR                        ASM_CONST(0x0000000000000002)
 #define TLB1_SR                        ASM_CONST(0x0000000000000001)
 
-#ifdef CONFIG_PPC_EARLY_DEBUG_WSP
-#define WSP_UART_PHYS  0xffc000c000
-/* This needs to be careful chosen to hit a !0 congruence class
- * in the TLB since we bolt it in way 3, which is already occupied
- * by our linear mapping primary bolted entry in CC 0.
- */
-#define WSP_UART_VIRT  0xf000000000001000
-#endif
-
 /* A2 erativax attributes definitions */
 #define ERATIVAX_RS_IS_ALL             0x000
 #define ERATIVAX_RS_IS_TID             0x040
index 0e83e7d8c73f5d0845690928ce24f179f69e22b8..58abeda64cb7afa271078497f25e3ae5a8a0e26c 100644 (file)
@@ -16,13 +16,15 @@ struct thread_struct;
 extern struct task_struct *_switch(struct thread_struct *prev,
                                   struct thread_struct *next);
 #ifdef CONFIG_PPC_BOOK3S_64
-static inline void save_tar(struct thread_struct *prev)
+static inline void save_early_sprs(struct thread_struct *prev)
 {
        if (cpu_has_feature(CPU_FTR_ARCH_207S))
                prev->tar = mfspr(SPRN_TAR);
+       if (cpu_has_feature(CPU_FTR_DSCR))
+               prev->dscr = mfspr(SPRN_DSCR);
 }
 #else
-static inline void save_tar(struct thread_struct *prev) {}
+static inline void save_early_sprs(struct thread_struct *prev) {}
 #endif
 
 extern void enable_kernel_fp(void);
@@ -84,6 +86,8 @@ static inline void clear_task_ebb(struct task_struct *t)
 {
 #ifdef CONFIG_PPC_BOOK3S_64
     /* EBB perf events are not inherited, so clear all EBB state. */
+    t->thread.ebbrr = 0;
+    t->thread.ebbhr = 0;
     t->thread.bescr = 0;
     t->thread.mmcr2 = 0;
     t->thread.mmcr0 = 0;
diff --git a/arch/powerpc/include/asm/wsp.h b/arch/powerpc/include/asm/wsp.h
deleted file mode 100644 (file)
index c7dc830..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- *  Copyright 2011 Michael Ellerman, IBM Corp.
- *
- *  This program is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU General Public License
- *  as published by the Free Software Foundation; either version
- *  2 of the License, or (at your option) any later version.
- */
-#ifndef __ASM_POWERPC_WSP_H
-#define __ASM_POWERPC_WSP_H
-
-extern int wsp_get_chip_id(struct device_node *dn);
-
-#endif /* __ASM_POWERPC_WSP_H */
index 5b7657959faad198796fdc868f03f66eaf9db854..de2c0e4ee1aab1c13d0ac60d0d2300849665c48c 100644 (file)
@@ -41,5 +41,6 @@
 #define PPC_FEATURE2_EBB               0x10000000
 #define PPC_FEATURE2_ISEL              0x08000000
 #define PPC_FEATURE2_TAR               0x04000000
+#define PPC_FEATURE2_VEC_CRYPTO                0x02000000
 
 #endif /* _UAPI__ASM_POWERPC_CPUTABLE_H */
index fab19ec25597c0a8ac95732b42354a8f58a335a4..670c312d914e64508ddcf8599d639663504603f6 100644 (file)
@@ -43,7 +43,6 @@ obj-$(CONFIG_PPC_BOOK3S_64)   += cpu_setup_power.o
 obj-$(CONFIG_PPC_BOOK3S_64)    += mce.o mce_power.o
 obj64-$(CONFIG_RELOCATABLE)    += reloc_64.o
 obj-$(CONFIG_PPC_BOOK3E_64)    += exceptions-64e.o idle_book3e.o
-obj-$(CONFIG_PPC_A2)           += cpu_setup_a2.o
 obj-$(CONFIG_PPC64)            += vdso64/
 obj-$(CONFIG_ALTIVEC)          += vecemu.o
 obj-$(CONFIG_PPC_970_NAP)      += idle_power4.o
diff --git a/arch/powerpc/kernel/cpu_setup_a2.S b/arch/powerpc/kernel/cpu_setup_a2.S
deleted file mode 100644 (file)
index 61f079e..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- *  A2 specific assembly support code
- *
- *  Copyright 2009 Ben Herrenschmidt, IBM Corp.
- *
- *  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 <asm/asm-offsets.h>
-#include <asm/ppc_asm.h>
-#include <asm/ppc-opcode.h>
-#include <asm/processor.h>
-#include <asm/reg_a2.h>
-#include <asm/reg.h>
-#include <asm/thread_info.h>
-
-/*
- * Disable thdid and class fields in ERATs to bump PID to full 14 bits capacity.
- * This also prevents external LPID accesses but that isn't a problem when not a
- * guest. Under PV, this setting will be ignored and MMUCR will return the right
- * number of PID bits we can use.
- */
-#define MMUCR1_EXTEND_PID \
-       (MMUCR1_ICTID | MMUCR1_ITTID | MMUCR1_DCTID | \
-        MMUCR1_DTTID | MMUCR1_DCCD)
-
-/*
- * Use extended PIDs if enabled.
- * Don't clear the ERATs on context sync events and enable I & D LRU.
- * Enable ERAT back invalidate when tlbwe overwrites an entry.
- */
-#define INITIAL_MMUCR1 \
-       (MMUCR1_EXTEND_PID | MMUCR1_CSINV_NEVER | MMUCR1_IRRE | \
-        MMUCR1_DRRE | MMUCR1_TLBWE_BINV)
-
-_GLOBAL(__setup_cpu_a2)
-       /* Some of these are actually thread local and some are
-        * core local but doing it always won't hurt
-        */
-
-#ifdef CONFIG_PPC_ICSWX
-       /* Make sure ACOP starts out as zero */
-       li      r3,0
-       mtspr   SPRN_ACOP,r3
-
-       /* Skip the following if we are in Guest mode */
-       mfmsr   r3
-       andis.  r0,r3,MSR_GS@h
-       bne     _icswx_skip_guest
-
-       /* Enable icswx instruction */
-       mfspr   r3,SPRN_A2_CCR2
-       ori     r3,r3,A2_CCR2_ENABLE_ICSWX
-       mtspr   SPRN_A2_CCR2,r3
-
-       /* Unmask all CTs in HACOP */
-       li      r3,-1
-       mtspr   SPRN_HACOP,r3
-_icswx_skip_guest:
-#endif /* CONFIG_PPC_ICSWX */
-
-       /* Enable doorbell */
-       mfspr   r3,SPRN_A2_CCR2
-       oris     r3,r3,A2_CCR2_ENABLE_PC@h
-       mtspr   SPRN_A2_CCR2,r3
-       isync
-
-       /* Setup CCR0 to disable power saving for now as it's busted
-        * in the current implementations. Setup CCR1 to wake on
-        * interrupts normally (we write the default value but who
-        * knows what FW may have clobbered...)
-        */
-       li      r3,0
-       mtspr   SPRN_A2_CCR0, r3
-       LOAD_REG_IMMEDIATE(r3,0x0f0f0f0f)
-       mtspr   SPRN_A2_CCR1, r3
-
-       /* Initialise MMUCR1 */
-       lis     r3,INITIAL_MMUCR1@h
-       ori     r3,r3,INITIAL_MMUCR1@l
-       mtspr   SPRN_MMUCR1,r3
-
-       /* Set MMUCR2 to enable 4K, 64K, 1M, 16M and 1G pages */
-       LOAD_REG_IMMEDIATE(r3, 0x000a7531)
-       mtspr   SPRN_MMUCR2,r3
-
-       /* Set MMUCR3 to write all thids bit to the TLB */
-       LOAD_REG_IMMEDIATE(r3, 0x0000000f)
-       mtspr   SPRN_MMUCR3,r3
-
-       /* Don't do ERAT stuff if running guest mode */
-       mfmsr   r3
-       andis.  r0,r3,MSR_GS@h
-       bne     1f
-
-       /* Now set the I-ERAT watermark to 15 */
-       lis     r4,(MMUCR0_TLBSEL_I|MMUCR0_ECL)@h
-       mtspr   SPRN_MMUCR0, r4
-       li      r4,A2_IERAT_SIZE-1
-       PPC_ERATWE(R4,R4,3)
-
-       /* Now set the D-ERAT watermark to 31 */
-       lis     r4,(MMUCR0_TLBSEL_D|MMUCR0_ECL)@h
-       mtspr   SPRN_MMUCR0, r4
-       li      r4,A2_DERAT_SIZE-1
-       PPC_ERATWE(R4,R4,3)
-
-       /* And invalidate the beast just in case. That won't get rid of
-        * a bolted entry though it will be in LRU and so will go away eventually
-        * but let's not bother for now
-        */
-       PPC_ERATILX(0,0,R0)
-1:
-       blr
-
-_GLOBAL(__restore_cpu_a2)
-       b       __setup_cpu_a2
index 1557e7c2c7e15bc45120eedde3d288792676f235..46733535cc0b482a1ea35fe8411c556fb26fcb3b 100644 (file)
@@ -56,6 +56,7 @@ _GLOBAL(__setup_cpu_power8)
        li      r0,0
        mtspr   SPRN_LPID,r0
        mfspr   r3,SPRN_LPCR
+       ori     r3, r3, LPCR_PECEDH
        bl      __init_LPCR
        bl      __init_HFSCR
        bl      __init_tlb_power8
@@ -74,6 +75,7 @@ _GLOBAL(__restore_cpu_power8)
        li      r0,0
        mtspr   SPRN_LPID,r0
        mfspr   r3,SPRN_LPCR
+       ori     r3, r3, LPCR_PECEDH
        bl      __init_LPCR
        bl      __init_HFSCR
        bl      __init_tlb_power8
index c1faade6506dd581e3baf2aebfd93c7972fbd6dc..965291b4c2fa15a9f6f50cd8ca8b1e3a3067db02 100644 (file)
@@ -109,7 +109,8 @@ extern void __restore_cpu_e6500(void);
                                 PPC_FEATURE_PSERIES_PERFMON_COMPAT)
 #define COMMON_USER2_POWER8    (PPC_FEATURE2_ARCH_2_07 | \
                                 PPC_FEATURE2_HTM_COMP | PPC_FEATURE2_DSCR | \
-                                PPC_FEATURE2_ISEL | PPC_FEATURE2_TAR)
+                                PPC_FEATURE2_ISEL | PPC_FEATURE2_TAR | \
+                                PPC_FEATURE2_VEC_CRYPTO)
 #define COMMON_USER_PA6T       (COMMON_USER_PPC64 | PPC_FEATURE_PA6T |\
                                 PPC_FEATURE_TRUE_LE | \
                                 PPC_FEATURE_HAS_ALTIVEC_COMP)
@@ -2148,44 +2149,6 @@ static struct cpu_spec __initdata cpu_specs[] = {
        }
 #endif /* CONFIG_PPC32 */
 #endif /* CONFIG_E500 */
-
-#ifdef CONFIG_PPC_A2
-       {       /* Standard A2 (>= DD2) + FPU core */
-               .pvr_mask               = 0xffff0000,
-               .pvr_value              = 0x00480000,
-               .cpu_name               = "A2 (>= DD2)",
-               .cpu_features           = CPU_FTRS_A2,
-               .cpu_user_features      = COMMON_USER_PPC64,
-               .mmu_features           = MMU_FTRS_A2,
-               .icache_bsize           = 64,
-               .dcache_bsize           = 64,
-               .num_pmcs               = 0,
-               .cpu_setup              = __setup_cpu_a2,
-               .cpu_restore            = __restore_cpu_a2,
-               .machine_check          = machine_check_generic,
-               .platform               = "ppca2",
-       },
-       {       /* This is a default entry to get going, to be replaced by
-                * a real one at some stage
-                */
-#define CPU_FTRS_BASE_BOOK3E   (CPU_FTR_USE_TB | \
-           CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_SMT | \
-           CPU_FTR_NODSISRALIGN | CPU_FTR_NOEXECUTE)
-               .pvr_mask               = 0x00000000,
-               .pvr_value              = 0x00000000,
-               .cpu_name               = "Book3E",
-               .cpu_features           = CPU_FTRS_BASE_BOOK3E,
-               .cpu_user_features      = COMMON_USER_PPC64,
-               .mmu_features           = MMU_FTR_TYPE_3E | MMU_FTR_USE_TLBILX |
-                                         MMU_FTR_USE_TLBIVAX_BCAST |
-                                         MMU_FTR_LOCK_BCAST_INVAL,
-               .icache_bsize           = 64,
-               .dcache_bsize           = 64,
-               .num_pmcs               = 0,
-               .machine_check          = machine_check_generic,
-               .platform               = "power6",
-       },
-#endif /* CONFIG_PPC_A2 */
 };
 
 static struct cpu_spec the_cpu_spec;
index 7051ea3101b96af830faf0e44eadeb70884fb6fc..86e25702aaca8894701ff585b87ee08caa017199 100644 (file)
@@ -330,8 +330,8 @@ static int eeh_phb_check_failure(struct eeh_pe *pe)
        eeh_pe_state_mark(phb_pe, EEH_PE_ISOLATED);
        eeh_serialize_unlock(flags);
 
-       pr_err("EEH: PHB#%x failure detected\n",
-               phb_pe->phb->global_number);
+       pr_err("EEH: PHB#%x failure detected, location: %s\n",
+               phb_pe->phb->global_number, eeh_pe_loc_get(phb_pe));
        dump_stack();
        eeh_send_failure_event(phb_pe);
 
@@ -358,10 +358,11 @@ static int eeh_phb_check_failure(struct eeh_pe *pe)
 int eeh_dev_check_failure(struct eeh_dev *edev)
 {
        int ret;
+       int active_flags = (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE);
        unsigned long flags;
        struct device_node *dn;
        struct pci_dev *dev;
-       struct eeh_pe *pe;
+       struct eeh_pe *pe, *parent_pe, *phb_pe;
        int rc = 0;
        const char *location;
 
@@ -439,14 +440,34 @@ int eeh_dev_check_failure(struct eeh_dev *edev)
         */
        if ((ret < 0) ||
            (ret == EEH_STATE_NOT_SUPPORT) ||
-           (ret & (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) ==
-           (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) {
+           ((ret & active_flags) == active_flags)) {
                eeh_stats.false_positives++;
                pe->false_positives++;
                rc = 0;
                goto dn_unlock;
        }
 
+       /*
+        * It should be corner case that the parent PE has been
+        * put into frozen state as well. We should take care
+        * that at first.
+        */
+       parent_pe = pe->parent;
+       while (parent_pe) {
+               /* Hit the ceiling ? */
+               if (parent_pe->type & EEH_PE_PHB)
+                       break;
+
+               /* Frozen parent PE ? */
+               ret = eeh_ops->get_state(parent_pe, NULL);
+               if (ret > 0 &&
+                   (ret & active_flags) != active_flags)
+                       pe = parent_pe;
+
+               /* Next parent level */
+               parent_pe = parent_pe->parent;
+       }
+
        eeh_stats.slot_resets++;
 
        /* Avoid repeated reports of this failure, including problems
@@ -460,8 +481,11 @@ int eeh_dev_check_failure(struct eeh_dev *edev)
         * a stack trace will help the device-driver authors figure
         * out what happened.  So print that out.
         */
-       pr_err("EEH: Frozen PE#%x detected on PHB#%x\n",
-               pe->addr, pe->phb->global_number);
+       phb_pe = eeh_phb_pe_get(pe->phb);
+       pr_err("EEH: Frozen PHB#%x-PE#%x detected\n",
+              pe->phb->global_number, pe->addr);
+       pr_err("EEH: PE location: %s, PHB location: %s\n",
+              eeh_pe_loc_get(pe), eeh_pe_loc_get(phb_pe));
        dump_stack();
 
        eeh_send_failure_event(pe);
index 7100a5b96e7059caf572609dec9d7f9393f0106f..420da61d4ce001d74ba64b675f8615b65f6e812a 100644 (file)
@@ -447,8 +447,9 @@ static void *eeh_pe_detach_dev(void *data, void *userdata)
  * PE reset (for 3 times), we try to clear the frozen state
  * for 3 times as well.
  */
-static int eeh_clear_pe_frozen_state(struct eeh_pe *pe)
+static void *__eeh_clear_pe_frozen_state(void *data, void *flag)
 {
+       struct eeh_pe *pe = (struct eeh_pe *)data;
        int i, rc;
 
        for (i = 0; i < 3; i++) {
@@ -461,13 +462,24 @@ static int eeh_clear_pe_frozen_state(struct eeh_pe *pe)
        }
 
        /* The PE has been isolated, clear it */
-       if (rc)
+       if (rc) {
                pr_warn("%s: Can't clear frozen PHB#%x-PE#%x (%d)\n",
                        __func__, pe->phb->global_number, pe->addr, rc);
-       else
+               return (void *)pe;
+       }
+
+       return NULL;
+}
+
+static int eeh_clear_pe_frozen_state(struct eeh_pe *pe)
+{
+       void *rc;
+
+       rc = eeh_pe_traverse(pe, __eeh_clear_pe_frozen_state, NULL);
+       if (!rc)
                eeh_pe_state_clear(pe, EEH_PE_ISOLATED);
 
-       return rc;
+       return rc ? -EIO : 0;
 }
 
 /**
@@ -758,7 +770,7 @@ static void eeh_handle_special_event(void)
                        eeh_serialize_lock(&flags);
 
                        /* Purge all events */
-                       eeh_remove_event(NULL);
+                       eeh_remove_event(NULL, true);
 
                        list_for_each_entry(hose, &hose_list, list_node) {
                                phb_pe = eeh_phb_pe_get(hose);
@@ -777,7 +789,7 @@ static void eeh_handle_special_event(void)
                        eeh_serialize_lock(&flags);
 
                        /* Purge all events of the PHB */
-                       eeh_remove_event(pe);
+                       eeh_remove_event(pe, true);
 
                        if (rc == EEH_NEXT_ERR_DEAD_PHB)
                                eeh_pe_state_mark(pe, EEH_PE_ISOLATED);
index 72d748b56c86b2b9ae960e49b819dfedef36f61a..4eefb6e34dbb2f6edbf4990349e9c11b0b5d07f8 100644 (file)
@@ -152,24 +152,33 @@ int eeh_send_failure_event(struct eeh_pe *pe)
 /**
  * eeh_remove_event - Remove EEH event from the queue
  * @pe: Event binding to the PE
+ * @force: Event will be removed unconditionally
  *
  * On PowerNV platform, we might have subsequent coming events
  * is part of the former one. For that case, those subsequent
  * coming events are totally duplicated and unnecessary, thus
  * they should be removed.
  */
-void eeh_remove_event(struct eeh_pe *pe)
+void eeh_remove_event(struct eeh_pe *pe, bool force)
 {
        unsigned long flags;
        struct eeh_event *event, *tmp;
 
+       /*
+        * If we have NULL PE passed in, we have dead IOC
+        * or we're sure we can report all existing errors
+        * by the caller.
+        *
+        * With "force", the event with associated PE that
+        * have been isolated, the event won't be removed
+        * to avoid event lost.
+        */
        spin_lock_irqsave(&eeh_eventlist_lock, flags);
        list_for_each_entry_safe(event, tmp, &eeh_eventlist, list) {
-               /*
-                * If we don't have valid PE passed in, that means
-                * we already have event corresponding to dead IOC
-                * and all events should be purged.
-                */
+               if (!force && event->pe &&
+                   (event->pe->state & EEH_PE_ISOLATED))
+                       continue;
+
                if (!pe) {
                        list_del(&event->list);
                        kfree(event);
index 995c2a2846303d3885674d474bcbfd0cae90f9cc..fbd01eba44734651d7a9e324b9fca1f733107ff7 100644 (file)
@@ -791,6 +791,66 @@ void eeh_pe_restore_bars(struct eeh_pe *pe)
        eeh_pe_dev_traverse(pe, eeh_restore_one_device_bars, NULL);
 }
 
+/**
+ * eeh_pe_loc_get - Retrieve location code binding to the given PE
+ * @pe: EEH PE
+ *
+ * Retrieve the location code of the given PE. If the primary PE bus
+ * is root bus, we will grab location code from PHB device tree node
+ * or root port. Otherwise, the upstream bridge's device tree node
+ * of the primary PE bus will be checked for the location code.
+ */
+const char *eeh_pe_loc_get(struct eeh_pe *pe)
+{
+       struct pci_controller *hose;
+       struct pci_bus *bus = eeh_pe_bus_get(pe);
+       struct pci_dev *pdev;
+       struct device_node *dn;
+       const char *loc;
+
+       if (!bus)
+               return "N/A";
+
+       /* PHB PE or root PE ? */
+       if (pci_is_root_bus(bus)) {
+               hose = pci_bus_to_host(bus);
+               loc = of_get_property(hose->dn,
+                               "ibm,loc-code", NULL);
+               if (loc)
+                       return loc;
+               loc = of_get_property(hose->dn,
+                               "ibm,io-base-loc-code", NULL);
+               if (loc)
+                       return loc;
+
+               pdev = pci_get_slot(bus, 0x0);
+       } else {
+               pdev = bus->self;
+       }
+
+       if (!pdev) {
+               loc = "N/A";
+               goto out;
+       }
+
+       dn = pci_device_to_OF_node(pdev);
+       if (!dn) {
+               loc = "N/A";
+               goto out;
+       }
+
+       loc = of_get_property(dn, "ibm,loc-code", NULL);
+       if (!loc)
+               loc = of_get_property(dn, "ibm,slot-location-code", NULL);
+       if (!loc)
+               loc = "N/A";
+
+out:
+       if (pci_is_root_bus(bus) && pdev)
+               pci_dev_put(pdev);
+       return loc;
+}
+
 /**
  * eeh_pe_bus_get - Retrieve PCI bus according to the given PE
  * @pe: EEH PE
index 911d45366f596c764a4ad001025760de4f07c5e8..6528c5e2cc44800a201802858c23d34e92162041 100644 (file)
@@ -428,12 +428,6 @@ BEGIN_FTR_SECTION
        std     r24,THREAD_VRSAVE(r3)
 END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
 #endif /* CONFIG_ALTIVEC */
-#ifdef CONFIG_PPC64
-BEGIN_FTR_SECTION
-       mfspr   r25,SPRN_DSCR
-       std     r25,THREAD_DSCR(r3)
-END_FTR_SECTION_IFSET(CPU_FTR_DSCR)
-#endif
        and.    r0,r0,r22
        beq+    1f
        andc    r22,r22,r0
index 771b4e92e5d9c091fcedfa1dc4982fa6950c1405..bb9cac6c8051ab8a10467a099e8fa998b1f7d181 100644 (file)
@@ -1467,22 +1467,6 @@ a2_tlbinit_after_linear_map:
        .globl  a2_tlbinit_after_iprot_flush
 a2_tlbinit_after_iprot_flush:
 
-#ifdef CONFIG_PPC_EARLY_DEBUG_WSP
-       /* Now establish early debug mappings if applicable */
-       /* Restore the MAS0 we used for linear mapping load */
-       mtspr   SPRN_MAS0,r11
-
-       lis     r3,(MAS1_VALID | MAS1_IPROT)@h
-       ori     r3,r3,(BOOK3E_PAGESZ_4K << MAS1_TSIZE_SHIFT)
-       mtspr   SPRN_MAS1,r3
-       LOAD_REG_IMMEDIATE(r3, WSP_UART_VIRT | MAS2_I | MAS2_G)
-       mtspr   SPRN_MAS2,r3
-       LOAD_REG_IMMEDIATE(r3, WSP_UART_PHYS | MAS3_SR | MAS3_SW)
-       mtspr   SPRN_MAS7_MAS3,r3
-       /* re-use the MAS8 value from the linear mapping */
-       tlbwe
-#endif /* CONFIG_PPC_EARLY_DEBUG_WSP */
-
        PPC_TLBILX(0,0,R0)
        sync
        isync
index 20f11eb4dff7ee4e308565ec0910f57b5bd189e6..a7d36b19221d4710eb3e35db66b43fd5055e4c92 100644 (file)
@@ -439,9 +439,9 @@ BEGIN_FTR_SECTION
         * R9           = CR
         * Original R9 to R13 is saved on PACA_EXMC
         *
-        * Switch to mc_emergency stack and handle re-entrancy (though we
-        * currently don't test for overflow). Save MCE registers srr1,
-        * srr0, dar and dsisr and then set ME=1
+        * Switch to mc_emergency stack and handle re-entrancy (we limit
+        * the nested MCE upto level 4 to avoid stack overflow).
+        * Save MCE registers srr1, srr0, dar and dsisr and then set ME=1
         *
         * We use paca->in_mce to check whether this is the first entry or
         * nested machine check. We increment paca->in_mce to track nested
@@ -464,6 +464,9 @@ BEGIN_FTR_SECTION
 0:     subi    r1,r1,INT_FRAME_SIZE    /* alloc stack frame */
        addi    r10,r10,1               /* increment paca->in_mce */
        sth     r10,PACA_IN_MCE(r13)
+       /* Limit nested MCE to level 4 to avoid stack overflow */
+       cmpwi   r10,4
+       bgt     2f                      /* Check if we hit limit of 4 */
        std     r11,GPR1(r1)            /* Save r1 on the stack. */
        std     r11,0(r1)               /* make stack chain pointer */
        mfspr   r11,SPRN_SRR0           /* Save SRR0 */
@@ -482,10 +485,23 @@ BEGIN_FTR_SECTION
        ori     r11,r11,MSR_RI          /* turn on RI bit */
        ld      r12,PACAKBASE(r13)      /* get high part of &label */
        LOAD_HANDLER(r12, machine_check_handle_early)
-       mtspr   SPRN_SRR0,r12
+1:     mtspr   SPRN_SRR0,r12
        mtspr   SPRN_SRR1,r11
        rfid
        b       .       /* prevent speculative execution */
+2:
+       /* Stack overflow. Stay on emergency stack and panic.
+        * Keep the ME bit off while panic-ing, so that if we hit
+        * another machine check we checkstop.
+        */
+       addi    r1,r1,INT_FRAME_SIZE    /* go back to previous stack frame */
+       ld      r11,PACAKMSR(r13)
+       ld      r12,PACAKBASE(r13)
+       LOAD_HANDLER(r12, unrecover_mce)
+       li      r10,MSR_ME
+       andc    r11,r11,r10             /* Turn off MSR_ME */
+       b       1b
+       b       .       /* prevent speculative execution */
 END_FTR_SECTION_IFSET(CPU_FTR_HVMODE)
 
 machine_check_pSeries:
@@ -1389,6 +1405,7 @@ machine_check_handle_early:
        bl      save_nvgprs
        addi    r3,r1,STACK_FRAME_OVERHEAD
        bl      machine_check_early
+       std     r3,RESULT(r1)   /* Save result */
        ld      r12,_MSR(r1)
 #ifdef CONFIG_PPC_P7_NAP
        /*
@@ -1443,10 +1460,32 @@ machine_check_handle_early:
         */
        andi.   r11,r12,MSR_RI
        bne     2f
-1:     addi    r3,r1,STACK_FRAME_OVERHEAD
-       bl      unrecoverable_exception
-       b       1b
+1:     mfspr   r11,SPRN_SRR0
+       ld      r10,PACAKBASE(r13)
+       LOAD_HANDLER(r10,unrecover_mce)
+       mtspr   SPRN_SRR0,r10
+       ld      r10,PACAKMSR(r13)
+       /*
+        * We are going down. But there are chances that we might get hit by
+        * another MCE during panic path and we may run into unstable state
+        * with no way out. Hence, turn ME bit off while going down, so that
+        * when another MCE is hit during panic path, system will checkstop
+        * and hypervisor will get restarted cleanly by SP.
+        */
+       li      r3,MSR_ME
+       andc    r10,r10,r3              /* Turn off MSR_ME */
+       mtspr   SPRN_SRR1,r10
+       rfid
+       b       .
 2:
+       /*
+        * Check if we have successfully handled/recovered from error, if not
+        * then stay on emergency stack and panic.
+        */
+       ld      r3,RESULT(r1)   /* Load result */
+       cmpdi   r3,0            /* see if we handled MCE successfully */
+
+       beq     1b              /* if !handled then panic */
        /*
         * Return from MC interrupt.
         * Queue up the MCE event so that we can log it later, while
@@ -1460,6 +1499,17 @@ machine_check_handle_early:
        MACHINE_CHECK_HANDLER_WINDUP
        b       machine_check_pSeries
 
+unrecover_mce:
+       /* Invoke machine_check_exception to print MCE event and panic. */
+       addi    r3,r1,STACK_FRAME_OVERHEAD
+       bl      machine_check_exception
+       /*
+        * We will not reach here. Even if we did, there is no way out. Call
+        * unrecoverable_exception and die.
+        */
+1:     addi    r3,r1,STACK_FRAME_OVERHEAD
+       bl      unrecoverable_exception
+       b       1b
 /*
  * r13 points to the PACA, r9 contains the saved CR,
  * r12 contain the saved SRR1, SRR0 is still ready for return
index 67ee0d6c1070b3f02702dbc8442a905f3b39b8d6..7d7d8635227ac76bcd054b3fd04248ec90127c9d 100644 (file)
@@ -930,25 +930,6 @@ initial_mmu:
        tlbwe   r4,r0,TLB_DATA          /* Load the data portion of the entry */
        tlbwe   r3,r0,TLB_TAG           /* Load the tag portion of the entry */
 
-#if defined(CONFIG_SERIAL_TEXT_DEBUG) && defined(SERIAL_DEBUG_IO_BASE)
-
-       /* Load a TLB entry for the UART, so that ppc4xx_progress() can use
-        * the UARTs nice and early.  We use a 4k real==virtual mapping. */
-
-       lis     r3,SERIAL_DEBUG_IO_BASE@h
-       ori     r3,r3,SERIAL_DEBUG_IO_BASE@l
-       mr      r4,r3
-       clrrwi  r4,r4,12
-       ori     r4,r4,(TLB_WR|TLB_I|TLB_M|TLB_G)
-
-       clrrwi  r3,r3,12
-       ori     r3,r3,(TLB_VALID | TLB_PAGESZ(PAGESZ_4K))
-
-       li      r0,0                    /* TLB slot 0 */
-       tlbwe   r4,r0,TLB_DATA
-       tlbwe   r3,r0,TLB_TAG
-#endif /* CONFIG_SERIAL_DEBUG_TEXT && SERIAL_DEBUG_IO_BASE */
-
        isync
 
        /* Establish the exception vector base
index 8a1edbe26b8f34c99beafc8a1d3bff68ec99f765..be99774d3f44ac9ecc83d5e637a7bb4b4f460d80 100644 (file)
@@ -755,15 +755,15 @@ struct task_struct *__switch_to(struct task_struct *prev,
 
        WARN_ON(!irqs_disabled());
 
-       /* Back up the TAR across context switches.
+       /* Back up the TAR and DSCR across context switches.
         * Note that the TAR is not available for use in the kernel.  (To
         * provide this, the TAR should be backed up/restored on exception
         * entry/exit instead, and be in pt_regs.  FIXME, this should be in
         * pt_regs anyway (for debug).)
-        * Save the TAR here before we do treclaim/trecheckpoint as these
-        * will change the TAR.
+        * Save the TAR and DSCR here before we do treclaim/trecheckpoint as
+        * these will change them.
         */
-       save_tar(&prev->thread);
+       save_early_sprs(&prev->thread);
 
        __switch_to_tm(prev);
 
index d4d418376f994575f598ee7f9bab58d1db2449ce..e239df3768acfb3e14c55367c7ab737233f50771 100644 (file)
@@ -471,7 +471,7 @@ void __init smp_setup_cpu_maps(void)
                for (j = 0; j < nthreads && cpu < nr_cpu_ids; j++) {
                        DBG("    thread %d -> cpu %d (hard id %d)\n",
                            j, cpu, be32_to_cpu(intserv[j]));
-                       set_cpu_present(cpu, true);
+                       set_cpu_present(cpu, of_device_is_available(dn));
                        set_hard_smp_processor_id(cpu, be32_to_cpu(intserv[j]));
                        set_cpu_possible(cpu, true);
                        cpu++;
index 7753af2d261381bcc21bf3e21f8ee4f4880847e9..51a3ff78838aaf1eb6726e92cb871c128221eb2e 100644 (file)
@@ -749,7 +749,7 @@ int setup_profiling_timer(unsigned int multiplier)
 /* cpumask of CPUs with asymetric SMT dependancy */
 static const int powerpc_smt_flags(void)
 {
-       int flags = SD_SHARE_CPUPOWER | SD_SHARE_PKG_RESOURCES;
+       int flags = SD_SHARE_CPUCAPACITY | SD_SHARE_PKG_RESOURCES;
 
        if (cpu_has_feature(CPU_FTR_ASYM_SMT)) {
                printk_once(KERN_INFO "Enabling Asymmetric SMT scheduling\n");
index 7e711bdcc6da5adb399e8ac0a55097e76317fe51..9fff9cdcc5196c3a20b9129d7e407c1dbc113cb7 100644 (file)
@@ -551,7 +551,7 @@ void timer_interrupt(struct pt_regs * regs)
        may_hard_irq_enable();
 
 
-#if defined(CONFIG_PPC32) && defined(CONFIG_PMAC)
+#if defined(CONFIG_PPC32) && defined(CONFIG_PPC_PMAC)
        if (atomic_read(&ppc_n_lost_interrupts) != 0)
                do_IRQ(regs);
 #endif
index 1bd7ca298fa1b132167a47bbf310ed0ef6a0db0b..239f1cde3fff161b8f4b9cfc56beff61d9143845 100644 (file)
@@ -295,6 +295,8 @@ long machine_check_early(struct pt_regs *regs)
 {
        long handled = 0;
 
+       __get_cpu_var(irq_stat).mce_exceptions++;
+
        if (cur_cpu_spec && cur_cpu_spec->machine_check_early)
                handled = cur_cpu_spec->machine_check_early(regs);
        return handled;
index a15837519dca45474a141fc6328eee130bafd779..b7aa07279a63b1fb5afeb26061631ed74c84b0e3 100644 (file)
@@ -62,8 +62,6 @@ void __init udbg_early_init(void)
        udbg_init_cpm();
 #elif defined(CONFIG_PPC_EARLY_DEBUG_USBGECKO)
        udbg_init_usbgecko();
-#elif defined(CONFIG_PPC_EARLY_DEBUG_WSP)
-       udbg_init_wsp();
 #elif defined(CONFIG_PPC_EARLY_DEBUG_MEMCONS)
        /* In memory console */
        udbg_init_memcons();
index 75702e207b2986c4741cf243c2a49c955c30e05b..6e7c4923b5ea1bb24427bc47360576900bc44fe8 100644 (file)
@@ -296,14 +296,3 @@ void __init udbg_init_40x_realmode(void)
 }
 
 #endif /* CONFIG_PPC_EARLY_DEBUG_40x */
-
-
-#ifdef CONFIG_PPC_EARLY_DEBUG_WSP
-
-void __init udbg_init_wsp(void)
-{
-       udbg_uart_init_mmio((void *)WSP_UART_VIRT, 1);
-       udbg_uart_setup(57600, 50000000);
-}
-
-#endif /* CONFIG_PPC_EARLY_DEBUG_WSP */
index 768a9f977c001ee73e6d5aa2f0b8ee44ea3b35f5..3a5c568b1e89f43210c9cd2ee0ec9e9d70c62e24 100644 (file)
@@ -113,10 +113,8 @@ static long kvmppc_realmode_mc_power7(struct kvm_vcpu *vcpu)
         * We assume that if the condition is recovered then linux host
         * will have generated an error log event that we will pick
         * up and log later.
-        * Don't release mce event now. In case if condition is not
-        * recovered we do guest exit and go back to linux host machine
-        * check handler. Hence we need make sure that current mce event
-        * is available for linux host to consume.
+        * Don't release mce event now. We will queue up the event so that
+        * we can log the MCE event info on host console.
         */
        if (!get_mce_event(&mce_evt, MCE_EVENT_DONTRELEASE))
                goto out;
@@ -128,11 +126,12 @@ static long kvmppc_realmode_mc_power7(struct kvm_vcpu *vcpu)
 
 out:
        /*
-        * If we have handled the error, then release the mce event because
-        * we will be delivering machine check to guest.
+        * We are now going enter guest either through machine check
+        * interrupt (for unhandled errors) or will continue from
+        * current HSRR0 (for handled errors) in guest. Hence
+        * queue up the event so that we can log it from host console later.
         */
-       if (handled)
-               release_mce_event();
+       machine_check_queue_event();
 
        return handled;
 }
index 77356fd25ccc96a8b4fd17746ea08edc0da16060..868347ef09fd48bcf8bfd343becb49f6898887c5 100644 (file)
@@ -2257,15 +2257,28 @@ machine_check_realmode:
        mr      r3, r9          /* get vcpu pointer */
        bl      kvmppc_realmode_machine_check
        nop
-       cmpdi   r3, 0           /* continue exiting from guest? */
+       cmpdi   r3, 0           /* Did we handle MCE ? */
        ld      r9, HSTATE_KVM_VCPU(r13)
        li      r12, BOOK3S_INTERRUPT_MACHINE_CHECK
-       beq     mc_cont
+       /*
+        * Deliver unhandled/fatal (e.g. UE) MCE errors to guest through
+        * machine check interrupt (set HSRR0 to 0x200). And for handled
+        * errors (no-fatal), just go back to guest execution with current
+        * HSRR0 instead of exiting guest. This new approach will inject
+        * machine check to guest for fatal error causing guest to crash.
+        *
+        * The old code used to return to host for unhandled errors which
+        * was causing guest to hang with soft lockups inside guest and
+        * makes it difficult to recover guest instance.
+        */
+       ld      r10, VCPU_PC(r9)
+       ld      r11, VCPU_MSR(r9)
+       bne     2f      /* Continue guest execution. */
        /* If not, deliver a machine check.  SRR0/1 are already set */
        li      r10, BOOK3S_INTERRUPT_MACHINE_CHECK
        ld      r11, VCPU_MSR(r9)
        bl      kvmppc_msr_interrupt
-       b       fast_interrupt_c_return
+2:     b       fast_interrupt_c_return
 
 /*
  * Check the reason we woke from nap, and take appropriate action.
index c0511c27a7337d757c450361a0682c8d3ef8d68e..412dd46dd0b7ea7136af14e8559e5d9b78d2c08a 100644 (file)
@@ -1470,7 +1470,7 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
                                regs->gpr[rd] = byterev_4(val);
                        goto ldst_done;
 
-#ifdef CONFIG_PPC_CPU
+#ifdef CONFIG_PPC_FPU
                case 535:       /* lfsx */
                case 567:       /* lfsux */
                        if (!(regs->msr & MSR_FP))
index e76eba74d9da8972afeed2525c755959c304dd6f..8f87d92171228d470f9274ff4eee27e921dbfbed 100644 (file)
@@ -78,7 +78,7 @@ sk_load_byte_positive_offset:
        blr
 
 /*
- * BPF_S_LDX_B_MSH: ldxb  4*([offset]&0xf)
+ * BPF_LDX | BPF_B | BPF_MSH: ldxb  4*([offset]&0xf)
  * r_addr is the offset value
  */
        .globl sk_load_byte_msh
index 808ce1cae21ab998439854e31eacaced66c044cb..6dcdadefd8d059a7c65207af5b71be761116947c 100644 (file)
@@ -79,19 +79,11 @@ static void bpf_jit_build_prologue(struct sk_filter *fp, u32 *image,
        }
 
        switch (filter[0].code) {
-       case BPF_S_RET_K:
-       case BPF_S_LD_W_LEN:
-       case BPF_S_ANC_PROTOCOL:
-       case BPF_S_ANC_IFINDEX:
-       case BPF_S_ANC_MARK:
-       case BPF_S_ANC_RXHASH:
-       case BPF_S_ANC_VLAN_TAG:
-       case BPF_S_ANC_VLAN_TAG_PRESENT:
-       case BPF_S_ANC_CPU:
-       case BPF_S_ANC_QUEUE:
-       case BPF_S_LD_W_ABS:
-       case BPF_S_LD_H_ABS:
-       case BPF_S_LD_B_ABS:
+       case BPF_RET | BPF_K:
+       case BPF_LD | BPF_W | BPF_LEN:
+       case BPF_LD | BPF_W | BPF_ABS:
+       case BPF_LD | BPF_H | BPF_ABS:
+       case BPF_LD | BPF_B | BPF_ABS:
                /* first instruction sets A register (or is RET 'constant') */
                break;
        default:
@@ -144,6 +136,7 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
 
        for (i = 0; i < flen; i++) {
                unsigned int K = filter[i].k;
+               u16 code = bpf_anc_helper(&filter[i]);
 
                /*
                 * addrs[] maps a BPF bytecode address into a real offset from
@@ -151,35 +144,35 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                 */
                addrs[i] = ctx->idx * 4;
 
-               switch (filter[i].code) {
+               switch (code) {
                        /*** ALU ops ***/
-               case BPF_S_ALU_ADD_X: /* A += X; */
+               case BPF_ALU | BPF_ADD | BPF_X: /* A += X; */
                        ctx->seen |= SEEN_XREG;
                        PPC_ADD(r_A, r_A, r_X);
                        break;
-               case BPF_S_ALU_ADD_K: /* A += K; */
+               case BPF_ALU | BPF_ADD | BPF_K: /* A += K; */
                        if (!K)
                                break;
                        PPC_ADDI(r_A, r_A, IMM_L(K));
                        if (K >= 32768)
                                PPC_ADDIS(r_A, r_A, IMM_HA(K));
                        break;
-               case BPF_S_ALU_SUB_X: /* A -= X; */
+               case BPF_ALU | BPF_SUB | BPF_X: /* A -= X; */
                        ctx->seen |= SEEN_XREG;
                        PPC_SUB(r_A, r_A, r_X);
                        break;
-               case BPF_S_ALU_SUB_K: /* A -= K */
+               case BPF_ALU | BPF_SUB | BPF_K: /* A -= K */
                        if (!K)
                                break;
                        PPC_ADDI(r_A, r_A, IMM_L(-K));
                        if (K >= 32768)
                                PPC_ADDIS(r_A, r_A, IMM_HA(-K));
                        break;
-               case BPF_S_ALU_MUL_X: /* A *= X; */
+               case BPF_ALU | BPF_MUL | BPF_X: /* A *= X; */
                        ctx->seen |= SEEN_XREG;
                        PPC_MUL(r_A, r_A, r_X);
                        break;
-               case BPF_S_ALU_MUL_K: /* A *= K */
+               case BPF_ALU | BPF_MUL | BPF_K: /* A *= K */
                        if (K < 32768)
                                PPC_MULI(r_A, r_A, K);
                        else {
@@ -187,7 +180,7 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                                PPC_MUL(r_A, r_A, r_scratch1);
                        }
                        break;
-               case BPF_S_ALU_MOD_X: /* A %= X; */
+               case BPF_ALU | BPF_MOD | BPF_X: /* A %= X; */
                        ctx->seen |= SEEN_XREG;
                        PPC_CMPWI(r_X, 0);
                        if (ctx->pc_ret0 != -1) {
@@ -201,13 +194,13 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                        PPC_MUL(r_scratch1, r_X, r_scratch1);
                        PPC_SUB(r_A, r_A, r_scratch1);
                        break;
-               case BPF_S_ALU_MOD_K: /* A %= K; */
+               case BPF_ALU | BPF_MOD | BPF_K: /* A %= K; */
                        PPC_LI32(r_scratch2, K);
                        PPC_DIVWU(r_scratch1, r_A, r_scratch2);
                        PPC_MUL(r_scratch1, r_scratch2, r_scratch1);
                        PPC_SUB(r_A, r_A, r_scratch1);
                        break;
-               case BPF_S_ALU_DIV_X: /* A /= X; */
+               case BPF_ALU | BPF_DIV | BPF_X: /* A /= X; */
                        ctx->seen |= SEEN_XREG;
                        PPC_CMPWI(r_X, 0);
                        if (ctx->pc_ret0 != -1) {
@@ -223,17 +216,17 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                        }
                        PPC_DIVWU(r_A, r_A, r_X);
                        break;
-               case BPF_S_ALU_DIV_K: /* A /= K */
+               case BPF_ALU | BPF_DIV | BPF_K: /* A /= K */
                        if (K == 1)
                                break;
                        PPC_LI32(r_scratch1, K);
                        PPC_DIVWU(r_A, r_A, r_scratch1);
                        break;
-               case BPF_S_ALU_AND_X:
+               case BPF_ALU | BPF_AND | BPF_X:
                        ctx->seen |= SEEN_XREG;
                        PPC_AND(r_A, r_A, r_X);
                        break;
-               case BPF_S_ALU_AND_K:
+               case BPF_ALU | BPF_AND | BPF_K:
                        if (!IMM_H(K))
                                PPC_ANDI(r_A, r_A, K);
                        else {
@@ -241,51 +234,51 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                                PPC_AND(r_A, r_A, r_scratch1);
                        }
                        break;
-               case BPF_S_ALU_OR_X:
+               case BPF_ALU | BPF_OR | BPF_X:
                        ctx->seen |= SEEN_XREG;
                        PPC_OR(r_A, r_A, r_X);
                        break;
-               case BPF_S_ALU_OR_K:
+               case BPF_ALU | BPF_OR | BPF_K:
                        if (IMM_L(K))
                                PPC_ORI(r_A, r_A, IMM_L(K));
                        if (K >= 65536)
                                PPC_ORIS(r_A, r_A, IMM_H(K));
                        break;
-               case BPF_S_ANC_ALU_XOR_X:
-               case BPF_S_ALU_XOR_X: /* A ^= X */
+               case BPF_ANC | SKF_AD_ALU_XOR_X:
+               case BPF_ALU | BPF_XOR | BPF_X: /* A ^= X */
                        ctx->seen |= SEEN_XREG;
                        PPC_XOR(r_A, r_A, r_X);
                        break;
-               case BPF_S_ALU_XOR_K: /* A ^= K */
+               case BPF_ALU | BPF_XOR | BPF_K: /* A ^= K */
                        if (IMM_L(K))
                                PPC_XORI(r_A, r_A, IMM_L(K));
                        if (K >= 65536)
                                PPC_XORIS(r_A, r_A, IMM_H(K));
                        break;
-               case BPF_S_ALU_LSH_X: /* A <<= X; */
+               case BPF_ALU | BPF_LSH | BPF_X: /* A <<= X; */
                        ctx->seen |= SEEN_XREG;
                        PPC_SLW(r_A, r_A, r_X);
                        break;
-               case BPF_S_ALU_LSH_K:
+               case BPF_ALU | BPF_LSH | BPF_K:
                        if (K == 0)
                                break;
                        else
                                PPC_SLWI(r_A, r_A, K);
                        break;
-               case BPF_S_ALU_RSH_X: /* A >>= X; */
+               case BPF_ALU | BPF_RSH | BPF_X: /* A >>= X; */
                        ctx->seen |= SEEN_XREG;
                        PPC_SRW(r_A, r_A, r_X);
                        break;
-               case BPF_S_ALU_RSH_K: /* A >>= K; */
+               case BPF_ALU | BPF_RSH | BPF_K: /* A >>= K; */
                        if (K == 0)
                                break;
                        else
                                PPC_SRWI(r_A, r_A, K);
                        break;
-               case BPF_S_ALU_NEG:
+               case BPF_ALU | BPF_NEG:
                        PPC_NEG(r_A, r_A);
                        break;
-               case BPF_S_RET_K:
+               case BPF_RET | BPF_K:
                        PPC_LI32(r_ret, K);
                        if (!K) {
                                if (ctx->pc_ret0 == -1)
@@ -312,7 +305,7 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                                        PPC_BLR();
                        }
                        break;
-               case BPF_S_RET_A:
+               case BPF_RET | BPF_A:
                        PPC_MR(r_ret, r_A);
                        if (i != flen - 1) {
                                if (ctx->seen)
@@ -321,53 +314,53 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                                        PPC_BLR();
                        }
                        break;
-               case BPF_S_MISC_TAX: /* X = A */
+               case BPF_MISC | BPF_TAX: /* X = A */
                        PPC_MR(r_X, r_A);
                        break;
-               case BPF_S_MISC_TXA: /* A = X */
+               case BPF_MISC | BPF_TXA: /* A = X */
                        ctx->seen |= SEEN_XREG;
                        PPC_MR(r_A, r_X);
                        break;
 
                        /*** Constant loads/M[] access ***/
-               case BPF_S_LD_IMM: /* A = K */
+               case BPF_LD | BPF_IMM: /* A = K */
                        PPC_LI32(r_A, K);
                        break;
-               case BPF_S_LDX_IMM: /* X = K */
+               case BPF_LDX | BPF_IMM: /* X = K */
                        PPC_LI32(r_X, K);
                        break;
-               case BPF_S_LD_MEM: /* A = mem[K] */
+               case BPF_LD | BPF_MEM: /* A = mem[K] */
                        PPC_MR(r_A, r_M + (K & 0xf));
                        ctx->seen |= SEEN_MEM | (1<<(K & 0xf));
                        break;
-               case BPF_S_LDX_MEM: /* X = mem[K] */
+               case BPF_LDX | BPF_MEM: /* X = mem[K] */
                        PPC_MR(r_X, r_M + (K & 0xf));
                        ctx->seen |= SEEN_MEM | (1<<(K & 0xf));
                        break;
-               case BPF_S_ST: /* mem[K] = A */
+               case BPF_ST: /* mem[K] = A */
                        PPC_MR(r_M + (K & 0xf), r_A);
                        ctx->seen |= SEEN_MEM | (1<<(K & 0xf));
                        break;
-               case BPF_S_STX: /* mem[K] = X */
+               case BPF_STX: /* mem[K] = X */
                        PPC_MR(r_M + (K & 0xf), r_X);
                        ctx->seen |= SEEN_XREG | SEEN_MEM | (1<<(K & 0xf));
                        break;
-               case BPF_S_LD_W_LEN: /* A = skb->len; */
+               case BPF_LD | BPF_W | BPF_LEN: /*       A = skb->len; */
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4);
                        PPC_LWZ_OFFS(r_A, r_skb, offsetof(struct sk_buff, len));
                        break;
-               case BPF_S_LDX_W_LEN: /* X = skb->len; */
+               case BPF_LDX | BPF_W | BPF_LEN: /* X = skb->len; */
                        PPC_LWZ_OFFS(r_X, r_skb, offsetof(struct sk_buff, len));
                        break;
 
                        /*** Ancillary info loads ***/
-               case BPF_S_ANC_PROTOCOL: /* A = ntohs(skb->protocol); */
+               case BPF_ANC | SKF_AD_PROTOCOL: /* A = ntohs(skb->protocol); */
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff,
                                                  protocol) != 2);
                        PPC_NTOHS_OFFS(r_A, r_skb, offsetof(struct sk_buff,
                                                            protocol));
                        break;
-               case BPF_S_ANC_IFINDEX:
+               case BPF_ANC | SKF_AD_IFINDEX:
                        PPC_LD_OFFS(r_scratch1, r_skb, offsetof(struct sk_buff,
                                                                dev));
                        PPC_CMPDI(r_scratch1, 0);
@@ -384,33 +377,33 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                        PPC_LWZ_OFFS(r_A, r_scratch1,
                                     offsetof(struct net_device, ifindex));
                        break;
-               case BPF_S_ANC_MARK:
+               case BPF_ANC | SKF_AD_MARK:
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4);
                        PPC_LWZ_OFFS(r_A, r_skb, offsetof(struct sk_buff,
                                                          mark));
                        break;
-               case BPF_S_ANC_RXHASH:
+               case BPF_ANC | SKF_AD_RXHASH:
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4);
                        PPC_LWZ_OFFS(r_A, r_skb, offsetof(struct sk_buff,
                                                          hash));
                        break;
-               case BPF_S_ANC_VLAN_TAG:
-               case BPF_S_ANC_VLAN_TAG_PRESENT:
+               case BPF_ANC | SKF_AD_VLAN_TAG:
+               case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT:
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2);
                        PPC_LHZ_OFFS(r_A, r_skb, offsetof(struct sk_buff,
                                                          vlan_tci));
-                       if (filter[i].code == BPF_S_ANC_VLAN_TAG)
+                       if (code == (BPF_ANC | SKF_AD_VLAN_TAG))
                                PPC_ANDI(r_A, r_A, VLAN_VID_MASK);
                        else
                                PPC_ANDI(r_A, r_A, VLAN_TAG_PRESENT);
                        break;
-               case BPF_S_ANC_QUEUE:
+               case BPF_ANC | SKF_AD_QUEUE:
                        BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff,
                                                  queue_mapping) != 2);
                        PPC_LHZ_OFFS(r_A, r_skb, offsetof(struct sk_buff,
                                                          queue_mapping));
                        break;
-               case BPF_S_ANC_CPU:
+               case BPF_ANC | SKF_AD_CPU:
 #ifdef CONFIG_SMP
                        /*
                         * PACA ptr is r13:
@@ -426,13 +419,13 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                        break;
 
                        /*** Absolute loads from packet header/data ***/
-               case BPF_S_LD_W_ABS:
+               case BPF_LD | BPF_W | BPF_ABS:
                        func = CHOOSE_LOAD_FUNC(K, sk_load_word);
                        goto common_load;
-               case BPF_S_LD_H_ABS:
+               case BPF_LD | BPF_H | BPF_ABS:
                        func = CHOOSE_LOAD_FUNC(K, sk_load_half);
                        goto common_load;
-               case BPF_S_LD_B_ABS:
+               case BPF_LD | BPF_B | BPF_ABS:
                        func = CHOOSE_LOAD_FUNC(K, sk_load_byte);
                common_load:
                        /* Load from [K]. */
@@ -449,13 +442,13 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                        break;
 
                        /*** Indirect loads from packet header/data ***/
-               case BPF_S_LD_W_IND:
+               case BPF_LD | BPF_W | BPF_IND:
                        func = sk_load_word;
                        goto common_load_ind;
-               case BPF_S_LD_H_IND:
+               case BPF_LD | BPF_H | BPF_IND:
                        func = sk_load_half;
                        goto common_load_ind;
-               case BPF_S_LD_B_IND:
+               case BPF_LD | BPF_B | BPF_IND:
                        func = sk_load_byte;
                common_load_ind:
                        /*
@@ -473,31 +466,31 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                        PPC_BCC(COND_LT, exit_addr);
                        break;
 
-               case BPF_S_LDX_B_MSH:
+               case BPF_LDX | BPF_B | BPF_MSH:
                        func = CHOOSE_LOAD_FUNC(K, sk_load_byte_msh);
                        goto common_load;
                        break;
 
                        /*** Jump and branches ***/
-               case BPF_S_JMP_JA:
+               case BPF_JMP | BPF_JA:
                        if (K != 0)
                                PPC_JMP(addrs[i + 1 + K]);
                        break;
 
-               case BPF_S_JMP_JGT_K:
-               case BPF_S_JMP_JGT_X:
+               case BPF_JMP | BPF_JGT | BPF_K:
+               case BPF_JMP | BPF_JGT | BPF_X:
                        true_cond = COND_GT;
                        goto cond_branch;
-               case BPF_S_JMP_JGE_K:
-               case BPF_S_JMP_JGE_X:
+               case BPF_JMP | BPF_JGE | BPF_K:
+               case BPF_JMP | BPF_JGE | BPF_X:
                        true_cond = COND_GE;
                        goto cond_branch;
-               case BPF_S_JMP_JEQ_K:
-               case BPF_S_JMP_JEQ_X:
+               case BPF_JMP | BPF_JEQ | BPF_K:
+               case BPF_JMP | BPF_JEQ | BPF_X:
                        true_cond = COND_EQ;
                        goto cond_branch;
-               case BPF_S_JMP_JSET_K:
-               case BPF_S_JMP_JSET_X:
+               case BPF_JMP | BPF_JSET | BPF_K:
+               case BPF_JMP | BPF_JSET | BPF_X:
                        true_cond = COND_NE;
                        /* Fall through */
                cond_branch:
@@ -508,20 +501,20 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                                break;
                        }
 
-                       switch (filter[i].code) {
-                       case BPF_S_JMP_JGT_X:
-                       case BPF_S_JMP_JGE_X:
-                       case BPF_S_JMP_JEQ_X:
+                       switch (code) {
+                       case BPF_JMP | BPF_JGT | BPF_X:
+                       case BPF_JMP | BPF_JGE | BPF_X:
+                       case BPF_JMP | BPF_JEQ | BPF_X:
                                ctx->seen |= SEEN_XREG;
                                PPC_CMPLW(r_A, r_X);
                                break;
-                       case BPF_S_JMP_JSET_X:
+                       case BPF_JMP | BPF_JSET | BPF_X:
                                ctx->seen |= SEEN_XREG;
                                PPC_AND_DOT(r_scratch1, r_A, r_X);
                                break;
-                       case BPF_S_JMP_JEQ_K:
-                       case BPF_S_JMP_JGT_K:
-                       case BPF_S_JMP_JGE_K:
+                       case BPF_JMP | BPF_JEQ | BPF_K:
+                       case BPF_JMP | BPF_JGT | BPF_K:
+                       case BPF_JMP | BPF_JGE | BPF_K:
                                if (K < 32768)
                                        PPC_CMPLWI(r_A, K);
                                else {
@@ -529,7 +522,7 @@ static int bpf_jit_build_body(struct sk_filter *fp, u32 *image,
                                        PPC_CMPLW(r_A, r_scratch1);
                                }
                                break;
-                       case BPF_S_JMP_JSET_K:
+                       case BPF_JMP | BPF_JSET | BPF_K:
                                if (K < 32768)
                                        /* PPC_ANDI is /only/ dot-form */
                                        PPC_ANDI(r_scratch1, r_A, K);
index bf9c6d4cd26c34770c53aedcb12fd1a2ed6f4e5c..391b3f6b54a30eb4759ce2a2b898392bb796c0ac 100644 (file)
@@ -19,7 +19,6 @@ source "arch/powerpc/platforms/embedded6xx/Kconfig"
 source "arch/powerpc/platforms/44x/Kconfig"
 source "arch/powerpc/platforms/40x/Kconfig"
 source "arch/powerpc/platforms/amigaone/Kconfig"
-source "arch/powerpc/platforms/wsp/Kconfig"
 
 config KVM_GUEST
        bool "KVM Guest support"
index 43b65ad1970a1a8635e7c517b7cfb95d211bcf84..a41bd023647aff48b21da2d8af80760417f879c4 100644 (file)
@@ -148,10 +148,6 @@ config POWER4
        depends on PPC64 && PPC_BOOK3S
        def_bool y
 
-config PPC_A2
-       bool
-       depends on PPC_BOOK3E_64
-
 config TUNE_CELL
        bool "Optimize for Cell Broadband Engine"
        depends on PPC64 && PPC_BOOK3S
@@ -280,7 +276,7 @@ config VSX
 
 config PPC_ICSWX
        bool "Support for PowerPC icswx coprocessor instruction"
-       depends on POWER4 || PPC_A2
+       depends on POWER4
        default n
        ---help---
 
index 879b4a448498807e08745b731b55ebbe1ab89e8b..469ef170d218e24fadc665623787e70a7dbd39f8 100644 (file)
@@ -22,4 +22,3 @@ obj-$(CONFIG_PPC_CELL)                += cell/
 obj-$(CONFIG_PPC_PS3)          += ps3/
 obj-$(CONFIG_EMBEDDED6xx)      += embedded6xx/
 obj-$(CONFIG_AMIGAONE)         += amigaone/
-obj-$(CONFIG_PPC_WSP)          += wsp/
index 0ba3c9598358e795e39e717287397b2405bca1a6..bcfd6f063efa7e64d4e361af002f6e858ff10620 100644 (file)
@@ -35,7 +35,6 @@
 #define SPUFS_PS_MAP_SIZE      0x20000
 #define SPUFS_MFC_MAP_SIZE     0x1000
 #define SPUFS_CNTL_MAP_SIZE    0x1000
-#define SPUFS_CNTL_MAP_SIZE    0x1000
 #define SPUFS_SIGNAL_MAP_SIZE  PAGE_SIZE
 #define SPUFS_MSS_MAP_SIZE     0x1000
 
index c252ee95bddf31b17e676f0d46a852880502f131..45a8ed0585cd17cd1aef9675d759e7342c8ca334 100644 (file)
@@ -17,6 +17,7 @@ config PPC_POWERNV
        select CPU_FREQ_GOV_USERSPACE
        select CPU_FREQ_GOV_ONDEMAND
        select CPU_FREQ_GOV_CONSERVATIVE
+       select PPC_DOORBELL
        default y
 
 config PPC_POWERNV_RTAS
index 4ad0d345bc960eeec309848c4edc0e2fa23e6970..d55891f89a2ce2bf0c14b22b47564760dd16abf0 100644 (file)
@@ -1,9 +1,9 @@
 obj-y                  += setup.o opal-takeover.o opal-wrappers.o opal.o opal-async.o
 obj-y                  += opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o
 obj-y                  += rng.o opal-elog.o opal-dump.o opal-sysparam.o opal-sensor.o
-obj-y                  += opal-msglog.o subcore.o subcore-asm.o
+obj-y                  += opal-msglog.o
 
-obj-$(CONFIG_SMP)      += smp.o
+obj-$(CONFIG_SMP)      += smp.o subcore.o subcore-asm.o
 obj-$(CONFIG_PCI)      += pci.o pci-p5ioc2.o pci-ioda.o
 obj-$(CONFIG_EEH)      += eeh-ioda.o eeh-powernv.o
 obj-$(CONFIG_PPC_SCOM) += opal-xscom.o
index 753f08e36dfaa8d4cfa7884b9f27480899abbca7..8ad0c5b891f4e3083e3b554b980f0eae54a51ab7 100644 (file)
@@ -267,7 +267,7 @@ static int ioda_eeh_get_state(struct eeh_pe *pe)
 {
        s64 ret = 0;
        u8 fstate;
-       u16 pcierr;
+       __be16 pcierr;
        u32 pe_no;
        int result;
        struct pci_controller *hose = pe->phb;
@@ -316,7 +316,7 @@ static int ioda_eeh_get_state(struct eeh_pe *pe)
                result = 0;
                result &= ~EEH_STATE_RESET_ACTIVE;
 
-               if (pcierr != OPAL_EEH_PHB_ERROR) {
+               if (be16_to_cpu(pcierr) != OPAL_EEH_PHB_ERROR) {
                        result |= EEH_STATE_MMIO_ACTIVE;
                        result |= EEH_STATE_DMA_ACTIVE;
                        result |= EEH_STATE_MMIO_ENABLED;
@@ -705,18 +705,19 @@ static int ioda_eeh_next_error(struct eeh_pe **pe)
 {
        struct pci_controller *hose;
        struct pnv_phb *phb;
-       struct eeh_pe *phb_pe;
-       u64 frozen_pe_no;
-       u16 err_type, severity;
+       struct eeh_pe *phb_pe, *parent_pe;
+       __be64 frozen_pe_no;
+       __be16 err_type, severity;
+       int active_flags = (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE);
        long rc;
-       int ret = EEH_NEXT_ERR_NONE;
+       int state, ret = EEH_NEXT_ERR_NONE;
 
        /*
         * While running here, it's safe to purge the event queue.
         * And we should keep the cached OPAL notifier event sychronized
         * between the kernel and firmware.
         */
-       eeh_remove_event(NULL);
+       eeh_remove_event(NULL, false);
        opal_notifier_update_evt(OPAL_EVENT_PCI_ERROR, 0x0ul);
 
        list_for_each_entry(hose, &hose_list, list_node) {
@@ -742,8 +743,8 @@ static int ioda_eeh_next_error(struct eeh_pe **pe)
                }
 
                /* If the PHB doesn't have error, stop processing */
-               if (err_type == OPAL_EEH_NO_ERROR ||
-                   severity == OPAL_EEH_SEV_NO_ERROR) {
+               if (be16_to_cpu(err_type) == OPAL_EEH_NO_ERROR ||
+                   be16_to_cpu(severity) == OPAL_EEH_SEV_NO_ERROR) {
                        pr_devel("%s: No error found on PHB#%x\n",
                                 __func__, hose->global_number);
                        continue;
@@ -755,14 +756,14 @@ static int ioda_eeh_next_error(struct eeh_pe **pe)
                 * specific PHB.
                 */
                pr_devel("%s: Error (%d, %d, %llu) on PHB#%x\n",
-                        __func__, err_type, severity,
-                        frozen_pe_no, hose->global_number);
-               switch (err_type) {
+                        __func__, be16_to_cpu(err_type), be16_to_cpu(severity),
+                        be64_to_cpu(frozen_pe_no), hose->global_number);
+               switch (be16_to_cpu(err_type)) {
                case OPAL_EEH_IOC_ERROR:
-                       if (severity == OPAL_EEH_SEV_IOC_DEAD) {
+                       if (be16_to_cpu(severity) == OPAL_EEH_SEV_IOC_DEAD) {
                                pr_err("EEH: dead IOC detected\n");
                                ret = EEH_NEXT_ERR_DEAD_IOC;
-                       } else if (severity == OPAL_EEH_SEV_INF) {
+                       } else if (be16_to_cpu(severity) == OPAL_EEH_SEV_INF) {
                                pr_info("EEH: IOC informative error "
                                        "detected\n");
                                ioda_eeh_hub_diag(hose);
@@ -771,20 +772,26 @@ static int ioda_eeh_next_error(struct eeh_pe **pe)
 
                        break;
                case OPAL_EEH_PHB_ERROR:
-                       if (severity == OPAL_EEH_SEV_PHB_DEAD) {
+                       if (be16_to_cpu(severity) == OPAL_EEH_SEV_PHB_DEAD) {
                                *pe = phb_pe;
-                               pr_err("EEH: dead PHB#%x detected\n",
-                                       hose->global_number);
+                               pr_err("EEH: dead PHB#%x detected, "
+                                      "location: %s\n",
+                                      hose->global_number,
+                                      eeh_pe_loc_get(phb_pe));
                                ret = EEH_NEXT_ERR_DEAD_PHB;
-                       } else if (severity == OPAL_EEH_SEV_PHB_FENCED) {
+                       } else if (be16_to_cpu(severity) ==
+                                               OPAL_EEH_SEV_PHB_FENCED) {
                                *pe = phb_pe;
-                               pr_err("EEH: fenced PHB#%x detected\n",
-                                       hose->global_number);
+                               pr_err("EEH: Fenced PHB#%x detected, "
+                                      "location: %s\n",
+                                      hose->global_number,
+                                      eeh_pe_loc_get(phb_pe));
                                ret = EEH_NEXT_ERR_FENCED_PHB;
-                       } else if (severity == OPAL_EEH_SEV_INF) {
+                       } else if (be16_to_cpu(severity) == OPAL_EEH_SEV_INF) {
                                pr_info("EEH: PHB#%x informative error "
-                                       "detected\n",
-                                       hose->global_number);
+                                       "detected, location: %s\n",
+                                       hose->global_number,
+                                       eeh_pe_loc_get(phb_pe));
                                ioda_eeh_phb_diag(hose);
                                ret = EEH_NEXT_ERR_NONE;
                        }
@@ -792,34 +799,33 @@ static int ioda_eeh_next_error(struct eeh_pe **pe)
                        break;
                case OPAL_EEH_PE_ERROR:
                        /*
-                        * If we can't find the corresponding PE, the
-                        * PEEV / PEST would be messy. So we force an
-                        * fenced PHB so that it can be recovered.
-                        *
-                        * If the PE has been marked as isolated, that
-                        * should have been removed permanently or in
-                        * progress with recovery. We needn't report
-                        * it again.
+                        * If we can't find the corresponding PE, we
+                        * just try to unfreeze.
                         */
-                       if (ioda_eeh_get_pe(hose, frozen_pe_no, pe)) {
-                               *pe = phb_pe;
-                               pr_err("EEH: Escalated fenced PHB#%x "
-                                      "detected for PE#%llx\n",
-                                       hose->global_number,
-                                       frozen_pe_no);
-                               ret = EEH_NEXT_ERR_FENCED_PHB;
+                       if (ioda_eeh_get_pe(hose,
+                                           be64_to_cpu(frozen_pe_no), pe)) {
+                               /* Try best to clear it */
+                               pr_info("EEH: Clear non-existing PHB#%x-PE#%llx\n",
+                                       hose->global_number, frozen_pe_no);
+                               pr_info("EEH: PHB location: %s\n",
+                                       eeh_pe_loc_get(phb_pe));
+                               opal_pci_eeh_freeze_clear(phb->opal_id, frozen_pe_no,
+                                       OPAL_EEH_ACTION_CLEAR_FREEZE_ALL);
+                               ret = EEH_NEXT_ERR_NONE;
                        } else if ((*pe)->state & EEH_PE_ISOLATED) {
                                ret = EEH_NEXT_ERR_NONE;
                        } else {
                                pr_err("EEH: Frozen PE#%x on PHB#%x detected\n",
                                        (*pe)->addr, (*pe)->phb->global_number);
+                               pr_err("EEH: PE location: %s, PHB location: %s\n",
+                                       eeh_pe_loc_get(*pe), eeh_pe_loc_get(phb_pe));
                                ret = EEH_NEXT_ERR_FROZEN_PE;
                        }
 
                        break;
                default:
                        pr_warn("%s: Unexpected error type %d\n",
-                               __func__, err_type);
+                               __func__, be16_to_cpu(err_type));
                }
 
                /*
@@ -836,6 +842,31 @@ static int ioda_eeh_next_error(struct eeh_pe **pe)
                        ioda_eeh_phb_diag(hose);
                }
 
+               /*
+                * We probably have the frozen parent PE out there and
+                * we need have to handle frozen parent PE firstly.
+                */
+               if (ret == EEH_NEXT_ERR_FROZEN_PE) {
+                       parent_pe = (*pe)->parent;
+                       while (parent_pe) {
+                               /* Hit the ceiling ? */
+                               if (parent_pe->type & EEH_PE_PHB)
+                                       break;
+
+                               /* Frozen parent PE ? */
+                               state = ioda_eeh_get_state(parent_pe);
+                               if (state > 0 &&
+                                   (state & active_flags) != active_flags)
+                                       *pe = parent_pe;
+
+                               /* Next parent level */
+                               parent_pe = parent_pe->parent;
+                       }
+
+                       /* We possibly migrate to another PE */
+                       eeh_pe_state_mark(*pe, EEH_PE_ISOLATED);
+               }
+
                /*
                 * If we have no errors on the specific PHB or only
                 * informative error there, we continue poking it.
index 1bb25b9525046f14abfa2e38c2db188b0686ff94..44ed78af1a0dd53bb9e33f16b3ce465ffc5c6f4f 100644 (file)
@@ -37,7 +37,8 @@ static ssize_t opal_msglog_read(struct file *file, struct kobject *kobj,
 {
        struct memcons *mc = bin_attr->private;
        const char *conbuf;
-       size_t ret, first_read = 0;
+       ssize_t ret;
+       size_t first_read = 0;
        uint32_t out_pos, avail;
 
        if (!mc)
@@ -69,6 +70,9 @@ static ssize_t opal_msglog_read(struct file *file, struct kobject *kobj,
                to += first_read;
                count -= first_read;
                pos -= avail;
+
+               if (count <= 0)
+                       goto out;
        }
 
        /* Sanity check. The firmware should not do this to us. */
index d202f9bc3683f5ad0072282173ddfb510b1aec9a..9d1acf22a099dfc1bf474f6244af11d1153742f6 100644 (file)
@@ -260,10 +260,10 @@ void __init opal_sys_param_init(void)
                        attr[i].kobj_attr.attr.mode = S_IRUGO;
                        break;
                case OPAL_SYSPARAM_WRITE:
-                       attr[i].kobj_attr.attr.mode = S_IWUGO;
+                       attr[i].kobj_attr.attr.mode = S_IWUSR;
                        break;
                case OPAL_SYSPARAM_RW:
-                       attr[i].kobj_attr.attr.mode = S_IRUGO | S_IWUGO;
+                       attr[i].kobj_attr.attr.mode = S_IRUGO | S_IWUSR;
                        break;
                default:
                        break;
index eefbfcc3fd8c40a9d50b9839fb2292f723c7970d..f91a4e5d872e23adc5eacc274d8b07226ad000b0 100644 (file)
@@ -206,72 +206,91 @@ static void pnv_pci_dump_phb3_diag_data(struct pci_controller *hose,
 
        data = (struct OpalIoPhb3ErrorData*)common;
        pr_info("PHB3 PHB#%d Diag-data (Version: %d)\n",
-               hose->global_number, common->version);
+               hose->global_number, be32_to_cpu(common->version));
        if (data->brdgCtl)
                pr_info("brdgCtl:     %08x\n",
-                       data->brdgCtl);
+                       be32_to_cpu(data->brdgCtl));
        if (data->portStatusReg || data->rootCmplxStatus ||
            data->busAgentStatus)
                pr_info("UtlSts:      %08x %08x %08x\n",
-                       data->portStatusReg, data->rootCmplxStatus,
-                       data->busAgentStatus);
+                       be32_to_cpu(data->portStatusReg),
+                       be32_to_cpu(data->rootCmplxStatus),
+                       be32_to_cpu(data->busAgentStatus));
        if (data->deviceStatus || data->slotStatus   ||
            data->linkStatus   || data->devCmdStatus ||
            data->devSecStatus)
                pr_info("RootSts:     %08x %08x %08x %08x %08x\n",
-                       data->deviceStatus, data->slotStatus,
-                       data->linkStatus, data->devCmdStatus,
-                       data->devSecStatus);
+                       be32_to_cpu(data->deviceStatus),
+                       be32_to_cpu(data->slotStatus),
+                       be32_to_cpu(data->linkStatus),
+                       be32_to_cpu(data->devCmdStatus),
+                       be32_to_cpu(data->devSecStatus));
        if (data->rootErrorStatus || data->uncorrErrorStatus ||
            data->corrErrorStatus)
                pr_info("RootErrSts:  %08x %08x %08x\n",
-                       data->rootErrorStatus, data->uncorrErrorStatus,
-                       data->corrErrorStatus);
+                       be32_to_cpu(data->rootErrorStatus),
+                       be32_to_cpu(data->uncorrErrorStatus),
+                       be32_to_cpu(data->corrErrorStatus));
        if (data->tlpHdr1 || data->tlpHdr2 ||
            data->tlpHdr3 || data->tlpHdr4)
                pr_info("RootErrLog:  %08x %08x %08x %08x\n",
-                       data->tlpHdr1, data->tlpHdr2,
-                       data->tlpHdr3, data->tlpHdr4);
+                       be32_to_cpu(data->tlpHdr1),
+                       be32_to_cpu(data->tlpHdr2),
+                       be32_to_cpu(data->tlpHdr3),
+                       be32_to_cpu(data->tlpHdr4));
        if (data->sourceId || data->errorClass ||
            data->correlator)
                pr_info("RootErrLog1: %08x %016llx %016llx\n",
-                       data->sourceId, data->errorClass,
-                       data->correlator);
+                       be32_to_cpu(data->sourceId),
+                       be64_to_cpu(data->errorClass),
+                       be64_to_cpu(data->correlator));
        if (data->nFir)
                pr_info("nFir:        %016llx %016llx %016llx\n",
-                       data->nFir, data->nFirMask,
-                       data->nFirWOF);
+                       be64_to_cpu(data->nFir),
+                       be64_to_cpu(data->nFirMask),
+                       be64_to_cpu(data->nFirWOF));
        if (data->phbPlssr || data->phbCsr)
                pr_info("PhbSts:      %016llx %016llx\n",
-                       data->phbPlssr, data->phbCsr);
+                       be64_to_cpu(data->phbPlssr),
+                       be64_to_cpu(data->phbCsr));
        if (data->lemFir)
                pr_info("Lem:         %016llx %016llx %016llx\n",
-                       data->lemFir, data->lemErrorMask,
-                       data->lemWOF);
+                       be64_to_cpu(data->lemFir),
+                       be64_to_cpu(data->lemErrorMask),
+                       be64_to_cpu(data->lemWOF));
        if (data->phbErrorStatus)
                pr_info("PhbErr:      %016llx %016llx %016llx %016llx\n",
-                       data->phbErrorStatus, data->phbFirstErrorStatus,
-                       data->phbErrorLog0, data->phbErrorLog1);
+                       be64_to_cpu(data->phbErrorStatus),
+                       be64_to_cpu(data->phbFirstErrorStatus),
+                       be64_to_cpu(data->phbErrorLog0),
+                       be64_to_cpu(data->phbErrorLog1));
        if (data->mmioErrorStatus)
                pr_info("OutErr:      %016llx %016llx %016llx %016llx\n",
-                       data->mmioErrorStatus, data->mmioFirstErrorStatus,
-                       data->mmioErrorLog0, data->mmioErrorLog1);
+                       be64_to_cpu(data->mmioErrorStatus),
+                       be64_to_cpu(data->mmioFirstErrorStatus),
+                       be64_to_cpu(data->mmioErrorLog0),
+                       be64_to_cpu(data->mmioErrorLog1));
        if (data->dma0ErrorStatus)
                pr_info("InAErr:      %016llx %016llx %016llx %016llx\n",
-                       data->dma0ErrorStatus, data->dma0FirstErrorStatus,
-                       data->dma0ErrorLog0, data->dma0ErrorLog1);
+                       be64_to_cpu(data->dma0ErrorStatus),
+                       be64_to_cpu(data->dma0FirstErrorStatus),
+                       be64_to_cpu(data->dma0ErrorLog0),
+                       be64_to_cpu(data->dma0ErrorLog1));
        if (data->dma1ErrorStatus)
                pr_info("InBErr:      %016llx %016llx %016llx %016llx\n",
-                       data->dma1ErrorStatus, data->dma1FirstErrorStatus,
-                       data->dma1ErrorLog0, data->dma1ErrorLog1);
+                       be64_to_cpu(data->dma1ErrorStatus),
+                       be64_to_cpu(data->dma1FirstErrorStatus),
+                       be64_to_cpu(data->dma1ErrorLog0),
+                       be64_to_cpu(data->dma1ErrorLog1));
 
        for (i = 0; i < OPAL_PHB3_NUM_PEST_REGS; i++) {
-               if ((data->pestA[i] >> 63) == 0 &&
-                   (data->pestB[i] >> 63) == 0)
+               if ((be64_to_cpu(data->pestA[i]) >> 63) == 0 &&
+                   (be64_to_cpu(data->pestB[i]) >> 63) == 0)
                        continue;
 
                pr_info("PE[%3d] A/B: %016llx %016llx\n",
-                       i, data->pestA[i], data->pestB[i]);
+                               i, be64_to_cpu(data->pestA[i]),
+                               be64_to_cpu(data->pestB[i]));
        }
 }
 
@@ -284,7 +303,7 @@ void pnv_pci_dump_phb_diag_data(struct pci_controller *hose,
                return;
 
        common = (struct OpalIoPhbErrorCommon *)log_buff;
-       switch (common->ioType) {
+       switch (be32_to_cpu(common->ioType)) {
        case OPAL_PHB_ERROR_DATA_TYPE_P7IOC:
                pnv_pci_dump_p7ioc_diag_data(hose, common);
                break;
@@ -293,7 +312,7 @@ void pnv_pci_dump_phb_diag_data(struct pci_controller *hose,
                break;
        default:
                pr_warn("%s: Unrecognized ioType %d\n",
-                       __func__, common->ioType);
+                       __func__, be32_to_cpu(common->ioType));
        }
 }
 
index 8c16a5f9672809fbbbc3bdfb934459f9d3d2ebb8..d9b88fa7c5a349f26c040b20dcf5e72a67de8860 100644 (file)
 #include <asm/rtas.h>
 #include <asm/opal.h>
 #include <asm/kexec.h>
+#include <asm/smp.h>
 
 #include "powernv.h"
 
 static void __init pnv_setup_arch(void)
 {
+       set_arch_panic_timeout(10, ARCH_PANIC_TIMEOUT);
+
        /* Initialize SMP */
        pnv_smp_init();
 
index 0062a43a2e0d8e96698cf0cb090adce0170b4581..5fcfcf44e3a9b949e6536ebcf53038645286fde6 100644 (file)
@@ -32,6 +32,7 @@
 #include <asm/opal.h>
 #include <asm/runlatch.h>
 #include <asm/code-patching.h>
+#include <asm/dbell.h>
 
 #include "powernv.h"
 
@@ -46,6 +47,11 @@ static void pnv_smp_setup_cpu(int cpu)
 {
        if (cpu != boot_cpuid)
                xics_setup_cpu();
+
+#ifdef CONFIG_PPC_DOORBELL
+       if (cpu_has_feature(CPU_FTR_DBELL))
+               doorbell_setup_this_cpu();
+#endif
 }
 
 int pnv_smp_kick_cpu(int nr)
index 2cb8b776c84a5660e5ef225cc004fbdcc9109f96..756b482f819a425a1dcd74bdfe0ab178f6b0617d 100644 (file)
@@ -21,6 +21,7 @@ config PPC_PSERIES
        select HAVE_CONTEXT_TRACKING
        select HOTPLUG_CPU if SMP
        select ARCH_RANDOM
+       select PPC_DOORBELL
        default y
 
 config PPC_SPLPAR
diff --git a/arch/powerpc/platforms/wsp/Kconfig b/arch/powerpc/platforms/wsp/Kconfig
deleted file mode 100644 (file)
index 422a175..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-config PPC_WSP
-       bool
-       select PPC_A2
-       select GENERIC_TBSYNC
-       select PPC_ICSWX
-       select PPC_SCOM
-       select PPC_XICS
-       select PPC_ICP_NATIVE
-       select PCI
-       select PPC_IO_WORKAROUNDS if PCI
-       select PPC_INDIRECT_PIO if PCI
-       default n
-
-menu "WSP platform selection"
-       depends on PPC_BOOK3E_64
-
-config PPC_PSR2
-       bool "PowerEN System Reference Platform 2"
-       select EPAPR_BOOT
-       select PPC_WSP
-       default y
-
-config PPC_CHROMA
-       bool "PowerEN PCIe Chroma Card"
-       select EPAPR_BOOT
-       select PPC_WSP
-       select OF_DYNAMIC
-       default y
-
-endmenu
diff --git a/arch/powerpc/platforms/wsp/Makefile b/arch/powerpc/platforms/wsp/Makefile
deleted file mode 100644 (file)
index 162fc60..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-ccflags-y                      += $(NO_MINIMAL_TOC)
-
-obj-y                          += setup.o ics.o wsp.o
-obj-$(CONFIG_PPC_PSR2)         += psr2.o
-obj-$(CONFIG_PPC_CHROMA)       += chroma.o h8.o
-obj-$(CONFIG_PPC_WSP)          += opb_pic.o
-obj-$(CONFIG_PPC_WSP)          += scom_wsp.o
-obj-$(CONFIG_SMP)              += smp.o scom_smp.o
-obj-$(CONFIG_PCI)              += wsp_pci.o
-obj-$(CONFIG_PCI_MSI)          += msi.o
diff --git a/arch/powerpc/platforms/wsp/chroma.c b/arch/powerpc/platforms/wsp/chroma.c
deleted file mode 100644 (file)
index aaa46b3..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2008-2011, IBM Corporation
- *
- * 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/delay.h>
-#include <linux/init.h>
-#include <linux/irq.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/of.h>
-#include <linux/smp.h>
-#include <linux/time.h>
-#include <linux/of_fdt.h>
-
-#include <asm/machdep.h>
-#include <asm/udbg.h>
-
-#include "ics.h"
-#include "wsp.h"
-
-void __init chroma_setup_arch(void)
-{
-       wsp_setup_arch();
-       wsp_setup_h8();
-
-}
-
-static int __init chroma_probe(void)
-{
-       unsigned long root = of_get_flat_dt_root();
-
-       if (!of_flat_dt_is_compatible(root, "ibm,wsp-chroma"))
-               return 0;
-
-       return 1;
-}
-
-define_machine(chroma_md) {
-       .name                   = "Chroma PCIe",
-       .probe                  = chroma_probe,
-       .setup_arch             = chroma_setup_arch,
-       .restart                = wsp_h8_restart,
-       .power_off              = wsp_h8_power_off,
-       .halt                   = wsp_halt,
-       .calibrate_decr         = generic_calibrate_decr,
-       .init_IRQ               = wsp_setup_irq,
-       .progress               = udbg_progress,
-       .power_save             = book3e_idle,
-};
-
-machine_arch_initcall(chroma_md, wsp_probe_devices);
diff --git a/arch/powerpc/platforms/wsp/h8.c b/arch/powerpc/platforms/wsp/h8.c
deleted file mode 100644 (file)
index a3c87f3..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright 2008-2011, IBM Corporation
- *
- * 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/kernel.h>
-#include <linux/of.h>
-#include <linux/io.h>
-#include <linux/of_address.h>
-
-#include "wsp.h"
-
-/*
- * The UART connection to the H8 is over ttyS1 which is just a 16550.
- * We assume that FW has it setup right and no one messes with it.
- */
-
-
-static u8 __iomem *h8;
-
-#define RBR 0          /* Receiver Buffer Register */
-#define THR 0          /* Transmitter Holding Register */
-#define LSR 5          /* Line Status Register */
-#define LSR_DR 0x01    /* LSR value for Data-Ready */
-#define LSR_THRE 0x20  /* LSR value for Transmitter-Holding-Register-Empty */
-static void wsp_h8_putc(int c)
-{
-       u8 lsr;
-
-       do {
-               lsr = readb(h8 + LSR);
-       } while ((lsr & LSR_THRE) != LSR_THRE);
-       writeb(c, h8 + THR);
-}
-
-static int wsp_h8_getc(void)
-{
-       u8 lsr;
-
-       do {
-               lsr = readb(h8 + LSR);
-       } while ((lsr & LSR_DR) != LSR_DR);
-
-       return readb(h8 + RBR);
-}
-
-static void wsp_h8_puts(const char *s, int sz)
-{
-       int i;
-
-       for (i = 0; i < sz; i++) {
-               wsp_h8_putc(s[i]);
-
-               /* no flow control so wait for echo */
-               wsp_h8_getc();
-       }
-       wsp_h8_putc('\r');
-       wsp_h8_putc('\n');
-}
-
-static void wsp_h8_terminal_cmd(const char *cmd, int sz)
-{
-       hard_irq_disable();
-       wsp_h8_puts(cmd, sz);
-       /* should never return, but just in case */
-       for (;;)
-               continue;
-}
-
-
-void wsp_h8_restart(char *cmd)
-{
-       static const char restart[] = "warm-reset";
-
-       (void)cmd;
-       wsp_h8_terminal_cmd(restart, sizeof(restart) - 1);
-}
-
-void wsp_h8_power_off(void)
-{
-       static const char off[] = "power-off";
-
-       wsp_h8_terminal_cmd(off, sizeof(off) - 1);
-}
-
-static void __iomem *wsp_h8_getaddr(void)
-{
-       struct device_node *aliases;
-       struct device_node *uart;
-       struct property *path;
-       void __iomem *va = NULL;
-
-       /*
-        * there is nothing in the devtree to tell us which is mapped
-        * to the H8, but se know it is the second serial port.
-        */
-
-       aliases = of_find_node_by_path("/aliases");
-       if (aliases == NULL)
-               return NULL;
-
-       path = of_find_property(aliases, "serial1", NULL);
-       if (path == NULL)
-               goto out;
-
-       uart = of_find_node_by_path(path->value);
-       if (uart == NULL)
-               goto out;
-
-       va = of_iomap(uart, 0);
-
-       /* remove it so no one messes with it */
-       of_detach_node(uart);
-       of_node_put(uart);
-
-out:
-       of_node_put(aliases);
-
-       return va;
-}
-
-void __init wsp_setup_h8(void)
-{
-       h8 = wsp_h8_getaddr();
-
-       /* Devtree change? lets hard map it anyway */
-       if (h8 == NULL) {
-               pr_warn("UART to H8 could not be found");
-               h8 = ioremap(0xffc0008000ULL, 0x100);
-       }
-}
diff --git a/arch/powerpc/platforms/wsp/ics.c b/arch/powerpc/platforms/wsp/ics.c
deleted file mode 100644 (file)
index 9cd92e6..0000000
+++ /dev/null
@@ -1,762 +0,0 @@
-/*
- * Copyright 2008-2011 IBM Corporation.
- *
- *  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/cpu.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/kernel.h>
-#include <linux/msi.h>
-#include <linux/of.h>
-#include <linux/slab.h>
-#include <linux/smp.h>
-#include <linux/spinlock.h>
-#include <linux/types.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/xics.h>
-
-#include "wsp.h"
-#include "ics.h"
-
-
-/* WSP ICS */
-
-struct wsp_ics {
-       struct ics ics;
-       struct device_node *dn;
-       void __iomem *regs;
-       spinlock_t lock;
-       unsigned long *bitmap;
-       u32 chip_id;
-       u32 lsi_base;
-       u32 lsi_count;
-       u64 hwirq_start;
-       u64 count;
-#ifdef CONFIG_SMP
-       int *hwirq_cpu_map;
-#endif
-};
-
-#define to_wsp_ics(ics)        container_of(ics, struct wsp_ics, ics)
-
-#define INT_SRC_LAYER_BUID_REG(base)   ((base) + 0x00)
-#define IODA_TBL_ADDR_REG(base)                ((base) + 0x18)
-#define IODA_TBL_DATA_REG(base)                ((base) + 0x20)
-#define XIVE_UPDATE_REG(base)          ((base) + 0x28)
-#define ICS_INT_CAPS_REG(base)         ((base) + 0x30)
-
-#define TBL_AUTO_INCREMENT     ((1UL << 63) | (1UL << 15))
-#define TBL_SELECT_XIST                (1UL << 48)
-#define TBL_SELECT_XIVT                (1UL << 49)
-
-#define IODA_IRQ(irq)          ((irq) & (0x7FFULL))    /* HRM 5.1.3.4 */
-
-#define XIST_REQUIRED          0x8
-#define XIST_REJECTED          0x4
-#define XIST_PRESENTED         0x2
-#define XIST_PENDING           0x1
-
-#define XIVE_SERVER_SHIFT      42
-#define XIVE_SERVER_MASK       0xFFFFULL
-#define XIVE_PRIORITY_MASK     0xFFULL
-#define XIVE_PRIORITY_SHIFT    32
-#define XIVE_WRITE_ENABLE      (1ULL << 63)
-
-/*
- * The docs refer to a 6 bit field called ChipID, which consists of a
- * 3 bit NodeID and a 3 bit ChipID. On WSP the ChipID is always zero
- * so we ignore it, and every where we use "chip id" in this code we
- * mean the NodeID.
- */
-#define WSP_ICS_CHIP_SHIFT             17
-
-
-static struct wsp_ics *ics_list;
-static int num_ics;
-
-/* ICS Source controller accessors */
-
-static u64 wsp_ics_get_xive(struct wsp_ics *ics, unsigned int irq)
-{
-       unsigned long flags;
-       u64 xive;
-
-       spin_lock_irqsave(&ics->lock, flags);
-       out_be64(IODA_TBL_ADDR_REG(ics->regs), TBL_SELECT_XIVT | IODA_IRQ(irq));
-       xive = in_be64(IODA_TBL_DATA_REG(ics->regs));
-       spin_unlock_irqrestore(&ics->lock, flags);
-
-       return xive;
-}
-
-static void wsp_ics_set_xive(struct wsp_ics *ics, unsigned int irq, u64 xive)
-{
-       xive &= ~XIVE_ADDR_MASK;
-       xive |= (irq & XIVE_ADDR_MASK);
-       xive |= XIVE_WRITE_ENABLE;
-
-       out_be64(XIVE_UPDATE_REG(ics->regs), xive);
-}
-
-static u64 xive_set_server(u64 xive, unsigned int server)
-{
-       u64 mask = ~(XIVE_SERVER_MASK << XIVE_SERVER_SHIFT);
-
-       xive &= mask;
-       xive |= (server & XIVE_SERVER_MASK) << XIVE_SERVER_SHIFT;
-
-       return xive;
-}
-
-static u64 xive_set_priority(u64 xive, unsigned int priority)
-{
-       u64 mask = ~(XIVE_PRIORITY_MASK << XIVE_PRIORITY_SHIFT);
-
-       xive &= mask;
-       xive |= (priority & XIVE_PRIORITY_MASK) << XIVE_PRIORITY_SHIFT;
-
-       return xive;
-}
-
-
-#ifdef CONFIG_SMP
-/* Find logical CPUs within mask on a given chip and store result in ret */
-void cpus_on_chip(int chip_id, cpumask_t *mask, cpumask_t *ret)
-{
-       int cpu, chip;
-       struct device_node *cpu_dn, *dn;
-       const u32 *prop;
-
-       cpumask_clear(ret);
-       for_each_cpu(cpu, mask) {
-               cpu_dn = of_get_cpu_node(cpu, NULL);
-               if (!cpu_dn)
-                       continue;
-
-               prop = of_get_property(cpu_dn, "at-node", NULL);
-               if (!prop) {
-                       of_node_put(cpu_dn);
-                       continue;
-               }
-
-               dn = of_find_node_by_phandle(*prop);
-               of_node_put(cpu_dn);
-
-               chip = wsp_get_chip_id(dn);
-               if (chip == chip_id)
-                       cpumask_set_cpu(cpu, ret);
-
-               of_node_put(dn);
-       }
-}
-
-/* Store a suitable CPU to handle a hwirq in the ics->hwirq_cpu_map cache */
-static int cache_hwirq_map(struct wsp_ics *ics, unsigned int hwirq,
-                          const cpumask_t *affinity)
-{
-       cpumask_var_t avail, newmask;
-       int ret = -ENOMEM, cpu, cpu_rover = 0, target;
-       int index = hwirq - ics->hwirq_start;
-       unsigned int nodeid;
-
-       BUG_ON(index < 0 || index >= ics->count);
-
-       if (!ics->hwirq_cpu_map)
-               return -ENOMEM;
-
-       if (!distribute_irqs) {
-               ics->hwirq_cpu_map[hwirq - ics->hwirq_start] = xics_default_server;
-               return 0;
-       }
-
-       /* Allocate needed CPU masks */
-       if (!alloc_cpumask_var(&avail, GFP_KERNEL))
-               goto ret;
-       if (!alloc_cpumask_var(&newmask, GFP_KERNEL))
-               goto freeavail;
-
-       /* Find PBus attached to the source of this IRQ */
-       nodeid = (hwirq >> WSP_ICS_CHIP_SHIFT) & 0x3; /* 12:14 */
-
-       /* Find CPUs that could handle this IRQ */
-       if (affinity)
-               cpumask_and(avail, cpu_online_mask, affinity);
-       else
-               cpumask_copy(avail, cpu_online_mask);
-
-       /* Narrow selection down to logical CPUs on the same chip */
-       cpus_on_chip(nodeid, avail, newmask);
-
-       /* Ensure we haven't narrowed it down to 0 */
-       if (unlikely(cpumask_empty(newmask))) {
-               if (unlikely(cpumask_empty(avail))) {
-                       ret = -1;
-                       goto out;
-               }
-               cpumask_copy(newmask, avail);
-       }
-
-       /* Choose a CPU out of those we narrowed it down to in round robin */
-       target = hwirq % cpumask_weight(newmask);
-       for_each_cpu(cpu, newmask) {
-               if (cpu_rover++ >= target) {
-                       ics->hwirq_cpu_map[index] = get_hard_smp_processor_id(cpu);
-                       ret = 0;
-                       goto out;
-               }
-       }
-
-       /* Shouldn't happen */
-       WARN_ON(1);
-
-out:
-       free_cpumask_var(newmask);
-freeavail:
-       free_cpumask_var(avail);
-ret:
-       if (ret < 0) {
-               ics->hwirq_cpu_map[index] = cpumask_first(cpu_online_mask);
-               pr_warning("Error, falling hwirq 0x%x routing back to CPU %i\n",
-                          hwirq, ics->hwirq_cpu_map[index]);
-       }
-       return ret;
-}
-
-static void alloc_irq_map(struct wsp_ics *ics)
-{
-       int i;
-
-       ics->hwirq_cpu_map = kmalloc(sizeof(int) * ics->count, GFP_KERNEL);
-       if (!ics->hwirq_cpu_map) {
-               pr_warning("Allocate hwirq_cpu_map failed, "
-                          "IRQ balancing disabled\n");
-               return;
-       }
-
-       for (i=0; i < ics->count; i++)
-               ics->hwirq_cpu_map[i] = xics_default_server;
-}
-
-static int get_irq_server(struct wsp_ics *ics, unsigned int hwirq)
-{
-       int index = hwirq - ics->hwirq_start;
-
-       BUG_ON(index < 0 || index >= ics->count);
-
-       if (!ics->hwirq_cpu_map)
-               return xics_default_server;
-
-       return ics->hwirq_cpu_map[index];
-}
-#else /* !CONFIG_SMP */
-static int cache_hwirq_map(struct wsp_ics *ics, unsigned int hwirq,
-                          const cpumask_t *affinity)
-{
-       return 0;
-}
-
-static int get_irq_server(struct wsp_ics *ics, unsigned int hwirq)
-{
-       return xics_default_server;
-}
-
-static void alloc_irq_map(struct wsp_ics *ics) { }
-#endif
-
-static void wsp_chip_unmask_irq(struct irq_data *d)
-{
-       unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
-       struct wsp_ics *ics;
-       int server;
-       u64 xive;
-
-       if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
-               return;
-
-       ics = d->chip_data;
-       if (WARN_ON(!ics))
-               return;
-
-       server = get_irq_server(ics, hw_irq);
-
-       xive = wsp_ics_get_xive(ics, hw_irq);
-       xive = xive_set_server(xive, server);
-       xive = xive_set_priority(xive, DEFAULT_PRIORITY);
-       wsp_ics_set_xive(ics, hw_irq, xive);
-}
-
-static unsigned int wsp_chip_startup(struct irq_data *d)
-{
-       /* unmask it */
-       wsp_chip_unmask_irq(d);
-       return 0;
-}
-
-static void wsp_mask_real_irq(unsigned int hw_irq, struct wsp_ics *ics)
-{
-       u64 xive;
-
-       if (hw_irq == XICS_IPI)
-               return;
-
-       if (WARN_ON(!ics))
-               return;
-       xive = wsp_ics_get_xive(ics, hw_irq);
-       xive = xive_set_server(xive, xics_default_server);
-       xive = xive_set_priority(xive, LOWEST_PRIORITY);
-       wsp_ics_set_xive(ics, hw_irq, xive);
-}
-
-static void wsp_chip_mask_irq(struct irq_data *d)
-{
-       unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
-       struct wsp_ics *ics = d->chip_data;
-
-       if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
-               return;
-
-       wsp_mask_real_irq(hw_irq, ics);
-}
-
-static int wsp_chip_set_affinity(struct irq_data *d,
-                                const struct cpumask *cpumask, bool force)
-{
-       unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
-       struct wsp_ics *ics;
-       int ret;
-       u64 xive;
-
-       if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)
-               return -1;
-
-       ics = d->chip_data;
-       if (WARN_ON(!ics))
-               return -1;
-       xive = wsp_ics_get_xive(ics, hw_irq);
-
-       /*
-        * For the moment only implement delivery to all cpus or one cpu.
-        * Get current irq_server for the given irq
-        */
-       ret = cache_hwirq_map(ics, hw_irq, cpumask);
-       if (ret == -1) {
-               char cpulist[128];
-               cpumask_scnprintf(cpulist, sizeof(cpulist), cpumask);
-               pr_warning("%s: No online cpus in the mask %s for irq %d\n",
-                          __func__, cpulist, d->irq);
-               return -1;
-       } else if (ret == -ENOMEM) {
-               pr_warning("%s: Out of memory\n", __func__);
-               return -1;
-       }
-
-       xive = xive_set_server(xive, get_irq_server(ics, hw_irq));
-       wsp_ics_set_xive(ics, hw_irq, xive);
-
-       return IRQ_SET_MASK_OK;
-}
-
-static struct irq_chip wsp_irq_chip = {
-       .name = "WSP ICS",
-       .irq_startup            = wsp_chip_startup,
-       .irq_mask               = wsp_chip_mask_irq,
-       .irq_unmask             = wsp_chip_unmask_irq,
-       .irq_set_affinity       = wsp_chip_set_affinity
-};
-
-static int wsp_ics_host_match(struct ics *ics, struct device_node *dn)
-{
-       /* All ICSs in the system implement a global irq number space,
-        * so match against them all. */
-       return of_device_is_compatible(dn, "ibm,ppc-xics");
-}
-
-static int wsp_ics_match_hwirq(struct wsp_ics *wsp_ics, unsigned int hwirq)
-{
-       if (hwirq >= wsp_ics->hwirq_start &&
-           hwirq <  wsp_ics->hwirq_start + wsp_ics->count)
-               return 1;
-
-       return 0;
-}
-
-static int wsp_ics_map(struct ics *ics, unsigned int virq)
-{
-       struct wsp_ics *wsp_ics = to_wsp_ics(ics);
-       unsigned int hw_irq = virq_to_hw(virq);
-       unsigned long flags;
-
-       if (!wsp_ics_match_hwirq(wsp_ics, hw_irq))
-               return -ENOENT;
-
-       irq_set_chip_and_handler(virq, &wsp_irq_chip, handle_fasteoi_irq);
-
-       irq_set_chip_data(virq, wsp_ics);
-
-       spin_lock_irqsave(&wsp_ics->lock, flags);
-       bitmap_allocate_region(wsp_ics->bitmap, hw_irq - wsp_ics->hwirq_start, 0);
-       spin_unlock_irqrestore(&wsp_ics->lock, flags);
-
-       return 0;
-}
-
-static void wsp_ics_mask_unknown(struct ics *ics, unsigned long hw_irq)
-{
-       struct wsp_ics *wsp_ics = to_wsp_ics(ics);
-
-       if (!wsp_ics_match_hwirq(wsp_ics, hw_irq))
-               return;
-
-       pr_err("%s: IRQ %lu (real) is invalid, disabling it.\n", __func__, hw_irq);
-       wsp_mask_real_irq(hw_irq, wsp_ics);
-}
-
-static long wsp_ics_get_server(struct ics *ics, unsigned long hw_irq)
-{
-       struct wsp_ics *wsp_ics = to_wsp_ics(ics);
-
-       if (!wsp_ics_match_hwirq(wsp_ics, hw_irq))
-               return -ENOENT;
-
-       return get_irq_server(wsp_ics, hw_irq);
-}
-
-/* HW Number allocation API */
-
-static struct wsp_ics *wsp_ics_find_dn_ics(struct device_node *dn)
-{
-       struct device_node *iparent;
-       int i;
-
-       iparent = of_irq_find_parent(dn);
-       if (!iparent) {
-               pr_err("wsp_ics: Failed to find interrupt parent!\n");
-               return NULL;
-       }
-
-       for(i = 0; i < num_ics; i++) {
-               if(ics_list[i].dn == iparent)
-                       break;
-       }
-
-       if (i >= num_ics) {
-               pr_err("wsp_ics: Unable to find parent bitmap!\n");
-               return NULL;
-       }
-
-       return &ics_list[i];
-}
-
-int wsp_ics_alloc_irq(struct device_node *dn, int num)
-{
-       struct wsp_ics *ics;
-       int order, offset;
-
-       ics = wsp_ics_find_dn_ics(dn);
-       if (!ics)
-               return -ENODEV;
-
-       /* Fast, but overly strict if num isn't a power of two */
-       order = get_count_order(num);
-
-       spin_lock_irq(&ics->lock);
-       offset = bitmap_find_free_region(ics->bitmap, ics->count, order);
-       spin_unlock_irq(&ics->lock);
-
-       if (offset < 0)
-               return offset;
-
-       return offset + ics->hwirq_start;
-}
-
-void wsp_ics_free_irq(struct device_node *dn, unsigned int irq)
-{
-       struct wsp_ics *ics;
-
-       ics = wsp_ics_find_dn_ics(dn);
-       if (WARN_ON(!ics))
-               return;
-
-       spin_lock_irq(&ics->lock);
-       bitmap_release_region(ics->bitmap, irq, 0);
-       spin_unlock_irq(&ics->lock);
-}
-
-/* Initialisation */
-
-static int __init wsp_ics_bitmap_setup(struct wsp_ics *ics,
-                                     struct device_node *dn)
-{
-       int len, i, j, size;
-       u32 start, count;
-       const u32 *p;
-
-       size = BITS_TO_LONGS(ics->count) * sizeof(long);
-       ics->bitmap = kzalloc(size, GFP_KERNEL);
-       if (!ics->bitmap) {
-               pr_err("wsp_ics: ENOMEM allocating IRQ bitmap!\n");
-               return -ENOMEM;
-       }
-
-       spin_lock_init(&ics->lock);
-
-       p = of_get_property(dn, "available-ranges", &len);
-       if (!p || !len) {
-               /* FIXME this should be a WARN() once mambo is updated */
-               pr_err("wsp_ics: No available-ranges defined for %s\n",
-                       dn->full_name);
-               return 0;
-       }
-
-       if (len % (2 * sizeof(u32)) != 0) {
-               /* FIXME this should be a WARN() once mambo is updated */
-               pr_err("wsp_ics: Invalid available-ranges for %s\n",
-                       dn->full_name);
-               return 0;
-       }
-
-       bitmap_fill(ics->bitmap, ics->count);
-
-       for (i = 0; i < len / sizeof(u32); i += 2) {
-               start = of_read_number(p + i, 1);
-               count = of_read_number(p + i + 1, 1);
-
-               pr_devel("%s: start: %d count: %d\n", __func__, start, count);
-
-               if ((start + count) > (ics->hwirq_start + ics->count) ||
-                    start < ics->hwirq_start) {
-                       pr_err("wsp_ics: Invalid range! -> %d to %d\n",
-                                       start, start + count);
-                       break;
-               }
-
-               for (j = 0; j < count; j++)
-                       bitmap_release_region(ics->bitmap,
-                               (start + j) - ics->hwirq_start, 0);
-       }
-
-       /* Ensure LSIs are not available for allocation */
-       bitmap_allocate_region(ics->bitmap, ics->lsi_base,
-                              get_count_order(ics->lsi_count));
-
-       return 0;
-}
-
-static int __init wsp_ics_setup(struct wsp_ics *ics, struct device_node *dn)
-{
-       u32 lsi_buid, msi_buid, msi_base, msi_count;
-       void __iomem *regs;
-       const u32 *p;
-       int rc, len, i;
-       u64 caps, buid;
-
-       p = of_get_property(dn, "interrupt-ranges", &len);
-       if (!p || len < (2 * sizeof(u32))) {
-               pr_err("wsp_ics: No/bad interrupt-ranges found on %s\n",
-                       dn->full_name);
-               return -ENOENT;
-       }
-
-       if (len > (2 * sizeof(u32))) {
-               pr_err("wsp_ics: Multiple ics ranges not supported.\n");
-               return -EINVAL;
-       }
-
-       regs = of_iomap(dn, 0);
-       if (!regs) {
-               pr_err("wsp_ics: of_iomap(%s) failed\n", dn->full_name);
-               return -ENXIO;
-       }
-
-       ics->hwirq_start = of_read_number(p, 1);
-       ics->count = of_read_number(p + 1, 1);
-       ics->regs = regs;
-
-       ics->chip_id = wsp_get_chip_id(dn);
-       if (WARN_ON(ics->chip_id < 0))
-               ics->chip_id = 0;
-
-       /* Get some informations about the critter */
-       caps = in_be64(ICS_INT_CAPS_REG(ics->regs));
-       buid = in_be64(INT_SRC_LAYER_BUID_REG(ics->regs));
-       ics->lsi_count = caps >> 56;
-       msi_count = (caps >> 44) & 0x7ff;
-
-       /* Note: LSI BUID is 9 bits, but really only 3 are BUID and the
-        * rest is mixed in the interrupt number. We store the whole
-        * thing though
-        */
-       lsi_buid = (buid >> 48) & 0x1ff;
-       ics->lsi_base = (ics->chip_id << WSP_ICS_CHIP_SHIFT) | lsi_buid << 5;
-       msi_buid = (buid >> 37) & 0x7;
-       msi_base = (ics->chip_id << WSP_ICS_CHIP_SHIFT) | msi_buid << 11;
-
-       pr_info("wsp_ics: Found %s\n", dn->full_name);
-       pr_info("wsp_ics:    irq range : 0x%06llx..0x%06llx\n",
-               ics->hwirq_start, ics->hwirq_start + ics->count - 1);
-       pr_info("wsp_ics:    %4d LSIs : 0x%06x..0x%06x\n",
-               ics->lsi_count, ics->lsi_base,
-               ics->lsi_base + ics->lsi_count - 1);
-       pr_info("wsp_ics:    %4d MSIs : 0x%06x..0x%06x\n",
-               msi_count, msi_base,
-               msi_base + msi_count - 1);
-
-       /* Let's check the HW config is sane */
-       if (ics->lsi_base < ics->hwirq_start ||
-           (ics->lsi_base + ics->lsi_count) > (ics->hwirq_start + ics->count))
-               pr_warning("wsp_ics: WARNING ! LSIs out of interrupt-ranges !\n");
-       if (msi_base < ics->hwirq_start ||
-           (msi_base + msi_count) > (ics->hwirq_start + ics->count))
-               pr_warning("wsp_ics: WARNING ! MSIs out of interrupt-ranges !\n");
-
-       /* We don't check for overlap between LSI and MSI, which will happen
-        * if we use the same BUID, I'm not sure yet how legit that is.
-        */
-
-       rc = wsp_ics_bitmap_setup(ics, dn);
-       if (rc) {
-               iounmap(regs);
-               return rc;
-       }
-
-       ics->dn = of_node_get(dn);
-       alloc_irq_map(ics);
-
-       for(i = 0; i < ics->count; i++)
-               wsp_mask_real_irq(ics->hwirq_start + i, ics);
-
-       ics->ics.map = wsp_ics_map;
-       ics->ics.mask_unknown = wsp_ics_mask_unknown;
-       ics->ics.get_server = wsp_ics_get_server;
-       ics->ics.host_match = wsp_ics_host_match;
-
-       xics_register_ics(&ics->ics);
-
-       return 0;
-}
-
-static void __init wsp_ics_set_default_server(void)
-{
-       struct device_node *np;
-       u32 hwid;
-
-       /* Find the server number for the boot cpu. */
-       np = of_get_cpu_node(boot_cpuid, NULL);
-       BUG_ON(!np);
-
-       hwid = get_hard_smp_processor_id(boot_cpuid);
-
-       pr_info("wsp_ics: default server is %#x, CPU %s\n", hwid, np->full_name);
-       xics_default_server = hwid;
-
-       of_node_put(np);
-}
-
-static int __init wsp_ics_init(void)
-{
-       struct device_node *dn;
-       struct wsp_ics *ics;
-       int rc, found;
-
-       wsp_ics_set_default_server();
-
-       found = 0;
-       for_each_compatible_node(dn, NULL, "ibm,ppc-xics")
-               found++;
-
-       if (found == 0) {
-               pr_err("wsp_ics: No ICS's found!\n");
-               return -ENODEV;
-       }
-
-       ics_list = kmalloc(sizeof(*ics) * found, GFP_KERNEL);
-       if (!ics_list) {
-               pr_err("wsp_ics: No memory for structs.\n");
-               return -ENOMEM;
-       }
-
-       num_ics = 0;
-       ics = ics_list;
-       for_each_compatible_node(dn, NULL, "ibm,wsp-xics") {
-               rc = wsp_ics_setup(ics, dn);
-               if (rc == 0) {
-                       ics++;
-                       num_ics++;
-               }
-       }
-
-       if (found != num_ics) {
-               pr_err("wsp_ics: Failed setting up %d ICS's\n",
-                       found - num_ics);
-               return -1;
-       }
-
-       return 0;
-}
-
-void __init wsp_init_irq(void)
-{
-       wsp_ics_init();
-       xics_init();
-
-       /* We need to patch our irq chip's EOI to point to the right ICP */
-       wsp_irq_chip.irq_eoi = icp_ops->eoi;
-}
-
-#ifdef CONFIG_PCI_MSI
-static void wsp_ics_msi_unmask_irq(struct irq_data *d)
-{
-       wsp_chip_unmask_irq(d);
-       unmask_msi_irq(d);
-}
-
-static unsigned int wsp_ics_msi_startup(struct irq_data *d)
-{
-       wsp_ics_msi_unmask_irq(d);
-       return 0;
-}
-
-static void wsp_ics_msi_mask_irq(struct irq_data *d)
-{
-       mask_msi_irq(d);
-       wsp_chip_mask_irq(d);
-}
-
-/*
- * we do it this way because we reassinge default EOI handling in
- * irq_init() above
- */
-static void wsp_ics_eoi(struct irq_data *data)
-{
-       wsp_irq_chip.irq_eoi(data);
-}
-
-static struct irq_chip wsp_ics_msi = {
-       .name = "WSP ICS MSI",
-       .irq_startup = wsp_ics_msi_startup,
-       .irq_mask = wsp_ics_msi_mask_irq,
-       .irq_unmask = wsp_ics_msi_unmask_irq,
-       .irq_eoi = wsp_ics_eoi,
-       .irq_set_affinity = wsp_chip_set_affinity
-};
-
-void wsp_ics_set_msi_chip(unsigned int irq)
-{
-       irq_set_chip(irq, &wsp_ics_msi);
-}
-
-void wsp_ics_set_std_chip(unsigned int irq)
-{
-       irq_set_chip(irq, &wsp_irq_chip);
-}
-#endif /* CONFIG_PCI_MSI */
diff --git a/arch/powerpc/platforms/wsp/ics.h b/arch/powerpc/platforms/wsp/ics.h
deleted file mode 100644 (file)
index 07b644e..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2009 IBM Corporation.
- *
- *  This program is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU General Public License
- *  as published by the Free Software Foundation; either version
- *  2 of the License, or (at your option) any later version.
- */
-
-#ifndef __ICS_H
-#define __ICS_H
-
-#define XIVE_ADDR_MASK         0x7FFULL
-
-extern void wsp_init_irq(void);
-
-extern int wsp_ics_alloc_irq(struct device_node *dn, int num);
-extern void wsp_ics_free_irq(struct device_node *dn, unsigned int irq);
-
-#ifdef CONFIG_PCI_MSI
-extern void wsp_ics_set_msi_chip(unsigned int irq);
-extern void wsp_ics_set_std_chip(unsigned int irq);
-#endif /* CONFIG_PCI_MSI */
-
-#endif /* __ICS_H */
diff --git a/arch/powerpc/platforms/wsp/msi.c b/arch/powerpc/platforms/wsp/msi.c
deleted file mode 100644 (file)
index 380882f..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright 2011 Michael Ellerman, IBM Corp.
- *
- * 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/kernel.h>
-#include <linux/pci.h>
-#include <linux/msi.h>
-#include <linux/irq.h>
-#include <linux/interrupt.h>
-
-#include "msi.h"
-#include "ics.h"
-#include "wsp_pci.h"
-
-/* Magic addresses for 32 & 64-bit MSIs with hardcoded MVE 0 */
-#define MSI_ADDR_32            0xFFFF0000ul
-#define MSI_ADDR_64            0x1000000000000000ul
-
-int wsp_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
-{
-       struct pci_controller *phb;
-       struct msi_desc *entry;
-       struct msi_msg msg;
-       unsigned int virq;
-       int hwirq;
-
-       phb = pci_bus_to_host(dev->bus);
-       if (!phb)
-               return -ENOENT;
-
-       entry = list_first_entry(&dev->msi_list, struct msi_desc, list);
-       if (entry->msi_attrib.is_64) {
-               msg.address_lo = 0;
-               msg.address_hi = MSI_ADDR_64 >> 32;
-       } else {
-               msg.address_lo = MSI_ADDR_32;
-               msg.address_hi = 0;
-       }
-
-       list_for_each_entry(entry, &dev->msi_list, list) {
-               hwirq = wsp_ics_alloc_irq(phb->dn, 1);
-               if (hwirq < 0) {
-                       dev_warn(&dev->dev, "wsp_msi: hwirq alloc failed!\n");
-                       return hwirq;
-               }
-
-               virq = irq_create_mapping(NULL, hwirq);
-               if (virq == NO_IRQ) {
-                       dev_warn(&dev->dev, "wsp_msi: virq alloc failed!\n");
-                       return -1;
-               }
-
-               dev_dbg(&dev->dev, "wsp_msi: allocated irq %#x/%#x\n",
-                       hwirq, virq);
-
-               wsp_ics_set_msi_chip(virq);
-               irq_set_msi_desc(virq, entry);
-               msg.data = hwirq & XIVE_ADDR_MASK;
-               write_msi_msg(virq, &msg);
-       }
-
-       return 0;
-}
-
-void wsp_teardown_msi_irqs(struct pci_dev *dev)
-{
-       struct pci_controller *phb;
-       struct msi_desc *entry;
-       int hwirq;
-
-       phb = pci_bus_to_host(dev->bus);
-
-       dev_dbg(&dev->dev, "wsp_msi: tearing down msi irqs\n");
-
-       list_for_each_entry(entry, &dev->msi_list, list) {
-               if (entry->irq == NO_IRQ)
-                       continue;
-
-               irq_set_msi_desc(entry->irq, NULL);
-               wsp_ics_set_std_chip(entry->irq);
-
-               hwirq = virq_to_hw(entry->irq);
-               /* In this order to avoid racing with irq_create_mapping() */
-               irq_dispose_mapping(entry->irq);
-               wsp_ics_free_irq(phb->dn, hwirq);
-       }
-}
-
-void wsp_setup_phb_msi(struct pci_controller *phb)
-{
-       /* Create a single MVE at offset 0 that matches everything */
-       out_be64(phb->cfg_data + PCIE_REG_IODA_ADDR, PCIE_REG_IODA_AD_TBL_MVT);
-       out_be64(phb->cfg_data + PCIE_REG_IODA_DATA0, 1ull << 63);
-
-       ppc_md.setup_msi_irqs = wsp_setup_msi_irqs;
-       ppc_md.teardown_msi_irqs = wsp_teardown_msi_irqs;
-}
diff --git a/arch/powerpc/platforms/wsp/msi.h b/arch/powerpc/platforms/wsp/msi.h
deleted file mode 100644 (file)
index 0ab27b7..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright 2011 Michael Ellerman, IBM Corp.
- *
- *  This program is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU General Public License
- *  as published by the Free Software Foundation; either version
- *  2 of the License, or (at your option) any later version.
- */
-
-#ifndef __WSP_MSI_H
-#define __WSP_MSI_H
-
-#ifdef CONFIG_PCI_MSI
-extern void wsp_setup_phb_msi(struct pci_controller *phb);
-#else
-static inline void wsp_setup_phb_msi(struct pci_controller *phb) { }
-#endif
-
-#endif /* __WSP_MSI_H */
diff --git a/arch/powerpc/platforms/wsp/opb_pic.c b/arch/powerpc/platforms/wsp/opb_pic.c
deleted file mode 100644 (file)
index 3f67298..0000000
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * IBM Onboard Peripheral Bus Interrupt Controller
- *
- * Copyright 2010 Jack Miller, IBM Corporation.
- *
- * 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/interrupt.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/of.h>
-#include <linux/slab.h>
-#include <linux/time.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-
-#include <asm/reg_a2.h>
-#include <asm/irq.h>
-
-#define OPB_NR_IRQS 32
-
-#define OPB_MLSASIER   0x04    /* MLS Accumulated Status IER */
-#define OPB_MLSIR      0x50    /* MLS Interrupt Register */
-#define OPB_MLSIER     0x54    /* MLS Interrupt Enable Register */
-#define OPB_MLSIPR     0x58    /* MLS Interrupt Polarity Register */
-#define OPB_MLSIIR     0x5c    /* MLS Interrupt Inputs Register */
-
-static int opb_index = 0;
-
-struct opb_pic {
-       struct irq_domain *host;
-       void *regs;
-       int index;
-       spinlock_t lock;
-};
-
-static u32 opb_in(struct opb_pic *opb, int offset)
-{
-       return in_be32(opb->regs + offset);
-}
-
-static void opb_out(struct opb_pic *opb, int offset, u32 val)
-{
-       out_be32(opb->regs + offset, val);
-}
-
-static void opb_unmask_irq(struct irq_data *d)
-{
-       struct opb_pic *opb;
-       unsigned long flags;
-       u32 ier, bitset;
-
-       opb = d->chip_data;
-       bitset = (1 << (31 - irqd_to_hwirq(d)));
-
-       spin_lock_irqsave(&opb->lock, flags);
-
-       ier = opb_in(opb, OPB_MLSIER);
-       opb_out(opb, OPB_MLSIER, ier | bitset);
-       ier = opb_in(opb, OPB_MLSIER);
-
-       spin_unlock_irqrestore(&opb->lock, flags);
-}
-
-static void opb_mask_irq(struct irq_data *d)
-{
-       struct opb_pic *opb;
-       unsigned long flags;
-       u32 ier, mask;
-
-       opb = d->chip_data;
-       mask = ~(1 << (31 - irqd_to_hwirq(d)));
-
-       spin_lock_irqsave(&opb->lock, flags);
-
-       ier = opb_in(opb, OPB_MLSIER);
-       opb_out(opb, OPB_MLSIER, ier & mask);
-       ier = opb_in(opb, OPB_MLSIER); // Flush posted writes
-
-       spin_unlock_irqrestore(&opb->lock, flags);
-}
-
-static void opb_ack_irq(struct irq_data *d)
-{
-       struct opb_pic *opb;
-       unsigned long flags;
-       u32 bitset;
-
-       opb = d->chip_data;
-       bitset = (1 << (31 - irqd_to_hwirq(d)));
-
-       spin_lock_irqsave(&opb->lock, flags);
-
-       opb_out(opb, OPB_MLSIR, bitset);
-       opb_in(opb, OPB_MLSIR); // Flush posted writes
-
-       spin_unlock_irqrestore(&opb->lock, flags);
-}
-
-static void opb_mask_ack_irq(struct irq_data *d)
-{
-       struct opb_pic *opb;
-       unsigned long flags;
-       u32 bitset;
-       u32 ier, ir;
-
-       opb = d->chip_data;
-       bitset = (1 << (31 - irqd_to_hwirq(d)));
-
-       spin_lock_irqsave(&opb->lock, flags);
-
-       ier = opb_in(opb, OPB_MLSIER);
-       opb_out(opb, OPB_MLSIER, ier & ~bitset);
-       ier = opb_in(opb, OPB_MLSIER); // Flush posted writes
-
-       opb_out(opb, OPB_MLSIR, bitset);
-       ir = opb_in(opb, OPB_MLSIR); // Flush posted writes
-
-       spin_unlock_irqrestore(&opb->lock, flags);
-}
-
-static int opb_set_irq_type(struct irq_data *d, unsigned int flow)
-{
-       struct opb_pic *opb;
-       unsigned long flags;
-       int invert, ipr, mask, bit;
-
-       opb = d->chip_data;
-
-       /* The only information we're interested in in the type is whether it's
-        * a high or low trigger. For high triggered interrupts, the polarity
-        * set for it in the MLS Interrupt Polarity Register is 0, for low
-        * interrupts it's 1 so that the proper input in the MLS Interrupt Input
-        * Register is interrupted as asserting the interrupt. */
-
-       switch (flow) {
-               case IRQ_TYPE_NONE:
-                       opb_mask_irq(d);
-                       return 0;
-
-               case IRQ_TYPE_LEVEL_HIGH:
-                       invert = 0;
-                       break;
-
-               case IRQ_TYPE_LEVEL_LOW:
-                       invert = 1;
-                       break;
-
-               default:
-                       return -EINVAL;
-       }
-
-       bit = (1 << (31 - irqd_to_hwirq(d)));
-       mask = ~bit;
-
-       spin_lock_irqsave(&opb->lock, flags);
-
-       ipr = opb_in(opb, OPB_MLSIPR);
-       ipr = (ipr & mask) | (invert ? bit : 0);
-       opb_out(opb, OPB_MLSIPR, ipr);
-       ipr = opb_in(opb, OPB_MLSIPR);  // Flush posted writes
-
-       spin_unlock_irqrestore(&opb->lock, flags);
-
-       /* Record the type in the interrupt descriptor */
-       irqd_set_trigger_type(d, flow);
-
-       return 0;
-}
-
-static struct irq_chip opb_irq_chip = {
-       .name           = "OPB",
-       .irq_mask       = opb_mask_irq,
-       .irq_unmask     = opb_unmask_irq,
-       .irq_mask_ack   = opb_mask_ack_irq,
-       .irq_ack        = opb_ack_irq,
-       .irq_set_type   = opb_set_irq_type
-};
-
-static int opb_host_map(struct irq_domain *host, unsigned int virq,
-               irq_hw_number_t hwirq)
-{
-       struct opb_pic *opb;
-
-       opb = host->host_data;
-
-       /* Most of the important stuff is handled by the generic host code, like
-        * the lookup, so just attach some info to the virtual irq */
-
-       irq_set_chip_data(virq, opb);
-       irq_set_chip_and_handler(virq, &opb_irq_chip, handle_level_irq);
-       irq_set_irq_type(virq, IRQ_TYPE_NONE);
-
-       return 0;
-}
-
-static const struct irq_domain_ops opb_host_ops = {
-       .map = opb_host_map,
-       .xlate = irq_domain_xlate_twocell,
-};
-
-irqreturn_t opb_irq_handler(int irq, void *private)
-{
-       struct opb_pic *opb;
-       u32 ir, src, subvirq;
-
-       opb = (struct opb_pic *) private;
-
-       /* Read the OPB MLS Interrupt Register for
-        * asserted interrupts */
-       ir = opb_in(opb, OPB_MLSIR);
-       if (!ir)
-               return IRQ_NONE;
-
-       do {
-               /* Get 1 - 32 source, *NOT* bit */
-               src = 32 - ffs(ir);
-
-               /* Translate from the OPB's conception of interrupt number to
-                * Linux's virtual IRQ */
-
-               subvirq = irq_linear_revmap(opb->host, src);
-
-               generic_handle_irq(subvirq);
-       } while ((ir = opb_in(opb, OPB_MLSIR)));
-
-       return IRQ_HANDLED;
-}
-
-struct opb_pic *opb_pic_init_one(struct device_node *dn)
-{
-       struct opb_pic *opb;
-       struct resource res;
-
-       if (of_address_to_resource(dn, 0, &res)) {
-               printk(KERN_ERR "opb: Couldn't translate resource\n");
-               return  NULL;
-       }
-
-       opb = kzalloc(sizeof(struct opb_pic), GFP_KERNEL);
-       if (!opb) {
-               printk(KERN_ERR "opb: Failed to allocate opb struct!\n");
-               return NULL;
-       }
-
-       /* Get access to the OPB MMIO registers */
-       opb->regs = ioremap(res.start + 0x10000, 0x1000);
-       if (!opb->regs) {
-               printk(KERN_ERR "opb: Failed to allocate register space!\n");
-               goto free_opb;
-       }
-
-       /* Allocate an irq domain so that Linux knows that despite only
-        * having one interrupt to issue, we're the controller for multiple
-        * hardware IRQs, so later we can lookup their virtual IRQs. */
-
-       opb->host = irq_domain_add_linear(dn, OPB_NR_IRQS, &opb_host_ops, opb);
-       if (!opb->host) {
-               printk(KERN_ERR "opb: Failed to allocate IRQ host!\n");
-               goto free_regs;
-       }
-
-       opb->index = opb_index++;
-       spin_lock_init(&opb->lock);
-
-       /* Disable all interrupts by default */
-       opb_out(opb, OPB_MLSASIER, 0);
-       opb_out(opb, OPB_MLSIER, 0);
-
-       /* ACK any interrupts left by FW */
-       opb_out(opb, OPB_MLSIR, 0xFFFFFFFF);
-
-       return opb;
-
-free_regs:
-       iounmap(opb->regs);
-free_opb:
-       kfree(opb);
-       return NULL;
-}
-
-void __init opb_pic_init(void)
-{
-       struct device_node *dn;
-       struct opb_pic *opb;
-       int virq;
-       int rc;
-
-       /* Call init_one for each OPB device */
-       for_each_compatible_node(dn, NULL, "ibm,opb") {
-
-               /* Fill in an OPB struct */
-               opb = opb_pic_init_one(dn);
-               if (!opb) {
-                       printk(KERN_WARNING "opb: Failed to init node, skipped!\n");
-                       continue;
-               }
-
-               /* Map / get opb's hardware virtual irq */
-               virq = irq_of_parse_and_map(dn, 0);
-               if (virq <= 0) {
-                       printk("opb: irq_op_parse_and_map failed!\n");
-                       continue;
-               }
-
-               /* Attach opb interrupt handler to new virtual IRQ */
-               rc = request_irq(virq, opb_irq_handler, IRQF_NO_THREAD,
-                                "OPB LS Cascade", opb);
-               if (rc) {
-                       printk("opb: request_irq failed: %d\n", rc);
-                       continue;
-               }
-
-               printk("OPB%d init with %d IRQs at %p\n", opb->index,
-                               OPB_NR_IRQS, opb->regs);
-       }
-}
diff --git a/arch/powerpc/platforms/wsp/psr2.c b/arch/powerpc/platforms/wsp/psr2.c
deleted file mode 100644 (file)
index a87b414..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2008-2011, IBM Corporation
- *
- * 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/delay.h>
-#include <linux/init.h>
-#include <linux/irq.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/of.h>
-#include <linux/smp.h>
-#include <linux/time.h>
-#include <linux/of_fdt.h>
-
-#include <asm/machdep.h>
-#include <asm/udbg.h>
-
-#include "ics.h"
-#include "wsp.h"
-
-
-static void psr2_spin(void)
-{
-       hard_irq_disable();
-       for (;;)
-               continue;
-}
-
-static void psr2_restart(char *cmd)
-{
-       psr2_spin();
-}
-
-static int __init psr2_probe(void)
-{
-       unsigned long root = of_get_flat_dt_root();
-
-       if (of_flat_dt_is_compatible(root, "ibm,wsp-chroma")) {
-               /* chroma systems also claim they are psr2s */
-               return 0;
-       }
-
-       if (!of_flat_dt_is_compatible(root, "ibm,psr2"))
-               return 0;
-
-       return 1;
-}
-
-define_machine(psr2_md) {
-       .name                   = "PSR2 A2",
-       .probe                  = psr2_probe,
-       .setup_arch             = wsp_setup_arch,
-       .restart                = psr2_restart,
-       .power_off              = psr2_spin,
-       .halt                   = psr2_spin,
-       .calibrate_decr         = generic_calibrate_decr,
-       .init_IRQ               = wsp_setup_irq,
-       .progress               = udbg_progress,
-       .power_save             = book3e_idle,
-};
-
-machine_arch_initcall(psr2_md, wsp_probe_devices);
diff --git a/arch/powerpc/platforms/wsp/scom_smp.c b/arch/powerpc/platforms/wsp/scom_smp.c
deleted file mode 100644 (file)
index 8c79ce0..0000000
+++ /dev/null
@@ -1,435 +0,0 @@
-/*
- * SCOM support for A2 platforms
- *
- * Copyright 2007-2011 Benjamin Herrenschmidt, David Gibson,
- *                    Michael Ellerman, IBM Corp.
- *
- * 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/cpumask.h>
-#include <linux/io.h>
-#include <linux/of.h>
-#include <linux/spinlock.h>
-#include <linux/types.h>
-
-#include <asm/cputhreads.h>
-#include <asm/reg_a2.h>
-#include <asm/scom.h>
-#include <asm/udbg.h>
-#include <asm/code-patching.h>
-
-#include "wsp.h"
-
-#define SCOM_RAMC              0x2a            /* Ram Command */
-#define SCOM_RAMC_TGT1_EXT     0x80000000
-#define SCOM_RAMC_SRC1_EXT     0x40000000
-#define SCOM_RAMC_SRC2_EXT     0x20000000
-#define SCOM_RAMC_SRC3_EXT     0x10000000
-#define SCOM_RAMC_ENABLE       0x00080000
-#define SCOM_RAMC_THREADSEL    0x00060000
-#define SCOM_RAMC_EXECUTE      0x00010000
-#define SCOM_RAMC_MSR_OVERRIDE 0x00008000
-#define SCOM_RAMC_MSR_PR       0x00004000
-#define SCOM_RAMC_MSR_GS       0x00002000
-#define SCOM_RAMC_FORCE                0x00001000
-#define SCOM_RAMC_FLUSH                0x00000800
-#define SCOM_RAMC_INTERRUPT    0x00000004
-#define SCOM_RAMC_ERROR                0x00000002
-#define SCOM_RAMC_DONE         0x00000001
-#define SCOM_RAMI              0x29            /* Ram Instruction */
-#define SCOM_RAMIC             0x28            /* Ram Instruction and Command */
-#define SCOM_RAMIC_INSN                0xffffffff00000000
-#define SCOM_RAMD              0x2d            /* Ram Data */
-#define SCOM_RAMDH             0x2e            /* Ram Data High */
-#define SCOM_RAMDL             0x2f            /* Ram Data Low */
-#define SCOM_PCCR0             0x33            /* PC Configuration Register 0 */
-#define SCOM_PCCR0_ENABLE_DEBUG        0x80000000
-#define SCOM_PCCR0_ENABLE_RAM  0x40000000
-#define SCOM_THRCTL            0x30            /* Thread Control and Status */
-#define SCOM_THRCTL_T0_STOP    0x80000000
-#define SCOM_THRCTL_T1_STOP    0x40000000
-#define SCOM_THRCTL_T2_STOP    0x20000000
-#define SCOM_THRCTL_T3_STOP    0x10000000
-#define SCOM_THRCTL_T0_STEP    0x08000000
-#define SCOM_THRCTL_T1_STEP    0x04000000
-#define SCOM_THRCTL_T2_STEP    0x02000000
-#define SCOM_THRCTL_T3_STEP    0x01000000
-#define SCOM_THRCTL_T0_RUN     0x00800000
-#define SCOM_THRCTL_T1_RUN     0x00400000
-#define SCOM_THRCTL_T2_RUN     0x00200000
-#define SCOM_THRCTL_T3_RUN     0x00100000
-#define SCOM_THRCTL_T0_PM      0x00080000
-#define SCOM_THRCTL_T1_PM      0x00040000
-#define SCOM_THRCTL_T2_PM      0x00020000
-#define SCOM_THRCTL_T3_PM      0x00010000
-#define SCOM_THRCTL_T0_UDE     0x00008000
-#define SCOM_THRCTL_T1_UDE     0x00004000
-#define SCOM_THRCTL_T2_UDE     0x00002000
-#define SCOM_THRCTL_T3_UDE     0x00001000
-#define SCOM_THRCTL_ASYNC_DIS  0x00000800
-#define SCOM_THRCTL_TB_DIS     0x00000400
-#define SCOM_THRCTL_DEC_DIS    0x00000200
-#define SCOM_THRCTL_AND                0x31            /* Thread Control and Status */
-#define SCOM_THRCTL_OR         0x32            /* Thread Control and Status */
-
-
-static DEFINE_PER_CPU(scom_map_t, scom_ptrs);
-
-static scom_map_t get_scom(int cpu, struct device_node *np, int *first_thread)
-{
-       scom_map_t scom = per_cpu(scom_ptrs, cpu);
-       int tcpu;
-
-       if (scom_map_ok(scom)) {
-               *first_thread = 0;
-               return scom;
-       }
-
-       *first_thread = 1;
-
-       scom = scom_map_device(np, 0);
-
-       for (tcpu = cpu_first_thread_sibling(cpu);
-            tcpu <= cpu_last_thread_sibling(cpu); tcpu++)
-               per_cpu(scom_ptrs, tcpu) = scom;
-
-       /* Hack: for the boot core, this will actually get called on
-        * the second thread up, not the first so our test above will
-        * set first_thread incorrectly. */
-       if (cpu_first_thread_sibling(cpu) == 0)
-               *first_thread = 0;
-
-       return scom;
-}
-
-static int a2_scom_ram(scom_map_t scom, int thread, u32 insn, int extmask)
-{
-       u64 cmd, mask, val;
-       int n = 0;
-
-       cmd = ((u64)insn << 32) | (((u64)extmask & 0xf) << 28)
-               | ((u64)thread << 17) | SCOM_RAMC_ENABLE | SCOM_RAMC_EXECUTE;
-       mask = SCOM_RAMC_DONE | SCOM_RAMC_INTERRUPT | SCOM_RAMC_ERROR;
-
-       scom_write(scom, SCOM_RAMIC, cmd);
-
-       for (;;) {
-               if (scom_read(scom, SCOM_RAMC, &val) != 0) {
-                       pr_err("SCOM error on instruction 0x%08x, thread %d\n",
-                              insn, thread);
-                       return -1;
-               }
-               if (val & mask)
-                       break;
-               pr_devel("Waiting on RAMC = 0x%llx\n", val);
-               if (++n == 3) {
-                       pr_err("RAMC timeout on instruction 0x%08x, thread %d\n",
-                              insn, thread);
-                       return -1;
-               }
-       }
-
-       if (val & SCOM_RAMC_INTERRUPT) {
-               pr_err("RAMC interrupt on instruction 0x%08x, thread %d\n",
-                      insn, thread);
-               return -SCOM_RAMC_INTERRUPT;
-       }
-
-       if (val & SCOM_RAMC_ERROR) {
-               pr_err("RAMC error on instruction 0x%08x, thread %d\n",
-                      insn, thread);
-               return -SCOM_RAMC_ERROR;
-       }
-
-       return 0;
-}
-
-static int a2_scom_getgpr(scom_map_t scom, int thread, int gpr, int alt,
-                         u64 *out_gpr)
-{
-       int rc;
-
-       /* or rN, rN, rN */
-       u32 insn = 0x7c000378 | (gpr << 21) | (gpr << 16) | (gpr << 11);
-       rc = a2_scom_ram(scom, thread, insn, alt ? 0xf : 0x0);
-       if (rc)
-               return rc;
-
-       return scom_read(scom, SCOM_RAMD, out_gpr);
-}
-
-static int a2_scom_getspr(scom_map_t scom, int thread, int spr, u64 *out_spr)
-{
-       int rc, sprhi, sprlo;
-       u32 insn;
-
-       sprhi = spr >> 5;
-       sprlo = spr & 0x1f;
-       insn = 0x7c2002a6 | (sprlo << 16) | (sprhi << 11); /* mfspr r1,spr */
-
-       if (spr == 0x0ff0)
-               insn = 0x7c2000a6; /* mfmsr r1 */
-
-       rc = a2_scom_ram(scom, thread, insn, 0xf);
-       if (rc)
-               return rc;
-       return a2_scom_getgpr(scom, thread, 1, 1, out_spr);
-}
-
-static int a2_scom_setgpr(scom_map_t scom, int thread, int gpr,
-                         int alt, u64 val)
-{
-       u32 lis = 0x3c000000 | (gpr << 21);
-       u32 li = 0x38000000 | (gpr << 21);
-       u32 oris = 0x64000000 | (gpr << 21) | (gpr << 16);
-       u32 ori = 0x60000000 | (gpr << 21) | (gpr << 16);
-       u32 rldicr32 = 0x780007c6 | (gpr << 21) | (gpr << 16);
-       u32 highest = val >> 48;
-       u32 higher = (val >> 32) & 0xffff;
-       u32 high = (val >> 16) & 0xffff;
-       u32 low = val & 0xffff;
-       int lext = alt ? 0x8 : 0x0;
-       int oext = alt ? 0xf : 0x0;
-       int rc = 0;
-
-       if (highest)
-               rc |= a2_scom_ram(scom, thread, lis | highest, lext);
-
-       if (higher) {
-               if (highest)
-                       rc |= a2_scom_ram(scom, thread, oris | higher, oext);
-               else
-                       rc |= a2_scom_ram(scom, thread, li | higher, lext);
-       }
-
-       if (highest || higher)
-               rc |= a2_scom_ram(scom, thread, rldicr32, oext);
-
-       if (high) {
-               if (highest || higher)
-                       rc |= a2_scom_ram(scom, thread, oris | high, oext);
-               else
-                       rc |= a2_scom_ram(scom, thread, lis | high, lext);
-       }
-
-       if (highest || higher || high)
-               rc |= a2_scom_ram(scom, thread, ori | low, oext);
-       else
-               rc |= a2_scom_ram(scom, thread, li | low, lext);
-
-       return rc;
-}
-
-static int a2_scom_setspr(scom_map_t scom, int thread, int spr, u64 val)
-{
-       int sprhi = spr >> 5;
-       int sprlo = spr & 0x1f;
-       /* mtspr spr, r1 */
-       u32 insn = 0x7c2003a6 | (sprlo << 16) | (sprhi << 11);
-
-       if (spr == 0x0ff0)
-               insn = 0x7c200124; /* mtmsr r1 */
-
-       if (a2_scom_setgpr(scom, thread, 1, 1, val))
-               return -1;
-
-       return a2_scom_ram(scom, thread, insn, 0xf);
-}
-
-static int a2_scom_initial_tlb(scom_map_t scom, int thread)
-{
-       extern u32 a2_tlbinit_code_start[], a2_tlbinit_code_end[];
-       extern u32 a2_tlbinit_after_iprot_flush[];
-       extern u32 a2_tlbinit_after_linear_map[];
-       u32 assoc, entries, i;
-       u64 epn, tlbcfg;
-       u32 *p;
-       int rc;
-
-       /* Invalidate all entries (including iprot) */
-
-       rc = a2_scom_getspr(scom, thread, SPRN_TLB0CFG, &tlbcfg);
-       if (rc)
-               goto scom_fail;
-       entries = tlbcfg & TLBnCFG_N_ENTRY;
-       assoc = (tlbcfg & TLBnCFG_ASSOC) >> 24;
-       epn = 0;
-
-       /* Set MMUCR2 to enable 4K, 64K, 1M, 16M and 1G pages */
-       a2_scom_setspr(scom, thread, SPRN_MMUCR2, 0x000a7531);
-       /* Set MMUCR3 to write all thids bit to the TLB */
-       a2_scom_setspr(scom, thread, SPRN_MMUCR3, 0x0000000f);
-
-       /* Set MAS1 for 1G page size, and MAS2 to our initial EPN */
-       a2_scom_setspr(scom, thread, SPRN_MAS1, MAS1_TSIZE(BOOK3E_PAGESZ_1GB));
-       a2_scom_setspr(scom, thread, SPRN_MAS2, epn);
-       for (i = 0; i < entries; i++) {
-
-               a2_scom_setspr(scom, thread, SPRN_MAS0, MAS0_ESEL(i % assoc));
-
-               /* tlbwe */
-               rc = a2_scom_ram(scom, thread, 0x7c0007a4, 0);
-               if (rc)
-                       goto scom_fail;
-
-               /* Next entry is new address? */
-               if((i + 1) % assoc == 0) {
-                       epn += (1 << 30);
-                       a2_scom_setspr(scom, thread, SPRN_MAS2, epn);
-               }
-       }
-
-       /* Setup args for linear mapping */
-       rc = a2_scom_setgpr(scom, thread, 3, 0, MAS0_TLBSEL(0));
-       if (rc)
-               goto scom_fail;
-
-       /* Linear mapping */
-       for (p = a2_tlbinit_code_start; p < a2_tlbinit_after_linear_map; p++) {
-               rc = a2_scom_ram(scom, thread, *p, 0);
-               if (rc)
-                       goto scom_fail;
-       }
-
-       /*
-        * For the boot thread, between the linear mapping and the debug
-        * mappings there is a loop to flush iprot mappings. Ramming doesn't do
-        * branches, but the secondary threads don't need to be nearly as smart
-        * (i.e. we don't need to worry about invalidating the mapping we're
-        * standing on).
-        */
-
-       /* Debug mappings. Expects r11 = MAS0 from linear map (set above) */
-       for (p = a2_tlbinit_after_iprot_flush; p < a2_tlbinit_code_end; p++) {
-               rc = a2_scom_ram(scom, thread, *p, 0);
-               if (rc)
-                       goto scom_fail;
-       }
-
-scom_fail:
-       if (rc)
-               pr_err("Setting up initial TLB failed, err %d\n", rc);
-
-       if (rc == -SCOM_RAMC_INTERRUPT) {
-               /* Interrupt, dump some status */
-               int rc[10];
-               u64 iar, srr0, srr1, esr, mas0, mas1, mas2, mas7_3, mas8, ccr2;
-               rc[0] = a2_scom_getspr(scom, thread, SPRN_IAR, &iar);
-               rc[1] = a2_scom_getspr(scom, thread, SPRN_SRR0, &srr0);
-               rc[2] = a2_scom_getspr(scom, thread, SPRN_SRR1, &srr1);
-               rc[3] = a2_scom_getspr(scom, thread, SPRN_ESR, &esr);
-               rc[4] = a2_scom_getspr(scom, thread, SPRN_MAS0, &mas0);
-               rc[5] = a2_scom_getspr(scom, thread, SPRN_MAS1, &mas1);
-               rc[6] = a2_scom_getspr(scom, thread, SPRN_MAS2, &mas2);
-               rc[7] = a2_scom_getspr(scom, thread, SPRN_MAS7_MAS3, &mas7_3);
-               rc[8] = a2_scom_getspr(scom, thread, SPRN_MAS8, &mas8);
-               rc[9] = a2_scom_getspr(scom, thread, SPRN_A2_CCR2, &ccr2);
-               pr_err(" -> retreived IAR =0x%llx (err %d)\n", iar, rc[0]);
-               pr_err("    retreived SRR0=0x%llx (err %d)\n", srr0, rc[1]);
-               pr_err("    retreived SRR1=0x%llx (err %d)\n", srr1, rc[2]);
-               pr_err("    retreived ESR =0x%llx (err %d)\n", esr, rc[3]);
-               pr_err("    retreived MAS0=0x%llx (err %d)\n", mas0, rc[4]);
-               pr_err("    retreived MAS1=0x%llx (err %d)\n", mas1, rc[5]);
-               pr_err("    retreived MAS2=0x%llx (err %d)\n", mas2, rc[6]);
-               pr_err("    retreived MS73=0x%llx (err %d)\n", mas7_3, rc[7]);
-               pr_err("    retreived MAS8=0x%llx (err %d)\n", mas8, rc[8]);
-               pr_err("    retreived CCR2=0x%llx (err %d)\n", ccr2, rc[9]);
-       }
-
-       return rc;
-}
-
-int a2_scom_startup_cpu(unsigned int lcpu, int thr_idx, struct device_node *np)
-{
-       u64 init_iar, init_msr, init_ccr2;
-       unsigned long start_here;
-       int rc, core_setup;
-       scom_map_t scom;
-       u64 pccr0;
-
-       scom = get_scom(lcpu, np, &core_setup);
-       if (!scom) {
-               printk(KERN_ERR "Couldn't map SCOM for CPU%d\n", lcpu);
-               return -1;
-       }
-
-       pr_devel("Bringing up CPU%d using SCOM...\n", lcpu);
-
-       if (scom_read(scom, SCOM_PCCR0, &pccr0) != 0) {
-               printk(KERN_ERR "XSCOM failure readng PCCR0 on CPU%d\n", lcpu);
-               return -1;
-       }
-       scom_write(scom, SCOM_PCCR0, pccr0 | SCOM_PCCR0_ENABLE_DEBUG |
-                                    SCOM_PCCR0_ENABLE_RAM);
-
-       /* Stop the thead with THRCTL. If we are setting up the TLB we stop all
-        * threads. We also disable asynchronous interrupts while RAMing.
-        */
-       if (core_setup)
-               scom_write(scom, SCOM_THRCTL_OR,
-                             SCOM_THRCTL_T0_STOP |
-                             SCOM_THRCTL_T1_STOP |
-                             SCOM_THRCTL_T2_STOP |
-                             SCOM_THRCTL_T3_STOP |
-                             SCOM_THRCTL_ASYNC_DIS);
-       else
-               scom_write(scom, SCOM_THRCTL_OR, SCOM_THRCTL_T0_STOP >> thr_idx);
-
-       /* Flush its pipeline just in case */
-       scom_write(scom, SCOM_RAMC, ((u64)thr_idx << 17) |
-                     SCOM_RAMC_FLUSH | SCOM_RAMC_ENABLE);
-
-       a2_scom_getspr(scom, thr_idx, SPRN_IAR, &init_iar);
-       a2_scom_getspr(scom, thr_idx, 0x0ff0, &init_msr);
-       a2_scom_getspr(scom, thr_idx, SPRN_A2_CCR2, &init_ccr2);
-
-       /* Set MSR to MSR_CM (0x0ff0 is magic value for MSR_CM) */
-       rc = a2_scom_setspr(scom, thr_idx, 0x0ff0, MSR_CM);
-       if (rc) {
-               pr_err("Failed to set MSR ! err %d\n", rc);
-               return rc;
-       }
-
-       /* RAM in an sync/isync for the sake of it */
-       a2_scom_ram(scom, thr_idx, 0x7c0004ac, 0);
-       a2_scom_ram(scom, thr_idx, 0x4c00012c, 0);
-
-       if (core_setup) {
-               pr_devel("CPU%d is first thread in core, initializing TLB...\n",
-                        lcpu);
-               rc = a2_scom_initial_tlb(scom, thr_idx);
-               if (rc)
-                       goto fail;
-       }
-
-       start_here = ppc_function_entry(core_setup ? generic_secondary_smp_init
-                                       : generic_secondary_thread_init);
-       pr_devel("CPU%d entry point at 0x%lx...\n", lcpu, start_here);
-
-       rc |= a2_scom_setspr(scom, thr_idx, SPRN_IAR, start_here);
-       rc |= a2_scom_setgpr(scom, thr_idx, 3, 0,
-                            get_hard_smp_processor_id(lcpu));
-       /*
-        * Tell book3e_secondary_core_init not to set up the TLB, we've
-        * already done that.
-        */
-       rc |= a2_scom_setgpr(scom, thr_idx, 4, 0, 1);
-
-       rc |= a2_scom_setspr(scom, thr_idx, SPRN_TENS, 0x1 << thr_idx);
-
-       scom_write(scom, SCOM_RAMC, 0);
-       scom_write(scom, SCOM_THRCTL_AND, ~(SCOM_THRCTL_T0_STOP >> thr_idx));
-       scom_write(scom, SCOM_PCCR0, pccr0);
-fail:
-       pr_devel("  SCOM initialization %s\n", rc ? "failed" : "succeeded");
-       if (rc) {
-               pr_err("Old IAR=0x%08llx MSR=0x%08llx CCR2=0x%08llx\n",
-                      init_iar, init_msr, init_ccr2);
-       }
-
-       return rc;
-}
diff --git a/arch/powerpc/platforms/wsp/scom_wsp.c b/arch/powerpc/platforms/wsp/scom_wsp.c
deleted file mode 100644 (file)
index 6538b4d..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- *  SCOM backend for WSP
- *
- *  Copyright 2010 Benjamin Herrenschmidt, IBM Corp.
- *
- *  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/cpumask.h>
-#include <linux/io.h>
-#include <linux/of.h>
-#include <linux/spinlock.h>
-#include <linux/types.h>
-#include <linux/of_address.h>
-
-#include <asm/cputhreads.h>
-#include <asm/reg_a2.h>
-#include <asm/scom.h>
-#include <asm/udbg.h>
-
-#include "wsp.h"
-
-
-static scom_map_t wsp_scom_map(struct device_node *dev, u64 reg, u64 count)
-{
-       struct resource r;
-       u64 xscom_addr;
-
-       if (!of_get_property(dev, "scom-controller", NULL)) {
-               pr_err("%s: device %s is not a SCOM controller\n",
-                       __func__, dev->full_name);
-               return SCOM_MAP_INVALID;
-       }
-
-       if (of_address_to_resource(dev, 0, &r)) {
-               pr_debug("Failed to find SCOM controller address\n");
-               return 0;
-       }
-
-       /* Transform the SCOM address into an XSCOM offset */
-       xscom_addr = ((reg & 0x7f000000) >> 1) | ((reg & 0xfffff) << 3);
-
-       return (scom_map_t)ioremap(r.start + xscom_addr, count << 3);
-}
-
-static void wsp_scom_unmap(scom_map_t map)
-{
-       iounmap((void *)map);
-}
-
-static int wsp_scom_read(scom_map_t map, u64 reg, u64 *value)
-{
-       u64 __iomem *addr = (u64 __iomem *)map;
-
-       *value = in_be64(addr + reg);
-
-       return 0;
-}
-
-static int wsp_scom_write(scom_map_t map, u64 reg, u64 value)
-{
-       u64 __iomem *addr = (u64 __iomem *)map;
-
-       out_be64(addr + reg, value);
-
-       return 0;
-}
-
-static const struct scom_controller wsp_scom_controller = {
-       .map    = wsp_scom_map,
-       .unmap  = wsp_scom_unmap,
-       .read   = wsp_scom_read,
-       .write  = wsp_scom_write
-};
-
-void scom_init_wsp(void)
-{
-       scom_init(&wsp_scom_controller);
-}
diff --git a/arch/powerpc/platforms/wsp/setup.c b/arch/powerpc/platforms/wsp/setup.c
deleted file mode 100644 (file)
index 11ac2f0..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2010 Michael Ellerman, IBM Corporation
- *
- * 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/kernel.h>
-#include <linux/of_platform.h>
-
-#include "wsp.h"
-
-/*
- * Find chip-id by walking up device tree looking for ibm,wsp-chip-id property.
- * Won't work for nodes that are not a descendant of a wsp node.
- */
-int wsp_get_chip_id(struct device_node *dn)
-{
-       const u32 *p;
-       int rc;
-
-       /* Start looking at the specified node, not its parent */
-       dn = of_node_get(dn);
-       while (dn && !(p = of_get_property(dn, "ibm,wsp-chip-id", NULL)))
-               dn = of_get_next_parent(dn);
-
-       if (!dn)
-               return -1;
-
-       rc = *p;
-       of_node_put(dn);
-
-       return rc;
-}
diff --git a/arch/powerpc/platforms/wsp/smp.c b/arch/powerpc/platforms/wsp/smp.c
deleted file mode 100644 (file)
index 332a18b..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- *  SMP Support for A2 platforms
- *
- *  Copyright 2007 Benjamin Herrenschmidt, IBM Corp.
- *
- *  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/cpumask.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/of.h>
-#include <linux/smp.h>
-
-#include <asm/dbell.h>
-#include <asm/machdep.h>
-#include <asm/xics.h>
-
-#include "ics.h"
-#include "wsp.h"
-
-static void smp_a2_setup_cpu(int cpu)
-{
-       doorbell_setup_this_cpu();
-
-       if (cpu != boot_cpuid)
-               xics_setup_cpu();
-}
-
-int smp_a2_kick_cpu(int nr)
-{
-       const char *enable_method;
-       struct device_node *np;
-       int thr_idx;
-
-       if (nr < 0 || nr >= NR_CPUS)
-               return -ENOENT;
-
-       np = of_get_cpu_node(nr, &thr_idx);
-       if (!np)
-               return -ENODEV;
-
-       enable_method = of_get_property(np, "enable-method", NULL);
-       pr_devel("CPU%d has enable-method: \"%s\"\n", nr, enable_method);
-
-       if (!enable_method) {
-                printk(KERN_ERR "CPU%d has no enable-method\n", nr);
-               return -ENOENT;
-       } else if (strcmp(enable_method, "ibm,a2-scom") == 0) {
-               if (a2_scom_startup_cpu(nr, thr_idx, np))
-                       return -1;
-       } else {
-               printk(KERN_ERR "CPU%d: Don't understand enable-method \"%s\"\n",
-                       nr, enable_method);
-               return -EINVAL;
-       }
-
-       /*
-        * The processor is currently spinning, waiting for the
-        * cpu_start field to become non-zero After we set cpu_start,
-        * the processor will continue on to secondary_start
-        */
-       paca[nr].cpu_start = 1;
-
-       return 0;
-}
-
-static int __init smp_a2_probe(void)
-{
-       return num_possible_cpus();
-}
-
-static struct smp_ops_t a2_smp_ops = {
-       .message_pass   = NULL, /* Use smp_muxed_ipi_message_pass */
-       .cause_ipi      = doorbell_cause_ipi,
-       .probe          = smp_a2_probe,
-       .kick_cpu       = smp_a2_kick_cpu,
-       .setup_cpu      = smp_a2_setup_cpu,
-};
-
-void __init a2_setup_smp(void)
-{
-       smp_ops = &a2_smp_ops;
-}
diff --git a/arch/powerpc/platforms/wsp/wsp.c b/arch/powerpc/platforms/wsp/wsp.c
deleted file mode 100644 (file)
index 58cd1f0..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright 2008-2011, IBM Corporation
- *
- * 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/kernel.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/smp.h>
-#include <linux/delay.h>
-#include <linux/time.h>
-#include <linux/of_address.h>
-
-#include <asm/scom.h>
-
-#include "wsp.h"
-#include "ics.h"
-
-#define WSP_SOC_COMPATIBLE     "ibm,wsp-soc"
-#define PBIC_COMPATIBLE                "ibm,wsp-pbic"
-#define COPRO_COMPATIBLE       "ibm,wsp-coprocessor"
-
-static int __init wsp_probe_buses(void)
-{
-       static __initdata struct of_device_id bus_ids[] = {
-               /*
-                * every node in between needs to be here or you won't
-                * find it
-                */
-               { .compatible = WSP_SOC_COMPATIBLE, },
-               { .compatible = PBIC_COMPATIBLE, },
-               { .compatible = COPRO_COMPATIBLE, },
-               {},
-       };
-       of_platform_bus_probe(NULL, bus_ids, NULL);
-
-       return 0;
-}
-
-void __init wsp_setup_arch(void)
-{
-       /* init to some ~sane value until calibrate_delay() runs */
-       loops_per_jiffy = 50000000;
-
-       scom_init_wsp();
-
-       /* Setup SMP callback */
-#ifdef CONFIG_SMP
-       a2_setup_smp();
-#endif
-#ifdef CONFIG_PCI
-       wsp_setup_pci();
-#endif
-}
-
-void __init wsp_setup_irq(void)
-{
-       wsp_init_irq();
-       opb_pic_init();
-}
-
-
-int __init wsp_probe_devices(void)
-{
-       struct device_node *np;
-
-       /* Our RTC is a ds1500. It seems to be programatically compatible
-        * with the ds1511 for which we have a driver so let's use that
-        */
-       np = of_find_compatible_node(NULL, NULL, "dallas,ds1500");
-       if (np != NULL) {
-               struct resource res;
-               if (of_address_to_resource(np, 0, &res) == 0)
-                       platform_device_register_simple("ds1511", 0, &res, 1);
-       }
-
-       wsp_probe_buses();
-
-       return 0;
-}
-
-void wsp_halt(void)
-{
-       u64 val;
-       scom_map_t m;
-       struct device_node *dn;
-       struct device_node *mine;
-       struct device_node *me;
-       int rc;
-
-       me = of_get_cpu_node(smp_processor_id(), NULL);
-       mine = scom_find_parent(me);
-
-       /* This will halt all the A2s but not power off the chip */
-       for_each_node_with_property(dn, "scom-controller") {
-               if (dn == mine)
-                       continue;
-               m = scom_map(dn, 0, 1);
-
-               /* read-modify-write it so the HW probe does not get
-                * confused */
-               rc = scom_read(m, 0, &val);
-               if (rc == 0)
-                       scom_write(m, 0, val | 1);
-               scom_unmap(m);
-       }
-       m = scom_map(mine, 0, 1);
-       rc = scom_read(m, 0, &val);
-       if (rc == 0)
-               scom_write(m, 0, val | 1);
-       /* should never return */
-       scom_unmap(m);
-}
diff --git a/arch/powerpc/platforms/wsp/wsp.h b/arch/powerpc/platforms/wsp/wsp.h
deleted file mode 100644 (file)
index a563a8a..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef __WSP_H
-#define __WSP_H
-
-#include <asm/wsp.h>
-
-/* Devtree compatible strings for major devices */
-#define PCIE_COMPATIBLE     "ibm,wsp-pciex"
-
-extern void wsp_setup_arch(void);
-extern void wsp_setup_irq(void);
-extern int wsp_probe_devices(void);
-extern void wsp_halt(void);
-
-extern void wsp_setup_pci(void);
-extern void scom_init_wsp(void);
-
-extern void a2_setup_smp(void);
-extern int a2_scom_startup_cpu(unsigned int lcpu, int thr_idx,
-                              struct device_node *np);
-extern int smp_a2_kick_cpu(int nr);
-
-extern void opb_pic_init(void);
-
-/* chroma specific managment */
-extern void wsp_h8_restart(char *cmd);
-extern void wsp_h8_power_off(void);
-extern void __init wsp_setup_h8(void);
-
-#endif /*  __WSP_H */
diff --git a/arch/powerpc/platforms/wsp/wsp_pci.c b/arch/powerpc/platforms/wsp/wsp_pci.c
deleted file mode 100644 (file)
index 9a15e5b..0000000
+++ /dev/null
@@ -1,1134 +0,0 @@
-/*
- * Copyright 2010 Ben Herrenschmidt, IBM Corporation
- *
- * 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 DEBUG
-
-#include <linux/kernel.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <linux/bootmem.h>
-#include <linux/irq.h>
-#include <linux/interrupt.h>
-#include <linux/debugfs.h>
-
-#include <asm/sections.h>
-#include <asm/io.h>
-#include <asm/prom.h>
-#include <asm/pci-bridge.h>
-#include <asm/machdep.h>
-#include <asm/ppc-pci.h>
-#include <asm/iommu.h>
-#include <asm/io-workarounds.h>
-#include <asm/debug.h>
-
-#include "wsp.h"
-#include "wsp_pci.h"
-#include "msi.h"
-
-
-/* Max number of TVTs for one table. Only 32-bit tables can use
- * multiple TVTs and so the max currently supported is thus 8
- * since only 2G of DMA space is supported
- */
-#define MAX_TABLE_TVT_COUNT            8
-
-struct wsp_dma_table {
-       struct list_head        link;
-       struct iommu_table      table;
-       struct wsp_phb  *phb;
-       struct page             *tces[MAX_TABLE_TVT_COUNT];
-};
-
-/* We support DMA regions from 0...2G in 32bit space (no support for
- * 64-bit DMA just yet). Each device gets a separate TCE table (TVT
- * entry) with validation enabled (though not supported by SimiCS
- * just yet).
- *
- * To simplify things, we divide this 2G space into N regions based
- * on the constant below which could be turned into a tunable eventually
- *
- * We then assign dynamically those regions to devices as they show up.
- *
- * We use a bitmap as an allocator for these.
- *
- * Tables are allocated/created dynamically as devices are discovered,
- * multiple TVT entries are used if needed
- *
- * When 64-bit DMA support is added we should simply use a separate set
- * of larger regions (the HW supports 64 TVT entries). We can
- * additionally create a bypass region in 64-bit space for performances
- * though that would have a cost in term of security.
- *
- * If you set NUM_DMA32_REGIONS to 1, then a single table is shared
- * for all devices and bus/dev/fn validation is disabled
- *
- * Note that a DMA32 region cannot be smaller than 256M so the max
- * supported here for now is 8. We don't yet support sharing regions
- * between multiple devices so the max number of devices supported
- * is MAX_TABLE_TVT_COUNT.
- */
-#define NUM_DMA32_REGIONS      1
-
-struct wsp_phb {
-       struct pci_controller   *hose;
-
-       /* Lock controlling access to the list of dma tables.
-        * It does -not- protect against dma_* operations on
-        * those tables, those should be stopped before an entry
-        * is removed from the list.
-        *
-        * The lock is also used for error handling operations
-        */
-       spinlock_t              lock;
-       struct list_head        dma_tables;
-       unsigned long           dma32_map;
-       unsigned long           dma32_base;
-       unsigned int            dma32_num_regions;
-       unsigned long           dma32_region_size;
-
-       /* Debugfs stuff */
-       struct dentry           *ddir;
-
-       struct list_head        all;
-};
-static LIST_HEAD(wsp_phbs);
-
-//#define cfg_debug(fmt...)    pr_debug(fmt)
-#define cfg_debug(fmt...)
-
-
-static int wsp_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
-                                 int offset, int len, u32 *val)
-{
-       struct pci_controller *hose;
-       int suboff;
-       u64 addr;
-
-       hose = pci_bus_to_host(bus);
-       if (hose == NULL)
-               return PCIBIOS_DEVICE_NOT_FOUND;
-       if (offset >= 0x1000)
-               return  PCIBIOS_BAD_REGISTER_NUMBER;
-       addr = PCIE_REG_CA_ENABLE |
-               ((u64)bus->number) << PCIE_REG_CA_BUS_SHIFT |
-               ((u64)devfn) << PCIE_REG_CA_FUNC_SHIFT |
-               ((u64)offset & ~3) << PCIE_REG_CA_REG_SHIFT;
-       suboff = offset & 3;
-
-       /*
-        * Note: the caller has already checked that offset is
-        * suitably aligned and that len is 1, 2 or 4.
-        */
-
-       switch (len) {
-       case 1:
-               addr |= (0x8ul >> suboff) << PCIE_REG_CA_BE_SHIFT;
-               out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr);
-               *val = (in_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA)
-                       >> (suboff << 3)) & 0xff;
-               cfg_debug("read 1 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%02x\n",
-                         bus->number, devfn >> 3, devfn & 7,
-                         offset, suboff, addr, *val);
-               break;
-       case 2:
-               addr |= (0xcul >> suboff) << PCIE_REG_CA_BE_SHIFT;
-               out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr);
-               *val = (in_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA)
-                       >> (suboff << 3)) & 0xffff;
-               cfg_debug("read 2 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%04x\n",
-                         bus->number, devfn >> 3, devfn & 7,
-                         offset, suboff, addr, *val);
-               break;
-       default:
-               addr |= 0xful << PCIE_REG_CA_BE_SHIFT;
-               out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr);
-               *val = in_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA);
-               cfg_debug("read 4 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%08x\n",
-                         bus->number, devfn >> 3, devfn & 7,
-                         offset, suboff, addr, *val);
-               break;
-       }
-       return PCIBIOS_SUCCESSFUL;
-}
-
-static int wsp_pcie_write_config(struct pci_bus *bus, unsigned int devfn,
-                                  int offset, int len, u32 val)
-{
-       struct pci_controller *hose;
-       int suboff;
-       u64 addr;
-
-       hose = pci_bus_to_host(bus);
-       if (hose == NULL)
-               return PCIBIOS_DEVICE_NOT_FOUND;
-       if (offset >= 0x1000)
-               return  PCIBIOS_BAD_REGISTER_NUMBER;
-       addr = PCIE_REG_CA_ENABLE |
-               ((u64)bus->number) << PCIE_REG_CA_BUS_SHIFT |
-               ((u64)devfn) << PCIE_REG_CA_FUNC_SHIFT |
-               ((u64)offset & ~3) << PCIE_REG_CA_REG_SHIFT;
-       suboff = offset & 3;
-
-       /*
-        * Note: the caller has already checked that offset is
-        * suitably aligned and that len is 1, 2 or 4.
-        */
-       switch (len) {
-       case 1:
-               addr |= (0x8ul >> suboff) << PCIE_REG_CA_BE_SHIFT;
-               val <<= suboff << 3;
-               out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr);
-               out_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA, val);
-               cfg_debug("write 1 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%02x\n",
-                         bus->number, devfn >> 3, devfn & 7,
-                         offset, suboff, addr, val);
-               break;
-       case 2:
-               addr |= (0xcul >> suboff) << PCIE_REG_CA_BE_SHIFT;
-               val <<= suboff << 3;
-               out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr);
-               out_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA, val);
-               cfg_debug("write 2 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%04x\n",
-                         bus->number, devfn >> 3, devfn & 7,
-                         offset, suboff, addr, val);
-               break;
-       default:
-               addr |= 0xful << PCIE_REG_CA_BE_SHIFT;
-               out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr);
-               out_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA, val);
-               cfg_debug("write 4 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%08x\n",
-                         bus->number, devfn >> 3, devfn & 7,
-                         offset, suboff, addr, val);
-               break;
-       }
-       return PCIBIOS_SUCCESSFUL;
-}
-
-static struct pci_ops wsp_pcie_pci_ops =
-{
-       .read = wsp_pcie_read_config,
-       .write = wsp_pcie_write_config,
-};
-
-#define TCE_SHIFT              12
-#define TCE_PAGE_SIZE          (1 << TCE_SHIFT)
-#define TCE_PCI_WRITE          0x2              /* write from PCI allowed */
-#define TCE_PCI_READ           0x1              /* read from PCI allowed */
-#define TCE_RPN_MASK           0x3fffffffffful  /* 42-bit RPN (4K pages) */
-#define TCE_RPN_SHIFT          12
-
-//#define dma_debug(fmt...)    pr_debug(fmt)
-#define dma_debug(fmt...)
-
-static int tce_build_wsp(struct iommu_table *tbl, long index, long npages,
-                          unsigned long uaddr, enum dma_data_direction direction,
-                          struct dma_attrs *attrs)
-{
-       struct wsp_dma_table *ptbl = container_of(tbl,
-                                                   struct wsp_dma_table,
-                                                   table);
-       u64 proto_tce;
-       u64 *tcep;
-       u64 rpn;
-
-       proto_tce = TCE_PCI_READ;
-#ifdef CONFIG_WSP_DD1_WORKAROUND_DD1_TCE_BUGS
-       proto_tce |= TCE_PCI_WRITE;
-#else
-       if (direction != DMA_TO_DEVICE)
-               proto_tce |= TCE_PCI_WRITE;
-#endif
-
-       /* XXX Make this faster by factoring out the page address for
-        * within a TCE table
-        */
-       while (npages--) {
-               /* We don't use it->base as the table can be scattered */
-               tcep = (u64 *)page_address(ptbl->tces[index >> 16]);
-               tcep += (index & 0xffff);
-
-               /* can't move this out since we might cross LMB boundary */
-               rpn = __pa(uaddr) >> TCE_SHIFT;
-               *tcep = proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT;
-
-               dma_debug("[DMA] TCE %p set to 0x%016llx (dma addr: 0x%lx)\n",
-                         tcep, *tcep, (tbl->it_offset + index) << IOMMU_PAGE_SHIFT_4K);
-
-               uaddr += TCE_PAGE_SIZE;
-               index++;
-       }
-       return 0;
-}
-
-static void tce_free_wsp(struct iommu_table *tbl, long index, long npages)
-{
-       struct wsp_dma_table *ptbl = container_of(tbl,
-                                                   struct wsp_dma_table,
-                                                   table);
-#ifndef CONFIG_WSP_DD1_WORKAROUND_DD1_TCE_BUGS
-       struct pci_controller *hose = ptbl->phb->hose;
-#endif
-       u64 *tcep;
-
-       /* XXX Make this faster by factoring out the page address for
-        * within a TCE table. Also use line-kill option to kill multiple
-        * TCEs at once
-        */
-       while (npages--) {
-               /* We don't use it->base as the table can be scattered */
-               tcep = (u64 *)page_address(ptbl->tces[index >> 16]);
-               tcep += (index & 0xffff);
-               dma_debug("[DMA] TCE %p cleared\n", tcep);
-               *tcep = 0;
-#ifndef CONFIG_WSP_DD1_WORKAROUND_DD1_TCE_BUGS
-               /* Don't write there since it would pollute other MMIO accesses */
-               out_be64(hose->cfg_data + PCIE_REG_TCE_KILL,
-                        PCIE_REG_TCEKILL_SINGLE | PCIE_REG_TCEKILL_PS_4K |
-                        (__pa(tcep) & PCIE_REG_TCEKILL_ADDR_MASK));
-#endif
-               index++;
-       }
-}
-
-static struct wsp_dma_table *wsp_pci_create_dma32_table(struct wsp_phb *phb,
-                                                           unsigned int region,
-                                                           struct pci_dev *validate)
-{
-       struct pci_controller *hose = phb->hose;
-       unsigned long size = phb->dma32_region_size;
-       unsigned long addr = phb->dma32_region_size * region + phb->dma32_base;
-       struct wsp_dma_table *tbl;
-       int tvts_per_table, i, tvt, nid;
-       unsigned long flags;
-
-       nid = of_node_to_nid(phb->hose->dn);
-
-       /* Calculate how many TVTs are needed */
-       tvts_per_table = size / 0x10000000;
-       if (tvts_per_table == 0)
-               tvts_per_table = 1;
-
-       /* Calculate the base TVT index. We know all tables have the same
-        * size so we just do a simple multiply here
-        */
-       tvt = region * tvts_per_table;
-
-       pr_debug("         Region : %d\n", region);
-       pr_debug("      DMA range : 0x%08lx..0x%08lx\n", addr, addr + size - 1);
-       pr_debug(" Number of TVTs : %d\n", tvts_per_table);
-       pr_debug("       Base TVT : %d\n", tvt);
-       pr_debug("         Node   : %d\n", nid);
-
-       tbl = kzalloc_node(sizeof(struct wsp_dma_table), GFP_KERNEL, nid);
-       if (!tbl)
-               return ERR_PTR(-ENOMEM);
-       tbl->phb = phb;
-
-       /* Create as many TVTs as needed, each represents 256M at most */
-       for (i = 0; i < tvts_per_table; i++) {
-               u64 tvt_data1, tvt_data0;
-
-               /* Allocate table. We use a 4K TCE size for now always so
-                * one table is always 8 * (258M / 4K) == 512K
-                */
-               tbl->tces[i] = alloc_pages_node(nid, GFP_KERNEL, get_order(0x80000));
-               if (tbl->tces[i] == NULL)
-                       goto fail;
-               memset(page_address(tbl->tces[i]), 0, 0x80000);
-
-               pr_debug(" TCE table %d at : %p\n", i, page_address(tbl->tces[i]));
-
-               /* Table size. We currently set it to be the whole 256M region */
-               tvt_data0 = 2ull << IODA_TVT0_TCE_TABLE_SIZE_SHIFT;
-               /* IO page size set to 4K */
-               tvt_data1 = 1ull << IODA_TVT1_IO_PAGE_SIZE_SHIFT;
-               /* Shift in the address */
-               tvt_data0 |= __pa(page_address(tbl->tces[i])) << IODA_TVT0_TTA_SHIFT;
-
-               /* Validation stuff. We only validate fully bus/dev/fn for now
-                * one day maybe we can group devices but that isn't the case
-                * at the moment
-                */
-               if (validate) {
-                       tvt_data0 |= IODA_TVT0_BUSNUM_VALID_MASK;
-                       tvt_data0 |= validate->bus->number;
-                       tvt_data1 |= IODA_TVT1_DEVNUM_VALID;
-                       tvt_data1 |= ((u64)PCI_SLOT(validate->devfn))
-                               << IODA_TVT1_DEVNUM_VALUE_SHIFT;
-                       tvt_data1 |= IODA_TVT1_FUNCNUM_VALID;
-                       tvt_data1 |= ((u64)PCI_FUNC(validate->devfn))
-                               << IODA_TVT1_FUNCNUM_VALUE_SHIFT;
-               }
-
-               /* XX PE number is always 0 for now */
-
-               /* Program the values using the PHB lock */
-               spin_lock_irqsave(&phb->lock, flags);
-               out_be64(hose->cfg_data + PCIE_REG_IODA_ADDR,
-                        (tvt + i) | PCIE_REG_IODA_AD_TBL_TVT);
-               out_be64(hose->cfg_data + PCIE_REG_IODA_DATA1, tvt_data1);
-               out_be64(hose->cfg_data + PCIE_REG_IODA_DATA0, tvt_data0);
-               spin_unlock_irqrestore(&phb->lock, flags);
-       }
-
-       /* Init bits and pieces */
-       tbl->table.it_blocksize = 16;
-       tbl->table.it_page_shift = IOMMU_PAGE_SHIFT_4K;
-       tbl->table.it_offset = addr >> tbl->table.it_page_shift;
-       tbl->table.it_size = size >> tbl->table.it_page_shift;
-
-       /*
-        * It's already blank but we clear it anyway.
-        * Consider an aditiona interface that makes cleaing optional
-        */
-       iommu_init_table(&tbl->table, nid);
-
-       list_add(&tbl->link, &phb->dma_tables);
-       return tbl;
-
- fail:
-       pr_debug("  Failed to allocate a 256M TCE table !\n");
-       for (i = 0; i < tvts_per_table; i++)
-               if (tbl->tces[i])
-                       __free_pages(tbl->tces[i], get_order(0x80000));
-       kfree(tbl);
-       return ERR_PTR(-ENOMEM);
-}
-
-static void wsp_pci_dma_dev_setup(struct pci_dev *pdev)
-{
-       struct dev_archdata *archdata = &pdev->dev.archdata;
-       struct pci_controller *hose = pci_bus_to_host(pdev->bus);
-       struct wsp_phb *phb = hose->private_data;
-       struct wsp_dma_table *table = NULL;
-       unsigned long flags;
-       int i;
-
-       /* Don't assign an iommu table to a bridge */
-       if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
-               return;
-
-       pr_debug("%s: Setting up DMA...\n", pci_name(pdev));
-
-       spin_lock_irqsave(&phb->lock, flags);
-
-       /* If only one region, check if it already exist */
-       if (phb->dma32_num_regions == 1) {
-               spin_unlock_irqrestore(&phb->lock, flags);
-               if (list_empty(&phb->dma_tables))
-                       table = wsp_pci_create_dma32_table(phb, 0, NULL);
-               else
-                       table = list_first_entry(&phb->dma_tables,
-                                                struct wsp_dma_table,
-                                                link);
-       } else {
-               /* else find a free region */
-               for (i = 0; i < phb->dma32_num_regions && !table; i++) {
-                       if (__test_and_set_bit(i, &phb->dma32_map))
-                               continue;
-                       spin_unlock_irqrestore(&phb->lock, flags);
-                       table = wsp_pci_create_dma32_table(phb, i, pdev);
-               }
-       }
-
-       /* Check if we got an error */
-       if (IS_ERR(table)) {
-               pr_err("%s: Failed to create DMA table, err %ld !\n",
-                      pci_name(pdev), PTR_ERR(table));
-               return;
-       }
-
-       /* Or a valid table */
-       if (table) {
-               pr_info("%s: Setup iommu: 32-bit DMA region 0x%08lx..0x%08lx\n",
-                       pci_name(pdev),
-                       table->table.it_offset << IOMMU_PAGE_SHIFT_4K,
-                       (table->table.it_offset << IOMMU_PAGE_SHIFT_4K)
-                       + phb->dma32_region_size - 1);
-               archdata->dma_data.iommu_table_base = &table->table;
-               return;
-       }
-
-       /* Or no room */
-       spin_unlock_irqrestore(&phb->lock, flags);
-       pr_err("%s: Out of DMA space !\n", pci_name(pdev));
-}
-
-static void __init wsp_pcie_configure_hw(struct pci_controller *hose)
-{
-       u64 val;
-       int i;
-
-#define DUMP_REG(x) \
-       pr_debug("%-30s : 0x%016llx\n", #x, in_be64(hose->cfg_data + x))
-
-       /*
-        * Some WSP variants  has a bogus class code by default in the PCI-E
-        * root complex's built-in P2P bridge
-        */
-       val = in_be64(hose->cfg_data + PCIE_REG_SYS_CFG1);
-       pr_debug("PCI-E SYS_CFG1 : 0x%llx\n", val);
-       out_be64(hose->cfg_data + PCIE_REG_SYS_CFG1,
-                (val & ~PCIE_REG_SYS_CFG1_CLASS_CODE) | (PCI_CLASS_BRIDGE_PCI << 8));
-       pr_debug("PCI-E SYS_CFG1 : 0x%llx\n", in_be64(hose->cfg_data + PCIE_REG_SYS_CFG1));
-
-#ifdef CONFIG_WSP_DD1_WORKAROUND_DD1_TCE_BUGS
-       /* XXX Disable TCE caching, it doesn't work on DD1 */
-       out_be64(hose->cfg_data + 0xe50,
-                in_be64(hose->cfg_data + 0xe50) | (3ull << 62));
-       printk("PCI-E DEBUG CONTROL 5 = 0x%llx\n", in_be64(hose->cfg_data + 0xe50));
-#endif
-
-       /* Configure M32A and IO. IO is hard wired to be 1M for now */
-       out_be64(hose->cfg_data + PCIE_REG_IO_BASE_ADDR, hose->io_base_phys);
-       out_be64(hose->cfg_data + PCIE_REG_IO_BASE_MASK,
-                (~(hose->io_resource.end - hose->io_resource.start)) &
-                0x3fffffff000ul);
-       out_be64(hose->cfg_data + PCIE_REG_IO_START_ADDR, 0 | 1);
-
-       out_be64(hose->cfg_data + PCIE_REG_M32A_BASE_ADDR,
-                hose->mem_resources[0].start);
-       printk("Want to write to M32A_BASE_MASK : 0x%llx\n",
-                (~(hose->mem_resources[0].end -
-                   hose->mem_resources[0].start)) & 0x3ffffff0000ul);
-       out_be64(hose->cfg_data + PCIE_REG_M32A_BASE_MASK,
-                (~(hose->mem_resources[0].end -
-                   hose->mem_resources[0].start)) & 0x3ffffff0000ul);
-       out_be64(hose->cfg_data + PCIE_REG_M32A_START_ADDR,
-                (hose->mem_resources[0].start - hose->mem_offset[0]) | 1);
-
-       /* Clear all TVT entries
-        *
-        * XX Might get TVT count from device-tree
-        */
-       for (i = 0; i < IODA_TVT_COUNT; i++) {
-               out_be64(hose->cfg_data + PCIE_REG_IODA_ADDR,
-                        PCIE_REG_IODA_AD_TBL_TVT | i);
-               out_be64(hose->cfg_data + PCIE_REG_IODA_DATA1, 0);
-               out_be64(hose->cfg_data + PCIE_REG_IODA_DATA0, 0);
-       }
-
-       /* Kill the TCE cache */
-       out_be64(hose->cfg_data + PCIE_REG_PHB_CONFIG,
-                in_be64(hose->cfg_data + PCIE_REG_PHB_CONFIG) |
-                PCIE_REG_PHBC_64B_TCE_EN);
-
-       /* Enable 32 & 64-bit MSIs, IO space and M32A */
-       val = PCIE_REG_PHBC_32BIT_MSI_EN |
-             PCIE_REG_PHBC_IO_EN |
-             PCIE_REG_PHBC_64BIT_MSI_EN |
-             PCIE_REG_PHBC_M32A_EN;
-       if (iommu_is_off)
-               val |= PCIE_REG_PHBC_DMA_XLATE_BYPASS;
-       pr_debug("Will write config: 0x%llx\n", val);
-       out_be64(hose->cfg_data + PCIE_REG_PHB_CONFIG, val);
-
-       /* Enable error reporting */
-       out_be64(hose->cfg_data + 0xe00,
-                in_be64(hose->cfg_data + 0xe00) | 0x0008000000000000ull);
-
-       /* Mask an error that's generated when doing config space probe
-        *
-        * XXX Maybe we should only mask it around config space cycles... that or
-        * ignore it when we know we had a config space cycle recently ?
-        */
-       out_be64(hose->cfg_data + PCIE_REG_DMA_ERR_STATUS_MASK, 0x8000000000000000ull);
-       out_be64(hose->cfg_data + PCIE_REG_DMA_ERR1_STATUS_MASK, 0x8000000000000000ull);
-
-       /* Enable UTL errors, for now, all of them got to UTL irq 1
-        *
-        * We similarily mask one UTL error caused apparently during normal
-        * probing. We also mask the link up error
-        */
-       out_be64(hose->cfg_data + PCIE_UTL_SYS_BUS_AGENT_ERR_SEV, 0);
-       out_be64(hose->cfg_data + PCIE_UTL_RC_ERR_SEVERITY, 0);
-       out_be64(hose->cfg_data + PCIE_UTL_PCIE_PORT_ERROR_SEV, 0);
-       out_be64(hose->cfg_data + PCIE_UTL_SYS_BUS_AGENT_IRQ_EN, 0xffffffff00000000ull);
-       out_be64(hose->cfg_data + PCIE_UTL_PCIE_PORT_IRQ_EN, 0xff5fffff00000000ull);
-       out_be64(hose->cfg_data + PCIE_UTL_EP_ERR_IRQ_EN, 0xffffffff00000000ull);
-
-       DUMP_REG(PCIE_REG_IO_BASE_ADDR);
-       DUMP_REG(PCIE_REG_IO_BASE_MASK);
-       DUMP_REG(PCIE_REG_IO_START_ADDR);
-       DUMP_REG(PCIE_REG_M32A_BASE_ADDR);
-       DUMP_REG(PCIE_REG_M32A_BASE_MASK);
-       DUMP_REG(PCIE_REG_M32A_START_ADDR);
-       DUMP_REG(PCIE_REG_M32B_BASE_ADDR);
-       DUMP_REG(PCIE_REG_M32B_BASE_MASK);
-       DUMP_REG(PCIE_REG_M32B_START_ADDR);
-       DUMP_REG(PCIE_REG_M64_BASE_ADDR);
-       DUMP_REG(PCIE_REG_M64_BASE_MASK);
-       DUMP_REG(PCIE_REG_M64_START_ADDR);
-       DUMP_REG(PCIE_REG_PHB_CONFIG);
-}
-
-static void wsp_pci_wait_io_idle(struct wsp_phb *phb, unsigned long port)
-{
-       u64 val;
-       int i;
-
-       for (i = 0; i < 10000; i++) {
-               val = in_be64(phb->hose->cfg_data + 0xe08);
-               if ((val & 0x1900000000000000ull) == 0x0100000000000000ull)
-                       return;
-               udelay(1);
-       }
-       pr_warning("PCI IO timeout on domain %d port 0x%lx\n",
-                  phb->hose->global_number, port);
-}
-
-#define DEF_PCI_AC_RET_pio(name, ret, at, al, aa)              \
-static ret wsp_pci_##name at                                   \
-{                                                              \
-       struct iowa_bus *bus;                                   \
-       struct wsp_phb *phb;                                    \
-       unsigned long flags;                                    \
-       ret rval;                                               \
-       bus = iowa_pio_find_bus(aa);                            \
-       WARN_ON(!bus);                                          \
-       phb = bus->private;                                     \
-       spin_lock_irqsave(&phb->lock, flags);                   \
-       wsp_pci_wait_io_idle(phb, aa);                          \
-       rval = __do_##name al;                                  \
-       spin_unlock_irqrestore(&phb->lock, flags);              \
-       return rval;                                            \
-}
-
-#define DEF_PCI_AC_NORET_pio(name, at, al, aa)                 \
-static void wsp_pci_##name at                                  \
-{                                                              \
-       struct iowa_bus *bus;                                   \
-       struct wsp_phb *phb;                                    \
-       unsigned long flags;                                    \
-       bus = iowa_pio_find_bus(aa);                            \
-       WARN_ON(!bus);                                          \
-       phb = bus->private;                                     \
-       spin_lock_irqsave(&phb->lock, flags);                   \
-       wsp_pci_wait_io_idle(phb, aa);                          \
-       __do_##name al;                                         \
-       spin_unlock_irqrestore(&phb->lock, flags);              \
-}
-
-#define DEF_PCI_AC_RET_mem(name, ret, at, al, aa)
-#define DEF_PCI_AC_NORET_mem(name, at, al, aa)
-
-#define DEF_PCI_AC_RET(name, ret, at, al, space, aa)           \
-       DEF_PCI_AC_RET_##space(name, ret, at, al, aa)
-
-#define DEF_PCI_AC_NORET(name, at, al, space, aa)              \
-       DEF_PCI_AC_NORET_##space(name, at, al, aa)              \
-
-
-#include <asm/io-defs.h>
-
-#undef DEF_PCI_AC_RET
-#undef DEF_PCI_AC_NORET
-
-static struct ppc_pci_io wsp_pci_iops = {
-       .inb = wsp_pci_inb,
-       .inw = wsp_pci_inw,
-       .inl = wsp_pci_inl,
-       .outb = wsp_pci_outb,
-       .outw = wsp_pci_outw,
-       .outl = wsp_pci_outl,
-       .insb = wsp_pci_insb,
-       .insw = wsp_pci_insw,
-       .insl = wsp_pci_insl,
-       .outsb = wsp_pci_outsb,
-       .outsw = wsp_pci_outsw,
-       .outsl = wsp_pci_outsl,
-};
-
-static int __init wsp_setup_one_phb(struct device_node *np)
-{
-       struct pci_controller *hose;
-       struct wsp_phb *phb;
-
-       pr_info("PCI: Setting up PCIe host bridge 0x%s\n", np->full_name);
-
-       phb = zalloc_maybe_bootmem(sizeof(struct wsp_phb), GFP_KERNEL);
-       if (!phb)
-               return -ENOMEM;
-       hose = pcibios_alloc_controller(np);
-       if (!hose) {
-               /* Can't really free the phb */
-               return -ENOMEM;
-       }
-       hose->private_data = phb;
-       phb->hose = hose;
-
-       INIT_LIST_HEAD(&phb->dma_tables);
-       spin_lock_init(&phb->lock);
-
-       /* XXX Use bus-range property ? */
-       hose->first_busno = 0;
-       hose->last_busno = 0xff;
-
-       /* We use cfg_data as the address for the whole bridge MMIO space
-        */
-       hose->cfg_data = of_iomap(hose->dn, 0);
-
-       pr_debug("PCIe registers mapped at 0x%p\n", hose->cfg_data);
-
-       /* Get the ranges of the device-tree */
-       pci_process_bridge_OF_ranges(hose, np, 0);
-
-       /* XXX Force re-assigning of everything for now */
-       pci_add_flags(PCI_REASSIGN_ALL_BUS | PCI_REASSIGN_ALL_RSRC |
-                     PCI_ENABLE_PROC_DOMAINS);
-
-       /* Calculate how the TCE space is divided */
-       phb->dma32_base         = 0;
-       phb->dma32_num_regions  = NUM_DMA32_REGIONS;
-       if (phb->dma32_num_regions > MAX_TABLE_TVT_COUNT) {
-               pr_warning("IOMMU: Clamped to %d DMA32 regions\n",
-                          MAX_TABLE_TVT_COUNT);
-               phb->dma32_num_regions = MAX_TABLE_TVT_COUNT;
-       }
-       phb->dma32_region_size  = 0x80000000 / phb->dma32_num_regions;
-
-       BUG_ON(!is_power_of_2(phb->dma32_region_size));
-
-       /* Setup config ops */
-       hose->ops = &wsp_pcie_pci_ops;
-
-       /* Configure the HW */
-       wsp_pcie_configure_hw(hose);
-
-       /* Instanciate IO workarounds */
-       iowa_register_bus(hose, &wsp_pci_iops, NULL, phb);
-#ifdef CONFIG_PCI_MSI
-       wsp_setup_phb_msi(hose);
-#endif
-
-       /* Add to global list */
-       list_add(&phb->all, &wsp_phbs);
-
-       return 0;
-}
-
-void __init wsp_setup_pci(void)
-{
-       struct device_node *np;
-       int rc;
-
-       /* Find host bridges */
-       for_each_compatible_node(np, "pciex", PCIE_COMPATIBLE) {
-               rc = wsp_setup_one_phb(np);
-               if (rc)
-                       pr_err("Failed to setup PCIe bridge %s, rc=%d\n",
-                              np->full_name, rc);
-       }
-
-       /* Establish device-tree linkage */
-       pci_devs_phb_init();
-
-       /* Set DMA ops to use TCEs */
-       if (iommu_is_off) {
-               pr_info("PCI-E: Disabled TCEs, using direct DMA\n");
-               set_pci_dma_ops(&dma_direct_ops);
-       } else {
-               ppc_md.pci_dma_dev_setup = wsp_pci_dma_dev_setup;
-               ppc_md.tce_build = tce_build_wsp;
-               ppc_md.tce_free = tce_free_wsp;
-               set_pci_dma_ops(&dma_iommu_ops);
-       }
-}
-
-#define err_debug(fmt...)      pr_debug(fmt)
-//#define err_debug(fmt...)
-
-static int __init wsp_pci_get_err_irq_no_dt(struct device_node *np)
-{
-       const u32 *prop;
-       int hw_irq;
-
-       /* Ok, no interrupts property, let's try to find our child P2P */
-       np = of_get_next_child(np, NULL);
-       if (np == NULL)
-               return 0;
-
-       /* Grab it's interrupt map */
-       prop = of_get_property(np, "interrupt-map", NULL);
-       if (prop == NULL)
-               return 0;
-
-       /* Grab one of the interrupts in there, keep the low 4 bits */
-       hw_irq = prop[5] & 0xf;
-
-       /* 0..4 for PHB 0 and 5..9 for PHB 1 */
-       if (hw_irq < 5)
-               hw_irq = 4;
-       else
-               hw_irq = 9;
-       hw_irq |= prop[5] & ~0xf;
-
-       err_debug("PCI: Using 0x%x as error IRQ for %s\n",
-                 hw_irq, np->parent->full_name);
-       return irq_create_mapping(NULL, hw_irq);
-}
-
-static const struct {
-       u32 offset;
-       const char *name;
-} wsp_pci_regs[] = {
-#define DREG(x) { PCIE_REG_##x, #x }
-#define DUTL(x) { PCIE_UTL_##x, "UTL_" #x }
-       /* Architected registers except CONFIG_ and IODA
-         * to avoid side effects
-        */
-       DREG(DMA_CHAN_STATUS),
-       DREG(CPU_LOADSTORE_STATUS),
-       DREG(LOCK0),
-       DREG(LOCK1),
-       DREG(PHB_CONFIG),
-       DREG(IO_BASE_ADDR),
-       DREG(IO_BASE_MASK),
-       DREG(IO_START_ADDR),
-       DREG(M32A_BASE_ADDR),
-       DREG(M32A_BASE_MASK),
-       DREG(M32A_START_ADDR),
-       DREG(M32B_BASE_ADDR),
-       DREG(M32B_BASE_MASK),
-       DREG(M32B_START_ADDR),
-       DREG(M64_BASE_ADDR),
-       DREG(M64_BASE_MASK),
-       DREG(M64_START_ADDR),
-       DREG(TCE_KILL),
-       DREG(LOCK2),
-       DREG(PHB_GEN_CAP),
-       DREG(PHB_TCE_CAP),
-       DREG(PHB_IRQ_CAP),
-       DREG(PHB_EEH_CAP),
-       DREG(PAPR_ERR_INJ_CONTROL),
-       DREG(PAPR_ERR_INJ_ADDR),
-       DREG(PAPR_ERR_INJ_MASK),
-
-       /* UTL core regs */
-       DUTL(SYS_BUS_CONTROL),
-       DUTL(STATUS),
-       DUTL(SYS_BUS_AGENT_STATUS),
-       DUTL(SYS_BUS_AGENT_ERR_SEV),
-       DUTL(SYS_BUS_AGENT_IRQ_EN),
-       DUTL(SYS_BUS_BURST_SZ_CONF),
-       DUTL(REVISION_ID),
-       DUTL(OUT_POST_HDR_BUF_ALLOC),
-       DUTL(OUT_POST_DAT_BUF_ALLOC),
-       DUTL(IN_POST_HDR_BUF_ALLOC),
-       DUTL(IN_POST_DAT_BUF_ALLOC),
-       DUTL(OUT_NP_BUF_ALLOC),
-       DUTL(IN_NP_BUF_ALLOC),
-       DUTL(PCIE_TAGS_ALLOC),
-       DUTL(GBIF_READ_TAGS_ALLOC),
-
-       DUTL(PCIE_PORT_CONTROL),
-       DUTL(PCIE_PORT_STATUS),
-       DUTL(PCIE_PORT_ERROR_SEV),
-       DUTL(PCIE_PORT_IRQ_EN),
-       DUTL(RC_STATUS),
-       DUTL(RC_ERR_SEVERITY),
-       DUTL(RC_IRQ_EN),
-       DUTL(EP_STATUS),
-       DUTL(EP_ERR_SEVERITY),
-       DUTL(EP_ERR_IRQ_EN),
-       DUTL(PCI_PM_CTRL1),
-       DUTL(PCI_PM_CTRL2),
-
-       /* PCIe stack regs */
-       DREG(SYSTEM_CONFIG1),
-       DREG(SYSTEM_CONFIG2),
-       DREG(EP_SYSTEM_CONFIG),
-       DREG(EP_FLR),
-       DREG(EP_BAR_CONFIG),
-       DREG(LINK_CONFIG),
-       DREG(PM_CONFIG),
-       DREG(DLP_CONTROL),
-       DREG(DLP_STATUS),
-       DREG(ERR_REPORT_CONTROL),
-       DREG(SLOT_CONTROL1),
-       DREG(SLOT_CONTROL2),
-       DREG(UTL_CONFIG),
-       DREG(BUFFERS_CONFIG),
-       DREG(ERROR_INJECT),
-       DREG(SRIOV_CONFIG),
-       DREG(PF0_SRIOV_STATUS),
-       DREG(PF1_SRIOV_STATUS),
-       DREG(PORT_NUMBER),
-       DREG(POR_SYSTEM_CONFIG),
-
-       /* Internal logic regs */
-       DREG(PHB_VERSION),
-       DREG(RESET),
-       DREG(PHB_CONTROL),
-       DREG(PHB_TIMEOUT_CONTROL1),
-       DREG(PHB_QUIESCE_DMA),
-       DREG(PHB_DMA_READ_TAG_ACTV),
-       DREG(PHB_TCE_READ_TAG_ACTV),
-
-       /* FIR registers */
-       DREG(LEM_FIR_ACCUM),
-       DREG(LEM_FIR_AND_MASK),
-       DREG(LEM_FIR_OR_MASK),
-       DREG(LEM_ACTION0),
-       DREG(LEM_ACTION1),
-       DREG(LEM_ERROR_MASK),
-       DREG(LEM_ERROR_AND_MASK),
-       DREG(LEM_ERROR_OR_MASK),
-
-       /* Error traps registers */
-       DREG(PHB_ERR_STATUS),
-       DREG(PHB_ERR_STATUS),
-       DREG(PHB_ERR1_STATUS),
-       DREG(PHB_ERR_INJECT),
-       DREG(PHB_ERR_LEM_ENABLE),
-       DREG(PHB_ERR_IRQ_ENABLE),
-       DREG(PHB_ERR_FREEZE_ENABLE),
-       DREG(PHB_ERR_SIDE_ENABLE),
-       DREG(PHB_ERR_LOG_0),
-       DREG(PHB_ERR_LOG_1),
-       DREG(PHB_ERR_STATUS_MASK),
-       DREG(PHB_ERR1_STATUS_MASK),
-       DREG(MMIO_ERR_STATUS),
-       DREG(MMIO_ERR1_STATUS),
-       DREG(MMIO_ERR_INJECT),
-       DREG(MMIO_ERR_LEM_ENABLE),
-       DREG(MMIO_ERR_IRQ_ENABLE),
-       DREG(MMIO_ERR_FREEZE_ENABLE),
-       DREG(MMIO_ERR_SIDE_ENABLE),
-       DREG(MMIO_ERR_LOG_0),
-       DREG(MMIO_ERR_LOG_1),
-       DREG(MMIO_ERR_STATUS_MASK),
-       DREG(MMIO_ERR1_STATUS_MASK),
-       DREG(DMA_ERR_STATUS),
-       DREG(DMA_ERR1_STATUS),
-       DREG(DMA_ERR_INJECT),
-       DREG(DMA_ERR_LEM_ENABLE),
-       DREG(DMA_ERR_IRQ_ENABLE),
-       DREG(DMA_ERR_FREEZE_ENABLE),
-       DREG(DMA_ERR_SIDE_ENABLE),
-       DREG(DMA_ERR_LOG_0),
-       DREG(DMA_ERR_LOG_1),
-       DREG(DMA_ERR_STATUS_MASK),
-       DREG(DMA_ERR1_STATUS_MASK),
-
-       /* Debug and Trace registers */
-       DREG(PHB_DEBUG_CONTROL0),
-       DREG(PHB_DEBUG_STATUS0),
-       DREG(PHB_DEBUG_CONTROL1),
-       DREG(PHB_DEBUG_STATUS1),
-       DREG(PHB_DEBUG_CONTROL2),
-       DREG(PHB_DEBUG_STATUS2),
-       DREG(PHB_DEBUG_CONTROL3),
-       DREG(PHB_DEBUG_STATUS3),
-       DREG(PHB_DEBUG_CONTROL4),
-       DREG(PHB_DEBUG_STATUS4),
-       DREG(PHB_DEBUG_CONTROL5),
-       DREG(PHB_DEBUG_STATUS5),
-
-       /* Don't seem to exist ...
-       DREG(PHB_DEBUG_CONTROL6),
-       DREG(PHB_DEBUG_STATUS6),
-       */
-};
-
-static int wsp_pci_regs_show(struct seq_file *m, void *private)
-{
-       struct wsp_phb *phb = m->private;
-       struct pci_controller *hose = phb->hose;
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(wsp_pci_regs); i++) {
-               /* Skip write-only regs */
-               if (wsp_pci_regs[i].offset == 0xc08 ||
-                   wsp_pci_regs[i].offset == 0xc10 ||
-                   wsp_pci_regs[i].offset == 0xc38 ||
-                   wsp_pci_regs[i].offset == 0xc40)
-                       continue;
-               seq_printf(m, "0x%03x: 0x%016llx %s\n",
-                          wsp_pci_regs[i].offset,
-                          in_be64(hose->cfg_data + wsp_pci_regs[i].offset),
-                          wsp_pci_regs[i].name);
-       }
-       return 0;
-}
-
-static int wsp_pci_regs_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, wsp_pci_regs_show, inode->i_private);
-}
-
-static const struct file_operations wsp_pci_regs_fops = {
-       .open = wsp_pci_regs_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-};
-
-static int wsp_pci_reg_set(void *data, u64 val)
-{
-       out_be64((void __iomem *)data, val);
-       return 0;
-}
-
-static int wsp_pci_reg_get(void *data, u64 *val)
-{
-       *val = in_be64((void __iomem *)data);
-       return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(wsp_pci_reg_fops, wsp_pci_reg_get, wsp_pci_reg_set, "0x%llx\n");
-
-static irqreturn_t wsp_pci_err_irq(int irq, void *dev_id)
-{
-       struct wsp_phb *phb = dev_id;
-       struct pci_controller *hose = phb->hose;
-       irqreturn_t handled = IRQ_NONE;
-       struct wsp_pcie_err_log_data ed;
-
-       pr_err("PCI: Error interrupt on %s (PHB %d)\n",
-              hose->dn->full_name, hose->global_number);
- again:
-       memset(&ed, 0, sizeof(ed));
-
-       /* Read and clear UTL errors */
-       ed.utl_sys_err = in_be64(hose->cfg_data + PCIE_UTL_SYS_BUS_AGENT_STATUS);
-       if (ed.utl_sys_err)
-               out_be64(hose->cfg_data + PCIE_UTL_SYS_BUS_AGENT_STATUS, ed.utl_sys_err);
-       ed.utl_port_err = in_be64(hose->cfg_data + PCIE_UTL_PCIE_PORT_STATUS);
-       if (ed.utl_port_err)
-               out_be64(hose->cfg_data + PCIE_UTL_PCIE_PORT_STATUS, ed.utl_port_err);
-       ed.utl_rc_err = in_be64(hose->cfg_data + PCIE_UTL_RC_STATUS);
-       if (ed.utl_rc_err)
-               out_be64(hose->cfg_data + PCIE_UTL_RC_STATUS, ed.utl_rc_err);
-
-       /* Read and clear main trap errors */
-       ed.phb_err = in_be64(hose->cfg_data + PCIE_REG_PHB_ERR_STATUS);
-       if (ed.phb_err) {
-               ed.phb_err1 = in_be64(hose->cfg_data + PCIE_REG_PHB_ERR1_STATUS);
-               ed.phb_log0 = in_be64(hose->cfg_data + PCIE_REG_PHB_ERR_LOG_0);
-               ed.phb_log1 = in_be64(hose->cfg_data + PCIE_REG_PHB_ERR_LOG_1);
-               out_be64(hose->cfg_data + PCIE_REG_PHB_ERR1_STATUS, 0);
-               out_be64(hose->cfg_data + PCIE_REG_PHB_ERR_STATUS, 0);
-       }
-       ed.mmio_err = in_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_STATUS);
-       if (ed.mmio_err) {
-               ed.mmio_err1 = in_be64(hose->cfg_data + PCIE_REG_MMIO_ERR1_STATUS);
-               ed.mmio_log0 = in_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_LOG_0);
-               ed.mmio_log1 = in_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_LOG_1);
-               out_be64(hose->cfg_data + PCIE_REG_MMIO_ERR1_STATUS, 0);
-               out_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_STATUS, 0);
-       }
-       ed.dma_err = in_be64(hose->cfg_data + PCIE_REG_DMA_ERR_STATUS);
-       if (ed.dma_err) {
-               ed.dma_err1 = in_be64(hose->cfg_data + PCIE_REG_DMA_ERR1_STATUS);
-               ed.dma_log0 = in_be64(hose->cfg_data + PCIE_REG_DMA_ERR_LOG_0);
-               ed.dma_log1 = in_be64(hose->cfg_data + PCIE_REG_DMA_ERR_LOG_1);
-               out_be64(hose->cfg_data + PCIE_REG_DMA_ERR1_STATUS, 0);
-               out_be64(hose->cfg_data + PCIE_REG_DMA_ERR_STATUS, 0);
-       }
-
-       /* Now print things out */
-       if (ed.phb_err) {
-               pr_err("   PHB Error Status      : 0x%016llx\n", ed.phb_err);
-               pr_err("   PHB First Error Status: 0x%016llx\n", ed.phb_err1);
-               pr_err("   PHB Error Log 0       : 0x%016llx\n", ed.phb_log0);
-               pr_err("   PHB Error Log 1       : 0x%016llx\n", ed.phb_log1);
-       }
-       if (ed.mmio_err) {
-               pr_err("  MMIO Error Status      : 0x%016llx\n", ed.mmio_err);
-               pr_err("  MMIO First Error Status: 0x%016llx\n", ed.mmio_err1);
-               pr_err("  MMIO Error Log 0       : 0x%016llx\n", ed.mmio_log0);
-               pr_err("  MMIO Error Log 1       : 0x%016llx\n", ed.mmio_log1);
-       }
-       if (ed.dma_err) {
-               pr_err("   DMA Error Status      : 0x%016llx\n", ed.dma_err);
-               pr_err("   DMA First Error Status: 0x%016llx\n", ed.dma_err1);
-               pr_err("   DMA Error Log 0       : 0x%016llx\n", ed.dma_log0);
-               pr_err("   DMA Error Log 1       : 0x%016llx\n", ed.dma_log1);
-       }
-       if (ed.utl_sys_err)
-               pr_err("   UTL Sys Error Status  : 0x%016llx\n", ed.utl_sys_err);
-       if (ed.utl_port_err)
-               pr_err("   UTL Port Error Status : 0x%016llx\n", ed.utl_port_err);
-       if (ed.utl_rc_err)
-               pr_err("   UTL RC Error Status   : 0x%016llx\n", ed.utl_rc_err);
-
-       /* Interrupts are caused by the error traps. If we had any error there
-        * we loop again in case the UTL buffered some new stuff between
-        * going there and going to the traps
-        */
-       if (ed.dma_err || ed.mmio_err || ed.phb_err) {
-               handled = IRQ_HANDLED;
-               goto again;
-       }
-       return handled;
-}
-
-static void __init wsp_setup_pci_err_reporting(struct wsp_phb *phb)
-{
-       struct pci_controller *hose = phb->hose;
-       int err_irq, i, rc;
-       char fname[16];
-
-       /* Create a debugfs file for that PHB */
-       sprintf(fname, "phb%d", phb->hose->global_number);
-       phb->ddir = debugfs_create_dir(fname, powerpc_debugfs_root);
-
-       /* Some useful debug output */
-       if (phb->ddir) {
-               struct dentry *d = debugfs_create_dir("regs", phb->ddir);
-               char tmp[64];
-
-               for (i = 0; i < ARRAY_SIZE(wsp_pci_regs); i++) {
-                       sprintf(tmp, "%03x_%s", wsp_pci_regs[i].offset,
-                               wsp_pci_regs[i].name);
-                       debugfs_create_file(tmp, 0600, d,
-                                           hose->cfg_data + wsp_pci_regs[i].offset,
-                                           &wsp_pci_reg_fops);
-               }
-               debugfs_create_file("all_regs", 0600, phb->ddir, phb, &wsp_pci_regs_fops);
-       }
-
-       /* Find the IRQ number for that PHB */
-       err_irq = irq_of_parse_and_map(hose->dn, 0);
-       if (err_irq == 0)
-               /* XXX Error IRQ lacking from device-tree */
-               err_irq = wsp_pci_get_err_irq_no_dt(hose->dn);
-       if (err_irq == 0) {
-               pr_err("PCI: Failed to fetch error interrupt for %s\n",
-                      hose->dn->full_name);
-               return;
-       }
-       /* Request it */
-       rc = request_irq(err_irq, wsp_pci_err_irq, 0, "wsp_pci error", phb);
-       if (rc) {
-               pr_err("PCI: Failed to request interrupt for %s\n",
-                      hose->dn->full_name);
-       }
-       /* Enable interrupts for all errors for now */
-       out_be64(hose->cfg_data + PCIE_REG_PHB_ERR_IRQ_ENABLE, 0xffffffffffffffffull);
-       out_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_IRQ_ENABLE, 0xffffffffffffffffull);
-       out_be64(hose->cfg_data + PCIE_REG_DMA_ERR_IRQ_ENABLE, 0xffffffffffffffffull);
-}
-
-/*
- * This is called later to hookup with the error interrupt
- */
-static int __init wsp_setup_pci_late(void)
-{
-       struct wsp_phb *phb;
-
-       list_for_each_entry(phb, &wsp_phbs, all)
-               wsp_setup_pci_err_reporting(phb);
-
-       return 0;
-}
-arch_initcall(wsp_setup_pci_late);
diff --git a/arch/powerpc/platforms/wsp/wsp_pci.h b/arch/powerpc/platforms/wsp/wsp_pci.h
deleted file mode 100644 (file)
index 52e9bd9..0000000
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright 2010 Ben Herrenschmidt, IBM Corporation
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-#ifndef __WSP_PCI_H
-#define __WSP_PCI_H
-
-/* Architected registers */
-#define PCIE_REG_DMA_CHAN_STATUS       0x110
-#define PCIE_REG_CPU_LOADSTORE_STATUS  0x120
-
-#define PCIE_REG_CONFIG_DATA           0x130
-#define PCIE_REG_LOCK0                 0x138
-#define PCIE_REG_CONFIG_ADDRESS                0x140
-#define   PCIE_REG_CA_ENABLE                   0x8000000000000000ull
-#define          PCIE_REG_CA_BUS_MASK                  0x0ff0000000000000ull
-#define   PCIE_REG_CA_BUS_SHIFT                        (20+32)
-#define   PCIE_REG_CA_DEV_MASK                 0x000f800000000000ull
-#define   PCIE_REG_CA_DEV_SHIFT                        (15+32)
-#define   PCIE_REG_CA_FUNC_MASK                        0x0000700000000000ull
-#define   PCIE_REG_CA_FUNC_SHIFT               (12+32)
-#define   PCIE_REG_CA_REG_MASK                 0x00000fff00000000ull
-#define   PCIE_REG_CA_REG_SHIFT                        ( 0+32)
-#define   PCIE_REG_CA_BE_MASK                  0x00000000f0000000ull
-#define   PCIE_REG_CA_BE_SHIFT                 (   28)
-#define PCIE_REG_LOCK1                 0x148
-
-#define PCIE_REG_PHB_CONFIG            0x160
-#define   PCIE_REG_PHBC_64B_TCE_EN             0x2000000000000000ull
-#define   PCIE_REG_PHBC_MMIO_DMA_FREEZE_EN     0x1000000000000000ull
-#define   PCIE_REG_PHBC_32BIT_MSI_EN           0x0080000000000000ull
-#define   PCIE_REG_PHBC_M64_EN                 0x0040000000000000ull
-#define   PCIE_REG_PHBC_IO_EN                  0x0008000000000000ull
-#define   PCIE_REG_PHBC_64BIT_MSI_EN           0x0002000000000000ull
-#define   PCIE_REG_PHBC_M32A_EN                        0x0000800000000000ull
-#define   PCIE_REG_PHBC_M32B_EN                        0x0000400000000000ull
-#define   PCIE_REG_PHBC_MSI_PE_VALIDATE                0x0000200000000000ull
-#define   PCIE_REG_PHBC_DMA_XLATE_BYPASS       0x0000100000000000ull
-
-#define PCIE_REG_IO_BASE_ADDR          0x170
-#define PCIE_REG_IO_BASE_MASK          0x178
-#define PCIE_REG_IO_START_ADDR         0x180
-
-#define PCIE_REG_M32A_BASE_ADDR                0x190
-#define PCIE_REG_M32A_BASE_MASK                0x198
-#define PCIE_REG_M32A_START_ADDR       0x1a0
-
-#define PCIE_REG_M32B_BASE_ADDR                0x1b0
-#define PCIE_REG_M32B_BASE_MASK                0x1b8
-#define PCIE_REG_M32B_START_ADDR       0x1c0
-
-#define PCIE_REG_M64_BASE_ADDR         0x1e0
-#define PCIE_REG_M64_BASE_MASK         0x1e8
-#define PCIE_REG_M64_START_ADDR                0x1f0
-
-#define PCIE_REG_TCE_KILL              0x210
-#define   PCIE_REG_TCEKILL_SINGLE      0x8000000000000000ull
-#define   PCIE_REG_TCEKILL_ADDR_MASK   0x000003fffffffff8ull
-#define   PCIE_REG_TCEKILL_PS_4K       0
-#define   PCIE_REG_TCEKILL_PS_64K      1
-#define   PCIE_REG_TCEKILL_PS_16M      2
-#define   PCIE_REG_TCEKILL_PS_16G      3
-
-#define PCIE_REG_IODA_ADDR             0x220
-#define   PCIE_REG_IODA_AD_AUTOINC     0x8000000000000000ull
-#define   PCIE_REG_IODA_AD_TBL_MVT     0x0005000000000000ull
-#define   PCIE_REG_IODA_AD_TBL_PELT    0x0006000000000000ull
-#define   PCIE_REG_IODA_AD_TBL_PESTA   0x0007000000000000ull
-#define   PCIE_REG_IODA_AD_TBL_PESTB   0x0008000000000000ull
-#define   PCIE_REG_IODA_AD_TBL_TVT     0x0009000000000000ull
-#define   PCIE_REG_IODA_AD_TBL_TCE     0x000a000000000000ull
-#define PCIE_REG_IODA_DATA0            0x228
-#define PCIE_REG_IODA_DATA1            0x230
-
-#define PCIE_REG_LOCK2                 0x240
-
-#define PCIE_REG_PHB_GEN_CAP           0x250
-#define PCIE_REG_PHB_TCE_CAP           0x258
-#define PCIE_REG_PHB_IRQ_CAP           0x260
-#define PCIE_REG_PHB_EEH_CAP           0x268
-
-#define PCIE_REG_PAPR_ERR_INJ_CONTROL  0x2b0
-#define PCIE_REG_PAPR_ERR_INJ_ADDR     0x2b8
-#define PCIE_REG_PAPR_ERR_INJ_MASK     0x2c0
-
-
-#define PCIE_REG_SYS_CFG1              0x600
-#define   PCIE_REG_SYS_CFG1_CLASS_CODE 0x0000000000ffffffull
-
-#define IODA_TVT0_TTA_MASK             0x000fffffffff0000ull
-#define IODA_TVT0_TTA_SHIFT            4
-#define IODA_TVT0_BUSNUM_VALID_MASK    0x000000000000e000ull
-#define IODA_TVT0_TCE_TABLE_SIZE_MASK  0x0000000000001f00ull
-#define IODA_TVT0_TCE_TABLE_SIZE_SHIFT 8
-#define IODA_TVT0_BUSNUM_VALUE_MASK    0x00000000000000ffull
-#define IODA_TVT0_BUSNUM_VALID_SHIFT   0
-#define IODA_TVT1_DEVNUM_VALID         0x2000000000000000ull
-#define IODA_TVT1_DEVNUM_VALUE_MASK    0x1f00000000000000ull
-#define IODA_TVT1_DEVNUM_VALUE_SHIFT   56
-#define IODA_TVT1_FUNCNUM_VALID                0x0008000000000000ull
-#define IODA_TVT1_FUNCNUM_VALUE_MASK   0x0007000000000000ull
-#define IODA_TVT1_FUNCNUM_VALUE_SHIFT  48
-#define IODA_TVT1_IO_PAGE_SIZE_MASK    0x00001f0000000000ull
-#define IODA_TVT1_IO_PAGE_SIZE_SHIFT   40
-#define IODA_TVT1_PE_NUMBER_MASK       0x000000000000003full
-#define IODA_TVT1_PE_NUMBER_SHIFT      0
-
-#define IODA_TVT_COUNT                 64
-
-/* UTL Core registers */
-#define PCIE_UTL_SYS_BUS_CONTROL       0x400
-#define PCIE_UTL_STATUS                        0x408
-#define PCIE_UTL_SYS_BUS_AGENT_STATUS  0x410
-#define PCIE_UTL_SYS_BUS_AGENT_ERR_SEV 0x418
-#define PCIE_UTL_SYS_BUS_AGENT_IRQ_EN  0x420
-#define PCIE_UTL_SYS_BUS_BURST_SZ_CONF 0x440
-#define PCIE_UTL_REVISION_ID           0x448
-
-#define PCIE_UTL_OUT_POST_HDR_BUF_ALLOC        0x4c0
-#define PCIE_UTL_OUT_POST_DAT_BUF_ALLOC        0x4d0
-#define PCIE_UTL_IN_POST_HDR_BUF_ALLOC 0x4e0
-#define PCIE_UTL_IN_POST_DAT_BUF_ALLOC 0x4f0
-#define PCIE_UTL_OUT_NP_BUF_ALLOC      0x500
-#define PCIE_UTL_IN_NP_BUF_ALLOC       0x510
-#define PCIE_UTL_PCIE_TAGS_ALLOC       0x520
-#define PCIE_UTL_GBIF_READ_TAGS_ALLOC  0x530
-
-#define PCIE_UTL_PCIE_PORT_CONTROL     0x540
-#define PCIE_UTL_PCIE_PORT_STATUS      0x548
-#define PCIE_UTL_PCIE_PORT_ERROR_SEV   0x550
-#define PCIE_UTL_PCIE_PORT_IRQ_EN      0x558
-#define PCIE_UTL_RC_STATUS             0x560
-#define PCIE_UTL_RC_ERR_SEVERITY       0x568
-#define PCIE_UTL_RC_IRQ_EN             0x570
-#define PCIE_UTL_EP_STATUS             0x578
-#define PCIE_UTL_EP_ERR_SEVERITY       0x580
-#define PCIE_UTL_EP_ERR_IRQ_EN         0x588
-
-#define PCIE_UTL_PCI_PM_CTRL1          0x590
-#define PCIE_UTL_PCI_PM_CTRL2          0x598
-
-/* PCIe stack registers */
-#define PCIE_REG_SYSTEM_CONFIG1                0x600
-#define PCIE_REG_SYSTEM_CONFIG2                0x608
-#define PCIE_REG_EP_SYSTEM_CONFIG      0x618
-#define PCIE_REG_EP_FLR                        0x620
-#define PCIE_REG_EP_BAR_CONFIG         0x628
-#define PCIE_REG_LINK_CONFIG           0x630
-#define PCIE_REG_PM_CONFIG             0x640
-#define PCIE_REG_DLP_CONTROL           0x650
-#define PCIE_REG_DLP_STATUS            0x658
-#define PCIE_REG_ERR_REPORT_CONTROL    0x660
-#define PCIE_REG_SLOT_CONTROL1         0x670
-#define PCIE_REG_SLOT_CONTROL2         0x678
-#define PCIE_REG_UTL_CONFIG            0x680
-#define PCIE_REG_BUFFERS_CONFIG                0x690
-#define PCIE_REG_ERROR_INJECT          0x698
-#define PCIE_REG_SRIOV_CONFIG          0x6a0
-#define PCIE_REG_PF0_SRIOV_STATUS      0x6a8
-#define PCIE_REG_PF1_SRIOV_STATUS      0x6b0
-#define PCIE_REG_PORT_NUMBER           0x700
-#define PCIE_REG_POR_SYSTEM_CONFIG     0x708
-
-/* PHB internal logic registers */
-#define PCIE_REG_PHB_VERSION           0x800
-#define PCIE_REG_RESET                 0x808
-#define PCIE_REG_PHB_CONTROL           0x810
-#define PCIE_REG_PHB_TIMEOUT_CONTROL1  0x878
-#define PCIE_REG_PHB_QUIESCE_DMA       0x888
-#define PCIE_REG_PHB_DMA_READ_TAG_ACTV 0x900
-#define PCIE_REG_PHB_TCE_READ_TAG_ACTV 0x908
-
-/* FIR registers */
-#define PCIE_REG_LEM_FIR_ACCUM         0xc00
-#define PCIE_REG_LEM_FIR_AND_MASK      0xc08
-#define PCIE_REG_LEM_FIR_OR_MASK       0xc10
-#define PCIE_REG_LEM_ACTION0           0xc18
-#define PCIE_REG_LEM_ACTION1           0xc20
-#define PCIE_REG_LEM_ERROR_MASK                0xc30
-#define PCIE_REG_LEM_ERROR_AND_MASK    0xc38
-#define PCIE_REG_LEM_ERROR_OR_MASK     0xc40
-
-/* PHB Error registers */
-#define PCIE_REG_PHB_ERR_STATUS                0xc80
-#define PCIE_REG_PHB_ERR1_STATUS       0xc88
-#define PCIE_REG_PHB_ERR_INJECT                0xc90
-#define PCIE_REG_PHB_ERR_LEM_ENABLE    0xc98
-#define PCIE_REG_PHB_ERR_IRQ_ENABLE    0xca0
-#define PCIE_REG_PHB_ERR_FREEZE_ENABLE 0xca8
-#define PCIE_REG_PHB_ERR_SIDE_ENABLE   0xcb8
-#define PCIE_REG_PHB_ERR_LOG_0         0xcc0
-#define PCIE_REG_PHB_ERR_LOG_1         0xcc8
-#define PCIE_REG_PHB_ERR_STATUS_MASK   0xcd0
-#define PCIE_REG_PHB_ERR1_STATUS_MASK  0xcd8
-
-#define PCIE_REG_MMIO_ERR_STATUS       0xd00
-#define PCIE_REG_MMIO_ERR1_STATUS      0xd08
-#define PCIE_REG_MMIO_ERR_INJECT       0xd10
-#define PCIE_REG_MMIO_ERR_LEM_ENABLE   0xd18
-#define PCIE_REG_MMIO_ERR_IRQ_ENABLE   0xd20
-#define PCIE_REG_MMIO_ERR_FREEZE_ENABLE        0xd28
-#define PCIE_REG_MMIO_ERR_SIDE_ENABLE  0xd38
-#define PCIE_REG_MMIO_ERR_LOG_0                0xd40
-#define PCIE_REG_MMIO_ERR_LOG_1                0xd48
-#define PCIE_REG_MMIO_ERR_STATUS_MASK  0xd50
-#define PCIE_REG_MMIO_ERR1_STATUS_MASK 0xd58
-
-#define PCIE_REG_DMA_ERR_STATUS                0xd80
-#define PCIE_REG_DMA_ERR1_STATUS       0xd88
-#define PCIE_REG_DMA_ERR_INJECT                0xd90
-#define PCIE_REG_DMA_ERR_LEM_ENABLE    0xd98
-#define PCIE_REG_DMA_ERR_IRQ_ENABLE    0xda0
-#define PCIE_REG_DMA_ERR_FREEZE_ENABLE 0xda8
-#define PCIE_REG_DMA_ERR_SIDE_ENABLE   0xdb8
-#define PCIE_REG_DMA_ERR_LOG_0         0xdc0
-#define PCIE_REG_DMA_ERR_LOG_1         0xdc8
-#define PCIE_REG_DMA_ERR_STATUS_MASK   0xdd0
-#define PCIE_REG_DMA_ERR1_STATUS_MASK  0xdd8
-
-/* Shortcuts for access to the above using the PHB definitions
- * with an offset
- */
-#define PCIE_REG_ERR_PHB_OFFSET                0x0
-#define PCIE_REG_ERR_MMIO_OFFSET       0x80
-#define PCIE_REG_ERR_DMA_OFFSET                0x100
-
-/* Debug and Trace registers */
-#define PCIE_REG_PHB_DEBUG_CONTROL0    0xe00
-#define PCIE_REG_PHB_DEBUG_STATUS0     0xe08
-#define PCIE_REG_PHB_DEBUG_CONTROL1    0xe10
-#define PCIE_REG_PHB_DEBUG_STATUS1     0xe18
-#define PCIE_REG_PHB_DEBUG_CONTROL2    0xe20
-#define PCIE_REG_PHB_DEBUG_STATUS2     0xe28
-#define PCIE_REG_PHB_DEBUG_CONTROL3    0xe30
-#define PCIE_REG_PHB_DEBUG_STATUS3     0xe38
-#define PCIE_REG_PHB_DEBUG_CONTROL4    0xe40
-#define PCIE_REG_PHB_DEBUG_STATUS4     0xe48
-#define PCIE_REG_PHB_DEBUG_CONTROL5    0xe50
-#define PCIE_REG_PHB_DEBUG_STATUS5     0xe58
-#define PCIE_REG_PHB_DEBUG_CONTROL6    0xe60
-#define PCIE_REG_PHB_DEBUG_STATUS6     0xe68
-
-/* Definition for PCIe errors */
-struct wsp_pcie_err_log_data {
-       __u64   phb_err;
-       __u64   phb_err1;
-       __u64   phb_log0;
-       __u64   phb_log1;
-       __u64   mmio_err;
-       __u64   mmio_err1;
-       __u64   mmio_log0;
-       __u64   mmio_log1;
-       __u64   dma_err;
-       __u64   dma_err1;
-       __u64   dma_log0;
-       __u64   dma_log1;
-       __u64   utl_sys_err;
-       __u64   utl_port_err;
-       __u64   utl_rc_err;
-       __u64   unused;
-};
-
-#endif /* __WSP_PCI_H */
index 228cf91b91c14bc4e865e89371595363aa7858cc..ffd1169ebaab8387c6a15831f5c660b1f90fa243 100644 (file)
@@ -25,7 +25,6 @@
 #include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/phy.h>
-#include <linux/phy_fixed.h>
 #include <linux/spi/spi.h>
 #include <linux/fsl_devices.h>
 #include <linux/fs_enet_pd.h>
@@ -178,37 +177,6 @@ u32 get_baudrate(void)
 EXPORT_SYMBOL(get_baudrate);
 #endif /* CONFIG_CPM2 */
 
-#ifdef CONFIG_FIXED_PHY
-static int __init of_add_fixed_phys(void)
-{
-       int ret;
-       struct device_node *np;
-       u32 *fixed_link;
-       struct fixed_phy_status status = {};
-
-       for_each_node_by_name(np, "ethernet") {
-               fixed_link  = (u32 *)of_get_property(np, "fixed-link", NULL);
-               if (!fixed_link)
-                       continue;
-
-               status.link = 1;
-               status.duplex = fixed_link[1];
-               status.speed = fixed_link[2];
-               status.pause = fixed_link[3];
-               status.asym_pause = fixed_link[4];
-
-               ret = fixed_phy_add(PHY_POLL, fixed_link[0], &status);
-               if (ret) {
-                       of_node_put(np);
-                       return ret;
-               }
-       }
-
-       return 0;
-}
-arch_initcall(of_add_fixed_phys);
-#endif /* CONFIG_FIXED_PHY */
-
 #if defined(CONFIG_FSL_SOC_BOOKE) || defined(CONFIG_PPC_86xx)
 static __be32 __iomem *rstcr;
 
index 9dee47071af8a74bc61cf433da5366d8faef1d11..de8d9483bbe89136042d0685680c6bddc815849a 100644 (file)
@@ -26,6 +26,7 @@
 #include <asm/errno.h>
 #include <asm/xics.h>
 #include <asm/kvm_ppc.h>
+#include <asm/dbell.h>
 
 struct icp_ipl {
        union {
@@ -145,7 +146,13 @@ static unsigned int icp_native_get_irq(void)
 static void icp_native_cause_ipi(int cpu, unsigned long data)
 {
        kvmppc_set_host_ipi(cpu, 1);
-       icp_native_set_qirr(cpu, IPI_PRIORITY);
+#ifdef CONFIG_PPC_DOORBELL
+       if (cpu_has_feature(CPU_FTR_DBELL) &&
+           (cpumask_test_cpu(cpu, cpu_sibling_mask(smp_processor_id()))))
+               doorbell_cause_ipi(cpu, data);
+       else
+#endif
+               icp_native_set_qirr(cpu, IPI_PRIORITY);
 }
 
 void xics_wake_cpu(int cpu)
index bce3dcfe50583440f09e7229e08305559acc12f9..c98748617896b18c697f589de4089c635e87a1cc 100644 (file)
@@ -122,7 +122,7 @@ void xmon_printf(const char *format, ...)
 
        if (n && rc == 0) {
                /* No udbg hooks, fallback to printk() - dangerous */
-               printk(xmon_outbuf);
+               printk("%s", xmon_outbuf);
        }
 }
 
index e9f8fa9337fe1fae0d5d0ba9bf03a8426961a3c0..a2cbd875543a3f206328221c521be3e3aeeab596 100644 (file)
@@ -269,27 +269,17 @@ static void bpf_jit_noleaks(struct bpf_jit *jit, struct sock_filter *filter)
                EMIT4(0xa7c80000);
        /* Clear A if the first register does not set it. */
        switch (filter[0].code) {
-       case BPF_S_LD_W_ABS:
-       case BPF_S_LD_H_ABS:
-       case BPF_S_LD_B_ABS:
-       case BPF_S_LD_W_LEN:
-       case BPF_S_LD_W_IND:
-       case BPF_S_LD_H_IND:
-       case BPF_S_LD_B_IND:
-       case BPF_S_LD_IMM:
-       case BPF_S_LD_MEM:
-       case BPF_S_MISC_TXA:
-       case BPF_S_ANC_PROTOCOL:
-       case BPF_S_ANC_PKTTYPE:
-       case BPF_S_ANC_IFINDEX:
-       case BPF_S_ANC_MARK:
-       case BPF_S_ANC_QUEUE:
-       case BPF_S_ANC_HATYPE:
-       case BPF_S_ANC_RXHASH:
-       case BPF_S_ANC_CPU:
-       case BPF_S_ANC_VLAN_TAG:
-       case BPF_S_ANC_VLAN_TAG_PRESENT:
-       case BPF_S_RET_K:
+       case BPF_LD | BPF_W | BPF_ABS:
+       case BPF_LD | BPF_H | BPF_ABS:
+       case BPF_LD | BPF_B | BPF_ABS:
+       case BPF_LD | BPF_W | BPF_LEN:
+       case BPF_LD | BPF_W | BPF_IND:
+       case BPF_LD | BPF_H | BPF_IND:
+       case BPF_LD | BPF_B | BPF_IND:
+       case BPF_LD | BPF_IMM:
+       case BPF_LD | BPF_MEM:
+       case BPF_MISC | BPF_TXA:
+       case BPF_RET | BPF_K:
                /* first instruction sets A register */
                break;
        default: /* A = 0 */
@@ -304,15 +294,18 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
        unsigned int K;
        int offset;
        unsigned int mask;
+       u16 code;
 
        K = filter->k;
-       switch (filter->code) {
-       case BPF_S_ALU_ADD_X: /* A += X */
+       code = bpf_anc_helper(filter);
+
+       switch (code) {
+       case BPF_ALU | BPF_ADD | BPF_X: /* A += X */
                jit->seen |= SEEN_XREG;
                /* ar %r5,%r12 */
                EMIT2(0x1a5c);
                break;
-       case BPF_S_ALU_ADD_K: /* A += K */
+       case BPF_ALU | BPF_ADD | BPF_K: /* A += K */
                if (!K)
                        break;
                if (K <= 16383)
@@ -325,12 +318,12 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
                        /* a %r5,<d(K)>(%r13) */
                        EMIT4_DISP(0x5a50d000, EMIT_CONST(K));
                break;
-       case BPF_S_ALU_SUB_X: /* A -= X */
+       case BPF_ALU | BPF_SUB | BPF_X: /* A -= X */
                jit->seen |= SEEN_XREG;
                /* sr %r5,%r12 */
                EMIT2(0x1b5c);
                break;
-       case BPF_S_ALU_SUB_K: /* A -= K */
+       case BPF_ALU | BPF_SUB | BPF_K: /* A -= K */
                if (!K)
                        break;
                if (K <= 16384)
@@ -343,12 +336,12 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
                        /* s %r5,<d(K)>(%r13) */
                        EMIT4_DISP(0x5b50d000, EMIT_CONST(K));
                break;
-       case BPF_S_ALU_MUL_X: /* A *= X */
+       case BPF_ALU | BPF_MUL | BPF_X: /* A *= X */
                jit->seen |= SEEN_XREG;
                /* msr %r5,%r12 */
                EMIT4(0xb252005c);
                break;
-       case BPF_S_ALU_MUL_K: /* A *= K */
+       case BPF_ALU | BPF_MUL | BPF_K: /* A *= K */
                if (K <= 16383)
                        /* mhi %r5,K */
                        EMIT4_IMM(0xa75c0000, K);
@@ -359,7 +352,7 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
                        /* ms %r5,<d(K)>(%r13) */
                        EMIT4_DISP(0x7150d000, EMIT_CONST(K));
                break;
-       case BPF_S_ALU_DIV_X: /* A /= X */
+       case BPF_ALU | BPF_DIV | BPF_X: /* A /= X */
                jit->seen |= SEEN_XREG | SEEN_RET0;
                /* ltr %r12,%r12 */
                EMIT2(0x12cc);
@@ -370,7 +363,7 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
                /* dlr %r4,%r12 */
                EMIT4(0xb997004c);
                break;
-       case BPF_S_ALU_DIV_K: /* A /= K */
+       case BPF_ALU | BPF_DIV | BPF_K: /* A /= K */
                if (K == 1)
                        break;
                /* lhi %r4,0 */
@@ -378,7 +371,7 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
                /* dl %r4,<d(K)>(%r13) */
                EMIT6_DISP(0xe340d000, 0x0097, EMIT_CONST(K));
                break;
-       case BPF_S_ALU_MOD_X: /* A %= X */
+       case BPF_ALU | BPF_MOD | BPF_X: /* A %= X */
                jit->seen |= SEEN_XREG | SEEN_RET0;
                /* ltr %r12,%r12 */
                EMIT2(0x12cc);
@@ -391,7 +384,7 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
                /* lr %r5,%r4 */
                EMIT2(0x1854);
                break;
-       case BPF_S_ALU_MOD_K: /* A %= K */
+       case BPF_ALU | BPF_MOD | BPF_K: /* A %= K */
                if (K == 1) {
                        /* lhi %r5,0 */
                        EMIT4(0xa7580000);
@@ -404,12 +397,12 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
                /* lr %r5,%r4 */
                EMIT2(0x1854);
                break;
-       case BPF_S_ALU_AND_X: /* A &= X */
+       case BPF_ALU | BPF_AND | BPF_X: /* A &= X */
                jit->seen |= SEEN_XREG;
                /* nr %r5,%r12 */
                EMIT2(0x145c);
                break;
-       case BPF_S_ALU_AND_K: /* A &= K */
+       case BPF_ALU | BPF_AND | BPF_K: /* A &= K */
                if (test_facility(21))
                        /* nilf %r5,<K> */
                        EMIT6_IMM(0xc05b0000, K);
@@ -417,12 +410,12 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
                        /* n %r5,<d(K)>(%r13) */
                        EMIT4_DISP(0x5450d000, EMIT_CONST(K));
                break;
-       case BPF_S_ALU_OR_X: /* A |= X */
+       case BPF_ALU | BPF_OR | BPF_X: /* A |= X */
                jit->seen |= SEEN_XREG;
                /* or %r5,%r12 */
                EMIT2(0x165c);
                break;
-       case BPF_S_ALU_OR_K: /* A |= K */
+       case BPF_ALU | BPF_OR | BPF_K: /* A |= K */
                if (test_facility(21))
                        /* oilf %r5,<K> */
                        EMIT6_IMM(0xc05d0000, K);
@@ -430,55 +423,55 @@ static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
                        /* o %r5,<d(K)>(%r13) */
                        EMIT4_DISP(0x5650d000, EMIT_CONST(K));
                break;
-       case BPF_S_ANC_ALU_XOR_X: /* A ^= X; */
-       case BPF_S_ALU_XOR_X:
+       case BPF_ANC | SKF_AD_ALU_XOR_X: /* A ^= X; */
+       case BPF_ALU | BPF_XOR | BPF_X:
                jit->seen |= SEEN_XREG;
                /* xr %r5,%r12 */
                EMIT2(0x175c);
                break;
-       case BPF_S_ALU_XOR_K: /* A ^= K */
+       case BPF_ALU | BPF_XOR | BPF_K: /* A ^= K */
                if (!K)
                        break;
                /* x %r5,<d(K)>(%r13) */
                EMIT4_DISP(0x5750d000, EMIT_CONST(K));
                break;
-       case BPF_S_ALU_LSH_X: /* A <<= X; */
+       case BPF_ALU | BPF_LSH | BPF_X: /* A <<= X; */
                jit->seen |= SEEN_XREG;
                /* sll %r5,0(%r12) */
                EMIT4(0x8950c000);
                break;
-       case BPF_S_ALU_LSH_K: /* A <<= K */
+       case BPF_ALU | BPF_LSH | BPF_K: /* A <<= K */
                if (K == 0)
                        break;
                /* sll %r5,K */
                EMIT4_DISP(0x89500000, K);
                break;
-       case BPF_S_ALU_RSH_X: /* A >>= X; */
+       case BPF_ALU | BPF_RSH | BPF_X: /* A >>= X; */
                jit->seen |= SEEN_XREG;
                /* srl %r5,0(%r12) */
                EMIT4(0x8850c000);
                break;
-       case BPF_S_ALU_RSH_K: /* A >>= K; */
+       case BPF_ALU | BPF_RSH | BPF_K: /* A >>= K; */
                if (K == 0)
                        break;
                /* srl %r5,K */
                EMIT4_DISP(0x88500000, K);
                break;
-       case BPF_S_ALU_NEG: /* A = -A */
+       case BPF_ALU | BPF_NEG: /* A = -A */
                /* lnr %r5,%r5 */
                EMIT2(0x1155);
                break;
-       case BPF_S_JMP_JA: /* ip += K */
+       case BPF_JMP | BPF_JA: /* ip += K */
                offset = addrs[i + K] + jit->start - jit->prg;
                EMIT4_PCREL(0xa7f40000, offset);
                break;
-       case BPF_S_JMP_JGT_K: /* ip += (A > K) ? jt : jf */
+       case BPF_JMP | BPF_JGT | BPF_K: /* ip += (A > K) ? jt : jf */
                mask = 0x200000; /* jh */
                goto kbranch;
-       case BPF_S_JMP_JGE_K: /* ip += (A >= K) ? jt : jf */
+       case BPF_JMP | BPF_JGE | BPF_K: /* ip += (A >= K) ? jt : jf */
                mask = 0xa00000; /* jhe */
                goto kbranch;
-       case BPF_S_JMP_JEQ_K: /* ip += (A == K) ? jt : jf */
+       case BPF_JMP | BPF_JEQ | BPF_K: /* ip += (A == K) ? jt : jf */
                mask = 0x800000; /* je */
 kbranch:       /* Emit compare if the branch targets are different */
                if (filter->jt != filter->jf) {
@@ -511,7 +504,7 @@ branch:             if (filter->jt == filter->jf) {
                        EMIT4_PCREL(0xa7040000 | (mask ^ 0xf00000), offset);
                }
                break;
-       case BPF_S_JMP_JSET_K: /* ip += (A & K) ? jt : jf */
+       case BPF_JMP | BPF_JSET | BPF_K: /* ip += (A & K) ? jt : jf */
                mask = 0x700000; /* jnz */
                /* Emit test if the branch targets are different */
                if (filter->jt != filter->jf) {
@@ -525,13 +518,13 @@ branch:           if (filter->jt == filter->jf) {
                                EMIT4_IMM(0xa7510000, K);
                }
                goto branch;
-       case BPF_S_JMP_JGT_X: /* ip += (A > X) ? jt : jf */
+       case BPF_JMP | BPF_JGT | BPF_X: /* ip += (A > X) ? jt : jf */
                mask = 0x200000; /* jh */
                goto xbranch;
-       case BPF_S_JMP_JGE_X: /* ip += (A >= X) ? jt : jf */
+       case BPF_JMP | BPF_JGE | BPF_X: /* ip += (A >= X) ? jt : jf */
                mask = 0xa00000; /* jhe */
                goto xbranch;
-       case BPF_S_JMP_JEQ_X: /* ip += (A == X) ? jt : jf */
+       case BPF_JMP | BPF_JEQ | BPF_X: /* ip += (A == X) ? jt : jf */
                mask = 0x800000; /* je */
 xbranch:       /* Emit compare if the branch targets are different */
                if (filter->jt != filter->jf) {
@@ -540,7 +533,7 @@ branch:             if (filter->jt == filter->jf) {
                        EMIT2(0x195c);
                }
                goto branch;
-       case BPF_S_JMP_JSET_X: /* ip += (A & X) ? jt : jf */
+       case BPF_JMP | BPF_JSET | BPF_X: /* ip += (A & X) ? jt : jf */
                mask = 0x700000; /* jnz */
                /* Emit test if the branch targets are different */
                if (filter->jt != filter->jf) {
@@ -551,15 +544,15 @@ branch:           if (filter->jt == filter->jf) {
                        EMIT2(0x144c);
                }
                goto branch;
-       case BPF_S_LD_W_ABS: /* A = *(u32 *) (skb->data+K) */
+       case BPF_LD | BPF_W | BPF_ABS: /* A = *(u32 *) (skb->data+K) */
                jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_WORD;
                offset = jit->off_load_word;
                goto load_abs;
-       case BPF_S_LD_H_ABS: /* A = *(u16 *) (skb->data+K) */
+       case BPF_LD | BPF_H | BPF_ABS: /* A = *(u16 *) (skb->data+K) */
                jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_HALF;
                offset = jit->off_load_half;
                goto load_abs;
-       case BPF_S_LD_B_ABS: /* A = *(u8 *) (skb->data+K) */
+       case BPF_LD | BPF_B | BPF_ABS: /* A = *(u8 *) (skb->data+K) */
                jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_BYTE;
                offset = jit->off_load_byte;
 load_abs:      if ((int) K < 0)
@@ -573,19 +566,19 @@ load_abs: if ((int) K < 0)
                /* jnz <ret0> */
                EMIT4_PCREL(0xa7740000, (jit->ret0_ip - jit->prg));
                break;
-       case BPF_S_LD_W_IND: /* A = *(u32 *) (skb->data+K+X) */
+       case BPF_LD | BPF_W | BPF_IND: /* A = *(u32 *) (skb->data+K+X) */
                jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_IWORD;
                offset = jit->off_load_iword;
                goto call_fn;
-       case BPF_S_LD_H_IND: /* A = *(u16 *) (skb->data+K+X) */
+       case BPF_LD | BPF_H | BPF_IND: /* A = *(u16 *) (skb->data+K+X) */
                jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_IHALF;
                offset = jit->off_load_ihalf;
                goto call_fn;
-       case BPF_S_LD_B_IND: /* A = *(u8 *) (skb->data+K+X) */
+       case BPF_LD | BPF_B | BPF_IND: /* A = *(u8 *) (skb->data+K+X) */
                jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_IBYTE;
                offset = jit->off_load_ibyte;
                goto call_fn;
-       case BPF_S_LDX_B_MSH:
+       case BPF_LDX | BPF_B | BPF_MSH:
                /* X = (*(u8 *)(skb->data+K) & 0xf) << 2 */
                jit->seen |= SEEN_RET0;
                if ((int) K < 0) {
@@ -596,17 +589,17 @@ load_abs: if ((int) K < 0)
                jit->seen |= SEEN_DATAREF | SEEN_LOAD_BMSH;
                offset = jit->off_load_bmsh;
                goto call_fn;
-       case BPF_S_LD_W_LEN: /* A = skb->len; */
+       case BPF_LD | BPF_W | BPF_LEN: /*       A = skb->len; */
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4);
                /* l %r5,<d(len)>(%r2) */
                EMIT4_DISP(0x58502000, offsetof(struct sk_buff, len));
                break;
-       case BPF_S_LDX_W_LEN: /* X = skb->len; */
+       case BPF_LDX | BPF_W | BPF_LEN: /* X = skb->len; */
                jit->seen |= SEEN_XREG;
                /* l %r12,<d(len)>(%r2) */
                EMIT4_DISP(0x58c02000, offsetof(struct sk_buff, len));
                break;
-       case BPF_S_LD_IMM: /* A = K */
+       case BPF_LD | BPF_IMM: /* A = K */
                if (K <= 16383)
                        /* lhi %r5,K */
                        EMIT4_IMM(0xa7580000, K);
@@ -617,7 +610,7 @@ load_abs:   if ((int) K < 0)
                        /* l %r5,<d(K)>(%r13) */
                        EMIT4_DISP(0x5850d000, EMIT_CONST(K));
                break;
-       case BPF_S_LDX_IMM: /* X = K */
+       case BPF_LDX | BPF_IMM: /* X = K */
                jit->seen |= SEEN_XREG;
                if (K <= 16383)
                        /* lhi %r12,<K> */
@@ -629,29 +622,29 @@ load_abs: if ((int) K < 0)
                        /* l %r12,<d(K)>(%r13) */
                        EMIT4_DISP(0x58c0d000, EMIT_CONST(K));
                break;
-       case BPF_S_LD_MEM: /* A = mem[K] */
+       case BPF_LD | BPF_MEM: /* A = mem[K] */
                jit->seen |= SEEN_MEM;
                /* l %r5,<K>(%r15) */
                EMIT4_DISP(0x5850f000,
                           (jit->seen & SEEN_DATAREF) ? 160 + K*4 : K*4);
                break;
-       case BPF_S_LDX_MEM: /* X = mem[K] */
+       case BPF_LDX | BPF_MEM: /* X = mem[K] */
                jit->seen |= SEEN_XREG | SEEN_MEM;
                /* l %r12,<K>(%r15) */
                EMIT4_DISP(0x58c0f000,
                           (jit->seen & SEEN_DATAREF) ? 160 + K*4 : K*4);
                break;
-       case BPF_S_MISC_TAX: /* X = A */
+       case BPF_MISC | BPF_TAX: /* X = A */
                jit->seen |= SEEN_XREG;
                /* lr %r12,%r5 */
                EMIT2(0x18c5);
                break;
-       case BPF_S_MISC_TXA: /* A = X */
+       case BPF_MISC | BPF_TXA: /* A = X */
                jit->seen |= SEEN_XREG;
                /* lr %r5,%r12 */
                EMIT2(0x185c);
                break;
-       case BPF_S_RET_K:
+       case BPF_RET | BPF_K:
                if (K == 0) {
                        jit->seen |= SEEN_RET0;
                        if (last)
@@ -671,33 +664,33 @@ load_abs: if ((int) K < 0)
                        EMIT4_PCREL(0xa7f40000, jit->exit_ip - jit->prg);
                }
                break;
-       case BPF_S_RET_A:
+       case BPF_RET | BPF_A:
                /* llgfr %r2,%r5 */
                EMIT4(0xb9160025);
                /* j <exit> */
                EMIT4_PCREL(0xa7f40000, jit->exit_ip - jit->prg);
                break;
-       case BPF_S_ST: /* mem[K] = A */
+       case BPF_ST: /* mem[K] = A */
                jit->seen |= SEEN_MEM;
                /* st %r5,<K>(%r15) */
                EMIT4_DISP(0x5050f000,
                           (jit->seen & SEEN_DATAREF) ? 160 + K*4 : K*4);
                break;
-       case BPF_S_STX: /* mem[K] = X : mov %ebx,off8(%rbp) */
+       case BPF_STX: /* mem[K] = X : mov %ebx,off8(%rbp) */
                jit->seen |= SEEN_XREG | SEEN_MEM;
                /* st %r12,<K>(%r15) */
                EMIT4_DISP(0x50c0f000,
                           (jit->seen & SEEN_DATAREF) ? 160 + K*4 : K*4);
                break;
-       case BPF_S_ANC_PROTOCOL: /* A = ntohs(skb->protocol); */
+       case BPF_ANC | SKF_AD_PROTOCOL: /* A = ntohs(skb->protocol); */
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, protocol) != 2);
                /* lhi %r5,0 */
                EMIT4(0xa7580000);
                /* icm  %r5,3,<d(protocol)>(%r2) */
                EMIT4_DISP(0xbf532000, offsetof(struct sk_buff, protocol));
                break;
-       case BPF_S_ANC_IFINDEX: /* if (!skb->dev) return 0;
-                                * A = skb->dev->ifindex */
+       case BPF_ANC | SKF_AD_IFINDEX:  /* if (!skb->dev) return 0;
+                                        * A = skb->dev->ifindex */
                BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, ifindex) != 4);
                jit->seen |= SEEN_RET0;
                /* lg %r1,<d(dev)>(%r2) */
@@ -709,20 +702,20 @@ load_abs: if ((int) K < 0)
                /* l %r5,<d(ifindex)>(%r1) */
                EMIT4_DISP(0x58501000, offsetof(struct net_device, ifindex));
                break;
-       case BPF_S_ANC_MARK: /* A = skb->mark */
+       case BPF_ANC | SKF_AD_MARK: /* A = skb->mark */
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4);
                /* l %r5,<d(mark)>(%r2) */
                EMIT4_DISP(0x58502000, offsetof(struct sk_buff, mark));
                break;
-       case BPF_S_ANC_QUEUE: /* A = skb->queue_mapping */
+       case BPF_ANC | SKF_AD_QUEUE: /* A = skb->queue_mapping */
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, queue_mapping) != 2);
                /* lhi %r5,0 */
                EMIT4(0xa7580000);
                /* icm  %r5,3,<d(queue_mapping)>(%r2) */
                EMIT4_DISP(0xbf532000, offsetof(struct sk_buff, queue_mapping));
                break;
-       case BPF_S_ANC_HATYPE:  /* if (!skb->dev) return 0;
-                                * A = skb->dev->type */
+       case BPF_ANC | SKF_AD_HATYPE:   /* if (!skb->dev) return 0;
+                                        * A = skb->dev->type */
                BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, type) != 2);
                jit->seen |= SEEN_RET0;
                /* lg %r1,<d(dev)>(%r2) */
@@ -736,20 +729,20 @@ load_abs: if ((int) K < 0)
                /* icm  %r5,3,<d(type)>(%r1) */
                EMIT4_DISP(0xbf531000, offsetof(struct net_device, type));
                break;
-       case BPF_S_ANC_RXHASH: /* A = skb->hash */
+       case BPF_ANC | SKF_AD_RXHASH: /* A = skb->hash */
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4);
                /* l %r5,<d(hash)>(%r2) */
                EMIT4_DISP(0x58502000, offsetof(struct sk_buff, hash));
                break;
-       case BPF_S_ANC_VLAN_TAG:
-       case BPF_S_ANC_VLAN_TAG_PRESENT:
+       case BPF_ANC | SKF_AD_VLAN_TAG:
+       case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT:
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2);
                BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000);
                /* lhi %r5,0 */
                EMIT4(0xa7580000);
                /* icm  %r5,3,<d(vlan_tci)>(%r2) */
                EMIT4_DISP(0xbf532000, offsetof(struct sk_buff, vlan_tci));
-               if (filter->code == BPF_S_ANC_VLAN_TAG) {
+               if (code == (BPF_ANC | SKF_AD_VLAN_TAG)) {
                        /* nill %r5,0xefff */
                        EMIT4_IMM(0xa5570000, ~VLAN_TAG_PRESENT);
                } else {
@@ -759,7 +752,7 @@ load_abs:   if ((int) K < 0)
                        EMIT4_DISP(0x88500000, 12);
                }
                break;
-       case BPF_S_ANC_PKTTYPE:
+       case BPF_ANC | SKF_AD_PKTTYPE:
                if (pkt_type_offset < 0)
                        goto out;
                /* lhi %r5,0 */
@@ -769,7 +762,7 @@ load_abs:   if ((int) K < 0)
                /* srl %r5,5 */
                EMIT4_DISP(0x88500000, 5);
                break;
-       case BPF_S_ANC_CPU: /* A = smp_processor_id() */
+       case BPF_ANC | SKF_AD_CPU: /* A = smp_processor_id() */
 #ifdef CONFIG_SMP
                /* l %r5,<d(cpu_nr)> */
                EMIT4_DISP(0x58500000, offsetof(struct _lowcore, cpu_nr));
index bdbda1453aa9f168339896d6880e3e2acd44182f..04471dc64847269e815a26752ef029cd9206c154 100644 (file)
@@ -238,4 +238,16 @@ static inline __sum16 ip_compute_csum(const void *buff, int len)
        return csum_fold(csum_partial(buff, len, 0));
 }
 
+#define HAVE_ARCH_CSUM_ADD
+static inline __wsum csum_add(__wsum csum, __wsum addend)
+{
+       __asm__ __volatile__(
+               "addcc   %0, %1, %0\n"
+               "addx    %0, %%g0, %0"
+               : "=r" (csum)
+               : "r" (addend), "0" (csum));
+
+       return csum;
+}
+
 #endif /* !(__SPARC_CHECKSUM_H) */
index 019b9615e43c16b81230508109b2484d6313ee62..2ff81ae8f3afa17b0fd25a5a6786131a9e30bc1b 100644 (file)
@@ -164,4 +164,16 @@ static inline __sum16 ip_compute_csum(const void *buff, int len)
        return csum_fold(csum_partial(buff, len, 0));
 }
 
+#define HAVE_ARCH_CSUM_ADD
+static inline __wsum csum_add(__wsum csum, __wsum addend)
+{
+       __asm__ __volatile__(
+               "addcc   %0, %1, %0\n"
+               "addx    %0, %%g0, %0"
+               : "=r" (csum)
+               : "r" (addend), "0" (csum));
+
+       return csum;
+}
+
 #endif /* !(__SPARC64_CHECKSUM_H) */
index a82c6b2a9780cab8d882c2d13429f4889375229e..892a102671adafc03950f907612f23b6902f934a 100644 (file)
@@ -83,9 +83,9 @@ static void bpf_flush_icache(void *start_, void *end_)
 #define BNE            (F2(0, 2) | CONDNE)
 
 #ifdef CONFIG_SPARC64
-#define BNE_PTR                (F2(0, 1) | CONDNE | (2 << 20))
+#define BE_PTR         (F2(0, 1) | CONDE | (2 << 20))
 #else
-#define BNE_PTR                BNE
+#define BE_PTR         BE
 #endif
 
 #define SETHI(K, REG)  \
@@ -415,20 +415,11 @@ void bpf_jit_compile(struct sk_filter *fp)
                emit_reg_move(O7, r_saved_O7);
 
                switch (filter[0].code) {
-               case BPF_S_RET_K:
-               case BPF_S_LD_W_LEN:
-               case BPF_S_ANC_PROTOCOL:
-               case BPF_S_ANC_PKTTYPE:
-               case BPF_S_ANC_IFINDEX:
-               case BPF_S_ANC_MARK:
-               case BPF_S_ANC_RXHASH:
-               case BPF_S_ANC_VLAN_TAG:
-               case BPF_S_ANC_VLAN_TAG_PRESENT:
-               case BPF_S_ANC_CPU:
-               case BPF_S_ANC_QUEUE:
-               case BPF_S_LD_W_ABS:
-               case BPF_S_LD_H_ABS:
-               case BPF_S_LD_B_ABS:
+               case BPF_RET | BPF_K:
+               case BPF_LD | BPF_W | BPF_LEN:
+               case BPF_LD | BPF_W | BPF_ABS:
+               case BPF_LD | BPF_H | BPF_ABS:
+               case BPF_LD | BPF_B | BPF_ABS:
                        /* The first instruction sets the A register (or is
                         * a "RET 'constant'")
                         */
@@ -445,59 +436,60 @@ void bpf_jit_compile(struct sk_filter *fp)
                        unsigned int t_offset;
                        unsigned int f_offset;
                        u32 t_op, f_op;
+                       u16 code = bpf_anc_helper(&filter[i]);
                        int ilen;
 
-                       switch (filter[i].code) {
-                       case BPF_S_ALU_ADD_X:   /* A += X; */
+                       switch (code) {
+                       case BPF_ALU | BPF_ADD | BPF_X: /* A += X; */
                                emit_alu_X(ADD);
                                break;
-                       case BPF_S_ALU_ADD_K:   /* A += K; */
+                       case BPF_ALU | BPF_ADD | BPF_K: /* A += K; */
                                emit_alu_K(ADD, K);
                                break;
-                       case BPF_S_ALU_SUB_X:   /* A -= X; */
+                       case BPF_ALU | BPF_SUB | BPF_X: /* A -= X; */
                                emit_alu_X(SUB);
                                break;
-                       case BPF_S_ALU_SUB_K:   /* A -= K */
+                       case BPF_ALU | BPF_SUB | BPF_K: /* A -= K */
                                emit_alu_K(SUB, K);
                                break;
-                       case BPF_S_ALU_AND_X:   /* A &= X */
+                       case BPF_ALU | BPF_AND | BPF_X: /* A &= X */
                                emit_alu_X(AND);
                                break;
-                       case BPF_S_ALU_AND_K:   /* A &= K */
+                       case BPF_ALU | BPF_AND | BPF_K: /* A &= K */
                                emit_alu_K(AND, K);
                                break;
-                       case BPF_S_ALU_OR_X:    /* A |= X */
+                       case BPF_ALU | BPF_OR | BPF_X:  /* A |= X */
                                emit_alu_X(OR);
                                break;
-                       case BPF_S_ALU_OR_K:    /* A |= K */
+                       case BPF_ALU | BPF_OR | BPF_K:  /* A |= K */
                                emit_alu_K(OR, K);
                                break;
-                       case BPF_S_ANC_ALU_XOR_X: /* A ^= X; */
-                       case BPF_S_ALU_XOR_X:
+                       case BPF_ANC | SKF_AD_ALU_XOR_X: /* A ^= X; */
+                       case BPF_ALU | BPF_XOR | BPF_X:
                                emit_alu_X(XOR);
                                break;
-                       case BPF_S_ALU_XOR_K:   /* A ^= K */
+                       case BPF_ALU | BPF_XOR | BPF_K: /* A ^= K */
                                emit_alu_K(XOR, K);
                                break;
-                       case BPF_S_ALU_LSH_X:   /* A <<= X */
+                       case BPF_ALU | BPF_LSH | BPF_X: /* A <<= X */
                                emit_alu_X(SLL);
                                break;
-                       case BPF_S_ALU_LSH_K:   /* A <<= K */
+                       case BPF_ALU | BPF_LSH | BPF_K: /* A <<= K */
                                emit_alu_K(SLL, K);
                                break;
-                       case BPF_S_ALU_RSH_X:   /* A >>= X */
+                       case BPF_ALU | BPF_RSH | BPF_X: /* A >>= X */
                                emit_alu_X(SRL);
                                break;
-                       case BPF_S_ALU_RSH_K:   /* A >>= K */
+                       case BPF_ALU | BPF_RSH | BPF_K: /* A >>= K */
                                emit_alu_K(SRL, K);
                                break;
-                       case BPF_S_ALU_MUL_X:   /* A *= X; */
+                       case BPF_ALU | BPF_MUL | BPF_X: /* A *= X; */
                                emit_alu_X(MUL);
                                break;
-                       case BPF_S_ALU_MUL_K:   /* A *= K */
+                       case BPF_ALU | BPF_MUL | BPF_K: /* A *= K */
                                emit_alu_K(MUL, K);
                                break;
-                       case BPF_S_ALU_DIV_K:   /* A /= K with K != 0*/
+                       case BPF_ALU | BPF_DIV | BPF_K: /* A /= K with K != 0*/
                                if (K == 1)
                                        break;
                                emit_write_y(G0);
@@ -512,7 +504,7 @@ void bpf_jit_compile(struct sk_filter *fp)
 #endif
                                emit_alu_K(DIV, K);
                                break;
-                       case BPF_S_ALU_DIV_X:   /* A /= X; */
+                       case BPF_ALU | BPF_DIV | BPF_X: /* A /= X; */
                                emit_cmpi(r_X, 0);
                                if (pc_ret0 > 0) {
                                        t_offset = addrs[pc_ret0 - 1];
@@ -544,10 +536,10 @@ void bpf_jit_compile(struct sk_filter *fp)
 #endif
                                emit_alu_X(DIV);
                                break;
-                       case BPF_S_ALU_NEG:
+                       case BPF_ALU | BPF_NEG:
                                emit_neg();
                                break;
-                       case BPF_S_RET_K:
+                       case BPF_RET | BPF_K:
                                if (!K) {
                                        if (pc_ret0 == -1)
                                                pc_ret0 = i;
@@ -556,7 +548,7 @@ void bpf_jit_compile(struct sk_filter *fp)
                                        emit_loadimm(K, r_A);
                                }
                                /* Fallthrough */
-                       case BPF_S_RET_A:
+                       case BPF_RET | BPF_A:
                                if (seen_or_pass0) {
                                        if (i != flen - 1) {
                                                emit_jump(cleanup_addr);
@@ -573,18 +565,18 @@ void bpf_jit_compile(struct sk_filter *fp)
                                emit_jmpl(r_saved_O7, 8, G0);
                                emit_reg_move(r_A, O0); /* delay slot */
                                break;
-                       case BPF_S_MISC_TAX:
+                       case BPF_MISC | BPF_TAX:
                                seen |= SEEN_XREG;
                                emit_reg_move(r_A, r_X);
                                break;
-                       case BPF_S_MISC_TXA:
+                       case BPF_MISC | BPF_TXA:
                                seen |= SEEN_XREG;
                                emit_reg_move(r_X, r_A);
                                break;
-                       case BPF_S_ANC_CPU:
+                       case BPF_ANC | SKF_AD_CPU:
                                emit_load_cpu(r_A);
                                break;
-                       case BPF_S_ANC_PROTOCOL:
+                       case BPF_ANC | SKF_AD_PROTOCOL:
                                emit_skb_load16(protocol, r_A);
                                break;
 #if 0
@@ -592,38 +584,38 @@ void bpf_jit_compile(struct sk_filter *fp)
                                 * a bit field even though we very much
                                 * know what we are doing here.
                                 */
-                       case BPF_S_ANC_PKTTYPE:
+                       case BPF_ANC | SKF_AD_PKTTYPE:
                                __emit_skb_load8(pkt_type, r_A);
                                emit_alu_K(SRL, 5);
                                break;
 #endif
-                       case BPF_S_ANC_IFINDEX:
+                       case BPF_ANC | SKF_AD_IFINDEX:
                                emit_skb_loadptr(dev, r_A);
                                emit_cmpi(r_A, 0);
-                               emit_branch(BNE_PTR, cleanup_addr + 4);
+                               emit_branch(BE_PTR, cleanup_addr + 4);
                                emit_nop();
                                emit_load32(r_A, struct net_device, ifindex, r_A);
                                break;
-                       case BPF_S_ANC_MARK:
+                       case BPF_ANC | SKF_AD_MARK:
                                emit_skb_load32(mark, r_A);
                                break;
-                       case BPF_S_ANC_QUEUE:
+                       case BPF_ANC | SKF_AD_QUEUE:
                                emit_skb_load16(queue_mapping, r_A);
                                break;
-                       case BPF_S_ANC_HATYPE:
+                       case BPF_ANC | SKF_AD_HATYPE:
                                emit_skb_loadptr(dev, r_A);
                                emit_cmpi(r_A, 0);
-                               emit_branch(BNE_PTR, cleanup_addr + 4);
+                               emit_branch(BE_PTR, cleanup_addr + 4);
                                emit_nop();
                                emit_load16(r_A, struct net_device, type, r_A);
                                break;
-                       case BPF_S_ANC_RXHASH:
+                       case BPF_ANC | SKF_AD_RXHASH:
                                emit_skb_load32(hash, r_A);
                                break;
-                       case BPF_S_ANC_VLAN_TAG:
-                       case BPF_S_ANC_VLAN_TAG_PRESENT:
+                       case BPF_ANC | SKF_AD_VLAN_TAG:
+                       case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT:
                                emit_skb_load16(vlan_tci, r_A);
-                               if (filter[i].code == BPF_S_ANC_VLAN_TAG) {
+                               if (code == (BPF_ANC | SKF_AD_VLAN_TAG)) {
                                        emit_andi(r_A, VLAN_VID_MASK, r_A);
                                } else {
                                        emit_loadimm(VLAN_TAG_PRESENT, r_TMP);
@@ -631,44 +623,44 @@ void bpf_jit_compile(struct sk_filter *fp)
                                }
                                break;
 
-                       case BPF_S_LD_IMM:
+                       case BPF_LD | BPF_IMM:
                                emit_loadimm(K, r_A);
                                break;
-                       case BPF_S_LDX_IMM:
+                       case BPF_LDX | BPF_IMM:
                                emit_loadimm(K, r_X);
                                break;
-                       case BPF_S_LD_MEM:
+                       case BPF_LD | BPF_MEM:
                                emit_ldmem(K * 4, r_A);
                                break;
-                       case BPF_S_LDX_MEM:
+                       case BPF_LDX | BPF_MEM:
                                emit_ldmem(K * 4, r_X);
                                break;
-                       case BPF_S_ST:
+                       case BPF_ST:
                                emit_stmem(K * 4, r_A);
                                break;
-                       case BPF_S_STX:
+                       case BPF_STX:
                                emit_stmem(K * 4, r_X);
                                break;
 
 #define CHOOSE_LOAD_FUNC(K, func) \
        ((int)K < 0 ? ((int)K >= SKF_LL_OFF ? func##_negative_offset : func) : func##_positive_offset)
 
-                       case BPF_S_LD_W_ABS:
+                       case BPF_LD | BPF_W | BPF_ABS:
                                func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_word);
 common_load:                   seen |= SEEN_DATAREF;
                                emit_loadimm(K, r_OFF);
                                emit_call(func);
                                break;
-                       case BPF_S_LD_H_ABS:
+                       case BPF_LD | BPF_H | BPF_ABS:
                                func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_half);
                                goto common_load;
-                       case BPF_S_LD_B_ABS:
+                       case BPF_LD | BPF_B | BPF_ABS:
                                func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_byte);
                                goto common_load;
-                       case BPF_S_LDX_B_MSH:
+                       case BPF_LDX | BPF_B | BPF_MSH:
                                func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_byte_msh);
                                goto common_load;
-                       case BPF_S_LD_W_IND:
+                       case BPF_LD | BPF_W | BPF_IND:
                                func = bpf_jit_load_word;
 common_load_ind:               seen |= SEEN_DATAREF | SEEN_XREG;
                                if (K) {
@@ -683,13 +675,13 @@ common_load_ind:          seen |= SEEN_DATAREF | SEEN_XREG;
                                }
                                emit_call(func);
                                break;
-                       case BPF_S_LD_H_IND:
+                       case BPF_LD | BPF_H | BPF_IND:
                                func = bpf_jit_load_half;
                                goto common_load_ind;
-                       case BPF_S_LD_B_IND:
+                       case BPF_LD | BPF_B | BPF_IND:
                                func = bpf_jit_load_byte;
                                goto common_load_ind;
-                       case BPF_S_JMP_JA:
+                       case BPF_JMP | BPF_JA:
                                emit_jump(addrs[i + K]);
                                emit_nop();
                                break;
@@ -700,14 +692,14 @@ common_load_ind:          seen |= SEEN_DATAREF | SEEN_XREG;
                f_op = FOP;             \
                goto cond_branch
 
-                       COND_SEL(BPF_S_JMP_JGT_K, BGU, BLEU);
-                       COND_SEL(BPF_S_JMP_JGE_K, BGEU, BLU);
-                       COND_SEL(BPF_S_JMP_JEQ_K, BE, BNE);
-                       COND_SEL(BPF_S_JMP_JSET_K, BNE, BE);
-                       COND_SEL(BPF_S_JMP_JGT_X, BGU, BLEU);
-                       COND_SEL(BPF_S_JMP_JGE_X, BGEU, BLU);
-                       COND_SEL(BPF_S_JMP_JEQ_X, BE, BNE);
-                       COND_SEL(BPF_S_JMP_JSET_X, BNE, BE);
+                       COND_SEL(BPF_JMP | BPF_JGT | BPF_K, BGU, BLEU);
+                       COND_SEL(BPF_JMP | BPF_JGE | BPF_K, BGEU, BLU);
+                       COND_SEL(BPF_JMP | BPF_JEQ | BPF_K, BE, BNE);
+                       COND_SEL(BPF_JMP | BPF_JSET | BPF_K, BNE, BE);
+                       COND_SEL(BPF_JMP | BPF_JGT | BPF_X, BGU, BLEU);
+                       COND_SEL(BPF_JMP | BPF_JGE | BPF_X, BGEU, BLU);
+                       COND_SEL(BPF_JMP | BPF_JEQ | BPF_X, BE, BNE);
+                       COND_SEL(BPF_JMP | BPF_JSET | BPF_X, BNE, BE);
 
 cond_branch:                   f_offset = addrs[i + filter[i].jf];
                                t_offset = addrs[i + filter[i].jt];
@@ -719,20 +711,20 @@ cond_branch:                      f_offset = addrs[i + filter[i].jf];
                                        break;
                                }
 
-                               switch (filter[i].code) {
-                               case BPF_S_JMP_JGT_X:
-                               case BPF_S_JMP_JGE_X:
-                               case BPF_S_JMP_JEQ_X:
+                               switch (code) {
+                               case BPF_JMP | BPF_JGT | BPF_X:
+                               case BPF_JMP | BPF_JGE | BPF_X:
+                               case BPF_JMP | BPF_JEQ | BPF_X:
                                        seen |= SEEN_XREG;
                                        emit_cmp(r_A, r_X);
                                        break;
-                               case BPF_S_JMP_JSET_X:
+                               case BPF_JMP | BPF_JSET | BPF_X:
                                        seen |= SEEN_XREG;
                                        emit_btst(r_A, r_X);
                                        break;
-                               case BPF_S_JMP_JEQ_K:
-                               case BPF_S_JMP_JGT_K:
-                               case BPF_S_JMP_JGE_K:
+                               case BPF_JMP | BPF_JEQ | BPF_K:
+                               case BPF_JMP | BPF_JGT | BPF_K:
+                               case BPF_JMP | BPF_JGE | BPF_K:
                                        if (is_simm13(K)) {
                                                emit_cmpi(r_A, K);
                                        } else {
@@ -740,7 +732,7 @@ cond_branch:                        f_offset = addrs[i + filter[i].jf];
                                                emit_cmp(r_A, r_TMP);
                                        }
                                        break;
-                               case BPF_S_JMP_JSET_K:
+                               case BPF_JMP | BPF_JSET | BPF_K:
                                        if (is_simm13(K)) {
                                                emit_btsti(r_A, K);
                                        } else {
index 36e658a4291c685e12d554dfd74ac153179591dc..e4b1a9639c4ddfab7db3d91a080272b9c00fdba8 100644 (file)
@@ -111,8 +111,7 @@ endef
 KBUILD_KCONFIG := $(HOST_DIR)/um/Kconfig
 
 archheaders:
-       $(Q)$(MAKE) -C '$(srctree)' KBUILD_SRC= \
-               ARCH=$(HEADER_ARCH) O='$(objtree)' archheaders
+       $(Q)$(MAKE) KBUILD_SRC= ARCH=$(HEADER_ARCH) archheaders
 
 archprepare: include/generated/user_constants.h
 
index b660088c220d6c8996c348b2b6578314bc3878ea..fcefdda5136dde37c2a8893519912286f6551cf5 100644 (file)
@@ -121,6 +121,7 @@ config X86
        select MODULES_USE_ELF_RELA if X86_64
        select CLONE_BACKWARDS if X86_32
        select ARCH_USE_BUILTIN_BSWAP
+       select ARCH_USE_QUEUE_RWLOCK
        select OLD_SIGSUSPEND3 if X86_32 || IA32_EMULATION
        select OLD_SIGACTION if X86_32
        select COMPAT_OLD_SIGACTION if IA32_EMULATION
index 4582e8e1cd1ad43922d96b83a5cc2472d5ac72b7..7730c1c5c83aa7aaf6859170f812ad1f410c1a0b 100644 (file)
        .long (from) - . ;                                      \
        .long (to) - . + 0x7ffffff0 ;                           \
        .popsection
+
+# define _ASM_NOKPROBE(entry)                                  \
+       .pushsection "_kprobe_blacklist","aw" ;                 \
+       _ASM_ALIGN ;                                            \
+       _ASM_PTR (entry);                                       \
+       .popsection
 #else
 # define _ASM_EXTABLE(from,to)                                 \
        " .pushsection \"__ex_table\",\"a\"\n"                  \
@@ -71,6 +77,7 @@
        " .long (" #from ") - .\n"                              \
        " .long (" #to ") - . + 0x7ffffff0\n"                   \
        " .popsection\n"
+/* For C file, we already have NOKPROBE_SYMBOL macro */
 #endif
 
 #endif /* _ASM_X86_ASM_H */
index e6fd8a026c7be574e28d49d321d552d53c72b7cb..cd00e17744914c5b2e1d7d16da887a2809ebb5b5 100644 (file)
@@ -184,8 +184,15 @@ static inline unsigned add32_with_carry(unsigned a, unsigned b)
        asm("addl %2,%0\n\t"
            "adcl $0,%0"
            : "=r" (a)
-           : "0" (a), "r" (b));
+           : "0" (a), "rm" (b));
        return a;
 }
 
+#define HAVE_ARCH_CSUM_ADD
+static inline __wsum csum_add(__wsum csum, __wsum addend)
+{
+       return (__force __wsum)add32_with_carry((__force unsigned)csum,
+                                               (__force unsigned)addend);
+}
+
 #endif /* _ASM_X86_CHECKSUM_64_H */
index 9454c167629ff1dd7aac941e061f5ebda6da8868..53cdfb2857abe41f6f83b786a702cfb2fbd41ead 100644 (file)
@@ -116,4 +116,6 @@ struct kprobe_ctlblk {
 extern int kprobe_fault_handler(struct pt_regs *regs, int trapnr);
 extern int kprobe_exceptions_notify(struct notifier_block *self,
                                    unsigned long val, void *data);
+extern int kprobe_int3_handler(struct pt_regs *regs);
+extern int kprobe_debug_handler(struct pt_regs *regs);
 #endif /* _ASM_X86_KPROBES_H */
diff --git a/arch/x86/include/asm/qrwlock.h b/arch/x86/include/asm/qrwlock.h
new file mode 100644 (file)
index 0000000..70f46f0
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef _ASM_X86_QRWLOCK_H
+#define _ASM_X86_QRWLOCK_H
+
+#include <asm-generic/qrwlock_types.h>
+
+#if !defined(CONFIG_X86_OOSTORE) && !defined(CONFIG_X86_PPRO_FENCE)
+#define queue_write_unlock queue_write_unlock
+static inline void queue_write_unlock(struct qrwlock *lock)
+{
+        barrier();
+        ACCESS_ONCE(*(u8 *)&lock->cnts) = 0;
+}
+#endif
+
+#include <asm-generic/qrwlock.h>
+
+#endif /* _ASM_X86_QRWLOCK_H */
index 0f62f5482d91ec8fca86e884b6822fc467239dbc..54f1c8068c02340a4600049f843fd9f3067b51c5 100644 (file)
@@ -187,6 +187,7 @@ static inline void arch_spin_unlock_wait(arch_spinlock_t *lock)
                cpu_relax();
 }
 
+#ifndef CONFIG_QUEUE_RWLOCK
 /*
  * Read-write spinlocks, allowing multiple readers
  * but only one writer.
@@ -269,6 +270,9 @@ static inline void arch_write_unlock(arch_rwlock_t *rw)
        asm volatile(LOCK_PREFIX WRITE_LOCK_ADD(%1) "%0"
                     : "+m" (rw->write) : "i" (RW_LOCK_BIAS) : "memory");
 }
+#else
+#include <asm/qrwlock.h>
+#endif /* CONFIG_QUEUE_RWLOCK */
 
 #define arch_read_lock_flags(lock, flags) arch_read_lock(lock)
 #define arch_write_lock_flags(lock, flags) arch_write_lock(lock)
index 4f1bea19945bf8e4d6acf758c8c5fd42fce4bf2d..73c4c007200f066173f3f5628cefd62ccefa7d1e 100644 (file)
@@ -34,6 +34,10 @@ typedef struct arch_spinlock {
 
 #define __ARCH_SPIN_LOCK_UNLOCKED      { { 0 } }
 
+#ifdef CONFIG_QUEUE_RWLOCK
+#include <asm-generic/qrwlock_types.h>
+#else
 #include <asm/rwlock.h>
+#endif
 
 #endif /* _ASM_X86_SPINLOCK_TYPES_H */
index 8ba18842c48eac18aab3b90bfb9e0e3c292900a8..bc8352e7010a9e805c54068da84b3848dcc12048 100644 (file)
@@ -68,7 +68,7 @@ dotraplinkage void do_segment_not_present(struct pt_regs *, long);
 dotraplinkage void do_stack_segment(struct pt_regs *, long);
 #ifdef CONFIG_X86_64
 dotraplinkage void do_double_fault(struct pt_regs *, long);
-asmlinkage __kprobes struct pt_regs *sync_regs(struct pt_regs *);
+asmlinkage struct pt_regs *sync_regs(struct pt_regs *);
 #endif
 dotraplinkage void do_general_protection(struct pt_regs *, long);
 dotraplinkage void do_page_fault(struct pt_regs *, unsigned long);
@@ -103,7 +103,6 @@ static inline int get_si_code(unsigned long condition)
 
 extern int panic_on_unrecovered_nmi;
 
-void math_error(struct pt_regs *, int, int);
 void math_emulate(struct math_emu_info *);
 #ifndef CONFIG_X86_32
 asmlinkage void smp_thermal_interrupt(void);
index 93bee7b93854dc8a8aaa21cde812deed246e7c89..74f4c2ff6427292218521dc971c155d48b6405ff 100644 (file)
@@ -41,18 +41,18 @@ struct arch_uprobe {
                u8                      ixol[MAX_UINSN_BYTES];
        };
 
-       u16                             fixups;
        const struct uprobe_xol_ops     *ops;
 
        union {
-#ifdef CONFIG_X86_64
-               unsigned long                   rip_rela_target_address;
-#endif
                struct {
                        s32     offs;
                        u8      ilen;
                        u8      opc1;
-               }                               branch;
+               }                       branch;
+               struct {
+                       u8      fixups;
+                       u8      ilen;
+               }                       defparam;
        };
 };
 
index df94598ad05a845902e9897214cdceacc779a80d..703130f469ecf71978b9d67bf0fb50da9b31cbc9 100644 (file)
@@ -5,7 +5,6 @@
 #include <linux/mutex.h>
 #include <linux/list.h>
 #include <linux/stringify.h>
-#include <linux/kprobes.h>
 #include <linux/mm.h>
 #include <linux/vmalloc.h>
 #include <linux/memory.h>
@@ -551,7 +550,7 @@ void *__init_or_module text_poke_early(void *addr, const void *opcode,
  *
  * Note: Must be called under text_mutex.
  */
-void *__kprobes text_poke(void *addr, const void *opcode, size_t len)
+void *text_poke(void *addr, const void *opcode, size_t len)
 {
        unsigned long flags;
        char *vaddr;
index eab67047dec398200f16eae8668aa00f33c20809..c3fcb5de508391ca20684669b3746a2c734cad02 100644 (file)
@@ -60,7 +60,7 @@ void arch_trigger_all_cpu_backtrace(void)
        smp_mb__after_atomic();
 }
 
-static int __kprobes
+static int
 arch_trigger_all_cpu_backtrace_handler(unsigned int cmd, struct pt_regs *regs)
 {
        int cpu;
@@ -80,6 +80,7 @@ arch_trigger_all_cpu_backtrace_handler(unsigned int cmd, struct pt_regs *regs)
 
        return NMI_DONE;
 }
+NOKPROBE_SYMBOL(arch_trigger_all_cpu_backtrace_handler);
 
 static int __init register_trigger_all_cpu_backtrace(void)
 {
index 9d0a9795a0f80e56e7ff9acfd414ee7f5ed44459..81e08eff05eedbd2e839a14229464cbea456e84c 100644 (file)
@@ -2297,7 +2297,7 @@ int __ioapic_set_affinity(struct irq_data *data, const struct cpumask *mask,
        int err;
 
        if (!config_enabled(CONFIG_SMP))
-               return -1;
+               return -EPERM;
 
        if (!cpumask_intersects(mask, cpu_online_mask))
                return -EINVAL;
@@ -2328,7 +2328,7 @@ int native_ioapic_set_affinity(struct irq_data *data,
        int ret;
 
        if (!config_enabled(CONFIG_SMP))
-               return -1;
+               return -EPERM;
 
        raw_spin_lock_irqsave(&ioapic_lock, flags);
        ret = __ioapic_set_affinity(data, mask, &dest);
@@ -3001,9 +3001,11 @@ msi_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force)
        struct irq_cfg *cfg = data->chip_data;
        struct msi_msg msg;
        unsigned int dest;
+       int ret;
 
-       if (__ioapic_set_affinity(data, mask, &dest))
-               return -1;
+       ret = __ioapic_set_affinity(data, mask, &dest);
+       if (ret)
+               return ret;
 
        __get_cached_msi_msg(data->msi_desc, &msg);
 
@@ -3100,9 +3102,11 @@ dmar_msi_set_affinity(struct irq_data *data, const struct cpumask *mask,
        struct irq_cfg *cfg = data->chip_data;
        unsigned int dest, irq = data->irq;
        struct msi_msg msg;
+       int ret;
 
-       if (__ioapic_set_affinity(data, mask, &dest))
-               return -1;
+       ret = __ioapic_set_affinity(data, mask, &dest);
+       if (ret)
+               return ret;
 
        dmar_msi_read(irq, &msg);
 
@@ -3149,9 +3153,11 @@ static int hpet_msi_set_affinity(struct irq_data *data,
        struct irq_cfg *cfg = data->chip_data;
        struct msi_msg msg;
        unsigned int dest;
+       int ret;
 
-       if (__ioapic_set_affinity(data, mask, &dest))
-               return -1;
+       ret = __ioapic_set_affinity(data, mask, &dest);
+       if (ret)
+               return ret;
 
        hpet_msi_read(data->handler_data, &msg);
 
@@ -3218,9 +3224,11 @@ ht_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force)
 {
        struct irq_cfg *cfg = data->chip_data;
        unsigned int dest;
+       int ret;
 
-       if (__ioapic_set_affinity(data, mask, &dest))
-               return -1;
+       ret = __ioapic_set_affinity(data, mask, &dest);
+       if (ret)
+               return ret;
 
        target_ht_irq(data->irq, dest, cfg->vector);
        return IRQ_SET_MASK_OK_NOCOPY;
index 2cbbf88d8f2cb1084d25dcecb776793b8f715bc7..ef1b93f18ed1c328ca538863f89623afb45cb1a9 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/delay.h>
 #include <linux/sched.h>
 #include <linux/init.h>
+#include <linux/kprobes.h>
 #include <linux/kgdb.h>
 #include <linux/smp.h>
 #include <linux/io.h>
@@ -1193,6 +1194,7 @@ int is_debug_stack(unsigned long addr)
                (addr <= __get_cpu_var(debug_stack_addr) &&
                 addr > (__get_cpu_var(debug_stack_addr) - DEBUG_STKSZ));
 }
+NOKPROBE_SYMBOL(is_debug_stack);
 
 DEFINE_PER_CPU(u32, debug_idt_ctr);
 
@@ -1201,6 +1203,7 @@ void debug_stack_set_zero(void)
        this_cpu_inc(debug_idt_ctr);
        load_current_idt();
 }
+NOKPROBE_SYMBOL(debug_stack_set_zero);
 
 void debug_stack_reset(void)
 {
@@ -1209,6 +1212,7 @@ void debug_stack_reset(void)
        if (this_cpu_dec_return(debug_idt_ctr) == 0)
                load_current_idt();
 }
+NOKPROBE_SYMBOL(debug_stack_reset);
 
 #else  /* CONFIG_X86_64 */
 
index 76f98fe5b35c4a2f3860b762b54f039cb7ad642c..a450373e8e91698dd235725e8d8f9f35b3cec7cc 100644 (file)
@@ -132,15 +132,6 @@ static void __init ms_hyperv_init_platform(void)
                lapic_timer_frequency = hv_lapic_frequency;
                printk(KERN_INFO "HyperV: LAPIC Timer Frequency: %#x\n",
                                lapic_timer_frequency);
-
-               /*
-                * On Hyper-V, when we are booting off an EFI firmware stack,
-                * we do not have many legacy devices including PIC, PIT etc.
-                */
-               if (efi_enabled(EFI_BOOT)) {
-                       printk(KERN_INFO "HyperV: Using null_legacy_pic\n");
-                       legacy_pic = &null_legacy_pic;
-               }
        }
 #endif
 
index 89f3b7c1af2060556eb0aac096f09f2ecebeddf4..2bdfbff8a4f6165afb1e9931edcfb250c7113a34 100644 (file)
@@ -303,15 +303,6 @@ int x86_setup_perfctr(struct perf_event *event)
                hwc->sample_period = x86_pmu.max_period;
                hwc->last_period = hwc->sample_period;
                local64_set(&hwc->period_left, hwc->sample_period);
-       } else {
-               /*
-                * If we have a PMU initialized but no APIC
-                * interrupts, we cannot sample hardware
-                * events (user-space has to fall back and
-                * sample via a hrtimer based software event):
-                */
-               if (!x86_pmu.apic)
-                       return -EOPNOTSUPP;
        }
 
        if (attr->type == PERF_TYPE_RAW)
@@ -1293,7 +1284,7 @@ void perf_events_lapic_init(void)
        apic_write(APIC_LVTPC, APIC_DM_NMI);
 }
 
-static int __kprobes
+static int
 perf_event_nmi_handler(unsigned int cmd, struct pt_regs *regs)
 {
        u64 start_clock;
@@ -1311,6 +1302,7 @@ perf_event_nmi_handler(unsigned int cmd, struct pt_regs *regs)
 
        return ret;
 }
+NOKPROBE_SYMBOL(perf_event_nmi_handler);
 
 struct event_constraint emptyconstraint;
 struct event_constraint unconstrained;
@@ -1366,6 +1358,15 @@ static void __init pmu_check_apic(void)
        x86_pmu.apic = 0;
        pr_info("no APIC, boot with the \"lapic\" boot parameter to force-enable it.\n");
        pr_info("no hardware sampling interrupt available.\n");
+
+       /*
+        * If we have a PMU initialized but no APIC
+        * interrupts, we cannot sample hardware
+        * events (user-space has to fall back and
+        * sample via a hrtimer based software event):
+        */
+       pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;
+
 }
 
 static struct attribute_group x86_pmu_format_group = {
index 4c36bbe3173aa0f683a5a982cc857c6f3ffa3e0a..cbb1be3ed9e432aab5ff3b679e5e0ecc0bfd731a 100644 (file)
@@ -593,7 +593,7 @@ static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs)
        return 1;
 }
 
-static int __kprobes
+static int
 perf_ibs_nmi_handler(unsigned int cmd, struct pt_regs *regs)
 {
        int handled = 0;
@@ -606,6 +606,7 @@ perf_ibs_nmi_handler(unsigned int cmd, struct pt_regs *regs)
 
        return handled;
 }
+NOKPROBE_SYMBOL(perf_ibs_nmi_handler);
 
 static __init int perf_ibs_pmu_init(struct perf_ibs *perf_ibs, char *name)
 {
index d82d155aca8c7c6c44c9ba7150dae39e88e16fad..9dd2459a4c738d99bb4a75ebf51062bcbf6b21de 100644 (file)
@@ -384,6 +384,9 @@ static void intel_pmu_setup_sw_lbr_filter(struct perf_event *event)
        if (br_type & PERF_SAMPLE_BRANCH_NO_TX)
                mask |= X86_BR_NO_TX;
 
+       if (br_type & PERF_SAMPLE_BRANCH_COND)
+               mask |= X86_BR_JCC;
+
        /*
         * stash actual user request into reg, it may
         * be used by fixup code for some CPU
@@ -678,6 +681,7 @@ static const int nhm_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX] = {
         * NHM/WSM erratum: must include IND_JMP to capture IND_CALL
         */
        [PERF_SAMPLE_BRANCH_IND_CALL] = LBR_IND_CALL | LBR_IND_JMP,
+       [PERF_SAMPLE_BRANCH_COND]     = LBR_JCC,
 };
 
 static const int snb_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX] = {
@@ -689,6 +693,7 @@ static const int snb_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX] = {
        [PERF_SAMPLE_BRANCH_ANY_CALL]   = LBR_REL_CALL | LBR_IND_CALL
                                        | LBR_FAR,
        [PERF_SAMPLE_BRANCH_IND_CALL]   = LBR_IND_CALL,
+       [PERF_SAMPLE_BRANCH_COND]       = LBR_JCC,
 };
 
 /* core */
index d9c12d3022a70c68e3f69d4cf2d68bdc10123010..b74ebc7c4402e7eff3b21f4b87ba514e0c017056 100644 (file)
@@ -200,7 +200,7 @@ static arch_spinlock_t die_lock = __ARCH_SPIN_LOCK_UNLOCKED;
 static int die_owner = -1;
 static unsigned int die_nest_count;
 
-unsigned __kprobes long oops_begin(void)
+unsigned long oops_begin(void)
 {
        int cpu;
        unsigned long flags;
@@ -223,8 +223,9 @@ unsigned __kprobes long oops_begin(void)
        return flags;
 }
 EXPORT_SYMBOL_GPL(oops_begin);
+NOKPROBE_SYMBOL(oops_begin);
 
-void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, int signr)
+void oops_end(unsigned long flags, struct pt_regs *regs, int signr)
 {
        if (regs && kexec_should_crash(current))
                crash_kexec(regs);
@@ -247,8 +248,9 @@ void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, int signr)
                panic("Fatal exception");
        do_exit(signr);
 }
+NOKPROBE_SYMBOL(oops_end);
 
-int __kprobes __die(const char *str, struct pt_regs *regs, long err)
+int __die(const char *str, struct pt_regs *regs, long err)
 {
 #ifdef CONFIG_X86_32
        unsigned short ss;
@@ -291,6 +293,7 @@ int __kprobes __die(const char *str, struct pt_regs *regs, long err)
 #endif
        return 0;
 }
+NOKPROBE_SYMBOL(__die);
 
 /*
  * This is gone through when something in the kernel has done something bad
index 98313ffaae6ab0c651b904cbd68413bb3e341eb8..f0da82b8e63419b7d4469f077303492134b5ba5a 100644 (file)
@@ -314,10 +314,6 @@ ENTRY(ret_from_kernel_thread)
        CFI_ENDPROC
 ENDPROC(ret_from_kernel_thread)
 
-/*
- * Interrupt exit functions should be protected against kprobes
- */
-       .pushsection .kprobes.text, "ax"
 /*
  * Return to user mode is not as complex as all this looks,
  * but we want the default path for a system call return to
@@ -372,10 +368,6 @@ need_resched:
 END(resume_kernel)
 #endif
        CFI_ENDPROC
-/*
- * End of kprobes section
- */
-       .popsection
 
 /* SYSENTER_RETURN points to after the "sysenter" instruction in
    the vsyscall page.  See vsyscall-sysentry.S, which defines the symbol.  */
@@ -495,10 +487,6 @@ sysexit_audit:
        PTGS_TO_GS_EX
 ENDPROC(ia32_sysenter_target)
 
-/*
- * syscall stub including irq exit should be protected against kprobes
- */
-       .pushsection .kprobes.text, "ax"
        # system call handler stub
 ENTRY(system_call)
        RING0_INT_FRAME                 # can't unwind into user space anyway
@@ -690,10 +678,6 @@ syscall_badsys:
        jmp resume_userspace
 END(syscall_badsys)
        CFI_ENDPROC
-/*
- * End of kprobes section
- */
-       .popsection
 
 .macro FIXUP_ESPFIX_STACK
 /*
@@ -784,10 +768,6 @@ common_interrupt:
 ENDPROC(common_interrupt)
        CFI_ENDPROC
 
-/*
- *  Irq entries should be protected against kprobes
- */
-       .pushsection .kprobes.text, "ax"
 #define BUILD_INTERRUPT3(name, nr, fn) \
 ENTRY(name)                            \
        RING0_INT_FRAME;                \
@@ -964,10 +944,6 @@ ENTRY(spurious_interrupt_bug)
        jmp error_code
        CFI_ENDPROC
 END(spurious_interrupt_bug)
-/*
- * End of kprobes section
- */
-       .popsection
 
 #ifdef CONFIG_XEN
 /* Xen doesn't set %esp to be precisely what the normal sysenter
@@ -1242,11 +1218,6 @@ return_to_handler:
        jmp *%ecx
 #endif
 
-/*
- * Some functions should be protected against kprobes
- */
-       .pushsection .kprobes.text, "ax"
-
 #ifdef CONFIG_TRACING
 ENTRY(trace_page_fault)
        RING0_EC_FRAME
@@ -1460,7 +1431,3 @@ ENTRY(async_page_fault)
 END(async_page_fault)
 #endif
 
-/*
- * End of kprobes section
- */
-       .popsection
index 48a2644a082a4ccb25ede707403d1aa3fd212286..b25ca969edd27eca18210b013508d640775c6613 100644 (file)
@@ -284,8 +284,6 @@ ENDPROC(native_usergs_sysret64)
        TRACE_IRQS_OFF
        .endm
 
-/* save complete stack frame */
-       .pushsection .kprobes.text, "ax"
 ENTRY(save_paranoid)
        XCPT_FRAME 1 RDI+8
        cld
@@ -314,7 +312,6 @@ ENTRY(save_paranoid)
 1:     ret
        CFI_ENDPROC
 END(save_paranoid)
-       .popsection
 
 /*
  * A newly forked process directly context switches into this address.
@@ -772,10 +769,6 @@ END(interrupt)
        call \func
        .endm
 
-/*
- * Interrupt entry/exit should be protected against kprobes
- */
-       .pushsection .kprobes.text, "ax"
        /*
         * The interrupt stubs push (~vector+0x80) onto the stack and
         * then jump to common_interrupt.
@@ -982,11 +975,6 @@ END(__do_double_fault)
 # define __do_double_fault do_double_fault
 #endif
 
-/*
- * End of kprobes section
- */
-       .popsection
-
 /*
  * APIC interrupts.
  */
@@ -1321,11 +1309,6 @@ apicinterrupt3 HYPERVISOR_CALLBACK_VECTOR \
        hyperv_callback_vector hyperv_vector_handler
 #endif /* CONFIG_HYPERV */
 
-/*
- * Some functions should be protected against kprobes
- */
-       .pushsection .kprobes.text, "ax"
-
 idtentry debug do_debug has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK
 idtentry int3 do_int3 has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK
 idtentry stack_segment do_stack_segment has_error_code=1 paranoid=1
@@ -1742,7 +1725,3 @@ ENTRY(ignore_sysret)
        CFI_ENDPROC
 END(ignore_sysret)
 
-/*
- * End of kprobes section
- */
-       .popsection
index a67b47c31314ba7a2e30d7d9fc356ffc16f4f072..5f9cf20cdb68025ae7175860fba5abab136ef9f7 100644 (file)
@@ -32,7 +32,6 @@
 #include <linux/irqflags.h>
 #include <linux/notifier.h>
 #include <linux/kallsyms.h>
-#include <linux/kprobes.h>
 #include <linux/percpu.h>
 #include <linux/kdebug.h>
 #include <linux/kernel.h>
@@ -424,7 +423,7 @@ EXPORT_SYMBOL_GPL(hw_breakpoint_restore);
  * NOTIFY_STOP returned for all other cases
  *
  */
-static int __kprobes hw_breakpoint_handler(struct die_args *args)
+static int hw_breakpoint_handler(struct die_args *args)
 {
        int i, cpu, rc = NOTIFY_STOP;
        struct perf_event *bp;
@@ -511,7 +510,7 @@ static int __kprobes hw_breakpoint_handler(struct die_args *args)
 /*
  * Handle debug exception notifications.
  */
-int __kprobes hw_breakpoint_exceptions_notify(
+int hw_breakpoint_exceptions_notify(
                struct notifier_block *unused, unsigned long val, void *data)
 {
        if (val != DIE_DEBUG)
index 2e977b5d61ddee4e00d599f04236d70e8714fee0..8af817105e29cbc25ffaa8b2430a57c5de782dd6 100644 (file)
@@ -299,13 +299,31 @@ static void unmask_8259A(void)
 static void init_8259A(int auto_eoi)
 {
        unsigned long flags;
+       unsigned char probe_val = ~(1 << PIC_CASCADE_IR);
+       unsigned char new_val;
 
        i8259A_auto_eoi = auto_eoi;
 
        raw_spin_lock_irqsave(&i8259A_lock, flags);
 
-       outb(0xff, PIC_MASTER_IMR);     /* mask all of 8259A-1 */
+       /*
+        * Check to see if we have a PIC.
+        * Mask all except the cascade and read
+        * back the value we just wrote. If we don't
+        * have a PIC, we will read 0xff as opposed to the
+        * value we wrote.
+        */
        outb(0xff, PIC_SLAVE_IMR);      /* mask all of 8259A-2 */
+       outb(probe_val, PIC_MASTER_IMR);
+       new_val = inb(PIC_MASTER_IMR);
+       if (new_val != probe_val) {
+               printk(KERN_INFO "Using NULL legacy PIC\n");
+               legacy_pic = &null_legacy_pic;
+               raw_spin_unlock_irqrestore(&i8259A_lock, flags);
+               return;
+       }
+
+       outb(0xff, PIC_MASTER_IMR);     /* mask all of 8259A-1 */
 
        /*
         * outb_pic - this has to work on a wide range of PC hardware.
index 11ccfb0a63e78d68908440c1690b3f544ee9c6c7..922d285810246aaf4382104d01be52cf6cf31767 100644 (file)
@@ -365,6 +365,7 @@ void fixup_irqs(void)
        struct irq_desc *desc;
        struct irq_data *data;
        struct irq_chip *chip;
+       int ret;
 
        for_each_irq_desc(irq, desc) {
                int break_affinity = 0;
@@ -403,10 +404,14 @@ void fixup_irqs(void)
                if (!irqd_can_move_in_process_context(data) && chip->irq_mask)
                        chip->irq_mask(data);
 
-               if (chip->irq_set_affinity)
-                       chip->irq_set_affinity(data, affinity, true);
-               else if (!(warned++))
-                       set_affinity = 0;
+               if (chip->irq_set_affinity) {
+                       ret = chip->irq_set_affinity(data, affinity, true);
+                       if (ret == -ENOSPC)
+                               pr_crit("IRQ %d set affinity failed because there are no available vectors.  The device assigned to this IRQ is unstable.\n", irq);
+               } else {
+                       if (!(warned++))
+                               set_affinity = 0;
+               }
 
                /*
                 * We unmask if the irq was not marked masked by the
index 61b17dc2c277346ae2c869da691ec19ab000e1ad..7596df664901eed5a7aea5003ab83da49d34a615 100644 (file)
@@ -112,7 +112,8 @@ struct kretprobe_blackpoint kretprobe_blacklist[] = {
 
 const int kretprobe_blacklist_size = ARRAY_SIZE(kretprobe_blacklist);
 
-static void __kprobes __synthesize_relative_insn(void *from, void *to, u8 op)
+static nokprobe_inline void
+__synthesize_relative_insn(void *from, void *to, u8 op)
 {
        struct __arch_relative_insn {
                u8 op;
@@ -125,21 +126,23 @@ static void __kprobes __synthesize_relative_insn(void *from, void *to, u8 op)
 }
 
 /* Insert a jump instruction at address 'from', which jumps to address 'to'.*/
-void __kprobes synthesize_reljump(void *from, void *to)
+void synthesize_reljump(void *from, void *to)
 {
        __synthesize_relative_insn(from, to, RELATIVEJUMP_OPCODE);
 }
+NOKPROBE_SYMBOL(synthesize_reljump);
 
 /* Insert a call instruction at address 'from', which calls address 'to'.*/
-void __kprobes synthesize_relcall(void *from, void *to)
+void synthesize_relcall(void *from, void *to)
 {
        __synthesize_relative_insn(from, to, RELATIVECALL_OPCODE);
 }
+NOKPROBE_SYMBOL(synthesize_relcall);
 
 /*
  * Skip the prefixes of the instruction.
  */
-static kprobe_opcode_t *__kprobes skip_prefixes(kprobe_opcode_t *insn)
+static kprobe_opcode_t *skip_prefixes(kprobe_opcode_t *insn)
 {
        insn_attr_t attr;
 
@@ -154,12 +157,13 @@ static kprobe_opcode_t *__kprobes skip_prefixes(kprobe_opcode_t *insn)
 #endif
        return insn;
 }
+NOKPROBE_SYMBOL(skip_prefixes);
 
 /*
  * Returns non-zero if opcode is boostable.
  * RIP relative instructions are adjusted at copying time in 64 bits mode
  */
-int __kprobes can_boost(kprobe_opcode_t *opcodes)
+int can_boost(kprobe_opcode_t *opcodes)
 {
        kprobe_opcode_t opcode;
        kprobe_opcode_t *orig_opcodes = opcodes;
@@ -260,7 +264,7 @@ unsigned long recover_probed_instruction(kprobe_opcode_t *buf, unsigned long add
 }
 
 /* Check if paddr is at an instruction boundary */
-static int __kprobes can_probe(unsigned long paddr)
+static int can_probe(unsigned long paddr)
 {
        unsigned long addr, __addr, offset = 0;
        struct insn insn;
@@ -299,7 +303,7 @@ static int __kprobes can_probe(unsigned long paddr)
 /*
  * Returns non-zero if opcode modifies the interrupt flag.
  */
-static int __kprobes is_IF_modifier(kprobe_opcode_t *insn)
+static int is_IF_modifier(kprobe_opcode_t *insn)
 {
        /* Skip prefixes */
        insn = skip_prefixes(insn);
@@ -322,7 +326,7 @@ static int __kprobes is_IF_modifier(kprobe_opcode_t *insn)
  * If not, return null.
  * Only applicable to 64-bit x86.
  */
-int __kprobes __copy_instruction(u8 *dest, u8 *src)
+int __copy_instruction(u8 *dest, u8 *src)
 {
        struct insn insn;
        kprobe_opcode_t buf[MAX_INSN_SIZE];
@@ -365,7 +369,7 @@ int __kprobes __copy_instruction(u8 *dest, u8 *src)
        return insn.length;
 }
 
-static int __kprobes arch_copy_kprobe(struct kprobe *p)
+static int arch_copy_kprobe(struct kprobe *p)
 {
        int ret;
 
@@ -392,7 +396,7 @@ static int __kprobes arch_copy_kprobe(struct kprobe *p)
        return 0;
 }
 
-int __kprobes arch_prepare_kprobe(struct kprobe *p)
+int arch_prepare_kprobe(struct kprobe *p)
 {
        if (alternatives_text_reserved(p->addr, p->addr))
                return -EINVAL;
@@ -407,17 +411,17 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
        return arch_copy_kprobe(p);
 }
 
-void __kprobes arch_arm_kprobe(struct kprobe *p)
+void arch_arm_kprobe(struct kprobe *p)
 {
        text_poke(p->addr, ((unsigned char []){BREAKPOINT_INSTRUCTION}), 1);
 }
 
-void __kprobes arch_disarm_kprobe(struct kprobe *p)
+void arch_disarm_kprobe(struct kprobe *p)
 {
        text_poke(p->addr, &p->opcode, 1);
 }
 
-void __kprobes arch_remove_kprobe(struct kprobe *p)
+void arch_remove_kprobe(struct kprobe *p)
 {
        if (p->ainsn.insn) {
                free_insn_slot(p->ainsn.insn, (p->ainsn.boostable == 1));
@@ -425,7 +429,8 @@ void __kprobes arch_remove_kprobe(struct kprobe *p)
        }
 }
 
-static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
+static nokprobe_inline void
+save_previous_kprobe(struct kprobe_ctlblk *kcb)
 {
        kcb->prev_kprobe.kp = kprobe_running();
        kcb->prev_kprobe.status = kcb->kprobe_status;
@@ -433,7 +438,8 @@ static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
        kcb->prev_kprobe.saved_flags = kcb->kprobe_saved_flags;
 }
 
-static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)
+static nokprobe_inline void
+restore_previous_kprobe(struct kprobe_ctlblk *kcb)
 {
        __this_cpu_write(current_kprobe, kcb->prev_kprobe.kp);
        kcb->kprobe_status = kcb->prev_kprobe.status;
@@ -441,8 +447,9 @@ static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)
        kcb->kprobe_saved_flags = kcb->prev_kprobe.saved_flags;
 }
 
-static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs,
-                               struct kprobe_ctlblk *kcb)
+static nokprobe_inline void
+set_current_kprobe(struct kprobe *p, struct pt_regs *regs,
+                  struct kprobe_ctlblk *kcb)
 {
        __this_cpu_write(current_kprobe, p);
        kcb->kprobe_saved_flags = kcb->kprobe_old_flags
@@ -451,7 +458,7 @@ static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs,
                kcb->kprobe_saved_flags &= ~X86_EFLAGS_IF;
 }
 
-static void __kprobes clear_btf(void)
+static nokprobe_inline void clear_btf(void)
 {
        if (test_thread_flag(TIF_BLOCKSTEP)) {
                unsigned long debugctl = get_debugctlmsr();
@@ -461,7 +468,7 @@ static void __kprobes clear_btf(void)
        }
 }
 
-static void __kprobes restore_btf(void)
+static nokprobe_inline void restore_btf(void)
 {
        if (test_thread_flag(TIF_BLOCKSTEP)) {
                unsigned long debugctl = get_debugctlmsr();
@@ -471,8 +478,7 @@ static void __kprobes restore_btf(void)
        }
 }
 
-void __kprobes
-arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs)
+void arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs)
 {
        unsigned long *sara = stack_addr(regs);
 
@@ -481,9 +487,10 @@ arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs)
        /* Replace the return addr with trampoline addr */
        *sara = (unsigned long) &kretprobe_trampoline;
 }
+NOKPROBE_SYMBOL(arch_prepare_kretprobe);
 
-static void __kprobes
-setup_singlestep(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb, int reenter)
+static void setup_singlestep(struct kprobe *p, struct pt_regs *regs,
+                            struct kprobe_ctlblk *kcb, int reenter)
 {
        if (setup_detour_execution(p, regs, reenter))
                return;
@@ -519,22 +526,24 @@ setup_singlestep(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *k
        else
                regs->ip = (unsigned long)p->ainsn.insn;
 }
+NOKPROBE_SYMBOL(setup_singlestep);
 
 /*
  * We have reentered the kprobe_handler(), since another probe was hit while
  * within the handler. We save the original kprobes variables and just single
  * step on the instruction of the new probe without calling any user handlers.
  */
-static int __kprobes
-reenter_kprobe(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb)
+static int reenter_kprobe(struct kprobe *p, struct pt_regs *regs,
+                         struct kprobe_ctlblk *kcb)
 {
        switch (kcb->kprobe_status) {
        case KPROBE_HIT_SSDONE:
        case KPROBE_HIT_ACTIVE:
+       case KPROBE_HIT_SS:
                kprobes_inc_nmissed_count(p);
                setup_singlestep(p, regs, kcb, 1);
                break;
-       case KPROBE_HIT_SS:
+       case KPROBE_REENTER:
                /* A probe has been hit in the codepath leading up to, or just
                 * after, single-stepping of a probed instruction. This entire
                 * codepath should strictly reside in .kprobes.text section.
@@ -553,12 +562,13 @@ reenter_kprobe(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb
 
        return 1;
 }
+NOKPROBE_SYMBOL(reenter_kprobe);
 
 /*
  * Interrupts are disabled on entry as trap3 is an interrupt gate and they
  * remain disabled throughout this function.
  */
-static int __kprobes kprobe_handler(struct pt_regs *regs)
+int kprobe_int3_handler(struct pt_regs *regs)
 {
        kprobe_opcode_t *addr;
        struct kprobe *p;
@@ -621,12 +631,13 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
        preempt_enable_no_resched();
        return 0;
 }
+NOKPROBE_SYMBOL(kprobe_int3_handler);
 
 /*
  * When a retprobed function returns, this code saves registers and
  * calls trampoline_handler() runs, which calls the kretprobe's handler.
  */
-static void __used __kprobes kretprobe_trampoline_holder(void)
+static void __used kretprobe_trampoline_holder(void)
 {
        asm volatile (
                        ".global kretprobe_trampoline\n"
@@ -657,11 +668,13 @@ static void __used __kprobes kretprobe_trampoline_holder(void)
 #endif
                        "       ret\n");
 }
+NOKPROBE_SYMBOL(kretprobe_trampoline_holder);
+NOKPROBE_SYMBOL(kretprobe_trampoline);
 
 /*
  * Called from kretprobe_trampoline
  */
-__visible __used __kprobes void *trampoline_handler(struct pt_regs *regs)
+__visible __used void *trampoline_handler(struct pt_regs *regs)
 {
        struct kretprobe_instance *ri = NULL;
        struct hlist_head *head, empty_rp;
@@ -747,6 +760,7 @@ __visible __used __kprobes void *trampoline_handler(struct pt_regs *regs)
        }
        return (void *)orig_ret_address;
 }
+NOKPROBE_SYMBOL(trampoline_handler);
 
 /*
  * Called after single-stepping.  p->addr is the address of the
@@ -775,8 +789,8 @@ __visible __used __kprobes void *trampoline_handler(struct pt_regs *regs)
  * jump instruction after the copied instruction, that jumps to the next
  * instruction after the probepoint.
  */
-static void __kprobes
-resume_execution(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb)
+static void resume_execution(struct kprobe *p, struct pt_regs *regs,
+                            struct kprobe_ctlblk *kcb)
 {
        unsigned long *tos = stack_addr(regs);
        unsigned long copy_ip = (unsigned long)p->ainsn.insn;
@@ -851,12 +865,13 @@ resume_execution(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *k
 no_change:
        restore_btf();
 }
+NOKPROBE_SYMBOL(resume_execution);
 
 /*
  * Interrupts are disabled on entry as trap1 is an interrupt gate and they
  * remain disabled throughout this function.
  */
-static int __kprobes post_kprobe_handler(struct pt_regs *regs)
+int kprobe_debug_handler(struct pt_regs *regs)
 {
        struct kprobe *cur = kprobe_running();
        struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
@@ -891,8 +906,9 @@ static int __kprobes post_kprobe_handler(struct pt_regs *regs)
 
        return 1;
 }
+NOKPROBE_SYMBOL(kprobe_debug_handler);
 
-int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
+int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
 {
        struct kprobe *cur = kprobe_running();
        struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
@@ -949,12 +965,13 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
 
        return 0;
 }
+NOKPROBE_SYMBOL(kprobe_fault_handler);
 
 /*
  * Wrapper routine for handling exceptions.
  */
-int __kprobes
-kprobe_exceptions_notify(struct notifier_block *self, unsigned long val, void *data)
+int kprobe_exceptions_notify(struct notifier_block *self, unsigned long val,
+                            void *data)
 {
        struct die_args *args = data;
        int ret = NOTIFY_DONE;
@@ -962,22 +979,7 @@ kprobe_exceptions_notify(struct notifier_block *self, unsigned long val, void *d
        if (args->regs && user_mode_vm(args->regs))
                return ret;
 
-       switch (val) {
-       case DIE_INT3:
-               if (kprobe_handler(args->regs))
-                       ret = NOTIFY_STOP;
-               break;
-       case DIE_DEBUG:
-               if (post_kprobe_handler(args->regs)) {
-                       /*
-                        * Reset the BS bit in dr6 (pointed by args->err) to
-                        * denote completion of processing
-                        */
-                       (*(unsigned long *)ERR_PTR(args->err)) &= ~DR_STEP;
-                       ret = NOTIFY_STOP;
-               }
-               break;
-       case DIE_GPF:
+       if (val == DIE_GPF) {
                /*
                 * To be potentially processing a kprobe fault and to
                 * trust the result from kprobe_running(), we have
@@ -986,14 +988,12 @@ kprobe_exceptions_notify(struct notifier_block *self, unsigned long val, void *d
                if (!preemptible() && kprobe_running() &&
                    kprobe_fault_handler(args->regs, args->trapnr))
                        ret = NOTIFY_STOP;
-               break;
-       default:
-               break;
        }
        return ret;
 }
+NOKPROBE_SYMBOL(kprobe_exceptions_notify);
 
-int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
+int setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
 {
        struct jprobe *jp = container_of(p, struct jprobe, kp);
        unsigned long addr;
@@ -1017,8 +1017,9 @@ int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
        regs->ip = (unsigned long)(jp->entry);
        return 1;
 }
+NOKPROBE_SYMBOL(setjmp_pre_handler);
 
-void __kprobes jprobe_return(void)
+void jprobe_return(void)
 {
        struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
 
@@ -1034,8 +1035,10 @@ void __kprobes jprobe_return(void)
                        "       nop                     \n"::"b"
                        (kcb->jprobe_saved_sp):"memory");
 }
+NOKPROBE_SYMBOL(jprobe_return);
+NOKPROBE_SYMBOL(jprobe_return_end);
 
-int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
+int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
 {
        struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
        u8 *addr = (u8 *) (regs->ip - 1);
@@ -1063,13 +1066,22 @@ int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
        }
        return 0;
 }
+NOKPROBE_SYMBOL(longjmp_break_handler);
+
+bool arch_within_kprobe_blacklist(unsigned long addr)
+{
+       return  (addr >= (unsigned long)__kprobes_text_start &&
+                addr < (unsigned long)__kprobes_text_end) ||
+               (addr >= (unsigned long)__entry_text_start &&
+                addr < (unsigned long)__entry_text_end);
+}
 
 int __init arch_init_kprobes(void)
 {
        return 0;
 }
 
-int __kprobes arch_trampoline_kprobe(struct kprobe *p)
+int arch_trampoline_kprobe(struct kprobe *p)
 {
        return 0;
 }
index 23ef5c556f06145a2da51b51995a08afeae95dae..717b02a22e67638e8511c5d2ae299afa201739d8 100644 (file)
@@ -25,8 +25,9 @@
 
 #include "common.h"
 
-static int __skip_singlestep(struct kprobe *p, struct pt_regs *regs,
-                            struct kprobe_ctlblk *kcb)
+static nokprobe_inline
+int __skip_singlestep(struct kprobe *p, struct pt_regs *regs,
+                     struct kprobe_ctlblk *kcb)
 {
        /*
         * Emulate singlestep (and also recover regs->ip)
@@ -41,18 +42,19 @@ static int __skip_singlestep(struct kprobe *p, struct pt_regs *regs,
        return 1;
 }
 
-int __kprobes skip_singlestep(struct kprobe *p, struct pt_regs *regs,
-                             struct kprobe_ctlblk *kcb)
+int skip_singlestep(struct kprobe *p, struct pt_regs *regs,
+                   struct kprobe_ctlblk *kcb)
 {
        if (kprobe_ftrace(p))
                return __skip_singlestep(p, regs, kcb);
        else
                return 0;
 }
+NOKPROBE_SYMBOL(skip_singlestep);
 
 /* Ftrace callback handler for kprobes */
-void __kprobes kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
-                                    struct ftrace_ops *ops, struct pt_regs *regs)
+void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
+                          struct ftrace_ops *ops, struct pt_regs *regs)
 {
        struct kprobe *p;
        struct kprobe_ctlblk *kcb;
@@ -84,8 +86,9 @@ void __kprobes kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
 end:
        local_irq_restore(flags);
 }
+NOKPROBE_SYMBOL(kprobe_ftrace_handler);
 
-int __kprobes arch_prepare_kprobe_ftrace(struct kprobe *p)
+int arch_prepare_kprobe_ftrace(struct kprobe *p)
 {
        p->ainsn.insn = NULL;
        p->ainsn.boostable = -1;
index 898160b42e4392daddd224c55ab62fb467752dd7..f304773285ae360810e4290b67aa4c6f0e832ef0 100644 (file)
@@ -77,7 +77,7 @@ unsigned long __recover_optprobed_insn(kprobe_opcode_t *buf, unsigned long addr)
 }
 
 /* Insert a move instruction which sets a pointer to eax/rdi (1st arg). */
-static void __kprobes synthesize_set_arg1(kprobe_opcode_t *addr, unsigned long val)
+static void synthesize_set_arg1(kprobe_opcode_t *addr, unsigned long val)
 {
 #ifdef CONFIG_X86_64
        *addr++ = 0x48;
@@ -138,7 +138,8 @@ asm (
 #define INT3_SIZE sizeof(kprobe_opcode_t)
 
 /* Optimized kprobe call back function: called from optinsn */
-static void __kprobes optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs)
+static void
+optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs)
 {
        struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
        unsigned long flags;
@@ -168,8 +169,9 @@ static void __kprobes optimized_callback(struct optimized_kprobe *op, struct pt_
        }
        local_irq_restore(flags);
 }
+NOKPROBE_SYMBOL(optimized_callback);
 
-static int __kprobes copy_optimized_instructions(u8 *dest, u8 *src)
+static int copy_optimized_instructions(u8 *dest, u8 *src)
 {
        int len = 0, ret;
 
@@ -189,7 +191,7 @@ static int __kprobes copy_optimized_instructions(u8 *dest, u8 *src)
 }
 
 /* Check whether insn is indirect jump */
-static int __kprobes insn_is_indirect_jump(struct insn *insn)
+static int insn_is_indirect_jump(struct insn *insn)
 {
        return ((insn->opcode.bytes[0] == 0xff &&
                (X86_MODRM_REG(insn->modrm.value) & 6) == 4) || /* Jump */
@@ -224,7 +226,7 @@ static int insn_jump_into_range(struct insn *insn, unsigned long start, int len)
 }
 
 /* Decode whole function to ensure any instructions don't jump into target */
-static int __kprobes can_optimize(unsigned long paddr)
+static int can_optimize(unsigned long paddr)
 {
        unsigned long addr, size = 0, offset = 0;
        struct insn insn;
@@ -275,7 +277,7 @@ static int __kprobes can_optimize(unsigned long paddr)
 }
 
 /* Check optimized_kprobe can actually be optimized. */
-int __kprobes arch_check_optimized_kprobe(struct optimized_kprobe *op)
+int arch_check_optimized_kprobe(struct optimized_kprobe *op)
 {
        int i;
        struct kprobe *p;
@@ -290,15 +292,15 @@ int __kprobes arch_check_optimized_kprobe(struct optimized_kprobe *op)
 }
 
 /* Check the addr is within the optimized instructions. */
-int __kprobes
-arch_within_optimized_kprobe(struct optimized_kprobe *op, unsigned long addr)
+int arch_within_optimized_kprobe(struct optimized_kprobe *op,
+                                unsigned long addr)
 {
        return ((unsigned long)op->kp.addr <= addr &&
                (unsigned long)op->kp.addr + op->optinsn.size > addr);
 }
 
 /* Free optimized instruction slot */
-static __kprobes
+static
 void __arch_remove_optimized_kprobe(struct optimized_kprobe *op, int dirty)
 {
        if (op->optinsn.insn) {
@@ -308,7 +310,7 @@ void __arch_remove_optimized_kprobe(struct optimized_kprobe *op, int dirty)
        }
 }
 
-void __kprobes arch_remove_optimized_kprobe(struct optimized_kprobe *op)
+void arch_remove_optimized_kprobe(struct optimized_kprobe *op)
 {
        __arch_remove_optimized_kprobe(op, 1);
 }
@@ -318,7 +320,7 @@ void __kprobes arch_remove_optimized_kprobe(struct optimized_kprobe *op)
  * Target instructions MUST be relocatable (checked inside)
  * This is called when new aggr(opt)probe is allocated or reused.
  */
-int __kprobes arch_prepare_optimized_kprobe(struct optimized_kprobe *op)
+int arch_prepare_optimized_kprobe(struct optimized_kprobe *op)
 {
        u8 *buf;
        int ret;
@@ -372,7 +374,7 @@ int __kprobes arch_prepare_optimized_kprobe(struct optimized_kprobe *op)
  * Replace breakpoints (int3) with relative jumps.
  * Caller must call with locking kprobe_mutex and text_mutex.
  */
-void __kprobes arch_optimize_kprobes(struct list_head *oplist)
+void arch_optimize_kprobes(struct list_head *oplist)
 {
        struct optimized_kprobe *op, *tmp;
        u8 insn_buf[RELATIVEJUMP_SIZE];
@@ -398,7 +400,7 @@ void __kprobes arch_optimize_kprobes(struct list_head *oplist)
 }
 
 /* Replace a relative jump with a breakpoint (int3).  */
-void __kprobes arch_unoptimize_kprobe(struct optimized_kprobe *op)
+void arch_unoptimize_kprobe(struct optimized_kprobe *op)
 {
        u8 insn_buf[RELATIVEJUMP_SIZE];
 
@@ -424,8 +426,7 @@ extern void arch_unoptimize_kprobes(struct list_head *oplist,
        }
 }
 
-int  __kprobes
-setup_detour_execution(struct kprobe *p, struct pt_regs *regs, int reenter)
+int setup_detour_execution(struct kprobe *p, struct pt_regs *regs, int reenter)
 {
        struct optimized_kprobe *op;
 
@@ -441,3 +442,4 @@ setup_detour_execution(struct kprobe *p, struct pt_regs *regs, int reenter)
        }
        return 0;
 }
+NOKPROBE_SYMBOL(setup_detour_execution);
index 7e97371387fdd80eaeb6c1fb587d0b48c1593511..3dd8e2c4d74a9ed4a124baf36a83de78be1be02d 100644 (file)
@@ -251,8 +251,9 @@ u32 kvm_read_and_reset_pf_reason(void)
        return reason;
 }
 EXPORT_SYMBOL_GPL(kvm_read_and_reset_pf_reason);
+NOKPROBE_SYMBOL(kvm_read_and_reset_pf_reason);
 
-dotraplinkage void __kprobes
+dotraplinkage void
 do_async_page_fault(struct pt_regs *regs, unsigned long error_code)
 {
        enum ctx_state prev_state;
@@ -276,6 +277,7 @@ do_async_page_fault(struct pt_regs *regs, unsigned long error_code)
                break;
        }
 }
+NOKPROBE_SYMBOL(do_async_page_fault);
 
 static void __init paravirt_ops_setup(void)
 {
index b4872b999a713d7fc08f7578b672d804a30dd13e..c3e985d1751ced9dbab5ac0aa7c38f9623b449f4 100644 (file)
@@ -110,7 +110,7 @@ static void nmi_max_handler(struct irq_work *w)
                a->handler, whole_msecs, decimal_msecs);
 }
 
-static int __kprobes nmi_handle(unsigned int type, struct pt_regs *regs, bool b2b)
+static int nmi_handle(unsigned int type, struct pt_regs *regs, bool b2b)
 {
        struct nmi_desc *desc = nmi_to_desc(type);
        struct nmiaction *a;
@@ -146,6 +146,7 @@ static int __kprobes nmi_handle(unsigned int type, struct pt_regs *regs, bool b2
        /* return total number of NMI events handled */
        return handled;
 }
+NOKPROBE_SYMBOL(nmi_handle);
 
 int __register_nmi_handler(unsigned int type, struct nmiaction *action)
 {
@@ -208,7 +209,7 @@ void unregister_nmi_handler(unsigned int type, const char *name)
 }
 EXPORT_SYMBOL_GPL(unregister_nmi_handler);
 
-static __kprobes void
+static void
 pci_serr_error(unsigned char reason, struct pt_regs *regs)
 {
        /* check to see if anyone registered against these types of errors */
@@ -238,8 +239,9 @@ pci_serr_error(unsigned char reason, struct pt_regs *regs)
        reason = (reason & NMI_REASON_CLEAR_MASK) | NMI_REASON_CLEAR_SERR;
        outb(reason, NMI_REASON_PORT);
 }
+NOKPROBE_SYMBOL(pci_serr_error);
 
-static __kprobes void
+static void
 io_check_error(unsigned char reason, struct pt_regs *regs)
 {
        unsigned long i;
@@ -269,8 +271,9 @@ io_check_error(unsigned char reason, struct pt_regs *regs)
        reason &= ~NMI_REASON_CLEAR_IOCHK;
        outb(reason, NMI_REASON_PORT);
 }
+NOKPROBE_SYMBOL(io_check_error);
 
-static __kprobes void
+static void
 unknown_nmi_error(unsigned char reason, struct pt_regs *regs)
 {
        int handled;
@@ -298,11 +301,12 @@ unknown_nmi_error(unsigned char reason, struct pt_regs *regs)
 
        pr_emerg("Dazed and confused, but trying to continue\n");
 }
+NOKPROBE_SYMBOL(unknown_nmi_error);
 
 static DEFINE_PER_CPU(bool, swallow_nmi);
 static DEFINE_PER_CPU(unsigned long, last_nmi_rip);
 
-static __kprobes void default_do_nmi(struct pt_regs *regs)
+static void default_do_nmi(struct pt_regs *regs)
 {
        unsigned char reason = 0;
        int handled;
@@ -401,6 +405,7 @@ static __kprobes void default_do_nmi(struct pt_regs *regs)
        else
                unknown_nmi_error(reason, regs);
 }
+NOKPROBE_SYMBOL(default_do_nmi);
 
 /*
  * NMIs can hit breakpoints which will cause it to lose its
@@ -520,7 +525,7 @@ static inline void nmi_nesting_postprocess(void)
 }
 #endif
 
-dotraplinkage notrace __kprobes void
+dotraplinkage notrace void
 do_nmi(struct pt_regs *regs, long error_code)
 {
        nmi_nesting_preprocess(regs);
@@ -537,6 +542,7 @@ do_nmi(struct pt_regs *regs, long error_code)
        /* On i386, may loop back to preprocess */
        nmi_nesting_postprocess();
 }
+NOKPROBE_SYMBOL(do_nmi);
 
 void stop_nmi(void)
 {
index 1b10af835c31d9e45d237f31d7061a8e58946b57..548d25f00c90ad010379b0b36884cabee82b6e02 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/efi.h>
 #include <linux/bcd.h>
 #include <linux/highmem.h>
+#include <linux/kprobes.h>
 
 #include <asm/bug.h>
 #include <asm/paravirt.h>
@@ -389,6 +390,11 @@ __visible struct pv_cpu_ops pv_cpu_ops = {
        .end_context_switch = paravirt_nop,
 };
 
+/* At this point, native_get/set_debugreg has real function entries */
+NOKPROBE_SYMBOL(native_get_debugreg);
+NOKPROBE_SYMBOL(native_set_debugreg);
+NOKPROBE_SYMBOL(native_load_idt);
+
 struct pv_apic_ops pv_apic_ops = {
 #ifdef CONFIG_X86_LOCAL_APIC
        .startup_ipi_hook = paravirt_nop,
index 898d077617a99ab7c6ef055b06f409c8222a4249..ca5b02d405c3ba0c17e8c7e060222559a6a1d9b3 100644 (file)
@@ -413,12 +413,11 @@ void set_personality_ia32(bool x32)
        set_thread_flag(TIF_ADDR32);
 
        /* Mark the associated mm as containing 32-bit tasks. */
-       if (current->mm)
-               current->mm->context.ia32_compat = 1;
-
        if (x32) {
                clear_thread_flag(TIF_IA32);
                set_thread_flag(TIF_X32);
+               if (current->mm)
+                       current->mm->context.ia32_compat = TIF_X32;
                current->personality &= ~READ_IMPLIES_EXEC;
                /* is_compat_task() uses the presence of the x32
                   syscall bit flag to determine compat status */
@@ -426,6 +425,8 @@ void set_personality_ia32(bool x32)
        } else {
                set_thread_flag(TIF_IA32);
                clear_thread_flag(TIF_X32);
+               if (current->mm)
+                       current->mm->context.ia32_compat = TIF_IA32;
                current->personality |= force_personality32;
                /* Prepare the first "return" to user space */
                current_thread_info()->status |= TS_COMPAT;
index f73b5d435bdca59ff7c12c157a36997773fc2e07..c6eb418c562779f4383a9c17b737e748f5394c7e 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/ptrace.h>
+#include <linux/uprobes.h>
 #include <linux/string.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
@@ -106,7 +107,7 @@ static inline void preempt_conditional_cli(struct pt_regs *regs)
        preempt_count_dec();
 }
 
-static int __kprobes
+static nokprobe_inline int
 do_trap_no_signal(struct task_struct *tsk, int trapnr, char *str,
                  struct pt_regs *regs, long error_code)
 {
@@ -136,7 +137,38 @@ do_trap_no_signal(struct task_struct *tsk, int trapnr, char *str,
        return -1;
 }
 
-static void __kprobes
+static siginfo_t *fill_trap_info(struct pt_regs *regs, int signr, int trapnr,
+                               siginfo_t *info)
+{
+       unsigned long siaddr;
+       int sicode;
+
+       switch (trapnr) {
+       default:
+               return SEND_SIG_PRIV;
+
+       case X86_TRAP_DE:
+               sicode = FPE_INTDIV;
+               siaddr = uprobe_get_trap_addr(regs);
+               break;
+       case X86_TRAP_UD:
+               sicode = ILL_ILLOPN;
+               siaddr = uprobe_get_trap_addr(regs);
+               break;
+       case X86_TRAP_AC:
+               sicode = BUS_ADRALN;
+               siaddr = 0;
+               break;
+       }
+
+       info->si_signo = signr;
+       info->si_errno = 0;
+       info->si_code = sicode;
+       info->si_addr = (void __user *)siaddr;
+       return info;
+}
+
+static void
 do_trap(int trapnr, int signr, char *str, struct pt_regs *regs,
        long error_code, siginfo_t *info)
 {
@@ -168,60 +200,43 @@ do_trap(int trapnr, int signr, char *str, struct pt_regs *regs,
        }
 #endif
 
-       if (info)
-               force_sig_info(signr, info, tsk);
-       else
-               force_sig(signr, tsk);
+       force_sig_info(signr, info ?: SEND_SIG_PRIV, tsk);
 }
+NOKPROBE_SYMBOL(do_trap);
 
-#define DO_ERROR(trapnr, signr, str, name)                             \
-dotraplinkage void do_##name(struct pt_regs *regs, long error_code)    \
-{                                                                      \
-       enum ctx_state prev_state;                                      \
-                                                                       \
-       prev_state = exception_enter();                                 \
-       if (notify_die(DIE_TRAP, str, regs, error_code,                 \
-                       trapnr, signr) == NOTIFY_STOP) {                \
-               exception_exit(prev_state);                             \
-               return;                                                 \
-       }                                                               \
-       conditional_sti(regs);                                          \
-       do_trap(trapnr, signr, str, regs, error_code, NULL);            \
-       exception_exit(prev_state);                                     \
+static void do_error_trap(struct pt_regs *regs, long error_code, char *str,
+                         unsigned long trapnr, int signr)
+{
+       enum ctx_state prev_state = exception_enter();
+       siginfo_t info;
+
+       if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) !=
+                       NOTIFY_STOP) {
+               conditional_sti(regs);
+               do_trap(trapnr, signr, str, regs, error_code,
+                       fill_trap_info(regs, signr, trapnr, &info));
+       }
+
+       exception_exit(prev_state);
 }
 
-#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr)                \
+#define DO_ERROR(trapnr, signr, str, name)                             \
 dotraplinkage void do_##name(struct pt_regs *regs, long error_code)    \
 {                                                                      \
-       siginfo_t info;                                                 \
-       enum ctx_state prev_state;                                      \
-                                                                       \
-       info.si_signo = signr;                                          \
-       info.si_errno = 0;                                              \
-       info.si_code = sicode;                                          \
-       info.si_addr = (void __user *)siaddr;                           \
-       prev_state = exception_enter();                                 \
-       if (notify_die(DIE_TRAP, str, regs, error_code,                 \
-                       trapnr, signr) == NOTIFY_STOP) {                \
-               exception_exit(prev_state);                             \
-               return;                                                 \
-       }                                                               \
-       conditional_sti(regs);                                          \
-       do_trap(trapnr, signr, str, regs, error_code, &info);           \
-       exception_exit(prev_state);                                     \
+       do_error_trap(regs, error_code, str, trapnr, signr);            \
 }
 
-DO_ERROR_INFO(X86_TRAP_DE,     SIGFPE,  "divide error",                        divide_error,                FPE_INTDIV, regs->ip )
-DO_ERROR     (X86_TRAP_OF,     SIGSEGV, "overflow",                    overflow                                          )
-DO_ERROR     (X86_TRAP_BR,     SIGSEGV, "bounds",                      bounds                                            )
-DO_ERROR_INFO(X86_TRAP_UD,     SIGILL,  "invalid opcode",              invalid_op,                  ILL_ILLOPN, regs->ip )
-DO_ERROR     (X86_TRAP_OLD_MF, SIGFPE,  "coprocessor segment overrun", coprocessor_segment_overrun                       )
-DO_ERROR     (X86_TRAP_TS,     SIGSEGV, "invalid TSS",                 invalid_TSS                                       )
-DO_ERROR     (X86_TRAP_NP,     SIGBUS,  "segment not present",         segment_not_present                               )
+DO_ERROR(X86_TRAP_DE,     SIGFPE,  "divide error",             divide_error)
+DO_ERROR(X86_TRAP_OF,     SIGSEGV, "overflow",                 overflow)
+DO_ERROR(X86_TRAP_BR,     SIGSEGV, "bounds",                   bounds)
+DO_ERROR(X86_TRAP_UD,     SIGILL,  "invalid opcode",           invalid_op)
+DO_ERROR(X86_TRAP_OLD_MF, SIGFPE,  "coprocessor segment overrun",coprocessor_segment_overrun)
+DO_ERROR(X86_TRAP_TS,     SIGSEGV, "invalid TSS",              invalid_TSS)
+DO_ERROR(X86_TRAP_NP,     SIGBUS,  "segment not present",      segment_not_present)
 #ifdef CONFIG_X86_32
-DO_ERROR     (X86_TRAP_SS,     SIGBUS,  "stack segment",               stack_segment                                     )
+DO_ERROR(X86_TRAP_SS,     SIGBUS,  "stack segment",            stack_segment)
 #endif
-DO_ERROR_INFO(X86_TRAP_AC,     SIGBUS,  "alignment check",             alignment_check,             BUS_ADRALN, 0        )
+DO_ERROR(X86_TRAP_AC,     SIGBUS,  "alignment check",          alignment_check)
 
 #ifdef CONFIG_X86_64
 /* Runs on IST stack */
@@ -263,7 +278,7 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code)
 }
 #endif
 
-dotraplinkage void __kprobes
+dotraplinkage void
 do_general_protection(struct pt_regs *regs, long error_code)
 {
        struct task_struct *tsk;
@@ -305,13 +320,14 @@ do_general_protection(struct pt_regs *regs, long error_code)
                pr_cont("\n");
        }
 
-       force_sig(SIGSEGV, tsk);
+       force_sig_info(SIGSEGV, SEND_SIG_PRIV, tsk);
 exit:
        exception_exit(prev_state);
 }
+NOKPROBE_SYMBOL(do_general_protection);
 
 /* May run on IST stack. */
-dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_code)
+dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code)
 {
        enum ctx_state prev_state;
 
@@ -327,13 +343,18 @@ dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_co
        if (poke_int3_handler(regs))
                return;
 
-       prev_state = exception_enter();
 #ifdef CONFIG_KGDB_LOW_LEVEL_TRAP
        if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP,
                                SIGTRAP) == NOTIFY_STOP)
                goto exit;
 #endif /* CONFIG_KGDB_LOW_LEVEL_TRAP */
 
+#ifdef CONFIG_KPROBES
+       if (kprobe_int3_handler(regs))
+               return;
+#endif
+       prev_state = exception_enter();
+
        if (notify_die(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP,
                        SIGTRAP) == NOTIFY_STOP)
                goto exit;
@@ -350,6 +371,7 @@ dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_co
 exit:
        exception_exit(prev_state);
 }
+NOKPROBE_SYMBOL(do_int3);
 
 #ifdef CONFIG_X86_64
 /*
@@ -357,7 +379,7 @@ dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_co
  * for scheduling or signal handling. The actual stack switch is done in
  * entry.S
  */
-asmlinkage __visible __kprobes struct pt_regs *sync_regs(struct pt_regs *eregs)
+asmlinkage __visible struct pt_regs *sync_regs(struct pt_regs *eregs)
 {
        struct pt_regs *regs = eregs;
        /* Did already sync */
@@ -376,6 +398,7 @@ asmlinkage __visible __kprobes struct pt_regs *sync_regs(struct pt_regs *eregs)
                *regs = *eregs;
        return regs;
 }
+NOKPROBE_SYMBOL(sync_regs);
 #endif
 
 /*
@@ -402,7 +425,7 @@ asmlinkage __visible __kprobes struct pt_regs *sync_regs(struct pt_regs *eregs)
  *
  * May run on IST stack.
  */
-dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
+dotraplinkage void do_debug(struct pt_regs *regs, long error_code)
 {
        struct task_struct *tsk = current;
        enum ctx_state prev_state;
@@ -410,8 +433,6 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
        unsigned long dr6;
        int si_code;
 
-       prev_state = exception_enter();
-
        get_debugreg(dr6, 6);
 
        /* Filter out all the reserved bits which are preset to 1 */
@@ -440,6 +461,12 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
        /* Store the virtualized DR6 value */
        tsk->thread.debugreg6 = dr6;
 
+#ifdef CONFIG_KPROBES
+       if (kprobe_debug_handler(regs))
+               goto exit;
+#endif
+       prev_state = exception_enter();
+
        if (notify_die(DIE_DEBUG, "debug", regs, (long)&dr6, error_code,
                                                        SIGTRAP) == NOTIFY_STOP)
                goto exit;
@@ -482,13 +509,14 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
 exit:
        exception_exit(prev_state);
 }
+NOKPROBE_SYMBOL(do_debug);
 
 /*
  * Note that we play around with the 'TS' bit in an attempt to get
  * the correct behaviour even in the presence of the asynchronous
  * IRQ13 behaviour
  */
-void math_error(struct pt_regs *regs, int error_code, int trapnr)
+static void math_error(struct pt_regs *regs, int error_code, int trapnr)
 {
        struct task_struct *task = current;
        siginfo_t info;
@@ -518,7 +546,7 @@ void math_error(struct pt_regs *regs, int error_code, int trapnr)
        task->thread.error_code = error_code;
        info.si_signo = SIGFPE;
        info.si_errno = 0;
-       info.si_addr = (void __user *)regs->ip;
+       info.si_addr = (void __user *)uprobe_get_trap_addr(regs);
        if (trapnr == X86_TRAP_MF) {
                unsigned short cwd, swd;
                /*
@@ -645,7 +673,7 @@ void math_state_restore(void)
         */
        if (unlikely(restore_fpu_checking(tsk))) {
                drop_init_fpu(tsk);
-               force_sig(SIGSEGV, tsk);
+               force_sig_info(SIGSEGV, SEND_SIG_PRIV, tsk);
                return;
        }
 
@@ -653,7 +681,7 @@ void math_state_restore(void)
 }
 EXPORT_SYMBOL_GPL(math_state_restore);
 
-dotraplinkage void __kprobes
+dotraplinkage void
 do_device_not_available(struct pt_regs *regs, long error_code)
 {
        enum ctx_state prev_state;
@@ -679,6 +707,7 @@ do_device_not_available(struct pt_regs *regs, long error_code)
 #endif
        exception_exit(prev_state);
 }
+NOKPROBE_SYMBOL(do_device_not_available);
 
 #ifdef CONFIG_X86_32
 dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code)
index ace22916ade3ce60859ead56db7f2c8f08ff9760..5d1cbfe4ae58eea4276bbbd9fc342188cafeeb95 100644 (file)
 
 /* Post-execution fixups. */
 
-/* No fixup needed */
-#define UPROBE_FIX_NONE                0x0
-
 /* Adjust IP back to vicinity of actual insn */
-#define UPROBE_FIX_IP          0x1
+#define UPROBE_FIX_IP          0x01
 
 /* Adjust the return address of a call insn */
-#define UPROBE_FIX_CALL        0x2
+#define UPROBE_FIX_CALL                0x02
 
 /* Instruction will modify TF, don't change it */
-#define UPROBE_FIX_SETF        0x4
+#define UPROBE_FIX_SETF                0x04
 
-#define UPROBE_FIX_RIP_AX      0x8000
-#define UPROBE_FIX_RIP_CX      0x4000
+#define UPROBE_FIX_RIP_SI      0x08
+#define UPROBE_FIX_RIP_DI      0x10
+#define UPROBE_FIX_RIP_BX      0x20
+#define UPROBE_FIX_RIP_MASK    \
+       (UPROBE_FIX_RIP_SI | UPROBE_FIX_RIP_DI | UPROBE_FIX_RIP_BX)
 
 #define        UPROBE_TRAP_NR          UINT_MAX
 
@@ -67,6 +67,7 @@
  * to keep gcc from statically optimizing it out, as variable_test_bit makes
  * some versions of gcc to think only *(unsigned long*) is used.
  */
+#if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION)
 static volatile u32 good_insns_32[256 / 32] = {
        /*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f         */
        /*      ----------------------------------------------         */
@@ -89,33 +90,12 @@ static volatile u32 good_insns_32[256 / 32] = {
        /*      ----------------------------------------------         */
        /*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f         */
 };
+#else
+#define good_insns_32  NULL
+#endif
 
-/* Using this for both 64-bit and 32-bit apps */
-static volatile u32 good_2byte_insns[256 / 32] = {
-       /*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f         */
-       /*      ----------------------------------------------         */
-       W(0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1) | /* 00 */
-       W(0x10, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1) , /* 10 */
-       W(0x20, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1) | /* 20 */
-       W(0x30, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* 30 */
-       W(0x40, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 40 */
-       W(0x50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 50 */
-       W(0x60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 60 */
-       W(0x70, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1) , /* 70 */
-       W(0x80, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 80 */
-       W(0x90, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 90 */
-       W(0xa0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1) | /* a0 */
-       W(0xb0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1) , /* b0 */
-       W(0xc0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* c0 */
-       W(0xd0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* d0 */
-       W(0xe0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* e0 */
-       W(0xf0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)   /* f0 */
-       /*      ----------------------------------------------         */
-       /*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f         */
-};
-
-#ifdef CONFIG_X86_64
 /* Good-instruction tables for 64-bit apps */
+#if defined(CONFIG_X86_64)
 static volatile u32 good_insns_64[256 / 32] = {
        /*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f         */
        /*      ----------------------------------------------         */
@@ -138,7 +118,33 @@ static volatile u32 good_insns_64[256 / 32] = {
        /*      ----------------------------------------------         */
        /*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f         */
 };
+#else
+#define good_insns_64  NULL
 #endif
+
+/* Using this for both 64-bit and 32-bit apps */
+static volatile u32 good_2byte_insns[256 / 32] = {
+       /*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f         */
+       /*      ----------------------------------------------         */
+       W(0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1) | /* 00 */
+       W(0x10, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1) , /* 10 */
+       W(0x20, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1) | /* 20 */
+       W(0x30, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* 30 */
+       W(0x40, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 40 */
+       W(0x50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 50 */
+       W(0x60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 60 */
+       W(0x70, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1) , /* 70 */
+       W(0x80, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 80 */
+       W(0x90, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 90 */
+       W(0xa0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1) | /* a0 */
+       W(0xb0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1) , /* b0 */
+       W(0xc0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* c0 */
+       W(0xd0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* d0 */
+       W(0xe0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* e0 */
+       W(0xf0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)   /* f0 */
+       /*      ----------------------------------------------         */
+       /*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f         */
+};
 #undef W
 
 /*
@@ -209,16 +215,25 @@ static bool is_prefix_bad(struct insn *insn)
        return false;
 }
 
-static int validate_insn_32bits(struct arch_uprobe *auprobe, struct insn *insn)
+static int uprobe_init_insn(struct arch_uprobe *auprobe, struct insn *insn, bool x86_64)
 {
-       insn_init(insn, auprobe->insn, false);
+       u32 volatile *good_insns;
+
+       insn_init(insn, auprobe->insn, x86_64);
+       /* has the side-effect of processing the entire instruction */
+       insn_get_length(insn);
+       if (WARN_ON_ONCE(!insn_complete(insn)))
+               return -ENOEXEC;
 
-       /* Skip good instruction prefixes; reject "bad" ones. */
-       insn_get_opcode(insn);
        if (is_prefix_bad(insn))
                return -ENOTSUPP;
 
-       if (test_bit(OPCODE1(insn), (unsigned long *)good_insns_32))
+       if (x86_64)
+               good_insns = good_insns_64;
+       else
+               good_insns = good_insns_32;
+
+       if (test_bit(OPCODE1(insn), (unsigned long *)good_insns))
                return 0;
 
        if (insn->opcode.nbytes == 2) {
@@ -230,14 +245,18 @@ static int validate_insn_32bits(struct arch_uprobe *auprobe, struct insn *insn)
 }
 
 #ifdef CONFIG_X86_64
+static inline bool is_64bit_mm(struct mm_struct *mm)
+{
+       return  !config_enabled(CONFIG_IA32_EMULATION) ||
+               !(mm->context.ia32_compat == TIF_IA32);
+}
 /*
  * If arch_uprobe->insn doesn't use rip-relative addressing, return
  * immediately.  Otherwise, rewrite the instruction so that it accesses
  * its memory operand indirectly through a scratch register.  Set
- * arch_uprobe->fixups and arch_uprobe->rip_rela_target_address
- * accordingly.  (The contents of the scratch register will be saved
- * before we single-step the modified instruction, and restored
- * afterward.)
+ * defparam->fixups accordingly. (The contents of the scratch register
+ * will be saved before we single-step the modified instruction,
+ * and restored afterward).
  *
  * We do this because a rip-relative instruction can access only a
  * relatively small area (+/- 2 GB from the instruction), and the XOL
@@ -248,164 +267,192 @@ static int validate_insn_32bits(struct arch_uprobe *auprobe, struct insn *insn)
  *
  * Some useful facts about rip-relative instructions:
  *
- *  - There's always a modrm byte.
+ *  - There's always a modrm byte with bit layout "00 reg 101".
  *  - There's never a SIB byte.
  *  - The displacement is always 4 bytes.
+ *  - REX.B=1 bit in REX prefix, which normally extends r/m field,
+ *    has no effect on rip-relative mode. It doesn't make modrm byte
+ *    with r/m=101 refer to register 1101 = R13.
  */
-static void
-handle_riprel_insn(struct arch_uprobe *auprobe, struct insn *insn)
+static void riprel_analyze(struct arch_uprobe *auprobe, struct insn *insn)
 {
        u8 *cursor;
        u8 reg;
+       u8 reg2;
 
        if (!insn_rip_relative(insn))
                return;
 
        /*
-        * insn_rip_relative() would have decoded rex_prefix, modrm.
+        * insn_rip_relative() would have decoded rex_prefix, vex_prefix, modrm.
         * Clear REX.b bit (extension of MODRM.rm field):
-        * we want to encode rax/rcx, not r8/r9.
+        * we want to encode low numbered reg, not r8+.
         */
        if (insn->rex_prefix.nbytes) {
                cursor = auprobe->insn + insn_offset_rex_prefix(insn);
-               *cursor &= 0xfe;        /* Clearing REX.B bit */
+               /* REX byte has 0100wrxb layout, clearing REX.b bit */
+               *cursor &= 0xfe;
+       }
+       /*
+        * Similar treatment for VEX3 prefix.
+        * TODO: add XOP/EVEX treatment when insn decoder supports them
+        */
+       if (insn->vex_prefix.nbytes == 3) {
+               /*
+                * vex2:     c5    rvvvvLpp   (has no b bit)
+                * vex3/xop: c4/8f rxbmmmmm wvvvvLpp
+                * evex:     62    rxbR00mm wvvvv1pp zllBVaaa
+                *   (evex will need setting of both b and x since
+                *   in non-sib encoding evex.x is 4th bit of MODRM.rm)
+                * Setting VEX3.b (setting because it has inverted meaning):
+                */
+               cursor = auprobe->insn + insn_offset_vex_prefix(insn) + 1;
+               *cursor |= 0x20;
        }
 
+       /*
+        * Convert from rip-relative addressing to register-relative addressing
+        * via a scratch register.
+        *
+        * This is tricky since there are insns with modrm byte
+        * which also use registers not encoded in modrm byte:
+        * [i]div/[i]mul: implicitly use dx:ax
+        * shift ops: implicitly use cx
+        * cmpxchg: implicitly uses ax
+        * cmpxchg8/16b: implicitly uses dx:ax and bx:cx
+        *   Encoding: 0f c7/1 modrm
+        *   The code below thinks that reg=1 (cx), chooses si as scratch.
+        * mulx: implicitly uses dx: mulx r/m,r1,r2 does r1:r2 = dx * r/m.
+        *   First appeared in Haswell (BMI2 insn). It is vex-encoded.
+        *   Example where none of bx,cx,dx can be used as scratch reg:
+        *   c4 e2 63 f6 0d disp32   mulx disp32(%rip),%ebx,%ecx
+        * [v]pcmpistri: implicitly uses cx, xmm0
+        * [v]pcmpistrm: implicitly uses xmm0
+        * [v]pcmpestri: implicitly uses ax, dx, cx, xmm0
+        * [v]pcmpestrm: implicitly uses ax, dx, xmm0
+        *   Evil SSE4.2 string comparison ops from hell.
+        * maskmovq/[v]maskmovdqu: implicitly uses (ds:rdi) as destination.
+        *   Encoding: 0f f7 modrm, 66 0f f7 modrm, vex-encoded: c5 f9 f7 modrm.
+        *   Store op1, byte-masked by op2 msb's in each byte, to (ds:rdi).
+        *   AMD says it has no 3-operand form (vex.vvvv must be 1111)
+        *   and that it can have only register operands, not mem
+        *   (its modrm byte must have mode=11).
+        *   If these restrictions will ever be lifted,
+        *   we'll need code to prevent selection of di as scratch reg!
+        *
+        * Summary: I don't know any insns with modrm byte which
+        * use SI register implicitly. DI register is used only
+        * by one insn (maskmovq) and BX register is used
+        * only by one too (cmpxchg8b).
+        * BP is stack-segment based (may be a problem?).
+        * AX, DX, CX are off-limits (many implicit users).
+        * SP is unusable (it's stack pointer - think about "pop mem";
+        * also, rsp+disp32 needs sib encoding -> insn length change).
+        */
+
+       reg = MODRM_REG(insn);  /* Fetch modrm.reg */
+       reg2 = 0xff;            /* Fetch vex.vvvv */
+       if (insn->vex_prefix.nbytes == 2)
+               reg2 = insn->vex_prefix.bytes[1];
+       else if (insn->vex_prefix.nbytes == 3)
+               reg2 = insn->vex_prefix.bytes[2];
+       /*
+        * TODO: add XOP, EXEV vvvv reading.
+        *
+        * vex.vvvv field is in bits 6-3, bits are inverted.
+        * But in 32-bit mode, high-order bit may be ignored.
+        * Therefore, let's consider only 3 low-order bits.
+        */
+       reg2 = ((reg2 >> 3) & 0x7) ^ 0x7;
+       /*
+        * Register numbering is ax,cx,dx,bx, sp,bp,si,di, r8..r15.
+        *
+        * Choose scratch reg. Order is important: must not select bx
+        * if we can use si (cmpxchg8b case!)
+        */
+       if (reg != 6 && reg2 != 6) {
+               reg2 = 6;
+               auprobe->defparam.fixups |= UPROBE_FIX_RIP_SI;
+       } else if (reg != 7 && reg2 != 7) {
+               reg2 = 7;
+               auprobe->defparam.fixups |= UPROBE_FIX_RIP_DI;
+               /* TODO (paranoia): force maskmovq to not use di */
+       } else {
+               reg2 = 3;
+               auprobe->defparam.fixups |= UPROBE_FIX_RIP_BX;
+       }
        /*
         * Point cursor at the modrm byte.  The next 4 bytes are the
         * displacement.  Beyond the displacement, for some instructions,
         * is the immediate operand.
         */
        cursor = auprobe->insn + insn_offset_modrm(insn);
-       insn_get_length(insn);
-
        /*
-        * Convert from rip-relative addressing to indirect addressing
-        * via a scratch register.  Change the r/m field from 0x5 (%rip)
-        * to 0x0 (%rax) or 0x1 (%rcx), and squeeze out the offset field.
+        * Change modrm from "00 reg 101" to "10 reg reg2". Example:
+        * 89 05 disp32  mov %eax,disp32(%rip) becomes
+        * 89 86 disp32  mov %eax,disp32(%rsi)
         */
-       reg = MODRM_REG(insn);
-       if (reg == 0) {
-               /*
-                * The register operand (if any) is either the A register
-                * (%rax, %eax, etc.) or (if the 0x4 bit is set in the
-                * REX prefix) %r8.  In any case, we know the C register
-                * is NOT the register operand, so we use %rcx (register
-                * #1) for the scratch register.
-                */
-               auprobe->fixups = UPROBE_FIX_RIP_CX;
-               /* Change modrm from 00 000 101 to 00 000 001. */
-               *cursor = 0x1;
-       } else {
-               /* Use %rax (register #0) for the scratch register. */
-               auprobe->fixups = UPROBE_FIX_RIP_AX;
-               /* Change modrm from 00 xxx 101 to 00 xxx 000 */
-               *cursor = (reg << 3);
-       }
-
-       /* Target address = address of next instruction + (signed) offset */
-       auprobe->rip_rela_target_address = (long)insn->length + insn->displacement.value;
+       *cursor = 0x80 | (reg << 3) | reg2;
+}
 
-       /* Displacement field is gone; slide immediate field (if any) over. */
-       if (insn->immediate.nbytes) {
-               cursor++;
-               memmove(cursor, cursor + insn->displacement.nbytes, insn->immediate.nbytes);
-       }
+static inline unsigned long *
+scratch_reg(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+       if (auprobe->defparam.fixups & UPROBE_FIX_RIP_SI)
+               return &regs->si;
+       if (auprobe->defparam.fixups & UPROBE_FIX_RIP_DI)
+               return &regs->di;
+       return &regs->bx;
 }
 
 /*
  * If we're emulating a rip-relative instruction, save the contents
  * of the scratch register and store the target address in that register.
  */
-static void
-pre_xol_rip_insn(struct arch_uprobe *auprobe, struct pt_regs *regs,
-                               struct arch_uprobe_task *autask)
-{
-       if (auprobe->fixups & UPROBE_FIX_RIP_AX) {
-               autask->saved_scratch_register = regs->ax;
-               regs->ax = current->utask->vaddr;
-               regs->ax += auprobe->rip_rela_target_address;
-       } else if (auprobe->fixups & UPROBE_FIX_RIP_CX) {
-               autask->saved_scratch_register = regs->cx;
-               regs->cx = current->utask->vaddr;
-               regs->cx += auprobe->rip_rela_target_address;
-       }
-}
-
-static void
-handle_riprel_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs, long *correction)
+static void riprel_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
-       if (auprobe->fixups & (UPROBE_FIX_RIP_AX | UPROBE_FIX_RIP_CX)) {
-               struct arch_uprobe_task *autask;
-
-               autask = &current->utask->autask;
-               if (auprobe->fixups & UPROBE_FIX_RIP_AX)
-                       regs->ax = autask->saved_scratch_register;
-               else
-                       regs->cx = autask->saved_scratch_register;
+       if (auprobe->defparam.fixups & UPROBE_FIX_RIP_MASK) {
+               struct uprobe_task *utask = current->utask;
+               unsigned long *sr = scratch_reg(auprobe, regs);
 
-               /*
-                * The original instruction includes a displacement, and so
-                * is 4 bytes longer than what we've just single-stepped.
-                * Caller may need to apply other fixups to handle stuff
-                * like "jmpq *...(%rip)" and "callq *...(%rip)".
-                */
-               if (correction)
-                       *correction += 4;
+               utask->autask.saved_scratch_register = *sr;
+               *sr = utask->vaddr + auprobe->defparam.ilen;
        }
 }
 
-static int validate_insn_64bits(struct arch_uprobe *auprobe, struct insn *insn)
+static void riprel_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
-       insn_init(insn, auprobe->insn, true);
-
-       /* Skip good instruction prefixes; reject "bad" ones. */
-       insn_get_opcode(insn);
-       if (is_prefix_bad(insn))
-               return -ENOTSUPP;
+       if (auprobe->defparam.fixups & UPROBE_FIX_RIP_MASK) {
+               struct uprobe_task *utask = current->utask;
+               unsigned long *sr = scratch_reg(auprobe, regs);
 
-       if (test_bit(OPCODE1(insn), (unsigned long *)good_insns_64))
-               return 0;
-
-       if (insn->opcode.nbytes == 2) {
-               if (test_bit(OPCODE2(insn), (unsigned long *)good_2byte_insns))
-                       return 0;
+               *sr = utask->autask.saved_scratch_register;
        }
-       return -ENOTSUPP;
 }
-
-static int validate_insn_bits(struct arch_uprobe *auprobe, struct mm_struct *mm, struct insn *insn)
+#else /* 32-bit: */
+static inline bool is_64bit_mm(struct mm_struct *mm)
 {
-       if (mm->context.ia32_compat)
-               return validate_insn_32bits(auprobe, insn);
-       return validate_insn_64bits(auprobe, insn);
+       return false;
 }
-#else /* 32-bit: */
 /*
  * No RIP-relative addressing on 32-bit
  */
-static void handle_riprel_insn(struct arch_uprobe *auprobe, struct insn *insn)
+static void riprel_analyze(struct arch_uprobe *auprobe, struct insn *insn)
 {
 }
-static void pre_xol_rip_insn(struct arch_uprobe *auprobe, struct pt_regs *regs,
-                               struct arch_uprobe_task *autask)
+static void riprel_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
 }
-static void handle_riprel_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs,
-                                       long *correction)
+static void riprel_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
 }
-
-static int validate_insn_bits(struct arch_uprobe *auprobe, struct mm_struct *mm,  struct insn *insn)
-{
-       return validate_insn_32bits(auprobe, insn);
-}
 #endif /* CONFIG_X86_64 */
 
 struct uprobe_xol_ops {
        bool    (*emulate)(struct arch_uprobe *, struct pt_regs *);
        int     (*pre_xol)(struct arch_uprobe *, struct pt_regs *);
        int     (*post_xol)(struct arch_uprobe *, struct pt_regs *);
+       void    (*abort)(struct arch_uprobe *, struct pt_regs *);
 };
 
 static inline int sizeof_long(void)
@@ -415,50 +462,67 @@ static inline int sizeof_long(void)
 
 static int default_pre_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
-       pre_xol_rip_insn(auprobe, regs, &current->utask->autask);
+       riprel_pre_xol(auprobe, regs);
        return 0;
 }
 
-/*
- * Adjust the return address pushed by a call insn executed out of line.
- */
-static int adjust_ret_addr(unsigned long sp, long correction)
+static int push_ret_address(struct pt_regs *regs, unsigned long ip)
 {
-       int rasize = sizeof_long();
-       long ra;
-
-       if (copy_from_user(&ra, (void __user *)sp, rasize))
-               return -EFAULT;
+       unsigned long new_sp = regs->sp - sizeof_long();
 
-       ra += correction;
-       if (copy_to_user((void __user *)sp, &ra, rasize))
+       if (copy_to_user((void __user *)new_sp, &ip, sizeof_long()))
                return -EFAULT;
 
+       regs->sp = new_sp;
        return 0;
 }
 
+/*
+ * We have to fix things up as follows:
+ *
+ * Typically, the new ip is relative to the copied instruction.  We need
+ * to make it relative to the original instruction (FIX_IP).  Exceptions
+ * are return instructions and absolute or indirect jump or call instructions.
+ *
+ * If the single-stepped instruction was a call, the return address that
+ * is atop the stack is the address following the copied instruction.  We
+ * need to make it the address following the original instruction (FIX_CALL).
+ *
+ * If the original instruction was a rip-relative instruction such as
+ * "movl %edx,0xnnnn(%rip)", we have instead executed an equivalent
+ * instruction using a scratch register -- e.g., "movl %edx,0xnnnn(%rsi)".
+ * We need to restore the contents of the scratch register
+ * (FIX_RIP_reg).
+ */
 static int default_post_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
        struct uprobe_task *utask = current->utask;
-       long correction = (long)(utask->vaddr - utask->xol_vaddr);
 
-       handle_riprel_post_xol(auprobe, regs, &correction);
-       if (auprobe->fixups & UPROBE_FIX_IP)
+       riprel_post_xol(auprobe, regs);
+       if (auprobe->defparam.fixups & UPROBE_FIX_IP) {
+               long correction = utask->vaddr - utask->xol_vaddr;
                regs->ip += correction;
-
-       if (auprobe->fixups & UPROBE_FIX_CALL) {
-               if (adjust_ret_addr(regs->sp, correction)) {
-                       regs->sp += sizeof_long();
+       } else if (auprobe->defparam.fixups & UPROBE_FIX_CALL) {
+               regs->sp += sizeof_long(); /* Pop incorrect return address */
+               if (push_ret_address(regs, utask->vaddr + auprobe->defparam.ilen))
                        return -ERESTART;
-               }
        }
+       /* popf; tell the caller to not touch TF */
+       if (auprobe->defparam.fixups & UPROBE_FIX_SETF)
+               utask->autask.saved_tf = true;
 
        return 0;
 }
 
+static void default_abort_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+       riprel_post_xol(auprobe, regs);
+}
+
 static struct uprobe_xol_ops default_xol_ops = {
        .pre_xol  = default_pre_xol_op,
        .post_xol = default_post_xol_op,
+       .abort    = default_abort_op,
 };
 
 static bool branch_is_call(struct arch_uprobe *auprobe)
@@ -520,7 +584,6 @@ static bool branch_emulate_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
        unsigned long offs = (long)auprobe->branch.offs;
 
        if (branch_is_call(auprobe)) {
-               unsigned long new_sp = regs->sp - sizeof_long();
                /*
                 * If it fails we execute this (mangled, see the comment in
                 * branch_clear_offset) insn out-of-line. In the likely case
@@ -530,9 +593,8 @@ static bool branch_emulate_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
                 *
                 * But there is corner case, see the comment in ->post_xol().
                 */
-               if (copy_to_user((void __user *)new_sp, &new_ip, sizeof_long()))
+               if (push_ret_address(regs, new_ip))
                        return false;
-               regs->sp = new_sp;
        } else if (!check_jmp_cond(auprobe, regs)) {
                offs = 0;
        }
@@ -583,11 +645,7 @@ static struct uprobe_xol_ops branch_xol_ops = {
 static int branch_setup_xol_ops(struct arch_uprobe *auprobe, struct insn *insn)
 {
        u8 opc1 = OPCODE1(insn);
-
-       /* has the side-effect of processing the entire instruction */
-       insn_get_length(insn);
-       if (WARN_ON_ONCE(!insn_complete(insn)))
-               return -ENOEXEC;
+       int i;
 
        switch (opc1) {
        case 0xeb:      /* jmp 8 */
@@ -612,6 +670,16 @@ static int branch_setup_xol_ops(struct arch_uprobe *auprobe, struct insn *insn)
                        return -ENOSYS;
        }
 
+       /*
+        * 16-bit overrides such as CALLW (66 e8 nn nn) are not supported.
+        * Intel and AMD behavior differ in 64-bit mode: Intel ignores 66 prefix.
+        * No one uses these insns, reject any branch insns with such prefix.
+        */
+       for (i = 0; i < insn->prefixes.nbytes; i++) {
+               if (insn->prefixes.bytes[i] == 0x66)
+                       return -ENOTSUPP;
+       }
+
        auprobe->branch.opc1 = opc1;
        auprobe->branch.ilen = insn->length;
        auprobe->branch.offs = insn->immediate.value;
@@ -630,10 +698,10 @@ static int branch_setup_xol_ops(struct arch_uprobe *auprobe, struct insn *insn)
 int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long addr)
 {
        struct insn insn;
-       bool fix_ip = true, fix_call = false;
+       u8 fix_ip_or_call = UPROBE_FIX_IP;
        int ret;
 
-       ret = validate_insn_bits(auprobe, mm, &insn);
+       ret = uprobe_init_insn(auprobe, &insn, is_64bit_mm(mm));
        if (ret)
                return ret;
 
@@ -642,44 +710,39 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
                return ret;
 
        /*
-        * Figure out which fixups arch_uprobe_post_xol() will need to perform,
-        * and annotate arch_uprobe->fixups accordingly. To start with, ->fixups
-        * is either zero or it reflects rip-related fixups.
+        * Figure out which fixups default_post_xol_op() will need to perform,
+        * and annotate defparam->fixups accordingly.
         */
        switch (OPCODE1(&insn)) {
        case 0x9d:              /* popf */
-               auprobe->fixups |= UPROBE_FIX_SETF;
+               auprobe->defparam.fixups |= UPROBE_FIX_SETF;
                break;
        case 0xc3:              /* ret or lret -- ip is correct */
        case 0xcb:
        case 0xc2:
        case 0xca:
-               fix_ip = false;
+       case 0xea:              /* jmp absolute -- ip is correct */
+               fix_ip_or_call = 0;
                break;
        case 0x9a:              /* call absolute - Fix return addr, not ip */
-               fix_call = true;
-               fix_ip = false;
-               break;
-       case 0xea:              /* jmp absolute -- ip is correct */
-               fix_ip = false;
+               fix_ip_or_call = UPROBE_FIX_CALL;
                break;
        case 0xff:
-               insn_get_modrm(&insn);
                switch (MODRM_REG(&insn)) {
                case 2: case 3:                 /* call or lcall, indirect */
-                       fix_call = true;
+                       fix_ip_or_call = UPROBE_FIX_CALL;
+                       break;
                case 4: case 5:                 /* jmp or ljmp, indirect */
-                       fix_ip = false;
+                       fix_ip_or_call = 0;
+                       break;
                }
                /* fall through */
        default:
-               handle_riprel_insn(auprobe, &insn);
+               riprel_analyze(auprobe, &insn);
        }
 
-       if (fix_ip)
-               auprobe->fixups |= UPROBE_FIX_IP;
-       if (fix_call)
-               auprobe->fixups |= UPROBE_FIX_CALL;
+       auprobe->defparam.ilen = insn.length;
+       auprobe->defparam.fixups |= fix_ip_or_call;
 
        auprobe->ops = &default_xol_ops;
        return 0;
@@ -694,6 +757,12 @@ int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
        struct uprobe_task *utask = current->utask;
 
+       if (auprobe->ops->pre_xol) {
+               int err = auprobe->ops->pre_xol(auprobe, regs);
+               if (err)
+                       return err;
+       }
+
        regs->ip = utask->xol_vaddr;
        utask->autask.saved_trap_nr = current->thread.trap_nr;
        current->thread.trap_nr = UPROBE_TRAP_NR;
@@ -703,8 +772,6 @@ int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
        if (test_tsk_thread_flag(current, TIF_BLOCKSTEP))
                set_task_blockstep(current, false);
 
-       if (auprobe->ops->pre_xol)
-               return auprobe->ops->pre_xol(auprobe, regs);
        return 0;
 }
 
@@ -732,56 +799,42 @@ bool arch_uprobe_xol_was_trapped(struct task_struct *t)
  * single-step, we single-stepped a copy of the instruction.
  *
  * This function prepares to resume execution after the single-step.
- * We have to fix things up as follows:
- *
- * Typically, the new ip is relative to the copied instruction.  We need
- * to make it relative to the original instruction (FIX_IP).  Exceptions
- * are return instructions and absolute or indirect jump or call instructions.
- *
- * If the single-stepped instruction was a call, the return address that
- * is atop the stack is the address following the copied instruction.  We
- * need to make it the address following the original instruction (FIX_CALL).
- *
- * If the original instruction was a rip-relative instruction such as
- * "movl %edx,0xnnnn(%rip)", we have instead executed an equivalent
- * instruction using a scratch register -- e.g., "movl %edx,(%rax)".
- * We need to restore the contents of the scratch register and adjust
- * the ip, keeping in mind that the instruction we executed is 4 bytes
- * shorter than the original instruction (since we squeezed out the offset
- * field).  (FIX_RIP_AX or FIX_RIP_CX)
  */
 int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
        struct uprobe_task *utask = current->utask;
+       bool send_sigtrap = utask->autask.saved_tf;
+       int err = 0;
 
        WARN_ON_ONCE(current->thread.trap_nr != UPROBE_TRAP_NR);
+       current->thread.trap_nr = utask->autask.saved_trap_nr;
 
        if (auprobe->ops->post_xol) {
-               int err = auprobe->ops->post_xol(auprobe, regs);
+               err = auprobe->ops->post_xol(auprobe, regs);
                if (err) {
-                       arch_uprobe_abort_xol(auprobe, regs);
                        /*
-                        * Restart the probed insn. ->post_xol() must ensure
-                        * this is really possible if it returns -ERESTART.
+                        * Restore ->ip for restart or post mortem analysis.
+                        * ->post_xol() must not return -ERESTART unless this
+                        * is really possible.
                         */
+                       regs->ip = utask->vaddr;
                        if (err == -ERESTART)
-                               return 0;
-                       return err;
+                               err = 0;
+                       send_sigtrap = false;
                }
        }
-
-       current->thread.trap_nr = utask->autask.saved_trap_nr;
        /*
         * arch_uprobe_pre_xol() doesn't save the state of TIF_BLOCKSTEP
         * so we can get an extra SIGTRAP if we do not clear TF. We need
         * to examine the opcode to make it right.
         */
-       if (utask->autask.saved_tf)
+       if (send_sigtrap)
                send_sig(SIGTRAP, current, 0);
-       else if (!(auprobe->fixups & UPROBE_FIX_SETF))
+
+       if (!utask->autask.saved_tf)
                regs->flags &= ~X86_EFLAGS_TF;
 
-       return 0;
+       return err;
 }
 
 /* callback routine for handling exceptions. */
@@ -815,18 +868,18 @@ int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val,
 
 /*
  * This function gets called when XOL instruction either gets trapped or
- * the thread has a fatal signal, or if arch_uprobe_post_xol() failed.
- * Reset the instruction pointer to its probed address for the potential
- * restart or for post mortem analysis.
+ * the thread has a fatal signal. Reset the instruction pointer to its
+ * probed address for the potential restart or for post mortem analysis.
  */
 void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
        struct uprobe_task *utask = current->utask;
 
-       current->thread.trap_nr = utask->autask.saved_trap_nr;
-       handle_riprel_post_xol(auprobe, regs, NULL);
-       instruction_pointer_set(regs, utask->vaddr);
+       if (auprobe->ops->abort)
+               auprobe->ops->abort(auprobe, regs);
 
+       current->thread.trap_nr = utask->autask.saved_trap_nr;
+       regs->ip = utask->vaddr;
        /* clear TF if it was set by us in arch_uprobe_pre_xol() */
        if (!utask->autask.saved_tf)
                regs->flags &= ~X86_EFLAGS_TF;
index 2930ae05d77305a3c3f76b821f843203969ffb2b..28f85c916712232e951f30a0e9819b56357a87a9 100644 (file)
@@ -4,8 +4,8 @@
  *  (inspired by Andi Kleen's thunk_64.S)
  * Subject to the GNU public license, v.2. No warranty of any kind.
  */
-
        #include <linux/linkage.h>
+       #include <asm/asm.h>
 
 #ifdef CONFIG_TRACE_IRQFLAGS
        /* put return address in eax (arg1) */
@@ -22,6 +22,7 @@
        popl %ecx
        popl %eax
        ret
+       _ASM_NOKPROBE(\name)
        .endm
 
        thunk_ra trace_hardirqs_on_thunk,trace_hardirqs_on_caller
index a63efd6bb6a5a24e9553abc8aef18e198be0953f..92d9feaff42b04fa0dd42f1c1f686cf3a43e7636 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/linkage.h>
 #include <asm/dwarf2.h>
 #include <asm/calling.h>
+#include <asm/asm.h>
 
        /* rdi: arg1 ... normal C conventions. rax is saved/restored. */
        .macro THUNK name, func, put_ret_addr_in_rdi=0
@@ -25,6 +26,7 @@
        call \func
        jmp  restore
        CFI_ENDPROC
+       _ASM_NOKPROBE(\name)
        .endm
 
 #ifdef CONFIG_TRACE_IRQFLAGS
@@ -43,3 +45,4 @@ restore:
        RESTORE_ARGS
        ret
        CFI_ENDPROC
+       _ASM_NOKPROBE(restore)
index 858b47b5221be716eba34760cfd44d511b9183e9..36642793e315fc8bb4b8682e5bf8f4dbe8f4ab7c 100644 (file)
@@ -8,7 +8,7 @@
 #include <linux/kdebug.h>              /* oops_begin/end, ...          */
 #include <linux/module.h>              /* search_exception_table       */
 #include <linux/bootmem.h>             /* max_low_pfn                  */
-#include <linux/kprobes.h>             /* __kprobes, ...               */
+#include <linux/kprobes.h>             /* NOKPROBE_SYMBOL, ...         */
 #include <linux/mmiotrace.h>           /* kmmio_handler, ...           */
 #include <linux/perf_event.h>          /* perf_sw_event                */
 #include <linux/hugetlb.h>             /* hstate_index_to_shift        */
@@ -46,7 +46,7 @@ enum x86_pf_error_code {
  * Returns 0 if mmiotrace is disabled, or if the fault is not
  * handled by mmiotrace:
  */
-static inline int __kprobes
+static nokprobe_inline int
 kmmio_fault(struct pt_regs *regs, unsigned long addr)
 {
        if (unlikely(is_kmmio_active()))
@@ -55,7 +55,7 @@ kmmio_fault(struct pt_regs *regs, unsigned long addr)
        return 0;
 }
 
-static inline int __kprobes kprobes_fault(struct pt_regs *regs)
+static nokprobe_inline int kprobes_fault(struct pt_regs *regs)
 {
        int ret = 0;
 
@@ -262,7 +262,7 @@ void vmalloc_sync_all(void)
  *
  *   Handle a fault on the vmalloc or module mapping area
  */
-static noinline __kprobes int vmalloc_fault(unsigned long address)
+static noinline int vmalloc_fault(unsigned long address)
 {
        unsigned long pgd_paddr;
        pmd_t *pmd_k;
@@ -292,6 +292,7 @@ static noinline __kprobes int vmalloc_fault(unsigned long address)
 
        return 0;
 }
+NOKPROBE_SYMBOL(vmalloc_fault);
 
 /*
  * Did it hit the DOS screen memory VA from vm86 mode?
@@ -359,7 +360,7 @@ void vmalloc_sync_all(void)
  *
  * This assumes no large pages in there.
  */
-static noinline __kprobes int vmalloc_fault(unsigned long address)
+static noinline int vmalloc_fault(unsigned long address)
 {
        pgd_t *pgd, *pgd_ref;
        pud_t *pud, *pud_ref;
@@ -426,6 +427,7 @@ static noinline __kprobes int vmalloc_fault(unsigned long address)
 
        return 0;
 }
+NOKPROBE_SYMBOL(vmalloc_fault);
 
 #ifdef CONFIG_CPU_SUP_AMD
 static const char errata93_warning[] =
@@ -928,7 +930,7 @@ static int spurious_fault_check(unsigned long error_code, pte_t *pte)
  * There are no security implications to leaving a stale TLB when
  * increasing the permissions on a page.
  */
-static noinline __kprobes int
+static noinline int
 spurious_fault(unsigned long error_code, unsigned long address)
 {
        pgd_t *pgd;
@@ -976,6 +978,7 @@ spurious_fault(unsigned long error_code, unsigned long address)
 
        return ret;
 }
+NOKPROBE_SYMBOL(spurious_fault);
 
 int show_unhandled_signals = 1;
 
@@ -1031,7 +1034,7 @@ static inline bool smap_violation(int error_code, struct pt_regs *regs)
  * {,trace_}do_page_fault() have notrace on. Having this an actual function
  * guarantees there's a function trace entry.
  */
-static void __kprobes noinline
+static noinline void
 __do_page_fault(struct pt_regs *regs, unsigned long error_code,
                unsigned long address)
 {
@@ -1254,8 +1257,9 @@ __do_page_fault(struct pt_regs *regs, unsigned long error_code,
 
        up_read(&mm->mmap_sem);
 }
+NOKPROBE_SYMBOL(__do_page_fault);
 
-dotraplinkage void __kprobes notrace
+dotraplinkage void notrace
 do_page_fault(struct pt_regs *regs, unsigned long error_code)
 {
        unsigned long address = read_cr2(); /* Get the faulting address */
@@ -1273,10 +1277,12 @@ do_page_fault(struct pt_regs *regs, unsigned long error_code)
        __do_page_fault(regs, error_code, address);
        exception_exit(prev_state);
 }
+NOKPROBE_SYMBOL(do_page_fault);
 
 #ifdef CONFIG_TRACING
-static void trace_page_fault_entries(unsigned long address, struct pt_regs *regs,
-                                    unsigned long error_code)
+static nokprobe_inline void
+trace_page_fault_entries(unsigned long address, struct pt_regs *regs,
+                        unsigned long error_code)
 {
        if (user_mode(regs))
                trace_page_fault_user(address, regs, error_code);
@@ -1284,7 +1290,7 @@ static void trace_page_fault_entries(unsigned long address, struct pt_regs *regs
                trace_page_fault_kernel(address, regs, error_code);
 }
 
-dotraplinkage void __kprobes notrace
+dotraplinkage void notrace
 trace_do_page_fault(struct pt_regs *regs, unsigned long error_code)
 {
        /*
@@ -1301,4 +1307,5 @@ trace_do_page_fault(struct pt_regs *regs, unsigned long error_code)
        __do_page_fault(regs, error_code, address);
        exception_exit(prev_state);
 }
+NOKPROBE_SYMBOL(trace_do_page_fault);
 #endif /* CONFIG_TRACING */
index 01495755701bd3d068db95df291ef71096d9cf33..6440221ced0d4925d3fee4a0c11b424a6b696f2d 100644 (file)
 
 /*
  * Calling convention :
- * rdi : skb pointer
+ * rbx : skb pointer (callee saved)
  * esi : offset of byte(s) to fetch in skb (can be scratched)
- * r : copy of skb->data
+ * r10 : copy of skb->data
  * r9d : hlen = skb->len - skb->data_len
  */
-#define SKBDATA        %r8
+#define SKBDATA        %r10
 #define SKF_MAX_NEG_OFF    $(-0x200000) /* SKF_LL_OFF from filter.h */
+#define MAX_BPF_STACK (512 /* from filter.h */ + \
+       32 /* space for rbx,r13,r14,r15 */ + \
+       8 /* space for skb_copy_bits */)
 
 sk_load_word:
        .globl  sk_load_word
@@ -68,53 +71,31 @@ sk_load_byte_positive_offset:
        movzbl  (SKBDATA,%rsi),%eax
        ret
 
-/**
- * sk_load_byte_msh - BPF_S_LDX_B_MSH helper
- *
- * Implements BPF_S_LDX_B_MSH : ldxb  4*([offset]&0xf)
- * Must preserve A accumulator (%eax)
- * Inputs : %esi is the offset value
- */
-sk_load_byte_msh:
-       .globl  sk_load_byte_msh
-       test    %esi,%esi
-       js      bpf_slow_path_byte_msh_neg
-
-sk_load_byte_msh_positive_offset:
-       .globl  sk_load_byte_msh_positive_offset
-       cmp     %esi,%r9d      /* if (offset >= hlen) goto bpf_slow_path_byte_msh */
-       jle     bpf_slow_path_byte_msh
-       movzbl  (SKBDATA,%rsi),%ebx
-       and     $15,%bl
-       shl     $2,%bl
-       ret
-
 /* rsi contains offset and can be scratched */
 #define bpf_slow_path_common(LEN)              \
-       push    %rdi;    /* save skb */         \
+       mov     %rbx, %rdi; /* arg1 == skb */   \
        push    %r9;                            \
        push    SKBDATA;                        \
 /* rsi already has offset */                   \
        mov     $LEN,%ecx;      /* len */       \
-       lea     -12(%rbp),%rdx;                 \
+       lea     - MAX_BPF_STACK + 32(%rbp),%rdx;                        \
        call    skb_copy_bits;                  \
        test    %eax,%eax;                      \
        pop     SKBDATA;                        \
-       pop     %r9;                            \
-       pop     %rdi
+       pop     %r9;
 
 
 bpf_slow_path_word:
        bpf_slow_path_common(4)
        js      bpf_error
-       mov     -12(%rbp),%eax
+       mov     - MAX_BPF_STACK + 32(%rbp),%eax
        bswap   %eax
        ret
 
 bpf_slow_path_half:
        bpf_slow_path_common(2)
        js      bpf_error
-       mov     -12(%rbp),%ax
+       mov     - MAX_BPF_STACK + 32(%rbp),%ax
        rol     $8,%ax
        movzwl  %ax,%eax
        ret
@@ -122,21 +103,11 @@ bpf_slow_path_half:
 bpf_slow_path_byte:
        bpf_slow_path_common(1)
        js      bpf_error
-       movzbl  -12(%rbp),%eax
-       ret
-
-bpf_slow_path_byte_msh:
-       xchg    %eax,%ebx /* dont lose A , X is about to be scratched */
-       bpf_slow_path_common(1)
-       js      bpf_error
-       movzbl  -12(%rbp),%eax
-       and     $15,%al
-       shl     $2,%al
-       xchg    %eax,%ebx
+       movzbl  - MAX_BPF_STACK + 32(%rbp),%eax
        ret
 
 #define sk_negative_common(SIZE)                               \
-       push    %rdi;   /* save skb */                          \
+       mov     %rbx, %rdi; /* arg1 == skb */                   \
        push    %r9;                                            \
        push    SKBDATA;                                        \
 /* rsi already has offset */                                   \
@@ -145,10 +116,8 @@ bpf_slow_path_byte_msh:
        test    %rax,%rax;                                      \
        pop     SKBDATA;                                        \
        pop     %r9;                                            \
-       pop     %rdi;                                           \
        jz      bpf_error
 
-
 bpf_slow_path_word_neg:
        cmp     SKF_MAX_NEG_OFF, %esi   /* test range */
        jl      bpf_error       /* offset lower -> error  */
@@ -179,22 +148,12 @@ sk_load_byte_negative_offset:
        movzbl  (%rax), %eax
        ret
 
-bpf_slow_path_byte_msh_neg:
-       cmp     SKF_MAX_NEG_OFF, %esi
-       jl      bpf_error
-sk_load_byte_msh_negative_offset:
-       .globl  sk_load_byte_msh_negative_offset
-       xchg    %eax,%ebx /* dont lose A , X is about to be scratched */
-       sk_negative_common(1)
-       movzbl  (%rax),%eax
-       and     $15,%al
-       shl     $2,%al
-       xchg    %eax,%ebx
-       ret
-
 bpf_error:
 # force a return 0 from jit handler
-       xor             %eax,%eax
-       mov             -8(%rbp),%rbx
+       xor     %eax,%eax
+       mov     - MAX_BPF_STACK(%rbp),%rbx
+       mov     - MAX_BPF_STACK + 8(%rbp),%r13
+       mov     - MAX_BPF_STACK + 16(%rbp),%r14
+       mov     - MAX_BPF_STACK + 24(%rbp),%r15
        leaveq
        ret
index 6d5663a599a7a362756b5bcb6d3ff3e30ee34bae..99bef86ed6dffe48490cf49a383c19ff8ad72287 100644 (file)
@@ -1,6 +1,7 @@
 /* bpf_jit_comp.c : BPF JIT compiler
  *
  * Copyright (C) 2011-2013 Eric Dumazet (eric.dumazet@gmail.com)
+ * Internal BPF Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
 #include <linux/if_vlan.h>
 #include <linux/random.h>
 
-/*
- * Conventions :
- *  EAX : BPF A accumulator
- *  EBX : BPF X accumulator
- *  RDI : pointer to skb   (first argument given to JIT function)
- *  RBP : frame pointer (even if CONFIG_FRAME_POINTER=n)
- *  ECX,EDX,ESI : scratch registers
- *  r9d : skb->len - skb->data_len (headlen)
- *  r8  : skb->data
- * -8(RBP) : saved RBX value
- * -16(RBP)..-80(RBP) : BPF_MEMWORDS values
- */
 int bpf_jit_enable __read_mostly;
 
 /*
  * assembly code in arch/x86/net/bpf_jit.S
  */
-extern u8 sk_load_word[], sk_load_half[], sk_load_byte[], sk_load_byte_msh[];
+extern u8 sk_load_word[], sk_load_half[], sk_load_byte[];
 extern u8 sk_load_word_positive_offset[], sk_load_half_positive_offset[];
-extern u8 sk_load_byte_positive_offset[], sk_load_byte_msh_positive_offset[];
+extern u8 sk_load_byte_positive_offset[];
 extern u8 sk_load_word_negative_offset[], sk_load_half_negative_offset[];
-extern u8 sk_load_byte_negative_offset[], sk_load_byte_msh_negative_offset[];
+extern u8 sk_load_byte_negative_offset[];
 
 static inline u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len)
 {
@@ -56,30 +45,44 @@ static inline u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len)
 #define EMIT2(b1, b2)          EMIT((b1) + ((b2) << 8), 2)
 #define EMIT3(b1, b2, b3)      EMIT((b1) + ((b2) << 8) + ((b3) << 16), 3)
 #define EMIT4(b1, b2, b3, b4)   EMIT((b1) + ((b2) << 8) + ((b3) << 16) + ((b4) << 24), 4)
-#define EMIT1_off32(b1, off)   do { EMIT1(b1); EMIT(off, 4);} while (0)
-
-#define CLEAR_A() EMIT2(0x31, 0xc0) /* xor %eax,%eax */
-#define CLEAR_X() EMIT2(0x31, 0xdb) /* xor %ebx,%ebx */
+#define EMIT1_off32(b1, off) \
+       do {EMIT1(b1); EMIT(off, 4); } while (0)
+#define EMIT2_off32(b1, b2, off) \
+       do {EMIT2(b1, b2); EMIT(off, 4); } while (0)
+#define EMIT3_off32(b1, b2, b3, off) \
+       do {EMIT3(b1, b2, b3); EMIT(off, 4); } while (0)
+#define EMIT4_off32(b1, b2, b3, b4, off) \
+       do {EMIT4(b1, b2, b3, b4); EMIT(off, 4); } while (0)
 
 static inline bool is_imm8(int value)
 {
        return value <= 127 && value >= -128;
 }
 
-static inline bool is_near(int offset)
+static inline bool is_simm32(s64 value)
 {
-       return offset <= 127 && offset >= -128;
+       return value == (s64) (s32) value;
 }
 
-#define EMIT_JMP(offset)                                               \
-do {                                                                   \
-       if (offset) {                                                   \
-               if (is_near(offset))                                    \
-                       EMIT2(0xeb, offset); /* jmp .+off8 */           \
-               else                                                    \
-                       EMIT1_off32(0xe9, offset); /* jmp .+off32 */    \
-       }                                                               \
-} while (0)
+/* mov dst, src */
+#define EMIT_mov(DST, SRC) \
+       do {if (DST != SRC) \
+               EMIT3(add_2mod(0x48, DST, SRC), 0x89, add_2reg(0xC0, DST, SRC)); \
+       } while (0)
+
+static int bpf_size_to_x86_bytes(int bpf_size)
+{
+       if (bpf_size == BPF_W)
+               return 4;
+       else if (bpf_size == BPF_H)
+               return 2;
+       else if (bpf_size == BPF_B)
+               return 1;
+       else if (bpf_size == BPF_DW)
+               return 4; /* imm32 */
+       else
+               return 0;
+}
 
 /* list of x86 cond jumps opcodes (. + s8)
  * Add 0x10 (and an extra 0x0f) to generate far jumps (. + s32)
@@ -90,27 +93,8 @@ do {                                                                 \
 #define X86_JNE 0x75
 #define X86_JBE 0x76
 #define X86_JA  0x77
-
-#define EMIT_COND_JMP(op, offset)                              \
-do {                                                           \
-       if (is_near(offset))                                    \
-               EMIT2(op, offset); /* jxx .+off8 */             \
-       else {                                                  \
-               EMIT2(0x0f, op + 0x10);                         \
-               EMIT(offset, 4); /* jxx .+off32 */              \
-       }                                                       \
-} while (0)
-
-#define COND_SEL(CODE, TOP, FOP)       \
-       case CODE:                      \
-               t_op = TOP;             \
-               f_op = FOP;             \
-               goto cond_branch
-
-
-#define SEEN_DATAREF 1 /* might call external helpers */
-#define SEEN_XREG    2 /* ebx is used */
-#define SEEN_MEM     4 /* use mem[] for temporary storage */
+#define X86_JGE 0x7D
+#define X86_JG  0x7F
 
 static inline void bpf_flush_icache(void *start, void *end)
 {
@@ -125,26 +109,6 @@ static inline void bpf_flush_icache(void *start, void *end)
 #define CHOOSE_LOAD_FUNC(K, func) \
        ((int)K < 0 ? ((int)K >= SKF_LL_OFF ? func##_negative_offset : func) : func##_positive_offset)
 
-/* Helper to find the offset of pkt_type in sk_buff
- * We want to make sure its still a 3bit field starting at a byte boundary.
- */
-#define PKT_TYPE_MAX 7
-static int pkt_type_offset(void)
-{
-       struct sk_buff skb_probe = {
-               .pkt_type = ~0,
-       };
-       char *ct = (char *)&skb_probe;
-       unsigned int off;
-
-       for (off = 0; off < sizeof(struct sk_buff); off++) {
-               if (ct[off] == PKT_TYPE_MAX)
-                       return off;
-       }
-       pr_err_once("Please fix pkt_type_offset(), as pkt_type couldn't be found\n");
-       return -1;
-}
-
 struct bpf_binary_header {
        unsigned int    pages;
        /* Note : for security reasons, bpf code will follow a randomly
@@ -178,583 +142,771 @@ static struct bpf_binary_header *bpf_alloc_binary(unsigned int proglen,
        return header;
 }
 
-void bpf_jit_compile(struct sk_filter *fp)
+/* pick a register outside of BPF range for JIT internal work */
+#define AUX_REG (MAX_BPF_REG + 1)
+
+/* the following table maps BPF registers to x64 registers.
+ * x64 register r12 is unused, since if used as base address register
+ * in load/store instructions, it always needs an extra byte of encoding
+ */
+static const int reg2hex[] = {
+       [BPF_REG_0] = 0,  /* rax */
+       [BPF_REG_1] = 7,  /* rdi */
+       [BPF_REG_2] = 6,  /* rsi */
+       [BPF_REG_3] = 2,  /* rdx */
+       [BPF_REG_4] = 1,  /* rcx */
+       [BPF_REG_5] = 0,  /* r8 */
+       [BPF_REG_6] = 3,  /* rbx callee saved */
+       [BPF_REG_7] = 5,  /* r13 callee saved */
+       [BPF_REG_8] = 6,  /* r14 callee saved */
+       [BPF_REG_9] = 7,  /* r15 callee saved */
+       [BPF_REG_FP] = 5, /* rbp readonly */
+       [AUX_REG] = 3,    /* r11 temp register */
+};
+
+/* is_ereg() == true if BPF register 'reg' maps to x64 r8..r15
+ * which need extra byte of encoding.
+ * rax,rcx,...,rbp have simpler encoding
+ */
+static inline bool is_ereg(u32 reg)
 {
-       u8 temp[64];
-       u8 *prog;
-       unsigned int proglen, oldproglen = 0;
-       int ilen, i;
-       int t_offset, f_offset;
-       u8 t_op, f_op, seen = 0, pass;
-       u8 *image = NULL;
-       struct bpf_binary_header *header = NULL;
-       u8 *func;
-       int pc_ret0 = -1; /* bpf index of first RET #0 instruction (if any) */
-       unsigned int cleanup_addr; /* epilogue code offset */
-       unsigned int *addrs;
-       const struct sock_filter *filter = fp->insns;
-       int flen = fp->len;
+       if (reg == BPF_REG_5 || reg == AUX_REG ||
+           (reg >= BPF_REG_7 && reg <= BPF_REG_9))
+               return true;
+       else
+               return false;
+}
 
-       if (!bpf_jit_enable)
-               return;
+/* add modifiers if 'reg' maps to x64 registers r8..r15 */
+static inline u8 add_1mod(u8 byte, u32 reg)
+{
+       if (is_ereg(reg))
+               byte |= 1;
+       return byte;
+}
 
-       addrs = kmalloc(flen * sizeof(*addrs), GFP_KERNEL);
-       if (addrs == NULL)
-               return;
+static inline u8 add_2mod(u8 byte, u32 r1, u32 r2)
+{
+       if (is_ereg(r1))
+               byte |= 1;
+       if (is_ereg(r2))
+               byte |= 4;
+       return byte;
+}
 
-       /* Before first pass, make a rough estimation of addrs[]
-        * each bpf instruction is translated to less than 64 bytes
+/* encode 'dst_reg' register into x64 opcode 'byte' */
+static inline u8 add_1reg(u8 byte, u32 dst_reg)
+{
+       return byte + reg2hex[dst_reg];
+}
+
+/* encode 'dst_reg' and 'src_reg' registers into x64 opcode 'byte' */
+static inline u8 add_2reg(u8 byte, u32 dst_reg, u32 src_reg)
+{
+       return byte + reg2hex[dst_reg] + (reg2hex[src_reg] << 3);
+}
+
+struct jit_context {
+       unsigned int cleanup_addr; /* epilogue code offset */
+       bool seen_ld_abs;
+};
+
+static int do_jit(struct sk_filter *bpf_prog, int *addrs, u8 *image,
+                 int oldproglen, struct jit_context *ctx)
+{
+       struct sock_filter_int *insn = bpf_prog->insnsi;
+       int insn_cnt = bpf_prog->len;
+       u8 temp[64];
+       int i;
+       int proglen = 0;
+       u8 *prog = temp;
+       int stacksize = MAX_BPF_STACK +
+               32 /* space for rbx, r13, r14, r15 */ +
+               8 /* space for skb_copy_bits() buffer */;
+
+       EMIT1(0x55); /* push rbp */
+       EMIT3(0x48, 0x89, 0xE5); /* mov rbp,rsp */
+
+       /* sub rsp, stacksize */
+       EMIT3_off32(0x48, 0x81, 0xEC, stacksize);
+
+       /* all classic BPF filters use R6(rbx) save it */
+
+       /* mov qword ptr [rbp-X],rbx */
+       EMIT3_off32(0x48, 0x89, 0x9D, -stacksize);
+
+       /* sk_convert_filter() maps classic BPF register X to R7 and uses R8
+        * as temporary, so all tcpdump filters need to spill/fill R7(r13) and
+        * R8(r14). R9(r15) spill could be made conditional, but there is only
+        * one 'bpf_error' return path out of helper functions inside bpf_jit.S
+        * The overhead of extra spill is negligible for any filter other
+        * than synthetic ones. Therefore not worth adding complexity.
         */
-       for (proglen = 0, i = 0; i < flen; i++) {
-               proglen += 64;
-               addrs[i] = proglen;
+
+       /* mov qword ptr [rbp-X],r13 */
+       EMIT3_off32(0x4C, 0x89, 0xAD, -stacksize + 8);
+       /* mov qword ptr [rbp-X],r14 */
+       EMIT3_off32(0x4C, 0x89, 0xB5, -stacksize + 16);
+       /* mov qword ptr [rbp-X],r15 */
+       EMIT3_off32(0x4C, 0x89, 0xBD, -stacksize + 24);
+
+       /* clear A and X registers */
+       EMIT2(0x31, 0xc0); /* xor eax, eax */
+       EMIT3(0x4D, 0x31, 0xED); /* xor r13, r13 */
+
+       if (ctx->seen_ld_abs) {
+               /* r9d : skb->len - skb->data_len (headlen)
+                * r10 : skb->data
+                */
+               if (is_imm8(offsetof(struct sk_buff, len)))
+                       /* mov %r9d, off8(%rdi) */
+                       EMIT4(0x44, 0x8b, 0x4f,
+                             offsetof(struct sk_buff, len));
+               else
+                       /* mov %r9d, off32(%rdi) */
+                       EMIT3_off32(0x44, 0x8b, 0x8f,
+                                   offsetof(struct sk_buff, len));
+
+               if (is_imm8(offsetof(struct sk_buff, data_len)))
+                       /* sub %r9d, off8(%rdi) */
+                       EMIT4(0x44, 0x2b, 0x4f,
+                             offsetof(struct sk_buff, data_len));
+               else
+                       EMIT3_off32(0x44, 0x2b, 0x8f,
+                                   offsetof(struct sk_buff, data_len));
+
+               if (is_imm8(offsetof(struct sk_buff, data)))
+                       /* mov %r10, off8(%rdi) */
+                       EMIT4(0x4c, 0x8b, 0x57,
+                             offsetof(struct sk_buff, data));
+               else
+                       /* mov %r10, off32(%rdi) */
+                       EMIT3_off32(0x4c, 0x8b, 0x97,
+                                   offsetof(struct sk_buff, data));
        }
-       cleanup_addr = proglen; /* epilogue address */
 
-       for (pass = 0; pass < 10; pass++) {
-               u8 seen_or_pass0 = (pass == 0) ? (SEEN_XREG | SEEN_DATAREF | SEEN_MEM) : seen;
-               /* no prologue/epilogue for trivial filters (RET something) */
-               proglen = 0;
-               prog = temp;
+       for (i = 0; i < insn_cnt; i++, insn++) {
+               const s32 imm32 = insn->imm;
+               u32 dst_reg = insn->dst_reg;
+               u32 src_reg = insn->src_reg;
+               u8 b1 = 0, b2 = 0, b3 = 0;
+               s64 jmp_offset;
+               u8 jmp_cond;
+               int ilen;
+               u8 *func;
+
+               switch (insn->code) {
+                       /* ALU */
+               case BPF_ALU | BPF_ADD | BPF_X:
+               case BPF_ALU | BPF_SUB | BPF_X:
+               case BPF_ALU | BPF_AND | BPF_X:
+               case BPF_ALU | BPF_OR | BPF_X:
+               case BPF_ALU | BPF_XOR | BPF_X:
+               case BPF_ALU64 | BPF_ADD | BPF_X:
+               case BPF_ALU64 | BPF_SUB | BPF_X:
+               case BPF_ALU64 | BPF_AND | BPF_X:
+               case BPF_ALU64 | BPF_OR | BPF_X:
+               case BPF_ALU64 | BPF_XOR | BPF_X:
+                       switch (BPF_OP(insn->code)) {
+                       case BPF_ADD: b2 = 0x01; break;
+                       case BPF_SUB: b2 = 0x29; break;
+                       case BPF_AND: b2 = 0x21; break;
+                       case BPF_OR: b2 = 0x09; break;
+                       case BPF_XOR: b2 = 0x31; break;
+                       }
+                       if (BPF_CLASS(insn->code) == BPF_ALU64)
+                               EMIT1(add_2mod(0x48, dst_reg, src_reg));
+                       else if (is_ereg(dst_reg) || is_ereg(src_reg))
+                               EMIT1(add_2mod(0x40, dst_reg, src_reg));
+                       EMIT2(b2, add_2reg(0xC0, dst_reg, src_reg));
+                       break;
 
-               if (seen_or_pass0) {
-                       EMIT4(0x55, 0x48, 0x89, 0xe5); /* push %rbp; mov %rsp,%rbp */
-                       EMIT4(0x48, 0x83, 0xec, 96);    /* subq  $96,%rsp       */
-                       /* note : must save %rbx in case bpf_error is hit */
-                       if (seen_or_pass0 & (SEEN_XREG | SEEN_DATAREF))
-                               EMIT4(0x48, 0x89, 0x5d, 0xf8); /* mov %rbx, -8(%rbp) */
-                       if (seen_or_pass0 & SEEN_XREG)
-                               CLEAR_X(); /* make sure we dont leek kernel memory */
-
-                       /*
-                        * If this filter needs to access skb data,
-                        * loads r9 and r8 with :
-                        *  r9 = skb->len - skb->data_len
-                        *  r8 = skb->data
+                       /* mov dst, src */
+               case BPF_ALU64 | BPF_MOV | BPF_X:
+                       EMIT_mov(dst_reg, src_reg);
+                       break;
+
+                       /* mov32 dst, src */
+               case BPF_ALU | BPF_MOV | BPF_X:
+                       if (is_ereg(dst_reg) || is_ereg(src_reg))
+                               EMIT1(add_2mod(0x40, dst_reg, src_reg));
+                       EMIT2(0x89, add_2reg(0xC0, dst_reg, src_reg));
+                       break;
+
+                       /* neg dst */
+               case BPF_ALU | BPF_NEG:
+               case BPF_ALU64 | BPF_NEG:
+                       if (BPF_CLASS(insn->code) == BPF_ALU64)
+                               EMIT1(add_1mod(0x48, dst_reg));
+                       else if (is_ereg(dst_reg))
+                               EMIT1(add_1mod(0x40, dst_reg));
+                       EMIT2(0xF7, add_1reg(0xD8, dst_reg));
+                       break;
+
+               case BPF_ALU | BPF_ADD | BPF_K:
+               case BPF_ALU | BPF_SUB | BPF_K:
+               case BPF_ALU | BPF_AND | BPF_K:
+               case BPF_ALU | BPF_OR | BPF_K:
+               case BPF_ALU | BPF_XOR | BPF_K:
+               case BPF_ALU64 | BPF_ADD | BPF_K:
+               case BPF_ALU64 | BPF_SUB | BPF_K:
+               case BPF_ALU64 | BPF_AND | BPF_K:
+               case BPF_ALU64 | BPF_OR | BPF_K:
+               case BPF_ALU64 | BPF_XOR | BPF_K:
+                       if (BPF_CLASS(insn->code) == BPF_ALU64)
+                               EMIT1(add_1mod(0x48, dst_reg));
+                       else if (is_ereg(dst_reg))
+                               EMIT1(add_1mod(0x40, dst_reg));
+
+                       switch (BPF_OP(insn->code)) {
+                       case BPF_ADD: b3 = 0xC0; break;
+                       case BPF_SUB: b3 = 0xE8; break;
+                       case BPF_AND: b3 = 0xE0; break;
+                       case BPF_OR: b3 = 0xC8; break;
+                       case BPF_XOR: b3 = 0xF0; break;
+                       }
+
+                       if (is_imm8(imm32))
+                               EMIT3(0x83, add_1reg(b3, dst_reg), imm32);
+                       else
+                               EMIT2_off32(0x81, add_1reg(b3, dst_reg), imm32);
+                       break;
+
+               case BPF_ALU64 | BPF_MOV | BPF_K:
+                       /* optimization: if imm32 is positive,
+                        * use 'mov eax, imm32' (which zero-extends imm32)
+                        * to save 2 bytes
                         */
-                       if (seen_or_pass0 & SEEN_DATAREF) {
-                               if (offsetof(struct sk_buff, len) <= 127)
-                                       /* mov    off8(%rdi),%r9d */
-                                       EMIT4(0x44, 0x8b, 0x4f, offsetof(struct sk_buff, len));
-                               else {
-                                       /* mov    off32(%rdi),%r9d */
-                                       EMIT3(0x44, 0x8b, 0x8f);
-                                       EMIT(offsetof(struct sk_buff, len), 4);
-                               }
-                               if (is_imm8(offsetof(struct sk_buff, data_len)))
-                                       /* sub    off8(%rdi),%r9d */
-                                       EMIT4(0x44, 0x2b, 0x4f, offsetof(struct sk_buff, data_len));
-                               else {
-                                       EMIT3(0x44, 0x2b, 0x8f);
-                                       EMIT(offsetof(struct sk_buff, data_len), 4);
-                               }
+                       if (imm32 < 0) {
+                               /* 'mov rax, imm32' sign extends imm32 */
+                               b1 = add_1mod(0x48, dst_reg);
+                               b2 = 0xC7;
+                               b3 = 0xC0;
+                               EMIT3_off32(b1, b2, add_1reg(b3, dst_reg), imm32);
+                               break;
+                       }
 
-                               if (is_imm8(offsetof(struct sk_buff, data)))
-                                       /* mov off8(%rdi),%r8 */
-                                       EMIT4(0x4c, 0x8b, 0x47, offsetof(struct sk_buff, data));
-                               else {
-                                       /* mov off32(%rdi),%r8 */
-                                       EMIT3(0x4c, 0x8b, 0x87);
-                                       EMIT(offsetof(struct sk_buff, data), 4);
-                               }
+               case BPF_ALU | BPF_MOV | BPF_K:
+                       /* mov %eax, imm32 */
+                       if (is_ereg(dst_reg))
+                               EMIT1(add_1mod(0x40, dst_reg));
+                       EMIT1_off32(add_1reg(0xB8, dst_reg), imm32);
+                       break;
+
+                       /* dst %= src, dst /= src, dst %= imm32, dst /= imm32 */
+               case BPF_ALU | BPF_MOD | BPF_X:
+               case BPF_ALU | BPF_DIV | BPF_X:
+               case BPF_ALU | BPF_MOD | BPF_K:
+               case BPF_ALU | BPF_DIV | BPF_K:
+               case BPF_ALU64 | BPF_MOD | BPF_X:
+               case BPF_ALU64 | BPF_DIV | BPF_X:
+               case BPF_ALU64 | BPF_MOD | BPF_K:
+               case BPF_ALU64 | BPF_DIV | BPF_K:
+                       EMIT1(0x50); /* push rax */
+                       EMIT1(0x52); /* push rdx */
+
+                       if (BPF_SRC(insn->code) == BPF_X)
+                               /* mov r11, src_reg */
+                               EMIT_mov(AUX_REG, src_reg);
+                       else
+                               /* mov r11, imm32 */
+                               EMIT3_off32(0x49, 0xC7, 0xC3, imm32);
+
+                       /* mov rax, dst_reg */
+                       EMIT_mov(BPF_REG_0, dst_reg);
+
+                       /* xor edx, edx
+                        * equivalent to 'xor rdx, rdx', but one byte less
+                        */
+                       EMIT2(0x31, 0xd2);
+
+                       if (BPF_SRC(insn->code) == BPF_X) {
+                               /* if (src_reg == 0) return 0 */
+
+                               /* cmp r11, 0 */
+                               EMIT4(0x49, 0x83, 0xFB, 0x00);
+
+                               /* jne .+9 (skip over pop, pop, xor and jmp) */
+                               EMIT2(X86_JNE, 1 + 1 + 2 + 5);
+                               EMIT1(0x5A); /* pop rdx */
+                               EMIT1(0x58); /* pop rax */
+                               EMIT2(0x31, 0xc0); /* xor eax, eax */
+
+                               /* jmp cleanup_addr
+                                * addrs[i] - 11, because there are 11 bytes
+                                * after this insn: div, mov, pop, pop, mov
+                                */
+                               jmp_offset = ctx->cleanup_addr - (addrs[i] - 11);
+                               EMIT1_off32(0xE9, jmp_offset);
                        }
-               }
 
-               switch (filter[0].code) {
-               case BPF_S_RET_K:
-               case BPF_S_LD_W_LEN:
-               case BPF_S_ANC_PROTOCOL:
-               case BPF_S_ANC_IFINDEX:
-               case BPF_S_ANC_MARK:
-               case BPF_S_ANC_RXHASH:
-               case BPF_S_ANC_CPU:
-               case BPF_S_ANC_VLAN_TAG:
-               case BPF_S_ANC_VLAN_TAG_PRESENT:
-               case BPF_S_ANC_QUEUE:
-               case BPF_S_ANC_PKTTYPE:
-               case BPF_S_LD_W_ABS:
-               case BPF_S_LD_H_ABS:
-               case BPF_S_LD_B_ABS:
-                       /* first instruction sets A register (or is RET 'constant') */
+                       if (BPF_CLASS(insn->code) == BPF_ALU64)
+                               /* div r11 */
+                               EMIT3(0x49, 0xF7, 0xF3);
+                       else
+                               /* div r11d */
+                               EMIT3(0x41, 0xF7, 0xF3);
+
+                       if (BPF_OP(insn->code) == BPF_MOD)
+                               /* mov r11, rdx */
+                               EMIT3(0x49, 0x89, 0xD3);
+                       else
+                               /* mov r11, rax */
+                               EMIT3(0x49, 0x89, 0xC3);
+
+                       EMIT1(0x5A); /* pop rdx */
+                       EMIT1(0x58); /* pop rax */
+
+                       /* mov dst_reg, r11 */
+                       EMIT_mov(dst_reg, AUX_REG);
                        break;
-               default:
-                       /* make sure we dont leak kernel information to user */
-                       CLEAR_A(); /* A = 0 */
-               }
 
-               for (i = 0; i < flen; i++) {
-                       unsigned int K = filter[i].k;
+               case BPF_ALU | BPF_MUL | BPF_K:
+               case BPF_ALU | BPF_MUL | BPF_X:
+               case BPF_ALU64 | BPF_MUL | BPF_K:
+               case BPF_ALU64 | BPF_MUL | BPF_X:
+                       EMIT1(0x50); /* push rax */
+                       EMIT1(0x52); /* push rdx */
+
+                       /* mov r11, dst_reg */
+                       EMIT_mov(AUX_REG, dst_reg);
+
+                       if (BPF_SRC(insn->code) == BPF_X)
+                               /* mov rax, src_reg */
+                               EMIT_mov(BPF_REG_0, src_reg);
+                       else
+                               /* mov rax, imm32 */
+                               EMIT3_off32(0x48, 0xC7, 0xC0, imm32);
+
+                       if (BPF_CLASS(insn->code) == BPF_ALU64)
+                               EMIT1(add_1mod(0x48, AUX_REG));
+                       else if (is_ereg(AUX_REG))
+                               EMIT1(add_1mod(0x40, AUX_REG));
+                       /* mul(q) r11 */
+                       EMIT2(0xF7, add_1reg(0xE0, AUX_REG));
+
+                       /* mov r11, rax */
+                       EMIT_mov(AUX_REG, BPF_REG_0);
+
+                       EMIT1(0x5A); /* pop rdx */
+                       EMIT1(0x58); /* pop rax */
+
+                       /* mov dst_reg, r11 */
+                       EMIT_mov(dst_reg, AUX_REG);
+                       break;
 
-                       switch (filter[i].code) {
-                       case BPF_S_ALU_ADD_X: /* A += X; */
-                               seen |= SEEN_XREG;
-                               EMIT2(0x01, 0xd8);              /* add %ebx,%eax */
-                               break;
-                       case BPF_S_ALU_ADD_K: /* A += K; */
-                               if (!K)
-                                       break;
-                               if (is_imm8(K))
-                                       EMIT3(0x83, 0xc0, K);   /* add imm8,%eax */
-                               else
-                                       EMIT1_off32(0x05, K);   /* add imm32,%eax */
-                               break;
-                       case BPF_S_ALU_SUB_X: /* A -= X; */
-                               seen |= SEEN_XREG;
-                               EMIT2(0x29, 0xd8);              /* sub    %ebx,%eax */
-                               break;
-                       case BPF_S_ALU_SUB_K: /* A -= K */
-                               if (!K)
-                                       break;
-                               if (is_imm8(K))
-                                       EMIT3(0x83, 0xe8, K); /* sub imm8,%eax */
-                               else
-                                       EMIT1_off32(0x2d, K); /* sub imm32,%eax */
-                               break;
-                       case BPF_S_ALU_MUL_X: /* A *= X; */
-                               seen |= SEEN_XREG;
-                               EMIT3(0x0f, 0xaf, 0xc3);        /* imul %ebx,%eax */
-                               break;
-                       case BPF_S_ALU_MUL_K: /* A *= K */
-                               if (is_imm8(K))
-                                       EMIT3(0x6b, 0xc0, K); /* imul imm8,%eax,%eax */
-                               else {
-                                       EMIT2(0x69, 0xc0);              /* imul imm32,%eax */
-                                       EMIT(K, 4);
-                               }
-                               break;
-                       case BPF_S_ALU_DIV_X: /* A /= X; */
-                               seen |= SEEN_XREG;
-                               EMIT2(0x85, 0xdb);      /* test %ebx,%ebx */
-                               if (pc_ret0 > 0) {
-                                       /* addrs[pc_ret0 - 1] is start address of target
-                                        * (addrs[i] - 4) is the address following this jmp
-                                        * ("xor %edx,%edx; div %ebx" being 4 bytes long)
-                                        */
-                                       EMIT_COND_JMP(X86_JE, addrs[pc_ret0 - 1] -
-                                                               (addrs[i] - 4));
-                               } else {
-                                       EMIT_COND_JMP(X86_JNE, 2 + 5);
-                                       CLEAR_A();
-                                       EMIT1_off32(0xe9, cleanup_addr - (addrs[i] - 4)); /* jmp .+off32 */
-                               }
-                               EMIT4(0x31, 0xd2, 0xf7, 0xf3); /* xor %edx,%edx; div %ebx */
-                               break;
-                       case BPF_S_ALU_MOD_X: /* A %= X; */
-                               seen |= SEEN_XREG;
-                               EMIT2(0x85, 0xdb);      /* test %ebx,%ebx */
-                               if (pc_ret0 > 0) {
-                                       /* addrs[pc_ret0 - 1] is start address of target
-                                        * (addrs[i] - 6) is the address following this jmp
-                                        * ("xor %edx,%edx; div %ebx;mov %edx,%eax" being 6 bytes long)
-                                        */
-                                       EMIT_COND_JMP(X86_JE, addrs[pc_ret0 - 1] -
-                                                               (addrs[i] - 6));
-                               } else {
-                                       EMIT_COND_JMP(X86_JNE, 2 + 5);
-                                       CLEAR_A();
-                                       EMIT1_off32(0xe9, cleanup_addr - (addrs[i] - 6)); /* jmp .+off32 */
-                               }
-                               EMIT2(0x31, 0xd2);      /* xor %edx,%edx */
-                               EMIT2(0xf7, 0xf3);      /* div %ebx */
-                               EMIT2(0x89, 0xd0);      /* mov %edx,%eax */
-                               break;
-                       case BPF_S_ALU_MOD_K: /* A %= K; */
-                               if (K == 1) {
-                                       CLEAR_A();
-                                       break;
-                               }
-                               EMIT2(0x31, 0xd2);      /* xor %edx,%edx */
-                               EMIT1(0xb9);EMIT(K, 4); /* mov imm32,%ecx */
-                               EMIT2(0xf7, 0xf1);      /* div %ecx */
-                               EMIT2(0x89, 0xd0);      /* mov %edx,%eax */
-                               break;
-                       case BPF_S_ALU_DIV_K: /* A /= K */
-                               if (K == 1)
-                                       break;
-                               EMIT2(0x31, 0xd2);      /* xor %edx,%edx */
-                               EMIT1(0xb9);EMIT(K, 4); /* mov imm32,%ecx */
-                               EMIT2(0xf7, 0xf1);      /* div %ecx */
-                               break;
-                       case BPF_S_ALU_AND_X:
-                               seen |= SEEN_XREG;
-                               EMIT2(0x21, 0xd8);              /* and %ebx,%eax */
-                               break;
-                       case BPF_S_ALU_AND_K:
-                               if (K >= 0xFFFFFF00) {
-                                       EMIT2(0x24, K & 0xFF); /* and imm8,%al */
-                               } else if (K >= 0xFFFF0000) {
-                                       EMIT2(0x66, 0x25);      /* and imm16,%ax */
-                                       EMIT(K, 2);
-                               } else {
-                                       EMIT1_off32(0x25, K);   /* and imm32,%eax */
-                               }
-                               break;
-                       case BPF_S_ALU_OR_X:
-                               seen |= SEEN_XREG;
-                               EMIT2(0x09, 0xd8);              /* or %ebx,%eax */
-                               break;
-                       case BPF_S_ALU_OR_K:
-                               if (is_imm8(K))
-                                       EMIT3(0x83, 0xc8, K); /* or imm8,%eax */
-                               else
-                                       EMIT1_off32(0x0d, K);   /* or imm32,%eax */
-                               break;
-                       case BPF_S_ANC_ALU_XOR_X: /* A ^= X; */
-                       case BPF_S_ALU_XOR_X:
-                               seen |= SEEN_XREG;
-                               EMIT2(0x31, 0xd8);              /* xor %ebx,%eax */
-                               break;
-                       case BPF_S_ALU_XOR_K: /* A ^= K; */
-                               if (K == 0)
-                                       break;
-                               if (is_imm8(K))
-                                       EMIT3(0x83, 0xf0, K);   /* xor imm8,%eax */
-                               else
-                                       EMIT1_off32(0x35, K);   /* xor imm32,%eax */
-                               break;
-                       case BPF_S_ALU_LSH_X: /* A <<= X; */
-                               seen |= SEEN_XREG;
-                               EMIT4(0x89, 0xd9, 0xd3, 0xe0);  /* mov %ebx,%ecx; shl %cl,%eax */
-                               break;
-                       case BPF_S_ALU_LSH_K:
-                               if (K == 0)
-                                       break;
-                               else if (K == 1)
-                                       EMIT2(0xd1, 0xe0); /* shl %eax */
-                               else
-                                       EMIT3(0xc1, 0xe0, K);
-                               break;
-                       case BPF_S_ALU_RSH_X: /* A >>= X; */
-                               seen |= SEEN_XREG;
-                               EMIT4(0x89, 0xd9, 0xd3, 0xe8);  /* mov %ebx,%ecx; shr %cl,%eax */
-                               break;
-                       case BPF_S_ALU_RSH_K: /* A >>= K; */
-                               if (K == 0)
-                                       break;
-                               else if (K == 1)
-                                       EMIT2(0xd1, 0xe8); /* shr %eax */
-                               else
-                                       EMIT3(0xc1, 0xe8, K);
-                               break;
-                       case BPF_S_ALU_NEG:
-                               EMIT2(0xf7, 0xd8);              /* neg %eax */
-                               break;
-                       case BPF_S_RET_K:
-                               if (!K) {
-                                       if (pc_ret0 == -1)
-                                               pc_ret0 = i;
-                                       CLEAR_A();
-                               } else {
-                                       EMIT1_off32(0xb8, K);   /* mov $imm32,%eax */
-                               }
-                               /* fallinto */
-                       case BPF_S_RET_A:
-                               if (seen_or_pass0) {
-                                       if (i != flen - 1) {
-                                               EMIT_JMP(cleanup_addr - addrs[i]);
-                                               break;
-                                       }
-                                       if (seen_or_pass0 & SEEN_XREG)
-                                               EMIT4(0x48, 0x8b, 0x5d, 0xf8);  /* mov  -8(%rbp),%rbx */
-                                       EMIT1(0xc9);            /* leaveq */
-                               }
-                               EMIT1(0xc3);            /* ret */
-                               break;
-                       case BPF_S_MISC_TAX: /* X = A */
-                               seen |= SEEN_XREG;
-                               EMIT2(0x89, 0xc3);      /* mov    %eax,%ebx */
-                               break;
-                       case BPF_S_MISC_TXA: /* A = X */
-                               seen |= SEEN_XREG;
-                               EMIT2(0x89, 0xd8);      /* mov    %ebx,%eax */
-                               break;
-                       case BPF_S_LD_IMM: /* A = K */
-                               if (!K)
-                                       CLEAR_A();
-                               else
-                                       EMIT1_off32(0xb8, K); /* mov $imm32,%eax */
-                               break;
-                       case BPF_S_LDX_IMM: /* X = K */
-                               seen |= SEEN_XREG;
-                               if (!K)
-                                       CLEAR_X();
+                       /* shifts */
+               case BPF_ALU | BPF_LSH | BPF_K:
+               case BPF_ALU | BPF_RSH | BPF_K:
+               case BPF_ALU | BPF_ARSH | BPF_K:
+               case BPF_ALU64 | BPF_LSH | BPF_K:
+               case BPF_ALU64 | BPF_RSH | BPF_K:
+               case BPF_ALU64 | BPF_ARSH | BPF_K:
+                       if (BPF_CLASS(insn->code) == BPF_ALU64)
+                               EMIT1(add_1mod(0x48, dst_reg));
+                       else if (is_ereg(dst_reg))
+                               EMIT1(add_1mod(0x40, dst_reg));
+
+                       switch (BPF_OP(insn->code)) {
+                       case BPF_LSH: b3 = 0xE0; break;
+                       case BPF_RSH: b3 = 0xE8; break;
+                       case BPF_ARSH: b3 = 0xF8; break;
+                       }
+                       EMIT3(0xC1, add_1reg(b3, dst_reg), imm32);
+                       break;
+
+               case BPF_ALU | BPF_END | BPF_FROM_BE:
+                       switch (imm32) {
+                       case 16:
+                               /* emit 'ror %ax, 8' to swap lower 2 bytes */
+                               EMIT1(0x66);
+                               if (is_ereg(dst_reg))
+                                       EMIT1(0x41);
+                               EMIT3(0xC1, add_1reg(0xC8, dst_reg), 8);
+                               break;
+                       case 32:
+                               /* emit 'bswap eax' to swap lower 4 bytes */
+                               if (is_ereg(dst_reg))
+                                       EMIT2(0x41, 0x0F);
                                else
-                                       EMIT1_off32(0xbb, K); /* mov $imm32,%ebx */
-                               break;
-                       case BPF_S_LD_MEM: /* A = mem[K] : mov off8(%rbp),%eax */
-                               seen |= SEEN_MEM;
-                               EMIT3(0x8b, 0x45, 0xf0 - K*4);
-                               break;
-                       case BPF_S_LDX_MEM: /* X = mem[K] : mov off8(%rbp),%ebx */
-                               seen |= SEEN_XREG | SEEN_MEM;
-                               EMIT3(0x8b, 0x5d, 0xf0 - K*4);
-                               break;
-                       case BPF_S_ST: /* mem[K] = A : mov %eax,off8(%rbp) */
-                               seen |= SEEN_MEM;
-                               EMIT3(0x89, 0x45, 0xf0 - K*4);
-                               break;
-                       case BPF_S_STX: /* mem[K] = X : mov %ebx,off8(%rbp) */
-                               seen |= SEEN_XREG | SEEN_MEM;
-                               EMIT3(0x89, 0x5d, 0xf0 - K*4);
-                               break;
-                       case BPF_S_LD_W_LEN: /* A = skb->len; */
-                               BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4);
-                               if (is_imm8(offsetof(struct sk_buff, len)))
-                                       /* mov    off8(%rdi),%eax */
-                                       EMIT3(0x8b, 0x47, offsetof(struct sk_buff, len));
-                               else {
-                                       EMIT2(0x8b, 0x87);
-                                       EMIT(offsetof(struct sk_buff, len), 4);
-                               }
-                               break;
-                       case BPF_S_LDX_W_LEN: /* X = skb->len; */
-                               seen |= SEEN_XREG;
-                               if (is_imm8(offsetof(struct sk_buff, len)))
-                                       /* mov off8(%rdi),%ebx */
-                                       EMIT3(0x8b, 0x5f, offsetof(struct sk_buff, len));
-                               else {
-                                       EMIT2(0x8b, 0x9f);
-                                       EMIT(offsetof(struct sk_buff, len), 4);
-                               }
-                               break;
-                       case BPF_S_ANC_PROTOCOL: /* A = ntohs(skb->protocol); */
-                               BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, protocol) != 2);
-                               if (is_imm8(offsetof(struct sk_buff, protocol))) {
-                                       /* movzwl off8(%rdi),%eax */
-                                       EMIT4(0x0f, 0xb7, 0x47, offsetof(struct sk_buff, protocol));
-                               } else {
-                                       EMIT3(0x0f, 0xb7, 0x87); /* movzwl off32(%rdi),%eax */
-                                       EMIT(offsetof(struct sk_buff, protocol), 4);
-                               }
-                               EMIT2(0x86, 0xc4); /* ntohs() : xchg   %al,%ah */
-                               break;
-                       case BPF_S_ANC_IFINDEX:
-                               if (is_imm8(offsetof(struct sk_buff, dev))) {
-                                       /* movq off8(%rdi),%rax */
-                                       EMIT4(0x48, 0x8b, 0x47, offsetof(struct sk_buff, dev));
-                               } else {
-                                       EMIT3(0x48, 0x8b, 0x87); /* movq off32(%rdi),%rax */
-                                       EMIT(offsetof(struct sk_buff, dev), 4);
-                               }
-                               EMIT3(0x48, 0x85, 0xc0);        /* test %rax,%rax */
-                               EMIT_COND_JMP(X86_JE, cleanup_addr - (addrs[i] - 6));
-                               BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, ifindex) != 4);
-                               EMIT2(0x8b, 0x80);      /* mov off32(%rax),%eax */
-                               EMIT(offsetof(struct net_device, ifindex), 4);
-                               break;
-                       case BPF_S_ANC_MARK:
-                               BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4);
-                               if (is_imm8(offsetof(struct sk_buff, mark))) {
-                                       /* mov off8(%rdi),%eax */
-                                       EMIT3(0x8b, 0x47, offsetof(struct sk_buff, mark));
-                               } else {
-                                       EMIT2(0x8b, 0x87);
-                                       EMIT(offsetof(struct sk_buff, mark), 4);
-                               }
-                               break;
-                       case BPF_S_ANC_RXHASH:
-                               BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4);
-                               if (is_imm8(offsetof(struct sk_buff, hash))) {
-                                       /* mov off8(%rdi),%eax */
-                                       EMIT3(0x8b, 0x47, offsetof(struct sk_buff, hash));
-                               } else {
-                                       EMIT2(0x8b, 0x87);
-                                       EMIT(offsetof(struct sk_buff, hash), 4);
-                               }
-                               break;
-                       case BPF_S_ANC_QUEUE:
-                               BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, queue_mapping) != 2);
-                               if (is_imm8(offsetof(struct sk_buff, queue_mapping))) {
-                                       /* movzwl off8(%rdi),%eax */
-                                       EMIT4(0x0f, 0xb7, 0x47, offsetof(struct sk_buff, queue_mapping));
-                               } else {
-                                       EMIT3(0x0f, 0xb7, 0x87); /* movzwl off32(%rdi),%eax */
-                                       EMIT(offsetof(struct sk_buff, queue_mapping), 4);
-                               }
-                               break;
-                       case BPF_S_ANC_CPU:
-#ifdef CONFIG_SMP
-                               EMIT4(0x65, 0x8b, 0x04, 0x25); /* mov %gs:off32,%eax */
-                               EMIT((u32)(unsigned long)&cpu_number, 4); /* A = smp_processor_id(); */
-#else
-                               CLEAR_A();
-#endif
+                                       EMIT1(0x0F);
+                               EMIT1(add_1reg(0xC8, dst_reg));
                                break;
-                       case BPF_S_ANC_VLAN_TAG:
-                       case BPF_S_ANC_VLAN_TAG_PRESENT:
-                               BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2);
-                               if (is_imm8(offsetof(struct sk_buff, vlan_tci))) {
-                                       /* movzwl off8(%rdi),%eax */
-                                       EMIT4(0x0f, 0xb7, 0x47, offsetof(struct sk_buff, vlan_tci));
-                               } else {
-                                       EMIT3(0x0f, 0xb7, 0x87); /* movzwl off32(%rdi),%eax */
-                                       EMIT(offsetof(struct sk_buff, vlan_tci), 4);
-                               }
-                               BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000);
-                               if (filter[i].code == BPF_S_ANC_VLAN_TAG) {
-                                       EMIT3(0x80, 0xe4, 0xef); /* and    $0xef,%ah */
-                               } else {
-                                       EMIT3(0xc1, 0xe8, 0x0c); /* shr    $0xc,%eax */
-                                       EMIT3(0x83, 0xe0, 0x01); /* and    $0x1,%eax */
-                               }
-                               break;
-                       case BPF_S_ANC_PKTTYPE:
-                       {
-                               int off = pkt_type_offset();
-
-                               if (off < 0)
-                                       goto out;
-                               if (is_imm8(off)) {
-                                       /* movzbl off8(%rdi),%eax */
-                                       EMIT4(0x0f, 0xb6, 0x47, off);
-                               } else {
-                                       /* movbl off32(%rdi),%eax */
-                                       EMIT3(0x0f, 0xb6, 0x87);
-                                       EMIT(off, 4);
-                               }
-                               EMIT3(0x83, 0xe0, PKT_TYPE_MAX); /* and    $0x7,%eax */
+                       case 64:
+                               /* emit 'bswap rax' to swap 8 bytes */
+                               EMIT3(add_1mod(0x48, dst_reg), 0x0F,
+                                     add_1reg(0xC8, dst_reg));
                                break;
                        }
-                       case BPF_S_LD_W_ABS:
-                               func = CHOOSE_LOAD_FUNC(K, sk_load_word);
-common_load:                   seen |= SEEN_DATAREF;
-                               t_offset = func - (image + addrs[i]);
-                               EMIT1_off32(0xbe, K); /* mov imm32,%esi */
-                               EMIT1_off32(0xe8, t_offset); /* call */
-                               break;
-                       case BPF_S_LD_H_ABS:
-                               func = CHOOSE_LOAD_FUNC(K, sk_load_half);
-                               goto common_load;
-                       case BPF_S_LD_B_ABS:
-                               func = CHOOSE_LOAD_FUNC(K, sk_load_byte);
-                               goto common_load;
-                       case BPF_S_LDX_B_MSH:
-                               func = CHOOSE_LOAD_FUNC(K, sk_load_byte_msh);
-                               seen |= SEEN_DATAREF | SEEN_XREG;
-                               t_offset = func - (image + addrs[i]);
-                               EMIT1_off32(0xbe, K);   /* mov imm32,%esi */
-                               EMIT1_off32(0xe8, t_offset); /* call sk_load_byte_msh */
-                               break;
-                       case BPF_S_LD_W_IND:
-                               func = sk_load_word;
-common_load_ind:               seen |= SEEN_DATAREF | SEEN_XREG;
-                               t_offset = func - (image + addrs[i]);
-                               if (K) {
-                                       if (is_imm8(K)) {
-                                               EMIT3(0x8d, 0x73, K); /* lea imm8(%rbx), %esi */
-                                       } else {
-                                               EMIT2(0x8d, 0xb3); /* lea imm32(%rbx),%esi */
-                                               EMIT(K, 4);
-                                       }
-                               } else {
-                                       EMIT2(0x89,0xde); /* mov %ebx,%esi */
-                               }
-                               EMIT1_off32(0xe8, t_offset);    /* call sk_load_xxx_ind */
-                               break;
-                       case BPF_S_LD_H_IND:
-                               func = sk_load_half;
-                               goto common_load_ind;
-                       case BPF_S_LD_B_IND:
-                               func = sk_load_byte;
-                               goto common_load_ind;
-                       case BPF_S_JMP_JA:
-                               t_offset = addrs[i + K] - addrs[i];
-                               EMIT_JMP(t_offset);
-                               break;
-                       COND_SEL(BPF_S_JMP_JGT_K, X86_JA, X86_JBE);
-                       COND_SEL(BPF_S_JMP_JGE_K, X86_JAE, X86_JB);
-                       COND_SEL(BPF_S_JMP_JEQ_K, X86_JE, X86_JNE);
-                       COND_SEL(BPF_S_JMP_JSET_K,X86_JNE, X86_JE);
-                       COND_SEL(BPF_S_JMP_JGT_X, X86_JA, X86_JBE);
-                       COND_SEL(BPF_S_JMP_JGE_X, X86_JAE, X86_JB);
-                       COND_SEL(BPF_S_JMP_JEQ_X, X86_JE, X86_JNE);
-                       COND_SEL(BPF_S_JMP_JSET_X,X86_JNE, X86_JE);
-
-cond_branch:                   f_offset = addrs[i + filter[i].jf] - addrs[i];
-                               t_offset = addrs[i + filter[i].jt] - addrs[i];
-
-                               /* same targets, can avoid doing the test :) */
-                               if (filter[i].jt == filter[i].jf) {
-                                       EMIT_JMP(t_offset);
-                                       break;
-                               }
+                       break;
+
+               case BPF_ALU | BPF_END | BPF_FROM_LE:
+                       break;
+
+                       /* ST: *(u8*)(dst_reg + off) = imm */
+               case BPF_ST | BPF_MEM | BPF_B:
+                       if (is_ereg(dst_reg))
+                               EMIT2(0x41, 0xC6);
+                       else
+                               EMIT1(0xC6);
+                       goto st;
+               case BPF_ST | BPF_MEM | BPF_H:
+                       if (is_ereg(dst_reg))
+                               EMIT3(0x66, 0x41, 0xC7);
+                       else
+                               EMIT2(0x66, 0xC7);
+                       goto st;
+               case BPF_ST | BPF_MEM | BPF_W:
+                       if (is_ereg(dst_reg))
+                               EMIT2(0x41, 0xC7);
+                       else
+                               EMIT1(0xC7);
+                       goto st;
+               case BPF_ST | BPF_MEM | BPF_DW:
+                       EMIT2(add_1mod(0x48, dst_reg), 0xC7);
+
+st:                    if (is_imm8(insn->off))
+                               EMIT2(add_1reg(0x40, dst_reg), insn->off);
+                       else
+                               EMIT1_off32(add_1reg(0x80, dst_reg), insn->off);
+
+                       EMIT(imm32, bpf_size_to_x86_bytes(BPF_SIZE(insn->code)));
+                       break;
+
+                       /* STX: *(u8*)(dst_reg + off) = src_reg */
+               case BPF_STX | BPF_MEM | BPF_B:
+                       /* emit 'mov byte ptr [rax + off], al' */
+                       if (is_ereg(dst_reg) || is_ereg(src_reg) ||
+                           /* have to add extra byte for x86 SIL, DIL regs */
+                           src_reg == BPF_REG_1 || src_reg == BPF_REG_2)
+                               EMIT2(add_2mod(0x40, dst_reg, src_reg), 0x88);
+                       else
+                               EMIT1(0x88);
+                       goto stx;
+               case BPF_STX | BPF_MEM | BPF_H:
+                       if (is_ereg(dst_reg) || is_ereg(src_reg))
+                               EMIT3(0x66, add_2mod(0x40, dst_reg, src_reg), 0x89);
+                       else
+                               EMIT2(0x66, 0x89);
+                       goto stx;
+               case BPF_STX | BPF_MEM | BPF_W:
+                       if (is_ereg(dst_reg) || is_ereg(src_reg))
+                               EMIT2(add_2mod(0x40, dst_reg, src_reg), 0x89);
+                       else
+                               EMIT1(0x89);
+                       goto stx;
+               case BPF_STX | BPF_MEM | BPF_DW:
+                       EMIT2(add_2mod(0x48, dst_reg, src_reg), 0x89);
+stx:                   if (is_imm8(insn->off))
+                               EMIT2(add_2reg(0x40, dst_reg, src_reg), insn->off);
+                       else
+                               EMIT1_off32(add_2reg(0x80, dst_reg, src_reg),
+                                           insn->off);
+                       break;
+
+                       /* LDX: dst_reg = *(u8*)(src_reg + off) */
+               case BPF_LDX | BPF_MEM | BPF_B:
+                       /* emit 'movzx rax, byte ptr [rax + off]' */
+                       EMIT3(add_2mod(0x48, src_reg, dst_reg), 0x0F, 0xB6);
+                       goto ldx;
+               case BPF_LDX | BPF_MEM | BPF_H:
+                       /* emit 'movzx rax, word ptr [rax + off]' */
+                       EMIT3(add_2mod(0x48, src_reg, dst_reg), 0x0F, 0xB7);
+                       goto ldx;
+               case BPF_LDX | BPF_MEM | BPF_W:
+                       /* emit 'mov eax, dword ptr [rax+0x14]' */
+                       if (is_ereg(dst_reg) || is_ereg(src_reg))
+                               EMIT2(add_2mod(0x40, src_reg, dst_reg), 0x8B);
+                       else
+                               EMIT1(0x8B);
+                       goto ldx;
+               case BPF_LDX | BPF_MEM | BPF_DW:
+                       /* emit 'mov rax, qword ptr [rax+0x14]' */
+                       EMIT2(add_2mod(0x48, src_reg, dst_reg), 0x8B);
+ldx:                   /* if insn->off == 0 we can save one extra byte, but
+                        * special case of x86 r13 which always needs an offset
+                        * is not worth the hassle
+                        */
+                       if (is_imm8(insn->off))
+                               EMIT2(add_2reg(0x40, src_reg, dst_reg), insn->off);
+                       else
+                               EMIT1_off32(add_2reg(0x80, src_reg, dst_reg),
+                                           insn->off);
+                       break;
+
+                       /* STX XADD: lock *(u32*)(dst_reg + off) += src_reg */
+               case BPF_STX | BPF_XADD | BPF_W:
+                       /* emit 'lock add dword ptr [rax + off], eax' */
+                       if (is_ereg(dst_reg) || is_ereg(src_reg))
+                               EMIT3(0xF0, add_2mod(0x40, dst_reg, src_reg), 0x01);
+                       else
+                               EMIT2(0xF0, 0x01);
+                       goto xadd;
+               case BPF_STX | BPF_XADD | BPF_DW:
+                       EMIT3(0xF0, add_2mod(0x48, dst_reg, src_reg), 0x01);
+xadd:                  if (is_imm8(insn->off))
+                               EMIT2(add_2reg(0x40, dst_reg, src_reg), insn->off);
+                       else
+                               EMIT1_off32(add_2reg(0x80, dst_reg, src_reg),
+                                           insn->off);
+                       break;
+
+                       /* call */
+               case BPF_JMP | BPF_CALL:
+                       func = (u8 *) __bpf_call_base + imm32;
+                       jmp_offset = func - (image + addrs[i]);
+                       if (ctx->seen_ld_abs) {
+                               EMIT2(0x41, 0x52); /* push %r10 */
+                               EMIT2(0x41, 0x51); /* push %r9 */
+                               /* need to adjust jmp offset, since
+                                * pop %r9, pop %r10 take 4 bytes after call insn
+                                */
+                               jmp_offset += 4;
+                       }
+                       if (!imm32 || !is_simm32(jmp_offset)) {
+                               pr_err("unsupported bpf func %d addr %p image %p\n",
+                                      imm32, func, image);
+                               return -EINVAL;
+                       }
+                       EMIT1_off32(0xE8, jmp_offset);
+                       if (ctx->seen_ld_abs) {
+                               EMIT2(0x41, 0x59); /* pop %r9 */
+                               EMIT2(0x41, 0x5A); /* pop %r10 */
+                       }
+                       break;
+
+                       /* cond jump */
+               case BPF_JMP | BPF_JEQ | BPF_X:
+               case BPF_JMP | BPF_JNE | BPF_X:
+               case BPF_JMP | BPF_JGT | BPF_X:
+               case BPF_JMP | BPF_JGE | BPF_X:
+               case BPF_JMP | BPF_JSGT | BPF_X:
+               case BPF_JMP | BPF_JSGE | BPF_X:
+                       /* cmp dst_reg, src_reg */
+                       EMIT3(add_2mod(0x48, dst_reg, src_reg), 0x39,
+                             add_2reg(0xC0, dst_reg, src_reg));
+                       goto emit_cond_jmp;
+
+               case BPF_JMP | BPF_JSET | BPF_X:
+                       /* test dst_reg, src_reg */
+                       EMIT3(add_2mod(0x48, dst_reg, src_reg), 0x85,
+                             add_2reg(0xC0, dst_reg, src_reg));
+                       goto emit_cond_jmp;
+
+               case BPF_JMP | BPF_JSET | BPF_K:
+                       /* test dst_reg, imm32 */
+                       EMIT1(add_1mod(0x48, dst_reg));
+                       EMIT2_off32(0xF7, add_1reg(0xC0, dst_reg), imm32);
+                       goto emit_cond_jmp;
+
+               case BPF_JMP | BPF_JEQ | BPF_K:
+               case BPF_JMP | BPF_JNE | BPF_K:
+               case BPF_JMP | BPF_JGT | BPF_K:
+               case BPF_JMP | BPF_JGE | BPF_K:
+               case BPF_JMP | BPF_JSGT | BPF_K:
+               case BPF_JMP | BPF_JSGE | BPF_K:
+                       /* cmp dst_reg, imm8/32 */
+                       EMIT1(add_1mod(0x48, dst_reg));
+
+                       if (is_imm8(imm32))
+                               EMIT3(0x83, add_1reg(0xF8, dst_reg), imm32);
+                       else
+                               EMIT2_off32(0x81, add_1reg(0xF8, dst_reg), imm32);
+
+emit_cond_jmp:         /* convert BPF opcode to x86 */
+                       switch (BPF_OP(insn->code)) {
+                       case BPF_JEQ:
+                               jmp_cond = X86_JE;
+                               break;
+                       case BPF_JSET:
+                       case BPF_JNE:
+                               jmp_cond = X86_JNE;
+                               break;
+                       case BPF_JGT:
+                               /* GT is unsigned '>', JA in x86 */
+                               jmp_cond = X86_JA;
+                               break;
+                       case BPF_JGE:
+                               /* GE is unsigned '>=', JAE in x86 */
+                               jmp_cond = X86_JAE;
+                               break;
+                       case BPF_JSGT:
+                               /* signed '>', GT in x86 */
+                               jmp_cond = X86_JG;
+                               break;
+                       case BPF_JSGE:
+                               /* signed '>=', GE in x86 */
+                               jmp_cond = X86_JGE;
+                               break;
+                       default: /* to silence gcc warning */
+                               return -EFAULT;
+                       }
+                       jmp_offset = addrs[i + insn->off] - addrs[i];
+                       if (is_imm8(jmp_offset)) {
+                               EMIT2(jmp_cond, jmp_offset);
+                       } else if (is_simm32(jmp_offset)) {
+                               EMIT2_off32(0x0F, jmp_cond + 0x10, jmp_offset);
+                       } else {
+                               pr_err("cond_jmp gen bug %llx\n", jmp_offset);
+                               return -EFAULT;
+                       }
+
+                       break;
 
-                               switch (filter[i].code) {
-                               case BPF_S_JMP_JGT_X:
-                               case BPF_S_JMP_JGE_X:
-                               case BPF_S_JMP_JEQ_X:
-                                       seen |= SEEN_XREG;
-                                       EMIT2(0x39, 0xd8); /* cmp %ebx,%eax */
-                                       break;
-                               case BPF_S_JMP_JSET_X:
-                                       seen |= SEEN_XREG;
-                                       EMIT2(0x85, 0xd8); /* test %ebx,%eax */
-                                       break;
-                               case BPF_S_JMP_JEQ_K:
-                                       if (K == 0) {
-                                               EMIT2(0x85, 0xc0); /* test   %eax,%eax */
-                                               break;
-                                       }
-                               case BPF_S_JMP_JGT_K:
-                               case BPF_S_JMP_JGE_K:
-                                       if (K <= 127)
-                                               EMIT3(0x83, 0xf8, K); /* cmp imm8,%eax */
+               case BPF_JMP | BPF_JA:
+                       jmp_offset = addrs[i + insn->off] - addrs[i];
+                       if (!jmp_offset)
+                               /* optimize out nop jumps */
+                               break;
+emit_jmp:
+                       if (is_imm8(jmp_offset)) {
+                               EMIT2(0xEB, jmp_offset);
+                       } else if (is_simm32(jmp_offset)) {
+                               EMIT1_off32(0xE9, jmp_offset);
+                       } else {
+                               pr_err("jmp gen bug %llx\n", jmp_offset);
+                               return -EFAULT;
+                       }
+                       break;
+
+               case BPF_LD | BPF_IND | BPF_W:
+                       func = sk_load_word;
+                       goto common_load;
+               case BPF_LD | BPF_ABS | BPF_W:
+                       func = CHOOSE_LOAD_FUNC(imm32, sk_load_word);
+common_load:           ctx->seen_ld_abs = true;
+                       jmp_offset = func - (image + addrs[i]);
+                       if (!func || !is_simm32(jmp_offset)) {
+                               pr_err("unsupported bpf func %d addr %p image %p\n",
+                                      imm32, func, image);
+                               return -EINVAL;
+                       }
+                       if (BPF_MODE(insn->code) == BPF_ABS) {
+                               /* mov %esi, imm32 */
+                               EMIT1_off32(0xBE, imm32);
+                       } else {
+                               /* mov %rsi, src_reg */
+                               EMIT_mov(BPF_REG_2, src_reg);
+                               if (imm32) {
+                                       if (is_imm8(imm32))
+                                               /* add %esi, imm8 */
+                                               EMIT3(0x83, 0xC6, imm32);
                                        else
-                                               EMIT1_off32(0x3d, K); /* cmp imm32,%eax */
-                                       break;
-                               case BPF_S_JMP_JSET_K:
-                                       if (K <= 0xFF)
-                                               EMIT2(0xa8, K); /* test imm8,%al */
-                                       else if (!(K & 0xFFFF00FF))
-                                               EMIT3(0xf6, 0xc4, K >> 8); /* test imm8,%ah */
-                                       else if (K <= 0xFFFF) {
-                                               EMIT2(0x66, 0xa9); /* test imm16,%ax */
-                                               EMIT(K, 2);
-                                       } else {
-                                               EMIT1_off32(0xa9, K); /* test imm32,%eax */
-                                       }
-                                       break;
+                                               /* add %esi, imm32 */
+                                               EMIT2_off32(0x81, 0xC6, imm32);
                                }
-                               if (filter[i].jt != 0) {
-                                       if (filter[i].jf && f_offset)
-                                               t_offset += is_near(f_offset) ? 2 : 5;
-                                       EMIT_COND_JMP(t_op, t_offset);
-                                       if (filter[i].jf)
-                                               EMIT_JMP(f_offset);
-                                       break;
-                               }
-                               EMIT_COND_JMP(f_op, f_offset);
-                               break;
-                       default:
-                               /* hmm, too complex filter, give up with jit compiler */
-                               goto out;
                        }
-                       ilen = prog - temp;
-                       if (image) {
-                               if (unlikely(proglen + ilen > oldproglen)) {
-                                       pr_err("bpb_jit_compile fatal error\n");
-                                       kfree(addrs);
-                                       module_free(NULL, header);
-                                       return;
-                               }
-                               memcpy(image + proglen, temp, ilen);
+                       /* skb pointer is in R6 (%rbx), it will be copied into
+                        * %rdi if skb_copy_bits() call is necessary.
+                        * sk_load_* helpers also use %r10 and %r9d.
+                        * See bpf_jit.S
+                        */
+                       EMIT1_off32(0xE8, jmp_offset); /* call */
+                       break;
+
+               case BPF_LD | BPF_IND | BPF_H:
+                       func = sk_load_half;
+                       goto common_load;
+               case BPF_LD | BPF_ABS | BPF_H:
+                       func = CHOOSE_LOAD_FUNC(imm32, sk_load_half);
+                       goto common_load;
+               case BPF_LD | BPF_IND | BPF_B:
+                       func = sk_load_byte;
+                       goto common_load;
+               case BPF_LD | BPF_ABS | BPF_B:
+                       func = CHOOSE_LOAD_FUNC(imm32, sk_load_byte);
+                       goto common_load;
+
+               case BPF_JMP | BPF_EXIT:
+                       if (i != insn_cnt - 1) {
+                               jmp_offset = ctx->cleanup_addr - addrs[i];
+                               goto emit_jmp;
                        }
-                       proglen += ilen;
-                       addrs[i] = proglen;
-                       prog = temp;
+                       /* update cleanup_addr */
+                       ctx->cleanup_addr = proglen;
+                       /* mov rbx, qword ptr [rbp-X] */
+                       EMIT3_off32(0x48, 0x8B, 0x9D, -stacksize);
+                       /* mov r13, qword ptr [rbp-X] */
+                       EMIT3_off32(0x4C, 0x8B, 0xAD, -stacksize + 8);
+                       /* mov r14, qword ptr [rbp-X] */
+                       EMIT3_off32(0x4C, 0x8B, 0xB5, -stacksize + 16);
+                       /* mov r15, qword ptr [rbp-X] */
+                       EMIT3_off32(0x4C, 0x8B, 0xBD, -stacksize + 24);
+
+                       EMIT1(0xC9); /* leave */
+                       EMIT1(0xC3); /* ret */
+                       break;
+
+               default:
+                       /* By design x64 JIT should support all BPF instructions
+                        * This error will be seen if new instruction was added
+                        * to interpreter, but not to JIT
+                        * or if there is junk in sk_filter
+                        */
+                       pr_err("bpf_jit: unknown opcode %02x\n", insn->code);
+                       return -EINVAL;
                }
-               /* last bpf instruction is always a RET :
-                * use it to give the cleanup instruction(s) addr
-                */
-               cleanup_addr = proglen - 1; /* ret */
-               if (seen_or_pass0)
-                       cleanup_addr -= 1; /* leaveq */
-               if (seen_or_pass0 & SEEN_XREG)
-                       cleanup_addr -= 4; /* mov  -8(%rbp),%rbx */
 
+               ilen = prog - temp;
+               if (image) {
+                       if (unlikely(proglen + ilen > oldproglen)) {
+                               pr_err("bpf_jit_compile fatal error\n");
+                               return -EFAULT;
+                       }
+                       memcpy(image + proglen, temp, ilen);
+               }
+               proglen += ilen;
+               addrs[i] = proglen;
+               prog = temp;
+       }
+       return proglen;
+}
+
+void bpf_jit_compile(struct sk_filter *prog)
+{
+}
+
+void bpf_int_jit_compile(struct sk_filter *prog)
+{
+       struct bpf_binary_header *header = NULL;
+       int proglen, oldproglen = 0;
+       struct jit_context ctx = {};
+       u8 *image = NULL;
+       int *addrs;
+       int pass;
+       int i;
+
+       if (!bpf_jit_enable)
+               return;
+
+       if (!prog || !prog->len)
+               return;
+
+       addrs = kmalloc(prog->len * sizeof(*addrs), GFP_KERNEL);
+       if (!addrs)
+               return;
+
+       /* Before first pass, make a rough estimation of addrs[]
+        * each bpf instruction is translated to less than 64 bytes
+        */
+       for (proglen = 0, i = 0; i < prog->len; i++) {
+               proglen += 64;
+               addrs[i] = proglen;
+       }
+       ctx.cleanup_addr = proglen;
+
+       for (pass = 0; pass < 10; pass++) {
+               proglen = do_jit(prog, addrs, image, oldproglen, &ctx);
+               if (proglen <= 0) {
+                       image = NULL;
+                       if (header)
+                               module_free(NULL, header);
+                       goto out;
+               }
                if (image) {
                        if (proglen != oldproglen)
-                               pr_err("bpb_jit_compile proglen=%u != oldproglen=%u\n", proglen, oldproglen);
+                               pr_err("bpf_jit: proglen=%d != oldproglen=%d\n",
+                                      proglen, oldproglen);
                        break;
                }
                if (proglen == oldproglen) {
@@ -766,17 +918,16 @@ cond_branch:                      f_offset = addrs[i + filter[i].jf] - addrs[i];
        }
 
        if (bpf_jit_enable > 1)
-               bpf_jit_dump(flen, proglen, pass, image);
+               bpf_jit_dump(prog->len, proglen, 0, image);
 
        if (image) {
                bpf_flush_icache(header, image + proglen);
                set_memory_ro((unsigned long)header, header->pages);
-               fp->bpf_func = (void *)image;
-               fp->jited = 1;
+               prog->bpf_func = (void *)image;
+               prog->jited = 1;
        }
 out:
        kfree(addrs);
-       return;
 }
 
 static void bpf_jit_free_deferred(struct work_struct *work)
index 204814e88e464480248a6ddef6fe292d095514ad..d4725fc0395d037e9ae3b861a241f9e9d229a580 100644 (file)
@@ -2780,7 +2780,7 @@ static struct pci_driver fore200e_pca_driver = {
 
 static int __init fore200e_module_init(void)
 {
-       int err;
+       int err = 0;
 
        printk(FORE200E "FORE Systems 200E-series ATM driver - version " FORE200E_VERSION "\n");
 
index 1bdf104e90bb7f1924acf51f78caa39817d78efb..b621f56a36be5850b1abc4d1619b665fbce7ccf3 100644 (file)
@@ -2551,12 +2551,12 @@ idt77252_close(struct atm_vcc *vcc)
                timeout = 5 * 1000;
                while (atomic_read(&vc->scq->used) > 0) {
                        timeout = msleep_interruptible(timeout);
-                       if (!timeout)
+                       if (!timeout) {
+                               pr_warn("%s: SCQ drain timeout: %u used\n",
+                                       card->name, atomic_read(&vc->scq->used));
                                break;
+                       }
                }
-               if (!timeout)
-                       printk("%s: SCQ drain timeout: %u used\n",
-                              card->name, atomic_read(&vc->scq->used));
 
                writel(TCMDQ_HALT | vc->index, SAR_REG_TCMDQ);
                clear_scd(card, vc->scq, vc->class);
index 4c95b503b09ee3593ecfe1e2ca035788295b9f80..bbeb404b3a07068ae2d3c94a44d10fbe6101d080 100644 (file)
@@ -541,7 +541,6 @@ static int rbd_open(struct block_device *bdev, fmode_t mode)
                return -ENOENT;
 
        (void) get_device(&rbd_dev->dev);
-       set_device_ro(bdev, rbd_dev->mapping.read_only);
 
        return 0;
 }
@@ -559,10 +558,76 @@ static void rbd_release(struct gendisk *disk, fmode_t mode)
        put_device(&rbd_dev->dev);
 }
 
+static int rbd_ioctl_set_ro(struct rbd_device *rbd_dev, unsigned long arg)
+{
+       int ret = 0;
+       int val;
+       bool ro;
+       bool ro_changed = false;
+
+       /* get_user() may sleep, so call it before taking rbd_dev->lock */
+       if (get_user(val, (int __user *)(arg)))
+               return -EFAULT;
+
+       ro = val ? true : false;
+       /* Snapshot doesn't allow to write*/
+       if (rbd_dev->spec->snap_id != CEPH_NOSNAP && !ro)
+               return -EROFS;
+
+       spin_lock_irq(&rbd_dev->lock);
+       /* prevent others open this device */
+       if (rbd_dev->open_count > 1) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       if (rbd_dev->mapping.read_only != ro) {
+               rbd_dev->mapping.read_only = ro;
+               ro_changed = true;
+       }
+
+out:
+       spin_unlock_irq(&rbd_dev->lock);
+       /* set_disk_ro() may sleep, so call it after releasing rbd_dev->lock */
+       if (ret == 0 && ro_changed)
+               set_disk_ro(rbd_dev->disk, ro ? 1 : 0);
+
+       return ret;
+}
+
+static int rbd_ioctl(struct block_device *bdev, fmode_t mode,
+                       unsigned int cmd, unsigned long arg)
+{
+       struct rbd_device *rbd_dev = bdev->bd_disk->private_data;
+       int ret = 0;
+
+       switch (cmd) {
+       case BLKROSET:
+               ret = rbd_ioctl_set_ro(rbd_dev, arg);
+               break;
+       default:
+               ret = -ENOTTY;
+       }
+
+       return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static int rbd_compat_ioctl(struct block_device *bdev, fmode_t mode,
+                               unsigned int cmd, unsigned long arg)
+{
+       return rbd_ioctl(bdev, mode, cmd, arg);
+}
+#endif /* CONFIG_COMPAT */
+
 static const struct block_device_operations rbd_bd_ops = {
        .owner                  = THIS_MODULE,
        .open                   = rbd_open,
        .release                = rbd_release,
+       .ioctl                  = rbd_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl           = rbd_compat_ioctl,
+#endif
 };
 
 /*
@@ -1382,6 +1447,13 @@ static void rbd_obj_request_put(struct rbd_obj_request *obj_request)
        kref_put(&obj_request->kref, rbd_obj_request_destroy);
 }
 
+static void rbd_img_request_get(struct rbd_img_request *img_request)
+{
+       dout("%s: img %p (was %d)\n", __func__, img_request,
+            atomic_read(&img_request->kref.refcount));
+       kref_get(&img_request->kref);
+}
+
 static bool img_request_child_test(struct rbd_img_request *img_request);
 static void rbd_parent_request_destroy(struct kref *kref);
 static void rbd_img_request_destroy(struct kref *kref);
@@ -2142,6 +2214,7 @@ static void rbd_img_obj_callback(struct rbd_obj_request *obj_request)
        img_request->next_completion = which;
 out:
        spin_unlock_irq(&img_request->completion_lock);
+       rbd_img_request_put(img_request);
 
        if (!more)
                rbd_img_request_complete(img_request);
@@ -2242,6 +2315,7 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request,
                        goto out_unwind;
                obj_request->osd_req = osd_req;
                obj_request->callback = rbd_img_obj_callback;
+               rbd_img_request_get(img_request);
 
                if (write_request) {
                        osd_req_op_alloc_hint_init(osd_req, which,
@@ -2872,56 +2946,55 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data)
 }
 
 /*
- * Request sync osd watch/unwatch.  The value of "start" determines
- * whether a watch request is being initiated or torn down.
+ * Initiate a watch request, synchronously.
  */
-static int __rbd_dev_header_watch_sync(struct rbd_device *rbd_dev, bool start)
+static int rbd_dev_header_watch_sync(struct rbd_device *rbd_dev)
 {
        struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
        struct rbd_obj_request *obj_request;
        int ret;
 
-       rbd_assert(start ^ !!rbd_dev->watch_event);
-       rbd_assert(start ^ !!rbd_dev->watch_request);
+       rbd_assert(!rbd_dev->watch_event);
+       rbd_assert(!rbd_dev->watch_request);
 
-       if (start) {
-               ret = ceph_osdc_create_event(osdc, rbd_watch_cb, rbd_dev,
-                                               &rbd_dev->watch_event);
-               if (ret < 0)
-                       return ret;
-               rbd_assert(rbd_dev->watch_event != NULL);
-       }
+       ret = ceph_osdc_create_event(osdc, rbd_watch_cb, rbd_dev,
+                                    &rbd_dev->watch_event);
+       if (ret < 0)
+               return ret;
+
+       rbd_assert(rbd_dev->watch_event);
 
-       ret = -ENOMEM;
        obj_request = rbd_obj_request_create(rbd_dev->header_name, 0, 0,
-                                                       OBJ_REQUEST_NODATA);
-       if (!obj_request)
+                                            OBJ_REQUEST_NODATA);
+       if (!obj_request) {
+               ret = -ENOMEM;
                goto out_cancel;
+       }
 
        obj_request->osd_req = rbd_osd_req_create(rbd_dev, true, 1,
                                                  obj_request);
-       if (!obj_request->osd_req)
-               goto out_cancel;
+       if (!obj_request->osd_req) {
+               ret = -ENOMEM;
+               goto out_put;
+       }
 
-       if (start)
-               ceph_osdc_set_request_linger(osdc, obj_request->osd_req);
-       else
-               ceph_osdc_unregister_linger_request(osdc,
-                                       rbd_dev->watch_request->osd_req);
+       ceph_osdc_set_request_linger(osdc, obj_request->osd_req);
 
        osd_req_op_watch_init(obj_request->osd_req, 0, CEPH_OSD_OP_WATCH,
-                               rbd_dev->watch_event->cookie, 0, start ? 1 : 0);
+                             rbd_dev->watch_event->cookie, 0, 1);
        rbd_osd_req_format_write(obj_request);
 
        ret = rbd_obj_request_submit(osdc, obj_request);
        if (ret)
-               goto out_cancel;
+               goto out_linger;
+
        ret = rbd_obj_request_wait(obj_request);
        if (ret)
-               goto out_cancel;
+               goto out_linger;
+
        ret = obj_request->result;
        if (ret)
-               goto out_cancel;
+               goto out_linger;
 
        /*
         * A watch request is set to linger, so the underlying osd
@@ -2931,36 +3004,84 @@ static int __rbd_dev_header_watch_sync(struct rbd_device *rbd_dev, bool start)
         * it.  We'll drop that reference (below) after we've
         * unregistered it.
         */
-       if (start) {
-               rbd_dev->watch_request = obj_request;
+       rbd_dev->watch_request = obj_request;
 
-               return 0;
+       return 0;
+
+out_linger:
+       ceph_osdc_unregister_linger_request(osdc, obj_request->osd_req);
+out_put:
+       rbd_obj_request_put(obj_request);
+out_cancel:
+       ceph_osdc_cancel_event(rbd_dev->watch_event);
+       rbd_dev->watch_event = NULL;
+
+       return ret;
+}
+
+/*
+ * Tear down a watch request, synchronously.
+ */
+static int __rbd_dev_header_unwatch_sync(struct rbd_device *rbd_dev)
+{
+       struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
+       struct rbd_obj_request *obj_request;
+       int ret;
+
+       rbd_assert(rbd_dev->watch_event);
+       rbd_assert(rbd_dev->watch_request);
+
+       obj_request = rbd_obj_request_create(rbd_dev->header_name, 0, 0,
+                                            OBJ_REQUEST_NODATA);
+       if (!obj_request) {
+               ret = -ENOMEM;
+               goto out_cancel;
+       }
+
+       obj_request->osd_req = rbd_osd_req_create(rbd_dev, true, 1,
+                                                 obj_request);
+       if (!obj_request->osd_req) {
+               ret = -ENOMEM;
+               goto out_put;
        }
 
+       osd_req_op_watch_init(obj_request->osd_req, 0, CEPH_OSD_OP_WATCH,
+                             rbd_dev->watch_event->cookie, 0, 0);
+       rbd_osd_req_format_write(obj_request);
+
+       ret = rbd_obj_request_submit(osdc, obj_request);
+       if (ret)
+               goto out_put;
+
+       ret = rbd_obj_request_wait(obj_request);
+       if (ret)
+               goto out_put;
+
+       ret = obj_request->result;
+       if (ret)
+               goto out_put;
+
        /* We have successfully torn down the watch request */
 
+       ceph_osdc_unregister_linger_request(osdc,
+                                           rbd_dev->watch_request->osd_req);
        rbd_obj_request_put(rbd_dev->watch_request);
        rbd_dev->watch_request = NULL;
+
+out_put:
+       rbd_obj_request_put(obj_request);
 out_cancel:
-       /* Cancel the event if we're tearing down, or on error */
        ceph_osdc_cancel_event(rbd_dev->watch_event);
        rbd_dev->watch_event = NULL;
-       if (obj_request)
-               rbd_obj_request_put(obj_request);
 
        return ret;
 }
 
-static int rbd_dev_header_watch_sync(struct rbd_device *rbd_dev)
-{
-       return __rbd_dev_header_watch_sync(rbd_dev, true);
-}
-
 static void rbd_dev_header_unwatch_sync(struct rbd_device *rbd_dev)
 {
        int ret;
 
-       ret = __rbd_dev_header_watch_sync(rbd_dev, false);
+       ret = __rbd_dev_header_unwatch_sync(rbd_dev);
        if (ret) {
                rbd_warn(rbd_dev, "unable to tear down watch request: %d\n",
                         ret);
@@ -3058,7 +3179,6 @@ static void rbd_request_fn(struct request_queue *q)
                __releases(q->queue_lock) __acquires(q->queue_lock)
 {
        struct rbd_device *rbd_dev = q->queuedata;
-       bool read_only = rbd_dev->mapping.read_only;
        struct request *rq;
        int result;
 
@@ -3094,7 +3214,7 @@ static void rbd_request_fn(struct request_queue *q)
 
                if (write_request) {
                        result = -EROFS;
-                       if (read_only)
+                       if (rbd_dev->mapping.read_only)
                                goto end_request;
                        rbd_assert(rbd_dev->spec->snap_id == CEPH_NOSNAP);
                }
@@ -4682,6 +4802,38 @@ static int rbd_add_parse_args(const char *buf,
        return ret;
 }
 
+/*
+ * Return pool id (>= 0) or a negative error code.
+ */
+static int rbd_add_get_pool_id(struct rbd_client *rbdc, const char *pool_name)
+{
+       u64 newest_epoch;
+       unsigned long timeout = rbdc->client->options->mount_timeout * HZ;
+       int tries = 0;
+       int ret;
+
+again:
+       ret = ceph_pg_poolid_by_name(rbdc->client->osdc.osdmap, pool_name);
+       if (ret == -ENOENT && tries++ < 1) {
+               ret = ceph_monc_do_get_version(&rbdc->client->monc, "osdmap",
+                                              &newest_epoch);
+               if (ret < 0)
+                       return ret;
+
+               if (rbdc->client->osdc.osdmap->epoch < newest_epoch) {
+                       ceph_monc_request_next_osdmap(&rbdc->client->monc);
+                       (void) ceph_monc_wait_osdmap(&rbdc->client->monc,
+                                                    newest_epoch, timeout);
+                       goto again;
+               } else {
+                       /* the osdmap we have is new enough */
+                       return -ENOENT;
+               }
+       }
+
+       return ret;
+}
+
 /*
  * An rbd format 2 image has a unique identifier, distinct from the
  * name given to it by the user.  Internally, that identifier is
@@ -4752,7 +4904,7 @@ static int rbd_dev_image_id(struct rbd_device *rbd_dev)
 
                image_id = ceph_extract_encoded_string(&p, p + ret,
                                                NULL, GFP_NOIO);
-               ret = IS_ERR(image_id) ? PTR_ERR(image_id) : 0;
+               ret = PTR_ERR_OR_ZERO(image_id);
                if (!ret)
                        rbd_dev->image_format = 2;
        } else {
@@ -4907,6 +5059,7 @@ static int rbd_dev_device_setup(struct rbd_device *rbd_dev)
        if (ret)
                goto err_out_disk;
        set_capacity(rbd_dev->disk, rbd_dev->mapping.size / SECTOR_SIZE);
+       set_disk_ro(rbd_dev->disk, rbd_dev->mapping.read_only);
 
        ret = rbd_bus_add_dev(rbd_dev);
        if (ret)
@@ -5053,7 +5206,6 @@ static ssize_t do_rbd_add(struct bus_type *bus,
        struct rbd_options *rbd_opts = NULL;
        struct rbd_spec *spec = NULL;
        struct rbd_client *rbdc;
-       struct ceph_osd_client *osdc;
        bool read_only;
        int rc = -ENOMEM;
 
@@ -5075,8 +5227,7 @@ static ssize_t do_rbd_add(struct bus_type *bus,
        }
 
        /* pick the pool */
-       osdc = &rbdc->client->osdc;
-       rc = ceph_pg_poolid_by_name(osdc->osdmap, spec->pool_name);
+       rc = rbd_add_get_pool_id(rbdc, spec->pool_name);
        if (rc < 0)
                goto err_out_client;
        spec->pool_id = (u64)rc;
@@ -5387,6 +5538,7 @@ static int __init rbd_init(void)
 
 static void __exit rbd_exit(void)
 {
+       ida_destroy(&rbd_dev_id_ida);
        rbd_sysfs_cleanup();
        if (single_major)
                unregister_blkdev(rbd_major, RBD_DRV_NAME);
index a83b57e57b6370572d53325638355a0d94ce24bf..f98380648cb3513fe47ac19213ce0b105a0d1873 100644 (file)
@@ -193,9 +193,10 @@ static int ath3k_load_firmware(struct usb_device *udev,
        sent += 20;
        count -= 20;
 
+       pipe = usb_sndbulkpipe(udev, 0x02);
+
        while (count) {
                size = min_t(uint, count, BULK_SIZE);
-               pipe = usb_sndbulkpipe(udev, 0x02);
                memcpy(send_buf, firmware->data + sent, size);
 
                err = usb_bulk_msg(udev, pipe, send_buf, size,
index 7399303d7d9978447ff722d823c1dcece408029a..dc79f88f8717f478c8d8ab6d0a7d45849f772ac1 100644 (file)
@@ -59,6 +59,8 @@ struct btmrvl_device {
 };
 
 struct btmrvl_adapter {
+       void *hw_regs_buf;
+       u8 *hw_regs;
        u32 int_count;
        struct sk_buff_head tx_queue;
        u8 psmode;
@@ -140,7 +142,7 @@ void btmrvl_interrupt(struct btmrvl_private *priv);
 bool btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb);
 int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb);
 
-int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd);
+int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, u8 subcmd);
 int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv);
 int btmrvl_enable_ps(struct btmrvl_private *priv);
 int btmrvl_prepare_command(struct btmrvl_private *priv);
index 2c4997ce248484703a1b859c5e518396fcdbfa64..e9dbddb0b8f1efb1f15ede65f50d80390ca370c3 100644 (file)
@@ -24,6 +24,7 @@
 #include <net/bluetooth/hci_core.h>
 
 #include "btmrvl_drv.h"
+#include "btmrvl_sdio.h"
 
 #define VERSION "1.0"
 
@@ -201,7 +202,7 @@ static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 opcode,
        return 0;
 }
 
-int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd)
+int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, u8 subcmd)
 {
        int ret;
 
@@ -337,10 +338,25 @@ static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb)
 
 static void btmrvl_init_adapter(struct btmrvl_private *priv)
 {
+       int buf_size;
+
        skb_queue_head_init(&priv->adapter->tx_queue);
 
        priv->adapter->ps_state = PS_AWAKE;
 
+       buf_size = ALIGN_SZ(SDIO_BLOCK_SIZE, BTSDIO_DMA_ALIGN);
+       priv->adapter->hw_regs_buf = kzalloc(buf_size, GFP_KERNEL);
+       if (!priv->adapter->hw_regs_buf) {
+               priv->adapter->hw_regs = NULL;
+               BT_ERR("Unable to allocate buffer for hw_regs.");
+       } else {
+               priv->adapter->hw_regs =
+                       (u8 *)ALIGN_ADDR(priv->adapter->hw_regs_buf,
+                                        BTSDIO_DMA_ALIGN);
+               BT_DBG("hw_regs_buf=%p hw_regs=%p",
+                      priv->adapter->hw_regs_buf, priv->adapter->hw_regs);
+       }
+
        init_waitqueue_head(&priv->adapter->cmd_wait_q);
 }
 
@@ -348,6 +364,7 @@ static void btmrvl_free_adapter(struct btmrvl_private *priv)
 {
        skb_queue_purge(&priv->adapter->tx_queue);
 
+       kfree(priv->adapter->hw_regs_buf);
        kfree(priv->adapter);
 
        priv->adapter = NULL;
index 1b52c9f5230d324d0476a2d7dd3f308e7fd723d8..9dedca516ff50567a278fb9a511dbcdfd7a1980c 100644 (file)
@@ -64,6 +64,7 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8688 = {
        .io_port_0 = 0x00,
        .io_port_1 = 0x01,
        .io_port_2 = 0x02,
+       .int_read_to_clear = false,
 };
 static const struct btmrvl_sdio_card_reg btmrvl_reg_87xx = {
        .cfg = 0x00,
@@ -80,6 +81,7 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_87xx = {
        .io_port_0 = 0x78,
        .io_port_1 = 0x79,
        .io_port_2 = 0x7a,
+       .int_read_to_clear = false,
 };
 
 static const struct btmrvl_sdio_card_reg btmrvl_reg_88xx = {
@@ -97,6 +99,9 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_88xx = {
        .io_port_0 = 0xd8,
        .io_port_1 = 0xd9,
        .io_port_2 = 0xda,
+       .int_read_to_clear = true,
+       .host_int_rsr = 0x01,
+       .card_misc_cfg = 0xcc,
 };
 
 static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
@@ -667,46 +672,78 @@ static int btmrvl_sdio_process_int_status(struct btmrvl_private *priv)
        return 0;
 }
 
-static void btmrvl_sdio_interrupt(struct sdio_func *func)
+static int btmrvl_sdio_read_to_clear(struct btmrvl_sdio_card *card, u8 *ireg)
 {
-       struct btmrvl_private *priv;
-       struct btmrvl_sdio_card *card;
-       ulong flags;
-       u8 ireg = 0;
+       struct btmrvl_adapter *adapter = card->priv->adapter;
        int ret;
 
-       card = sdio_get_drvdata(func);
-       if (!card || !card->priv) {
-               BT_ERR("sbi_interrupt(%p) card or priv is "
-                               "NULL, card=%p\n", func, card);
-               return;
+       ret = sdio_readsb(card->func, adapter->hw_regs, 0, SDIO_BLOCK_SIZE);
+       if (ret) {
+               BT_ERR("sdio_readsb: read int hw_regs failed: %d", ret);
+               return ret;
        }
 
-       priv = card->priv;
+       *ireg = adapter->hw_regs[card->reg->host_intstatus];
+       BT_DBG("hw_regs[%#x]=%#x", card->reg->host_intstatus, *ireg);
+
+       return 0;
+}
 
-       ireg = sdio_readb(card->func, card->reg->host_intstatus, &ret);
+static int btmrvl_sdio_write_to_clear(struct btmrvl_sdio_card *card, u8 *ireg)
+{
+       int ret;
+
+       *ireg = sdio_readb(card->func, card->reg->host_intstatus, &ret);
        if (ret) {
-               BT_ERR("sdio_readb: read int status register failed");
-               return;
+               BT_ERR("sdio_readb: read int status failed: %d", ret);
+               return ret;
        }
 
-       if (ireg != 0) {
+       if (*ireg) {
                /*
                 * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
                 * Clear the interrupt status register and re-enable the
                 * interrupt.
                 */
-               BT_DBG("ireg = 0x%x", ireg);
+               BT_DBG("int_status = 0x%x", *ireg);
 
-               sdio_writeb(card->func, ~(ireg) & (DN_LD_HOST_INT_STATUS |
-                                       UP_LD_HOST_INT_STATUS),
-                               card->reg->host_intstatus, &ret);
+               sdio_writeb(card->func, ~(*ireg) & (DN_LD_HOST_INT_STATUS |
+                                                   UP_LD_HOST_INT_STATUS),
+                           card->reg->host_intstatus, &ret);
                if (ret) {
-                       BT_ERR("sdio_writeb: clear int status register failed");
-                       return;
+                       BT_ERR("sdio_writeb: clear int status failed: %d", ret);
+                       return ret;
                }
        }
 
+       return 0;
+}
+
+static void btmrvl_sdio_interrupt(struct sdio_func *func)
+{
+       struct btmrvl_private *priv;
+       struct btmrvl_sdio_card *card;
+       ulong flags;
+       u8 ireg = 0;
+       int ret;
+
+       card = sdio_get_drvdata(func);
+       if (!card || !card->priv) {
+               BT_ERR("sbi_interrupt(%p) card or priv is "
+                               "NULL, card=%p\n", func, card);
+               return;
+       }
+
+       priv = card->priv;
+
+       if (card->reg->int_read_to_clear)
+               ret = btmrvl_sdio_read_to_clear(card, &ireg);
+       else
+               ret = btmrvl_sdio_write_to_clear(card, &ireg);
+
+       if (ret)
+               return;
+
        spin_lock_irqsave(&priv->driver_lock, flags);
        sdio_ireg |= ireg;
        spin_unlock_irqrestore(&priv->driver_lock, flags);
@@ -777,6 +814,30 @@ static int btmrvl_sdio_register_dev(struct btmrvl_sdio_card *card)
 
        BT_DBG("SDIO FUNC%d IO port: 0x%x", func->num, card->ioport);
 
+       if (card->reg->int_read_to_clear) {
+               reg = sdio_readb(func, card->reg->host_int_rsr, &ret);
+               if (ret < 0) {
+                       ret = -EIO;
+                       goto release_irq;
+               }
+               sdio_writeb(func, reg | 0x3f, card->reg->host_int_rsr, &ret);
+               if (ret < 0) {
+                       ret = -EIO;
+                       goto release_irq;
+               }
+
+               reg = sdio_readb(func, card->reg->card_misc_cfg, &ret);
+               if (ret < 0) {
+                       ret = -EIO;
+                       goto release_irq;
+               }
+               sdio_writeb(func, reg | 0x10, card->reg->card_misc_cfg, &ret);
+               if (ret < 0) {
+                       ret = -EIO;
+                       goto release_irq;
+               }
+       }
+
        sdio_set_drvdata(func, card);
 
        sdio_release_host(func);
index 43d35a609ca9a94795afb731d230fa88ca109bef..d4dd3b0fa53d16d68da0101494e43ca4664716c2 100644 (file)
@@ -78,6 +78,9 @@ struct btmrvl_sdio_card_reg {
        u8 io_port_0;
        u8 io_port_1;
        u8 io_port_2;
+       bool int_read_to_clear;
+       u8 host_int_rsr;
+       u8 card_misc_cfg;
 };
 
 struct btmrvl_sdio_card {
index a7dfbf9a3afb6be53e372f78d9ee8202bdb17d08..a1c80b0c7663d25baf2224bae0513b3bb29c6543 100644 (file)
@@ -49,6 +49,7 @@ static struct usb_driver btusb_driver;
 #define BTUSB_WRONG_SCO_MTU    0x40
 #define BTUSB_ATH3012          0x80
 #define BTUSB_INTEL            0x100
+#define BTUSB_BCM_PATCHRAM     0x200
 
 static const struct usb_device_id btusb_table[] = {
        /* Generic Bluetooth USB device */
@@ -111,7 +112,8 @@ static const struct usb_device_id btusb_table[] = {
        { USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01) },
 
        /* Broadcom devices with vendor specific id */
-       { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01) },
+       { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01),
+         .driver_info = BTUSB_BCM_PATCHRAM },
 
        /* Belkin F8065bf - Broadcom based */
        { USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01) },
@@ -1381,6 +1383,154 @@ static int btusb_setup_intel(struct hci_dev *hdev)
        return 0;
 }
 
+static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
+{
+       struct btusb_data *data = hci_get_drvdata(hdev);
+       struct usb_device *udev = data->udev;
+       char fw_name[64];
+       const struct firmware *fw;
+       const u8 *fw_ptr;
+       size_t fw_size;
+       const struct hci_command_hdr *cmd;
+       const u8 *cmd_param;
+       u16 opcode;
+       struct sk_buff *skb;
+       struct hci_rp_read_local_version *ver;
+       long ret;
+
+       snprintf(fw_name, sizeof(fw_name), "brcm/%s-%04x-%04x.hcd",
+                udev->product ? udev->product : "BCM",
+                le16_to_cpu(udev->descriptor.idVendor),
+                le16_to_cpu(udev->descriptor.idProduct));
+
+       ret = request_firmware(&fw, fw_name, &hdev->dev);
+       if (ret < 0) {
+               BT_INFO("%s: BCM: patch %s not found", hdev->name,
+                       fw_name);
+               return 0;
+       }
+
+       /* Reset */
+       skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               ret = PTR_ERR(skb);
+               BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret);
+               goto done;
+       }
+       kfree_skb(skb);
+
+       /* Read Local Version Info */
+       skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
+                            HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               ret = PTR_ERR(skb);
+               BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)",
+                       hdev->name, ret);
+               goto done;
+       }
+
+       if (skb->len != sizeof(*ver)) {
+               BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch",
+                       hdev->name);
+               kfree_skb(skb);
+               ret = -EIO;
+               goto done;
+       }
+
+       ver = (struct hci_rp_read_local_version *) skb->data;
+       BT_INFO("%s: BCM: patching hci_ver=%02x hci_rev=%04x lmp_ver=%02x "
+               "lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev,
+               ver->lmp_ver, ver->lmp_subver);
+       kfree_skb(skb);
+
+       /* Start Download */
+       skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               ret = PTR_ERR(skb);
+               BT_ERR("%s: BCM: Download Minidrv command failed (%ld)",
+                       hdev->name, ret);
+               goto reset_fw;
+       }
+       kfree_skb(skb);
+
+       /* 50 msec delay after Download Minidrv completes */
+       msleep(50);
+
+       fw_ptr = fw->data;
+       fw_size = fw->size;
+
+       while (fw_size >= sizeof(*cmd)) {
+               cmd = (struct hci_command_hdr *) fw_ptr;
+               fw_ptr += sizeof(*cmd);
+               fw_size -= sizeof(*cmd);
+
+               if (fw_size < cmd->plen) {
+                       BT_ERR("%s: BCM: patch %s is corrupted",
+                               hdev->name, fw_name);
+                       ret = -EINVAL;
+                       goto reset_fw;
+               }
+
+               cmd_param = fw_ptr;
+               fw_ptr += cmd->plen;
+               fw_size -= cmd->plen;
+
+               opcode = le16_to_cpu(cmd->opcode);
+
+               skb = __hci_cmd_sync(hdev, opcode, cmd->plen, cmd_param,
+                                    HCI_INIT_TIMEOUT);
+               if (IS_ERR(skb)) {
+                       ret = PTR_ERR(skb);
+                       BT_ERR("%s: BCM: patch command %04x failed (%ld)",
+                               hdev->name, opcode, ret);
+                       goto reset_fw;
+               }
+               kfree_skb(skb);
+       }
+
+       /* 250 msec delay after Launch Ram completes */
+       msleep(250);
+
+reset_fw:
+       /* Reset */
+       skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               ret = PTR_ERR(skb);
+               BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret);
+               goto done;
+       }
+       kfree_skb(skb);
+
+       /* Read Local Version Info */
+       skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
+                            HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               ret = PTR_ERR(skb);
+               BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)",
+                       hdev->name, ret);
+               goto done;
+       }
+
+       if (skb->len != sizeof(*ver)) {
+               BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch",
+                       hdev->name);
+               kfree_skb(skb);
+               ret = -EIO;
+               goto done;
+       }
+
+       ver = (struct hci_rp_read_local_version *) skb->data;
+       BT_INFO("%s: BCM: firmware hci_ver=%02x hci_rev=%04x lmp_ver=%02x "
+               "lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev,
+               ver->lmp_ver, ver->lmp_subver);
+       kfree_skb(skb);
+
+done:
+       release_firmware(fw);
+
+       return ret;
+}
+
 static int btusb_probe(struct usb_interface *intf,
                                const struct usb_device_id *id)
 {
@@ -1486,6 +1636,9 @@ static int btusb_probe(struct usb_interface *intf,
        if (id->driver_info & BTUSB_BCM92035)
                hdev->setup = btusb_setup_bcm92035;
 
+       if (id->driver_info & BTUSB_BCM_PATCHRAM)
+               hdev->setup = btusb_setup_bcm_patchram;
+
        if (id->driver_info & BTUSB_INTEL)
                hdev->setup = btusb_setup_intel;
 
index 7048a583fe51a695a044ad541a0f894e3bf7c510..66db9a803373efb92c8966c9f69fdb6b8ec7aa59 100644 (file)
@@ -55,13 +55,6 @@ struct h4_struct {
        struct sk_buff_head txq;
 };
 
-/* H4 receiver States */
-#define H4_W4_PACKET_TYPE      0
-#define H4_W4_EVENT_HDR                1
-#define H4_W4_ACL_HDR          2
-#define H4_W4_SCO_HDR          3
-#define H4_W4_DATA             4
-
 /* Initialize protocol */
 static int h4_open(struct hci_uart *hu)
 {
index 527a43da3d33724ba81f15f1dd7f14f1bb99d40c..3795fce8a8303e66a004fe12bcf6d9008a7d5c63 100644 (file)
@@ -116,9 +116,25 @@ static struct ti_dt_clk am43xx_clks[] = {
 
 int __init am43xx_dt_clk_init(void)
 {
+       struct clk *clk1, *clk2;
+
        ti_dt_clocks_register(am43xx_clks);
 
        omap2_clk_disable_autoidle_all();
 
+       /*
+        * cpsw_cpts_rft_clk  has got the choice of 3 clocksources
+        * dpll_core_m4_ck, dpll_core_m5_ck and dpll_disp_m2_ck.
+        * By default dpll_core_m4_ck is selected, witn this as clock
+        * source the CPTS doesnot work properly. It gives clockcheck errors
+        * while running PTP.
+        * clockcheck: clock jumped backward or running slower than expected!
+        * By selecting dpll_core_m5_ck as the clocksource fixes this issue.
+        * In AM335x dpll_core_m5_ck is the default clocksource.
+        */
+       clk1 = clk_get_sys(NULL, "cpsw_cpts_rft_clk");
+       clk2 = clk_get_sys(NULL, "dpll_core_m5_ck");
+       clk_set_parent(clk1, clk2);
+
        return 0;
 }
index 719f6fb5b1c35d00108c47a61918741200eae75d..74f5788d50b12986c68fdfdda10d92218314c45e 100644 (file)
@@ -73,12 +73,10 @@ static int fastsleep_loop(struct cpuidle_device *dev,
                return index;
 
        new_lpcr = old_lpcr;
-       new_lpcr &= ~(LPCR_MER | LPCR_PECE); /* lpcr[mer] must be 0 */
-
-       /* exit powersave upon external interrupt, but not decrementer
-        * interrupt.
+       /* Do not exit powersave upon decrementer as we've setup the timer
+        * offload.
         */
-       new_lpcr |= LPCR_PECE0;
+       new_lpcr &= ~LPCR_PECE1;
 
        mtspr(SPRN_LPCR, new_lpcr);
        power7_sleep();
index 136d6a283e0a3818846eab4e78e7e9245b7d6f07..9634f20e392611b32349aaa348aa3980f2344ecd 100644 (file)
@@ -187,8 +187,11 @@ static int poll_idle(struct cpuidle_device *dev,
 
        t1 = ktime_get();
        local_irq_enable();
-       while (!need_resched())
-               cpu_relax();
+       if (!current_set_polling_and_test()) {
+               while (!need_resched())
+                       cpu_relax();
+       }
+       current_clr_polling();
 
        t2 = ktime_get();
        diff = ktime_to_us(ktime_sub(t2, t1));
index f066fa23cc050f2fb8ea1ed605d24774f11956dc..02f177aeb16c8488be0335186680ed8947b39c00 100644 (file)
@@ -313,7 +313,7 @@ config CRYPTO_DEV_S5P
 
 config CRYPTO_DEV_NX
        bool "Support for IBM Power7+ in-Nest cryptographic acceleration"
-       depends on PPC64 && IBMVIO
+       depends on PPC64 && IBMVIO && !CPU_LITTLE_ENDIAN
        default n
        help
          Support for Power7+ in-Nest cryptographic acceleration.
index 6c8b032cacba7c0e7a21d37a5dd4de9d9d42e19b..ed9350d42764e06bb38eacfdf8756ae8ac43b2ba 100644 (file)
@@ -404,7 +404,7 @@ static u32  next_vp;
  * performance critical channels (IDE, SCSI and Network) will be uniformly
  * distributed across all available CPUs.
  */
-static void init_vp_index(struct vmbus_channel *channel, uuid_le *type_guid)
+static void init_vp_index(struct vmbus_channel *channel, const uuid_le *type_guid)
 {
        u32 cur_cpu;
        int i;
index 18d1a8404cbc0e1f9ef7dcb12d07132fba66cdb3..22b750749a39c90393012cac83db6ff4bbff64ad 100644 (file)
@@ -649,9 +649,9 @@ extern struct vmbus_connection vmbus_connection;
 
 /* General vmbus interface */
 
-struct hv_device *vmbus_device_create(uuid_le *type,
-                                        uuid_le *instance,
-                                        struct vmbus_channel *channel);
+struct hv_device *vmbus_device_create(const uuid_le *type,
+                                     const uuid_le *instance,
+                                     struct vmbus_channel *channel);
 
 int vmbus_device_register(struct hv_device *child_device_obj);
 void vmbus_device_unregister(struct hv_device *device_obj);
index 8e53a3c2607e00c07e066b28f11c056aef4a6abd..4d6b26979fbd54e457dfbcea4bfc9a6a26ec846c 100644 (file)
@@ -435,7 +435,7 @@ static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env)
        return ret;
 }
 
-static uuid_le null_guid;
+static const uuid_le null_guid;
 
 static inline bool is_null_guid(const __u8 *guid)
 {
@@ -450,7 +450,7 @@ static inline bool is_null_guid(const __u8 *guid)
  */
 static const struct hv_vmbus_device_id *hv_vmbus_get_id(
                                        const struct hv_vmbus_device_id *id,
-                                       __u8 *guid)
+                                       const __u8 *guid)
 {
        for (; !is_null_guid(id->guid); id++)
                if (!memcmp(&id->guid, guid, sizeof(uuid_le)))
@@ -779,9 +779,9 @@ EXPORT_SYMBOL_GPL(vmbus_driver_unregister);
  * vmbus_device_create - Creates and registers a new child device
  * on the vmbus.
  */
-struct hv_device *vmbus_device_create(uuid_le *type,
-                                           uuid_le *instance,
-                                           struct vmbus_channel *channel)
+struct hv_device *vmbus_device_create(const uuid_le *type,
+                                     const uuid_le *instance,
+                                     struct vmbus_channel *channel)
 {
        struct hv_device *child_device_obj;
 
index 96d7131ab974bbb1baffcef9be2a4521890c82e9..5e153f6d4b48f2d36abcceec8dcee0996e5bf6d1 100644 (file)
@@ -234,12 +234,16 @@ static void release_tid(struct c4iw_rdev *rdev, u32 hwtid, struct sk_buff *skb)
 
 static void set_emss(struct c4iw_ep *ep, u16 opt)
 {
-       ep->emss = ep->com.dev->rdev.lldi.mtus[GET_TCPOPT_MSS(opt)] - 40;
+       ep->emss = ep->com.dev->rdev.lldi.mtus[GET_TCPOPT_MSS(opt)] -
+                  sizeof(struct iphdr) - sizeof(struct tcphdr);
        ep->mss = ep->emss;
        if (GET_TCPOPT_TSTAMP(opt))
                ep->emss -= 12;
        if (ep->emss < 128)
                ep->emss = 128;
+       if (ep->emss & 7)
+               PDBG("Warning: misaligned mtu idx %u mss %u emss=%u\n",
+                    GET_TCPOPT_MSS(opt), ep->mss, ep->emss);
        PDBG("%s mss_idx %u mss %u emss=%u\n", __func__, GET_TCPOPT_MSS(opt),
             ep->mss, ep->emss);
 }
@@ -473,7 +477,7 @@ static void send_flowc(struct c4iw_ep *ep, struct sk_buff *skb)
        flowc->mnemval[5].mnemonic = FW_FLOWC_MNEM_RCVNXT;
        flowc->mnemval[5].val = cpu_to_be32(ep->rcv_seq);
        flowc->mnemval[6].mnemonic = FW_FLOWC_MNEM_SNDBUF;
-       flowc->mnemval[6].val = cpu_to_be32(snd_win);
+       flowc->mnemval[6].val = cpu_to_be32(ep->snd_win);
        flowc->mnemval[7].mnemonic = FW_FLOWC_MNEM_MSS;
        flowc->mnemval[7].val = cpu_to_be32(ep->emss);
        /* Pad WR to 16 byte boundary */
@@ -565,6 +569,17 @@ static void c4iw_record_pm_msg(struct c4iw_ep *ep,
                sizeof(ep->com.mapped_remote_addr));
 }
 
+static void best_mtu(const unsigned short *mtus, unsigned short mtu,
+                    unsigned int *idx, int use_ts)
+{
+       unsigned short hdr_size = sizeof(struct iphdr) +
+                                 sizeof(struct tcphdr) +
+                                 (use_ts ? 12 : 0);
+       unsigned short data_size = mtu - hdr_size;
+
+       cxgb4_best_aligned_mtu(mtus, hdr_size, data_size, 8, idx);
+}
+
 static int send_connect(struct c4iw_ep *ep)
 {
        struct cpl_act_open_req *req;
@@ -591,6 +606,7 @@ static int send_connect(struct c4iw_ep *ep)
                                   &ep->com.mapped_local_addr;
        struct sockaddr_in6 *ra6 = (struct sockaddr_in6 *)
                                   &ep->com.mapped_remote_addr;
+       int win;
 
        wrlen = (ep->com.remote_addr.ss_family == AF_INET) ?
                        roundup(sizev4, 16) :
@@ -606,8 +622,18 @@ static int send_connect(struct c4iw_ep *ep)
        }
        set_wr_txq(skb, CPL_PRIORITY_SETUP, ep->ctrlq_idx);
 
-       cxgb4_best_mtu(ep->com.dev->rdev.lldi.mtus, ep->mtu, &mtu_idx);
+       best_mtu(ep->com.dev->rdev.lldi.mtus, ep->mtu, &mtu_idx,
+                enable_tcp_timestamps);
        wscale = compute_wscale(rcv_win);
+
+       /*
+        * Specify the largest window that will fit in opt0. The
+        * remainder will be specified in the rx_data_ack.
+        */
+       win = ep->rcv_win >> 10;
+       if (win > RCV_BUFSIZ_MASK)
+               win = RCV_BUFSIZ_MASK;
+
        opt0 = (nocong ? NO_CONG(1) : 0) |
               KEEP_ALIVE(1) |
               DELACK(1) |
@@ -618,7 +644,7 @@ static int send_connect(struct c4iw_ep *ep)
               SMAC_SEL(ep->smac_idx) |
               DSCP(ep->tos) |
               ULP_MODE(ULP_MODE_TCPDDP) |
-              RCV_BUFSIZ(rcv_win>>10);
+              RCV_BUFSIZ(win);
        opt2 = RX_CHANNEL(0) |
               CCTRL_ECN(enable_ecn) |
               RSS_QUEUE_VALID | RSS_QUEUE(ep->rss_qid);
@@ -674,6 +700,13 @@ static int send_connect(struct c4iw_ep *ep)
                        req6->opt2 = cpu_to_be32(opt2);
                }
        } else {
+               u32 isn = (prandom_u32() & ~7UL) - 1;
+
+               opt2 |= T5_OPT_2_VALID;
+               opt2 |= CONG_CNTRL_VALID; /* OPT_2_ISS for T5 */
+               if (peer2peer)
+                       isn += 4;
+
                if (ep->com.remote_addr.ss_family == AF_INET) {
                        t5_req = (struct cpl_t5_act_open_req *)
                                 skb_put(skb, wrlen);
@@ -690,6 +723,9 @@ static int send_connect(struct c4iw_ep *ep)
                                                     cxgb4_select_ntuple(
                                             ep->com.dev->rdev.lldi.ports[0],
                                             ep->l2t)));
+                       t5_req->rsvd = cpu_to_be32(isn);
+                       PDBG("%s snd_isn %u\n", __func__,
+                            be32_to_cpu(t5_req->rsvd));
                        t5_req->opt2 = cpu_to_be32(opt2);
                } else {
                        t5_req6 = (struct cpl_t5_act_open_req6 *)
@@ -713,6 +749,9 @@ static int send_connect(struct c4iw_ep *ep)
                                                        cxgb4_select_ntuple(
                                                ep->com.dev->rdev.lldi.ports[0],
                                                ep->l2t));
+                       t5_req6->rsvd = cpu_to_be32(isn);
+                       PDBG("%s snd_isn %u\n", __func__,
+                            be32_to_cpu(t5_req6->rsvd));
                        t5_req6->opt2 = cpu_to_be32(opt2);
                }
        }
@@ -1186,6 +1225,14 @@ static int update_rx_credits(struct c4iw_ep *ep, u32 credits)
                return 0;
        }
 
+       /*
+        * If we couldn't specify the entire rcv window at connection setup
+        * due to the limit in the number of bits in the RCV_BUFSIZ field,
+        * then add the overage in to the credits returned.
+        */
+       if (ep->rcv_win > RCV_BUFSIZ_MASK * 1024)
+               credits += ep->rcv_win - RCV_BUFSIZ_MASK * 1024;
+
        req = (struct cpl_rx_data_ack *) skb_put(skb, wrlen);
        memset(req, 0, wrlen);
        INIT_TP_WR(req, ep->hwtid);
@@ -1659,6 +1706,7 @@ static void send_fw_act_open_req(struct c4iw_ep *ep, unsigned int atid)
        unsigned int mtu_idx;
        int wscale;
        struct sockaddr_in *sin;
+       int win;
 
        skb = get_skb(NULL, sizeof(*req), GFP_KERNEL);
        req = (struct fw_ofld_connection_wr *)__skb_put(skb, sizeof(*req));
@@ -1681,8 +1729,18 @@ static void send_fw_act_open_req(struct c4iw_ep *ep, unsigned int atid)
                        htons(F_FW_OFLD_CONNECTION_WR_CPLRXDATAACK);
        req->tcb.tx_max = (__force __be32) jiffies;
        req->tcb.rcv_adv = htons(1);
-       cxgb4_best_mtu(ep->com.dev->rdev.lldi.mtus, ep->mtu, &mtu_idx);
+       best_mtu(ep->com.dev->rdev.lldi.mtus, ep->mtu, &mtu_idx,
+                enable_tcp_timestamps);
        wscale = compute_wscale(rcv_win);
+
+       /*
+        * Specify the largest window that will fit in opt0. The
+        * remainder will be specified in the rx_data_ack.
+        */
+       win = ep->rcv_win >> 10;
+       if (win > RCV_BUFSIZ_MASK)
+               win = RCV_BUFSIZ_MASK;
+
        req->tcb.opt0 = (__force __be64) (TCAM_BYPASS(1) |
                (nocong ? NO_CONG(1) : 0) |
                KEEP_ALIVE(1) |
@@ -1694,7 +1752,7 @@ static void send_fw_act_open_req(struct c4iw_ep *ep, unsigned int atid)
                SMAC_SEL(ep->smac_idx) |
                DSCP(ep->tos) |
                ULP_MODE(ULP_MODE_TCPDDP) |
-               RCV_BUFSIZ(rcv_win >> 10));
+               RCV_BUFSIZ(win));
        req->tcb.opt2 = (__force __be32) (PACE(1) |
                TX_QUEUE(ep->com.dev->rdev.lldi.tx_modq[ep->tx_chan]) |
                RX_CHANNEL(0) |
@@ -1731,6 +1789,13 @@ static int is_neg_adv(unsigned int status)
               status == CPL_ERR_KEEPALV_NEG_ADVICE;
 }
 
+static void set_tcp_window(struct c4iw_ep *ep, struct port_info *pi)
+{
+       ep->snd_win = snd_win;
+       ep->rcv_win = rcv_win;
+       PDBG("%s snd_win %d rcv_win %d\n", __func__, ep->snd_win, ep->rcv_win);
+}
+
 #define ACT_OPEN_RETRY_COUNT 2
 
 static int import_ep(struct c4iw_ep *ep, int iptype, __u8 *peer_ip,
@@ -1779,6 +1844,7 @@ static int import_ep(struct c4iw_ep *ep, int iptype, __u8 *peer_ip,
                ep->ctrlq_idx = cxgb4_port_idx(pdev);
                ep->rss_qid = cdev->rdev.lldi.rxq_ids[
                        cxgb4_port_idx(pdev) * step];
+               set_tcp_window(ep, (struct port_info *)netdev_priv(pdev));
                dev_put(pdev);
        } else {
                pdev = get_real_dev(n->dev);
@@ -1797,6 +1863,7 @@ static int import_ep(struct c4iw_ep *ep, int iptype, __u8 *peer_ip,
                        cdev->rdev.lldi.nchan;
                ep->rss_qid = cdev->rdev.lldi.rxq_ids[
                        cxgb4_port_idx(pdev) * step];
+               set_tcp_window(ep, (struct port_info *)netdev_priv(pdev));
 
                if (clear_mpa_v1) {
                        ep->retry_with_mpa_v1 = 0;
@@ -2027,13 +2094,36 @@ static void accept_cr(struct c4iw_ep *ep, struct sk_buff *skb,
        u64 opt0;
        u32 opt2;
        int wscale;
+       struct cpl_t5_pass_accept_rpl *rpl5 = NULL;
+       int win;
 
        PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
        BUG_ON(skb_cloned(skb));
-       skb_trim(skb, sizeof(*rpl));
+
        skb_get(skb);
-       cxgb4_best_mtu(ep->com.dev->rdev.lldi.mtus, ep->mtu, &mtu_idx);
+       rpl = cplhdr(skb);
+       if (is_t5(ep->com.dev->rdev.lldi.adapter_type)) {
+               skb_trim(skb, roundup(sizeof(*rpl5), 16));
+               rpl5 = (void *)rpl;
+               INIT_TP_WR(rpl5, ep->hwtid);
+       } else {
+               skb_trim(skb, sizeof(*rpl));
+               INIT_TP_WR(rpl, ep->hwtid);
+       }
+       OPCODE_TID(rpl) = cpu_to_be32(MK_OPCODE_TID(CPL_PASS_ACCEPT_RPL,
+                                                   ep->hwtid));
+
+       best_mtu(ep->com.dev->rdev.lldi.mtus, ep->mtu, &mtu_idx,
+                enable_tcp_timestamps && req->tcpopt.tstamp);
        wscale = compute_wscale(rcv_win);
+
+       /*
+        * Specify the largest window that will fit in opt0. The
+        * remainder will be specified in the rx_data_ack.
+        */
+       win = ep->rcv_win >> 10;
+       if (win > RCV_BUFSIZ_MASK)
+               win = RCV_BUFSIZ_MASK;
        opt0 = (nocong ? NO_CONG(1) : 0) |
               KEEP_ALIVE(1) |
               DELACK(1) |
@@ -2044,7 +2134,7 @@ static void accept_cr(struct c4iw_ep *ep, struct sk_buff *skb,
               SMAC_SEL(ep->smac_idx) |
               DSCP(ep->tos >> 2) |
               ULP_MODE(ULP_MODE_TCPDDP) |
-              RCV_BUFSIZ(rcv_win>>10);
+              RCV_BUFSIZ(win);
        opt2 = RX_CHANNEL(0) |
               RSS_QUEUE_VALID | RSS_QUEUE(ep->rss_qid);
 
@@ -2064,14 +2154,18 @@ static void accept_cr(struct c4iw_ep *ep, struct sk_buff *skb,
                        opt2 |= CCTRL_ECN(1);
        }
        if (is_t5(ep->com.dev->rdev.lldi.adapter_type)) {
+               u32 isn = (prandom_u32() & ~7UL) - 1;
                opt2 |= T5_OPT_2_VALID;
                opt2 |= V_CONG_CNTRL(CONG_ALG_TAHOE);
+               opt2 |= CONG_CNTRL_VALID; /* OPT_2_ISS for T5 */
+               rpl5 = (void *)rpl;
+               memset(&rpl5->iss, 0, roundup(sizeof(*rpl5)-sizeof(*rpl), 16));
+               if (peer2peer)
+                       isn += 4;
+               rpl5->iss = cpu_to_be32(isn);
+               PDBG("%s iss %u\n", __func__, be32_to_cpu(rpl5->iss));
        }
 
-       rpl = cplhdr(skb);
-       INIT_TP_WR(rpl, ep->hwtid);
-       OPCODE_TID(rpl) = cpu_to_be32(MK_OPCODE_TID(CPL_PASS_ACCEPT_RPL,
-                                     ep->hwtid));
        rpl->opt0 = cpu_to_be64(opt0);
        rpl->opt2 = cpu_to_be32(opt2);
        set_wr_txq(skb, CPL_PRIORITY_SETUP, ep->ctrlq_idx);
@@ -2136,6 +2230,7 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb)
        int err;
        u16 peer_mss = ntohs(req->tcpopt.mss);
        int iptype;
+       unsigned short hdrs;
 
        parent_ep = lookup_stid(t, stid);
        if (!parent_ep) {
@@ -2193,8 +2288,10 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb)
                goto reject;
        }
 
-       if (peer_mss && child_ep->mtu > (peer_mss + 40))
-               child_ep->mtu = peer_mss + 40;
+       hdrs = sizeof(struct iphdr) + sizeof(struct tcphdr) +
+              ((enable_tcp_timestamps && req->tcpopt.tstamp) ? 12 : 0);
+       if (peer_mss && child_ep->mtu > (peer_mss + hdrs))
+               child_ep->mtu = peer_mss + hdrs;
 
        state_set(&child_ep->com, CONNECTING);
        child_ep->com.dev = dev;
index 7151a02b4ebb3e3f48de3c270046684065d0e0d5..c04292c950f1750ac5fa2fc35b1ea4fda6a76079 100644 (file)
@@ -134,7 +134,8 @@ static int create_cq(struct c4iw_rdev *rdev, struct t4_cq *cq,
                        V_FW_RI_RES_WR_IQANUS(0) |
                        V_FW_RI_RES_WR_IQANUD(1) |
                        F_FW_RI_RES_WR_IQANDST |
-                       V_FW_RI_RES_WR_IQANDSTINDEX(*rdev->lldi.rxq_ids));
+                       V_FW_RI_RES_WR_IQANDSTINDEX(
+                               rdev->lldi.ciq_ids[cq->vector]));
        res->u.cq.iqdroprss_to_iqesize = cpu_to_be16(
                        F_FW_RI_RES_WR_IQDROPRSS |
                        V_FW_RI_RES_WR_IQPCIECH(2) |
@@ -870,6 +871,9 @@ struct ib_cq *c4iw_create_cq(struct ib_device *ibdev, int entries,
 
        rhp = to_c4iw_dev(ibdev);
 
+       if (vector >= rhp->rdev.lldi.nciq)
+               return ERR_PTR(-EINVAL);
+
        chp = kzalloc(sizeof(*chp), GFP_KERNEL);
        if (!chp)
                return ERR_PTR(-ENOMEM);
@@ -915,6 +919,7 @@ struct ib_cq *c4iw_create_cq(struct ib_device *ibdev, int entries,
        }
        chp->cq.size = hwentries;
        chp->cq.memsize = memsize;
+       chp->cq.vector = vector;
 
        ret = create_cq(&rhp->rdev, &chp->cq,
                        ucontext ? &ucontext->uctx : &rhp->rdev.uctx);
index 6f533fbcc4b3d89fe1f4c9da2ae3fd6220720819..125bc5d1e175ba4b18085fa0323dd304689b504b 100644 (file)
@@ -810,6 +810,8 @@ struct c4iw_ep {
        u8 retry_with_mpa_v1;
        u8 tried_with_mpa_v1;
        unsigned int retry_count;
+       int snd_win;
+       int rcv_win;
 };
 
 static inline void print_addr(struct c4iw_ep_common *epc, const char *func,
index c777e22bd8d538800fb8c616c8f20f987b21d7d7..b1d305338de6a06477d41ce11eb6f9519b3007c9 100644 (file)
@@ -500,7 +500,7 @@ int c4iw_register_device(struct c4iw_dev *dev)
        dev->ibdev.node_type = RDMA_NODE_RNIC;
        memcpy(dev->ibdev.node_desc, C4IW_NODE_DESC, sizeof(C4IW_NODE_DESC));
        dev->ibdev.phys_port_cnt = dev->rdev.lldi.nports;
-       dev->ibdev.num_comp_vectors = 1;
+       dev->ibdev.num_comp_vectors =  dev->rdev.lldi.nciq;
        dev->ibdev.dma_device = &(dev->rdev.lldi.pdev->dev);
        dev->ibdev.query_device = c4iw_query_device;
        dev->ibdev.query_port = c4iw_query_port;
index 2178f31984104542c3cb0cb0439e0865e0262774..68b0a6bf4eb00e23f8b737828ff85e465cc24173 100644 (file)
@@ -542,6 +542,7 @@ struct t4_cq {
        size_t memsize;
        __be64 bits_type_ts;
        u32 cqid;
+       int vector;
        u16 size; /* including status page */
        u16 cidx;
        u16 sw_pidx;
index 6121ca08fe588bff67aab81fe7df06119287292b..91289a051af928c7ffb04bfb7ac2ee8c342be7fe 100644 (file)
@@ -848,6 +848,7 @@ enum {                     /* TCP congestion control algorithms */
 #define V_CONG_CNTRL(x) ((x) << S_CONG_CNTRL)
 #define G_CONG_CNTRL(x) (((x) >> S_CONG_CNTRL) & M_CONG_CNTRL)
 
+#define CONG_CNTRL_VALID   (1 << 18)
 #define T5_OPT_2_VALID       (1 << 31)
 
 #endif /* _T4FW_RI_API_H_ */
index c4b3940845e60570fcbc5e5d0fa5ef807551d45e..078cadd6c797afeb0e22267fb76e5362a4b97326 100644 (file)
@@ -105,5 +105,5 @@ static const struct ethtool_ops ipoib_ethtool_ops = {
 
 void ipoib_set_ethtool_ops(struct net_device *dev)
 {
-       SET_ETHTOOL_OPS(dev, &ipoib_ethtool_ops);
+       dev->ethtool_ops = &ipoib_ethtool_ops;
 }
index 2e2d903db838f75e6105a88f875de8acf3667162..8d44a4060634c084971f6755aa246ff384c9a40a 100644 (file)
 #include "iscsi_iser.h"
 
 /* Register user buffer memory and initialize passive rdma
- *  dto descriptor. Total data size is stored in
- *  iser_task->data[ISER_DIR_IN].data_len
+ *  dto descriptor. Data size is stored in
+ *  task->data[ISER_DIR_IN].data_len, Protection size
+ *  os stored in task->prot[ISER_DIR_IN].data_len
  */
-static int iser_prepare_read_cmd(struct iscsi_task *task,
-                                unsigned int edtl)
+static int iser_prepare_read_cmd(struct iscsi_task *task)
 
 {
        struct iscsi_iser_task *iser_task = task->dd_data;
@@ -73,14 +73,6 @@ static int iser_prepare_read_cmd(struct iscsi_task *task,
                        return err;
        }
 
-       if (edtl > iser_task->data[ISER_DIR_IN].data_len) {
-               iser_err("Total data length: %ld, less than EDTL: "
-                        "%d, in READ cmd BHS itt: %d, conn: 0x%p\n",
-                        iser_task->data[ISER_DIR_IN].data_len, edtl,
-                        task->itt, iser_task->ib_conn);
-               return -EINVAL;
-       }
-
        err = device->iser_reg_rdma_mem(iser_task, ISER_DIR_IN);
        if (err) {
                iser_err("Failed to set up Data-IN RDMA\n");
@@ -100,8 +92,9 @@ static int iser_prepare_read_cmd(struct iscsi_task *task,
 }
 
 /* Register user buffer memory and initialize passive rdma
- *  dto descriptor. Total data size is stored in
- *  task->data[ISER_DIR_OUT].data_len
+ *  dto descriptor. Data size is stored in
+ *  task->data[ISER_DIR_OUT].data_len, Protection size
+ *  is stored at task->prot[ISER_DIR_OUT].data_len
  */
 static int
 iser_prepare_write_cmd(struct iscsi_task *task,
@@ -135,14 +128,6 @@ iser_prepare_write_cmd(struct iscsi_task *task,
                        return err;
        }
 
-       if (edtl > iser_task->data[ISER_DIR_OUT].data_len) {
-               iser_err("Total data length: %ld, less than EDTL: %d, "
-                        "in WRITE cmd BHS itt: %d, conn: 0x%p\n",
-                        iser_task->data[ISER_DIR_OUT].data_len,
-                        edtl, task->itt, task->conn);
-               return -EINVAL;
-       }
-
        err = device->iser_reg_rdma_mem(iser_task, ISER_DIR_OUT);
        if (err != 0) {
                iser_err("Failed to register write cmd RDMA mem\n");
@@ -417,11 +402,12 @@ int iser_send_command(struct iscsi_conn *conn,
        if (scsi_prot_sg_count(sc)) {
                prot_buf->buf  = scsi_prot_sglist(sc);
                prot_buf->size = scsi_prot_sg_count(sc);
-               prot_buf->data_len = sc->prot_sdb->length;
+               prot_buf->data_len = data_buf->data_len >>
+                                    ilog2(sc->device->sector_size) * 8;
        }
 
        if (hdr->flags & ISCSI_FLAG_CMD_READ) {
-               err = iser_prepare_read_cmd(task, edtl);
+               err = iser_prepare_read_cmd(task);
                if (err)
                        goto send_command_error;
        }
index b9d647468b99e66ed44c4a1176744ef5eefdbc82..d4c7928a0f36143844f3ca0b4735f9c22a1c148a 100644 (file)
@@ -663,8 +663,9 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
 
        pi_support = np->tpg_np->tpg->tpg_attrib.t10_pi;
        if (pi_support && !device->pi_capable) {
-               pr_err("Protection information requested but not supported\n");
-               ret = -EINVAL;
+               pr_err("Protection information requested but not supported, "
+                      "rejecting connect request\n");
+               ret = rdma_reject(cma_id, NULL, 0);
                goto out_mr;
        }
 
@@ -787,14 +788,12 @@ isert_disconnect_work(struct work_struct *work)
                isert_put_conn(isert_conn);
                return;
        }
-       if (!isert_conn->logout_posted) {
-               pr_debug("Calling rdma_disconnect for !logout_posted from"
-                        " isert_disconnect_work\n");
+
+       if (isert_conn->disconnect) {
+               /* Send DREQ/DREP towards our initiator */
                rdma_disconnect(isert_conn->conn_cm_id);
-               mutex_unlock(&isert_conn->conn_mutex);
-               iscsit_cause_connection_reinstatement(isert_conn->conn, 0);
-               goto wake_up;
        }
+
        mutex_unlock(&isert_conn->conn_mutex);
 
 wake_up:
@@ -803,10 +802,11 @@ isert_disconnect_work(struct work_struct *work)
 }
 
 static void
-isert_disconnected_handler(struct rdma_cm_id *cma_id)
+isert_disconnected_handler(struct rdma_cm_id *cma_id, bool disconnect)
 {
        struct isert_conn *isert_conn = (struct isert_conn *)cma_id->context;
 
+       isert_conn->disconnect = disconnect;
        INIT_WORK(&isert_conn->conn_logout_work, isert_disconnect_work);
        schedule_work(&isert_conn->conn_logout_work);
 }
@@ -815,29 +815,28 @@ static int
 isert_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
 {
        int ret = 0;
+       bool disconnect = false;
 
        pr_debug("isert_cma_handler: event %d status %d conn %p id %p\n",
                 event->event, event->status, cma_id->context, cma_id);
 
        switch (event->event) {
        case RDMA_CM_EVENT_CONNECT_REQUEST:
-               pr_debug("RDMA_CM_EVENT_CONNECT_REQUEST: >>>>>>>>>>>>>>>\n");
                ret = isert_connect_request(cma_id, event);
                break;
        case RDMA_CM_EVENT_ESTABLISHED:
-               pr_debug("RDMA_CM_EVENT_ESTABLISHED >>>>>>>>>>>>>>\n");
                isert_connected_handler(cma_id);
                break;
-       case RDMA_CM_EVENT_DISCONNECTED:
-               pr_debug("RDMA_CM_EVENT_DISCONNECTED: >>>>>>>>>>>>>>\n");
-               isert_disconnected_handler(cma_id);
-               break;
-       case RDMA_CM_EVENT_DEVICE_REMOVAL:
-       case RDMA_CM_EVENT_ADDR_CHANGE:
+       case RDMA_CM_EVENT_ADDR_CHANGE:    /* FALLTHRU */
+       case RDMA_CM_EVENT_DISCONNECTED:   /* FALLTHRU */
+       case RDMA_CM_EVENT_DEVICE_REMOVAL: /* FALLTHRU */
+               disconnect = true;
+       case RDMA_CM_EVENT_TIMEWAIT_EXIT:  /* FALLTHRU */
+               isert_disconnected_handler(cma_id, disconnect);
                break;
        case RDMA_CM_EVENT_CONNECT_ERROR:
        default:
-               pr_err("Unknown RDMA CMA event: %d\n", event->event);
+               pr_err("Unhandled RDMA CMA event: %d\n", event->event);
                break;
        }
 
@@ -1054,7 +1053,9 @@ isert_put_login_tx(struct iscsi_conn *conn, struct iscsi_login *login,
        }
        if (!login->login_failed) {
                if (login->login_complete) {
-                       if (isert_conn->conn_device->use_fastreg) {
+                       if (!conn->sess->sess_ops->SessionType &&
+                           isert_conn->conn_device->use_fastreg) {
+                               /* Normal Session and fastreg is used */
                                u8 pi_support = login->np->tpg_np->tpg->tpg_attrib.t10_pi;
 
                                ret = isert_conn_create_fastreg_pool(isert_conn,
@@ -1824,11 +1825,8 @@ isert_do_control_comp(struct work_struct *work)
                break;
        case ISTATE_SEND_LOGOUTRSP:
                pr_debug("Calling iscsit_logout_post_handler >>>>>>>>>>>>>>\n");
-               /*
-                * Call atomic_dec(&isert_conn->post_send_buf_count)
-                * from isert_wait_conn()
-                */
-               isert_conn->logout_posted = true;
+
+               atomic_dec(&isert_conn->post_send_buf_count);
                iscsit_logout_post_handler(cmd, cmd->conn);
                break;
        case ISTATE_SEND_TEXTRSP:
@@ -2034,6 +2032,8 @@ isert_cq_rx_comp_err(struct isert_conn *isert_conn)
        isert_conn->state = ISER_CONN_DOWN;
        mutex_unlock(&isert_conn->conn_mutex);
 
+       iscsit_cause_connection_reinstatement(isert_conn->conn, 0);
+
        complete(&isert_conn->conn_wait_comp_err);
 }
 
@@ -2320,7 +2320,7 @@ isert_put_text_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
        int rc;
 
        isert_create_send_desc(isert_conn, isert_cmd, &isert_cmd->tx_desc);
-       rc = iscsit_build_text_rsp(cmd, conn, hdr);
+       rc = iscsit_build_text_rsp(cmd, conn, hdr, ISCSI_INFINIBAND);
        if (rc < 0)
                return rc;
 
@@ -3156,9 +3156,14 @@ isert_accept_np(struct iscsi_np *np, struct iscsi_conn *conn)
                return -ENODEV;
 
        spin_lock_bh(&np->np_thread_lock);
-       if (np->np_thread_state == ISCSI_NP_THREAD_RESET) {
+       if (np->np_thread_state >= ISCSI_NP_THREAD_RESET) {
                spin_unlock_bh(&np->np_thread_lock);
-               pr_debug("ISCSI_NP_THREAD_RESET for isert_accept_np\n");
+               pr_debug("np_thread_state %d for isert_accept_np\n",
+                        np->np_thread_state);
+               /**
+                * No point in stalling here when np_thread
+                * is in state RESET/SHUTDOWN/EXIT - bail
+                **/
                return -ENODEV;
        }
        spin_unlock_bh(&np->np_thread_lock);
@@ -3208,15 +3213,9 @@ static void isert_wait_conn(struct iscsi_conn *conn)
        struct isert_conn *isert_conn = conn->context;
 
        pr_debug("isert_wait_conn: Starting \n");
-       /*
-        * Decrement post_send_buf_count for special case when called
-        * from isert_do_control_comp() -> iscsit_logout_post_handler()
-        */
-       mutex_lock(&isert_conn->conn_mutex);
-       if (isert_conn->logout_posted)
-               atomic_dec(&isert_conn->post_send_buf_count);
 
-       if (isert_conn->conn_cm_id && isert_conn->state != ISER_CONN_DOWN) {
+       mutex_lock(&isert_conn->conn_mutex);
+       if (isert_conn->conn_cm_id) {
                pr_debug("Calling rdma_disconnect from isert_wait_conn\n");
                rdma_disconnect(isert_conn->conn_cm_id);
        }
@@ -3293,6 +3292,7 @@ static int __init isert_init(void)
 
 static void __exit isert_exit(void)
 {
+       flush_scheduled_work();
        destroy_workqueue(isert_comp_wq);
        destroy_workqueue(isert_rx_wq);
        iscsit_unregister_transport(&iser_target_transport);
index da6612e6800004b0984880d54ef57bcaab1b0be1..04f51f7bf614735b47d782b2ffa97d417e25fcd5 100644 (file)
@@ -116,7 +116,6 @@ struct isert_device;
 
 struct isert_conn {
        enum iser_conn_state    state;
-       bool                    logout_posted;
        int                     post_recv_buf_count;
        atomic_t                post_send_buf_count;
        u32                     responder_resources;
@@ -151,6 +150,7 @@ struct isert_conn {
 #define ISERT_COMP_BATCH_COUNT 8
        int                     conn_comp_batch;
        struct llist_head       conn_comp_llist;
+       bool                    disconnect;
 };
 
 #define ISERT_MAX_CQ 64
index 9816c51eb5c240be8c19b9c26bbd25d50fbca652..7641b3096ea67627a9a77b708380f4c681ed0ab9 100644 (file)
@@ -1,11 +1,3 @@
-config ISDN_DRV_AVMB1_VERBOSE_REASON
-       bool "Verbose reason code reporting"
-       default y
-       help
-         If you say Y here, the CAPI drivers will give verbose reasons for
-         disconnecting. This will increase the size of the kernel by 7 KB. If
-         unsure, say Y.
-
 config CAPI_TRACE
        bool "CAPI trace support"
        default y
@@ -17,7 +9,7 @@ config CAPI_TRACE
          If unsure, say Y.
 
 config ISDN_CAPI_CAPI20
-       tristate "CAPI2.0 /dev/capi support"
+       tristate "CAPI2.0 /dev/capi20 support"
        help
          This option will provide the CAPI 2.0 interface to userspace
          applications via /dev/capi20. Applications should use the
@@ -42,3 +34,11 @@ config ISDN_CAPI_CAPIDRV
          the legacy isdn4linux link layer.  If you have a card which is
          supported by a CAPI driver, but still want to use old features like
          ippp interfaces or ttyI emulation, say Y/M here.
+
+config ISDN_CAPI_CAPIDRV_VERBOSE
+       bool "Verbose reason code reporting"
+       depends on ISDN_CAPI_CAPIDRV
+       help
+         If you say Y here, the capidrv interface will give verbose reasons
+         for disconnecting. This will increase the size of the kernel by 7 KB.
+         If unsure, say N.
index ac6f72b455d174fa97c317bce1475946de7a83eb..f9a87ed2392b893da0feb125723d9ab53db99e0f 100644 (file)
@@ -1271,7 +1271,7 @@ static int __init capinc_tty_init(void)
                return -ENOMEM;
        }
        drv->driver_name = "capi_nc";
-       drv->name = "capi";
+       drv->name = "capi!";
        drv->major = 0;
        drv->minor_start = 0;
        drv->type = TTY_DRIVER_TYPE_SERIAL;
@@ -1417,7 +1417,7 @@ static int __init capi_init(void)
                return PTR_ERR(capi_class);
        }
 
-       device_create(capi_class, NULL, MKDEV(capi_major, 0), NULL, "capi");
+       device_create(capi_class, NULL, MKDEV(capi_major, 0), NULL, "capi20");
 
        if (capinc_tty_init() < 0) {
                device_destroy(capi_class, MKDEV(capi_major, 0));
index cc9f1927a322ec9d8aa1a5a05cad351711d9f861..fd6d28f3fc36e9223e759a1c3aaffa8b2de84641 100644 (file)
@@ -763,6 +763,201 @@ static inline int new_bchan(capidrv_contr *card)
 }
 
 /* ------------------------------------------------------------------- */
+static char *capi_info2str(u16 reason)
+{
+#ifndef CONFIG_ISDN_CAPI_CAPIDRV_VERBOSE
+       return "..";
+#else
+       switch (reason) {
+
+/*-- informative values (corresponding message was processed) -----*/
+       case 0x0001:
+               return "NCPI not supported by current protocol, NCPI ignored";
+       case 0x0002:
+               return "Flags not supported by current protocol, flags ignored";
+       case 0x0003:
+               return "Alert already sent by another application";
+
+/*-- error information concerning CAPI_REGISTER -----*/
+       case 0x1001:
+               return "Too many applications";
+       case 0x1002:
+               return "Logical block size too small, must be at least 128 Bytes";
+       case 0x1003:
+               return "Buffer exceeds 64 kByte";
+       case 0x1004:
+               return "Message buffer size too small, must be at least 1024 Bytes";
+       case 0x1005:
+               return "Max. number of logical connections not supported";
+       case 0x1006:
+               return "Reserved";
+       case 0x1007:
+               return "The message could not be accepted because of an internal busy condition";
+       case 0x1008:
+               return "OS resource error (no memory ?)";
+       case 0x1009:
+               return "CAPI not installed";
+       case 0x100A:
+               return "Controller does not support external equipment";
+       case 0x100B:
+               return "Controller does only support external equipment";
+
+/*-- error information concerning message exchange functions -----*/
+       case 0x1101:
+               return "Illegal application number";
+       case 0x1102:
+               return "Illegal command or subcommand or message length less than 12 bytes";
+       case 0x1103:
+               return "The message could not be accepted because of a queue full condition !! The error code does not imply that CAPI cannot receive messages directed to another controller, PLCI or NCCI";
+       case 0x1104:
+               return "Queue is empty";
+       case 0x1105:
+               return "Queue overflow, a message was lost !! This indicates a configuration error. The only recovery from this error is to perform a CAPI_RELEASE";
+       case 0x1106:
+               return "Unknown notification parameter";
+       case 0x1107:
+               return "The Message could not be accepted because of an internal busy condition";
+       case 0x1108:
+               return "OS Resource error (no memory ?)";
+       case 0x1109:
+               return "CAPI not installed";
+       case 0x110A:
+               return "Controller does not support external equipment";
+       case 0x110B:
+               return "Controller does only support external equipment";
+
+/*-- error information concerning resource / coding problems -----*/
+       case 0x2001:
+               return "Message not supported in current state";
+       case 0x2002:
+               return "Illegal Controller / PLCI / NCCI";
+       case 0x2003:
+               return "Out of PLCI";
+       case 0x2004:
+               return "Out of NCCI";
+       case 0x2005:
+               return "Out of LISTEN";
+       case 0x2006:
+               return "Out of FAX resources (protocol T.30)";
+       case 0x2007:
+               return "Illegal message parameter coding";
+
+/*-- error information concerning requested services  -----*/
+       case 0x3001:
+               return "B1 protocol not supported";
+       case 0x3002:
+               return "B2 protocol not supported";
+       case 0x3003:
+               return "B3 protocol not supported";
+       case 0x3004:
+               return "B1 protocol parameter not supported";
+       case 0x3005:
+               return "B2 protocol parameter not supported";
+       case 0x3006:
+               return "B3 protocol parameter not supported";
+       case 0x3007:
+               return "B protocol combination not supported";
+       case 0x3008:
+               return "NCPI not supported";
+       case 0x3009:
+               return "CIP Value unknown";
+       case 0x300A:
+               return "Flags not supported (reserved bits)";
+       case 0x300B:
+               return "Facility not supported";
+       case 0x300C:
+               return "Data length not supported by current protocol";
+       case 0x300D:
+               return "Reset procedure not supported by current protocol";
+
+/*-- informations about the clearing of a physical connection -----*/
+       case 0x3301:
+               return "Protocol error layer 1 (broken line or B-channel removed by signalling protocol)";
+       case 0x3302:
+               return "Protocol error layer 2";
+       case 0x3303:
+               return "Protocol error layer 3";
+       case 0x3304:
+               return "Another application got that call";
+/*-- T.30 specific reasons -----*/
+       case 0x3311:
+               return "Connecting not successful (remote station is no FAX G3 machine)";
+       case 0x3312:
+               return "Connecting not successful (training error)";
+       case 0x3313:
+               return "Disconnected before transfer (remote station does not support transfer mode, e.g. resolution)";
+       case 0x3314:
+               return "Disconnected during transfer (remote abort)";
+       case 0x3315:
+               return "Disconnected during transfer (remote procedure error, e.g. unsuccessful repetition of T.30 commands)";
+       case 0x3316:
+               return "Disconnected during transfer (local tx data underrun)";
+       case 0x3317:
+               return "Disconnected during transfer (local rx data overflow)";
+       case 0x3318:
+               return "Disconnected during transfer (local abort)";
+       case 0x3319:
+               return "Illegal parameter coding (e.g. SFF coding error)";
+
+/*-- disconnect causes from the network according to ETS 300 102-1/Q.931 -----*/
+       case 0x3481: return "Unallocated (unassigned) number";
+       case 0x3482: return "No route to specified transit network";
+       case 0x3483: return "No route to destination";
+       case 0x3486: return "Channel unacceptable";
+       case 0x3487:
+               return "Call awarded and being delivered in an established channel";
+       case 0x3490: return "Normal call clearing";
+       case 0x3491: return "User busy";
+       case 0x3492: return "No user responding";
+       case 0x3493: return "No answer from user (user alerted)";
+       case 0x3495: return "Call rejected";
+       case 0x3496: return "Number changed";
+       case 0x349A: return "Non-selected user clearing";
+       case 0x349B: return "Destination out of order";
+       case 0x349C: return "Invalid number format";
+       case 0x349D: return "Facility rejected";
+       case 0x349E: return "Response to STATUS ENQUIRY";
+       case 0x349F: return "Normal, unspecified";
+       case 0x34A2: return "No circuit / channel available";
+       case 0x34A6: return "Network out of order";
+       case 0x34A9: return "Temporary failure";
+       case 0x34AA: return "Switching equipment congestion";
+       case 0x34AB: return "Access information discarded";
+       case 0x34AC: return "Requested circuit / channel not available";
+       case 0x34AF: return "Resources unavailable, unspecified";
+       case 0x34B1: return "Quality of service unavailable";
+       case 0x34B2: return "Requested facility not subscribed";
+       case 0x34B9: return "Bearer capability not authorized";
+       case 0x34BA: return "Bearer capability not presently available";
+       case 0x34BF: return "Service or option not available, unspecified";
+       case 0x34C1: return "Bearer capability not implemented";
+       case 0x34C2: return "Channel type not implemented";
+       case 0x34C5: return "Requested facility not implemented";
+       case 0x34C6: return "Only restricted digital information bearer capability is available";
+       case 0x34CF: return "Service or option not implemented, unspecified";
+       case 0x34D1: return "Invalid call reference value";
+       case 0x34D2: return "Identified channel does not exist";
+       case 0x34D3: return "A suspended call exists, but this call identity does not";
+       case 0x34D4: return "Call identity in use";
+       case 0x34D5: return "No call suspended";
+       case 0x34D6: return "Call having the requested call identity has been cleared";
+       case 0x34D8: return "Incompatible destination";
+       case 0x34DB: return "Invalid transit network selection";
+       case 0x34DF: return "Invalid message, unspecified";
+       case 0x34E0: return "Mandatory information element is missing";
+       case 0x34E1: return "Message type non-existent or not implemented";
+       case 0x34E2: return "Message not compatible with call state or message type non-existent or not implemented";
+       case 0x34E3: return "Information element non-existent or not implemented";
+       case 0x34E4: return "Invalid information element contents";
+       case 0x34E5: return "Message not compatible with call state";
+       case 0x34E6: return "Recovery on timer expiry";
+       case 0x34EF: return "Protocol error, unspecified";
+       case 0x34FF: return "Interworking, unspecified";
+
+       default: return "No additional information";
+       }
+#endif
+}
 
 static void handle_controller(_cmsg *cmsg)
 {
index d26f17033b6863af4550498df3812dca718326c1..6e797e502cfaf0fc2c2459de6e3d80885c145117 100644 (file)
 
 /* from CAPI2.0 DDK AVM Berlin GmbH */
 
-#ifndef CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON
-char *capi_info2str(u16 reason)
-{
-       return "..";
-}
-#else
-char *capi_info2str(u16 reason)
-{
-       switch (reason) {
-
-/*-- informative values (corresponding message was processed) -----*/
-       case 0x0001:
-               return "NCPI not supported by current protocol, NCPI ignored";
-       case 0x0002:
-               return "Flags not supported by current protocol, flags ignored";
-       case 0x0003:
-               return "Alert already sent by another application";
-
-/*-- error information concerning CAPI_REGISTER -----*/
-       case 0x1001:
-               return "Too many applications";
-       case 0x1002:
-               return "Logical block size too small, must be at least 128 Bytes";
-       case 0x1003:
-               return "Buffer exceeds 64 kByte";
-       case 0x1004:
-               return "Message buffer size too small, must be at least 1024 Bytes";
-       case 0x1005:
-               return "Max. number of logical connections not supported";
-       case 0x1006:
-               return "Reserved";
-       case 0x1007:
-               return "The message could not be accepted because of an internal busy condition";
-       case 0x1008:
-               return "OS resource error (no memory ?)";
-       case 0x1009:
-               return "CAPI not installed";
-       case 0x100A:
-               return "Controller does not support external equipment";
-       case 0x100B:
-               return "Controller does only support external equipment";
-
-/*-- error information concerning message exchange functions -----*/
-       case 0x1101:
-               return "Illegal application number";
-       case 0x1102:
-               return "Illegal command or subcommand or message length less than 12 bytes";
-       case 0x1103:
-               return "The message could not be accepted because of a queue full condition !! The error code does not imply that CAPI cannot receive messages directed to another controller, PLCI or NCCI";
-       case 0x1104:
-               return "Queue is empty";
-       case 0x1105:
-               return "Queue overflow, a message was lost !! This indicates a configuration error. The only recovery from this error is to perform a CAPI_RELEASE";
-       case 0x1106:
-               return "Unknown notification parameter";
-       case 0x1107:
-               return "The Message could not be accepted because of an internal busy condition";
-       case 0x1108:
-               return "OS Resource error (no memory ?)";
-       case 0x1109:
-               return "CAPI not installed";
-       case 0x110A:
-               return "Controller does not support external equipment";
-       case 0x110B:
-               return "Controller does only support external equipment";
-
-/*-- error information concerning resource / coding problems -----*/
-       case 0x2001:
-               return "Message not supported in current state";
-       case 0x2002:
-               return "Illegal Controller / PLCI / NCCI";
-       case 0x2003:
-               return "Out of PLCI";
-       case 0x2004:
-               return "Out of NCCI";
-       case 0x2005:
-               return "Out of LISTEN";
-       case 0x2006:
-               return "Out of FAX resources (protocol T.30)";
-       case 0x2007:
-               return "Illegal message parameter coding";
-
-/*-- error information concerning requested services  -----*/
-       case 0x3001:
-               return "B1 protocol not supported";
-       case 0x3002:
-               return "B2 protocol not supported";
-       case 0x3003:
-               return "B3 protocol not supported";
-       case 0x3004:
-               return "B1 protocol parameter not supported";
-       case 0x3005:
-               return "B2 protocol parameter not supported";
-       case 0x3006:
-               return "B3 protocol parameter not supported";
-       case 0x3007:
-               return "B protocol combination not supported";
-       case 0x3008:
-               return "NCPI not supported";
-       case 0x3009:
-               return "CIP Value unknown";
-       case 0x300A:
-               return "Flags not supported (reserved bits)";
-       case 0x300B:
-               return "Facility not supported";
-       case 0x300C:
-               return "Data length not supported by current protocol";
-       case 0x300D:
-               return "Reset procedure not supported by current protocol";
-
-/*-- informations about the clearing of a physical connection -----*/
-       case 0x3301:
-               return "Protocol error layer 1 (broken line or B-channel removed by signalling protocol)";
-       case 0x3302:
-               return "Protocol error layer 2";
-       case 0x3303:
-               return "Protocol error layer 3";
-       case 0x3304:
-               return "Another application got that call";
-/*-- T.30 specific reasons -----*/
-       case 0x3311:
-               return "Connecting not successful (remote station is no FAX G3 machine)";
-       case 0x3312:
-               return "Connecting not successful (training error)";
-       case 0x3313:
-               return "Disconnected before transfer (remote station does not support transfer mode, e.g. resolution)";
-       case 0x3314:
-               return "Disconnected during transfer (remote abort)";
-       case 0x3315:
-               return "Disconnected during transfer (remote procedure error, e.g. unsuccessful repetition of T.30 commands)";
-       case 0x3316:
-               return "Disconnected during transfer (local tx data underrun)";
-       case 0x3317:
-               return "Disconnected during transfer (local rx data overflow)";
-       case 0x3318:
-               return "Disconnected during transfer (local abort)";
-       case 0x3319:
-               return "Illegal parameter coding (e.g. SFF coding error)";
-
-/*-- disconnect causes from the network according to ETS 300 102-1/Q.931 -----*/
-       case 0x3481: return "Unallocated (unassigned) number";
-       case 0x3482: return "No route to specified transit network";
-       case 0x3483: return "No route to destination";
-       case 0x3486: return "Channel unacceptable";
-       case 0x3487:
-               return "Call awarded and being delivered in an established channel";
-       case 0x3490: return "Normal call clearing";
-       case 0x3491: return "User busy";
-       case 0x3492: return "No user responding";
-       case 0x3493: return "No answer from user (user alerted)";
-       case 0x3495: return "Call rejected";
-       case 0x3496: return "Number changed";
-       case 0x349A: return "Non-selected user clearing";
-       case 0x349B: return "Destination out of order";
-       case 0x349C: return "Invalid number format";
-       case 0x349D: return "Facility rejected";
-       case 0x349E: return "Response to STATUS ENQUIRY";
-       case 0x349F: return "Normal, unspecified";
-       case 0x34A2: return "No circuit / channel available";
-       case 0x34A6: return "Network out of order";
-       case 0x34A9: return "Temporary failure";
-       case 0x34AA: return "Switching equipment congestion";
-       case 0x34AB: return "Access information discarded";
-       case 0x34AC: return "Requested circuit / channel not available";
-       case 0x34AF: return "Resources unavailable, unspecified";
-       case 0x34B1: return "Quality of service unavailable";
-       case 0x34B2: return "Requested facility not subscribed";
-       case 0x34B9: return "Bearer capability not authorized";
-       case 0x34BA: return "Bearer capability not presently available";
-       case 0x34BF: return "Service or option not available, unspecified";
-       case 0x34C1: return "Bearer capability not implemented";
-       case 0x34C2: return "Channel type not implemented";
-       case 0x34C5: return "Requested facility not implemented";
-       case 0x34C6: return "Only restricted digital information bearer capability is available";
-       case 0x34CF: return "Service or option not implemented, unspecified";
-       case 0x34D1: return "Invalid call reference value";
-       case 0x34D2: return "Identified channel does not exist";
-       case 0x34D3: return "A suspended call exists, but this call identity does not";
-       case 0x34D4: return "Call identity in use";
-       case 0x34D5: return "No call suspended";
-       case 0x34D6: return "Call having the requested call identity has been cleared";
-       case 0x34D8: return "Incompatible destination";
-       case 0x34DB: return "Invalid transit network selection";
-       case 0x34DF: return "Invalid message, unspecified";
-       case 0x34E0: return "Mandatory information element is missing";
-       case 0x34E1: return "Message type non-existent or not implemented";
-       case 0x34E2: return "Message not compatible with call state or message type non-existent or not implemented";
-       case 0x34E3: return "Information element non-existent or not implemented";
-       case 0x34E4: return "Invalid information element contents";
-       case 0x34E5: return "Message not compatible with call state";
-       case 0x34E6: return "Recovery on timer expiry";
-       case 0x34EF: return "Protocol error, unspecified";
-       case 0x34FF: return "Interworking, unspecified";
-
-       default: return "No additional information";
-       }
-}
-#endif
-
 typedef struct {
        int typ;
        size_t off;
@@ -1073,4 +874,3 @@ EXPORT_SYMBOL(capi_cmsg_header);
 EXPORT_SYMBOL(capi_cmd2str);
 EXPORT_SYMBOL(capi_cmsg2str);
 EXPORT_SYMBOL(capi_message2str);
-EXPORT_SYMBOL(capi_info2str);
index 414dbf6da89afd5890c73785cbcb41f3ab903e54..fc9f9d03fa13b879ba382d079cb35a1108b19d0d 100644 (file)
@@ -197,25 +197,6 @@ typedef struct _hfc4s8s_hw {
 
 
 
-/***************************/
-/* inline function defines */
-/***************************/
-#ifdef HISAX_HFC4S8S_PCIMEM    /* inline functions memory mapped */
-
-/* memory write and dummy IO read to avoid PCI byte merge problems */
-#define Write_hfc8(a, b, c) {(*((volatile u_char *)(a->membase + b)) = c); inb(a->iobase + 4);}
-/* memory write without dummy IO access for fifo data access */
-#define fWrite_hfc8(a, b, c) (*((volatile u_char *)(a->membase + b)) = c)
-#define Read_hfc8(a, b) (*((volatile u_char *)(a->membase + b)))
-#define Write_hfc16(a, b, c) (*((volatile unsigned short *)(a->membase + b)) = c)
-#define Read_hfc16(a, b) (*((volatile unsigned short *)(a->membase + b)))
-#define Write_hfc32(a, b, c) (*((volatile unsigned long *)(a->membase + b)) = c)
-#define Read_hfc32(a, b) (*((volatile unsigned long *)(a->membase + b)))
-#define wait_busy(a) {while ((Read_hfc8(a, R_STATUS) & M_BUSY));}
-#define PCI_ENA_MEMIO  0x03
-
-#else
-
 /* inline functions io mapped */
 static inline void
 SetRegAddr(hfc4s8s_hw *a, u_char b)
@@ -306,8 +287,6 @@ wait_busy(hfc4s8s_hw *a)
 
 #define PCI_ENA_REGIO  0x01
 
-#endif                         /* HISAX_HFC4S8S_PCIMEM */
-
 /******************************************************/
 /* function to read critical counter registers that   */
 /* may be updated by the chip during read             */
@@ -724,26 +703,15 @@ rx_d_frame(struct hfc4s8s_l1 *l1p, int ech)
                                return;
                        } else {
                                /* read errornous D frame */
-
-#ifndef HISAX_HFC4S8S_PCIMEM
                                SetRegAddr(l1p->hw, A_FIFO_DATA0);
-#endif
 
                                while (z1 >= 4) {
-#ifdef HISAX_HFC4S8S_PCIMEM
-                                       Read_hfc32(l1p->hw, A_FIFO_DATA0);
-#else
                                        fRead_hfc32(l1p->hw);
-#endif
                                        z1 -= 4;
                                }
 
                                while (z1--)
-#ifdef HISAX_HFC4S8S_PCIMEM
-                                       Read_hfc8(l1p->hw, A_FIFO_DATA0);
-#else
-                               fRead_hfc8(l1p->hw);
-#endif
+                                       fRead_hfc8(l1p->hw);
 
                                Write_hfc8(l1p->hw, A_INC_RES_FIFO, 1);
                                wait_busy(l1p->hw);
@@ -753,27 +721,16 @@ rx_d_frame(struct hfc4s8s_l1 *l1p, int ech)
 
                cp = skb->data;
 
-#ifndef HISAX_HFC4S8S_PCIMEM
                SetRegAddr(l1p->hw, A_FIFO_DATA0);
-#endif
 
                while (z1 >= 4) {
-#ifdef HISAX_HFC4S8S_PCIMEM
-                       *((unsigned long *) cp) =
-                               Read_hfc32(l1p->hw, A_FIFO_DATA0);
-#else
                        *((unsigned long *) cp) = fRead_hfc32(l1p->hw);
-#endif
                        cp += 4;
                        z1 -= 4;
                }
 
                while (z1--)
-#ifdef HISAX_HFC4S8S_PCIMEM
-                       *cp++ = Read_hfc8(l1p->hw, A_FIFO_DATA0);
-#else
-               *cp++ = fRead_hfc8(l1p->hw);
-#endif
+                       *cp++ = fRead_hfc8(l1p->hw);
 
                Write_hfc8(l1p->hw, A_INC_RES_FIFO, 1); /* increment f counter */
                wait_busy(l1p->hw);
@@ -859,28 +816,17 @@ rx_b_frame(struct hfc4s8s_btype *bch)
                        wait_busy(l1->hw);
                        return;
                }
-#ifndef HISAX_HFC4S8S_PCIMEM
                SetRegAddr(l1->hw, A_FIFO_DATA0);
-#endif
 
                while (z1 >= 4) {
-#ifdef HISAX_HFC4S8S_PCIMEM
-                       *((unsigned long *) bch->rx_ptr) =
-                               Read_hfc32(l1->hw, A_FIFO_DATA0);
-#else
                        *((unsigned long *) bch->rx_ptr) =
                                fRead_hfc32(l1->hw);
-#endif
                        bch->rx_ptr += 4;
                        z1 -= 4;
                }
 
                while (z1--)
-#ifdef HISAX_HFC4S8S_PCIMEM
-                       *(bch->rx_ptr++) = Read_hfc8(l1->hw, A_FIFO_DATA0);
-#else
-               *(bch->rx_ptr++) = fRead_hfc8(l1->hw);
-#endif
+                       *(bch->rx_ptr++) = fRead_hfc8(l1->hw);
 
                if (hdlc_complete) {
                        /* increment f counter */
@@ -940,29 +886,17 @@ tx_d_frame(struct hfc4s8s_l1 *l1p)
        if ((skb = skb_dequeue(&l1p->d_tx_queue))) {
                cp = skb->data;
                cnt = skb->len;
-#ifndef HISAX_HFC4S8S_PCIMEM
                SetRegAddr(l1p->hw, A_FIFO_DATA0);
-#endif
 
                while (cnt >= 4) {
-#ifdef HISAX_HFC4S8S_PCIMEM
-                       fWrite_hfc32(l1p->hw, A_FIFO_DATA0,
-                                    *(unsigned long *) cp);
-#else
                        SetRegAddr(l1p->hw, A_FIFO_DATA0);
                        fWrite_hfc32(l1p->hw, *(unsigned long *) cp);
-#endif
                        cp += 4;
                        cnt -= 4;
                }
 
-#ifdef HISAX_HFC4S8S_PCIMEM
-               while (cnt--)
-                       fWrite_hfc8(l1p->hw, A_FIFO_DATA0, *cp++);
-#else
                while (cnt--)
                        fWrite_hfc8(l1p->hw, *cp++);
-#endif
 
                l1p->tx_cnt = skb->truesize;
                Write_hfc8(l1p->hw, A_INC_RES_FIFO, 1); /* increment f counter */
@@ -1037,26 +971,15 @@ tx_b_frame(struct hfc4s8s_btype *bch)
                cp = skb->data + bch->tx_cnt;
                bch->tx_cnt += cnt;
 
-#ifndef HISAX_HFC4S8S_PCIMEM
                SetRegAddr(l1->hw, A_FIFO_DATA0);
-#endif
                while (cnt >= 4) {
-#ifdef HISAX_HFC4S8S_PCIMEM
-                       fWrite_hfc32(l1->hw, A_FIFO_DATA0,
-                                    *(unsigned long *) cp);
-#else
                        fWrite_hfc32(l1->hw, *(unsigned long *) cp);
-#endif
                        cp += 4;
                        cnt -= 4;
                }
 
                while (cnt--)
-#ifdef HISAX_HFC4S8S_PCIMEM
-                       fWrite_hfc8(l1->hw, A_FIFO_DATA0, *cp++);
-#else
-               fWrite_hfc8(l1->hw, *cp++);
-#endif
+                       fWrite_hfc8(l1->hw, *cp++);
 
                if (bch->tx_cnt >= skb->len) {
                        if (bch->mode == L1_MODE_HDLC) {
@@ -1281,10 +1204,8 @@ hfc4s8s_interrupt(int intno, void *dev_id)
        if (!hw || !(hw->mr.r_irq_ctrl & M_GLOB_IRQ_EN))
                return IRQ_NONE;
 
-#ifndef        HISAX_HFC4S8S_PCIMEM
        /* read current selected regsister */
        old_ioreg = GetRegAddr(hw);
-#endif
 
        /* Layer 1 State change */
        hw->mr.r_irq_statech |=
@@ -1292,9 +1213,7 @@ hfc4s8s_interrupt(int intno, void *dev_id)
        if (!
            (b = (Read_hfc8(hw, R_STATUS) & (M_MISC_IRQSTA | M_FR_IRQSTA)))
            && !hw->mr.r_irq_statech) {
-#ifndef        HISAX_HFC4S8S_PCIMEM
                SetRegAddr(hw, old_ioreg);
-#endif
                return IRQ_NONE;
        }
 
@@ -1322,9 +1241,7 @@ hfc4s8s_interrupt(int intno, void *dev_id)
        /* queue the request to allow other cards to interrupt */
        schedule_work(&hw->tqueue);
 
-#ifndef        HISAX_HFC4S8S_PCIMEM
        SetRegAddr(hw, old_ioreg);
-#endif
        return IRQ_HANDLED;
 }                              /* hfc4s8s_interrupt */
 
@@ -1471,13 +1388,8 @@ static void
 release_pci_ports(hfc4s8s_hw *hw)
 {
        pci_write_config_word(hw->pdev, PCI_COMMAND, 0);
-#ifdef HISAX_HFC4S8S_PCIMEM
-       if (hw->membase)
-               iounmap((void *) hw->membase);
-#else
        if (hw->iobase)
                release_region(hw->iobase, 8);
-#endif
 }
 
 /*****************************************/
@@ -1486,11 +1398,7 @@ release_pci_ports(hfc4s8s_hw *hw)
 static void
 enable_pci_ports(hfc4s8s_hw *hw)
 {
-#ifdef HISAX_HFC4S8S_PCIMEM
-       pci_write_config_word(hw->pdev, PCI_COMMAND, PCI_ENA_MEMIO);
-#else
        pci_write_config_word(hw->pdev, PCI_COMMAND, PCI_ENA_REGIO);
-#endif
 }
 
 /*************************************/
@@ -1561,15 +1469,9 @@ setup_instance(hfc4s8s_hw *hw)
                       hw->irq);
                goto out;
        }
-#ifdef HISAX_HFC4S8S_PCIMEM
-       printk(KERN_INFO
-              "HFC-4S/8S: found PCI card at membase 0x%p, irq %d\n",
-              hw->hw_membase, hw->irq);
-#else
        printk(KERN_INFO
               "HFC-4S/8S: found PCI card at iobase 0x%x, irq %d\n",
               hw->iobase, hw->irq);
-#endif
 
        hfc_hardware_enable(hw, 1, 0);
 
@@ -1614,17 +1516,12 @@ hfc4s8s_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        hw->irq = pdev->irq;
        hw->iobase = pci_resource_start(pdev, 0);
 
-#ifdef HISAX_HFC4S8S_PCIMEM
-       hw->hw_membase = (u_char *) pci_resource_start(pdev, 1);
-       hw->membase = ioremap((ulong) hw->hw_membase, 256);
-#else
        if (!request_region(hw->iobase, 8, hw->card_name)) {
                printk(KERN_INFO
                       "HFC-4S/8S: failed to request address space at 0x%04x\n",
                       hw->iobase);
                goto out;
        }
-#endif
 
        pci_set_drvdata(pdev, hw);
        err = setup_instance(hw);
index a5da511e3c9ae4f381ae1526d2ab2779039aba9a..61ac6323744602ff27a95b56042c19d5d4e527e1 100644 (file)
@@ -634,7 +634,7 @@ isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg)
 #ifdef CONFIG_IPPP_FILTER
        case PPPIOCSPASS:
        {
-               struct sock_fprog fprog;
+               struct sock_fprog_kern fprog;
                struct sock_filter *code;
                int err, len = get_filter(argp, &code);
 
@@ -653,7 +653,7 @@ isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg)
        }
        case PPPIOCSACTIVE:
        {
-               struct sock_fprog fprog;
+               struct sock_fprog_kern fprog;
                struct sock_filter *code;
                int err, len = get_filter(argp, &code);
 
index 2c0d2c2bf94648e273b7614a65e1bc09976663e8..9f454d76cc060984317e31310b60e34080e0c6c5 100644 (file)
@@ -287,11 +287,9 @@ l1oip_socket_send(struct l1oip *hc, u8 localcodec, u8 channel, u32 chanmask,
        p = frame;
 
        /* restart timer */
-       if ((int)(hc->keep_tl.expires-jiffies) < 5 * HZ) {
-               del_timer(&hc->keep_tl);
-               hc->keep_tl.expires = jiffies + L1OIP_KEEPALIVE * HZ;
-               add_timer(&hc->keep_tl);
-       } else
+       if (time_before(hc->keep_tl.expires, jiffies + 5 * HZ))
+               mod_timer(&hc->keep_tl, jiffies + L1OIP_KEEPALIVE * HZ);
+       else
                hc->keep_tl.expires = jiffies + L1OIP_KEEPALIVE * HZ;
 
        if (debug & DEBUG_L1OIP_MSG)
@@ -621,11 +619,9 @@ l1oip_socket_parse(struct l1oip *hc, struct sockaddr_in *sin, u8 *buf, int len)
                goto multiframe;
 
        /* restart timer */
-       if ((int)(hc->timeout_tl.expires-jiffies) < 5 * HZ || !hc->timeout_on) {
+       if (time_before(hc->timeout_tl.expires, jiffies + 5 * HZ) || !hc->timeout_on) {
                hc->timeout_on = 1;
-               del_timer(&hc->timeout_tl);
-               hc->timeout_tl.expires = jiffies + L1OIP_TIMEOUT * HZ;
-               add_timer(&hc->timeout_tl);
+               mod_timer(&hc->timeout_tl, jiffies + L1OIP_TIMEOUT * HZ);
        } else /* only adjust timer */
                hc->timeout_tl.expires = jiffies + L1OIP_TIMEOUT * HZ;
 
index 20f1655e6d7595c328b5499476f4dde7773cccd0..8108c698b5483c9250ec532f33af815b453aae1e 100644 (file)
@@ -93,7 +93,9 @@ config VIDEO_M32R_AR_M64278
 
 config VIDEO_OMAP3
        tristate "OMAP 3 Camera support"
-       depends on OMAP_IOVMM && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3
+       depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3
+       select ARM_DMA_USE_IOMMU
+       select OMAP_IOMMU
        ---help---
          Driver for an OMAP 3 camera controller.
 
index e8847e79e31aefc236a279d0b93c2b1953131567..254975a9174ebdf73d253e3bb5e8d8abce46e2c9 100644 (file)
@@ -3,7 +3,7 @@
 ccflags-$(CONFIG_VIDEO_OMAP3_DEBUG) += -DDEBUG
 
 omap3-isp-objs += \
-       isp.o ispqueue.o ispvideo.o \
+       isp.o ispvideo.o \
        ispcsiphy.o ispccp2.o ispcsi2.o \
        ispccdc.o isppreview.o ispresizer.o \
        ispstat.o isph3a_aewb.o isph3a_af.o isphist.o
index 06a0df434249a7f9e658d5cae51960d6b4e965d3..2c7aa67205693f36287756296aaa6e8debf0bc1c 100644 (file)
@@ -69,6 +69,8 @@
 #include <linux/sched.h>
 #include <linux/vmalloc.h>
 
+#include <asm/dma-iommu.h>
+
 #include <media/v4l2-common.h>
 #include <media/v4l2-device.h>
 
@@ -1397,14 +1399,14 @@ int omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
        if (isp_pipeline_is_last(me)) {
                struct isp_video *video = pipe->output;
                unsigned long flags;
-               spin_lock_irqsave(&video->queue->irqlock, flags);
+               spin_lock_irqsave(&video->irqlock, flags);
                if (video->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) {
-                       spin_unlock_irqrestore(&video->queue->irqlock, flags);
+                       spin_unlock_irqrestore(&video->irqlock, flags);
                        atomic_set(stopping, 0);
                        smp_mb();
                        return 0;
                }
-               spin_unlock_irqrestore(&video->queue->irqlock, flags);
+               spin_unlock_irqrestore(&video->irqlock, flags);
                if (!wait_event_timeout(*wait, !atomic_read(stopping),
                                        msecs_to_jiffies(1000))) {
                        atomic_set(stopping, 0);
@@ -1625,7 +1627,7 @@ struct isp_device *omap3isp_get(struct isp_device *isp)
  * Decrement the reference count on the ISP. If the last reference is released,
  * power-down all submodules, disable clocks and free temporary buffers.
  */
-void omap3isp_put(struct isp_device *isp)
+static void __omap3isp_put(struct isp_device *isp, bool save_ctx)
 {
        if (isp == NULL)
                return;
@@ -1634,7 +1636,7 @@ void omap3isp_put(struct isp_device *isp)
        BUG_ON(isp->ref_count == 0);
        if (--isp->ref_count == 0) {
                isp_disable_interrupts(isp);
-               if (isp->domain) {
+               if (save_ctx) {
                        isp_save_ctx(isp);
                        isp->has_context = 1;
                }
@@ -1648,6 +1650,11 @@ void omap3isp_put(struct isp_device *isp)
        mutex_unlock(&isp->isp_mutex);
 }
 
+void omap3isp_put(struct isp_device *isp)
+{
+       __omap3isp_put(isp, true);
+}
+
 /* --------------------------------------------------------------------------
  * Platform device driver
  */
@@ -2120,6 +2127,61 @@ static int isp_initialize_modules(struct isp_device *isp)
        return ret;
 }
 
+static void isp_detach_iommu(struct isp_device *isp)
+{
+       arm_iommu_release_mapping(isp->mapping);
+       isp->mapping = NULL;
+       iommu_group_remove_device(isp->dev);
+}
+
+static int isp_attach_iommu(struct isp_device *isp)
+{
+       struct dma_iommu_mapping *mapping;
+       struct iommu_group *group;
+       int ret;
+
+       /* Create a device group and add the device to it. */
+       group = iommu_group_alloc();
+       if (IS_ERR(group)) {
+               dev_err(isp->dev, "failed to allocate IOMMU group\n");
+               return PTR_ERR(group);
+       }
+
+       ret = iommu_group_add_device(group, isp->dev);
+       iommu_group_put(group);
+
+       if (ret < 0) {
+               dev_err(isp->dev, "failed to add device to IPMMU group\n");
+               return ret;
+       }
+
+       /*
+        * Create the ARM mapping, used by the ARM DMA mapping core to allocate
+        * VAs. This will allocate a corresponding IOMMU domain.
+        */
+       mapping = arm_iommu_create_mapping(&platform_bus_type, SZ_1G, SZ_2G);
+       if (IS_ERR(mapping)) {
+               dev_err(isp->dev, "failed to create ARM IOMMU mapping\n");
+               ret = PTR_ERR(mapping);
+               goto error;
+       }
+
+       isp->mapping = mapping;
+
+       /* Attach the ARM VA mapping to the device. */
+       ret = arm_iommu_attach_device(isp->dev, mapping);
+       if (ret < 0) {
+               dev_err(isp->dev, "failed to attach device to VA mapping\n");
+               goto error;
+       }
+
+       return 0;
+
+error:
+       isp_detach_iommu(isp);
+       return ret;
+}
+
 /*
  * isp_remove - Remove ISP platform device
  * @pdev: Pointer to ISP platform device
@@ -2135,10 +2197,8 @@ static int isp_remove(struct platform_device *pdev)
        isp_xclk_cleanup(isp);
 
        __omap3isp_get(isp, false);
-       iommu_detach_device(isp->domain, &pdev->dev);
-       iommu_domain_free(isp->domain);
-       isp->domain = NULL;
-       omap3isp_put(isp);
+       isp_detach_iommu(isp);
+       __omap3isp_put(isp, false);
 
        return 0;
 }
@@ -2265,39 +2325,32 @@ static int isp_probe(struct platform_device *pdev)
                }
        }
 
-       isp->domain = iommu_domain_alloc(pdev->dev.bus);
-       if (!isp->domain) {
-               dev_err(isp->dev, "can't alloc iommu domain\n");
-               ret = -ENOMEM;
+       /* IOMMU */
+       ret = isp_attach_iommu(isp);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "unable to attach to IOMMU\n");
                goto error_isp;
        }
 
-       ret = iommu_attach_device(isp->domain, &pdev->dev);
-       if (ret) {
-               dev_err(&pdev->dev, "can't attach iommu device: %d\n", ret);
-               ret = -EPROBE_DEFER;
-               goto free_domain;
-       }
-
        /* Interrupt */
        isp->irq_num = platform_get_irq(pdev, 0);
        if (isp->irq_num <= 0) {
                dev_err(isp->dev, "No IRQ resource\n");
                ret = -ENODEV;
-               goto detach_dev;
+               goto error_iommu;
        }
 
        if (devm_request_irq(isp->dev, isp->irq_num, isp_isr, IRQF_SHARED,
                             "OMAP3 ISP", isp)) {
                dev_err(isp->dev, "Unable to request IRQ\n");
                ret = -EINVAL;
-               goto detach_dev;
+               goto error_iommu;
        }
 
        /* Entities */
        ret = isp_initialize_modules(isp);
        if (ret < 0)
-               goto detach_dev;
+               goto error_iommu;
 
        ret = isp_register_entities(isp);
        if (ret < 0)
@@ -2310,14 +2363,11 @@ static int isp_probe(struct platform_device *pdev)
 
 error_modules:
        isp_cleanup_modules(isp);
-detach_dev:
-       iommu_detach_device(isp->domain, &pdev->dev);
-free_domain:
-       iommu_domain_free(isp->domain);
-       isp->domain = NULL;
+error_iommu:
+       isp_detach_iommu(isp);
 error_isp:
        isp_xclk_cleanup(isp);
-       omap3isp_put(isp);
+       __omap3isp_put(isp, false);
 error:
        mutex_destroy(&isp->isp_mutex);
 
index 6d5e69711907e655175ffe943fa5e2602582b556..2c314eea125275942db5076c34ac4613d4d02537 100644 (file)
@@ -45,8 +45,6 @@
 #include "ispcsi2.h"
 #include "ispccp2.h"
 
-#define IOMMU_FLAG (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8)
-
 #define ISP_TOK_TERM           0xFFFFFFFF      /*
                                                 * terminating token for ISP
                                                 * modules reg list
@@ -152,6 +150,7 @@ struct isp_xclk {
  *             regions.
  * @mmio_base_phys: Array with physical L4 bus addresses for ISP register
  *                  regions.
+ * @mapping: IOMMU mapping
  * @stat_lock: Spinlock for handling statistics
  * @isp_mutex: Mutex for serializing requests to ISP.
  * @stop_failure: Indicates that an entity failed to stop.
@@ -171,7 +170,6 @@ struct isp_xclk {
  * @isp_res: Pointer to current settings for ISP Resizer.
  * @isp_prev: Pointer to current settings for ISP Preview.
  * @isp_ccdc: Pointer to current settings for ISP CCDC.
- * @iommu: Pointer to requested IOMMU instance for ISP.
  * @platform_cb: ISP driver callback function pointers for platform code
  *
  * This structure is used to store the OMAP ISP Information.
@@ -189,6 +187,8 @@ struct isp_device {
        void __iomem *mmio_base[OMAP3_ISP_IOMEM_LAST];
        unsigned long mmio_base_phys[OMAP3_ISP_IOMEM_LAST];
 
+       struct dma_iommu_mapping *mapping;
+
        /* ISP Obj */
        spinlock_t stat_lock;   /* common lock for statistic drivers */
        struct mutex isp_mutex; /* For handling ref_count field */
@@ -219,8 +219,6 @@ struct isp_device {
 
        unsigned int sbl_resources;
        unsigned int subclk_resources;
-
-       struct iommu_domain *domain;
 };
 
 #define v4l2_dev_to_isp_device(dev) \
index 4d920c800ff5fc1e863199b332acadb57294210b..9f727d20f06d98298dcf36a4e0a9e8390ad2d9da 100644 (file)
@@ -30,7 +30,6 @@
 #include <linux/device.h>
 #include <linux/dma-mapping.h>
 #include <linux/mm.h>
-#include <linux/omap-iommu.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <media/v4l2-event.h>
@@ -206,7 +205,8 @@ static int ccdc_lsc_validate_config(struct isp_ccdc_device *ccdc,
  * ccdc_lsc_program_table - Program Lens Shading Compensation table address.
  * @ccdc: Pointer to ISP CCDC device.
  */
-static void ccdc_lsc_program_table(struct isp_ccdc_device *ccdc, u32 addr)
+static void ccdc_lsc_program_table(struct isp_ccdc_device *ccdc,
+                                  dma_addr_t addr)
 {
        isp_reg_writel(to_isp_device(ccdc), addr,
                       OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_TABLE_BASE);
@@ -333,7 +333,7 @@ static int __ccdc_lsc_configure(struct isp_ccdc_device *ccdc,
                return -EBUSY;
 
        ccdc_lsc_setup_regs(ccdc, &req->config);
-       ccdc_lsc_program_table(ccdc, req->table);
+       ccdc_lsc_program_table(ccdc, req->table.dma);
        return 0;
 }
 
@@ -368,11 +368,12 @@ static void ccdc_lsc_free_request(struct isp_ccdc_device *ccdc,
        if (req == NULL)
                return;
 
-       if (req->iovm)
-               dma_unmap_sg(isp->dev, req->iovm->sgt->sgl,
-                            req->iovm->sgt->nents, DMA_TO_DEVICE);
-       if (req->table)
-               omap_iommu_vfree(isp->domain, isp->dev, req->table);
+       if (req->table.addr) {
+               sg_free_table(&req->table.sgt);
+               dma_free_coherent(isp->dev, req->config.size, req->table.addr,
+                                 req->table.dma);
+       }
+
        kfree(req);
 }
 
@@ -416,7 +417,6 @@ static int ccdc_lsc_config(struct isp_ccdc_device *ccdc,
        struct isp_device *isp = to_isp_device(ccdc);
        struct ispccdc_lsc_config_req *req;
        unsigned long flags;
-       void *table;
        u16 update;
        int ret;
 
@@ -444,38 +444,31 @@ static int ccdc_lsc_config(struct isp_ccdc_device *ccdc,
 
                req->enable = 1;
 
-               req->table = omap_iommu_vmalloc(isp->domain, isp->dev, 0,
-                                       req->config.size, IOMMU_FLAG);
-               if (IS_ERR_VALUE(req->table)) {
-                       req->table = 0;
-                       ret = -ENOMEM;
-                       goto done;
-               }
-
-               req->iovm = omap_find_iovm_area(isp->dev, req->table);
-               if (req->iovm == NULL) {
+               req->table.addr = dma_alloc_coherent(isp->dev, req->config.size,
+                                                    &req->table.dma,
+                                                    GFP_KERNEL);
+               if (req->table.addr == NULL) {
                        ret = -ENOMEM;
                        goto done;
                }
 
-               if (!dma_map_sg(isp->dev, req->iovm->sgt->sgl,
-                               req->iovm->sgt->nents, DMA_TO_DEVICE)) {
-                       ret = -ENOMEM;
-                       req->iovm = NULL;
+               ret = dma_get_sgtable(isp->dev, &req->table.sgt,
+                                     req->table.addr, req->table.dma,
+                                     req->config.size);
+               if (ret < 0)
                        goto done;
-               }
 
-               dma_sync_sg_for_cpu(isp->dev, req->iovm->sgt->sgl,
-                                   req->iovm->sgt->nents, DMA_TO_DEVICE);
+               dma_sync_sg_for_cpu(isp->dev, req->table.sgt.sgl,
+                                   req->table.sgt.nents, DMA_TO_DEVICE);
 
-               table = omap_da_to_va(isp->dev, req->table);
-               if (copy_from_user(table, config->lsc, req->config.size)) {
+               if (copy_from_user(req->table.addr, config->lsc,
+                                  req->config.size)) {
                        ret = -EFAULT;
                        goto done;
                }
 
-               dma_sync_sg_for_device(isp->dev, req->iovm->sgt->sgl,
-                                      req->iovm->sgt->nents, DMA_TO_DEVICE);
+               dma_sync_sg_for_device(isp->dev, req->table.sgt.sgl,
+                                      req->table.sgt.nents, DMA_TO_DEVICE);
        }
 
        spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
@@ -584,7 +577,7 @@ static void ccdc_configure_fpc(struct isp_ccdc_device *ccdc)
        if (!ccdc->fpc_en)
                return;
 
-       isp_reg_writel(isp, ccdc->fpc.fpcaddr, OMAP3_ISP_IOMEM_CCDC,
+       isp_reg_writel(isp, ccdc->fpc.dma, OMAP3_ISP_IOMEM_CCDC,
                       ISPCCDC_FPC_ADDR);
        /* The FPNUM field must be set before enabling FPC. */
        isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT),
@@ -724,8 +717,9 @@ static int ccdc_config(struct isp_ccdc_device *ccdc,
        ccdc->shadow_update = 0;
 
        if (OMAP3ISP_CCDC_FPC & ccdc_struct->update) {
-               u32 table_old = 0;
-               u32 table_new;
+               struct omap3isp_ccdc_fpc fpc;
+               struct ispccdc_fpc fpc_old = { .addr = NULL, };
+               struct ispccdc_fpc fpc_new;
                u32 size;
 
                if (ccdc->state != ISP_PIPELINE_STREAM_STOPPED)
@@ -734,35 +728,39 @@ static int ccdc_config(struct isp_ccdc_device *ccdc,
                ccdc->fpc_en = !!(OMAP3ISP_CCDC_FPC & ccdc_struct->flag);
 
                if (ccdc->fpc_en) {
-                       if (copy_from_user(&ccdc->fpc, ccdc_struct->fpc,
-                                          sizeof(ccdc->fpc)))
+                       if (copy_from_user(&fpc, ccdc_struct->fpc, sizeof(fpc)))
                                return -EFAULT;
 
+                       size = fpc.fpnum * 4;
+
                        /*
-                        * table_new must be 64-bytes aligned, but it's
-                        * already done by omap_iommu_vmalloc().
+                        * The table address must be 64-bytes aligned, which is
+                        * guaranteed by dma_alloc_coherent().
                         */
-                       size = ccdc->fpc.fpnum * 4;
-                       table_new = omap_iommu_vmalloc(isp->domain, isp->dev,
-                                                       0, size, IOMMU_FLAG);
-                       if (IS_ERR_VALUE(table_new))
+                       fpc_new.fpnum = fpc.fpnum;
+                       fpc_new.addr = dma_alloc_coherent(isp->dev, size,
+                                                         &fpc_new.dma,
+                                                         GFP_KERNEL);
+                       if (fpc_new.addr == NULL)
                                return -ENOMEM;
 
-                       if (copy_from_user(omap_da_to_va(isp->dev, table_new),
-                                          (__force void __user *)
-                                          ccdc->fpc.fpcaddr, size)) {
-                               omap_iommu_vfree(isp->domain, isp->dev,
-                                                               table_new);
+                       if (copy_from_user(fpc_new.addr,
+                                          (__force void __user *)fpc.fpcaddr,
+                                          size)) {
+                               dma_free_coherent(isp->dev, size, fpc_new.addr,
+                                                 fpc_new.dma);
                                return -EFAULT;
                        }
 
-                       table_old = ccdc->fpc.fpcaddr;
-                       ccdc->fpc.fpcaddr = table_new;
+                       fpc_old = ccdc->fpc;
+                       ccdc->fpc = fpc_new;
                }
 
                ccdc_configure_fpc(ccdc);
-               if (table_old != 0)
-                       omap_iommu_vfree(isp->domain, isp->dev, table_old);
+
+               if (fpc_old.addr != NULL)
+                       dma_free_coherent(isp->dev, fpc_old.fpnum * 4,
+                                         fpc_old.addr, fpc_old.dma);
        }
 
        return ccdc_lsc_config(ccdc, ccdc_struct);
@@ -1523,7 +1521,7 @@ static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc)
 
        buffer = omap3isp_video_buffer_next(&ccdc->video_out);
        if (buffer != NULL) {
-               ccdc_set_outaddr(ccdc, buffer->isp_addr);
+               ccdc_set_outaddr(ccdc, buffer->dma);
                restart = 1;
        }
 
@@ -1662,7 +1660,7 @@ static int ccdc_video_queue(struct isp_video *video, struct isp_buffer *buffer)
        if (!(ccdc->output & CCDC_OUTPUT_MEMORY))
                return -ENODEV;
 
-       ccdc_set_outaddr(ccdc, buffer->isp_addr);
+       ccdc_set_outaddr(ccdc, buffer->dma);
 
        /* We now have a buffer queued on the output, restart the pipeline
         * on the next CCDC interrupt if running in continuous mode (or when
@@ -2580,8 +2578,9 @@ void omap3isp_ccdc_cleanup(struct isp_device *isp)
        cancel_work_sync(&ccdc->lsc.table_work);
        ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue);
 
-       if (ccdc->fpc.fpcaddr != 0)
-               omap_iommu_vfree(isp->domain, isp->dev, ccdc->fpc.fpcaddr);
+       if (ccdc->fpc.addr != NULL)
+               dma_free_coherent(isp->dev, ccdc->fpc.fpnum * 4, ccdc->fpc.addr,
+                                 ccdc->fpc.dma);
 
        mutex_destroy(&ccdc->ioctl_lock);
 }
index 9d24e4107864fc4905cbc13f8b2787634a27a24c..f65061602c71e585695503d30c4d9f340af1a27a 100644 (file)
@@ -46,6 +46,12 @@ enum ccdc_input_entity {
 
 #define        OMAP3ISP_CCDC_NEVENTS   16
 
+struct ispccdc_fpc {
+       void *addr;
+       dma_addr_t dma;
+       unsigned int fpnum;
+};
+
 enum ispccdc_lsc_state {
        LSC_STATE_STOPPED = 0,
        LSC_STATE_STOPPING = 1,
@@ -57,8 +63,12 @@ struct ispccdc_lsc_config_req {
        struct list_head list;
        struct omap3isp_ccdc_lsc_config config;
        unsigned char enable;
-       u32 table;
-       struct iovm_struct *iovm;
+
+       struct {
+               void *addr;
+               dma_addr_t dma;
+               struct sg_table sgt;
+       } table;
 };
 
 /*
@@ -136,7 +146,7 @@ struct isp_ccdc_device {
                     fpc_en:1;
        struct omap3isp_ccdc_blcomp blcomp;
        struct omap3isp_ccdc_bclamp clamp;
-       struct omap3isp_ccdc_fpc fpc;
+       struct ispccdc_fpc fpc;
        struct ispccdc_lsc lsc;
        unsigned int update;
        unsigned int shadow_update;
index b30b67d22a58cfb83990b589926be86c95d8c2bc..f3801db9095ca301bf53e564e9d9f4eb33a13327 100644 (file)
@@ -549,7 +549,7 @@ static void ccp2_isr_buffer(struct isp_ccp2_device *ccp2)
 
        buffer = omap3isp_video_buffer_next(&ccp2->video_in);
        if (buffer != NULL)
-               ccp2_set_inaddr(ccp2, buffer->isp_addr);
+               ccp2_set_inaddr(ccp2, buffer->dma);
 
        pipe->state |= ISP_PIPELINE_IDLE_INPUT;
 
@@ -940,7 +940,7 @@ static int ccp2_video_queue(struct isp_video *video, struct isp_buffer *buffer)
 {
        struct isp_ccp2_device *ccp2 = &video->isp->isp_ccp2;
 
-       ccp2_set_inaddr(ccp2, buffer->isp_addr);
+       ccp2_set_inaddr(ccp2, buffer->dma);
        return 0;
 }
 
index 620560828a48cc9d395bfba0ddbb2c7f017ed84b..5a2e47e58b846543f651d38acf5fba05012e2c3e 100644 (file)
@@ -695,7 +695,7 @@ static void csi2_isr_buffer(struct isp_csi2_device *csi2)
        if (buffer == NULL)
                return;
 
-       csi2_set_outaddr(csi2, buffer->isp_addr);
+       csi2_set_outaddr(csi2, buffer->dma);
        csi2_ctx_enable(isp, csi2, 0, 1);
 }
 
@@ -812,7 +812,7 @@ static int csi2_queue(struct isp_video *video, struct isp_buffer *buffer)
        struct isp_device *isp = video->isp;
        struct isp_csi2_device *csi2 = &isp->isp_csi2a;
 
-       csi2_set_outaddr(csi2, buffer->isp_addr);
+       csi2_set_outaddr(csi2, buffer->dma);
 
        /*
         * If streaming was enabled before there was a buffer queued
index 75fd82b152ba362f97bafef11a4e8bbffd444fb0..d6811ce263eb113102cceb7611a42e4c5f21f11d 100644 (file)
@@ -47,7 +47,7 @@ static void h3a_aewb_setup_regs(struct ispstat *aewb, void *priv)
        if (aewb->state == ISPSTAT_DISABLED)
                return;
 
-       isp_reg_writel(aewb->isp, aewb->active_buf->iommu_addr,
+       isp_reg_writel(aewb->isp, aewb->active_buf->dma_addr,
                       OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWBUFST);
 
        if (!aewb->update)
index a0bf5af32438559958b39c1d62360e35d491ad48..6fc960cd30f57accab300b749aa7049dcf5efd28 100644 (file)
@@ -51,7 +51,7 @@ static void h3a_af_setup_regs(struct ispstat *af, void *priv)
        if (af->state == ISPSTAT_DISABLED)
                return;
 
-       isp_reg_writel(af->isp, af->active_buf->iommu_addr, OMAP3_ISP_IOMEM_H3A,
+       isp_reg_writel(af->isp, af->active_buf->dma_addr, OMAP3_ISP_IOMEM_H3A,
                       ISPH3A_AFBUFST);
 
        if (!af->update)
index 395b2b068c7553eb27a544dab4953040563a7119..720809b07e75f4779f20da64aa1b0af498bad012 100644 (file)
@@ -1499,14 +1499,14 @@ static void preview_isr_buffer(struct isp_prev_device *prev)
        if (prev->input == PREVIEW_INPUT_MEMORY) {
                buffer = omap3isp_video_buffer_next(&prev->video_in);
                if (buffer != NULL)
-                       preview_set_inaddr(prev, buffer->isp_addr);
+                       preview_set_inaddr(prev, buffer->dma);
                pipe->state |= ISP_PIPELINE_IDLE_INPUT;
        }
 
        if (prev->output & PREVIEW_OUTPUT_MEMORY) {
                buffer = omap3isp_video_buffer_next(&prev->video_out);
                if (buffer != NULL) {
-                       preview_set_outaddr(prev, buffer->isp_addr);
+                       preview_set_outaddr(prev, buffer->dma);
                        restart = 1;
                }
                pipe->state |= ISP_PIPELINE_IDLE_OUTPUT;
@@ -1577,10 +1577,10 @@ static int preview_video_queue(struct isp_video *video,
        struct isp_prev_device *prev = &video->isp->isp_prev;
 
        if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
-               preview_set_inaddr(prev, buffer->isp_addr);
+               preview_set_inaddr(prev, buffer->dma);
 
        if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               preview_set_outaddr(prev, buffer->isp_addr);
+               preview_set_outaddr(prev, buffer->dma);
 
        return 0;
 }
diff --git a/drivers/media/platform/omap3isp/ispqueue.c b/drivers/media/platform/omap3isp/ispqueue.c
deleted file mode 100644 (file)
index a5e6585..0000000
+++ /dev/null
@@ -1,1161 +0,0 @@
-/*
- * ispqueue.c
- *
- * TI OMAP3 ISP - Video buffers queue handling
- *
- * Copyright (C) 2010 Nokia Corporation
- *
- * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- *          Sakari Ailus <sakari.ailus@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * 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 St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#include <asm/cacheflush.h>
-#include <linux/dma-mapping.h>
-#include <linux/mm.h>
-#include <linux/pagemap.h>
-#include <linux/poll.h>
-#include <linux/scatterlist.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-
-#include "ispqueue.h"
-
-/* -----------------------------------------------------------------------------
- * Video buffers management
- */
-
-/*
- * isp_video_buffer_cache_sync - Keep the buffers coherent between CPU and ISP
- *
- * The typical operation required here is Cache Invalidation across
- * the (user space) buffer address range. And this _must_ be done
- * at QBUF stage (and *only* at QBUF).
- *
- * We try to use optimal cache invalidation function:
- * - dmac_map_area:
- *    - used when the number of pages are _low_.
- *    - it becomes quite slow as the number of pages increase.
- *       - for 648x492 viewfinder (150 pages) it takes 1.3 ms.
- *       - for 5 Mpix buffer (2491 pages) it takes between 25-50 ms.
- *
- * - flush_cache_all:
- *    - used when the number of pages are _high_.
- *    - time taken in the range of 500-900 us.
- *    - has a higher penalty but, as whole dcache + icache is invalidated
- */
-/*
- * FIXME: dmac_inv_range crashes randomly on the user space buffer
- *        address. Fall back to flush_cache_all for now.
- */
-#define ISP_CACHE_FLUSH_PAGES_MAX       0
-
-static void isp_video_buffer_cache_sync(struct isp_video_buffer *buf)
-{
-       if (buf->skip_cache)
-               return;
-
-       if (buf->vbuf.m.userptr == 0 || buf->npages == 0 ||
-           buf->npages > ISP_CACHE_FLUSH_PAGES_MAX)
-               flush_cache_all();
-       else {
-               dmac_map_area((void *)buf->vbuf.m.userptr, buf->vbuf.length,
-                             DMA_FROM_DEVICE);
-               outer_inv_range(buf->vbuf.m.userptr,
-                               buf->vbuf.m.userptr + buf->vbuf.length);
-       }
-}
-
-/*
- * isp_video_buffer_lock_vma - Prevent VMAs from being unmapped
- *
- * Lock the VMAs underlying the given buffer into memory. This avoids the
- * userspace buffer mapping from being swapped out, making VIPT cache handling
- * easier.
- *
- * Note that the pages will not be freed as the buffers have been locked to
- * memory using by a call to get_user_pages(), but the userspace mapping could
- * still disappear if the VMAs are not locked. This is caused by the memory
- * management code trying to be as lock-less as possible, which results in the
- * userspace mapping manager not finding out that the pages are locked under
- * some conditions.
- */
-static int isp_video_buffer_lock_vma(struct isp_video_buffer *buf, int lock)
-{
-       struct vm_area_struct *vma;
-       unsigned long start;
-       unsigned long end;
-       int ret = 0;
-
-       if (buf->vbuf.memory == V4L2_MEMORY_MMAP)
-               return 0;
-
-       /* We can be called from workqueue context if the current task dies to
-        * unlock the VMAs. In that case there's no current memory management
-        * context so unlocking can't be performed, but the VMAs have been or
-        * are getting destroyed anyway so it doesn't really matter.
-        */
-       if (!current || !current->mm)
-               return lock ? -EINVAL : 0;
-
-       start = buf->vbuf.m.userptr;
-       end = buf->vbuf.m.userptr + buf->vbuf.length - 1;
-
-       down_write(&current->mm->mmap_sem);
-       spin_lock(&current->mm->page_table_lock);
-
-       do {
-               vma = find_vma(current->mm, start);
-               if (vma == NULL) {
-                       ret = -EFAULT;
-                       goto out;
-               }
-
-               if (lock)
-                       vma->vm_flags |= VM_LOCKED;
-               else
-                       vma->vm_flags &= ~VM_LOCKED;
-
-               start = vma->vm_end + 1;
-       } while (vma->vm_end < end);
-
-       if (lock)
-               buf->vm_flags |= VM_LOCKED;
-       else
-               buf->vm_flags &= ~VM_LOCKED;
-
-out:
-       spin_unlock(&current->mm->page_table_lock);
-       up_write(&current->mm->mmap_sem);
-       return ret;
-}
-
-/*
- * isp_video_buffer_sglist_kernel - Build a scatter list for a vmalloc'ed buffer
- *
- * Iterate over the vmalloc'ed area and create a scatter list entry for every
- * page.
- */
-static int isp_video_buffer_sglist_kernel(struct isp_video_buffer *buf)
-{
-       struct scatterlist *sglist;
-       unsigned int npages;
-       unsigned int i;
-       void *addr;
-
-       addr = buf->vaddr;
-       npages = PAGE_ALIGN(buf->vbuf.length) >> PAGE_SHIFT;
-
-       sglist = vmalloc(npages * sizeof(*sglist));
-       if (sglist == NULL)
-               return -ENOMEM;
-
-       sg_init_table(sglist, npages);
-
-       for (i = 0; i < npages; ++i, addr += PAGE_SIZE) {
-               struct page *page = vmalloc_to_page(addr);
-
-               if (page == NULL || PageHighMem(page)) {
-                       vfree(sglist);
-                       return -EINVAL;
-               }
-
-               sg_set_page(&sglist[i], page, PAGE_SIZE, 0);
-       }
-
-       buf->sglen = npages;
-       buf->sglist = sglist;
-
-       return 0;
-}
-
-/*
- * isp_video_buffer_sglist_user - Build a scatter list for a userspace buffer
- *
- * Walk the buffer pages list and create a 1:1 mapping to a scatter list.
- */
-static int isp_video_buffer_sglist_user(struct isp_video_buffer *buf)
-{
-       struct scatterlist *sglist;
-       unsigned int offset = buf->offset;
-       unsigned int i;
-
-       sglist = vmalloc(buf->npages * sizeof(*sglist));
-       if (sglist == NULL)
-               return -ENOMEM;
-
-       sg_init_table(sglist, buf->npages);
-
-       for (i = 0; i < buf->npages; ++i) {
-               if (PageHighMem(buf->pages[i])) {
-                       vfree(sglist);
-                       return -EINVAL;
-               }
-
-               sg_set_page(&sglist[i], buf->pages[i], PAGE_SIZE - offset,
-                           offset);
-               offset = 0;
-       }
-
-       buf->sglen = buf->npages;
-       buf->sglist = sglist;
-
-       return 0;
-}
-
-/*
- * isp_video_buffer_sglist_pfnmap - Build a scatter list for a VM_PFNMAP buffer
- *
- * Create a scatter list of physically contiguous pages starting at the buffer
- * memory physical address.
- */
-static int isp_video_buffer_sglist_pfnmap(struct isp_video_buffer *buf)
-{
-       struct scatterlist *sglist;
-       unsigned int offset = buf->offset;
-       unsigned long pfn = buf->paddr >> PAGE_SHIFT;
-       unsigned int i;
-
-       sglist = vmalloc(buf->npages * sizeof(*sglist));
-       if (sglist == NULL)
-               return -ENOMEM;
-
-       sg_init_table(sglist, buf->npages);
-
-       for (i = 0; i < buf->npages; ++i, ++pfn) {
-               sg_set_page(&sglist[i], pfn_to_page(pfn), PAGE_SIZE - offset,
-                           offset);
-               /* PFNMAP buffers will not get DMA-mapped, set the DMA address
-                * manually.
-                */
-               sg_dma_address(&sglist[i]) = (pfn << PAGE_SHIFT) + offset;
-               offset = 0;
-       }
-
-       buf->sglen = buf->npages;
-       buf->sglist = sglist;
-
-       return 0;
-}
-
-/*
- * isp_video_buffer_cleanup - Release pages for a userspace VMA.
- *
- * Release pages locked by a call isp_video_buffer_prepare_user and free the
- * pages table.
- */
-static void isp_video_buffer_cleanup(struct isp_video_buffer *buf)
-{
-       enum dma_data_direction direction;
-       unsigned int i;
-
-       if (buf->queue->ops->buffer_cleanup)
-               buf->queue->ops->buffer_cleanup(buf);
-
-       if (!(buf->vm_flags & VM_PFNMAP)) {
-               direction = buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE
-                         ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
-               dma_unmap_sg(buf->queue->dev, buf->sglist, buf->sglen,
-                            direction);
-       }
-
-       vfree(buf->sglist);
-       buf->sglist = NULL;
-       buf->sglen = 0;
-
-       if (buf->pages != NULL) {
-               isp_video_buffer_lock_vma(buf, 0);
-
-               for (i = 0; i < buf->npages; ++i)
-                       page_cache_release(buf->pages[i]);
-
-               vfree(buf->pages);
-               buf->pages = NULL;
-       }
-
-       buf->npages = 0;
-       buf->skip_cache = false;
-}
-
-/*
- * isp_video_buffer_prepare_user - Pin userspace VMA pages to memory.
- *
- * This function creates a list of pages for a userspace VMA. The number of
- * pages is first computed based on the buffer size, and pages are then
- * retrieved by a call to get_user_pages.
- *
- * Pages are pinned to memory by get_user_pages, making them available for DMA
- * transfers. However, due to memory management optimization, it seems the
- * get_user_pages doesn't guarantee that the pinned pages will not be written
- * to swap and removed from the userspace mapping(s). When this happens, a page
- * fault can be generated when accessing those unmapped pages.
- *
- * If the fault is triggered by a page table walk caused by VIPT cache
- * management operations, the page fault handler might oops if the MM semaphore
- * is held, as it can't handle kernel page faults in that case. To fix that, a
- * fixup entry needs to be added to the cache management code, or the userspace
- * VMA must be locked to avoid removing pages from the userspace mapping in the
- * first place.
- *
- * If the number of pages retrieved is smaller than the number required by the
- * buffer size, the function returns -EFAULT.
- */
-static int isp_video_buffer_prepare_user(struct isp_video_buffer *buf)
-{
-       unsigned long data;
-       unsigned int first;
-       unsigned int last;
-       int ret;
-
-       data = buf->vbuf.m.userptr;
-       first = (data & PAGE_MASK) >> PAGE_SHIFT;
-       last = ((data + buf->vbuf.length - 1) & PAGE_MASK) >> PAGE_SHIFT;
-
-       buf->offset = data & ~PAGE_MASK;
-       buf->npages = last - first + 1;
-       buf->pages = vmalloc(buf->npages * sizeof(buf->pages[0]));
-       if (buf->pages == NULL)
-               return -ENOMEM;
-
-       down_read(&current->mm->mmap_sem);
-       ret = get_user_pages(current, current->mm, data & PAGE_MASK,
-                            buf->npages,
-                            buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE, 0,
-                            buf->pages, NULL);
-       up_read(&current->mm->mmap_sem);
-
-       if (ret != buf->npages) {
-               buf->npages = ret < 0 ? 0 : ret;
-               isp_video_buffer_cleanup(buf);
-               return -EFAULT;
-       }
-
-       ret = isp_video_buffer_lock_vma(buf, 1);
-       if (ret < 0)
-               isp_video_buffer_cleanup(buf);
-
-       return ret;
-}
-
-/*
- * isp_video_buffer_prepare_pfnmap - Validate a VM_PFNMAP userspace buffer
- *
- * Userspace VM_PFNMAP buffers are supported only if they are contiguous in
- * memory and if they span a single VMA.
- *
- * Return 0 if the buffer is valid, or -EFAULT otherwise.
- */
-static int isp_video_buffer_prepare_pfnmap(struct isp_video_buffer *buf)
-{
-       struct vm_area_struct *vma;
-       unsigned long prev_pfn;
-       unsigned long this_pfn;
-       unsigned long start;
-       unsigned long end;
-       dma_addr_t pa = 0;
-       int ret = -EFAULT;
-
-       start = buf->vbuf.m.userptr;
-       end = buf->vbuf.m.userptr + buf->vbuf.length - 1;
-
-       buf->offset = start & ~PAGE_MASK;
-       buf->npages = (end >> PAGE_SHIFT) - (start >> PAGE_SHIFT) + 1;
-       buf->pages = NULL;
-
-       down_read(&current->mm->mmap_sem);
-       vma = find_vma(current->mm, start);
-       if (vma == NULL || vma->vm_end < end)
-               goto done;
-
-       for (prev_pfn = 0; start <= end; start += PAGE_SIZE) {
-               ret = follow_pfn(vma, start, &this_pfn);
-               if (ret)
-                       goto done;
-
-               if (prev_pfn == 0)
-                       pa = this_pfn << PAGE_SHIFT;
-               else if (this_pfn != prev_pfn + 1) {
-                       ret = -EFAULT;
-                       goto done;
-               }
-
-               prev_pfn = this_pfn;
-       }
-
-       buf->paddr = pa + buf->offset;
-       ret = 0;
-
-done:
-       up_read(&current->mm->mmap_sem);
-       return ret;
-}
-
-/*
- * isp_video_buffer_prepare_vm_flags - Get VMA flags for a userspace address
- *
- * This function locates the VMAs for the buffer's userspace address and checks
- * that their flags match. The only flag that we need to care for at the moment
- * is VM_PFNMAP.
- *
- * The buffer vm_flags field is set to the first VMA flags.
- *
- * Return -EFAULT if no VMA can be found for part of the buffer, or if the VMAs
- * have incompatible flags.
- */
-static int isp_video_buffer_prepare_vm_flags(struct isp_video_buffer *buf)
-{
-       struct vm_area_struct *vma;
-       pgprot_t uninitialized_var(vm_page_prot);
-       unsigned long start;
-       unsigned long end;
-       int ret = -EFAULT;
-
-       start = buf->vbuf.m.userptr;
-       end = buf->vbuf.m.userptr + buf->vbuf.length - 1;
-
-       down_read(&current->mm->mmap_sem);
-
-       do {
-               vma = find_vma(current->mm, start);
-               if (vma == NULL)
-                       goto done;
-
-               if (start == buf->vbuf.m.userptr) {
-                       buf->vm_flags = vma->vm_flags;
-                       vm_page_prot = vma->vm_page_prot;
-               }
-
-               if ((buf->vm_flags ^ vma->vm_flags) & VM_PFNMAP)
-                       goto done;
-
-               if (vm_page_prot != vma->vm_page_prot)
-                       goto done;
-
-               start = vma->vm_end + 1;
-       } while (vma->vm_end < end);
-
-       /* Skip cache management to enhance performances for non-cached or
-        * write-combining buffers.
-        */
-       if (vm_page_prot == pgprot_noncached(vm_page_prot) ||
-           vm_page_prot == pgprot_writecombine(vm_page_prot))
-               buf->skip_cache = true;
-
-       ret = 0;
-
-done:
-       up_read(&current->mm->mmap_sem);
-       return ret;
-}
-
-/*
- * isp_video_buffer_prepare - Make a buffer ready for operation
- *
- * Preparing a buffer involves:
- *
- * - validating VMAs (userspace buffers only)
- * - locking pages and VMAs into memory (userspace buffers only)
- * - building page and scatter-gather lists
- * - mapping buffers for DMA operation
- * - performing driver-specific preparation
- *
- * The function must be called in userspace context with a valid mm context
- * (this excludes cleanup paths such as sys_close when the userspace process
- * segfaults).
- */
-static int isp_video_buffer_prepare(struct isp_video_buffer *buf)
-{
-       enum dma_data_direction direction;
-       int ret;
-
-       switch (buf->vbuf.memory) {
-       case V4L2_MEMORY_MMAP:
-               ret = isp_video_buffer_sglist_kernel(buf);
-               break;
-
-       case V4L2_MEMORY_USERPTR:
-               ret = isp_video_buffer_prepare_vm_flags(buf);
-               if (ret < 0)
-                       return ret;
-
-               if (buf->vm_flags & VM_PFNMAP) {
-                       ret = isp_video_buffer_prepare_pfnmap(buf);
-                       if (ret < 0)
-                               return ret;
-
-                       ret = isp_video_buffer_sglist_pfnmap(buf);
-               } else {
-                       ret = isp_video_buffer_prepare_user(buf);
-                       if (ret < 0)
-                               return ret;
-
-                       ret = isp_video_buffer_sglist_user(buf);
-               }
-               break;
-
-       default:
-               return -EINVAL;
-       }
-
-       if (ret < 0)
-               goto done;
-
-       if (!(buf->vm_flags & VM_PFNMAP)) {
-               direction = buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE
-                         ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
-               ret = dma_map_sg(buf->queue->dev, buf->sglist, buf->sglen,
-                                direction);
-               if (ret != buf->sglen) {
-                       ret = -EFAULT;
-                       goto done;
-               }
-       }
-
-       if (buf->queue->ops->buffer_prepare)
-               ret = buf->queue->ops->buffer_prepare(buf);
-
-done:
-       if (ret < 0) {
-               isp_video_buffer_cleanup(buf);
-               return ret;
-       }
-
-       return ret;
-}
-
-/*
- * isp_video_queue_query - Query the status of a given buffer
- *
- * Locking: must be called with the queue lock held.
- */
-static void isp_video_buffer_query(struct isp_video_buffer *buf,
-                                  struct v4l2_buffer *vbuf)
-{
-       memcpy(vbuf, &buf->vbuf, sizeof(*vbuf));
-
-       if (buf->vma_use_count)
-               vbuf->flags |= V4L2_BUF_FLAG_MAPPED;
-
-       switch (buf->state) {
-       case ISP_BUF_STATE_ERROR:
-               vbuf->flags |= V4L2_BUF_FLAG_ERROR;
-               /* Fallthrough */
-       case ISP_BUF_STATE_DONE:
-               vbuf->flags |= V4L2_BUF_FLAG_DONE;
-               break;
-       case ISP_BUF_STATE_QUEUED:
-       case ISP_BUF_STATE_ACTIVE:
-               vbuf->flags |= V4L2_BUF_FLAG_QUEUED;
-               break;
-       case ISP_BUF_STATE_IDLE:
-       default:
-               break;
-       }
-}
-
-/*
- * isp_video_buffer_wait - Wait for a buffer to be ready
- *
- * In non-blocking mode, return immediately with 0 if the buffer is ready or
- * -EAGAIN if the buffer is in the QUEUED or ACTIVE state.
- *
- * In blocking mode, wait (interruptibly but with no timeout) on the buffer wait
- * queue using the same condition.
- */
-static int isp_video_buffer_wait(struct isp_video_buffer *buf, int nonblocking)
-{
-       if (nonblocking) {
-               return (buf->state != ISP_BUF_STATE_QUEUED &&
-                       buf->state != ISP_BUF_STATE_ACTIVE)
-                       ? 0 : -EAGAIN;
-       }
-
-       return wait_event_interruptible(buf->wait,
-               buf->state != ISP_BUF_STATE_QUEUED &&
-               buf->state != ISP_BUF_STATE_ACTIVE);
-}
-
-/* -----------------------------------------------------------------------------
- * Queue management
- */
-
-/*
- * isp_video_queue_free - Free video buffers memory
- *
- * Buffers can only be freed if the queue isn't streaming and if no buffer is
- * mapped to userspace. Return -EBUSY if those conditions aren't satisfied.
- *
- * This function must be called with the queue lock held.
- */
-static int isp_video_queue_free(struct isp_video_queue *queue)
-{
-       unsigned int i;
-
-       if (queue->streaming)
-               return -EBUSY;
-
-       for (i = 0; i < queue->count; ++i) {
-               if (queue->buffers[i]->vma_use_count != 0)
-                       return -EBUSY;
-       }
-
-       for (i = 0; i < queue->count; ++i) {
-               struct isp_video_buffer *buf = queue->buffers[i];
-
-               isp_video_buffer_cleanup(buf);
-
-               vfree(buf->vaddr);
-               buf->vaddr = NULL;
-
-               kfree(buf);
-               queue->buffers[i] = NULL;
-       }
-
-       INIT_LIST_HEAD(&queue->queue);
-       queue->count = 0;
-       return 0;
-}
-
-/*
- * isp_video_queue_alloc - Allocate video buffers memory
- *
- * This function must be called with the queue lock held.
- */
-static int isp_video_queue_alloc(struct isp_video_queue *queue,
-                                unsigned int nbuffers,
-                                unsigned int size, enum v4l2_memory memory)
-{
-       struct isp_video_buffer *buf;
-       unsigned int i;
-       void *mem;
-       int ret;
-
-       /* Start by freeing the buffers. */
-       ret = isp_video_queue_free(queue);
-       if (ret < 0)
-               return ret;
-
-       /* Bail out if no buffers should be allocated. */
-       if (nbuffers == 0)
-               return 0;
-
-       /* Initialize the allocated buffers. */
-       for (i = 0; i < nbuffers; ++i) {
-               buf = kzalloc(queue->bufsize, GFP_KERNEL);
-               if (buf == NULL)
-                       break;
-
-               if (memory == V4L2_MEMORY_MMAP) {
-                       /* Allocate video buffers memory for mmap mode. Align
-                        * the size to the page size.
-                        */
-                       mem = vmalloc_32_user(PAGE_ALIGN(size));
-                       if (mem == NULL) {
-                               kfree(buf);
-                               break;
-                       }
-
-                       buf->vbuf.m.offset = i * PAGE_ALIGN(size);
-                       buf->vaddr = mem;
-               }
-
-               buf->vbuf.index = i;
-               buf->vbuf.length = size;
-               buf->vbuf.type = queue->type;
-               buf->vbuf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
-               buf->vbuf.field = V4L2_FIELD_NONE;
-               buf->vbuf.memory = memory;
-
-               buf->queue = queue;
-               init_waitqueue_head(&buf->wait);
-
-               queue->buffers[i] = buf;
-       }
-
-       if (i == 0)
-               return -ENOMEM;
-
-       queue->count = i;
-       return nbuffers;
-}
-
-/**
- * omap3isp_video_queue_cleanup - Clean up the video buffers queue
- * @queue: Video buffers queue
- *
- * Free all allocated resources and clean up the video buffers queue. The queue
- * must not be busy (no ongoing video stream) and buffers must have been
- * unmapped.
- *
- * Return 0 on success or -EBUSY if the queue is busy or buffers haven't been
- * unmapped.
- */
-int omap3isp_video_queue_cleanup(struct isp_video_queue *queue)
-{
-       return isp_video_queue_free(queue);
-}
-
-/**
- * omap3isp_video_queue_init - Initialize the video buffers queue
- * @queue: Video buffers queue
- * @type: V4L2 buffer type (capture or output)
- * @ops: Driver-specific queue operations
- * @dev: Device used for DMA operations
- * @bufsize: Size of the driver-specific buffer structure
- *
- * Initialize the video buffers queue with the supplied parameters.
- *
- * The queue type must be one of V4L2_BUF_TYPE_VIDEO_CAPTURE or
- * V4L2_BUF_TYPE_VIDEO_OUTPUT. Other buffer types are not supported yet.
- *
- * Buffer objects will be allocated using the given buffer size to allow room
- * for driver-specific fields. Driver-specific buffer structures must start
- * with a struct isp_video_buffer field. Drivers with no driver-specific buffer
- * structure must pass the size of the isp_video_buffer structure in the bufsize
- * parameter.
- *
- * Return 0 on success.
- */
-int omap3isp_video_queue_init(struct isp_video_queue *queue,
-                             enum v4l2_buf_type type,
-                             const struct isp_video_queue_operations *ops,
-                             struct device *dev, unsigned int bufsize)
-{
-       INIT_LIST_HEAD(&queue->queue);
-       mutex_init(&queue->lock);
-       spin_lock_init(&queue->irqlock);
-
-       queue->type = type;
-       queue->ops = ops;
-       queue->dev = dev;
-       queue->bufsize = bufsize;
-
-       return 0;
-}
-
-/* -----------------------------------------------------------------------------
- * V4L2 operations
- */
-
-/**
- * omap3isp_video_queue_reqbufs - Allocate video buffers memory
- *
- * This function is intended to be used as a VIDIOC_REQBUFS ioctl handler. It
- * allocated video buffer objects and, for MMAP buffers, buffer memory.
- *
- * If the number of buffers is 0, all buffers are freed and the function returns
- * without performing any allocation.
- *
- * If the number of buffers is not 0, currently allocated buffers (if any) are
- * freed and the requested number of buffers are allocated. Depending on
- * driver-specific requirements and on memory availability, a number of buffer
- * smaller or bigger than requested can be allocated. This isn't considered as
- * an error.
- *
- * Return 0 on success or one of the following error codes:
- *
- * -EINVAL if the buffer type or index are invalid
- * -EBUSY if the queue is busy (streaming or buffers mapped)
- * -ENOMEM if the buffers can't be allocated due to an out-of-memory condition
- */
-int omap3isp_video_queue_reqbufs(struct isp_video_queue *queue,
-                                struct v4l2_requestbuffers *rb)
-{
-       unsigned int nbuffers = rb->count;
-       unsigned int size;
-       int ret;
-
-       if (rb->type != queue->type)
-               return -EINVAL;
-
-       queue->ops->queue_prepare(queue, &nbuffers, &size);
-       if (size == 0)
-               return -EINVAL;
-
-       nbuffers = min_t(unsigned int, nbuffers, ISP_VIDEO_MAX_BUFFERS);
-
-       mutex_lock(&queue->lock);
-
-       ret = isp_video_queue_alloc(queue, nbuffers, size, rb->memory);
-       if (ret < 0)
-               goto done;
-
-       rb->count = ret;
-       ret = 0;
-
-done:
-       mutex_unlock(&queue->lock);
-       return ret;
-}
-
-/**
- * omap3isp_video_queue_querybuf - Query the status of a buffer in a queue
- *
- * This function is intended to be used as a VIDIOC_QUERYBUF ioctl handler. It
- * returns the status of a given video buffer.
- *
- * Return 0 on success or -EINVAL if the buffer type or index are invalid.
- */
-int omap3isp_video_queue_querybuf(struct isp_video_queue *queue,
-                                 struct v4l2_buffer *vbuf)
-{
-       struct isp_video_buffer *buf;
-       int ret = 0;
-
-       if (vbuf->type != queue->type)
-               return -EINVAL;
-
-       mutex_lock(&queue->lock);
-
-       if (vbuf->index >= queue->count) {
-               ret = -EINVAL;
-               goto done;
-       }
-
-       buf = queue->buffers[vbuf->index];
-       isp_video_buffer_query(buf, vbuf);
-
-done:
-       mutex_unlock(&queue->lock);
-       return ret;
-}
-
-/**
- * omap3isp_video_queue_qbuf - Queue a buffer
- *
- * This function is intended to be used as a VIDIOC_QBUF ioctl handler.
- *
- * The v4l2_buffer structure passed from userspace is first sanity tested. If
- * sane, the buffer is then processed and added to the main queue and, if the
- * queue is streaming, to the IRQ queue.
- *
- * Before being enqueued, USERPTR buffers are checked for address changes. If
- * the buffer has a different userspace address, the old memory area is unlocked
- * and the new memory area is locked.
- */
-int omap3isp_video_queue_qbuf(struct isp_video_queue *queue,
-                             struct v4l2_buffer *vbuf)
-{
-       struct isp_video_buffer *buf;
-       unsigned long flags;
-       int ret = -EINVAL;
-
-       if (vbuf->type != queue->type)
-               goto done;
-
-       mutex_lock(&queue->lock);
-
-       if (vbuf->index >= queue->count)
-               goto done;
-
-       buf = queue->buffers[vbuf->index];
-
-       if (vbuf->memory != buf->vbuf.memory)
-               goto done;
-
-       if (buf->state != ISP_BUF_STATE_IDLE)
-               goto done;
-
-       if (vbuf->memory == V4L2_MEMORY_USERPTR &&
-           vbuf->length < buf->vbuf.length)
-               goto done;
-
-       if (vbuf->memory == V4L2_MEMORY_USERPTR &&
-           vbuf->m.userptr != buf->vbuf.m.userptr) {
-               isp_video_buffer_cleanup(buf);
-               buf->vbuf.m.userptr = vbuf->m.userptr;
-               buf->prepared = 0;
-       }
-
-       if (!buf->prepared) {
-               ret = isp_video_buffer_prepare(buf);
-               if (ret < 0)
-                       goto done;
-               buf->prepared = 1;
-       }
-
-       isp_video_buffer_cache_sync(buf);
-
-       buf->state = ISP_BUF_STATE_QUEUED;
-       list_add_tail(&buf->stream, &queue->queue);
-
-       if (queue->streaming) {
-               spin_lock_irqsave(&queue->irqlock, flags);
-               queue->ops->buffer_queue(buf);
-               spin_unlock_irqrestore(&queue->irqlock, flags);
-       }
-
-       ret = 0;
-
-done:
-       mutex_unlock(&queue->lock);
-       return ret;
-}
-
-/**
- * omap3isp_video_queue_dqbuf - Dequeue a buffer
- *
- * This function is intended to be used as a VIDIOC_DQBUF ioctl handler.
- *
- * Wait until a buffer is ready to be dequeued, remove it from the queue and
- * copy its information to the v4l2_buffer structure.
- *
- * If the nonblocking argument is not zero and no buffer is ready, return
- * -EAGAIN immediately instead of waiting.
- *
- * If no buffer has been enqueued, or if the requested buffer type doesn't match
- * the queue type, return -EINVAL.
- */
-int omap3isp_video_queue_dqbuf(struct isp_video_queue *queue,
-                              struct v4l2_buffer *vbuf, int nonblocking)
-{
-       struct isp_video_buffer *buf;
-       int ret;
-
-       if (vbuf->type != queue->type)
-               return -EINVAL;
-
-       mutex_lock(&queue->lock);
-
-       if (list_empty(&queue->queue)) {
-               ret = -EINVAL;
-               goto done;
-       }
-
-       buf = list_first_entry(&queue->queue, struct isp_video_buffer, stream);
-       ret = isp_video_buffer_wait(buf, nonblocking);
-       if (ret < 0)
-               goto done;
-
-       list_del(&buf->stream);
-
-       isp_video_buffer_query(buf, vbuf);
-       buf->state = ISP_BUF_STATE_IDLE;
-       vbuf->flags &= ~V4L2_BUF_FLAG_QUEUED;
-
-done:
-       mutex_unlock(&queue->lock);
-       return ret;
-}
-
-/**
- * omap3isp_video_queue_streamon - Start streaming
- *
- * This function is intended to be used as a VIDIOC_STREAMON ioctl handler. It
- * starts streaming on the queue and calls the buffer_queue operation for all
- * queued buffers.
- *
- * Return 0 on success.
- */
-int omap3isp_video_queue_streamon(struct isp_video_queue *queue)
-{
-       struct isp_video_buffer *buf;
-       unsigned long flags;
-
-       mutex_lock(&queue->lock);
-
-       if (queue->streaming)
-               goto done;
-
-       queue->streaming = 1;
-
-       spin_lock_irqsave(&queue->irqlock, flags);
-       list_for_each_entry(buf, &queue->queue, stream)
-               queue->ops->buffer_queue(buf);
-       spin_unlock_irqrestore(&queue->irqlock, flags);
-
-done:
-       mutex_unlock(&queue->lock);
-       return 0;
-}
-
-/**
- * omap3isp_video_queue_streamoff - Stop streaming
- *
- * This function is intended to be used as a VIDIOC_STREAMOFF ioctl handler. It
- * stops streaming on the queue and wakes up all the buffers.
- *
- * Drivers must stop the hardware and synchronize with interrupt handlers and/or
- * delayed works before calling this function to make sure no buffer will be
- * touched by the driver and/or hardware.
- */
-void omap3isp_video_queue_streamoff(struct isp_video_queue *queue)
-{
-       struct isp_video_buffer *buf;
-       unsigned long flags;
-       unsigned int i;
-
-       mutex_lock(&queue->lock);
-
-       if (!queue->streaming)
-               goto done;
-
-       queue->streaming = 0;
-
-       spin_lock_irqsave(&queue->irqlock, flags);
-       for (i = 0; i < queue->count; ++i) {
-               buf = queue->buffers[i];
-
-               if (buf->state == ISP_BUF_STATE_ACTIVE)
-                       wake_up(&buf->wait);
-
-               buf->state = ISP_BUF_STATE_IDLE;
-       }
-       spin_unlock_irqrestore(&queue->irqlock, flags);
-
-       INIT_LIST_HEAD(&queue->queue);
-
-done:
-       mutex_unlock(&queue->lock);
-}
-
-/**
- * omap3isp_video_queue_discard_done - Discard all buffers marked as DONE
- *
- * This function is intended to be used with suspend/resume operations. It
- * discards all 'done' buffers as they would be too old to be requested after
- * resume.
- *
- * Drivers must stop the hardware and synchronize with interrupt handlers and/or
- * delayed works before calling this function to make sure no buffer will be
- * touched by the driver and/or hardware.
- */
-void omap3isp_video_queue_discard_done(struct isp_video_queue *queue)
-{
-       struct isp_video_buffer *buf;
-       unsigned int i;
-
-       mutex_lock(&queue->lock);
-
-       if (!queue->streaming)
-               goto done;
-
-       for (i = 0; i < queue->count; ++i) {
-               buf = queue->buffers[i];
-
-               if (buf->state == ISP_BUF_STATE_DONE)
-                       buf->state = ISP_BUF_STATE_ERROR;
-       }
-
-done:
-       mutex_unlock(&queue->lock);
-}
-
-static void isp_video_queue_vm_open(struct vm_area_struct *vma)
-{
-       struct isp_video_buffer *buf = vma->vm_private_data;
-
-       buf->vma_use_count++;
-}
-
-static void isp_video_queue_vm_close(struct vm_area_struct *vma)
-{
-       struct isp_video_buffer *buf = vma->vm_private_data;
-
-       buf->vma_use_count--;
-}
-
-static const struct vm_operations_struct isp_video_queue_vm_ops = {
-       .open = isp_video_queue_vm_open,
-       .close = isp_video_queue_vm_close,
-};
-
-/**
- * omap3isp_video_queue_mmap - Map buffers to userspace
- *
- * This function is intended to be used as an mmap() file operation handler. It
- * maps a buffer to userspace based on the VMA offset.
- *
- * Only buffers of memory type MMAP are supported.
- */
-int omap3isp_video_queue_mmap(struct isp_video_queue *queue,
-                        struct vm_area_struct *vma)
-{
-       struct isp_video_buffer *uninitialized_var(buf);
-       unsigned long size;
-       unsigned int i;
-       int ret = 0;
-
-       mutex_lock(&queue->lock);
-
-       for (i = 0; i < queue->count; ++i) {
-               buf = queue->buffers[i];
-               if ((buf->vbuf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
-                       break;
-       }
-
-       if (i == queue->count) {
-               ret = -EINVAL;
-               goto done;
-       }
-
-       size = vma->vm_end - vma->vm_start;
-
-       if (buf->vbuf.memory != V4L2_MEMORY_MMAP ||
-           size != PAGE_ALIGN(buf->vbuf.length)) {
-               ret = -EINVAL;
-               goto done;
-       }
-
-       ret = remap_vmalloc_range(vma, buf->vaddr, 0);
-       if (ret < 0)
-               goto done;
-
-       vma->vm_ops = &isp_video_queue_vm_ops;
-       vma->vm_private_data = buf;
-       isp_video_queue_vm_open(vma);
-
-done:
-       mutex_unlock(&queue->lock);
-       return ret;
-}
-
-/**
- * omap3isp_video_queue_poll - Poll video queue state
- *
- * This function is intended to be used as a poll() file operation handler. It
- * polls the state of the video buffer at the front of the queue and returns an
- * events mask.
- *
- * If no buffer is present at the front of the queue, POLLERR is returned.
- */
-unsigned int omap3isp_video_queue_poll(struct isp_video_queue *queue,
-                                      struct file *file, poll_table *wait)
-{
-       struct isp_video_buffer *buf;
-       unsigned int mask = 0;
-
-       mutex_lock(&queue->lock);
-       if (list_empty(&queue->queue)) {
-               mask |= POLLERR;
-               goto done;
-       }
-       buf = list_first_entry(&queue->queue, struct isp_video_buffer, stream);
-
-       poll_wait(file, &buf->wait, wait);
-       if (buf->state == ISP_BUF_STATE_DONE ||
-           buf->state == ISP_BUF_STATE_ERROR) {
-               if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
-                       mask |= POLLIN | POLLRDNORM;
-               else
-                       mask |= POLLOUT | POLLWRNORM;
-       }
-
-done:
-       mutex_unlock(&queue->lock);
-       return mask;
-}
diff --git a/drivers/media/platform/omap3isp/ispqueue.h b/drivers/media/platform/omap3isp/ispqueue.h
deleted file mode 100644 (file)
index 3e048ad..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * ispqueue.h
- *
- * TI OMAP3 ISP - Video buffers queue handling
- *
- * Copyright (C) 2010 Nokia Corporation
- *
- * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- *          Sakari Ailus <sakari.ailus@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * 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 St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#ifndef OMAP3_ISP_QUEUE_H
-#define OMAP3_ISP_QUEUE_H
-
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/mm_types.h>
-#include <linux/mutex.h>
-#include <linux/videodev2.h>
-#include <linux/wait.h>
-
-struct isp_video_queue;
-struct page;
-struct scatterlist;
-
-#define ISP_VIDEO_MAX_BUFFERS          16
-
-/**
- * enum isp_video_buffer_state - ISP video buffer state
- * @ISP_BUF_STATE_IDLE:        The buffer is under userspace control (dequeued
- *     or not queued yet).
- * @ISP_BUF_STATE_QUEUED: The buffer has been queued but isn't used by the
- *     device yet.
- * @ISP_BUF_STATE_ACTIVE: The buffer is in use for an active video transfer.
- * @ISP_BUF_STATE_ERROR: The device is done with the buffer and an error
- *     occurred. For capture device the buffer likely contains corrupted data or
- *     no data at all.
- * @ISP_BUF_STATE_DONE: The device is done with the buffer and no error occurred.
- *     For capture devices the buffer contains valid data.
- */
-enum isp_video_buffer_state {
-       ISP_BUF_STATE_IDLE,
-       ISP_BUF_STATE_QUEUED,
-       ISP_BUF_STATE_ACTIVE,
-       ISP_BUF_STATE_ERROR,
-       ISP_BUF_STATE_DONE,
-};
-
-/**
- * struct isp_video_buffer - ISP video buffer
- * @vma_use_count: Number of times the buffer is mmap'ed to userspace
- * @stream: List head for insertion into main queue
- * @queue: ISP buffers queue this buffer belongs to
- * @prepared: Whether the buffer has been prepared
- * @skip_cache: Whether to skip cache management operations for this buffer
- * @vaddr: Memory virtual address (for kernel buffers)
- * @vm_flags: Buffer VMA flags (for userspace buffers)
- * @offset: Offset inside the first page (for userspace buffers)
- * @npages: Number of pages (for userspace buffers)
- * @pages: Pages table (for userspace non-VM_PFNMAP buffers)
- * @paddr: Memory physical address (for userspace VM_PFNMAP buffers)
- * @sglen: Number of elements in the scatter list (for non-VM_PFNMAP buffers)
- * @sglist: Scatter list (for non-VM_PFNMAP buffers)
- * @vbuf: V4L2 buffer
- * @irqlist: List head for insertion into IRQ queue
- * @state: Current buffer state
- * @wait: Wait queue to signal buffer completion
- */
-struct isp_video_buffer {
-       unsigned long vma_use_count;
-       struct list_head stream;
-       struct isp_video_queue *queue;
-       unsigned int prepared:1;
-       bool skip_cache;
-
-       /* For kernel buffers. */
-       void *vaddr;
-
-       /* For userspace buffers. */
-       vm_flags_t vm_flags;
-       unsigned long offset;
-       unsigned int npages;
-       struct page **pages;
-       dma_addr_t paddr;
-
-       /* For all buffers except VM_PFNMAP. */
-       unsigned int sglen;
-       struct scatterlist *sglist;
-
-       /* Touched by the interrupt handler. */
-       struct v4l2_buffer vbuf;
-       struct list_head irqlist;
-       enum isp_video_buffer_state state;
-       wait_queue_head_t wait;
-};
-
-#define to_isp_video_buffer(vb)        container_of(vb, struct isp_video_buffer, vb)
-
-/**
- * struct isp_video_queue_operations - Driver-specific operations
- * @queue_prepare: Called before allocating buffers. Drivers should clamp the
- *     number of buffers according to their requirements, and must return the
- *     buffer size in bytes.
- * @buffer_prepare: Called the first time a buffer is queued, or after changing
- *     the userspace memory address for a USERPTR buffer, with the queue lock
- *     held. Drivers should perform device-specific buffer preparation (such as
- *     mapping the buffer memory in an IOMMU). This operation is optional.
- * @buffer_queue: Called when a buffer is being added to the queue with the
- *     queue irqlock spinlock held.
- * @buffer_cleanup: Called before freeing buffers, or before changing the
- *     userspace memory address for a USERPTR buffer, with the queue lock held.
- *     Drivers must perform cleanup operations required to undo the
- *     buffer_prepare call. This operation is optional.
- */
-struct isp_video_queue_operations {
-       void (*queue_prepare)(struct isp_video_queue *queue,
-                             unsigned int *nbuffers, unsigned int *size);
-       int  (*buffer_prepare)(struct isp_video_buffer *buf);
-       void (*buffer_queue)(struct isp_video_buffer *buf);
-       void (*buffer_cleanup)(struct isp_video_buffer *buf);
-};
-
-/**
- * struct isp_video_queue - ISP video buffers queue
- * @type: Type of video buffers handled by this queue
- * @ops: Queue operations
- * @dev: Device used for DMA operations
- * @bufsize: Size of a driver-specific buffer object
- * @count: Number of currently allocated buffers
- * @buffers: ISP video buffers
- * @lock: Mutex to protect access to the buffers, main queue and state
- * @irqlock: Spinlock to protect access to the IRQ queue
- * @streaming: Queue state, indicates whether the queue is streaming
- * @queue: List of all queued buffers
- */
-struct isp_video_queue {
-       enum v4l2_buf_type type;
-       const struct isp_video_queue_operations *ops;
-       struct device *dev;
-       unsigned int bufsize;
-
-       unsigned int count;
-       struct isp_video_buffer *buffers[ISP_VIDEO_MAX_BUFFERS];
-       struct mutex lock;
-       spinlock_t irqlock;
-
-       unsigned int streaming:1;
-
-       struct list_head queue;
-};
-
-int omap3isp_video_queue_cleanup(struct isp_video_queue *queue);
-int omap3isp_video_queue_init(struct isp_video_queue *queue,
-                             enum v4l2_buf_type type,
-                             const struct isp_video_queue_operations *ops,
-                             struct device *dev, unsigned int bufsize);
-
-int omap3isp_video_queue_reqbufs(struct isp_video_queue *queue,
-                                struct v4l2_requestbuffers *rb);
-int omap3isp_video_queue_querybuf(struct isp_video_queue *queue,
-                                 struct v4l2_buffer *vbuf);
-int omap3isp_video_queue_qbuf(struct isp_video_queue *queue,
-                             struct v4l2_buffer *vbuf);
-int omap3isp_video_queue_dqbuf(struct isp_video_queue *queue,
-                              struct v4l2_buffer *vbuf, int nonblocking);
-int omap3isp_video_queue_streamon(struct isp_video_queue *queue);
-void omap3isp_video_queue_streamoff(struct isp_video_queue *queue);
-void omap3isp_video_queue_discard_done(struct isp_video_queue *queue);
-int omap3isp_video_queue_mmap(struct isp_video_queue *queue,
-                             struct vm_area_struct *vma);
-unsigned int omap3isp_video_queue_poll(struct isp_video_queue *queue,
-                                      struct file *file, poll_table *wait);
-
-#endif /* OMAP3_ISP_QUEUE_H */
index 86369df81d7481fbcdece03d1653baa3fe8b6d43..6f077c2377db6a7b785f1f8a4b688c2996c0d2b1 100644 (file)
@@ -1040,7 +1040,7 @@ static void resizer_isr_buffer(struct isp_res_device *res)
         */
        buffer = omap3isp_video_buffer_next(&res->video_out);
        if (buffer != NULL) {
-               resizer_set_outaddr(res, buffer->isp_addr);
+               resizer_set_outaddr(res, buffer->dma);
                restart = 1;
        }
 
@@ -1049,7 +1049,7 @@ static void resizer_isr_buffer(struct isp_res_device *res)
        if (res->input == RESIZER_INPUT_MEMORY) {
                buffer = omap3isp_video_buffer_next(&res->video_in);
                if (buffer != NULL)
-                       resizer_set_inaddr(res, buffer->isp_addr);
+                       resizer_set_inaddr(res, buffer->dma);
                pipe->state |= ISP_PIPELINE_IDLE_INPUT;
        }
 
@@ -1101,7 +1101,7 @@ static int resizer_video_queue(struct isp_video *video,
        struct isp_res_device *res = &video->isp->isp_res;
 
        if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
-               resizer_set_inaddr(res, buffer->isp_addr);
+               resizer_set_inaddr(res, buffer->dma);
 
        /*
         * We now have a buffer queued on the output. Despite what the
@@ -1116,7 +1116,7 @@ static int resizer_video_queue(struct isp_video *video,
         * continuous mode or when starting the stream.
         */
        if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               resizer_set_outaddr(res, buffer->isp_addr);
+               resizer_set_outaddr(res, buffer->dma);
 
        return 0;
 }
index 5707f85c4cc4cba883e6107e892ba864fe8bc4f5..e6cbc1eaf4cab03fa5c29ab1ffba1de8d7bb6cec 100644 (file)
  */
 
 #include <linux/dma-mapping.h>
-#include <linux/omap-iommu.h>
 #include <linux/slab.h>
 #include <linux/uaccess.h>
 
 #include "isp.h"
 
-#define IS_COHERENT_BUF(stat)  ((stat)->dma_ch >= 0)
+#define ISP_STAT_USES_DMAENGINE(stat)  ((stat)->dma_ch >= 0)
 
 /*
  * MAGIC_SIZE must always be the greatest common divisor of
@@ -77,21 +76,10 @@ static void __isp_stat_buf_sync_magic(struct ispstat *stat,
                                        dma_addr_t, unsigned long, size_t,
                                        enum dma_data_direction))
 {
-       struct device *dev = stat->isp->dev;
-       struct page *pg;
-       dma_addr_t dma_addr;
-       u32 offset;
-
-       /* Initial magic words */
-       pg = vmalloc_to_page(buf->virt_addr);
-       dma_addr = pfn_to_dma(dev, page_to_pfn(pg));
-       dma_sync(dev, dma_addr, 0, MAGIC_SIZE, dir);
-
-       /* Final magic words */
-       pg = vmalloc_to_page(buf->virt_addr + buf_size);
-       dma_addr = pfn_to_dma(dev, page_to_pfn(pg));
-       offset = ((u32)buf->virt_addr + buf_size) & ~PAGE_MASK;
-       dma_sync(dev, dma_addr, offset, MAGIC_SIZE, dir);
+       /* Sync the initial and final magic words. */
+       dma_sync(stat->isp->dev, buf->dma_addr, 0, MAGIC_SIZE, dir);
+       dma_sync(stat->isp->dev, buf->dma_addr + (buf_size & PAGE_MASK),
+                buf_size & ~PAGE_MASK, MAGIC_SIZE, dir);
 }
 
 static void isp_stat_buf_sync_magic_for_device(struct ispstat *stat,
@@ -99,7 +87,7 @@ static void isp_stat_buf_sync_magic_for_device(struct ispstat *stat,
                                               u32 buf_size,
                                               enum dma_data_direction dir)
 {
-       if (IS_COHERENT_BUF(stat))
+       if (ISP_STAT_USES_DMAENGINE(stat))
                return;
 
        __isp_stat_buf_sync_magic(stat, buf, buf_size, dir,
@@ -111,7 +99,7 @@ static void isp_stat_buf_sync_magic_for_cpu(struct ispstat *stat,
                                            u32 buf_size,
                                            enum dma_data_direction dir)
 {
-       if (IS_COHERENT_BUF(stat))
+       if (ISP_STAT_USES_DMAENGINE(stat))
                return;
 
        __isp_stat_buf_sync_magic(stat, buf, buf_size, dir,
@@ -180,21 +168,21 @@ static void isp_stat_buf_insert_magic(struct ispstat *stat,
 static void isp_stat_buf_sync_for_device(struct ispstat *stat,
                                         struct ispstat_buffer *buf)
 {
-       if (IS_COHERENT_BUF(stat))
+       if (ISP_STAT_USES_DMAENGINE(stat))
                return;
 
-       dma_sync_sg_for_device(stat->isp->dev, buf->iovm->sgt->sgl,
-                              buf->iovm->sgt->nents, DMA_FROM_DEVICE);
+       dma_sync_sg_for_device(stat->isp->dev, buf->sgt.sgl,
+                              buf->sgt.nents, DMA_FROM_DEVICE);
 }
 
 static void isp_stat_buf_sync_for_cpu(struct ispstat *stat,
                                      struct ispstat_buffer *buf)
 {
-       if (IS_COHERENT_BUF(stat))
+       if (ISP_STAT_USES_DMAENGINE(stat))
                return;
 
-       dma_sync_sg_for_cpu(stat->isp->dev, buf->iovm->sgt->sgl,
-                           buf->iovm->sgt->nents, DMA_FROM_DEVICE);
+       dma_sync_sg_for_cpu(stat->isp->dev, buf->sgt.sgl,
+                           buf->sgt.nents, DMA_FROM_DEVICE);
 }
 
 static void isp_stat_buf_clear(struct ispstat *stat)
@@ -354,29 +342,21 @@ static struct ispstat_buffer *isp_stat_buf_get(struct ispstat *stat,
 
 static void isp_stat_bufs_free(struct ispstat *stat)
 {
-       struct isp_device *isp = stat->isp;
-       int i;
+       struct device *dev = ISP_STAT_USES_DMAENGINE(stat)
+                          ? NULL : stat->isp->dev;
+       unsigned int i;
 
        for (i = 0; i < STAT_MAX_BUFS; i++) {
                struct ispstat_buffer *buf = &stat->buf[i];
 
-               if (!IS_COHERENT_BUF(stat)) {
-                       if (IS_ERR_OR_NULL((void *)buf->iommu_addr))
-                               continue;
-                       if (buf->iovm)
-                               dma_unmap_sg(isp->dev, buf->iovm->sgt->sgl,
-                                            buf->iovm->sgt->nents,
-                                            DMA_FROM_DEVICE);
-                       omap_iommu_vfree(isp->domain, isp->dev,
-                                                       buf->iommu_addr);
-               } else {
-                       if (!buf->virt_addr)
-                               continue;
-                       dma_free_coherent(stat->isp->dev, stat->buf_alloc_size,
-                                         buf->virt_addr, buf->dma_addr);
-               }
-               buf->iommu_addr = 0;
-               buf->iovm = NULL;
+               if (!buf->virt_addr)
+                       continue;
+
+               sg_free_table(&buf->sgt);
+
+               dma_free_coherent(dev, stat->buf_alloc_size, buf->virt_addr,
+                                 buf->dma_addr);
+
                buf->dma_addr = 0;
                buf->virt_addr = NULL;
                buf->empty = 1;
@@ -389,83 +369,51 @@ static void isp_stat_bufs_free(struct ispstat *stat)
        stat->active_buf = NULL;
 }
 
-static int isp_stat_bufs_alloc_iommu(struct ispstat *stat, unsigned int size)
-{
-       struct isp_device *isp = stat->isp;
-       int i;
-
-       stat->buf_alloc_size = size;
-
-       for (i = 0; i < STAT_MAX_BUFS; i++) {
-               struct ispstat_buffer *buf = &stat->buf[i];
-               struct iovm_struct *iovm;
-
-               WARN_ON(buf->dma_addr);
-               buf->iommu_addr = omap_iommu_vmalloc(isp->domain, isp->dev, 0,
-                                                       size, IOMMU_FLAG);
-               if (IS_ERR((void *)buf->iommu_addr)) {
-                       dev_err(stat->isp->dev,
-                                "%s: Can't acquire memory for "
-                                "buffer %d\n", stat->subdev.name, i);
-                       isp_stat_bufs_free(stat);
-                       return -ENOMEM;
-               }
-
-               iovm = omap_find_iovm_area(isp->dev, buf->iommu_addr);
-               if (!iovm ||
-                   !dma_map_sg(isp->dev, iovm->sgt->sgl, iovm->sgt->nents,
-                               DMA_FROM_DEVICE)) {
-                       isp_stat_bufs_free(stat);
-                       return -ENOMEM;
-               }
-               buf->iovm = iovm;
-
-               buf->virt_addr = omap_da_to_va(stat->isp->dev,
-                                         (u32)buf->iommu_addr);
-               buf->empty = 1;
-               dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated."
-                       "iommu_addr=0x%08lx virt_addr=0x%08lx",
-                       stat->subdev.name, i, buf->iommu_addr,
-                       (unsigned long)buf->virt_addr);
-       }
-
-       return 0;
-}
-
-static int isp_stat_bufs_alloc_dma(struct ispstat *stat, unsigned int size)
+static int isp_stat_bufs_alloc_one(struct device *dev,
+                                  struct ispstat_buffer *buf,
+                                  unsigned int size)
 {
-       int i;
-
-       stat->buf_alloc_size = size;
-
-       for (i = 0; i < STAT_MAX_BUFS; i++) {
-               struct ispstat_buffer *buf = &stat->buf[i];
-
-               WARN_ON(buf->iommu_addr);
-               buf->virt_addr = dma_alloc_coherent(stat->isp->dev, size,
-                                       &buf->dma_addr, GFP_KERNEL | GFP_DMA);
+       int ret;
 
-               if (!buf->virt_addr || !buf->dma_addr) {
-                       dev_info(stat->isp->dev,
-                                "%s: Can't acquire memory for "
-                                "DMA buffer %d\n", stat->subdev.name, i);
-                       isp_stat_bufs_free(stat);
-                       return -ENOMEM;
-               }
-               buf->empty = 1;
+       buf->virt_addr = dma_alloc_coherent(dev, size, &buf->dma_addr,
+                                           GFP_KERNEL | GFP_DMA);
+       if (!buf->virt_addr)
+               return -ENOMEM;
 
-               dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated."
-                       "dma_addr=0x%08lx virt_addr=0x%08lx\n",
-                       stat->subdev.name, i, (unsigned long)buf->dma_addr,
-                       (unsigned long)buf->virt_addr);
+       ret = dma_get_sgtable(dev, &buf->sgt, buf->virt_addr, buf->dma_addr,
+                             size);
+       if (ret < 0) {
+               dma_free_coherent(dev, size, buf->virt_addr, buf->dma_addr);
+               buf->virt_addr = NULL;
+               buf->dma_addr = 0;
+               return ret;
        }
 
        return 0;
 }
 
+/*
+ * The device passed to the DMA API depends on whether the statistics block uses
+ * ISP DMA, external DMA or PIO to transfer data.
+ *
+ * The first case (for the AEWB and AF engines) passes the ISP device, resulting
+ * in the DMA buffers being mapped through the ISP IOMMU.
+ *
+ * The second case (for the histogram engine) should pass the DMA engine device.
+ * As that device isn't accessible through the OMAP DMA engine API the driver
+ * passes NULL instead, resulting in the buffers being mapped directly as
+ * physical pages.
+ *
+ * The third case (for the histogram engine) doesn't require any mapping. The
+ * buffers could be allocated with kmalloc/vmalloc, but we still use
+ * dma_alloc_coherent() for consistency purpose.
+ */
 static int isp_stat_bufs_alloc(struct ispstat *stat, u32 size)
 {
+       struct device *dev = ISP_STAT_USES_DMAENGINE(stat)
+                          ? NULL : stat->isp->dev;
        unsigned long flags;
+       unsigned int i;
 
        spin_lock_irqsave(&stat->isp->stat_lock, flags);
 
@@ -489,10 +437,31 @@ static int isp_stat_bufs_alloc(struct ispstat *stat, u32 size)
 
        isp_stat_bufs_free(stat);
 
-       if (IS_COHERENT_BUF(stat))
-               return isp_stat_bufs_alloc_dma(stat, size);
-       else
-               return isp_stat_bufs_alloc_iommu(stat, size);
+       stat->buf_alloc_size = size;
+
+       for (i = 0; i < STAT_MAX_BUFS; i++) {
+               struct ispstat_buffer *buf = &stat->buf[i];
+               int ret;
+
+               ret = isp_stat_bufs_alloc_one(dev, buf, size);
+               if (ret < 0) {
+                       dev_err(stat->isp->dev,
+                               "%s: Failed to allocate DMA buffer %u\n",
+                               stat->subdev.name, i);
+                       isp_stat_bufs_free(stat);
+                       return ret;
+               }
+
+               buf->empty = 1;
+
+               dev_dbg(stat->isp->dev,
+                       "%s: buffer[%u] allocated. dma=0x%08lx virt=0x%08lx",
+                       stat->subdev.name, i,
+                       (unsigned long)buf->dma_addr,
+                       (unsigned long)buf->virt_addr);
+       }
+
+       return 0;
 }
 
 static void isp_stat_queue_event(struct ispstat *stat, int err)
index 9a047c929b9f95d47a758267cc341c92bd4f5c8c..58d6ac7cb6648ce18f2e8284cc85fe9d96199242 100644 (file)
@@ -46,8 +46,7 @@
 struct ispstat;
 
 struct ispstat_buffer {
-       unsigned long iommu_addr;
-       struct iovm_struct *iovm;
+       struct sg_table sgt;
        void *virt_addr;
        dma_addr_t dma_addr;
        struct timespec ts;
index 85b4036ba5e43296ce647c4f857e8484b430846c..e36bac26476c0fd7ac382f5e85effc07a7a0b202 100644 (file)
@@ -27,7 +27,6 @@
 #include <linux/clk.h>
 #include <linux/mm.h>
 #include <linux/module.h>
-#include <linux/omap-iommu.h>
 #include <linux/pagemap.h>
 #include <linux/scatterlist.h>
 #include <linux/sched.h>
@@ -35,6 +34,7 @@
 #include <linux/vmalloc.h>
 #include <media/v4l2-dev.h>
 #include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
 
 #include "ispvideo.h"
 #include "isp.h"
@@ -325,91 +325,37 @@ isp_video_check_format(struct isp_video *video, struct isp_video_fh *vfh)
        return ret;
 }
 
-/* -----------------------------------------------------------------------------
- * IOMMU management
- */
-
-#define IOMMU_FLAG     (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8)
-
-/*
- * ispmmu_vmap - Wrapper for Virtual memory mapping of a scatter gather list
- * @isp: Device pointer specific to the OMAP3 ISP.
- * @sglist: Pointer to source Scatter gather list to allocate.
- * @sglen: Number of elements of the scatter-gatter list.
- *
- * Returns a resulting mapped device address by the ISP MMU, or -ENOMEM if
- * we ran out of memory.
- */
-static dma_addr_t
-ispmmu_vmap(struct isp_device *isp, const struct scatterlist *sglist, int sglen)
-{
-       struct sg_table *sgt;
-       u32 da;
-
-       sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
-       if (sgt == NULL)
-               return -ENOMEM;
-
-       sgt->sgl = (struct scatterlist *)sglist;
-       sgt->nents = sglen;
-       sgt->orig_nents = sglen;
-
-       da = omap_iommu_vmap(isp->domain, isp->dev, 0, sgt, IOMMU_FLAG);
-       if (IS_ERR_VALUE(da))
-               kfree(sgt);
-
-       return da;
-}
-
-/*
- * ispmmu_vunmap - Unmap a device address from the ISP MMU
- * @isp: Device pointer specific to the OMAP3 ISP.
- * @da: Device address generated from a ispmmu_vmap call.
- */
-static void ispmmu_vunmap(struct isp_device *isp, dma_addr_t da)
-{
-       struct sg_table *sgt;
-
-       sgt = omap_iommu_vunmap(isp->domain, isp->dev, (u32)da);
-       kfree(sgt);
-}
-
 /* -----------------------------------------------------------------------------
  * Video queue operations
  */
 
-static void isp_video_queue_prepare(struct isp_video_queue *queue,
-                                   unsigned int *nbuffers, unsigned int *size)
+static int isp_video_queue_setup(struct vb2_queue *queue,
+                                const struct v4l2_format *fmt,
+                                unsigned int *count, unsigned int *num_planes,
+                                unsigned int sizes[], void *alloc_ctxs[])
 {
-       struct isp_video_fh *vfh =
-               container_of(queue, struct isp_video_fh, queue);
+       struct isp_video_fh *vfh = vb2_get_drv_priv(queue);
        struct isp_video *video = vfh->video;
 
-       *size = vfh->format.fmt.pix.sizeimage;
-       if (*size == 0)
-               return;
+       *num_planes = 1;
 
-       *nbuffers = min(*nbuffers, video->capture_mem / PAGE_ALIGN(*size));
-}
+       sizes[0] = vfh->format.fmt.pix.sizeimage;
+       if (sizes[0] == 0)
+               return -EINVAL;
 
-static void isp_video_buffer_cleanup(struct isp_video_buffer *buf)
-{
-       struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue);
-       struct isp_buffer *buffer = to_isp_buffer(buf);
-       struct isp_video *video = vfh->video;
+       alloc_ctxs[0] = video->alloc_ctx;
 
-       if (buffer->isp_addr) {
-               ispmmu_vunmap(video->isp, buffer->isp_addr);
-               buffer->isp_addr = 0;
-       }
+       *count = min(*count, video->capture_mem / PAGE_ALIGN(sizes[0]));
+
+       return 0;
 }
 
-static int isp_video_buffer_prepare(struct isp_video_buffer *buf)
+static int isp_video_buffer_prepare(struct vb2_buffer *buf)
 {
-       struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue);
+       struct isp_video_fh *vfh = vb2_get_drv_priv(buf->vb2_queue);
        struct isp_buffer *buffer = to_isp_buffer(buf);
        struct isp_video *video = vfh->video;
-       unsigned long addr;
+       dma_addr_t addr;
 
        /* Refuse to prepare the buffer is the video node has registered an
         * error. We don't need to take any lock here as the operation is
@@ -420,19 +366,16 @@ static int isp_video_buffer_prepare(struct isp_video_buffer *buf)
        if (unlikely(video->error))
                return -EIO;
 
-       addr = ispmmu_vmap(video->isp, buf->sglist, buf->sglen);
-       if (IS_ERR_VALUE(addr))
-               return -EIO;
-
+       addr = vb2_dma_contig_plane_dma_addr(buf, 0);
        if (!IS_ALIGNED(addr, 32)) {
-               dev_dbg(video->isp->dev, "Buffer address must be "
-                       "aligned to 32 bytes boundary.\n");
-               ispmmu_vunmap(video->isp, buffer->isp_addr);
+               dev_dbg(video->isp->dev,
+                       "Buffer address must be aligned to 32 bytes boundary.\n");
                return -EINVAL;
        }
 
-       buf->vbuf.bytesused = vfh->format.fmt.pix.sizeimage;
-       buffer->isp_addr = addr;
+       vb2_set_plane_payload(&buffer->vb, 0, vfh->format.fmt.pix.sizeimage);
+       buffer->dma = addr;
+
        return 0;
 }
 
@@ -445,9 +388,9 @@ static int isp_video_buffer_prepare(struct isp_video_buffer *buf)
  * If the pipeline is busy, it will be restarted in the output module interrupt
  * handler.
  */
-static void isp_video_buffer_queue(struct isp_video_buffer *buf)
+static void isp_video_buffer_queue(struct vb2_buffer *buf)
 {
-       struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue);
+       struct isp_video_fh *vfh = vb2_get_drv_priv(buf->vb2_queue);
        struct isp_buffer *buffer = to_isp_buffer(buf);
        struct isp_video *video = vfh->video;
        struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity);
@@ -456,14 +399,18 @@ static void isp_video_buffer_queue(struct isp_video_buffer *buf)
        unsigned int empty;
        unsigned int start;
 
+       spin_lock_irqsave(&video->irqlock, flags);
+
        if (unlikely(video->error)) {
-               buf->state = ISP_BUF_STATE_ERROR;
-               wake_up(&buf->wait);
+               vb2_buffer_done(&buffer->vb, VB2_BUF_STATE_ERROR);
+               spin_unlock_irqrestore(&video->irqlock, flags);
                return;
        }
 
        empty = list_empty(&video->dmaqueue);
-       list_add_tail(&buffer->buffer.irqlist, &video->dmaqueue);
+       list_add_tail(&buffer->irqlist, &video->dmaqueue);
+
+       spin_unlock_irqrestore(&video->irqlock, flags);
 
        if (empty) {
                if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -487,23 +434,22 @@ static void isp_video_buffer_queue(struct isp_video_buffer *buf)
        }
 }
 
-static const struct isp_video_queue_operations isp_video_queue_ops = {
-       .queue_prepare = &isp_video_queue_prepare,
-       .buffer_prepare = &isp_video_buffer_prepare,
-       .buffer_queue = &isp_video_buffer_queue,
-       .buffer_cleanup = &isp_video_buffer_cleanup,
+static const struct vb2_ops isp_video_queue_ops = {
+       .queue_setup = isp_video_queue_setup,
+       .buf_prepare = isp_video_buffer_prepare,
+       .buf_queue = isp_video_buffer_queue,
 };
 
 /*
  * omap3isp_video_buffer_next - Complete the current buffer and return the next
  * @video: ISP video object
  *
- * Remove the current video buffer from the DMA queue and fill its timestamp,
- * field count and state fields before waking up its completion handler.
+ * Remove the current video buffer from the DMA queue and fill its timestamp and
+ * field count before handing it back to videobuf2.
  *
- * For capture video nodes the buffer state is set to ISP_BUF_STATE_DONE if no
- * error has been flagged in the pipeline, or to ISP_BUF_STATE_ERROR otherwise.
- * For video output nodes the buffer state is always set to ISP_BUF_STATE_DONE.
+ * For capture video nodes the buffer state is set to VB2_BUF_STATE_DONE if no
+ * error has been flagged in the pipeline, or to VB2_BUF_STATE_ERROR otherwise.
+ * For video output nodes the buffer state is always set to VB2_BUF_STATE_DONE.
  *
  * The DMA queue is expected to contain at least one buffer.
  *
@@ -513,26 +459,25 @@ static const struct isp_video_queue_operations isp_video_queue_ops = {
 struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video)
 {
        struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity);
-       struct isp_video_queue *queue = video->queue;
        enum isp_pipeline_state state;
-       struct isp_video_buffer *buf;
+       struct isp_buffer *buf;
        unsigned long flags;
        struct timespec ts;
 
-       spin_lock_irqsave(&queue->irqlock, flags);
+       spin_lock_irqsave(&video->irqlock, flags);
        if (WARN_ON(list_empty(&video->dmaqueue))) {
-               spin_unlock_irqrestore(&queue->irqlock, flags);
+               spin_unlock_irqrestore(&video->irqlock, flags);
                return NULL;
        }
 
-       buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer,
+       buf = list_first_entry(&video->dmaqueue, struct isp_buffer,
                               irqlist);
        list_del(&buf->irqlist);
-       spin_unlock_irqrestore(&queue->irqlock, flags);
+       spin_unlock_irqrestore(&video->irqlock, flags);
 
        ktime_get_ts(&ts);
-       buf->vbuf.timestamp.tv_sec = ts.tv_sec;
-       buf->vbuf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+       buf->vb.v4l2_buf.timestamp.tv_sec = ts.tv_sec;
+       buf->vb.v4l2_buf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
 
        /* Do frame number propagation only if this is the output video node.
         * Frame number either comes from the CSI receivers or it gets
@@ -541,22 +486,27 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video)
         * first, so the input number might lag behind by 1 in some cases.
         */
        if (video == pipe->output && !pipe->do_propagation)
-               buf->vbuf.sequence = atomic_inc_return(&pipe->frame_number);
+               buf->vb.v4l2_buf.sequence =
+                       atomic_inc_return(&pipe->frame_number);
        else
-               buf->vbuf.sequence = atomic_read(&pipe->frame_number);
+               buf->vb.v4l2_buf.sequence = atomic_read(&pipe->frame_number);
 
        /* Report pipeline errors to userspace on the capture device side. */
-       if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->error) {
-               buf->state = ISP_BUF_STATE_ERROR;
+       if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->error) {
+               state = VB2_BUF_STATE_ERROR;
                pipe->error = false;
        } else {
-               buf->state = ISP_BUF_STATE_DONE;
+               state = VB2_BUF_STATE_DONE;
        }
 
-       wake_up(&buf->wait);
+       vb2_buffer_done(&buf->vb, state);
+
+       spin_lock_irqsave(&video->irqlock, flags);
 
        if (list_empty(&video->dmaqueue)) {
-               if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               spin_unlock_irqrestore(&video->irqlock, flags);
+
+               if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
                        state = ISP_PIPELINE_QUEUE_OUTPUT
                              | ISP_PIPELINE_STREAM;
                else
@@ -571,16 +521,19 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video)
                return NULL;
        }
 
-       if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) {
-               spin_lock_irqsave(&pipe->lock, flags);
+       if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) {
+               spin_lock(&pipe->lock);
                pipe->state &= ~ISP_PIPELINE_STREAM;
-               spin_unlock_irqrestore(&pipe->lock, flags);
+               spin_unlock(&pipe->lock);
        }
 
-       buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer,
+       buf = list_first_entry(&video->dmaqueue, struct isp_buffer,
                               irqlist);
-       buf->state = ISP_BUF_STATE_ACTIVE;
-       return to_isp_buffer(buf);
+       buf->vb.state = VB2_BUF_STATE_ACTIVE;
+
+       spin_unlock_irqrestore(&video->irqlock, flags);
+
+       return buf;
 }
 
 /*
@@ -592,25 +545,22 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video)
  */
 void omap3isp_video_cancel_stream(struct isp_video *video)
 {
-       struct isp_video_queue *queue = video->queue;
        unsigned long flags;
 
-       spin_lock_irqsave(&queue->irqlock, flags);
+       spin_lock_irqsave(&video->irqlock, flags);
 
        while (!list_empty(&video->dmaqueue)) {
-               struct isp_video_buffer *buf;
+               struct isp_buffer *buf;
 
                buf = list_first_entry(&video->dmaqueue,
-                                      struct isp_video_buffer, irqlist);
+                                      struct isp_buffer, irqlist);
                list_del(&buf->irqlist);
-
-               buf->state = ISP_BUF_STATE_ERROR;
-               wake_up(&buf->wait);
+               vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
        }
 
        video->error = true;
 
-       spin_unlock_irqrestore(&queue->irqlock, flags);
+       spin_unlock_irqrestore(&video->irqlock, flags);
 }
 
 /*
@@ -627,12 +577,15 @@ void omap3isp_video_resume(struct isp_video *video, int continuous)
 {
        struct isp_buffer *buf = NULL;
 
-       if (continuous && video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               omap3isp_video_queue_discard_done(video->queue);
+       if (continuous && video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+               mutex_lock(&video->queue_lock);
+               vb2_discard_done(video->queue);
+               mutex_unlock(&video->queue_lock);
+       }
 
        if (!list_empty(&video->dmaqueue)) {
                buf = list_first_entry(&video->dmaqueue,
-                                      struct isp_buffer, buffer.irqlist);
+                                      struct isp_buffer, irqlist);
                video->ops->queue(video, buf);
                video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_QUEUED;
        } else {
@@ -840,33 +793,56 @@ static int
 isp_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)
 {
        struct isp_video_fh *vfh = to_isp_video_fh(fh);
+       struct isp_video *video = video_drvdata(file);
+       int ret;
 
-       return omap3isp_video_queue_reqbufs(&vfh->queue, rb);
+       mutex_lock(&video->queue_lock);
+       ret = vb2_reqbufs(&vfh->queue, rb);
+       mutex_unlock(&video->queue_lock);
+
+       return ret;
 }
 
 static int
 isp_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
 {
        struct isp_video_fh *vfh = to_isp_video_fh(fh);
+       struct isp_video *video = video_drvdata(file);
+       int ret;
+
+       mutex_lock(&video->queue_lock);
+       ret = vb2_querybuf(&vfh->queue, b);
+       mutex_unlock(&video->queue_lock);
 
-       return omap3isp_video_queue_querybuf(&vfh->queue, b);
+       return ret;
 }
 
 static int
 isp_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
 {
        struct isp_video_fh *vfh = to_isp_video_fh(fh);
+       struct isp_video *video = video_drvdata(file);
+       int ret;
 
-       return omap3isp_video_queue_qbuf(&vfh->queue, b);
+       mutex_lock(&video->queue_lock);
+       ret = vb2_qbuf(&vfh->queue, b);
+       mutex_unlock(&video->queue_lock);
+
+       return ret;
 }
 
 static int
 isp_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
 {
        struct isp_video_fh *vfh = to_isp_video_fh(fh);
+       struct isp_video *video = video_drvdata(file);
+       int ret;
+
+       mutex_lock(&video->queue_lock);
+       ret = vb2_dqbuf(&vfh->queue, b, file->f_flags & O_NONBLOCK);
+       mutex_unlock(&video->queue_lock);
 
-       return omap3isp_video_queue_dqbuf(&vfh->queue, b,
-                                         file->f_flags & O_NONBLOCK);
+       return ret;
 }
 
 static int isp_video_check_external_subdevs(struct isp_video *video,
@@ -1006,11 +982,6 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
 
        mutex_lock(&video->stream_lock);
 
-       if (video->streaming) {
-               mutex_unlock(&video->stream_lock);
-               return -EBUSY;
-       }
-
        /* Start streaming on the pipeline. No link touching an entity in the
         * pipeline can be activated or deactivated once streaming is started.
         */
@@ -1069,7 +1040,9 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
        INIT_LIST_HEAD(&video->dmaqueue);
        atomic_set(&pipe->frame_number, -1);
 
-       ret = omap3isp_video_queue_streamon(&vfh->queue);
+       mutex_lock(&video->queue_lock);
+       ret = vb2_streamon(&vfh->queue, type);
+       mutex_unlock(&video->queue_lock);
        if (ret < 0)
                goto err_check_format;
 
@@ -1082,19 +1055,19 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
                                              ISP_PIPELINE_STREAM_CONTINUOUS);
                if (ret < 0)
                        goto err_set_stream;
-               spin_lock_irqsave(&video->queue->irqlock, flags);
+               spin_lock_irqsave(&video->irqlock, flags);
                if (list_empty(&video->dmaqueue))
                        video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN;
-               spin_unlock_irqrestore(&video->queue->irqlock, flags);
+               spin_unlock_irqrestore(&video->irqlock, flags);
        }
 
-       video->streaming = 1;
-
        mutex_unlock(&video->stream_lock);
        return 0;
 
 err_set_stream:
-       omap3isp_video_queue_streamoff(&vfh->queue);
+       mutex_lock(&video->queue_lock);
+       vb2_streamoff(&vfh->queue, type);
+       mutex_unlock(&video->queue_lock);
 err_check_format:
        media_entity_pipeline_stop(&video->video.entity);
 err_pipeline_start:
@@ -1130,9 +1103,9 @@ isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
        mutex_lock(&video->stream_lock);
 
        /* Make sure we're not streaming yet. */
-       mutex_lock(&vfh->queue.lock);
-       streaming = vfh->queue.streaming;
-       mutex_unlock(&vfh->queue.lock);
+       mutex_lock(&video->queue_lock);
+       streaming = vb2_is_streaming(&vfh->queue);
+       mutex_unlock(&video->queue_lock);
 
        if (!streaming)
                goto done;
@@ -1151,9 +1124,12 @@ isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
 
        /* Stop the stream. */
        omap3isp_pipeline_set_stream(pipe, ISP_PIPELINE_STREAM_STOPPED);
-       omap3isp_video_queue_streamoff(&vfh->queue);
+       omap3isp_video_cancel_stream(video);
+
+       mutex_lock(&video->queue_lock);
+       vb2_streamoff(&vfh->queue, type);
+       mutex_unlock(&video->queue_lock);
        video->queue = NULL;
-       video->streaming = 0;
        video->error = false;
 
        if (video->isp->pdata->set_constraints)
@@ -1223,6 +1199,7 @@ static int isp_video_open(struct file *file)
 {
        struct isp_video *video = video_drvdata(file);
        struct isp_video_fh *handle;
+       struct vb2_queue *queue;
        int ret = 0;
 
        handle = kzalloc(sizeof(*handle), GFP_KERNEL);
@@ -1244,9 +1221,20 @@ static int isp_video_open(struct file *file)
                goto done;
        }
 
-       omap3isp_video_queue_init(&handle->queue, video->type,
-                                 &isp_video_queue_ops, video->isp->dev,
-                                 sizeof(struct isp_buffer));
+       queue = &handle->queue;
+       queue->type = video->type;
+       queue->io_modes = VB2_MMAP | VB2_USERPTR;
+       queue->drv_priv = handle;
+       queue->ops = &isp_video_queue_ops;
+       queue->mem_ops = &vb2_dma_contig_memops;
+       queue->buf_struct_size = sizeof(struct isp_buffer);
+       queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+
+       ret = vb2_queue_init(&handle->queue);
+       if (ret < 0) {
+               omap3isp_put(video->isp);
+               goto done;
+       }
 
        memset(&handle->format, 0, sizeof(handle->format));
        handle->format.type = video->type;
@@ -1273,9 +1261,9 @@ static int isp_video_release(struct file *file)
        /* Disable streaming and free the buffers queue resources. */
        isp_video_streamoff(file, vfh, video->type);
 
-       mutex_lock(&handle->queue.lock);
-       omap3isp_video_queue_cleanup(&handle->queue);
-       mutex_unlock(&handle->queue.lock);
+       mutex_lock(&video->queue_lock);
+       vb2_queue_release(&handle->queue);
+       mutex_unlock(&video->queue_lock);
 
        omap3isp_pipeline_pm_use(&video->video.entity, 0);
 
@@ -1292,16 +1280,27 @@ static int isp_video_release(struct file *file)
 static unsigned int isp_video_poll(struct file *file, poll_table *wait)
 {
        struct isp_video_fh *vfh = to_isp_video_fh(file->private_data);
-       struct isp_video_queue *queue = &vfh->queue;
+       struct isp_video *video = video_drvdata(file);
+       int ret;
 
-       return omap3isp_video_queue_poll(queue, file, wait);
+       mutex_lock(&video->queue_lock);
+       ret = vb2_poll(&vfh->queue, file, wait);
+       mutex_unlock(&video->queue_lock);
+
+       return ret;
 }
 
 static int isp_video_mmap(struct file *file, struct vm_area_struct *vma)
 {
        struct isp_video_fh *vfh = to_isp_video_fh(file->private_data);
+       struct isp_video *video = video_drvdata(file);
+       int ret;
+
+       mutex_lock(&video->queue_lock);
+       ret = vb2_mmap(&vfh->queue, vma);
+       mutex_unlock(&video->queue_lock);
 
-       return omap3isp_video_queue_mmap(&vfh->queue, vma);
+       return ret;
 }
 
 static struct v4l2_file_operations isp_video_fops = {
@@ -1342,15 +1341,23 @@ int omap3isp_video_init(struct isp_video *video, const char *name)
                return -EINVAL;
        }
 
+       video->alloc_ctx = vb2_dma_contig_init_ctx(video->isp->dev);
+       if (IS_ERR(video->alloc_ctx))
+               return PTR_ERR(video->alloc_ctx);
+
        ret = media_entity_init(&video->video.entity, 1, &video->pad, 0);
-       if (ret < 0)
+       if (ret < 0) {
+               vb2_dma_contig_cleanup_ctx(video->alloc_ctx);
                return ret;
+       }
 
        mutex_init(&video->mutex);
        atomic_set(&video->active, 0);
 
        spin_lock_init(&video->pipe.lock);
        mutex_init(&video->stream_lock);
+       mutex_init(&video->queue_lock);
+       spin_lock_init(&video->irqlock);
 
        /* Initialize the video device. */
        if (video->ops == NULL)
@@ -1371,7 +1378,9 @@ int omap3isp_video_init(struct isp_video *video, const char *name)
 
 void omap3isp_video_cleanup(struct isp_video *video)
 {
+       vb2_dma_contig_cleanup_ctx(video->alloc_ctx);
        media_entity_cleanup(&video->video.entity);
+       mutex_destroy(&video->queue_lock);
        mutex_destroy(&video->stream_lock);
        mutex_destroy(&video->mutex);
 }
index 4e194076cc60d611727a6441231f79006dbfadfc..7d2e82122ecda431d417ae10ba8193b9fb9154ec 100644 (file)
@@ -30,8 +30,7 @@
 #include <media/media-entity.h>
 #include <media/v4l2-dev.h>
 #include <media/v4l2-fh.h>
-
-#include "ispqueue.h"
+#include <media/videobuf2-core.h>
 
 #define ISP_VIDEO_DRIVER_NAME          "ispvideo"
 #define ISP_VIDEO_DRIVER_VERSION       "0.0.2"
@@ -124,17 +123,19 @@ static inline int isp_pipeline_ready(struct isp_pipeline *pipe)
                               ISP_PIPELINE_IDLE_OUTPUT);
 }
 
-/*
- * struct isp_buffer - ISP buffer
- * @buffer: ISP video buffer
- * @isp_addr: MMU mapped address (a.k.a. device address) of the buffer.
+/**
+ * struct isp_buffer - ISP video buffer
+ * @vb: videobuf2 buffer
+ * @irqlist: List head for insertion into IRQ queue
+ * @dma: DMA address
  */
 struct isp_buffer {
-       struct isp_video_buffer buffer;
-       dma_addr_t isp_addr;
+       struct vb2_buffer vb;
+       struct list_head irqlist;
+       dma_addr_t dma;
 };
 
-#define to_isp_buffer(buf)     container_of(buf, struct isp_buffer, buffer)
+#define to_isp_buffer(buf)     container_of(buf, struct isp_buffer, vb)
 
 enum isp_video_dmaqueue_flags {
        /* Set if DMA queue becomes empty when ISP_PIPELINE_STREAM_CONTINUOUS */
@@ -172,16 +173,16 @@ struct isp_video {
        unsigned int bpl_value;         /* bytes per line value */
        unsigned int bpl_padding;       /* padding at end of line */
 
-       /* Entity video node streaming */
-       unsigned int streaming:1;
-
        /* Pipeline state */
        struct isp_pipeline pipe;
        struct mutex stream_lock;       /* pipeline and stream states */
        bool error;
 
        /* Video buffers queue */
-       struct isp_video_queue *queue;
+       void *alloc_ctx;
+       struct vb2_queue *queue;
+       struct mutex queue_lock;        /* protects the queue */
+       spinlock_t irqlock;             /* protects dmaqueue */
        struct list_head dmaqueue;
        enum isp_video_dmaqueue_flags dmaqueue_flags;
 
@@ -193,7 +194,7 @@ struct isp_video {
 struct isp_video_fh {
        struct v4l2_fh vfh;
        struct isp_video *video;
-       struct isp_video_queue queue;
+       struct vb2_queue queue;
        struct v4l2_format format;
        struct v4l2_fract timeperframe;
 };
index 349e659d75fb861359165665638fad93a2293631..7c4489c4236502042fc1fcb3a0ff545f2f7639a7 100644 (file)
@@ -1199,6 +1199,30 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
 }
 EXPORT_SYMBOL_GPL(vb2_buffer_done);
 
+/**
+ * vb2_discard_done() - discard all buffers marked as DONE
+ * @q:         videobuf2 queue
+ *
+ * This function is intended to be used with suspend/resume operations. It
+ * discards all 'done' buffers as they would be too old to be requested after
+ * resume.
+ *
+ * Drivers must stop the hardware and synchronize with interrupt handlers and/or
+ * delayed works before calling this function to make sure no buffer will be
+ * touched by the driver and/or hardware.
+ */
+void vb2_discard_done(struct vb2_queue *q)
+{
+       struct vb2_buffer *vb;
+       unsigned long flags;
+
+       spin_lock_irqsave(&q->done_lock, flags);
+       list_for_each_entry(vb, &q->done_list, done_entry)
+               vb->state = VB2_BUF_STATE_ERROR;
+       spin_unlock_irqrestore(&q->done_lock, flags);
+}
+EXPORT_SYMBOL_GPL(vb2_discard_done);
+
 /**
  * __fill_vb2_buffer() - fill a vb2_buffer with information provided in a
  * v4l2_buffer by the userspace. The caller has already verified that struct
index 0a87e56913411a9abab46c99b13053abee3d96af..cc8d4a6099cdc602071af7616e1451896e7f6959 100644 (file)
@@ -448,7 +448,6 @@ mmc_spi_command_send(struct mmc_spi_host *host,
 {
        struct scratch          *data = host->data;
        u8                      *cp = data->status;
-       u32                     arg = cmd->arg;
        int                     status;
        struct spi_transfer     *t;
 
@@ -465,14 +464,12 @@ mmc_spi_command_send(struct mmc_spi_host *host,
         * We init the whole buffer to all-ones, which is what we need
         * to write while we're reading (later) response data.
         */
-       memset(cp++, 0xff, sizeof(data->status));
+       memset(cp, 0xff, sizeof(data->status));
 
-       *cp++ = 0x40 | cmd->opcode;
-       *cp++ = (u8)(arg >> 24);
-       *cp++ = (u8)(arg >> 16);
-       *cp++ = (u8)(arg >> 8);
-       *cp++ = (u8)arg;
-       *cp++ = (crc7(0, &data->status[1], 5) << 1) | 0x01;
+       cp[1] = 0x40 | cmd->opcode;
+       put_unaligned_be32(cmd->arg, cp+2);
+       cp[6] = crc7_be(0, cp+1, 5) | 0x01;
+       cp += 7;
 
        /* Then, read up to 13 bytes (while writing all-ones):
         *  - N(CR) (== 1..8) bytes of all-ones
@@ -711,10 +708,7 @@ mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t,
         * so we have to cope with this situation and check the response
         * bit-by-bit. Arggh!!!
         */
-       pattern  = scratch->status[0] << 24;
-       pattern |= scratch->status[1] << 16;
-       pattern |= scratch->status[2] << 8;
-       pattern |= scratch->status[3];
+       pattern = get_unaligned_be32(scratch->status);
 
        /* First 3 bit of pattern are undefined */
        pattern |= 0xE0000000;
index b667a51ed21517a3ee6cf2be6ab4c7e306a713a2..0dfeaf5da3f2c914e2fd43e40d4bf4f3d8aff079 100644 (file)
@@ -157,7 +157,7 @@ static inline struct aggregator *__get_first_agg(struct port *port)
 
        rcu_read_lock();
        first_slave = bond_first_slave_rcu(bond);
-       agg = first_slave ? &(SLAVE_AD_INFO(first_slave).aggregator) : NULL;
+       agg = first_slave ? &(SLAVE_AD_INFO(first_slave)->aggregator) : NULL;
        rcu_read_unlock();
 
        return agg;
@@ -192,7 +192,7 @@ static inline void __enable_port(struct port *port)
 {
        struct slave *slave = port->slave;
 
-       if ((slave->link == BOND_LINK_UP) && IS_UP(slave->dev))
+       if ((slave->link == BOND_LINK_UP) && bond_slave_is_up(slave))
                bond_set_slave_active_flags(slave, BOND_SLAVE_NOTIFY_LATER);
 }
 
@@ -241,7 +241,7 @@ static inline int __check_agg_selection_timer(struct port *port)
  */
 static inline void __get_state_machine_lock(struct port *port)
 {
-       spin_lock_bh(&(SLAVE_AD_INFO(port->slave).state_machine_lock));
+       spin_lock_bh(&(SLAVE_AD_INFO(port->slave)->state_machine_lock));
 }
 
 /**
@@ -250,7 +250,7 @@ static inline void __get_state_machine_lock(struct port *port)
  */
 static inline void __release_state_machine_lock(struct port *port)
 {
-       spin_unlock_bh(&(SLAVE_AD_INFO(port->slave).state_machine_lock));
+       spin_unlock_bh(&(SLAVE_AD_INFO(port->slave)->state_machine_lock));
 }
 
 /**
@@ -350,7 +350,7 @@ static u8 __get_duplex(struct port *port)
 static inline void __initialize_port_locks(struct slave *slave)
 {
        /* make sure it isn't called twice */
-       spin_lock_init(&(SLAVE_AD_INFO(slave).state_machine_lock));
+       spin_lock_init(&(SLAVE_AD_INFO(slave)->state_machine_lock));
 }
 
 /* Conversions */
@@ -688,8 +688,8 @@ static struct aggregator *__get_active_agg(struct aggregator *aggregator)
        struct slave *slave;
 
        bond_for_each_slave_rcu(bond, slave, iter)
-               if (SLAVE_AD_INFO(slave).aggregator.is_active)
-                       return &(SLAVE_AD_INFO(slave).aggregator);
+               if (SLAVE_AD_INFO(slave)->aggregator.is_active)
+                       return &(SLAVE_AD_INFO(slave)->aggregator);
 
        return NULL;
 }
@@ -1293,7 +1293,7 @@ static void ad_port_selection_logic(struct port *port)
        }
        /* search on all aggregators for a suitable aggregator for this port */
        bond_for_each_slave(bond, slave, iter) {
-               aggregator = &(SLAVE_AD_INFO(slave).aggregator);
+               aggregator = &(SLAVE_AD_INFO(slave)->aggregator);
 
                /* keep a free aggregator for later use(if needed) */
                if (!aggregator->lag_ports) {
@@ -1504,7 +1504,7 @@ static void ad_agg_selection_logic(struct aggregator *agg)
        best = (active && agg_device_up(active)) ? active : NULL;
 
        bond_for_each_slave_rcu(bond, slave, iter) {
-               agg = &(SLAVE_AD_INFO(slave).aggregator);
+               agg = &(SLAVE_AD_INFO(slave)->aggregator);
 
                agg->is_active = 0;
 
@@ -1549,7 +1549,7 @@ static void ad_agg_selection_logic(struct aggregator *agg)
                         best->slave ? best->slave->dev->name : "NULL");
 
                bond_for_each_slave_rcu(bond, slave, iter) {
-                       agg = &(SLAVE_AD_INFO(slave).aggregator);
+                       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,
@@ -1840,16 +1840,16 @@ void bond_3ad_bind_slave(struct slave *slave)
        struct aggregator *aggregator;
 
        /* check that the slave has not been initialized yet. */
-       if (SLAVE_AD_INFO(slave).port.slave != slave) {
+       if (SLAVE_AD_INFO(slave)->port.slave != slave) {
 
                /* port initialization */
-               port = &(SLAVE_AD_INFO(slave).port);
+               port = &(SLAVE_AD_INFO(slave)->port);
 
                ad_initialize_port(port, bond->params.lacp_fast);
 
                __initialize_port_locks(slave);
                port->slave = slave;
-               port->actor_port_number = SLAVE_AD_INFO(slave).id;
+               port->actor_port_number = SLAVE_AD_INFO(slave)->id;
                /* key is determined according to the link speed, duplex and user key(which
                 * is yet not supported)
                 */
@@ -1874,7 +1874,7 @@ void bond_3ad_bind_slave(struct slave *slave)
                __disable_port(port);
 
                /* aggregator initialization */
-               aggregator = &(SLAVE_AD_INFO(slave).aggregator);
+               aggregator = &(SLAVE_AD_INFO(slave)->aggregator);
 
                ad_initialize_agg(aggregator);
 
@@ -1903,8 +1903,8 @@ void bond_3ad_unbind_slave(struct slave *slave)
        struct slave *slave_iter;
        struct list_head *iter;
 
-       aggregator = &(SLAVE_AD_INFO(slave).aggregator);
-       port = &(SLAVE_AD_INFO(slave).port);
+       aggregator = &(SLAVE_AD_INFO(slave)->aggregator);
+       port = &(SLAVE_AD_INFO(slave)->port);
 
        /* if slave is null, the whole port is not initialized */
        if (!port->slave) {
@@ -1932,7 +1932,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
                    (aggregator->lag_ports->next_port_in_aggregator)) {
                        /* find new aggregator for the related port(s) */
                        bond_for_each_slave(bond, slave_iter, iter) {
-                               new_aggregator = &(SLAVE_AD_INFO(slave_iter).aggregator);
+                               new_aggregator = &(SLAVE_AD_INFO(slave_iter)->aggregator);
                                /* if the new aggregator is empty, or it is
                                 * connected to our port only
                                 */
@@ -2010,7 +2010,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
 
        /* find the aggregator that this port is connected to */
        bond_for_each_slave(bond, slave_iter, iter) {
-               temp_aggregator = &(SLAVE_AD_INFO(slave_iter).aggregator);
+               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;
@@ -2076,7 +2076,7 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
        if (BOND_AD_INFO(bond).agg_select_timer &&
            !(--BOND_AD_INFO(bond).agg_select_timer)) {
                slave = bond_first_slave_rcu(bond);
-               port = slave ? &(SLAVE_AD_INFO(slave).port) : NULL;
+               port = slave ? &(SLAVE_AD_INFO(slave)->port) : NULL;
 
                /* select the active aggregator for the bond */
                if (port) {
@@ -2094,7 +2094,7 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
 
        /* for each port run the state machines */
        bond_for_each_slave_rcu(bond, slave, iter) {
-               port = &(SLAVE_AD_INFO(slave).port);
+               port = &(SLAVE_AD_INFO(slave)->port);
                if (!port->slave) {
                        pr_warn_ratelimited("%s: Warning: Found an uninitialized port\n",
                                            bond->dev->name);
@@ -2155,7 +2155,7 @@ static int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave,
 
        if (length >= sizeof(struct lacpdu)) {
 
-               port = &(SLAVE_AD_INFO(slave).port);
+               port = &(SLAVE_AD_INFO(slave)->port);
 
                if (!port->slave) {
                        pr_warn_ratelimited("%s: Warning: port of slave %s is uninitialized\n",
@@ -2212,7 +2212,7 @@ void bond_3ad_adapter_speed_changed(struct slave *slave)
 {
        struct port *port;
 
-       port = &(SLAVE_AD_INFO(slave).port);
+       port = &(SLAVE_AD_INFO(slave)->port);
 
        /* if slave is null, the whole port is not initialized */
        if (!port->slave) {
@@ -2245,7 +2245,7 @@ void bond_3ad_adapter_duplex_changed(struct slave *slave)
 {
        struct port *port;
 
-       port = &(SLAVE_AD_INFO(slave).port);
+       port = &(SLAVE_AD_INFO(slave)->port);
 
        /* if slave is null, the whole port is not initialized */
        if (!port->slave) {
@@ -2279,7 +2279,7 @@ void bond_3ad_handle_link_change(struct slave *slave, char link)
 {
        struct port *port;
 
-       port = &(SLAVE_AD_INFO(slave).port);
+       port = &(SLAVE_AD_INFO(slave)->port);
 
        /* if slave is null, the whole port is not initialized */
        if (!port->slave) {
@@ -2347,7 +2347,7 @@ int bond_3ad_set_carrier(struct bonding *bond)
                ret = 0;
                goto out;
        }
-       active = __get_active_agg(&(SLAVE_AD_INFO(first_slave).aggregator));
+       active = __get_active_agg(&(SLAVE_AD_INFO(first_slave)->aggregator));
        if (active) {
                /* are enough slaves available to consider link up? */
                if (active->num_of_ports < bond->params.min_links) {
@@ -2384,7 +2384,7 @@ int __bond_3ad_get_active_agg_info(struct bonding *bond,
        struct port *port;
 
        bond_for_each_slave_rcu(bond, slave, iter) {
-               port = &(SLAVE_AD_INFO(slave).port);
+               port = &(SLAVE_AD_INFO(slave)->port);
                if (port->aggregator && port->aggregator->is_active) {
                        aggregator = port->aggregator;
                        break;
@@ -2440,22 +2440,22 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev)
                goto err_free;
        }
 
-       slave_agg_no = bond_xmit_hash(bond, skb, slaves_in_agg);
+       slave_agg_no = bond_xmit_hash(bond, skb) % slaves_in_agg;
        first_ok_slave = NULL;
 
        bond_for_each_slave_rcu(bond, slave, iter) {
-               agg = SLAVE_AD_INFO(slave).port.aggregator;
+               agg = SLAVE_AD_INFO(slave)->port.aggregator;
                if (!agg || agg->aggregator_identifier != agg_id)
                        continue;
 
                if (slave_agg_no >= 0) {
-                       if (!first_ok_slave && SLAVE_IS_OK(slave))
+                       if (!first_ok_slave && bond_slave_can_tx(slave))
                                first_ok_slave = slave;
                        slave_agg_no--;
                        continue;
                }
 
-               if (SLAVE_IS_OK(slave)) {
+               if (bond_slave_can_tx(slave)) {
                        bond_dev_queue_xmit(bond, skb, slave->dev);
                        goto out;
                }
@@ -2522,7 +2522,7 @@ void bond_3ad_update_lacp_rate(struct bonding *bond)
 
        lacp_fast = bond->params.lacp_fast;
        bond_for_each_slave(bond, slave, iter) {
-               port = &(SLAVE_AD_INFO(slave).port);
+               port = &(SLAVE_AD_INFO(slave)->port);
                __get_state_machine_lock(port);
                if (lacp_fast)
                        port->actor_oper_port_state |= AD_STATE_LACP_TIMEOUT;
index 93580a47cc548851a36d1ff4cb45e88636565a3e..76c0dade233f904324631dba34be0f370d193179 100644 (file)
@@ -229,7 +229,7 @@ static struct slave *tlb_get_least_loaded_slave(struct bonding *bond)
 
        /* Find the slave with the largest gap */
        bond_for_each_slave_rcu(bond, slave, iter) {
-               if (SLAVE_IS_OK(slave)) {
+               if (bond_slave_can_tx(slave)) {
                        long long gap = compute_gap(slave);
 
                        if (max_gap < gap) {
@@ -384,7 +384,7 @@ static struct slave *rlb_next_rx_slave(struct bonding *bond)
        bool found = false;
 
        bond_for_each_slave(bond, slave, iter) {
-               if (!SLAVE_IS_OK(slave))
+               if (!bond_slave_can_tx(slave))
                        continue;
                if (!found) {
                        if (!before || before->speed < slave->speed)
@@ -417,7 +417,7 @@ static struct slave *__rlb_next_rx_slave(struct bonding *bond)
        bool found = false;
 
        bond_for_each_slave_rcu(bond, slave, iter) {
-               if (!SLAVE_IS_OK(slave))
+               if (!bond_slave_can_tx(slave))
                        continue;
                if (!found) {
                        if (!before || before->speed < slave->speed)
@@ -755,7 +755,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_rcu(bond, arp->mac_src))
+       if (!bond_slave_has_mac_rx(bond, arp->mac_src))
                return NULL;
 
        if (arp->op_code == htons(ARPOP_REPLY)) {
@@ -1039,11 +1039,14 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[],
        struct bonding *bond = bond_get_bond_by_slave(slave);
        struct net_device *upper;
        struct list_head *iter;
+       struct bond_vlan_tag tags[BOND_MAX_VLAN_ENCAP];
 
        /* send untagged */
        alb_send_lp_vid(slave, mac_addr, 0, 0);
 
-       /* loop through vlans and send one packet for each */
+       /* loop through all devices and see if we need to send a packet
+        * for that device.
+        */
        rcu_read_lock();
        netdev_for_each_all_upper_dev_rcu(bond->dev, upper, iter) {
                if (is_vlan_dev(upper) && vlan_get_encap_level(upper) == 0) {
@@ -1059,6 +1062,16 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[],
                                                vlan_dev_vlan_id(upper));
                        }
                }
+
+               /* If this is a macvlan device, then only send updates
+                * when strict_match is turned off.
+                */
+               if (netif_is_macvlan(upper) && !strict_match) {
+                       memset(tags, 0, sizeof(tags));
+                       bond_verify_device_path(bond->dev, upper, tags);
+                       alb_send_lp_vid(slave, upper->dev_addr,
+                                       tags[0].vlan_proto, tags[0].vlan_id);
+               }
        }
        rcu_read_unlock();
 }
@@ -1068,7 +1081,7 @@ static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[])
        struct net_device *dev = slave->dev;
        struct sockaddr s_addr;
 
-       if (slave->bond->params.mode == BOND_MODE_TLB) {
+       if (BOND_MODE(slave->bond) == BOND_MODE_TLB) {
                memcpy(dev->dev_addr, addr, dev->addr_len);
                return 0;
        }
@@ -1111,13 +1124,13 @@ static void alb_swap_mac_addr(struct slave *slave1, struct slave *slave2)
 static void alb_fasten_mac_swap(struct bonding *bond, struct slave *slave1,
                                struct slave *slave2)
 {
-       int slaves_state_differ = (SLAVE_IS_OK(slave1) != SLAVE_IS_OK(slave2));
+       int slaves_state_differ = (bond_slave_can_tx(slave1) != bond_slave_can_tx(slave2));
        struct slave *disabled_slave = NULL;
 
        ASSERT_RTNL();
 
        /* fasten the change in the switch */
-       if (SLAVE_IS_OK(slave1)) {
+       if (bond_slave_can_tx(slave1)) {
                alb_send_learning_packets(slave1, slave1->dev->dev_addr, false);
                if (bond->alb_info.rlb_enabled) {
                        /* inform the clients that the mac address
@@ -1129,7 +1142,7 @@ static void alb_fasten_mac_swap(struct bonding *bond, struct slave *slave1,
                disabled_slave = slave1;
        }
 
-       if (SLAVE_IS_OK(slave2)) {
+       if (bond_slave_can_tx(slave2)) {
                alb_send_learning_packets(slave2, slave2->dev->dev_addr, false);
                if (bond->alb_info.rlb_enabled) {
                        /* inform the clients that the mac address
@@ -1358,6 +1371,77 @@ void bond_alb_deinitialize(struct bonding *bond)
                rlb_deinitialize(bond);
 }
 
+static int bond_do_alb_xmit(struct sk_buff *skb, struct bonding *bond,
+               struct slave *tx_slave)
+{
+       struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
+       struct ethhdr *eth_data = eth_hdr(skb);
+
+       if (!tx_slave) {
+               /* unbalanced or unassigned, send through primary */
+               tx_slave = rcu_dereference(bond->curr_active_slave);
+               if (bond->params.tlb_dynamic_lb)
+                       bond_info->unbalanced_load += skb->len;
+       }
+
+       if (tx_slave && bond_slave_can_tx(tx_slave)) {
+               if (tx_slave != rcu_dereference(bond->curr_active_slave)) {
+                       ether_addr_copy(eth_data->h_source,
+                                       tx_slave->dev->dev_addr);
+               }
+
+               bond_dev_queue_xmit(bond, skb, tx_slave->dev);
+               goto out;
+       }
+
+       if (tx_slave && bond->params.tlb_dynamic_lb) {
+               _lock_tx_hashtbl(bond);
+               __tlb_clear_slave(bond, tx_slave, 0);
+               _unlock_tx_hashtbl(bond);
+       }
+
+       /* no suitable interface, frame not sent */
+       dev_kfree_skb_any(skb);
+out:
+       return NETDEV_TX_OK;
+}
+
+int bond_tlb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
+{
+       struct bonding *bond = netdev_priv(bond_dev);
+       struct ethhdr *eth_data;
+       struct slave *tx_slave = NULL;
+       u32 hash_index;
+
+       skb_reset_mac_header(skb);
+       eth_data = eth_hdr(skb);
+
+       /* Do not TX balance any multicast or broadcast */
+       if (!is_multicast_ether_addr(eth_data->h_dest)) {
+               switch (skb->protocol) {
+               case htons(ETH_P_IP):
+               case htons(ETH_P_IPX):
+                   /* In case of IPX, it will falback to L2 hash */
+               case htons(ETH_P_IPV6):
+                       hash_index = bond_xmit_hash(bond, skb);
+                       if (bond->params.tlb_dynamic_lb) {
+                               tx_slave = tlb_choose_channel(bond,
+                                                             hash_index & 0xFF,
+                                                             skb->len);
+                       } else {
+                               struct list_head *iter;
+                               int idx = hash_index % bond->slave_cnt;
+
+                               bond_for_each_slave_rcu(bond, tx_slave, iter)
+                                       if (--idx < 0)
+                                               break;
+                       }
+                       break;
+               }
+       }
+       return bond_do_alb_xmit(skb, bond, tx_slave);
+}
+
 int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
 {
        struct bonding *bond = netdev_priv(bond_dev);
@@ -1366,7 +1450,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
        struct slave *tx_slave = NULL;
        static const __be32 ip_bcast = htonl(0xffffffff);
        int hash_size = 0;
-       int do_tx_balance = 1;
+       bool do_tx_balance = true;
        u32 hash_index = 0;
        const u8 *hash_start = NULL;
        struct ipv6hdr *ip6hdr;
@@ -1381,7 +1465,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                if (ether_addr_equal_64bits(eth_data->h_dest, mac_bcast) ||
                    (iph->daddr == ip_bcast) ||
                    (iph->protocol == IPPROTO_IGMP)) {
-                       do_tx_balance = 0;
+                       do_tx_balance = false;
                        break;
                }
                hash_start = (char *)&(iph->daddr);
@@ -1393,7 +1477,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                 * that here just in case.
                 */
                if (ether_addr_equal_64bits(eth_data->h_dest, mac_bcast)) {
-                       do_tx_balance = 0;
+                       do_tx_balance = false;
                        break;
                }
 
@@ -1401,7 +1485,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                 * broadcasts in IPv4.
                 */
                if (ether_addr_equal_64bits(eth_data->h_dest, mac_v6_allmcast)) {
-                       do_tx_balance = 0;
+                       do_tx_balance = false;
                        break;
                }
 
@@ -1411,7 +1495,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                 */
                ip6hdr = ipv6_hdr(skb);
                if (ipv6_addr_any(&ip6hdr->saddr)) {
-                       do_tx_balance = 0;
+                       do_tx_balance = false;
                        break;
                }
 
@@ -1421,7 +1505,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
        case ETH_P_IPX:
                if (ipx_hdr(skb)->ipx_checksum != IPX_NO_CHECKSUM) {
                        /* something is wrong with this packet */
-                       do_tx_balance = 0;
+                       do_tx_balance = false;
                        break;
                }
 
@@ -1430,7 +1514,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                         * this family since it has an "ARP" like
                         * mechanism
                         */
-                       do_tx_balance = 0;
+                       do_tx_balance = false;
                        break;
                }
 
@@ -1438,12 +1522,12 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                hash_size = ETH_ALEN;
                break;
        case ETH_P_ARP:
-               do_tx_balance = 0;
+               do_tx_balance = false;
                if (bond_info->rlb_enabled)
                        tx_slave = rlb_arp_xmit(skb, bond);
                break;
        default:
-               do_tx_balance = 0;
+               do_tx_balance = false;
                break;
        }
 
@@ -1452,32 +1536,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
                tx_slave = tlb_choose_channel(bond, hash_index, skb->len);
        }
 
-       if (!tx_slave) {
-               /* unbalanced or unassigned, send through primary */
-               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 != rcu_dereference(bond->curr_active_slave)) {
-                       ether_addr_copy(eth_data->h_source,
-                                       tx_slave->dev->dev_addr);
-               }
-
-               bond_dev_queue_xmit(bond, skb, tx_slave->dev);
-               goto out;
-       }
-
-       if (tx_slave) {
-               _lock_tx_hashtbl(bond);
-               __tlb_clear_slave(bond, tx_slave, 0);
-               _unlock_tx_hashtbl(bond);
-       }
-
-       /* no suitable interface, frame not sent */
-       dev_kfree_skb_any(skb);
-out:
-       return NETDEV_TX_OK;
+       return bond_do_alb_xmit(skb, bond, tx_slave);
 }
 
 void bond_alb_monitor(struct work_struct *work)
@@ -1514,8 +1573,10 @@ void bond_alb_monitor(struct work_struct *work)
                        /* If updating current_active, use all currently
                         * user mac addreses (!strict_match).  Otherwise, only
                         * use mac of the slave device.
+                        * In RLB mode, we always use strict matches.
                         */
-                       strict_match = (slave != bond->curr_active_slave);
+                       strict_match = (slave != bond->curr_active_slave ||
+                                       bond_info->rlb_enabled);
                        alb_send_learning_packets(slave, slave->dev->dev_addr,
                                                  strict_match);
                }
@@ -1719,7 +1780,7 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
        /* 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) {
+       if (BOND_MODE(bond) == BOND_MODE_TLB) {
                struct sockaddr sa;
                u8 tmp_addr[ETH_ALEN];
 
index e09dd4bfafffcf585b8f853f7661e2e416c58602..5fc76c01636cb6eb0e9e96d14fc0c79566741900 100644 (file)
@@ -175,6 +175,7 @@ void bond_alb_deinit_slave(struct bonding *bond, struct slave *slave);
 void bond_alb_handle_link_change(struct bonding *bond, struct slave *slave, char link);
 void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave);
 int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev);
+int bond_tlb_xmit(struct sk_buff *skb, struct net_device *bond_dev);
 void bond_alb_monitor(struct work_struct *);
 int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr);
 void bond_alb_clear_vlan(struct bonding *bond, unsigned short vlan_id);
index 2d3f7fa541ffe755fc1bf5f9e51aeaa464b4e032..658e761c4568dff39ef18db8c548f8ed349c8ee0 100644 (file)
@@ -23,7 +23,7 @@ static int bond_debug_rlb_hash_show(struct seq_file *m, void *v)
        struct rlb_client_info *client_info;
        u32 hash_index;
 
-       if (bond->params.mode != BOND_MODE_ALB)
+       if (BOND_MODE(bond) != BOND_MODE_ALB)
                return 0;
 
        seq_printf(m, "SourceIP        DestinationIP   "
index d3a67896d43541f0b8ebc0b4e193db945087bc56..04f35f960cb87a30a89a1308d248cf459b56e2ce 100644 (file)
@@ -343,7 +343,7 @@ static int bond_set_carrier(struct bonding *bond)
        if (!bond_has_slaves(bond))
                goto down;
 
-       if (bond->params.mode == BOND_MODE_8023AD)
+       if (BOND_MODE(bond) == BOND_MODE_8023AD)
                return bond_3ad_set_carrier(bond);
 
        bond_for_each_slave(bond, slave, iter) {
@@ -497,7 +497,7 @@ static int bond_set_promiscuity(struct bonding *bond, int inc)
        struct list_head *iter;
        int err = 0;
 
-       if (USES_PRIMARY(bond->params.mode)) {
+       if (bond_uses_primary(bond)) {
                /* write lock already acquired */
                if (bond->curr_active_slave) {
                        err = dev_set_promiscuity(bond->curr_active_slave->dev,
@@ -523,7 +523,7 @@ static int bond_set_allmulti(struct bonding *bond, int inc)
        struct list_head *iter;
        int err = 0;
 
-       if (USES_PRIMARY(bond->params.mode)) {
+       if (bond_uses_primary(bond)) {
                /* write lock already acquired */
                if (bond->curr_active_slave) {
                        err = dev_set_allmulti(bond->curr_active_slave->dev,
@@ -574,7 +574,7 @@ static void bond_hw_addr_flush(struct net_device *bond_dev,
        dev_uc_unsync(slave_dev, bond_dev);
        dev_mc_unsync(slave_dev, bond_dev);
 
-       if (bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                /* del lacpdu mc addr from mc list */
                u8 lacpdu_multicast[ETH_ALEN] = MULTICAST_LACPDU_ADDR;
 
@@ -585,8 +585,8 @@ static void bond_hw_addr_flush(struct net_device *bond_dev,
 /*--------------------------- Active slave change ---------------------------*/
 
 /* Update the hardware address list and promisc/allmulti for the new and
- * old active slaves (if any).  Modes that are !USES_PRIMARY keep all
- * slaves up date at all times; only the USES_PRIMARY modes need to call
+ * old active slaves (if any).  Modes that are not using primary keep all
+ * slaves up date at all times; only the modes that use primary need to call
  * this function to swap these settings during a failover.
  */
 static void bond_hw_addr_swap(struct bonding *bond, struct slave *new_active,
@@ -747,7 +747,7 @@ static struct slave *bond_find_best_slave(struct bonding *bond)
        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) &&
+               if (slave->link == BOND_LINK_BACK && bond_slave_is_up(slave) &&
                    slave->delay < mintime) {
                        mintime = slave->delay;
                        bestslave = slave;
@@ -801,7 +801,7 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
                new_active->last_link_up = jiffies;
 
                if (new_active->link == BOND_LINK_BACK) {
-                       if (USES_PRIMARY(bond->params.mode)) {
+                       if (bond_uses_primary(bond)) {
                                pr_info("%s: making interface %s the new active one %d ms earlier\n",
                                        bond->dev->name, new_active->dev->name,
                                        (bond->params.updelay - new_active->delay) * bond->params.miimon);
@@ -810,20 +810,20 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
                        new_active->delay = 0;
                        new_active->link = BOND_LINK_UP;
 
-                       if (bond->params.mode == BOND_MODE_8023AD)
+                       if (BOND_MODE(bond) == BOND_MODE_8023AD)
                                bond_3ad_handle_link_change(new_active, BOND_LINK_UP);
 
                        if (bond_is_lb(bond))
                                bond_alb_handle_link_change(bond, new_active, BOND_LINK_UP);
                } else {
-                       if (USES_PRIMARY(bond->params.mode)) {
+                       if (bond_uses_primary(bond)) {
                                pr_info("%s: making interface %s the new active one\n",
                                        bond->dev->name, new_active->dev->name);
                        }
                }
        }
 
-       if (USES_PRIMARY(bond->params.mode))
+       if (bond_uses_primary(bond))
                bond_hw_addr_swap(bond, new_active, old_active);
 
        if (bond_is_lb(bond)) {
@@ -838,7 +838,7 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
                rcu_assign_pointer(bond->curr_active_slave, new_active);
        }
 
-       if (bond->params.mode == BOND_MODE_ACTIVEBACKUP) {
+       if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP) {
                if (old_active)
                        bond_set_slave_inactive_flags(old_active,
                                                      BOND_SLAVE_NOTIFY_NOW);
@@ -876,8 +876,8 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
         * resend only if bond is brought up with the affected
         * bonding modes and the retransmission is enabled */
        if (netif_running(bond->dev) && (bond->params.resend_igmp > 0) &&
-           ((USES_PRIMARY(bond->params.mode) && new_active) ||
-            bond->params.mode == BOND_MODE_ROUNDROBIN)) {
+           ((bond_uses_primary(bond) && new_active) ||
+            BOND_MODE(bond) == BOND_MODE_ROUNDROBIN)) {
                bond->igmp_retrans = bond->params.resend_igmp;
                queue_delayed_work(bond->wq, &bond->mcast_work, 1);
        }
@@ -958,7 +958,7 @@ static void bond_netpoll_cleanup(struct net_device *bond_dev)
        struct slave *slave;
 
        bond_for_each_slave(bond, slave, iter)
-               if (IS_UP(slave->dev))
+               if (bond_slave_is_up(slave))
                        slave_disable_netpoll(slave);
 }
 
@@ -1038,6 +1038,7 @@ static void bond_compute_features(struct bonding *bond)
 
        if (!bond_has_slaves(bond))
                goto done;
+       vlan_features &= NETIF_F_ALL_FOR_ALL;
 
        bond_for_each_slave(bond, slave, iter) {
                vlan_features = netdev_increment_features(vlan_features,
@@ -1084,7 +1085,7 @@ static bool bond_should_deliver_exact_match(struct sk_buff *skb,
                                            struct bonding *bond)
 {
        if (bond_is_slave_inactive(slave)) {
-               if (bond->params.mode == BOND_MODE_ALB &&
+               if (BOND_MODE(bond) == BOND_MODE_ALB &&
                    skb->pkt_type != PACKET_BROADCAST &&
                    skb->pkt_type != PACKET_MULTICAST)
                        return false;
@@ -1126,7 +1127,7 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb)
 
        skb->dev = bond->dev;
 
-       if (bond->params.mode == BOND_MODE_ALB &&
+       if (BOND_MODE(bond) == BOND_MODE_ALB &&
            bond->dev->priv_flags & IFF_BRIDGE_PORT &&
            skb->pkt_type == PACKET_HOST) {
 
@@ -1163,6 +1164,35 @@ static void bond_upper_dev_unlink(struct net_device *bond_dev,
        rtmsg_ifinfo(RTM_NEWLINK, slave_dev, IFF_SLAVE, GFP_KERNEL);
 }
 
+static struct slave *bond_alloc_slave(struct bonding *bond)
+{
+       struct slave *slave = NULL;
+
+       slave = kzalloc(sizeof(struct slave), GFP_KERNEL);
+       if (!slave)
+               return NULL;
+
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
+               SLAVE_AD_INFO(slave) = kzalloc(sizeof(struct ad_slave_info),
+                                              GFP_KERNEL);
+               if (!SLAVE_AD_INFO(slave)) {
+                       kfree(slave);
+                       return NULL;
+               }
+       }
+       return slave;
+}
+
+static void bond_free_slave(struct slave *slave)
+{
+       struct bonding *bond = bond_get_bond_by_slave(slave);
+
+       if (BOND_MODE(bond) == BOND_MODE_8023AD)
+               kfree(SLAVE_AD_INFO(slave));
+
+       kfree(slave);
+}
+
 /* enslave device <slave> to bond device <master> */
 int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
 {
@@ -1269,7 +1299,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                if (!bond_has_slaves(bond)) {
                        pr_warn("%s: Warning: The first slave device specified does not support setting the MAC address\n",
                                bond_dev->name);
-                       if (bond->params.mode == BOND_MODE_ACTIVEBACKUP) {
+                       if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP) {
                                bond->params.fail_over_mac = BOND_FOM_ACTIVE;
                                pr_warn("%s: Setting fail_over_mac to active for active-backup mode\n",
                                        bond_dev->name);
@@ -1290,11 +1320,14 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
            bond->dev->addr_assign_type == NET_ADDR_RANDOM)
                bond_set_dev_addr(bond->dev, slave_dev);
 
-       new_slave = kzalloc(sizeof(struct slave), GFP_KERNEL);
+       new_slave = bond_alloc_slave(bond);
        if (!new_slave) {
                res = -ENOMEM;
                goto err_undo_flags;
        }
+
+       new_slave->bond = bond;
+       new_slave->dev = slave_dev;
        /*
         * Set the new_slave's queue_id to be zero.  Queue ID mapping
         * is set via sysfs or module option if desired.
@@ -1317,7 +1350,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
        ether_addr_copy(new_slave->perm_hwaddr, slave_dev->dev_addr);
 
        if (!bond->params.fail_over_mac ||
-           bond->params.mode != BOND_MODE_ACTIVEBACKUP) {
+           BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) {
                /*
                 * Set slave to master's mac address.  The application already
                 * set the master's mac address to that of the first slave
@@ -1338,8 +1371,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                goto err_restore_mac;
        }
 
-       new_slave->bond = bond;
-       new_slave->dev = slave_dev;
        slave_dev->priv_flags |= IFF_BONDING;
 
        if (bond_is_lb(bond)) {
@@ -1351,10 +1382,10 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                        goto err_close;
        }
 
-       /* If the mode USES_PRIMARY, then the following is handled by
+       /* If the mode uses primary, then the following is handled by
         * bond_change_active_slave().
         */
-       if (!USES_PRIMARY(bond->params.mode)) {
+       if (!bond_uses_primary(bond)) {
                /* set promiscuity level to new slave */
                if (bond_dev->flags & IFF_PROMISC) {
                        res = dev_set_promiscuity(slave_dev, 1);
@@ -1377,7 +1408,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                netif_addr_unlock_bh(bond_dev);
        }
 
-       if (bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                /* add lacpdu mc addr to mc list */
                u8 lacpdu_multicast[ETH_ALEN] = MULTICAST_LACPDU_ADDR;
 
@@ -1450,7 +1481,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                 new_slave->link == BOND_LINK_DOWN ? "DOWN" :
                 (new_slave->link == BOND_LINK_UP ? "UP" : "BACK"));
 
-       if (USES_PRIMARY(bond->params.mode) && bond->params.primary[0]) {
+       if (bond_uses_primary(bond) && bond->params.primary[0]) {
                /* if there is a primary slave, remember it */
                if (strcmp(bond->params.primary, new_slave->dev->name) == 0) {
                        bond->primary_slave = new_slave;
@@ -1458,7 +1489,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                }
        }
 
-       switch (bond->params.mode) {
+       switch (BOND_MODE(bond)) {
        case BOND_MODE_ACTIVEBACKUP:
                bond_set_slave_inactive_flags(new_slave,
                                              BOND_SLAVE_NOTIFY_NOW);
@@ -1471,14 +1502,14 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
                bond_set_slave_inactive_flags(new_slave, BOND_SLAVE_NOTIFY_NOW);
                /* if this is the first slave */
                if (!prev_slave) {
-                       SLAVE_AD_INFO(new_slave).id = 1;
+                       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 {
-                       SLAVE_AD_INFO(new_slave).id =
-                               SLAVE_AD_INFO(prev_slave).id + 1;
+                       SLAVE_AD_INFO(new_slave)->id =
+                               SLAVE_AD_INFO(prev_slave)->id + 1;
                }
 
                bond_3ad_bind_slave(new_slave);
@@ -1539,7 +1570,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
        bond_compute_features(bond);
        bond_set_carrier(bond);
 
-       if (USES_PRIMARY(bond->params.mode)) {
+       if (bond_uses_primary(bond)) {
                block_netpoll_tx();
                write_lock_bh(&bond->curr_slave_lock);
                bond_select_active_slave(bond);
@@ -1563,7 +1594,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
        netdev_rx_handler_unregister(slave_dev);
 
 err_detach:
-       if (!USES_PRIMARY(bond->params.mode))
+       if (!bond_uses_primary(bond))
                bond_hw_addr_flush(bond_dev, slave_dev);
 
        vlan_vids_del_by_dev(slave_dev, bond_dev);
@@ -1585,7 +1616,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
 
 err_restore_mac:
        if (!bond->params.fail_over_mac ||
-           bond->params.mode != BOND_MODE_ACTIVEBACKUP) {
+           BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) {
                /* XXX TODO - fom follow mode needs to change master's
                 * MAC if this slave's MAC is in use by the bond, or at
                 * least print a warning.
@@ -1599,7 +1630,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
        dev_set_mtu(slave_dev, new_slave->original_mtu);
 
 err_free:
-       kfree(new_slave);
+       bond_free_slave(new_slave);
 
 err_undo_flags:
        /* Enslave of first slave has failed and we need to fix master's mac */
@@ -1661,7 +1692,7 @@ static int __bond_release_one(struct net_device *bond_dev,
        write_lock_bh(&bond->lock);
 
        /* Inform AD package of unbinding of slave. */
-       if (bond->params.mode == BOND_MODE_8023AD)
+       if (BOND_MODE(bond) == BOND_MODE_8023AD)
                bond_3ad_unbind_slave(slave);
 
        write_unlock_bh(&bond->lock);
@@ -1676,7 +1707,7 @@ static int __bond_release_one(struct net_device *bond_dev,
        bond->current_arp_slave = NULL;
 
        if (!all && (!bond->params.fail_over_mac ||
-                    bond->params.mode != BOND_MODE_ACTIVEBACKUP)) {
+                    BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP)) {
                if (ether_addr_equal_64bits(bond_dev->dev_addr, slave->perm_hwaddr) &&
                    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",
@@ -1748,10 +1779,10 @@ static int __bond_release_one(struct net_device *bond_dev,
        /* must do this from outside any spinlocks */
        vlan_vids_del_by_dev(slave_dev, bond_dev);
 
-       /* If the mode USES_PRIMARY, then this cases was handled above by
+       /* If the mode uses primary, then this cases was handled above by
         * bond_change_active_slave(..., NULL)
         */
-       if (!USES_PRIMARY(bond->params.mode)) {
+       if (!bond_uses_primary(bond)) {
                /* 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
@@ -1775,7 +1806,7 @@ static int __bond_release_one(struct net_device *bond_dev,
        dev_close(slave_dev);
 
        if (bond->params.fail_over_mac != BOND_FOM_ACTIVE ||
-           bond->params.mode != BOND_MODE_ACTIVEBACKUP) {
+           BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) {
                /* restore original ("permanent") mac address */
                ether_addr_copy(addr.sa_data, slave->perm_hwaddr);
                addr.sa_family = slave_dev->type;
@@ -1786,7 +1817,7 @@ static int __bond_release_one(struct net_device *bond_dev,
 
        slave_dev->priv_flags &= ~IFF_BONDING;
 
-       kfree(slave);
+       bond_free_slave(slave);
 
        return 0;  /* deletion OK */
 }
@@ -1821,7 +1852,7 @@ static int bond_info_query(struct net_device *bond_dev, struct ifbond *info)
 {
        struct bonding *bond = netdev_priv(bond_dev);
 
-       info->bond_mode = bond->params.mode;
+       info->bond_mode = BOND_MODE(bond);
        info->miimon = bond->params.miimon;
 
        info->num_slaves = bond->slave_cnt;
@@ -1877,7 +1908,7 @@ static int bond_miimon_inspect(struct bonding *bond)
                        if (slave->delay) {
                                pr_info("%s: link status down for %sinterface %s, disabling it in %d ms\n",
                                        bond->dev->name,
-                                       (bond->params.mode ==
+                                       (BOND_MODE(bond) ==
                                         BOND_MODE_ACTIVEBACKUP) ?
                                        (bond_is_active_slave(slave) ?
                                         "active " : "backup ") : "",
@@ -1968,10 +1999,10 @@ static void bond_miimon_commit(struct bonding *bond)
                        slave->link = BOND_LINK_UP;
                        slave->last_link_up = jiffies;
 
-                       if (bond->params.mode == BOND_MODE_8023AD) {
+                       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                                /* prevent it from being the active one */
                                bond_set_backup_slave(slave);
-                       } else if (bond->params.mode != BOND_MODE_ACTIVEBACKUP) {
+                       } else if (BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) {
                                /* make it immediately active */
                                bond_set_active_slave(slave);
                        } else if (slave != bond->primary_slave) {
@@ -1985,7 +2016,7 @@ static void bond_miimon_commit(struct bonding *bond)
                                slave->duplex ? "full" : "half");
 
                        /* notify ad that the link status has changed */
-                       if (bond->params.mode == BOND_MODE_8023AD)
+                       if (BOND_MODE(bond) == BOND_MODE_8023AD)
                                bond_3ad_handle_link_change(slave, BOND_LINK_UP);
 
                        if (bond_is_lb(bond))
@@ -2004,15 +2035,15 @@ static void bond_miimon_commit(struct bonding *bond)
 
                        slave->link = BOND_LINK_DOWN;
 
-                       if (bond->params.mode == BOND_MODE_ACTIVEBACKUP ||
-                           bond->params.mode == BOND_MODE_8023AD)
+                       if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP ||
+                           BOND_MODE(bond) == BOND_MODE_8023AD)
                                bond_set_slave_inactive_flags(slave,
                                                              BOND_SLAVE_NOTIFY_NOW);
 
                        pr_info("%s: link status definitely down for interface %s, disabling it\n",
                                bond->dev->name, slave->dev->name);
 
-                       if (bond->params.mode == BOND_MODE_8023AD)
+                       if (BOND_MODE(bond) == BOND_MODE_8023AD)
                                bond_3ad_handle_link_change(slave,
                                                            BOND_LINK_DOWN);
 
@@ -2175,9 +2206,9 @@ static void bond_arp_send(struct net_device *slave_dev, int arp_op,
  * When the path is validated, collect any vlan information in the
  * path.
  */
-static bool bond_verify_device_path(struct net_device *start_dev,
-                                   struct net_device *end_dev,
-                                   struct bond_vlan_tag *tags)
+bool bond_verify_device_path(struct net_device *start_dev,
+                            struct net_device *end_dev,
+                            struct bond_vlan_tag *tags)
 {
        struct net_device *upper;
        struct list_head  *iter;
@@ -2287,8 +2318,8 @@ int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
        int alen, is_arp = skb->protocol == __cpu_to_be16(ETH_P_ARP);
 
        if (!slave_do_arp_validate(bond, slave)) {
-               if ((slave_do_arp_validate_only(bond, slave) && is_arp) ||
-                   !slave_do_arp_validate_only(bond, slave))
+               if ((slave_do_arp_validate_only(bond) && is_arp) ||
+                   !slave_do_arp_validate_only(bond))
                        slave->last_rx = jiffies;
                return RX_HANDLER_ANOTHER;
        } else if (!is_arp) {
@@ -2456,7 +2487,7 @@ static void bond_loadbalance_arp_mon(struct work_struct *work)
                 * do - all replies will be rx'ed on same link causing slaves
                 * to be unstable during low/no traffic periods
                 */
-               if (IS_UP(slave->dev))
+               if (bond_slave_is_up(slave))
                        bond_arp_send_all(bond, slave);
        }
 
@@ -2678,10 +2709,10 @@ static bool bond_ab_arp_probe(struct bonding *bond)
        bond_set_slave_inactive_flags(curr_arp_slave, BOND_SLAVE_NOTIFY_LATER);
 
        bond_for_each_slave_rcu(bond, slave, iter) {
-               if (!found && !before && IS_UP(slave->dev))
+               if (!found && !before && bond_slave_is_up(slave))
                        before = slave;
 
-               if (found && !new_slave && IS_UP(slave->dev))
+               if (found && !new_slave && bond_slave_is_up(slave))
                        new_slave = slave;
                /* if the link state is up at this point, we
                 * mark it down - this can happen if we have
@@ -2690,7 +2721,7 @@ static bool bond_ab_arp_probe(struct bonding *bond)
                 * one the current slave so it is still marked
                 * up when it is actually down
                 */
-               if (!IS_UP(slave->dev) && slave->link == BOND_LINK_UP) {
+               if (!bond_slave_is_up(slave) && slave->link == BOND_LINK_UP) {
                        slave->link = BOND_LINK_DOWN;
                        if (slave->link_failure_count < UINT_MAX)
                                slave->link_failure_count++;
@@ -2853,7 +2884,7 @@ static int bond_slave_netdev_event(unsigned long event,
 
                bond_update_speed_duplex(slave);
 
-               if (bond->params.mode == BOND_MODE_8023AD) {
+               if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                        if (old_speed != slave->speed)
                                bond_3ad_adapter_speed_changed(slave);
                        if (old_duplex != slave->duplex)
@@ -2881,7 +2912,7 @@ static int bond_slave_netdev_event(unsigned long event,
                break;
        case NETDEV_CHANGENAME:
                /* we don't care if we don't have primary set */
-               if (!USES_PRIMARY(bond->params.mode) ||
+               if (!bond_uses_primary(bond) ||
                    !bond->params.primary[0])
                        break;
 
@@ -3011,20 +3042,18 @@ static bool bond_flow_dissect(struct bonding *bond, struct sk_buff *skb,
  * 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.
  */
-int bond_xmit_hash(struct bonding *bond, struct sk_buff *skb, int count)
+u32 bond_xmit_hash(struct bonding *bond, struct sk_buff *skb)
 {
        struct flow_keys flow;
        u32 hash;
 
        if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER2 ||
            !bond_flow_dissect(bond, skb, &flow))
-               return bond_eth_hash(skb) % count;
+               return bond_eth_hash(skb);
 
        if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER23 ||
            bond->params.xmit_policy == BOND_XMIT_POLICY_ENCAP23)
@@ -3035,7 +3064,7 @@ int bond_xmit_hash(struct bonding *bond, struct sk_buff *skb, int count)
        hash ^= (hash >> 16);
        hash ^= (hash >> 8);
 
-       return hash % count;
+       return hash;
 }
 
 /*-------------------------- Device entry points ----------------------------*/
@@ -3046,7 +3075,7 @@ static void bond_work_init_all(struct bonding *bond)
                          bond_resend_igmp_join_requests_delayed);
        INIT_DELAYED_WORK(&bond->alb_work, bond_alb_monitor);
        INIT_DELAYED_WORK(&bond->mii_work, bond_mii_monitor);
-       if (bond->params.mode == BOND_MODE_ACTIVEBACKUP)
+       if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP)
                INIT_DELAYED_WORK(&bond->arp_work, bond_activebackup_arp_mon);
        else
                INIT_DELAYED_WORK(&bond->arp_work, bond_loadbalance_arp_mon);
@@ -3073,7 +3102,7 @@ static int bond_open(struct net_device *bond_dev)
        if (bond_has_slaves(bond)) {
                read_lock(&bond->curr_slave_lock);
                bond_for_each_slave(bond, slave, iter) {
-                       if (USES_PRIMARY(bond->params.mode)
+                       if (bond_uses_primary(bond)
                                && (slave != bond->curr_active_slave)) {
                                bond_set_slave_inactive_flags(slave,
                                                              BOND_SLAVE_NOTIFY_NOW);
@@ -3092,9 +3121,10 @@ static int bond_open(struct net_device *bond_dev)
                /* bond_alb_initialize must be called before the timer
                 * is started.
                 */
-               if (bond_alb_initialize(bond, (bond->params.mode == BOND_MODE_ALB)))
+               if (bond_alb_initialize(bond, (BOND_MODE(bond) == BOND_MODE_ALB)))
                        return -ENOMEM;
-               queue_delayed_work(bond->wq, &bond->alb_work, 0);
+               if (bond->params.tlb_dynamic_lb)
+                       queue_delayed_work(bond->wq, &bond->alb_work, 0);
        }
 
        if (bond->params.miimon)  /* link check interval, in milliseconds. */
@@ -3105,7 +3135,7 @@ static int bond_open(struct net_device *bond_dev)
                bond->recv_probe = bond_arp_rcv;
        }
 
-       if (bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                queue_delayed_work(bond->wq, &bond->ad_work, 0);
                /* register to receive LACPDUs */
                bond->recv_probe = bond_3ad_lacpdu_recv;
@@ -3310,7 +3340,7 @@ static void bond_set_rx_mode(struct net_device *bond_dev)
 
 
        rcu_read_lock();
-       if (USES_PRIMARY(bond->params.mode)) {
+       if (bond_uses_primary(bond)) {
                slave = rcu_dereference(bond->curr_active_slave);
                if (slave) {
                        dev_uc_sync(slave->dev, bond_dev);
@@ -3464,7 +3494,7 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr)
        struct list_head *iter;
        int res = 0;
 
-       if (bond->params.mode == BOND_MODE_ALB)
+       if (BOND_MODE(bond) == BOND_MODE_ALB)
                return bond_alb_set_mac_address(bond_dev, addr);
 
 
@@ -3475,7 +3505,7 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr)
         * Returning an error causes ifenslave to fail.
         */
        if (bond->params.fail_over_mac &&
-           bond->params.mode == BOND_MODE_ACTIVEBACKUP)
+           BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP)
                return 0;
 
        if (!is_valid_ether_addr(sa->sa_data))
@@ -3555,7 +3585,7 @@ static void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int sl
        /* Here we start from the slave with slave_id */
        bond_for_each_slave_rcu(bond, slave, iter) {
                if (--i < 0) {
-                       if (slave_can_tx(slave)) {
+                       if (bond_slave_can_tx(slave)) {
                                bond_dev_queue_xmit(bond, skb, slave->dev);
                                return;
                        }
@@ -3567,7 +3597,7 @@ static void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int sl
        bond_for_each_slave_rcu(bond, slave, iter) {
                if (--i < 0)
                        break;
-               if (slave_can_tx(slave)) {
+               if (bond_slave_can_tx(slave)) {
                        bond_dev_queue_xmit(bond, skb, slave->dev);
                        return;
                }
@@ -3624,7 +3654,7 @@ static int bond_xmit_roundrobin(struct sk_buff *skb, struct net_device *bond_dev
         */
        if (iph->protocol == IPPROTO_IGMP && skb->protocol == htons(ETH_P_IP)) {
                slave = rcu_dereference(bond->curr_active_slave);
-               if (slave && slave_can_tx(slave))
+               if (slave && bond_slave_can_tx(slave))
                        bond_dev_queue_xmit(bond, skb, slave->dev);
                else
                        bond_xmit_slave_id(bond, skb, 0);
@@ -3662,7 +3692,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(bond, skb, bond->slave_cnt));
+       bond_xmit_slave_id(bond, skb, bond_xmit_hash(bond, skb) % bond->slave_cnt);
 
        return NETDEV_TX_OK;
 }
@@ -3677,7 +3707,7 @@ static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev)
        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) {
+               if (bond_slave_is_up(slave) && slave->link == BOND_LINK_UP) {
                        struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
 
                        if (!skb2) {
@@ -3689,7 +3719,7 @@ static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev)
                        bond_dev_queue_xmit(bond, skb2, slave->dev);
                }
        }
-       if (slave && IS_UP(slave->dev) && slave->link == BOND_LINK_UP)
+       if (slave && bond_slave_is_up(slave) && slave->link == BOND_LINK_UP)
                bond_dev_queue_xmit(bond, skb, slave->dev);
        else
                dev_kfree_skb_any(skb);
@@ -3714,7 +3744,7 @@ static inline int bond_slave_override(struct bonding *bond,
        /* Find out if any slaves have the same mapping as this skb. */
        bond_for_each_slave_rcu(bond, slave, iter) {
                if (slave->queue_id == skb->queue_mapping) {
-                       if (slave_can_tx(slave)) {
+                       if (bond_slave_can_tx(slave)) {
                                bond_dev_queue_xmit(bond, skb, slave->dev);
                                return 0;
                        }
@@ -3755,12 +3785,11 @@ static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev
 {
        struct bonding *bond = netdev_priv(dev);
 
-       if (TX_QUEUE_OVERRIDE(bond->params.mode)) {
-               if (!bond_slave_override(bond, skb))
-                       return NETDEV_TX_OK;
-       }
+       if (bond_should_override_tx_queue(bond) &&
+           !bond_slave_override(bond, skb))
+               return NETDEV_TX_OK;
 
-       switch (bond->params.mode) {
+       switch (BOND_MODE(bond)) {
        case BOND_MODE_ROUNDROBIN:
                return bond_xmit_roundrobin(skb, dev);
        case BOND_MODE_ACTIVEBACKUP:
@@ -3772,12 +3801,13 @@ static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev
        case BOND_MODE_8023AD:
                return bond_3ad_xmit_xor(skb, dev);
        case BOND_MODE_ALB:
-       case BOND_MODE_TLB:
                return bond_alb_xmit(skb, dev);
+       case BOND_MODE_TLB:
+               return bond_tlb_xmit(skb, dev);
        default:
                /* Should never happen, mode already checked */
                pr_err("%s: Error: Unknown bonding mode %d\n",
-                      dev->name, bond->params.mode);
+                      dev->name, BOND_MODE(bond));
                WARN_ON_ONCE(1);
                dev_kfree_skb_any(skb);
                return NETDEV_TX_OK;
@@ -3817,14 +3847,14 @@ static int bond_ethtool_get_settings(struct net_device *bond_dev,
        ecmd->duplex = DUPLEX_UNKNOWN;
        ecmd->port = PORT_OTHER;
 
-       /* Since SLAVE_IS_OK returns false for all inactive or down slaves, we
+       /* Since bond_slave_can_tx returns false for all inactive or down slaves, we
         * do not need to check mode.  Though link speed might not represent
         * the true receive or transmit bandwidth (not all modes are symmetric)
         * this is an accurate maximum.
         */
        read_lock(&bond->lock);
        bond_for_each_slave(bond, slave, iter) {
-               if (SLAVE_IS_OK(slave)) {
+               if (bond_slave_can_tx(slave)) {
                        if (slave->speed != SPEED_UNKNOWN)
                                speed += slave->speed;
                        if (ecmd->duplex == DUPLEX_UNKNOWN &&
@@ -3915,7 +3945,7 @@ void bond_setup(struct net_device *bond_dev)
        /* Initialize the device options */
        bond_dev->tx_queue_len = 0;
        bond_dev->flags |= IFF_MASTER|IFF_MULTICAST;
-       bond_dev->priv_flags |= IFF_BONDING;
+       bond_dev->priv_flags |= IFF_BONDING | IFF_UNICAST_FLT;
        bond_dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
 
        /* At first, we block adding VLANs. That's the only way to
@@ -3994,7 +4024,8 @@ static int bond_check_params(struct bond_params *params)
 
        if (xmit_hash_policy) {
                if ((bond_mode != BOND_MODE_XOR) &&
-                   (bond_mode != BOND_MODE_8023AD)) {
+                   (bond_mode != BOND_MODE_8023AD) &&
+                   (bond_mode != BOND_MODE_TLB)) {
                        pr_info("xmit_hash_policy param is irrelevant in mode %s\n",
                                bond_mode_name(bond_mode));
                } else {
@@ -4079,7 +4110,7 @@ static int bond_check_params(struct bond_params *params)
        }
 
        /* reset values for 802.3ad/TLB/ALB */
-       if (BOND_NO_USES_ARP(bond_mode)) {
+       if (!bond_mode_uses_arp(bond_mode)) {
                if (!miimon) {
                        pr_warn("Warning: miimon must be specified, otherwise bonding will not detect link failure, speed and duplex which are essential for 802.3ad operation\n");
                        pr_warn("Forcing miimon to 100msec\n");
@@ -4161,7 +4192,7 @@ static int bond_check_params(struct bond_params *params)
                   catch mistakes */
                __be32 ip;
                if (!in4_pton(arp_ip_target[i], -1, (u8 *)&ip, -1, NULL) ||
-                   IS_IP_TARGET_UNUSABLE_ADDRESS(ip)) {
+                   !bond_is_ip_target_ok(ip)) {
                        pr_warn("Warning: bad arp_ip_target module parameter (%s), ARP monitoring will not be performed\n",
                                arp_ip_target[i]);
                        arp_interval = 0;
@@ -4234,7 +4265,7 @@ static int bond_check_params(struct bond_params *params)
                pr_debug("Warning: either miimon or arp_interval and arp_ip_target module parameters must be specified, otherwise bonding will not detect link failures! see bonding.txt for details\n");
        }
 
-       if (primary && !USES_PRIMARY(bond_mode)) {
+       if (primary && !bond_mode_uses_primary(bond_mode)) {
                /* currently, using a primary only makes sense
                 * in active backup, TLB or ALB modes
                 */
@@ -4300,6 +4331,7 @@ static int bond_check_params(struct bond_params *params)
        params->min_links = min_links;
        params->lp_interval = lp_interval;
        params->packets_per_slave = packets_per_slave;
+       params->tlb_dynamic_lb = 1; /* Default value */
        if (packets_per_slave > 0) {
                params->reciprocal_packets_per_slave =
                        reciprocal_value(packets_per_slave);
index f847e165d252fb2a4528fe396b2c4f0553e81ac3..5ab3c1847e6760e2f3ef7d2ec35085c2d4bf655b 100644 (file)
@@ -56,10 +56,10 @@ static int bond_fill_slave_info(struct sk_buff *skb,
        if (nla_put_u16(skb, IFLA_BOND_SLAVE_QUEUE_ID, slave->queue_id))
                goto nla_put_failure;
 
-       if (slave->bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(slave->bond) == BOND_MODE_8023AD) {
                const struct aggregator *agg;
 
-               agg = SLAVE_AD_INFO(slave).port.aggregator;
+               agg = SLAVE_AD_INFO(slave)->port.aggregator;
                if (agg)
                        if (nla_put_u16(skb, IFLA_BOND_SLAVE_AD_AGGREGATOR_ID,
                                        agg->aggregator_identifier))
@@ -407,7 +407,7 @@ static int bond_fill_info(struct sk_buff *skb,
        unsigned int packets_per_slave;
        int i, targets_added;
 
-       if (nla_put_u8(skb, IFLA_BOND_MODE, bond->params.mode))
+       if (nla_put_u8(skb, IFLA_BOND_MODE, BOND_MODE(bond)))
                goto nla_put_failure;
 
        if (slave_dev &&
@@ -505,7 +505,7 @@ static int bond_fill_info(struct sk_buff *skb,
                       bond->params.ad_select))
                goto nla_put_failure;
 
-       if (bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                struct ad_info info;
 
                if (!bond_3ad_get_active_agg_info(bond, &info)) {
index 832070298446458483cdf1132cc46b63ba323a7d..540e0167bf24992037165d82c5af307d85f15f02 100644 (file)
@@ -70,6 +70,8 @@ static int bond_option_mode_set(struct bonding *bond,
                                const struct bond_opt_value *newval);
 static int bond_option_slaves_set(struct bonding *bond,
                                  const struct bond_opt_value *newval);
+static int bond_option_tlb_dynamic_lb_set(struct bonding *bond,
+                                 const struct bond_opt_value *newval);
 
 
 static const struct bond_opt_value bond_mode_tbl[] = {
@@ -180,6 +182,12 @@ static const struct bond_opt_value bond_lp_interval_tbl[] = {
        { NULL,      -1,      0},
 };
 
+static const struct bond_opt_value bond_tlb_dynamic_lb_tbl[] = {
+       { "off", 0,  0},
+       { "on",  1,  BOND_VALFLAG_DEFAULT},
+       { NULL,  -1, 0}
+};
+
 static const struct bond_option bond_opts[] = {
        [BOND_OPT_MODE] = {
                .id = BOND_OPT_MODE,
@@ -200,7 +208,7 @@ static const struct bond_option bond_opts[] = {
        [BOND_OPT_XMIT_HASH] = {
                .id = BOND_OPT_XMIT_HASH,
                .name = "xmit_hash_policy",
-               .desc = "balance-xor and 802.3ad hashing method",
+               .desc = "balance-xor, 802.3ad, and tlb hashing method",
                .values = bond_xmit_hashtype_tbl,
                .set = bond_option_xmit_hash_policy_set
        },
@@ -365,9 +373,33 @@ static const struct bond_option bond_opts[] = {
                .flags = BOND_OPTFLAG_RAWVAL,
                .set = bond_option_slaves_set
        },
+       [BOND_OPT_TLB_DYNAMIC_LB] = {
+               .id = BOND_OPT_TLB_DYNAMIC_LB,
+               .name = "tlb_dynamic_lb",
+               .desc = "Enable dynamic flow shuffling",
+               .unsuppmodes = BOND_MODE_ALL_EX(BIT(BOND_MODE_TLB)),
+               .values = bond_tlb_dynamic_lb_tbl,
+               .flags = BOND_OPTFLAG_IFDOWN,
+               .set = bond_option_tlb_dynamic_lb_set,
+       },
        { }
 };
 
+/* Searches for an option by name */
+const struct bond_option *bond_opt_get_by_name(const char *name)
+{
+       const struct bond_option *opt;
+       int option;
+
+       for (option = 0; option < BOND_OPT_LAST; option++) {
+               opt = bond_opt_get(option);
+               if (opt && !strcmp(opt->name, name))
+                       return opt;
+       }
+
+       return NULL;
+}
+
 /* Searches for a value in opt's values[] table */
 const struct bond_opt_value *bond_opt_get_val(unsigned int option, u64 val)
 {
@@ -641,7 +673,7 @@ const struct bond_option *bond_opt_get(unsigned int option)
 
 int bond_option_mode_set(struct bonding *bond, const struct bond_opt_value *newval)
 {
-       if (BOND_NO_USES_ARP(newval->value) && bond->params.arp_interval) {
+       if (!bond_mode_uses_arp(newval->value) && bond->params.arp_interval) {
                pr_info("%s: %s mode is incompatible with arp monitoring, start mii monitoring\n",
                        bond->dev->name, newval->string);
                /* disable arp monitoring */
@@ -662,7 +694,7 @@ int bond_option_mode_set(struct bonding *bond, const struct bond_opt_value *newv
 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;
+       return bond_uses_primary(bond) && slave ? slave->dev : NULL;
 }
 
 struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond)
@@ -727,7 +759,7 @@ static int bond_option_active_slave_set(struct bonding *bond,
                                bond->dev->name, new_active->dev->name);
                } else {
                        if (old_active && (new_active->link == BOND_LINK_UP) &&
-                           IS_UP(new_active->dev)) {
+                           bond_slave_is_up(new_active)) {
                                pr_info("%s: Setting %s as active slave\n",
                                        bond->dev->name, new_active->dev->name);
                                bond_change_active_slave(bond, new_active);
@@ -746,6 +778,10 @@ static int bond_option_active_slave_set(struct bonding *bond,
        return ret;
 }
 
+/* There are two tricky bits here.  First, if MII monitoring is activated, then
+ * we must disable ARP monitoring.  Second, if the timer isn't running, we must
+ * start it.
+ */
 static int bond_option_miimon_set(struct bonding *bond,
                                  const struct bond_opt_value *newval)
 {
@@ -784,6 +820,10 @@ static int bond_option_miimon_set(struct bonding *bond,
        return 0;
 }
 
+/* Set up and down delays. These must be multiples of the
+ * MII monitoring value, and are stored internally as the multiplier.
+ * Thus, we must translate to MS for the real world.
+ */
 static int bond_option_updelay_set(struct bonding *bond,
                                   const struct bond_opt_value *newval)
 {
@@ -842,6 +882,10 @@ static int bond_option_use_carrier_set(struct bonding *bond,
        return 0;
 }
 
+/* There are two tricky bits here.  First, if ARP monitoring is activated, then
+ * we must disable MII monitoring.  Second, if the ARP timer isn't running,
+ * we must start it.
+ */
 static int bond_option_arp_interval_set(struct bonding *bond,
                                        const struct bond_opt_value *newval)
 {
@@ -899,7 +943,7 @@ static int _bond_option_arp_ip_target_add(struct bonding *bond, __be32 target)
        __be32 *targets = bond->params.arp_targets;
        int ind;
 
-       if (IS_IP_TARGET_UNUSABLE_ADDRESS(target)) {
+       if (!bond_is_ip_target_ok(target)) {
                pr_err("%s: invalid ARP target %pI4 specified for addition\n",
                       bond->dev->name, &target);
                return -EINVAL;
@@ -944,7 +988,7 @@ static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
        unsigned long *targets_rx;
        int ind, i;
 
-       if (IS_IP_TARGET_UNUSABLE_ADDRESS(target)) {
+       if (!bond_is_ip_target_ok(target)) {
                pr_err("%s: invalid ARP target %pI4 specified for removal\n",
                       bond->dev->name, &target);
                return -EINVAL;
@@ -1338,3 +1382,13 @@ static int bond_option_slaves_set(struct bonding *bond,
        ret = -EPERM;
        goto out;
 }
+
+static int bond_option_tlb_dynamic_lb_set(struct bonding *bond,
+                                         const struct bond_opt_value *newval)
+{
+       pr_info("%s: Setting dynamic-lb to %s (%llu)\n",
+               bond->dev->name, newval->string, newval->value);
+       bond->params.tlb_dynamic_lb = newval->value;
+
+       return 0;
+}
index 12be9e1bfb0c0d048229a1698c2b384847fbd794..17ded5b291761ca9e85e2fa6c82514a4613815b5 100644 (file)
@@ -62,6 +62,7 @@ enum {
        BOND_OPT_RESEND_IGMP,
        BOND_OPT_LP_INTERVAL,
        BOND_OPT_SLAVES,
+       BOND_OPT_TLB_DYNAMIC_LB,
        BOND_OPT_LAST
 };
 
@@ -104,6 +105,7 @@ int bond_opt_tryset_rtnl(struct bonding *bond, unsigned int option, char *buf);
 const struct bond_opt_value *bond_opt_parse(const struct bond_option *opt,
                                            struct bond_opt_value *val);
 const struct bond_option *bond_opt_get(unsigned int option);
+const struct bond_option *bond_opt_get_by_name(const char *name);
 const struct bond_opt_value *bond_opt_get_val(unsigned int option, u64 val);
 
 /* This helper is used to initialize a bond_opt_value structure for parameter
index 013fdd0f45e94340917ee2aeecc8529d386862e2..b215b479bb3a6917ffd6d37f30f989996d176f5b 100644 (file)
@@ -72,9 +72,9 @@ static void bond_info_show_master(struct seq_file *seq)
        curr = rcu_dereference(bond->curr_active_slave);
 
        seq_printf(seq, "Bonding Mode: %s",
-                  bond_mode_name(bond->params.mode));
+                  bond_mode_name(BOND_MODE(bond)));
 
-       if (bond->params.mode == BOND_MODE_ACTIVEBACKUP &&
+       if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP &&
            bond->params.fail_over_mac) {
                optval = bond_opt_get_val(BOND_OPT_FAIL_OVER_MAC,
                                          bond->params.fail_over_mac);
@@ -83,15 +83,15 @@ static void bond_info_show_master(struct seq_file *seq)
 
        seq_printf(seq, "\n");
 
-       if (bond->params.mode == BOND_MODE_XOR ||
-               bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_XOR ||
+               BOND_MODE(bond) == BOND_MODE_8023AD) {
                optval = bond_opt_get_val(BOND_OPT_XMIT_HASH,
                                          bond->params.xmit_policy);
                seq_printf(seq, "Transmit Hash Policy: %s (%d)\n",
                           optval->string, bond->params.xmit_policy);
        }
 
-       if (USES_PRIMARY(bond->params.mode)) {
+       if (bond_uses_primary(bond)) {
                seq_printf(seq, "Primary Slave: %s",
                           (bond->primary_slave) ?
                           bond->primary_slave->dev->name : "None");
@@ -134,7 +134,7 @@ static void bond_info_show_master(struct seq_file *seq)
                seq_printf(seq, "\n");
        }
 
-       if (bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                struct ad_info ad_info;
 
                seq_puts(seq, "\n802.3ad info\n");
@@ -188,9 +188,9 @@ static void bond_info_show_slave(struct seq_file *seq,
 
        seq_printf(seq, "Permanent HW addr: %pM\n", slave->perm_hwaddr);
 
-       if (bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                const struct aggregator *agg
-                       = SLAVE_AD_INFO(slave).port.aggregator;
+                       = SLAVE_AD_INFO(slave)->port.aggregator;
 
                if (agg)
                        seq_printf(seq, "Aggregator ID: %d\n",
index 5f6babcfc26e6aa56881ef2dc24b1ed51e3d7539..daed52f68ce1614ec94772ad1628f09417e04bfe 100644 (file)
@@ -45,8 +45,7 @@
 #define to_dev(obj)    container_of(obj, struct device, kobj)
 #define to_bond(cd)    ((struct bonding *)(netdev_priv(to_net_dev(cd))))
 
-/*
- * "show" function for the bond_masters attribute.
+/* "show" function for the bond_masters attribute.
  * The class parameter is ignored.
  */
 static ssize_t bonding_show_bonds(struct class *cls,
@@ -88,14 +87,12 @@ static struct net_device *bond_get_by_name(struct bond_net *bn, const char *ifna
        return NULL;
 }
 
-/*
- * "store" function for the bond_masters attribute.  This is what
+/* "store" function for the bond_masters attribute.  This is what
  * creates and deletes entire bonds.
  *
  * The class parameter is ignored.
  *
  */
-
 static ssize_t bonding_store_bonds(struct class *cls,
                                   struct class_attribute *attr,
                                   const char *buffer, size_t count)
@@ -158,9 +155,26 @@ static const struct class_attribute class_attr_bonding_masters = {
        .store = bonding_store_bonds,
 };
 
-/*
- * Show the slaves in the current bond.
- */
+/* Generic "store" method for bonding sysfs option setting */
+static ssize_t bonding_sysfs_store_option(struct device *d,
+                                         struct device_attribute *attr,
+                                         const char *buffer, size_t count)
+{
+       struct bonding *bond = to_bond(d);
+       const struct bond_option *opt;
+       int ret;
+
+       opt = bond_opt_get_by_name(attr->attr.name);
+       if (WARN_ON(!opt))
+               return -ENOENT;
+       ret = bond_opt_tryset_rtnl(bond, opt->id, (char *)buffer);
+       if (!ret)
+               ret = count;
+
+       return ret;
+}
+
+/* Show the slaves in the current bond. */
 static ssize_t bonding_show_slaves(struct device *d,
                                   struct device_attribute *attr, char *buf)
 {
@@ -190,62 +204,24 @@ static ssize_t bonding_show_slaves(struct device *d,
 
        return res;
 }
-
-/*
- * Set the slaves in the current bond.
- * This is supposed to be only thin wrapper for bond_enslave and bond_release.
- * All hard work should be done there.
- */
-static ssize_t bonding_store_slaves(struct device *d,
-                                   struct device_attribute *attr,
-                                   const char *buffer, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_SLAVES, (char *)buffer);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(slaves, S_IRUGO | S_IWUSR, bonding_show_slaves,
-                  bonding_store_slaves);
+                  bonding_sysfs_store_option);
 
-/*
- * Show and set the bonding mode.  The bond interface must be down to
- * change the mode.
- */
+/* Show the bonding mode. */
 static ssize_t bonding_show_mode(struct device *d,
                                 struct device_attribute *attr, char *buf)
 {
        struct bonding *bond = to_bond(d);
        const struct bond_opt_value *val;
 
-       val = bond_opt_get_val(BOND_OPT_MODE, bond->params.mode);
+       val = bond_opt_get_val(BOND_OPT_MODE, BOND_MODE(bond));
 
-       return sprintf(buf, "%s %d\n", val->string, bond->params.mode);
-}
-
-static ssize_t bonding_store_mode(struct device *d,
-                                 struct device_attribute *attr,
-                                 const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_MODE, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
+       return sprintf(buf, "%s %d\n", val->string, BOND_MODE(bond));
 }
 static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
-                  bonding_show_mode, bonding_store_mode);
+                  bonding_show_mode, bonding_sysfs_store_option);
 
-/*
- * Show and set the bonding transmit hash method.
- */
+/* Show the bonding transmit hash method. */
 static ssize_t bonding_show_xmit_hash(struct device *d,
                                      struct device_attribute *attr,
                                      char *buf)
@@ -257,26 +233,10 @@ static ssize_t bonding_show_xmit_hash(struct device *d,
 
        return sprintf(buf, "%s %d\n", val->string, bond->params.xmit_policy);
 }
-
-static ssize_t bonding_store_xmit_hash(struct device *d,
-                                      struct device_attribute *attr,
-                                      const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_XMIT_HASH, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(xmit_hash_policy, S_IRUGO | S_IWUSR,
-                  bonding_show_xmit_hash, bonding_store_xmit_hash);
+                  bonding_show_xmit_hash, bonding_sysfs_store_option);
 
-/*
- * Show and set arp_validate.
- */
+/* Show arp_validate. */
 static ssize_t bonding_show_arp_validate(struct device *d,
                                         struct device_attribute *attr,
                                         char *buf)
@@ -289,26 +249,10 @@ static ssize_t bonding_show_arp_validate(struct device *d,
 
        return sprintf(buf, "%s %d\n", val->string, bond->params.arp_validate);
 }
-
-static ssize_t bonding_store_arp_validate(struct device *d,
-                                         struct device_attribute *attr,
-                                         const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ARP_VALIDATE, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
-
 static DEVICE_ATTR(arp_validate, S_IRUGO | S_IWUSR, bonding_show_arp_validate,
-                  bonding_store_arp_validate);
-/*
- * Show and set arp_all_targets.
- */
+                  bonding_sysfs_store_option);
+
+/* Show arp_all_targets. */
 static ssize_t bonding_show_arp_all_targets(struct device *d,
                                         struct device_attribute *attr,
                                         char *buf)
@@ -321,28 +265,10 @@ static ssize_t bonding_show_arp_all_targets(struct device *d,
        return sprintf(buf, "%s %d\n",
                       val->string, bond->params.arp_all_targets);
 }
-
-static ssize_t bonding_store_arp_all_targets(struct device *d,
-                                         struct device_attribute *attr,
-                                         const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ARP_ALL_TARGETS, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
-
 static DEVICE_ATTR(arp_all_targets, S_IRUGO | S_IWUSR,
-                  bonding_show_arp_all_targets, bonding_store_arp_all_targets);
+                  bonding_show_arp_all_targets, bonding_sysfs_store_option);
 
-/*
- * Show and store fail_over_mac.  User only allowed to change the
- * value when there are no slaves.
- */
+/* Show fail_over_mac. */
 static ssize_t bonding_show_fail_over_mac(struct device *d,
                                          struct device_attribute *attr,
                                          char *buf)
@@ -355,30 +281,10 @@ static ssize_t bonding_show_fail_over_mac(struct device *d,
 
        return sprintf(buf, "%s %d\n", val->string, bond->params.fail_over_mac);
 }
-
-static ssize_t bonding_store_fail_over_mac(struct device *d,
-                                          struct device_attribute *attr,
-                                          const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_FAIL_OVER_MAC, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
-
 static DEVICE_ATTR(fail_over_mac, S_IRUGO | S_IWUSR,
-                  bonding_show_fail_over_mac, bonding_store_fail_over_mac);
+                  bonding_show_fail_over_mac, bonding_sysfs_store_option);
 
-/*
- * Show and set the arp timer interval.  There are two tricky bits
- * here.  First, if ARP monitoring is activated, then we must disable
- * MII monitoring.  Second, if the ARP timer isn't running, we must
- * start it.
- */
+/* Show the arp timer interval. */
 static ssize_t bonding_show_arp_interval(struct device *d,
                                         struct device_attribute *attr,
                                         char *buf)
@@ -387,26 +293,10 @@ static ssize_t bonding_show_arp_interval(struct device *d,
 
        return sprintf(buf, "%d\n", bond->params.arp_interval);
 }
-
-static ssize_t bonding_store_arp_interval(struct device *d,
-                                         struct device_attribute *attr,
-                                         const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ARP_INTERVAL, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(arp_interval, S_IRUGO | S_IWUSR,
-                  bonding_show_arp_interval, bonding_store_arp_interval);
+                  bonding_show_arp_interval, bonding_sysfs_store_option);
 
-/*
- * Show and set the arp targets.
- */
+/* Show the arp targets. */
 static ssize_t bonding_show_arp_targets(struct device *d,
                                        struct device_attribute *attr,
                                        char *buf)
@@ -424,27 +314,10 @@ static ssize_t bonding_show_arp_targets(struct device *d,
 
        return res;
 }
+static DEVICE_ATTR(arp_ip_target, S_IRUGO | S_IWUSR,
+                  bonding_show_arp_targets, bonding_sysfs_store_option);
 
-static ssize_t bonding_store_arp_targets(struct device *d,
-                                        struct device_attribute *attr,
-                                        const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ARP_TARGETS, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
-static DEVICE_ATTR(arp_ip_target, S_IRUGO | S_IWUSR , bonding_show_arp_targets, bonding_store_arp_targets);
-
-/*
- * Show and set the up and down delays.  These must be multiples of the
- * MII monitoring value, and are stored internally as the multiplier.
- * Thus, we must translate to MS for the real world.
- */
+/* Show the up and down delays. */
 static ssize_t bonding_show_downdelay(struct device *d,
                                      struct device_attribute *attr,
                                      char *buf)
@@ -453,22 +326,8 @@ static ssize_t bonding_show_downdelay(struct device *d,
 
        return sprintf(buf, "%d\n", bond->params.downdelay * bond->params.miimon);
 }
-
-static ssize_t bonding_store_downdelay(struct device *d,
-                                      struct device_attribute *attr,
-                                      const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_DOWNDELAY, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(downdelay, S_IRUGO | S_IWUSR,
-                  bonding_show_downdelay, bonding_store_downdelay);
+                  bonding_show_downdelay, bonding_sysfs_store_option);
 
 static ssize_t bonding_show_updelay(struct device *d,
                                    struct device_attribute *attr,
@@ -479,27 +338,10 @@ static ssize_t bonding_show_updelay(struct device *d,
        return sprintf(buf, "%d\n", bond->params.updelay * bond->params.miimon);
 
 }
-
-static ssize_t bonding_store_updelay(struct device *d,
-                                    struct device_attribute *attr,
-                                    const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_UPDELAY, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(updelay, S_IRUGO | S_IWUSR,
-                  bonding_show_updelay, bonding_store_updelay);
+                  bonding_show_updelay, bonding_sysfs_store_option);
 
-/*
- * Show and set the LACP interval.  Interface must be down, and the mode
- * must be set to 802.3ad mode.
- */
+/* Show the LACP interval. */
 static ssize_t bonding_show_lacp(struct device *d,
                                 struct device_attribute *attr,
                                 char *buf)
@@ -511,22 +353,8 @@ static ssize_t bonding_show_lacp(struct device *d,
 
        return sprintf(buf, "%s %d\n", val->string, bond->params.lacp_fast);
 }
-
-static ssize_t bonding_store_lacp(struct device *d,
-                                 struct device_attribute *attr,
-                                 const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_LACP_RATE, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(lacp_rate, S_IRUGO | S_IWUSR,
-                  bonding_show_lacp, bonding_store_lacp);
+                  bonding_show_lacp, bonding_sysfs_store_option);
 
 static ssize_t bonding_show_min_links(struct device *d,
                                      struct device_attribute *attr,
@@ -536,22 +364,8 @@ static ssize_t bonding_show_min_links(struct device *d,
 
        return sprintf(buf, "%u\n", bond->params.min_links);
 }
-
-static ssize_t bonding_store_min_links(struct device *d,
-                                      struct device_attribute *attr,
-                                      const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_MINLINKS, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(min_links, S_IRUGO | S_IWUSR,
-                  bonding_show_min_links, bonding_store_min_links);
+                  bonding_show_min_links, bonding_sysfs_store_option);
 
 static ssize_t bonding_show_ad_select(struct device *d,
                                      struct device_attribute *attr,
@@ -564,27 +378,10 @@ static ssize_t bonding_show_ad_select(struct device *d,
 
        return sprintf(buf, "%s %d\n", val->string, bond->params.ad_select);
 }
-
-
-static ssize_t bonding_store_ad_select(struct device *d,
-                                      struct device_attribute *attr,
-                                      const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_AD_SELECT, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(ad_select, S_IRUGO | S_IWUSR,
-                  bonding_show_ad_select, bonding_store_ad_select);
+                  bonding_show_ad_select, bonding_sysfs_store_option);
 
-/*
- * Show and set the number of peer notifications to send after a failover event.
- */
+/* Show and set the number of peer notifications to send after a failover event. */
 static ssize_t bonding_show_num_peer_notif(struct device *d,
                                           struct device_attribute *attr,
                                           char *buf)
@@ -611,12 +408,7 @@ static DEVICE_ATTR(num_grat_arp, S_IRUGO | S_IWUSR,
 static DEVICE_ATTR(num_unsol_na, S_IRUGO | S_IWUSR,
                   bonding_show_num_peer_notif, bonding_store_num_peer_notif);
 
-/*
- * Show and set the MII monitor interval.  There are two tricky bits
- * here.  First, if MII monitoring is activated, then we must disable
- * ARP monitoring.  Second, if the timer isn't running, we must
- * start it.
- */
+/* Show the MII monitor interval. */
 static ssize_t bonding_show_miimon(struct device *d,
                                   struct device_attribute *attr,
                                   char *buf)
@@ -625,30 +417,10 @@ static ssize_t bonding_show_miimon(struct device *d,
 
        return sprintf(buf, "%d\n", bond->params.miimon);
 }
-
-static ssize_t bonding_store_miimon(struct device *d,
-                                   struct device_attribute *attr,
-                                   const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_MIIMON, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(miimon, S_IRUGO | S_IWUSR,
-                  bonding_show_miimon, bonding_store_miimon);
+                  bonding_show_miimon, bonding_sysfs_store_option);
 
-/*
- * Show and set the primary slave.  The store function is much
- * simpler than bonding_store_slaves function because it only needs to
- * handle one interface name.
- * The bond must be a mode that supports a primary for this be
- * set.
- */
+/* Show the primary slave. */
 static ssize_t bonding_show_primary(struct device *d,
                                    struct device_attribute *attr,
                                    char *buf)
@@ -661,26 +433,10 @@ static ssize_t bonding_show_primary(struct device *d,
 
        return count;
 }
-
-static ssize_t bonding_store_primary(struct device *d,
-                                    struct device_attribute *attr,
-                                    const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_PRIMARY, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(primary, S_IRUGO | S_IWUSR,
-                  bonding_show_primary, bonding_store_primary);
+                  bonding_show_primary, bonding_sysfs_store_option);
 
-/*
- * Show and set the primary_reselect flag.
- */
+/* Show the primary_reselect flag. */
 static ssize_t bonding_show_primary_reselect(struct device *d,
                                             struct device_attribute *attr,
                                             char *buf)
@@ -694,28 +450,10 @@ static ssize_t bonding_show_primary_reselect(struct device *d,
        return sprintf(buf, "%s %d\n",
                       val->string, bond->params.primary_reselect);
 }
-
-static ssize_t bonding_store_primary_reselect(struct device *d,
-                                             struct device_attribute *attr,
-                                             const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_PRIMARY_RESELECT,
-                                  (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(primary_reselect, S_IRUGO | S_IWUSR,
-                  bonding_show_primary_reselect,
-                  bonding_store_primary_reselect);
+                  bonding_show_primary_reselect, bonding_sysfs_store_option);
 
-/*
- * Show and set the use_carrier flag.
- */
+/* Show the use_carrier flag. */
 static ssize_t bonding_show_carrier(struct device *d,
                                    struct device_attribute *attr,
                                    char *buf)
@@ -724,27 +462,11 @@ static ssize_t bonding_show_carrier(struct device *d,
 
        return sprintf(buf, "%d\n", bond->params.use_carrier);
 }
-
-static ssize_t bonding_store_carrier(struct device *d,
-                                    struct device_attribute *attr,
-                                    const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_USE_CARRIER, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(use_carrier, S_IRUGO | S_IWUSR,
-                  bonding_show_carrier, bonding_store_carrier);
+                  bonding_show_carrier, bonding_sysfs_store_option);
 
 
-/*
- * Show and set currently active_slave.
- */
+/* Show currently active_slave. */
 static ssize_t bonding_show_active_slave(struct device *d,
                                         struct device_attribute *attr,
                                         char *buf)
@@ -761,27 +483,10 @@ static ssize_t bonding_show_active_slave(struct device *d,
 
        return count;
 }
-
-static ssize_t bonding_store_active_slave(struct device *d,
-                                         struct device_attribute *attr,
-                                         const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ACTIVE_SLAVE, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(active_slave, S_IRUGO | S_IWUSR,
-                  bonding_show_active_slave, bonding_store_active_slave);
-
+                  bonding_show_active_slave, bonding_sysfs_store_option);
 
-/*
- * Show link status of the bond interface.
- */
+/* Show link status of the bond interface. */
 static ssize_t bonding_show_mii_status(struct device *d,
                                       struct device_attribute *attr,
                                       char *buf)
@@ -792,9 +497,7 @@ static ssize_t bonding_show_mii_status(struct device *d,
 }
 static DEVICE_ATTR(mii_status, S_IRUGO, bonding_show_mii_status, NULL);
 
-/*
- * Show current 802.3ad aggregator ID.
- */
+/* Show current 802.3ad aggregator ID. */
 static ssize_t bonding_show_ad_aggregator(struct device *d,
                                          struct device_attribute *attr,
                                          char *buf)
@@ -802,7 +505,7 @@ static ssize_t bonding_show_ad_aggregator(struct device *d,
        int count = 0;
        struct bonding *bond = to_bond(d);
 
-       if (bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                struct ad_info ad_info;
                count = sprintf(buf, "%d\n",
                                bond_3ad_get_active_agg_info(bond, &ad_info)
@@ -814,9 +517,7 @@ static ssize_t bonding_show_ad_aggregator(struct device *d,
 static DEVICE_ATTR(ad_aggregator, S_IRUGO, bonding_show_ad_aggregator, NULL);
 
 
-/*
- * Show number of active 802.3ad ports.
- */
+/* Show number of active 802.3ad ports. */
 static ssize_t bonding_show_ad_num_ports(struct device *d,
                                         struct device_attribute *attr,
                                         char *buf)
@@ -824,7 +525,7 @@ static ssize_t bonding_show_ad_num_ports(struct device *d,
        int count = 0;
        struct bonding *bond = to_bond(d);
 
-       if (bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                struct ad_info ad_info;
                count = sprintf(buf, "%d\n",
                                bond_3ad_get_active_agg_info(bond, &ad_info)
@@ -836,9 +537,7 @@ static ssize_t bonding_show_ad_num_ports(struct device *d,
 static DEVICE_ATTR(ad_num_ports, S_IRUGO, bonding_show_ad_num_ports, NULL);
 
 
-/*
- * Show current 802.3ad actor key.
- */
+/* Show current 802.3ad actor key. */
 static ssize_t bonding_show_ad_actor_key(struct device *d,
                                         struct device_attribute *attr,
                                         char *buf)
@@ -846,7 +545,7 @@ static ssize_t bonding_show_ad_actor_key(struct device *d,
        int count = 0;
        struct bonding *bond = to_bond(d);
 
-       if (bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                struct ad_info ad_info;
                count = sprintf(buf, "%d\n",
                                bond_3ad_get_active_agg_info(bond, &ad_info)
@@ -858,9 +557,7 @@ static ssize_t bonding_show_ad_actor_key(struct device *d,
 static DEVICE_ATTR(ad_actor_key, S_IRUGO, bonding_show_ad_actor_key, NULL);
 
 
-/*
- * Show current 802.3ad partner key.
- */
+/* Show current 802.3ad partner key. */
 static ssize_t bonding_show_ad_partner_key(struct device *d,
                                           struct device_attribute *attr,
                                           char *buf)
@@ -868,7 +565,7 @@ static ssize_t bonding_show_ad_partner_key(struct device *d,
        int count = 0;
        struct bonding *bond = to_bond(d);
 
-       if (bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                struct ad_info ad_info;
                count = sprintf(buf, "%d\n",
                                bond_3ad_get_active_agg_info(bond, &ad_info)
@@ -880,9 +577,7 @@ static ssize_t bonding_show_ad_partner_key(struct device *d,
 static DEVICE_ATTR(ad_partner_key, S_IRUGO, bonding_show_ad_partner_key, NULL);
 
 
-/*
- * Show current 802.3ad partner mac.
- */
+/* Show current 802.3ad partner mac. */
 static ssize_t bonding_show_ad_partner_mac(struct device *d,
                                           struct device_attribute *attr,
                                           char *buf)
@@ -890,7 +585,7 @@ static ssize_t bonding_show_ad_partner_mac(struct device *d,
        int count = 0;
        struct bonding *bond = to_bond(d);
 
-       if (bond->params.mode == BOND_MODE_8023AD) {
+       if (BOND_MODE(bond) == BOND_MODE_8023AD) {
                struct ad_info ad_info;
                if (!bond_3ad_get_active_agg_info(bond, &ad_info))
                        count = sprintf(buf, "%pM\n", ad_info.partner_system);
@@ -900,9 +595,7 @@ static ssize_t bonding_show_ad_partner_mac(struct device *d,
 }
 static DEVICE_ATTR(ad_partner_mac, S_IRUGO, bonding_show_ad_partner_mac, NULL);
 
-/*
- * Show the queue_ids of the slaves in the current bond.
- */
+/* Show the queue_ids of the slaves in the current bond. */
 static ssize_t bonding_show_queue_id(struct device *d,
                                     struct device_attribute *attr,
                                     char *buf)
@@ -933,31 +626,11 @@ static ssize_t bonding_show_queue_id(struct device *d,
 
        return res;
 }
-
-/*
- * Set the queue_ids of the  slaves in the current bond.  The bond
- * interface must be enslaved for this to work.
- */
-static ssize_t bonding_store_queue_id(struct device *d,
-                                     struct device_attribute *attr,
-                                     const char *buffer, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_QUEUE_ID, (char *)buffer);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(queue_id, S_IRUGO | S_IWUSR, bonding_show_queue_id,
-                  bonding_store_queue_id);
+                  bonding_sysfs_store_option);
 
 
-/*
- * Show and set the all_slaves_active flag.
- */
+/* Show the all_slaves_active flag. */
 static ssize_t bonding_show_slaves_active(struct device *d,
                                          struct device_attribute *attr,
                                          char *buf)
@@ -966,27 +639,10 @@ static ssize_t bonding_show_slaves_active(struct device *d,
 
        return sprintf(buf, "%d\n", bond->params.all_slaves_active);
 }
-
-static ssize_t bonding_store_slaves_active(struct device *d,
-                                          struct device_attribute *attr,
-                                          const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ALL_SLAVES_ACTIVE,
-                                  (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
 static DEVICE_ATTR(all_slaves_active, S_IRUGO | S_IWUSR,
-                  bonding_show_slaves_active, bonding_store_slaves_active);
+                  bonding_show_slaves_active, bonding_sysfs_store_option);
 
-/*
- * Show and set the number of IGMP membership reports to send on link failure
- */
+/* Show the number of IGMP membership reports to send on link failure */
 static ssize_t bonding_show_resend_igmp(struct device *d,
                                        struct device_attribute *attr,
                                        char *buf)
@@ -995,23 +651,8 @@ static ssize_t bonding_show_resend_igmp(struct device *d,
 
        return sprintf(buf, "%d\n", bond->params.resend_igmp);
 }
-
-static ssize_t bonding_store_resend_igmp(struct device *d,
-                                        struct device_attribute *attr,
-                                        const char *buf, size_t count)
-{
-       struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_RESEND_IGMP, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
-}
-
 static DEVICE_ATTR(resend_igmp, S_IRUGO | S_IWUSR,
-                  bonding_show_resend_igmp, bonding_store_resend_igmp);
+                  bonding_show_resend_igmp, bonding_sysfs_store_option);
 
 
 static ssize_t bonding_show_lp_interval(struct device *d,
@@ -1019,25 +660,21 @@ static ssize_t bonding_show_lp_interval(struct device *d,
                                        char *buf)
 {
        struct bonding *bond = to_bond(d);
+
        return sprintf(buf, "%d\n", bond->params.lp_interval);
 }
+static DEVICE_ATTR(lp_interval, S_IRUGO | S_IWUSR,
+                  bonding_show_lp_interval, bonding_sysfs_store_option);
 
-static ssize_t bonding_store_lp_interval(struct device *d,
-                                        struct device_attribute *attr,
-                                        const char *buf, size_t count)
+static ssize_t bonding_show_tlb_dynamic_lb(struct device *d,
+                                          struct device_attribute *attr,
+                                          char *buf)
 {
        struct bonding *bond = to_bond(d);
-       int ret;
-
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_LP_INTERVAL, (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
+       return sprintf(buf, "%d\n", bond->params.tlb_dynamic_lb);
 }
-
-static DEVICE_ATTR(lp_interval, S_IRUGO | S_IWUSR,
-                  bonding_show_lp_interval, bonding_store_lp_interval);
+static DEVICE_ATTR(tlb_dynamic_lb, S_IRUGO | S_IWUSR,
+                  bonding_show_tlb_dynamic_lb, bonding_sysfs_store_option);
 
 static ssize_t bonding_show_packets_per_slave(struct device *d,
                                              struct device_attribute *attr,
@@ -1045,27 +682,11 @@ static ssize_t bonding_show_packets_per_slave(struct device *d,
 {
        struct bonding *bond = to_bond(d);
        unsigned int packets_per_slave = bond->params.packets_per_slave;
-       return sprintf(buf, "%u\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 ret;
 
-       ret = bond_opt_tryset_rtnl(bond, BOND_OPT_PACKETS_PER_SLAVE,
-                                  (char *)buf);
-       if (!ret)
-               ret = count;
-
-       return ret;
+       return sprintf(buf, "%u\n", packets_per_slave);
 }
-
 static DEVICE_ATTR(packets_per_slave, S_IRUGO | S_IWUSR,
-                  bonding_show_packets_per_slave,
-                  bonding_store_packets_per_slave);
+                  bonding_show_packets_per_slave, bonding_sysfs_store_option);
 
 static struct attribute *per_bond_attrs[] = {
        &dev_attr_slaves.attr,
@@ -1099,6 +720,7 @@ static struct attribute *per_bond_attrs[] = {
        &dev_attr_min_links.attr,
        &dev_attr_lp_interval.attr,
        &dev_attr_packets_per_slave.attr,
+       &dev_attr_tlb_dynamic_lb.attr,
        NULL,
 };
 
@@ -1107,8 +729,7 @@ static struct attribute_group bonding_group = {
        .attrs = per_bond_attrs,
 };
 
-/*
- * Initialize sysfs.  This sets up the bonding_masters file in
+/* Initialize sysfs.  This sets up the bonding_masters file in
  * /sys/class/net.
  */
 int bond_create_sysfs(struct bond_net *bn)
@@ -1120,8 +741,7 @@ int bond_create_sysfs(struct bond_net *bn)
 
        ret = netdev_class_create_file_ns(&bn->class_attr_bonding_masters,
                                          bn->net);
-       /*
-        * Permit multiple loads of the module by ignoring failures to
+       /* Permit multiple loads of the module by ignoring failures to
         * create the bonding_masters sysfs file.  Bonding devices
         * created by second or subsequent loads of the module will
         * not be listed in, or controllable by, bonding_masters, but
@@ -1144,16 +764,13 @@ int bond_create_sysfs(struct bond_net *bn)
 
 }
 
-/*
- * Remove /sys/class/net/bonding_masters.
- */
+/* Remove /sys/class/net/bonding_masters. */
 void bond_destroy_sysfs(struct bond_net *bn)
 {
        netdev_class_remove_file_ns(&bn->class_attr_bonding_masters, bn->net);
 }
 
-/*
- * Initialize sysfs for each bond.  This sets up and registers
+/* Initialize sysfs for each bond.  This sets up and registers
  * the 'bondctl' directory for each individual bond under /sys/class/net.
  */
 void bond_prepare_sysfs_group(struct bonding *bond)
index 2e4eec5450c80b726a7bbf1a47ae169cdd18db4f..198677f58ce0af4134b2491e90e6774fa2ae17b4 100644 (file)
@@ -69,8 +69,8 @@ static ssize_t ad_aggregator_id_show(struct slave *slave, char *buf)
 {
        const struct aggregator *agg;
 
-       if (slave->bond->params.mode == BOND_MODE_8023AD) {
-               agg = SLAVE_AD_INFO(slave).port.aggregator;
+       if (BOND_MODE(slave->bond) == BOND_MODE_8023AD) {
+               agg = SLAVE_AD_INFO(slave)->port.aggregator;
                if (agg)
                        return sprintf(buf, "%d\n",
                                       agg->aggregator_identifier);
index 00bea320e3b50c1eaa75a712f7afbbe65ac146fb..0b4d9cde0b05e33a34e5deaec3b28126f1cba8e1 100644 (file)
 
 #define BOND_DEFAULT_MIIMON    100
 
-#define IS_UP(dev)                                        \
-             ((((dev)->flags & IFF_UP) == IFF_UP)      && \
-              netif_running(dev)                       && \
-              netif_carrier_ok(dev))
-
-/*
- * Checks whether slave is ready for transmit.
- */
-#define SLAVE_IS_OK(slave)                             \
-                   (((slave)->dev->flags & IFF_UP)  && \
-                    netif_running((slave)->dev)     && \
-                    ((slave)->link == BOND_LINK_UP) && \
-                    bond_is_active_slave(slave))
-
-
-#define USES_PRIMARY(mode)                             \
-               (((mode) == BOND_MODE_ACTIVEBACKUP) ||  \
-                ((mode) == BOND_MODE_TLB)          ||  \
-                ((mode) == BOND_MODE_ALB))
-
-#define BOND_NO_USES_ARP(mode)                         \
-               (((mode) == BOND_MODE_8023AD)   ||      \
-                ((mode) == BOND_MODE_TLB)      ||      \
-                ((mode) == BOND_MODE_ALB))
-
-#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))
-
-#define IS_IP_TARGET_UNUSABLE_ADDRESS(a)       \
-       ((htonl(INADDR_BROADCAST) == a) ||      \
-        ipv4_is_zeronet(a))
 /*
  * 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.
@@ -90,6 +54,8 @@
        set_fs(fs);                     \
        res; })
 
+#define BOND_MODE(bond) ((bond)->params.mode)
+
 /* slave list primitives */
 #define bond_slave_list(bond) (&(bond)->dev->adj_list.lower)
 
@@ -175,6 +141,7 @@ struct bond_params {
        int resend_igmp;
        int lp_interval;
        int packets_per_slave;
+       int tlb_dynamic_lb;
        struct reciprocal_value reciprocal_packets_per_slave;
 };
 
@@ -183,8 +150,6 @@ struct bond_parm_tbl {
        int mode;
 };
 
-#define BOND_MAX_MODENAME_LEN 20
-
 struct slave {
        struct net_device *dev; /* first - useful for panic debug */
        struct bonding *bond; /* our master */
@@ -205,7 +170,7 @@ struct slave {
        u32    speed;
        u16    queue_id;
        u8     perm_hwaddr[ETH_ALEN];
-       struct ad_slave_info ad_info; /* HUGE - better to dynamically alloc */
+       struct ad_slave_info *ad_info;
        struct tlb_slave_info tlb_info;
 #ifdef CONFIG_NET_POLL_CONTROLLER
        struct netpoll *np;
@@ -285,14 +250,41 @@ static inline struct slave *bond_get_slave_by_dev(struct bonding *bond,
 
 static inline struct bonding *bond_get_bond_by_slave(struct slave *slave)
 {
-       if (!slave || !slave->bond)
-               return NULL;
        return slave->bond;
 }
 
+static inline bool bond_should_override_tx_queue(struct bonding *bond)
+{
+       return BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP ||
+              BOND_MODE(bond) == BOND_MODE_ROUNDROBIN;
+}
+
 static inline bool bond_is_lb(const struct bonding *bond)
 {
-       return BOND_MODE_IS_LB(bond->params.mode);
+       return BOND_MODE(bond) == BOND_MODE_TLB ||
+              BOND_MODE(bond) == BOND_MODE_ALB;
+}
+
+static inline bool bond_mode_uses_arp(int mode)
+{
+       return mode != BOND_MODE_8023AD && mode != BOND_MODE_TLB &&
+              mode != BOND_MODE_ALB;
+}
+
+static inline bool bond_mode_uses_primary(int mode)
+{
+       return mode == BOND_MODE_ACTIVEBACKUP || mode == BOND_MODE_TLB ||
+              mode == BOND_MODE_ALB;
+}
+
+static inline bool bond_uses_primary(struct bonding *bond)
+{
+       return bond_mode_uses_primary(BOND_MODE(bond));
+}
+
+static inline bool bond_slave_is_up(struct slave *slave)
+{
+       return netif_running(slave->dev) && netif_carrier_ok(slave->dev);
 }
 
 static inline void bond_set_active_slave(struct slave *slave)
@@ -365,6 +357,12 @@ static inline bool bond_is_active_slave(struct slave *slave)
        return !bond_slave_state(slave);
 }
 
+static inline bool bond_slave_can_tx(struct slave *slave)
+{
+       return bond_slave_is_up(slave) && slave->link == BOND_LINK_UP &&
+              bond_is_active_slave(slave);
+}
+
 #define BOND_PRI_RESELECT_ALWAYS       0
 #define BOND_PRI_RESELECT_BETTER       1
 #define BOND_PRI_RESELECT_FAILURE      2
@@ -396,12 +394,16 @@ static inline int slave_do_arp_validate(struct bonding *bond,
        return bond->params.arp_validate & (1 << bond_slave_state(slave));
 }
 
-static inline int slave_do_arp_validate_only(struct bonding *bond,
-                                            struct slave *slave)
+static inline int slave_do_arp_validate_only(struct bonding *bond)
 {
        return bond->params.arp_validate & BOND_ARP_FILTER;
 }
 
+static inline int bond_is_ip_target_ok(__be32 addr)
+{
+       return !ipv4_is_lbcast(addr) && !ipv4_is_zeronet(addr);
+}
+
 /* Get the oldest arp which we've received on this slave for bond's
  * arp_targets.
  */
@@ -479,16 +481,14 @@ static inline __be32 bond_confirm_addr(struct net_device *dev, __be32 dst, __be3
        return addr;
 }
 
-static inline bool slave_can_tx(struct slave *slave)
-{
-       if (IS_UP(slave->dev) && slave->link == BOND_LINK_UP &&
-           bond_is_active_slave(slave))
-               return true;
-       else
-               return false;
-}
-
-struct bond_net;
+struct bond_net {
+       struct net              *net;   /* Associated network namespace */
+       struct list_head        dev_list;
+#ifdef CONFIG_PROC_FS
+       struct proc_dir_entry   *proc_dir;
+#endif
+       struct class_attribute  class_attr_bonding_masters;
+};
 
 int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond, struct slave *slave);
 void bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *slave_dev);
@@ -500,7 +500,7 @@ int bond_sysfs_slave_add(struct slave *slave);
 void bond_sysfs_slave_del(struct slave *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);
-int bond_xmit_hash(struct bonding *bond, struct sk_buff *skb, int count);
+u32 bond_xmit_hash(struct bonding *bond, struct sk_buff *skb);
 void bond_select_active_slave(struct bonding *bond);
 void bond_change_active_slave(struct bonding *bond, struct slave *new_active);
 void bond_create_debugfs(void);
@@ -516,15 +516,9 @@ void bond_netlink_fini(void);
 struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond);
 struct net_device *bond_option_active_slave_get(struct bonding *bond);
 const char *bond_slave_link_status(s8 link);
-
-struct bond_net {
-       struct net *            net;    /* Associated network namespace */
-       struct list_head        dev_list;
-#ifdef CONFIG_PROC_FS
-       struct proc_dir_entry * proc_dir;
-#endif
-       struct class_attribute  class_attr_bonding_masters;
-};
+bool bond_verify_device_path(struct net_device *start_dev,
+                            struct net_device *end_dev,
+                            struct bond_vlan_tag *tags);
 
 #ifdef CONFIG_PROC_FS
 void bond_create_proc_entry(struct bonding *bond);
@@ -576,6 +570,27 @@ static inline struct slave *bond_slave_has_mac_rcu(struct bonding *bond,
        return NULL;
 }
 
+/* Caller must hold rcu_read_lock() for read */
+static inline bool bond_slave_has_mac_rx(struct bonding *bond, const u8 *mac)
+{
+       struct list_head *iter;
+       struct slave *tmp;
+       struct netdev_hw_addr *ha;
+
+       bond_for_each_slave_rcu(bond, tmp, iter)
+               if (ether_addr_equal_64bits(mac, tmp->dev->dev_addr))
+                       return true;
+
+       if (netdev_uc_empty(bond->dev))
+               return false;
+
+       netdev_for_each_uc_addr(ha, bond->dev)
+               if (ether_addr_equal_64bits(mac, ha->addr))
+                       return true;
+
+       return false;
+}
+
 /* Check if the ip is present in arp ip list, or first free slot if ip == 0
  * Returns -1 if not found, index if found
  */
index 9e7d95dae2c7038478d6efadddba81e2778f47a9..41688229c570eb92e9e79b2bee2bd5b9d6173e39 100644 (file)
@@ -65,7 +65,7 @@ config CAN_LEDS
 
 config CAN_AT91
        tristate "Atmel AT91 onchip CAN controller"
-       depends on ARM
+       depends on ARCH_AT91 || COMPILE_TEST
        ---help---
          This is a driver for the SoC CAN controller in Atmel's AT91SAM9263
          and AT91SAM9X5 processors.
@@ -77,12 +77,6 @@ config CAN_TI_HECC
          Driver for TI HECC (High End CAN Controller) module found on many
          TI devices. The device specifications are available from www.ti.com
 
-config CAN_MCP251X
-       tristate "Microchip MCP251x SPI CAN controllers"
-       depends on SPI && HAS_DMA
-       ---help---
-         Driver for the Microchip MCP251x SPI CAN controllers.
-
 config CAN_BFIN
        depends on BF534 || BF536 || BF537 || BF538 || BF539 || BF54x
        tristate "Analog Devices Blackfin on-chip CAN"
@@ -110,7 +104,7 @@ config CAN_FLEXCAN
 
 config PCH_CAN
        tristate "Intel EG20T PCH CAN controller"
-       depends on PCI
+       depends on PCI && (X86_32 || COMPILE_TEST)
        ---help---
          This driver is for PCH CAN of Topcliff (Intel EG20T PCH) which
          is an IOH for x86 embedded processor (Intel Atom E6xx series).
@@ -125,6 +119,24 @@ config CAN_GRCAN
          endian syntheses of the cores would need some modifications on
          the hardware level to work.
 
+config CAN_RCAR
+       tristate "Renesas R-Car CAN controller"
+       depends on ARM
+       ---help---
+         Say Y here if you want to use CAN controller found on Renesas R-Car
+         SoCs.
+
+         To compile this driver as a module, choose M here: the module will
+         be called rcar_can.
+
+config CAN_XILINXCAN
+       tristate "Xilinx CAN"
+       depends on ARCH_ZYNQ || MICROBLAZE || COMPILE_TEST
+       depends on COMMON_CLK && HAS_IOMEM
+       ---help---
+         Xilinx CAN driver. This driver supports both soft AXI CAN IP and
+         Zynq CANPS IP.
+
 source "drivers/net/can/mscan/Kconfig"
 
 source "drivers/net/can/sja1000/Kconfig"
@@ -133,6 +145,8 @@ source "drivers/net/can/c_can/Kconfig"
 
 source "drivers/net/can/cc770/Kconfig"
 
+source "drivers/net/can/spi/Kconfig"
+
 source "drivers/net/can/usb/Kconfig"
 
 source "drivers/net/can/softing/Kconfig"
index c7440392adbbaaabd5ca5b8ba872ba18c23f41d2..1697f22353a943315bdb5e162d3cdce4f7d535f7 100644 (file)
@@ -10,6 +10,7 @@ can-dev-y                     := dev.o
 
 can-dev-$(CONFIG_CAN_LEDS)     += led.o
 
+obj-y                          += spi/
 obj-y                          += usb/
 obj-y                          += softing/
 
@@ -19,11 +20,12 @@ obj-$(CONFIG_CAN_C_CAN)             += c_can/
 obj-$(CONFIG_CAN_CC770)                += cc770/
 obj-$(CONFIG_CAN_AT91)         += at91_can.o
 obj-$(CONFIG_CAN_TI_HECC)      += ti_hecc.o
-obj-$(CONFIG_CAN_MCP251X)      += mcp251x.o
 obj-$(CONFIG_CAN_BFIN)         += bfin_can.o
 obj-$(CONFIG_CAN_JANZ_ICAN3)   += janz-ican3.o
 obj-$(CONFIG_CAN_FLEXCAN)      += flexcan.o
 obj-$(CONFIG_PCH_CAN)          += pch_can.o
 obj-$(CONFIG_CAN_GRCAN)                += grcan.o
+obj-$(CONFIG_CAN_RCAR)         += rcar_can.o
+obj-$(CONFIG_CAN_XILINXCAN)    += xilinx_can.o
 
 ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
index 95e04e2002daec774d394d0f0912ca7e952f280f..8e78bb48f5a4399f0fef7e991d64984c638c077a 100644 (file)
@@ -252,8 +252,7 @@ static void c_can_obj_update(struct net_device *dev, int iface, u32 cmd, u32 obj
        struct c_can_priv *priv = netdev_priv(dev);
        int cnt, reg = C_CAN_IFACE(COMREQ_REG, iface);
 
-       priv->write_reg(priv, reg + 1, cmd);
-       priv->write_reg(priv, reg, obj);
+       priv->write_reg32(priv, reg, (cmd << 16) | obj);
 
        for (cnt = MIN_TIMEOUT_VALUE; cnt; cnt--) {
                if (!(priv->read_reg(priv, reg) & IF_COMR_BUSY))
@@ -328,8 +327,7 @@ static void c_can_setup_tx_object(struct net_device *dev, int iface,
                change_bit(idx, &priv->tx_dir);
        }
 
-       priv->write_reg(priv, C_CAN_IFACE(ARB1_REG, iface), arb);
-       priv->write_reg(priv, C_CAN_IFACE(ARB2_REG, iface), arb >> 16);
+       priv->write_reg32(priv, C_CAN_IFACE(ARB1_REG, iface), arb);
 
        priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), ctrl);
 
@@ -391,8 +389,7 @@ static int c_can_read_msg_object(struct net_device *dev, int iface, u32 ctrl)
 
        frame->can_dlc = get_can_dlc(ctrl & 0x0F);
 
-       arb = priv->read_reg(priv, C_CAN_IFACE(ARB1_REG, iface));
-       arb |= priv->read_reg(priv, C_CAN_IFACE(ARB2_REG, iface)) << 16;
+       arb = priv->read_reg32(priv, C_CAN_IFACE(ARB1_REG, iface));
 
        if (arb & IF_ARB_MSGXTD)
                frame->can_id = (arb & CAN_EFF_MASK) | CAN_EFF_FLAG;
@@ -424,12 +421,10 @@ static void c_can_setup_receive_object(struct net_device *dev, int iface,
        struct c_can_priv *priv = netdev_priv(dev);
 
        mask |= BIT(29);
-       priv->write_reg(priv, C_CAN_IFACE(MASK1_REG, iface), mask);
-       priv->write_reg(priv, C_CAN_IFACE(MASK2_REG, iface), mask >> 16);
+       priv->write_reg32(priv, C_CAN_IFACE(MASK1_REG, iface), mask);
 
        id |= IF_ARB_MSGVAL;
-       priv->write_reg(priv, C_CAN_IFACE(ARB1_REG, iface), id);
-       priv->write_reg(priv, C_CAN_IFACE(ARB2_REG, iface), id >> 16);
+       priv->write_reg32(priv, C_CAN_IFACE(ARB1_REG, iface), id);
 
        priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), mcont);
        c_can_object_put(dev, iface, obj, IF_COMM_RCV_SETUP);
index c56f1b1c11cacde9e41c5cd663fd69ce802f2025..99ad1aa576b045197f82780d64936f3b7fe5651a 100644 (file)
@@ -78,6 +78,7 @@ enum reg {
        C_CAN_INTPND2_REG,
        C_CAN_MSGVAL1_REG,
        C_CAN_MSGVAL2_REG,
+       C_CAN_FUNCTION_REG,
 };
 
 static const u16 reg_map_c_can[] = {
@@ -129,6 +130,7 @@ static const u16 reg_map_d_can[] = {
        [C_CAN_BRPEXT_REG]      = 0x0E,
        [C_CAN_INT_REG]         = 0x10,
        [C_CAN_TEST_REG]        = 0x14,
+       [C_CAN_FUNCTION_REG]    = 0x18,
        [C_CAN_TXRQST1_REG]     = 0x88,
        [C_CAN_TXRQST2_REG]     = 0x8A,
        [C_CAN_NEWDAT1_REG]     = 0x9C,
@@ -176,8 +178,10 @@ struct c_can_priv {
        atomic_t tx_active;
        unsigned long tx_dir;
        int last_status;
-       u16 (*read_reg) (struct c_can_priv *priv, enum reg index);
-       void (*write_reg) (struct c_can_priv *priv, enum reg index, u16 val);
+       u16 (*read_reg) (const struct c_can_priv *priv, enum reg index);
+       void (*write_reg) (const struct c_can_priv *priv, enum reg index, u16 val);
+       u32 (*read_reg32) (const struct c_can_priv *priv, enum reg index);
+       void (*write_reg32) (const struct c_can_priv *priv, enum reg index, u32 val);
        void __iomem *base;
        const u16 *regs;
        void *priv;             /* for board-specific data */
index fe5f6303b58400fb69913229c20da6a2f2a5d303..5d11e0e4225bf3c84442b9ec8ddea4a005b4717f 100644 (file)
 
 #include "c_can.h"
 
+#define PCI_DEVICE_ID_PCH_CAN  0x8818
+#define PCH_PCI_SOFT_RESET     0x01fc
+
 enum c_can_pci_reg_align {
        C_CAN_REG_ALIGN_16,
        C_CAN_REG_ALIGN_32,
+       C_CAN_REG_32,
 };
 
 struct c_can_pci_data {
@@ -31,6 +35,10 @@ struct c_can_pci_data {
        enum c_can_pci_reg_align reg_align;
        /* Set the frequency */
        unsigned int freq;
+       /* PCI bar number */
+       int bar;
+       /* Callback for reset */
+       void (*init)(const struct c_can_priv *priv, bool enable);
 };
 
 /*
@@ -39,30 +47,70 @@ struct c_can_pci_data {
  * registers can be aligned to a 16-bit boundary or 32-bit boundary etc.
  * Handle the same by providing a common read/write interface.
  */
-static u16 c_can_pci_read_reg_aligned_to_16bit(struct c_can_priv *priv,
+static u16 c_can_pci_read_reg_aligned_to_16bit(const struct c_can_priv *priv,
                                                enum reg index)
 {
        return readw(priv->base + priv->regs[index]);
 }
 
-static void c_can_pci_write_reg_aligned_to_16bit(struct c_can_priv *priv,
+static void c_can_pci_write_reg_aligned_to_16bit(const struct c_can_priv *priv,
                                                enum reg index, u16 val)
 {
        writew(val, priv->base + priv->regs[index]);
 }
 
-static u16 c_can_pci_read_reg_aligned_to_32bit(struct c_can_priv *priv,
+static u16 c_can_pci_read_reg_aligned_to_32bit(const struct c_can_priv *priv,
                                                enum reg index)
 {
        return readw(priv->base + 2 * priv->regs[index]);
 }
 
-static void c_can_pci_write_reg_aligned_to_32bit(struct c_can_priv *priv,
+static void c_can_pci_write_reg_aligned_to_32bit(const struct c_can_priv *priv,
                                                enum reg index, u16 val)
 {
        writew(val, priv->base + 2 * priv->regs[index]);
 }
 
+static u16 c_can_pci_read_reg_32bit(const struct c_can_priv *priv,
+                                   enum reg index)
+{
+       return (u16)ioread32(priv->base + 2 * priv->regs[index]);
+}
+
+static void c_can_pci_write_reg_32bit(const struct c_can_priv *priv,
+                                     enum reg index, u16 val)
+{
+       iowrite32((u32)val, priv->base + 2 * priv->regs[index]);
+}
+
+static u32 c_can_pci_read_reg32(const struct c_can_priv *priv, enum reg index)
+{
+       u32 val;
+
+       val = priv->read_reg(priv, index);
+       val |= ((u32) priv->read_reg(priv, index + 1)) << 16;
+
+       return val;
+}
+
+static void c_can_pci_write_reg32(const struct c_can_priv *priv, enum reg index,
+               u32 val)
+{
+       priv->write_reg(priv, index + 1, val >> 16);
+       priv->write_reg(priv, index, val);
+}
+
+static void c_can_pci_reset_pch(const struct c_can_priv *priv, bool enable)
+{
+       if (enable) {
+               u32 __iomem *addr = priv->base + PCH_PCI_SOFT_RESET;
+
+               /* write to sw reset register */
+               iowrite32(1, addr);
+               iowrite32(0, addr);
+       }
+}
+
 static int c_can_pci_probe(struct pci_dev *pdev,
                           const struct pci_device_id *ent)
 {
@@ -90,7 +138,8 @@ static int c_can_pci_probe(struct pci_dev *pdev,
                pci_set_master(pdev);
        }
 
-       addr = pci_iomap(pdev, 0, pci_resource_len(pdev, 0));
+       addr = pci_iomap(pdev, c_can_pci_data->bar,
+                        pci_resource_len(pdev, c_can_pci_data->bar));
        if (!addr) {
                dev_err(&pdev->dev,
                        "device has no PCI memory resources, "
@@ -147,10 +196,18 @@ static int c_can_pci_probe(struct pci_dev *pdev,
                priv->read_reg = c_can_pci_read_reg_aligned_to_16bit;
                priv->write_reg = c_can_pci_write_reg_aligned_to_16bit;
                break;
+       case C_CAN_REG_32:
+               priv->read_reg = c_can_pci_read_reg_32bit;
+               priv->write_reg = c_can_pci_write_reg_32bit;
+               break;
        default:
                ret = -EINVAL;
                goto out_free_c_can;
        }
+       priv->read_reg32 = c_can_pci_read_reg32;
+       priv->write_reg32 = c_can_pci_write_reg32;
+
+       priv->raminit = c_can_pci_data->init;
 
        ret = register_c_can_dev(dev);
        if (ret) {
@@ -198,6 +255,15 @@ static struct c_can_pci_data c_can_sta2x11= {
        .type = BOSCH_C_CAN,
        .reg_align = C_CAN_REG_ALIGN_32,
        .freq = 52000000, /* 52 Mhz */
+       .bar = 0,
+};
+
+static struct c_can_pci_data c_can_pch = {
+       .type = BOSCH_C_CAN,
+       .reg_align = C_CAN_REG_32,
+       .freq = 50000000, /* 50 MHz */
+       .init = c_can_pci_reset_pch,
+       .bar = 1,
 };
 
 #define C_CAN_ID(_vend, _dev, _driverdata) {           \
@@ -207,6 +273,8 @@ static struct c_can_pci_data c_can_sta2x11= {
 static DEFINE_PCI_DEVICE_TABLE(c_can_pci_tbl) = {
        C_CAN_ID(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_CAN,
                 c_can_sta2x11),
+       C_CAN_ID(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH_CAN,
+                c_can_pch),
        {},
 };
 static struct pci_driver c_can_pci_driver = {
index 1df0b322d1e461ee69c4e74a499f425cda23d7bf..824108cd9fd594a91c25b0b4a1d43d3341ad9a31 100644 (file)
@@ -40,6 +40,7 @@
 #define CAN_RAMINIT_START_MASK(i)      (0x001 << (i))
 #define CAN_RAMINIT_DONE_MASK(i)       (0x100 << (i))
 #define CAN_RAMINIT_ALL_MASK(i)                (0x101 << (i))
+#define DCAN_RAM_INIT_BIT              (1 << 3)
 static DEFINE_SPINLOCK(raminit_lock);
 /*
  * 16-bit c_can registers can be arranged differently in the memory
@@ -47,31 +48,31 @@ static DEFINE_SPINLOCK(raminit_lock);
  * registers can be aligned to a 16-bit boundary or 32-bit boundary etc.
  * Handle the same by providing a common read/write interface.
  */
-static u16 c_can_plat_read_reg_aligned_to_16bit(struct c_can_priv *priv,
+static u16 c_can_plat_read_reg_aligned_to_16bit(const struct c_can_priv *priv,
                                                enum reg index)
 {
        return readw(priv->base + priv->regs[index]);
 }
 
-static void c_can_plat_write_reg_aligned_to_16bit(struct c_can_priv *priv,
+static void c_can_plat_write_reg_aligned_to_16bit(const struct c_can_priv *priv,
                                                enum reg index, u16 val)
 {
        writew(val, priv->base + priv->regs[index]);
 }
 
-static u16 c_can_plat_read_reg_aligned_to_32bit(struct c_can_priv *priv,
+static u16 c_can_plat_read_reg_aligned_to_32bit(const struct c_can_priv *priv,
                                                enum reg index)
 {
        return readw(priv->base + 2 * priv->regs[index]);
 }
 
-static void c_can_plat_write_reg_aligned_to_32bit(struct c_can_priv *priv,
+static void c_can_plat_write_reg_aligned_to_32bit(const struct c_can_priv *priv,
                                                enum reg index, u16 val)
 {
        writew(val, priv->base + 2 * priv->regs[index]);
 }
 
-static void c_can_hw_raminit_wait(const struct c_can_priv *priv, u32 mask,
+static void c_can_hw_raminit_wait_ti(const struct c_can_priv *priv, u32 mask,
                                  u32 val)
 {
        /* We look only at the bits of our instance. */
@@ -80,7 +81,7 @@ static void c_can_hw_raminit_wait(const struct c_can_priv *priv, u32 mask,
                udelay(1);
 }
 
-static void c_can_hw_raminit(const struct c_can_priv *priv, bool enable)
+static void c_can_hw_raminit_ti(const struct c_can_priv *priv, bool enable)
 {
        u32 mask = CAN_RAMINIT_ALL_MASK(priv->instance);
        u32 ctrl;
@@ -96,18 +97,68 @@ static void c_can_hw_raminit(const struct c_can_priv *priv, bool enable)
        ctrl |= CAN_RAMINIT_DONE_MASK(priv->instance);
        writel(ctrl, priv->raminit_ctrlreg);
        ctrl &= ~CAN_RAMINIT_DONE_MASK(priv->instance);
-       c_can_hw_raminit_wait(priv, ctrl, mask);
+       c_can_hw_raminit_wait_ti(priv, ctrl, mask);
 
        if (enable) {
                /* Set start bit and wait for the done bit. */
                ctrl |= CAN_RAMINIT_START_MASK(priv->instance);
                writel(ctrl, priv->raminit_ctrlreg);
                ctrl |= CAN_RAMINIT_DONE_MASK(priv->instance);
-               c_can_hw_raminit_wait(priv, ctrl, mask);
+               c_can_hw_raminit_wait_ti(priv, ctrl, mask);
        }
        spin_unlock(&raminit_lock);
 }
 
+static u32 c_can_plat_read_reg32(const struct c_can_priv *priv, enum reg index)
+{
+       u32 val;
+
+       val = priv->read_reg(priv, index);
+       val |= ((u32) priv->read_reg(priv, index + 1)) << 16;
+
+       return val;
+}
+
+static void c_can_plat_write_reg32(const struct c_can_priv *priv, enum reg index,
+               u32 val)
+{
+       priv->write_reg(priv, index + 1, val >> 16);
+       priv->write_reg(priv, index, val);
+}
+
+static u32 d_can_plat_read_reg32(const struct c_can_priv *priv, enum reg index)
+{
+       return readl(priv->base + priv->regs[index]);
+}
+
+static void d_can_plat_write_reg32(const struct c_can_priv *priv, enum reg index,
+               u32 val)
+{
+       writel(val, priv->base + priv->regs[index]);
+}
+
+static void c_can_hw_raminit_wait(const struct c_can_priv *priv, u32 mask)
+{
+       while (priv->read_reg32(priv, C_CAN_FUNCTION_REG) & mask)
+               udelay(1);
+}
+
+static void c_can_hw_raminit(const struct c_can_priv *priv, bool enable)
+{
+       u32 ctrl;
+
+       ctrl = priv->read_reg32(priv, C_CAN_FUNCTION_REG);
+       ctrl &= ~DCAN_RAM_INIT_BIT;
+       priv->write_reg32(priv, C_CAN_FUNCTION_REG, ctrl);
+       c_can_hw_raminit_wait(priv, ctrl);
+
+       if (enable) {
+               ctrl |= DCAN_RAM_INIT_BIT;
+               priv->write_reg32(priv, C_CAN_FUNCTION_REG, ctrl);
+               c_can_hw_raminit_wait(priv, ctrl);
+       }
+}
+
 static struct platform_device_id c_can_id_table[] = {
        [BOSCH_C_CAN_PLATFORM] = {
                .name = KBUILD_MODNAME,
@@ -201,11 +252,15 @@ static int c_can_plat_probe(struct platform_device *pdev)
                case IORESOURCE_MEM_32BIT:
                        priv->read_reg = c_can_plat_read_reg_aligned_to_32bit;
                        priv->write_reg = c_can_plat_write_reg_aligned_to_32bit;
+                       priv->read_reg32 = c_can_plat_read_reg32;
+                       priv->write_reg32 = c_can_plat_write_reg32;
                        break;
                case IORESOURCE_MEM_16BIT:
                default:
                        priv->read_reg = c_can_plat_read_reg_aligned_to_16bit;
                        priv->write_reg = c_can_plat_write_reg_aligned_to_16bit;
+                       priv->read_reg32 = c_can_plat_read_reg32;
+                       priv->write_reg32 = c_can_plat_write_reg32;
                        break;
                }
                break;
@@ -214,6 +269,8 @@ static int c_can_plat_probe(struct platform_device *pdev)
                priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES;
                priv->read_reg = c_can_plat_read_reg_aligned_to_16bit;
                priv->write_reg = c_can_plat_write_reg_aligned_to_16bit;
+               priv->read_reg32 = d_can_plat_read_reg32;
+               priv->write_reg32 = d_can_plat_write_reg32;
 
                if (pdev->dev.of_node)
                        priv->instance = of_alias_get_id(pdev->dev.of_node, "d_can");
@@ -221,11 +278,20 @@ static int c_can_plat_probe(struct platform_device *pdev)
                        priv->instance = pdev->id;
 
                res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+               /* Not all D_CAN modules have a separate register for the D_CAN
+                * RAM initialization. Use default RAM init bit in D_CAN module
+                * if not specified in DT.
+                */
+               if (!res) {
+                       priv->raminit = c_can_hw_raminit;
+                       break;
+               }
+
                priv->raminit_ctrlreg = devm_ioremap_resource(&pdev->dev, res);
                if (IS_ERR(priv->raminit_ctrlreg) || priv->instance < 0)
                        dev_info(&pdev->dev, "control memory is not used for raminit\n");
                else
-                       priv->raminit = c_can_hw_raminit;
+                       priv->raminit = c_can_hw_raminit_ti;
                break;
        default:
                ret = -EINVAL;
index f19be5269e7be55ae92ccdb12f2274768aeb5ddf..81c711719490511718122ebc5ebf77bc6c883e6c 100644 (file)
@@ -1,5 +1,5 @@
 config CAN_MSCAN
-       depends on PPC || M68K
+       depends on PPC
        tristate "Support for Freescale MSCAN based chips"
        ---help---
          The Motorola Scalable Controller Area Network (MSCAN) definition
diff --git a/drivers/net/can/rcar_can.c b/drivers/net/can/rcar_can.c
new file mode 100644 (file)
index 0000000..5268d21
--- /dev/null
@@ -0,0 +1,876 @@
+/* Renesas R-Car CAN device driver
+ *
+ * Copyright (C) 2013 Cogent Embedded, Inc. <source@cogentembedded.com>
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ *
+ * 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/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/can/led.h>
+#include <linux/can/dev.h>
+#include <linux/clk.h>
+#include <linux/can/platform/rcar_can.h>
+
+#define RCAR_CAN_DRV_NAME      "rcar_can"
+
+/* Mailbox configuration:
+ * mailbox 60 - 63 - Rx FIFO mailboxes
+ * mailbox 56 - 59 - Tx FIFO mailboxes
+ * non-FIFO mailboxes are not used
+ */
+#define RCAR_CAN_N_MBX         64 /* Number of mailboxes in non-FIFO mode */
+#define RCAR_CAN_RX_FIFO_MBX   60 /* Mailbox - window to Rx FIFO */
+#define RCAR_CAN_TX_FIFO_MBX   56 /* Mailbox - window to Tx FIFO */
+#define RCAR_CAN_FIFO_DEPTH    4
+
+/* Mailbox registers structure */
+struct rcar_can_mbox_regs {
+       u32 id;         /* IDE and RTR bits, SID and EID */
+       u8 stub;        /* Not used */
+       u8 dlc;         /* Data Length Code - bits [0..3] */
+       u8 data[8];     /* Data Bytes */
+       u8 tsh;         /* Time Stamp Higher Byte */
+       u8 tsl;         /* Time Stamp Lower Byte */
+};
+
+struct rcar_can_regs {
+       struct rcar_can_mbox_regs mb[RCAR_CAN_N_MBX]; /* Mailbox registers */
+       u32 mkr_2_9[8]; /* Mask Registers 2-9 */
+       u32 fidcr[2];   /* FIFO Received ID Compare Register */
+       u32 mkivlr1;    /* Mask Invalid Register 1 */
+       u32 mier1;      /* Mailbox Interrupt Enable Register 1 */
+       u32 mkr_0_1[2]; /* Mask Registers 0-1 */
+       u32 mkivlr0;    /* Mask Invalid Register 0*/
+       u32 mier0;      /* Mailbox Interrupt Enable Register 0 */
+       u8 pad_440[0x3c0];
+       u8 mctl[64];    /* Message Control Registers */
+       u16 ctlr;       /* Control Register */
+       u16 str;        /* Status register */
+       u8 bcr[3];      /* Bit Configuration Register */
+       u8 clkr;        /* Clock Select Register */
+       u8 rfcr;        /* Receive FIFO Control Register */
+       u8 rfpcr;       /* Receive FIFO Pointer Control Register */
+       u8 tfcr;        /* Transmit FIFO Control Register */
+       u8 tfpcr;       /* Transmit FIFO Pointer Control Register */
+       u8 eier;        /* Error Interrupt Enable Register */
+       u8 eifr;        /* Error Interrupt Factor Judge Register */
+       u8 recr;        /* Receive Error Count Register */
+       u8 tecr;        /* Transmit Error Count Register */
+       u8 ecsr;        /* Error Code Store Register */
+       u8 cssr;        /* Channel Search Support Register */
+       u8 mssr;        /* Mailbox Search Status Register */
+       u8 msmr;        /* Mailbox Search Mode Register */
+       u16 tsr;        /* Time Stamp Register */
+       u8 afsr;        /* Acceptance Filter Support Register */
+       u8 pad_857;
+       u8 tcr;         /* Test Control Register */
+       u8 pad_859[7];
+       u8 ier;         /* Interrupt Enable Register */
+       u8 isr;         /* Interrupt Status Register */
+       u8 pad_862;
+       u8 mbsmr;       /* Mailbox Search Mask Register */
+};
+
+struct rcar_can_priv {
+       struct can_priv can;    /* Must be the first member! */
+       struct net_device *ndev;
+       struct napi_struct napi;
+       struct rcar_can_regs __iomem *regs;
+       struct clk *clk;
+       u8 tx_dlc[RCAR_CAN_FIFO_DEPTH];
+       u32 tx_head;
+       u32 tx_tail;
+       u8 clock_select;
+       u8 ier;
+};
+
+static const struct can_bittiming_const rcar_can_bittiming_const = {
+       .name = RCAR_CAN_DRV_NAME,
+       .tseg1_min = 4,
+       .tseg1_max = 16,
+       .tseg2_min = 2,
+       .tseg2_max = 8,
+       .sjw_max = 4,
+       .brp_min = 1,
+       .brp_max = 1024,
+       .brp_inc = 1,
+};
+
+/* Control Register bits */
+#define RCAR_CAN_CTLR_BOM      (3 << 11) /* Bus-Off Recovery Mode Bits */
+#define RCAR_CAN_CTLR_BOM_ENT  (1 << 11) /* Entry to halt mode */
+                                       /* at bus-off entry */
+#define RCAR_CAN_CTLR_SLPM     (1 << 10)
+#define RCAR_CAN_CTLR_CANM     (3 << 8) /* Operating Mode Select Bit */
+#define RCAR_CAN_CTLR_CANM_HALT        (1 << 9)
+#define RCAR_CAN_CTLR_CANM_RESET (1 << 8)
+#define RCAR_CAN_CTLR_CANM_FORCE_RESET (3 << 8)
+#define RCAR_CAN_CTLR_MLM      (1 << 3) /* Message Lost Mode Select */
+#define RCAR_CAN_CTLR_IDFM     (3 << 1) /* ID Format Mode Select Bits */
+#define RCAR_CAN_CTLR_IDFM_MIXED (1 << 2) /* Mixed ID mode */
+#define RCAR_CAN_CTLR_MBM      (1 << 0) /* Mailbox Mode select */
+
+/* Status Register bits */
+#define RCAR_CAN_STR_RSTST     (1 << 8) /* Reset Status Bit */
+
+/* FIFO Received ID Compare Registers 0 and 1 bits */
+#define RCAR_CAN_FIDCR_IDE     (1 << 31) /* ID Extension Bit */
+#define RCAR_CAN_FIDCR_RTR     (1 << 30) /* Remote Transmission Request Bit */
+
+/* Receive FIFO Control Register bits */
+#define RCAR_CAN_RFCR_RFEST    (1 << 7) /* Receive FIFO Empty Status Flag */
+#define RCAR_CAN_RFCR_RFE      (1 << 0) /* Receive FIFO Enable */
+
+/* Transmit FIFO Control Register bits */
+#define RCAR_CAN_TFCR_TFUST    (7 << 1) /* Transmit FIFO Unsent Message */
+                                       /* Number Status Bits */
+#define RCAR_CAN_TFCR_TFUST_SHIFT 1    /* Offset of Transmit FIFO Unsent */
+                                       /* Message Number Status Bits */
+#define RCAR_CAN_TFCR_TFE      (1 << 0) /* Transmit FIFO Enable */
+
+#define RCAR_CAN_N_RX_MKREGS1  2       /* Number of mask registers */
+                                       /* for Rx mailboxes 0-31 */
+#define RCAR_CAN_N_RX_MKREGS2  8
+
+/* Bit Configuration Register settings */
+#define RCAR_CAN_BCR_TSEG1(x)  (((x) & 0x0f) << 20)
+#define RCAR_CAN_BCR_BPR(x)    (((x) & 0x3ff) << 8)
+#define RCAR_CAN_BCR_SJW(x)    (((x) & 0x3) << 4)
+#define RCAR_CAN_BCR_TSEG2(x)  ((x) & 0x07)
+
+/* Mailbox and Mask Registers bits */
+#define RCAR_CAN_IDE           (1 << 31)
+#define RCAR_CAN_RTR           (1 << 30)
+#define RCAR_CAN_SID_SHIFT     18
+
+/* Mailbox Interrupt Enable Register 1 bits */
+#define RCAR_CAN_MIER1_RXFIE   (1 << 28) /* Receive  FIFO Interrupt Enable */
+#define RCAR_CAN_MIER1_TXFIE   (1 << 24) /* Transmit FIFO Interrupt Enable */
+
+/* Interrupt Enable Register bits */
+#define RCAR_CAN_IER_ERSIE     (1 << 5) /* Error (ERS) Interrupt Enable Bit */
+#define RCAR_CAN_IER_RXFIE     (1 << 4) /* Reception FIFO Interrupt */
+                                       /* Enable Bit */
+#define RCAR_CAN_IER_TXFIE     (1 << 3) /* Transmission FIFO Interrupt */
+                                       /* Enable Bit */
+/* Interrupt Status Register bits */
+#define RCAR_CAN_ISR_ERSF      (1 << 5) /* Error (ERS) Interrupt Status Bit */
+#define RCAR_CAN_ISR_RXFF      (1 << 4) /* Reception FIFO Interrupt */
+                                       /* Status Bit */
+#define RCAR_CAN_ISR_TXFF      (1 << 3) /* Transmission FIFO Interrupt */
+                                       /* Status Bit */
+
+/* Error Interrupt Enable Register bits */
+#define RCAR_CAN_EIER_BLIE     (1 << 7) /* Bus Lock Interrupt Enable */
+#define RCAR_CAN_EIER_OLIE     (1 << 6) /* Overload Frame Transmit */
+                                       /* Interrupt Enable */
+#define RCAR_CAN_EIER_ORIE     (1 << 5) /* Receive Overrun  Interrupt Enable */
+#define RCAR_CAN_EIER_BORIE    (1 << 4) /* Bus-Off Recovery Interrupt Enable */
+#define RCAR_CAN_EIER_BOEIE    (1 << 3) /* Bus-Off Entry Interrupt Enable */
+#define RCAR_CAN_EIER_EPIE     (1 << 2) /* Error Passive Interrupt Enable */
+#define RCAR_CAN_EIER_EWIE     (1 << 1) /* Error Warning Interrupt Enable */
+#define RCAR_CAN_EIER_BEIE     (1 << 0) /* Bus Error Interrupt Enable */
+
+/* Error Interrupt Factor Judge Register bits */
+#define RCAR_CAN_EIFR_BLIF     (1 << 7) /* Bus Lock Detect Flag */
+#define RCAR_CAN_EIFR_OLIF     (1 << 6) /* Overload Frame Transmission */
+                                        /* Detect Flag */
+#define RCAR_CAN_EIFR_ORIF     (1 << 5) /* Receive Overrun Detect Flag */
+#define RCAR_CAN_EIFR_BORIF    (1 << 4) /* Bus-Off Recovery Detect Flag */
+#define RCAR_CAN_EIFR_BOEIF    (1 << 3) /* Bus-Off Entry Detect Flag */
+#define RCAR_CAN_EIFR_EPIF     (1 << 2) /* Error Passive Detect Flag */
+#define RCAR_CAN_EIFR_EWIF     (1 << 1) /* Error Warning Detect Flag */
+#define RCAR_CAN_EIFR_BEIF     (1 << 0) /* Bus Error Detect Flag */
+
+/* Error Code Store Register bits */
+#define RCAR_CAN_ECSR_EDPM     (1 << 7) /* Error Display Mode Select Bit */
+#define RCAR_CAN_ECSR_ADEF     (1 << 6) /* ACK Delimiter Error Flag */
+#define RCAR_CAN_ECSR_BE0F     (1 << 5) /* Bit Error (dominant) Flag */
+#define RCAR_CAN_ECSR_BE1F     (1 << 4) /* Bit Error (recessive) Flag */
+#define RCAR_CAN_ECSR_CEF      (1 << 3) /* CRC Error Flag */
+#define RCAR_CAN_ECSR_AEF      (1 << 2) /* ACK Error Flag */
+#define RCAR_CAN_ECSR_FEF      (1 << 1) /* Form Error Flag */
+#define RCAR_CAN_ECSR_SEF      (1 << 0) /* Stuff Error Flag */
+
+#define RCAR_CAN_NAPI_WEIGHT   4
+#define MAX_STR_READS          0x100
+
+static void tx_failure_cleanup(struct net_device *ndev)
+{
+       int i;
+
+       for (i = 0; i < RCAR_CAN_FIFO_DEPTH; i++)
+               can_free_echo_skb(ndev, i);
+}
+
+static void rcar_can_error(struct net_device *ndev)
+{
+       struct rcar_can_priv *priv = netdev_priv(ndev);
+       struct net_device_stats *stats = &ndev->stats;
+       struct can_frame *cf;
+       struct sk_buff *skb;
+       u8 eifr, txerr = 0, rxerr = 0;
+
+       /* Propagate the error condition to the CAN stack */
+       skb = alloc_can_err_skb(ndev, &cf);
+
+       eifr = readb(&priv->regs->eifr);
+       if (eifr & (RCAR_CAN_EIFR_EWIF | RCAR_CAN_EIFR_EPIF)) {
+               txerr = readb(&priv->regs->tecr);
+               rxerr = readb(&priv->regs->recr);
+               if (skb) {
+                       cf->can_id |= CAN_ERR_CRTL;
+                       cf->data[6] = txerr;
+                       cf->data[7] = rxerr;
+               }
+       }
+       if (eifr & RCAR_CAN_EIFR_BEIF) {
+               int rx_errors = 0, tx_errors = 0;
+               u8 ecsr;
+
+               netdev_dbg(priv->ndev, "Bus error interrupt:\n");
+               if (skb) {
+                       cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT;
+                       cf->data[2] = CAN_ERR_PROT_UNSPEC;
+               }
+               ecsr = readb(&priv->regs->ecsr);
+               if (ecsr & RCAR_CAN_ECSR_ADEF) {
+                       netdev_dbg(priv->ndev, "ACK Delimiter Error\n");
+                       tx_errors++;
+                       writeb(~RCAR_CAN_ECSR_ADEF, &priv->regs->ecsr);
+                       if (skb)
+                               cf->data[3] |= CAN_ERR_PROT_LOC_ACK_DEL;
+               }
+               if (ecsr & RCAR_CAN_ECSR_BE0F) {
+                       netdev_dbg(priv->ndev, "Bit Error (dominant)\n");
+                       tx_errors++;
+                       writeb(~RCAR_CAN_ECSR_BE0F, &priv->regs->ecsr);
+                       if (skb)
+                               cf->data[2] |= CAN_ERR_PROT_BIT0;
+               }
+               if (ecsr & RCAR_CAN_ECSR_BE1F) {
+                       netdev_dbg(priv->ndev, "Bit Error (recessive)\n");
+                       tx_errors++;
+                       writeb(~RCAR_CAN_ECSR_BE1F, &priv->regs->ecsr);
+                       if (skb)
+                               cf->data[2] |= CAN_ERR_PROT_BIT1;
+               }
+               if (ecsr & RCAR_CAN_ECSR_CEF) {
+                       netdev_dbg(priv->ndev, "CRC Error\n");
+                       rx_errors++;
+                       writeb(~RCAR_CAN_ECSR_CEF, &priv->regs->ecsr);
+                       if (skb)
+                               cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
+               }
+               if (ecsr & RCAR_CAN_ECSR_AEF) {
+                       netdev_dbg(priv->ndev, "ACK Error\n");
+                       tx_errors++;
+                       writeb(~RCAR_CAN_ECSR_AEF, &priv->regs->ecsr);
+                       if (skb) {
+                               cf->can_id |= CAN_ERR_ACK;
+                               cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
+                       }
+               }
+               if (ecsr & RCAR_CAN_ECSR_FEF) {
+                       netdev_dbg(priv->ndev, "Form Error\n");
+                       rx_errors++;
+                       writeb(~RCAR_CAN_ECSR_FEF, &priv->regs->ecsr);
+                       if (skb)
+                               cf->data[2] |= CAN_ERR_PROT_FORM;
+               }
+               if (ecsr & RCAR_CAN_ECSR_SEF) {
+                       netdev_dbg(priv->ndev, "Stuff Error\n");
+                       rx_errors++;
+                       writeb(~RCAR_CAN_ECSR_SEF, &priv->regs->ecsr);
+                       if (skb)
+                               cf->data[2] |= CAN_ERR_PROT_STUFF;
+               }
+
+               priv->can.can_stats.bus_error++;
+               ndev->stats.rx_errors += rx_errors;
+               ndev->stats.tx_errors += tx_errors;
+               writeb(~RCAR_CAN_EIFR_BEIF, &priv->regs->eifr);
+       }
+       if (eifr & RCAR_CAN_EIFR_EWIF) {
+               netdev_dbg(priv->ndev, "Error warning interrupt\n");
+               priv->can.state = CAN_STATE_ERROR_WARNING;
+               priv->can.can_stats.error_warning++;
+               /* Clear interrupt condition */
+               writeb(~RCAR_CAN_EIFR_EWIF, &priv->regs->eifr);
+               if (skb)
+                       cf->data[1] = txerr > rxerr ? CAN_ERR_CRTL_TX_WARNING :
+                                             CAN_ERR_CRTL_RX_WARNING;
+       }
+       if (eifr & RCAR_CAN_EIFR_EPIF) {
+               netdev_dbg(priv->ndev, "Error passive interrupt\n");
+               priv->can.state = CAN_STATE_ERROR_PASSIVE;
+               priv->can.can_stats.error_passive++;
+               /* Clear interrupt condition */
+               writeb(~RCAR_CAN_EIFR_EPIF, &priv->regs->eifr);
+               if (skb)
+                       cf->data[1] = txerr > rxerr ? CAN_ERR_CRTL_TX_PASSIVE :
+                                             CAN_ERR_CRTL_RX_PASSIVE;
+       }
+       if (eifr & RCAR_CAN_EIFR_BOEIF) {
+               netdev_dbg(priv->ndev, "Bus-off entry interrupt\n");
+               tx_failure_cleanup(ndev);
+               priv->ier = RCAR_CAN_IER_ERSIE;
+               writeb(priv->ier, &priv->regs->ier);
+               priv->can.state = CAN_STATE_BUS_OFF;
+               /* Clear interrupt condition */
+               writeb(~RCAR_CAN_EIFR_BOEIF, &priv->regs->eifr);
+               can_bus_off(ndev);
+               if (skb)
+                       cf->can_id |= CAN_ERR_BUSOFF;
+       }
+       if (eifr & RCAR_CAN_EIFR_ORIF) {
+               netdev_dbg(priv->ndev, "Receive overrun error interrupt\n");
+               ndev->stats.rx_over_errors++;
+               ndev->stats.rx_errors++;
+               writeb(~RCAR_CAN_EIFR_ORIF, &priv->regs->eifr);
+               if (skb) {
+                       cf->can_id |= CAN_ERR_CRTL;
+                       cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+               }
+       }
+       if (eifr & RCAR_CAN_EIFR_OLIF) {
+               netdev_dbg(priv->ndev,
+                          "Overload Frame Transmission error interrupt\n");
+               ndev->stats.rx_over_errors++;
+               ndev->stats.rx_errors++;
+               writeb(~RCAR_CAN_EIFR_OLIF, &priv->regs->eifr);
+               if (skb) {
+                       cf->can_id |= CAN_ERR_PROT;
+                       cf->data[2] |= CAN_ERR_PROT_OVERLOAD;
+               }
+       }
+
+       if (skb) {
+               stats->rx_packets++;
+               stats->rx_bytes += cf->can_dlc;
+               netif_rx(skb);
+       }
+}
+
+static void rcar_can_tx_done(struct net_device *ndev)
+{
+       struct rcar_can_priv *priv = netdev_priv(ndev);
+       struct net_device_stats *stats = &ndev->stats;
+       u8 isr;
+
+       while (1) {
+               u8 unsent = readb(&priv->regs->tfcr);
+
+               unsent = (unsent & RCAR_CAN_TFCR_TFUST) >>
+                         RCAR_CAN_TFCR_TFUST_SHIFT;
+               if (priv->tx_head - priv->tx_tail <= unsent)
+                       break;
+               stats->tx_packets++;
+               stats->tx_bytes += priv->tx_dlc[priv->tx_tail %
+                                               RCAR_CAN_FIFO_DEPTH];
+               priv->tx_dlc[priv->tx_tail % RCAR_CAN_FIFO_DEPTH] = 0;
+               can_get_echo_skb(ndev, priv->tx_tail % RCAR_CAN_FIFO_DEPTH);
+               priv->tx_tail++;
+               netif_wake_queue(ndev);
+       }
+       /* Clear interrupt */
+       isr = readb(&priv->regs->isr);
+       writeb(isr & ~RCAR_CAN_ISR_TXFF, &priv->regs->isr);
+       can_led_event(ndev, CAN_LED_EVENT_TX);
+}
+
+static irqreturn_t rcar_can_interrupt(int irq, void *dev_id)
+{
+       struct net_device *ndev = dev_id;
+       struct rcar_can_priv *priv = netdev_priv(ndev);
+       u8 isr;
+
+       isr = readb(&priv->regs->isr);
+       if (!(isr & priv->ier))
+               return IRQ_NONE;
+
+       if (isr & RCAR_CAN_ISR_ERSF)
+               rcar_can_error(ndev);
+
+       if (isr & RCAR_CAN_ISR_TXFF)
+               rcar_can_tx_done(ndev);
+
+       if (isr & RCAR_CAN_ISR_RXFF) {
+               if (napi_schedule_prep(&priv->napi)) {
+                       /* Disable Rx FIFO interrupts */
+                       priv->ier &= ~RCAR_CAN_IER_RXFIE;
+                       writeb(priv->ier, &priv->regs->ier);
+                       __napi_schedule(&priv->napi);
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
+static void rcar_can_set_bittiming(struct net_device *dev)
+{
+       struct rcar_can_priv *priv = netdev_priv(dev);
+       struct can_bittiming *bt = &priv->can.bittiming;
+       u32 bcr;
+
+       bcr = RCAR_CAN_BCR_TSEG1(bt->phase_seg1 + bt->prop_seg - 1) |
+             RCAR_CAN_BCR_BPR(bt->brp - 1) | RCAR_CAN_BCR_SJW(bt->sjw - 1) |
+             RCAR_CAN_BCR_TSEG2(bt->phase_seg2 - 1);
+       /* Don't overwrite CLKR with 32-bit BCR access; CLKR has 8-bit access.
+        * All the registers are big-endian but they get byte-swapped on 32-bit
+        * read/write (but not on 8-bit, contrary to the manuals)...
+        */
+       writel((bcr << 8) | priv->clock_select, &priv->regs->bcr);
+}
+
+static void rcar_can_start(struct net_device *ndev)
+{
+       struct rcar_can_priv *priv = netdev_priv(ndev);
+       u16 ctlr;
+       int i;
+
+       /* Set controller to known mode:
+        * - FIFO mailbox mode
+        * - accept all messages
+        * - overrun mode
+        * CAN is in sleep mode after MCU hardware or software reset.
+        */
+       ctlr = readw(&priv->regs->ctlr);
+       ctlr &= ~RCAR_CAN_CTLR_SLPM;
+       writew(ctlr, &priv->regs->ctlr);
+       /* Go to reset mode */
+       ctlr |= RCAR_CAN_CTLR_CANM_FORCE_RESET;
+       writew(ctlr, &priv->regs->ctlr);
+       for (i = 0; i < MAX_STR_READS; i++) {
+               if (readw(&priv->regs->str) & RCAR_CAN_STR_RSTST)
+                       break;
+       }
+       rcar_can_set_bittiming(ndev);
+       ctlr |= RCAR_CAN_CTLR_IDFM_MIXED; /* Select mixed ID mode */
+       ctlr |= RCAR_CAN_CTLR_BOM_ENT;  /* Entry to halt mode automatically */
+                                       /* at bus-off */
+       ctlr |= RCAR_CAN_CTLR_MBM;      /* Select FIFO mailbox mode */
+       ctlr |= RCAR_CAN_CTLR_MLM;      /* Overrun mode */
+       writew(ctlr, &priv->regs->ctlr);
+
+       /* Accept all SID and EID */
+       writel(0, &priv->regs->mkr_2_9[6]);
+       writel(0, &priv->regs->mkr_2_9[7]);
+       /* In FIFO mailbox mode, write "0" to bits 24 to 31 */
+       writel(0, &priv->regs->mkivlr1);
+       /* Accept all frames */
+       writel(0, &priv->regs->fidcr[0]);
+       writel(RCAR_CAN_FIDCR_IDE | RCAR_CAN_FIDCR_RTR, &priv->regs->fidcr[1]);
+       /* Enable and configure FIFO mailbox interrupts */
+       writel(RCAR_CAN_MIER1_RXFIE | RCAR_CAN_MIER1_TXFIE, &priv->regs->mier1);
+
+       priv->ier = RCAR_CAN_IER_ERSIE | RCAR_CAN_IER_RXFIE |
+                   RCAR_CAN_IER_TXFIE;
+       writeb(priv->ier, &priv->regs->ier);
+
+       /* Accumulate error codes */
+       writeb(RCAR_CAN_ECSR_EDPM, &priv->regs->ecsr);
+       /* Enable error interrupts */
+       writeb(RCAR_CAN_EIER_EWIE | RCAR_CAN_EIER_EPIE | RCAR_CAN_EIER_BOEIE |
+              (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING ?
+              RCAR_CAN_EIER_BEIE : 0) | RCAR_CAN_EIER_ORIE |
+              RCAR_CAN_EIER_OLIE, &priv->regs->eier);
+       priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+       /* Go to operation mode */
+       writew(ctlr & ~RCAR_CAN_CTLR_CANM, &priv->regs->ctlr);
+       for (i = 0; i < MAX_STR_READS; i++) {
+               if (!(readw(&priv->regs->str) & RCAR_CAN_STR_RSTST))
+                       break;
+       }
+       /* Enable Rx and Tx FIFO */
+       writeb(RCAR_CAN_RFCR_RFE, &priv->regs->rfcr);
+       writeb(RCAR_CAN_TFCR_TFE, &priv->regs->tfcr);
+}
+
+static int rcar_can_open(struct net_device *ndev)
+{
+       struct rcar_can_priv *priv = netdev_priv(ndev);
+       int err;
+
+       err = clk_prepare_enable(priv->clk);
+       if (err) {
+               netdev_err(ndev, "clk_prepare_enable() failed, error %d\n",
+                          err);
+               goto out;
+       }
+       err = open_candev(ndev);
+       if (err) {
+               netdev_err(ndev, "open_candev() failed, error %d\n", err);
+               goto out_clock;
+       }
+       napi_enable(&priv->napi);
+       err = request_irq(ndev->irq, rcar_can_interrupt, 0, ndev->name, ndev);
+       if (err) {
+               netdev_err(ndev, "error requesting interrupt %x\n", ndev->irq);
+               goto out_close;
+       }
+       can_led_event(ndev, CAN_LED_EVENT_OPEN);
+       rcar_can_start(ndev);
+       netif_start_queue(ndev);
+       return 0;
+out_close:
+       napi_disable(&priv->napi);
+       close_candev(ndev);
+out_clock:
+       clk_disable_unprepare(priv->clk);
+out:
+       return err;
+}
+
+static void rcar_can_stop(struct net_device *ndev)
+{
+       struct rcar_can_priv *priv = netdev_priv(ndev);
+       u16 ctlr;
+       int i;
+
+       /* Go to (force) reset mode */
+       ctlr = readw(&priv->regs->ctlr);
+       ctlr |= RCAR_CAN_CTLR_CANM_FORCE_RESET;
+       writew(ctlr, &priv->regs->ctlr);
+       for (i = 0; i < MAX_STR_READS; i++) {
+               if (readw(&priv->regs->str) & RCAR_CAN_STR_RSTST)
+                       break;
+       }
+       writel(0, &priv->regs->mier0);
+       writel(0, &priv->regs->mier1);
+       writeb(0, &priv->regs->ier);
+       writeb(0, &priv->regs->eier);
+       /* Go to sleep mode */
+       ctlr |= RCAR_CAN_CTLR_SLPM;
+       writew(ctlr, &priv->regs->ctlr);
+       priv->can.state = CAN_STATE_STOPPED;
+}
+
+static int rcar_can_close(struct net_device *ndev)
+{
+       struct rcar_can_priv *priv = netdev_priv(ndev);
+
+       netif_stop_queue(ndev);
+       rcar_can_stop(ndev);
+       free_irq(ndev->irq, ndev);
+       napi_disable(&priv->napi);
+       clk_disable_unprepare(priv->clk);
+       close_candev(ndev);
+       can_led_event(ndev, CAN_LED_EVENT_STOP);
+       return 0;
+}
+
+static netdev_tx_t rcar_can_start_xmit(struct sk_buff *skb,
+                                      struct net_device *ndev)
+{
+       struct rcar_can_priv *priv = netdev_priv(ndev);
+       struct can_frame *cf = (struct can_frame *)skb->data;
+       u32 data, i;
+
+       if (can_dropped_invalid_skb(ndev, skb))
+               return NETDEV_TX_OK;
+
+       if (cf->can_id & CAN_EFF_FLAG)  /* Extended frame format */
+               data = (cf->can_id & CAN_EFF_MASK) | RCAR_CAN_IDE;
+       else                            /* Standard frame format */
+               data = (cf->can_id & CAN_SFF_MASK) << RCAR_CAN_SID_SHIFT;
+
+       if (cf->can_id & CAN_RTR_FLAG) { /* Remote transmission request */
+               data |= RCAR_CAN_RTR;
+       } else {
+               for (i = 0; i < cf->can_dlc; i++)
+                       writeb(cf->data[i],
+                              &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].data[i]);
+       }
+
+       writel(data, &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].id);
+
+       writeb(cf->can_dlc, &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].dlc);
+
+       priv->tx_dlc[priv->tx_head % RCAR_CAN_FIFO_DEPTH] = cf->can_dlc;
+       can_put_echo_skb(skb, ndev, priv->tx_head % RCAR_CAN_FIFO_DEPTH);
+       priv->tx_head++;
+       /* Start Tx: write 0xff to the TFPCR register to increment
+        * the CPU-side pointer for the transmit FIFO to the next
+        * mailbox location
+        */
+       writeb(0xff, &priv->regs->tfpcr);
+       /* Stop the queue if we've filled all FIFO entries */
+       if (priv->tx_head - priv->tx_tail >= RCAR_CAN_FIFO_DEPTH)
+               netif_stop_queue(ndev);
+
+       return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops rcar_can_netdev_ops = {
+       .ndo_open = rcar_can_open,
+       .ndo_stop = rcar_can_close,
+       .ndo_start_xmit = rcar_can_start_xmit,
+};
+
+static void rcar_can_rx_pkt(struct rcar_can_priv *priv)
+{
+       struct net_device_stats *stats = &priv->ndev->stats;
+       struct can_frame *cf;
+       struct sk_buff *skb;
+       u32 data;
+       u8 dlc;
+
+       skb = alloc_can_skb(priv->ndev, &cf);
+       if (!skb) {
+               stats->rx_dropped++;
+               return;
+       }
+
+       data = readl(&priv->regs->mb[RCAR_CAN_RX_FIFO_MBX].id);
+       if (data & RCAR_CAN_IDE)
+               cf->can_id = (data & CAN_EFF_MASK) | CAN_EFF_FLAG;
+       else
+               cf->can_id = (data >> RCAR_CAN_SID_SHIFT) & CAN_SFF_MASK;
+
+       dlc = readb(&priv->regs->mb[RCAR_CAN_RX_FIFO_MBX].dlc);
+       cf->can_dlc = get_can_dlc(dlc);
+       if (data & RCAR_CAN_RTR) {
+               cf->can_id |= CAN_RTR_FLAG;
+       } else {
+               for (dlc = 0; dlc < cf->can_dlc; dlc++)
+                       cf->data[dlc] =
+                       readb(&priv->regs->mb[RCAR_CAN_RX_FIFO_MBX].data[dlc]);
+       }
+
+       can_led_event(priv->ndev, CAN_LED_EVENT_RX);
+
+       stats->rx_bytes += cf->can_dlc;
+       stats->rx_packets++;
+       netif_receive_skb(skb);
+}
+
+static int rcar_can_rx_poll(struct napi_struct *napi, int quota)
+{
+       struct rcar_can_priv *priv = container_of(napi,
+                                                 struct rcar_can_priv, napi);
+       int num_pkts;
+
+       for (num_pkts = 0; num_pkts < quota; num_pkts++) {
+               u8 rfcr, isr;
+
+               isr = readb(&priv->regs->isr);
+               /* Clear interrupt bit */
+               if (isr & RCAR_CAN_ISR_RXFF)
+                       writeb(isr & ~RCAR_CAN_ISR_RXFF, &priv->regs->isr);
+               rfcr = readb(&priv->regs->rfcr);
+               if (rfcr & RCAR_CAN_RFCR_RFEST)
+                       break;
+               rcar_can_rx_pkt(priv);
+               /* Write 0xff to the RFPCR register to increment
+                * the CPU-side pointer for the receive FIFO
+                * to the next mailbox location
+                */
+               writeb(0xff, &priv->regs->rfpcr);
+       }
+       /* All packets processed */
+       if (num_pkts < quota) {
+               napi_complete(napi);
+               priv->ier |= RCAR_CAN_IER_RXFIE;
+               writeb(priv->ier, &priv->regs->ier);
+       }
+       return num_pkts;
+}
+
+static int rcar_can_do_set_mode(struct net_device *ndev, enum can_mode mode)
+{
+       switch (mode) {
+       case CAN_MODE_START:
+               rcar_can_start(ndev);
+               netif_wake_queue(ndev);
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int rcar_can_get_berr_counter(const struct net_device *dev,
+                                    struct can_berr_counter *bec)
+{
+       struct rcar_can_priv *priv = netdev_priv(dev);
+       int err;
+
+       err = clk_prepare_enable(priv->clk);
+       if (err)
+               return err;
+       bec->txerr = readb(&priv->regs->tecr);
+       bec->rxerr = readb(&priv->regs->recr);
+       clk_disable_unprepare(priv->clk);
+       return 0;
+}
+
+static int rcar_can_probe(struct platform_device *pdev)
+{
+       struct rcar_can_platform_data *pdata;
+       struct rcar_can_priv *priv;
+       struct net_device *ndev;
+       struct resource *mem;
+       void __iomem *addr;
+       int err = -ENODEV;
+       int irq;
+
+       pdata = dev_get_platdata(&pdev->dev);
+       if (!pdata) {
+               dev_err(&pdev->dev, "No platform data provided!\n");
+               goto fail;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (!irq) {
+               dev_err(&pdev->dev, "No IRQ resource\n");
+               goto fail;
+       }
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       addr = devm_ioremap_resource(&pdev->dev, mem);
+       if (IS_ERR(addr)) {
+               err = PTR_ERR(addr);
+               goto fail;
+       }
+
+       ndev = alloc_candev(sizeof(struct rcar_can_priv), RCAR_CAN_FIFO_DEPTH);
+       if (!ndev) {
+               dev_err(&pdev->dev, "alloc_candev() failed\n");
+               err = -ENOMEM;
+               goto fail;
+       }
+
+       priv = netdev_priv(ndev);
+
+       priv->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(priv->clk)) {
+               err = PTR_ERR(priv->clk);
+               dev_err(&pdev->dev, "cannot get clock: %d\n", err);
+               goto fail_clk;
+       }
+
+       ndev->netdev_ops = &rcar_can_netdev_ops;
+       ndev->irq = irq;
+       ndev->flags |= IFF_ECHO;
+       priv->ndev = ndev;
+       priv->regs = addr;
+       priv->clock_select = pdata->clock_select;
+       priv->can.clock.freq = clk_get_rate(priv->clk);
+       priv->can.bittiming_const = &rcar_can_bittiming_const;
+       priv->can.do_set_mode = rcar_can_do_set_mode;
+       priv->can.do_get_berr_counter = rcar_can_get_berr_counter;
+       priv->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING;
+       platform_set_drvdata(pdev, ndev);
+       SET_NETDEV_DEV(ndev, &pdev->dev);
+
+       netif_napi_add(ndev, &priv->napi, rcar_can_rx_poll,
+                      RCAR_CAN_NAPI_WEIGHT);
+       err = register_candev(ndev);
+       if (err) {
+               dev_err(&pdev->dev, "register_candev() failed, error %d\n",
+                       err);
+               goto fail_candev;
+       }
+
+       devm_can_led_init(ndev);
+
+       dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%u)\n",
+                priv->regs, ndev->irq);
+
+       return 0;
+fail_candev:
+       netif_napi_del(&priv->napi);
+fail_clk:
+       free_candev(ndev);
+fail:
+       return err;
+}
+
+static int rcar_can_remove(struct platform_device *pdev)
+{
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct rcar_can_priv *priv = netdev_priv(ndev);
+
+       unregister_candev(ndev);
+       netif_napi_del(&priv->napi);
+       free_candev(ndev);
+       return 0;
+}
+
+static int __maybe_unused rcar_can_suspend(struct device *dev)
+{
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct rcar_can_priv *priv = netdev_priv(ndev);
+       u16 ctlr;
+
+       if (netif_running(ndev)) {
+               netif_stop_queue(ndev);
+               netif_device_detach(ndev);
+       }
+       ctlr = readw(&priv->regs->ctlr);
+       ctlr |= RCAR_CAN_CTLR_CANM_HALT;
+       writew(ctlr, &priv->regs->ctlr);
+       ctlr |= RCAR_CAN_CTLR_SLPM;
+       writew(ctlr, &priv->regs->ctlr);
+       priv->can.state = CAN_STATE_SLEEPING;
+
+       clk_disable(priv->clk);
+       return 0;
+}
+
+static int __maybe_unused rcar_can_resume(struct device *dev)
+{
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct rcar_can_priv *priv = netdev_priv(ndev);
+       u16 ctlr;
+       int err;
+
+       err = clk_enable(priv->clk);
+       if (err) {
+               netdev_err(ndev, "clk_enable() failed, error %d\n", err);
+               return err;
+       }
+
+       ctlr = readw(&priv->regs->ctlr);
+       ctlr &= ~RCAR_CAN_CTLR_SLPM;
+       writew(ctlr, &priv->regs->ctlr);
+       ctlr &= ~RCAR_CAN_CTLR_CANM;
+       writew(ctlr, &priv->regs->ctlr);
+       priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+       if (netif_running(ndev)) {
+               netif_device_attach(ndev);
+               netif_start_queue(ndev);
+       }
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(rcar_can_pm_ops, rcar_can_suspend, rcar_can_resume);
+
+static struct platform_driver rcar_can_driver = {
+       .driver = {
+               .name = RCAR_CAN_DRV_NAME,
+               .owner = THIS_MODULE,
+               .pm = &rcar_can_pm_ops,
+       },
+       .probe = rcar_can_probe,
+       .remove = rcar_can_remove,
+};
+
+module_platform_driver(rcar_can_driver);
+
+MODULE_AUTHOR("Cogent Embedded, Inc.");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("CAN driver for Renesas R-Car SoC");
+MODULE_ALIAS("platform:" RCAR_CAN_DRV_NAME);
index 7d8c8f3672dd993119a28f478e5cc43514aa131c..bacd236ce3064d357271ff67e3ccacbfedddd953 100644 (file)
@@ -556,15 +556,6 @@ static int softing_card_boot(struct softing *card)
 /*
  * netdev sysfs
  */
-static ssize_t show_channel(struct device *dev, struct device_attribute *attr,
-               char *buf)
-{
-       struct net_device *ndev = to_net_dev(dev);
-       struct softing_priv *priv = netdev2softing(ndev);
-
-       return sprintf(buf, "%i\n", priv->index);
-}
-
 static ssize_t show_chip(struct device *dev, struct device_attribute *attr,
                char *buf)
 {
@@ -609,12 +600,10 @@ static ssize_t store_output(struct device *dev, struct device_attribute *attr,
        return count;
 }
 
-static const DEVICE_ATTR(channel, S_IRUGO, show_channel, NULL);
 static const DEVICE_ATTR(chip, S_IRUGO, show_chip, NULL);
 static const DEVICE_ATTR(output, S_IRUGO | S_IWUSR, show_output, store_output);
 
 static const struct attribute *const netdev_sysfs_attrs[] = {
-       &dev_attr_channel.attr,
        &dev_attr_chip.attr,
        &dev_attr_output.attr,
        NULL,
@@ -679,17 +668,20 @@ static int softing_netdev_register(struct net_device *netdev)
 {
        int ret;
 
-       netdev->sysfs_groups[0] = &netdev_sysfs_group;
        ret = register_candev(netdev);
        if (ret) {
                dev_alert(&netdev->dev, "register failed\n");
                return ret;
        }
+       if (sysfs_create_group(&netdev->dev.kobj, &netdev_sysfs_group) < 0)
+               netdev_alert(netdev, "sysfs group failed\n");
+
        return 0;
 }
 
 static void softing_netdev_cleanup(struct net_device *netdev)
 {
+       sysfs_remove_group(&netdev->dev.kobj, &netdev_sysfs_group);
        unregister_candev(netdev);
        free_candev(netdev);
 }
@@ -721,8 +713,6 @@ DEV_ATTR_RO(firmware_version, id.fw_version);
 DEV_ATTR_RO_STR(hardware, pdat->name);
 DEV_ATTR_RO(hardware_version, id.hw_version);
 DEV_ATTR_RO(license, id.license);
-DEV_ATTR_RO(frequency, id.freq);
-DEV_ATTR_RO(txpending, tx.pending);
 
 static struct attribute *softing_pdev_attrs[] = {
        &dev_attr_serial.attr,
@@ -731,8 +721,6 @@ static struct attribute *softing_pdev_attrs[] = {
        &dev_attr_hardware.attr,
        &dev_attr_hardware_version.attr,
        &dev_attr_license.attr,
-       &dev_attr_frequency.attr,
-       &dev_attr_txpending.attr,
        NULL,
 };
 
diff --git a/drivers/net/can/spi/Kconfig b/drivers/net/can/spi/Kconfig
new file mode 100644 (file)
index 0000000..148cae5
--- /dev/null
@@ -0,0 +1,10 @@
+menu "CAN SPI interfaces"
+       depends on SPI
+
+config CAN_MCP251X
+       tristate "Microchip MCP251x SPI CAN controllers"
+       depends on HAS_DMA
+       ---help---
+         Driver for the Microchip MCP251x SPI CAN controllers.
+
+endmenu
diff --git a/drivers/net/can/spi/Makefile b/drivers/net/can/spi/Makefile
new file mode 100644 (file)
index 0000000..90bcacf
--- /dev/null
@@ -0,0 +1,8 @@
+#
+#  Makefile for the Linux Controller Area Network SPI drivers.
+#
+
+
+obj-$(CONFIG_CAN_MCP251X)      += mcp251x.o
+
+ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
similarity index 96%
rename from drivers/net/can/mcp251x.c
rename to drivers/net/can/spi/mcp251x.c
index 28c11f81524524fe521eb0c24177fffa76323885..5df239e68812635e1d209f835e9a62dbe2fdf555 100644 (file)
 
 #define TX_ECHO_SKB_MAX        1
 
+#define MCP251X_OST_DELAY_MS   (5)
+
 #define DEVICE_NAME "mcp251x"
 
 static int mcp251x_enable_dma; /* Enable SPI DMA. Default: 0 (Off) */
@@ -624,50 +626,45 @@ static int mcp251x_setup(struct net_device *net, struct mcp251x_priv *priv,
 static int mcp251x_hw_reset(struct spi_device *spi)
 {
        struct mcp251x_priv *priv = spi_get_drvdata(spi);
+       u8 reg;
        int ret;
-       unsigned long timeout;
+
+       /* Wait for oscillator startup timer after power up */
+       mdelay(MCP251X_OST_DELAY_MS);
 
        priv->spi_tx_buf[0] = INSTRUCTION_RESET;
-       ret = spi_write(spi, priv->spi_tx_buf, 1);
-       if (ret) {
-               dev_err(&spi->dev, "reset failed: ret = %d\n", ret);
-               return -EIO;
-       }
+       ret = mcp251x_spi_trans(spi, 1);
+       if (ret)
+               return ret;
+
+       /* Wait for oscillator startup timer after reset */
+       mdelay(MCP251X_OST_DELAY_MS);
+       
+       reg = mcp251x_read_reg(spi, CANSTAT);
+       if ((reg & CANCTRL_REQOP_MASK) != CANCTRL_REQOP_CONF)
+               return -ENODEV;
 
-       /* Wait for reset to finish */
-       timeout = jiffies + HZ;
-       mdelay(10);
-       while ((mcp251x_read_reg(spi, CANSTAT) & CANCTRL_REQOP_MASK)
-              != CANCTRL_REQOP_CONF) {
-               schedule();
-               if (time_after(jiffies, timeout)) {
-                       dev_err(&spi->dev, "MCP251x didn't"
-                               " enter in conf mode after reset\n");
-                       return -EBUSY;
-               }
-       }
        return 0;
 }
 
 static int mcp251x_hw_probe(struct spi_device *spi)
 {
-       int st1, st2;
+       u8 ctrl;
+       int ret;
 
-       mcp251x_hw_reset(spi);
+       ret = mcp251x_hw_reset(spi);
+       if (ret)
+               return ret;
 
-       /*
-        * Please note that these are "magic values" based on after
-        * reset defaults taken from data sheet which allows us to see
-        * if we really have a chip on the bus (we avoid common all
-        * zeroes or all ones situations)
-        */
-       st1 = mcp251x_read_reg(spi, CANSTAT) & 0xEE;
-       st2 = mcp251x_read_reg(spi, CANCTRL) & 0x17;
+       ctrl = mcp251x_read_reg(spi, CANCTRL);
+
+       dev_dbg(&spi->dev, "CANCTRL 0x%02x\n", ctrl);
 
-       dev_dbg(&spi->dev, "CANSTAT 0x%02x CANCTRL 0x%02x\n", st1, st2);
+       /* Check for power up default value */
+       if ((ctrl & 0x17) != 0x07)
+               return -ENODEV;
 
-       /* Check for power up default values */
-       return (st1 == 0x80 && st2 == 0x07) ? 1 : 0;
+       return 0;
 }
 
 static int mcp251x_power_enable(struct regulator *reg, int enable)
@@ -776,7 +773,6 @@ static void mcp251x_restart_work_handler(struct work_struct *ws)
 
        mutex_lock(&priv->mcp_lock);
        if (priv->after_suspend) {
-               mdelay(10);
                mcp251x_hw_reset(spi);
                mcp251x_setup(net, priv, spi);
                if (priv->after_suspend & AFTER_SUSPEND_RESTART) {
@@ -955,7 +951,7 @@ static int mcp251x_open(struct net_device *net)
        priv->tx_len = 0;
 
        ret = request_threaded_irq(spi->irq, NULL, mcp251x_can_ist,
-                                  flags, DEVICE_NAME, priv);
+                                  flags | IRQF_ONESHOT, DEVICE_NAME, priv);
        if (ret) {
                dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq);
                mcp251x_power_enable(priv->transceiver, 0);
@@ -1032,8 +1028,8 @@ static int mcp251x_can_probe(struct spi_device *spi)
        struct mcp251x_platform_data *pdata = dev_get_platdata(&spi->dev);
        struct net_device *net;
        struct mcp251x_priv *priv;
-       int freq, ret = -ENODEV;
        struct clk *clk;
+       int freq, ret;
 
        clk = devm_clk_get(&spi->dev, NULL);
        if (IS_ERR(clk)) {
@@ -1076,6 +1072,18 @@ static int mcp251x_can_probe(struct spi_device *spi)
        priv->net = net;
        priv->clk = clk;
 
+       spi_set_drvdata(spi, priv);
+
+       /* Configure the SPI bus */
+       spi->bits_per_word = 8;
+       if (mcp251x_is_2510(spi))
+               spi->max_speed_hz = spi->max_speed_hz ? : 5 * 1000 * 1000;
+       else
+               spi->max_speed_hz = spi->max_speed_hz ? : 10 * 1000 * 1000;
+       ret = spi_setup(spi);
+       if (ret)
+               goto out_clk;
+
        priv->power = devm_regulator_get(&spi->dev, "vdd");
        priv->transceiver = devm_regulator_get(&spi->dev, "xceiver");
        if ((PTR_ERR(priv->power) == -EPROBE_DEFER) ||
@@ -1088,8 +1096,6 @@ static int mcp251x_can_probe(struct spi_device *spi)
        if (ret)
                goto out_clk;
 
-       spi_set_drvdata(spi, priv);
-
        priv->spi = spi;
        mutex_init(&priv->mcp_lock);
 
@@ -1134,20 +1140,11 @@ static int mcp251x_can_probe(struct spi_device *spi)
 
        SET_NETDEV_DEV(net, &spi->dev);
 
-       /* Configure the SPI bus */
-       spi->mode = spi->mode ? : SPI_MODE_0;
-       if (mcp251x_is_2510(spi))
-               spi->max_speed_hz = spi->max_speed_hz ? : 5 * 1000 * 1000;
-       else
-               spi->max_speed_hz = spi->max_speed_hz ? : 10 * 1000 * 1000;
-       spi->bits_per_word = 8;
-       spi_setup(spi);
-
        /* Here is OK to not lock the MCP, no one knows about it yet */
-       if (!mcp251x_hw_probe(spi)) {
-               ret = -ENODEV;
+       ret = mcp251x_hw_probe(spi);
+       if (ret)
                goto error_probe;
-       }
+
        mcp251x_hw_sleep(spi);
 
        ret = register_candev(net);
@@ -1156,7 +1153,7 @@ static int mcp251x_can_probe(struct spi_device *spi)
 
        devm_can_led_init(net);
 
-       return ret;
+       return 0;
 
 error_probe:
        if (mcp251x_enable_dma)
index fc96a3d83ebecde338cdc5fd1d7b12f5ee9fbc25..a77db919363c08baa2c76dbb35c60701ea2c68ff 100644 (file)
@@ -13,13 +13,21 @@ config CAN_ESD_USB2
           This driver supports the CAN-USB/2 interface
           from esd electronic system design gmbh (http://www.esd.eu).
 
+config CAN_GS_USB
+       tristate "Geschwister Schneider UG interfaces"
+       ---help---
+         This driver supports the Geschwister Schneider USB/CAN devices.
+         If unsure choose N,
+         choose Y for built in support,
+         M to compile as module (module will be named: gs_usb).
+
 config CAN_KVASER_USB
        tristate "Kvaser CAN/USB interface"
        ---help---
          This driver adds support for Kvaser CAN/USB devices like Kvaser
          Leaf Light.
 
-         The driver gives support for the following devices:
+         The driver provides support for the following devices:
            - Kvaser Leaf Light
            - Kvaser Leaf Professional HS
            - Kvaser Leaf SemiPro HS
@@ -36,6 +44,8 @@ config CAN_KVASER_USB
            - Kvaser Leaf Light "China"
            - Kvaser BlackBird SemiPro
            - Kvaser USBcan R
+           - Kvaser Leaf Light v2
+           - Kvaser Mini PCI Express HS
 
          If unsure, say N.
 
index becef460a91aeb28851e91752b67ddec84ef931c..7b9a393b1ac82a1caf4684a704d7121ad16dc3c9 100644 (file)
@@ -4,6 +4,7 @@
 
 obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
 obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o
+obj-$(CONFIG_CAN_GS_USB) += gs_usb.o
 obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
 obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
 obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o
diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c
new file mode 100644 (file)
index 0000000..04b0f84
--- /dev/null
@@ -0,0 +1,971 @@
+/* CAN driver for Geschwister Schneider USB/CAN devices.
+ *
+ * Copyright (C) 2013 Geschwister Schneider Technologie-,
+ * Entwicklungs- und Vertriebs UG (Haftungsbeschränkt).
+ *
+ * Many thanks to all socketcan devs!
+ *
+ * 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; version 2 of the License.
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/signal.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/usb.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+
+/* Device specific constants */
+#define USB_GSUSB_1_VENDOR_ID      0x1d50
+#define USB_GSUSB_1_PRODUCT_ID     0x606f
+
+#define GSUSB_ENDPOINT_IN          1
+#define GSUSB_ENDPOINT_OUT         2
+
+/* Device specific constants */
+enum gs_usb_breq {
+       GS_USB_BREQ_HOST_FORMAT = 0,
+       GS_USB_BREQ_BITTIMING,
+       GS_USB_BREQ_MODE,
+       GS_USB_BREQ_BERR,
+       GS_USB_BREQ_BT_CONST,
+       GS_USB_BREQ_DEVICE_CONFIG
+};
+
+enum gs_can_mode {
+       /* reset a channel. turns it off */
+       GS_CAN_MODE_RESET = 0,
+       /* starts a channel */
+       GS_CAN_MODE_START
+};
+
+enum gs_can_state {
+       GS_CAN_STATE_ERROR_ACTIVE = 0,
+       GS_CAN_STATE_ERROR_WARNING,
+       GS_CAN_STATE_ERROR_PASSIVE,
+       GS_CAN_STATE_BUS_OFF,
+       GS_CAN_STATE_STOPPED,
+       GS_CAN_STATE_SLEEPING
+};
+
+/* data types passed between host and device */
+struct gs_host_config {
+       u32 byte_order;
+} __packed;
+/* All data exchanged between host and device is exchanged in host byte order,
+ * thanks to the struct gs_host_config byte_order member, which is sent first
+ * to indicate the desired byte order.
+ */
+
+struct gs_device_config {
+       u8 reserved1;
+       u8 reserved2;
+       u8 reserved3;
+       u8 icount;
+       u32 sw_version;
+       u32 hw_version;
+} __packed;
+
+#define GS_CAN_MODE_NORMAL               0
+#define GS_CAN_MODE_LISTEN_ONLY          (1<<0)
+#define GS_CAN_MODE_LOOP_BACK            (1<<1)
+#define GS_CAN_MODE_TRIPLE_SAMPLE        (1<<2)
+#define GS_CAN_MODE_ONE_SHOT             (1<<3)
+
+struct gs_device_mode {
+       u32 mode;
+       u32 flags;
+} __packed;
+
+struct gs_device_state {
+       u32 state;
+       u32 rxerr;
+       u32 txerr;
+} __packed;
+
+struct gs_device_bittiming {
+       u32 prop_seg;
+       u32 phase_seg1;
+       u32 phase_seg2;
+       u32 sjw;
+       u32 brp;
+} __packed;
+
+#define GS_CAN_FEATURE_LISTEN_ONLY      (1<<0)
+#define GS_CAN_FEATURE_LOOP_BACK        (1<<1)
+#define GS_CAN_FEATURE_TRIPLE_SAMPLE    (1<<2)
+#define GS_CAN_FEATURE_ONE_SHOT         (1<<3)
+
+struct gs_device_bt_const {
+       u32 feature;
+       u32 fclk_can;
+       u32 tseg1_min;
+       u32 tseg1_max;
+       u32 tseg2_min;
+       u32 tseg2_max;
+       u32 sjw_max;
+       u32 brp_min;
+       u32 brp_max;
+       u32 brp_inc;
+} __packed;
+
+#define GS_CAN_FLAG_OVERFLOW 1
+
+struct gs_host_frame {
+       u32 echo_id;
+       u32 can_id;
+
+       u8 can_dlc;
+       u8 channel;
+       u8 flags;
+       u8 reserved;
+
+       u8 data[8];
+} __packed;
+/* The GS USB devices make use of the same flags and masks as in
+ * linux/can.h and linux/can/error.h, and no additional mapping is necessary.
+ */
+
+/* Only send a max of GS_MAX_TX_URBS frames per channel at a time. */
+#define GS_MAX_TX_URBS 10
+/* Only launch a max of GS_MAX_RX_URBS usb requests at a time. */
+#define GS_MAX_RX_URBS 30
+/* Maximum number of interfaces the driver supports per device.
+ * Current hardware only supports 2 interfaces. The future may vary.
+ */
+#define GS_MAX_INTF 2
+
+struct gs_tx_context {
+       struct gs_can *dev;
+       unsigned int echo_id;
+};
+
+struct gs_can {
+       struct can_priv can; /* must be the first member */
+
+       struct gs_usb *parent;
+
+       struct net_device *netdev;
+       struct usb_device *udev;
+       struct usb_interface *iface;
+
+       struct can_bittiming_const bt_const;
+       unsigned int channel;   /* channel number */
+
+       /* This lock prevents a race condition between xmit and recieve. */
+       spinlock_t tx_ctx_lock;
+       struct gs_tx_context tx_context[GS_MAX_TX_URBS];
+
+       struct usb_anchor tx_submitted;
+       atomic_t active_tx_urbs;
+};
+
+/* usb interface struct */
+struct gs_usb {
+       struct gs_can *canch[GS_MAX_INTF];
+       struct usb_anchor rx_submitted;
+       atomic_t active_channels;
+       struct usb_device *udev;
+};
+
+/* 'allocate' a tx context.
+ * returns a valid tx context or NULL if there is no space.
+ */
+static struct gs_tx_context *gs_alloc_tx_context(struct gs_can *dev)
+{
+       int i = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->tx_ctx_lock, flags);
+
+       for (; i < GS_MAX_TX_URBS; i++) {
+               if (dev->tx_context[i].echo_id == GS_MAX_TX_URBS) {
+                       dev->tx_context[i].echo_id = i;
+                       spin_unlock_irqrestore(&dev->tx_ctx_lock, flags);
+                       return &dev->tx_context[i];
+               }
+       }
+
+       spin_unlock_irqrestore(&dev->tx_ctx_lock, flags);
+       return NULL;
+}
+
+/* releases a tx context
+ */
+static void gs_free_tx_context(struct gs_tx_context *txc)
+{
+       txc->echo_id = GS_MAX_TX_URBS;
+}
+
+/* Get a tx context by id.
+ */
+static struct gs_tx_context *gs_get_tx_context(struct gs_can *dev, unsigned int id)
+{
+       unsigned long flags;
+
+       if (id < GS_MAX_TX_URBS) {
+               spin_lock_irqsave(&dev->tx_ctx_lock, flags);
+               if (dev->tx_context[id].echo_id == id) {
+                       spin_unlock_irqrestore(&dev->tx_ctx_lock, flags);
+                       return &dev->tx_context[id];
+               }
+               spin_unlock_irqrestore(&dev->tx_ctx_lock, flags);
+       }
+       return NULL;
+}
+
+static int gs_cmd_reset(struct gs_usb *gsusb, struct gs_can *gsdev)
+{
+       struct gs_device_mode *dm;
+       struct usb_interface *intf = gsdev->iface;
+       int rc;
+
+       dm = kzalloc(sizeof(*dm), GFP_KERNEL);
+       if (!dm)
+               return -ENOMEM;
+
+       dm->mode = GS_CAN_MODE_RESET;
+
+       rc = usb_control_msg(interface_to_usbdev(intf),
+                            usb_sndctrlpipe(interface_to_usbdev(intf), 0),
+                            GS_USB_BREQ_MODE,
+                            USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
+                            gsdev->channel,
+                            0,
+                            dm,
+                            sizeof(*dm),
+                            1000);
+
+       return rc;
+}
+
+static void gs_update_state(struct gs_can *dev, struct can_frame *cf)
+{
+       struct can_device_stats *can_stats = &dev->can.can_stats;
+
+       if (cf->can_id & CAN_ERR_RESTARTED) {
+               dev->can.state = CAN_STATE_ERROR_ACTIVE;
+               can_stats->restarts++;
+       } else if (cf->can_id & CAN_ERR_BUSOFF) {
+               dev->can.state = CAN_STATE_BUS_OFF;
+               can_stats->bus_off++;
+       } else if (cf->can_id & CAN_ERR_CRTL) {
+               if ((cf->data[1] & CAN_ERR_CRTL_TX_WARNING) ||
+                   (cf->data[1] & CAN_ERR_CRTL_RX_WARNING)) {
+                       dev->can.state = CAN_STATE_ERROR_WARNING;
+                       can_stats->error_warning++;
+               } else if ((cf->data[1] & CAN_ERR_CRTL_TX_PASSIVE) ||
+                          (cf->data[1] & CAN_ERR_CRTL_RX_PASSIVE)) {
+                       dev->can.state = CAN_STATE_ERROR_PASSIVE;
+                       can_stats->error_passive++;
+               } else {
+                       dev->can.state = CAN_STATE_ERROR_ACTIVE;
+               }
+       }
+}
+
+static void gs_usb_recieve_bulk_callback(struct urb *urb)
+{
+       struct gs_usb *usbcan = urb->context;
+       struct gs_can *dev;
+       struct net_device *netdev;
+       int rc;
+       struct net_device_stats *stats;
+       struct gs_host_frame *hf = urb->transfer_buffer;
+       struct gs_tx_context *txc;
+       struct can_frame *cf;
+       struct sk_buff *skb;
+
+       BUG_ON(!usbcan);
+
+       switch (urb->status) {
+       case 0: /* success */
+               break;
+       case -ENOENT:
+       case -ESHUTDOWN:
+               return;
+       default:
+               /* do not resubmit aborted urbs. eg: when device goes down */
+               return;
+       }
+
+       /* device reports out of range channel id */
+       if (hf->channel >= GS_MAX_INTF)
+               goto resubmit_urb;
+
+       dev = usbcan->canch[hf->channel];
+
+       netdev = dev->netdev;
+       stats = &netdev->stats;
+
+       if (!netif_device_present(netdev))
+               return;
+
+       if (hf->echo_id == -1) { /* normal rx */
+               skb = alloc_can_skb(dev->netdev, &cf);
+               if (!skb)
+                       return;
+
+               cf->can_id = hf->can_id;
+
+               cf->can_dlc = get_can_dlc(hf->can_dlc);
+               memcpy(cf->data, hf->data, 8);
+
+               /* ERROR frames tell us information about the controller */
+               if (hf->can_id & CAN_ERR_FLAG)
+                       gs_update_state(dev, cf);
+
+               netdev->stats.rx_packets++;
+               netdev->stats.rx_bytes += hf->can_dlc;
+
+               netif_rx(skb);
+       } else { /* echo_id == hf->echo_id */
+               if (hf->echo_id >= GS_MAX_TX_URBS) {
+                       netdev_err(netdev,
+                                  "Unexpected out of range echo id %d\n",
+                                  hf->echo_id);
+                       goto resubmit_urb;
+               }
+
+               netdev->stats.tx_packets++;
+               netdev->stats.tx_bytes += hf->can_dlc;
+
+               txc = gs_get_tx_context(dev, hf->echo_id);
+
+               /* bad devices send bad echo_ids. */
+               if (!txc) {
+                       netdev_err(netdev,
+                                  "Unexpected unused echo id %d\n",
+                                  hf->echo_id);
+                       goto resubmit_urb;
+               }
+
+               can_get_echo_skb(netdev, hf->echo_id);
+
+               gs_free_tx_context(txc);
+
+               netif_wake_queue(netdev);
+       }
+
+       if (hf->flags & GS_CAN_FLAG_OVERFLOW) {
+               skb = alloc_can_err_skb(netdev, &cf);
+               if (!skb)
+                       goto resubmit_urb;
+
+               cf->can_id |= CAN_ERR_CRTL;
+               cf->can_dlc = CAN_ERR_DLC;
+               cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+               stats->rx_over_errors++;
+               stats->rx_errors++;
+               netif_rx(skb);
+       }
+
+ resubmit_urb:
+       usb_fill_bulk_urb(urb,
+                         usbcan->udev,
+                         usb_rcvbulkpipe(usbcan->udev, GSUSB_ENDPOINT_IN),
+                         hf,
+                         sizeof(struct gs_host_frame),
+                         gs_usb_recieve_bulk_callback,
+                         usbcan
+                         );
+
+       rc = usb_submit_urb(urb, GFP_ATOMIC);
+
+       /* USB failure take down all interfaces */
+       if (rc == -ENODEV) {
+               for (rc = 0; rc < GS_MAX_INTF; rc++) {
+                       if (usbcan->canch[rc])
+                               netif_device_detach(usbcan->canch[rc]->netdev);
+               }
+       }
+}
+
+static int gs_usb_set_bittiming(struct net_device *netdev)
+{
+       struct gs_can *dev = netdev_priv(netdev);
+       struct can_bittiming *bt = &dev->can.bittiming;
+       struct usb_interface *intf = dev->iface;
+       int rc;
+       struct gs_device_bittiming *dbt;
+
+       dbt = kmalloc(sizeof(*dbt), GFP_KERNEL);
+       if (!dbt)
+               return -ENOMEM;
+
+       dbt->prop_seg = bt->prop_seg;
+       dbt->phase_seg1 = bt->phase_seg1;
+       dbt->phase_seg2 = bt->phase_seg2;
+       dbt->sjw = bt->sjw;
+       dbt->brp = bt->brp;
+
+       /* request bit timings */
+       rc = usb_control_msg(interface_to_usbdev(intf),
+                            usb_sndctrlpipe(interface_to_usbdev(intf), 0),
+                            GS_USB_BREQ_BITTIMING,
+                            USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
+                            dev->channel,
+                            0,
+                            dbt,
+                            sizeof(*dbt),
+                            1000);
+
+       kfree(dbt);
+
+       if (rc < 0)
+               dev_err(netdev->dev.parent, "Couldn't set bittimings (err=%d)",
+                       rc);
+
+       return rc;
+}
+
+static void gs_usb_xmit_callback(struct urb *urb)
+{
+       struct gs_tx_context *txc = urb->context;
+       struct gs_can *dev = txc->dev;
+       struct net_device *netdev = dev->netdev;
+
+       if (urb->status)
+               netdev_info(netdev, "usb xmit fail %d\n", txc->echo_id);
+
+       usb_free_coherent(urb->dev,
+                         urb->transfer_buffer_length,
+                         urb->transfer_buffer,
+                         urb->transfer_dma);
+
+       atomic_dec(&dev->active_tx_urbs);
+
+       if (!netif_device_present(netdev))
+               return;
+
+       if (netif_queue_stopped(netdev))
+               netif_wake_queue(netdev);
+}
+
+static netdev_tx_t gs_can_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+       struct gs_can *dev = netdev_priv(netdev);
+       struct net_device_stats *stats = &dev->netdev->stats;
+       struct urb *urb;
+       struct gs_host_frame *hf;
+       struct can_frame *cf;
+       int rc;
+       unsigned int idx;
+       struct gs_tx_context *txc;
+
+       if (can_dropped_invalid_skb(netdev, skb))
+               return NETDEV_TX_OK;
+
+       /* find an empty context to keep track of transmission */
+       txc = gs_alloc_tx_context(dev);
+       if (!txc)
+               return NETDEV_TX_BUSY;
+
+       /* create a URB, and a buffer for it */
+       urb = usb_alloc_urb(0, GFP_ATOMIC);
+       if (!urb) {
+               netdev_err(netdev, "No memory left for URB\n");
+               goto nomem_urb;
+       }
+
+       hf = usb_alloc_coherent(dev->udev, sizeof(*hf), GFP_ATOMIC,
+                               &urb->transfer_dma);
+       if (!hf) {
+               netdev_err(netdev, "No memory left for USB buffer\n");
+               goto nomem_hf;
+       }
+
+       idx = txc->echo_id;
+
+       if (idx >= GS_MAX_TX_URBS) {
+               netdev_err(netdev, "Invalid tx context %d\n", idx);
+               goto badidx;
+       }
+
+       hf->echo_id = idx;
+       hf->channel = dev->channel;
+
+       cf = (struct can_frame *)skb->data;
+
+       hf->can_id = cf->can_id;
+       hf->can_dlc = cf->can_dlc;
+       memcpy(hf->data, cf->data, cf->can_dlc);
+
+       usb_fill_bulk_urb(urb, dev->udev,
+                         usb_sndbulkpipe(dev->udev, GSUSB_ENDPOINT_OUT),
+                         hf,
+                         sizeof(*hf),
+                         gs_usb_xmit_callback,
+                         txc);
+
+       urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+       usb_anchor_urb(urb, &dev->tx_submitted);
+
+       can_put_echo_skb(skb, netdev, idx);
+
+       atomic_inc(&dev->active_tx_urbs);
+
+       rc = usb_submit_urb(urb, GFP_ATOMIC);
+       if (unlikely(rc)) {                     /* usb send failed */
+               atomic_dec(&dev->active_tx_urbs);
+
+               can_free_echo_skb(netdev, idx);
+               gs_free_tx_context(txc);
+
+               usb_unanchor_urb(urb);
+               usb_free_coherent(dev->udev,
+                                 sizeof(*hf),
+                                 hf,
+                                 urb->transfer_dma);
+
+
+               if (rc == -ENODEV) {
+                       netif_device_detach(netdev);
+               } else {
+                       netdev_err(netdev, "usb_submit failed (err=%d)\n", rc);
+                       stats->tx_dropped++;
+               }
+       } else {
+               /* Slow down tx path */
+               if (atomic_read(&dev->active_tx_urbs) >= GS_MAX_TX_URBS)
+                       netif_stop_queue(netdev);
+       }
+
+       /* let usb core take care of this urb */
+       usb_free_urb(urb);
+
+       return NETDEV_TX_OK;
+
+ badidx:
+       usb_free_coherent(dev->udev,
+                         sizeof(*hf),
+                         hf,
+                         urb->transfer_dma);
+ nomem_hf:
+       usb_free_urb(urb);
+
+ nomem_urb:
+       gs_free_tx_context(txc);
+       dev_kfree_skb(skb);
+       stats->tx_dropped++;
+       return NETDEV_TX_OK;
+}
+
+static int gs_can_open(struct net_device *netdev)
+{
+       struct gs_can *dev = netdev_priv(netdev);
+       struct gs_usb *parent = dev->parent;
+       int rc, i;
+       struct gs_device_mode *dm;
+       u32 ctrlmode;
+
+       rc = open_candev(netdev);
+       if (rc)
+               return rc;
+
+       if (atomic_add_return(1, &parent->active_channels) == 1) {
+               for (i = 0; i < GS_MAX_RX_URBS; i++) {
+                       struct urb *urb;
+                       u8 *buf;
+
+                       /* alloc rx urb */
+                       urb = usb_alloc_urb(0, GFP_KERNEL);
+                       if (!urb) {
+                               netdev_err(netdev,
+                                          "No memory left for URB\n");
+                               return -ENOMEM;
+                       }
+
+                       /* alloc rx buffer */
+                       buf = usb_alloc_coherent(dev->udev,
+                                                sizeof(struct gs_host_frame),
+                                                GFP_KERNEL,
+                                                &urb->transfer_dma);
+                       if (!buf) {
+                               netdev_err(netdev,
+                                          "No memory left for USB buffer\n");
+                               usb_free_urb(urb);
+                               return -ENOMEM;
+                       }
+
+                       /* fill, anchor, and submit rx urb */
+                       usb_fill_bulk_urb(urb,
+                                         dev->udev,
+                                         usb_rcvbulkpipe(dev->udev,
+                                                         GSUSB_ENDPOINT_IN),
+                                         buf,
+                                         sizeof(struct gs_host_frame),
+                                         gs_usb_recieve_bulk_callback,
+                                         parent);
+                       urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+                       usb_anchor_urb(urb, &parent->rx_submitted);
+
+                       rc = usb_submit_urb(urb, GFP_KERNEL);
+                       if (rc) {
+                               if (rc == -ENODEV)
+                                       netif_device_detach(dev->netdev);
+
+                               netdev_err(netdev,
+                                          "usb_submit failed (err=%d)\n",
+                                          rc);
+
+                               usb_unanchor_urb(urb);
+                               break;
+                       }
+
+                       /* Drop reference,
+                        * USB core will take care of freeing it
+                        */
+                       usb_free_urb(urb);
+               }
+       }
+
+       dm = kmalloc(sizeof(*dm), GFP_KERNEL);
+       if (!dm)
+               return -ENOMEM;
+
+       /* flags */
+       ctrlmode = dev->can.ctrlmode;
+       dm->flags = 0;
+
+       if (ctrlmode & CAN_CTRLMODE_LOOPBACK)
+               dm->flags |= GS_CAN_MODE_LOOP_BACK;
+       else if (ctrlmode & CAN_CTRLMODE_LISTENONLY)
+               dm->flags |= GS_CAN_MODE_LISTEN_ONLY;
+
+       /* Controller is not allowed to retry TX
+        * this mode is unavailable on atmels uc3c hardware
+        */
+       if (ctrlmode & CAN_CTRLMODE_ONE_SHOT)
+               dm->flags |= GS_CAN_MODE_ONE_SHOT;
+
+       if (ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+               dm->flags |= GS_CAN_MODE_TRIPLE_SAMPLE;
+
+       /* finally start device */
+       dm->mode = GS_CAN_MODE_START;
+       rc = usb_control_msg(interface_to_usbdev(dev->iface),
+                            usb_sndctrlpipe(interface_to_usbdev(dev->iface), 0),
+                            GS_USB_BREQ_MODE,
+                            USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
+                            dev->channel,
+                            0,
+                            dm,
+                            sizeof(*dm),
+                            1000);
+
+       if (rc < 0) {
+               netdev_err(netdev, "Couldn't start device (err=%d)\n", rc);
+               kfree(dm);
+               return rc;
+       }
+
+       kfree(dm);
+
+       dev->can.state = CAN_STATE_ERROR_ACTIVE;
+
+       if (!(dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY))
+               netif_start_queue(netdev);
+
+       return 0;
+}
+
+static int gs_can_close(struct net_device *netdev)
+{
+       int rc;
+       struct gs_can *dev = netdev_priv(netdev);
+       struct gs_usb *parent = dev->parent;
+
+       netif_stop_queue(netdev);
+
+       /* Stop polling */
+       if (atomic_dec_and_test(&parent->active_channels))
+               usb_kill_anchored_urbs(&parent->rx_submitted);
+
+       /* Stop sending URBs */
+       usb_kill_anchored_urbs(&dev->tx_submitted);
+       atomic_set(&dev->active_tx_urbs, 0);
+
+       /* reset the device */
+       rc = gs_cmd_reset(parent, dev);
+       if (rc < 0)
+               netdev_warn(netdev, "Couldn't shutdown device (err=%d)", rc);
+
+       /* reset tx contexts */
+       for (rc = 0; rc < GS_MAX_TX_URBS; rc++) {
+               dev->tx_context[rc].dev = dev;
+               dev->tx_context[rc].echo_id = GS_MAX_TX_URBS;
+       }
+
+       /* close the netdev */
+       close_candev(netdev);
+
+       return 0;
+}
+
+static const struct net_device_ops gs_usb_netdev_ops = {
+       .ndo_open = gs_can_open,
+       .ndo_stop = gs_can_close,
+       .ndo_start_xmit = gs_can_start_xmit,
+};
+
+static struct gs_can *gs_make_candev(unsigned int channel, struct usb_interface *intf)
+{
+       struct gs_can *dev;
+       struct net_device *netdev;
+       int rc;
+       struct gs_device_bt_const *bt_const;
+
+       bt_const = kmalloc(sizeof(*bt_const), GFP_KERNEL);
+       if (!bt_const)
+               return ERR_PTR(-ENOMEM);
+
+       /* fetch bit timing constants */
+       rc = usb_control_msg(interface_to_usbdev(intf),
+                            usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
+                            GS_USB_BREQ_BT_CONST,
+                            USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
+                            channel,
+                            0,
+                            bt_const,
+                            sizeof(*bt_const),
+                            1000);
+
+       if (rc < 0) {
+               dev_err(&intf->dev,
+                       "Couldn't get bit timing const for channel (err=%d)\n",
+                       rc);
+               kfree(bt_const);
+               return ERR_PTR(rc);
+       }
+
+       /* create netdev */
+       netdev = alloc_candev(sizeof(struct gs_can), GS_MAX_TX_URBS);
+       if (!netdev) {
+               dev_err(&intf->dev, "Couldn't allocate candev\n");
+               kfree(bt_const);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       dev = netdev_priv(netdev);
+
+       netdev->netdev_ops = &gs_usb_netdev_ops;
+
+       netdev->flags |= IFF_ECHO; /* we support full roundtrip echo */
+
+       /* dev settup */
+       strcpy(dev->bt_const.name, "gs_usb");
+       dev->bt_const.tseg1_min = bt_const->tseg1_min;
+       dev->bt_const.tseg1_max = bt_const->tseg1_max;
+       dev->bt_const.tseg2_min = bt_const->tseg2_min;
+       dev->bt_const.tseg2_max = bt_const->tseg2_max;
+       dev->bt_const.sjw_max = bt_const->sjw_max;
+       dev->bt_const.brp_min = bt_const->brp_min;
+       dev->bt_const.brp_max = bt_const->brp_max;
+       dev->bt_const.brp_inc = bt_const->brp_inc;
+
+       dev->udev = interface_to_usbdev(intf);
+       dev->iface = intf;
+       dev->netdev = netdev;
+       dev->channel = channel;
+
+       init_usb_anchor(&dev->tx_submitted);
+       atomic_set(&dev->active_tx_urbs, 0);
+       spin_lock_init(&dev->tx_ctx_lock);
+       for (rc = 0; rc < GS_MAX_TX_URBS; rc++) {
+               dev->tx_context[rc].dev = dev;
+               dev->tx_context[rc].echo_id = GS_MAX_TX_URBS;
+       }
+
+       /* can settup */
+       dev->can.state = CAN_STATE_STOPPED;
+       dev->can.clock.freq = bt_const->fclk_can;
+       dev->can.bittiming_const = &dev->bt_const;
+       dev->can.do_set_bittiming = gs_usb_set_bittiming;
+
+       dev->can.ctrlmode_supported = 0;
+
+       if (bt_const->feature & GS_CAN_FEATURE_LISTEN_ONLY)
+               dev->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
+
+       if (bt_const->feature & GS_CAN_FEATURE_LOOP_BACK)
+               dev->can.ctrlmode_supported |= CAN_CTRLMODE_LOOPBACK;
+
+       if (bt_const->feature & GS_CAN_FEATURE_TRIPLE_SAMPLE)
+               dev->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES;
+
+       if (bt_const->feature & GS_CAN_FEATURE_ONE_SHOT)
+               dev->can.ctrlmode_supported |= CAN_CTRLMODE_ONE_SHOT;
+
+       kfree(bt_const);
+
+       SET_NETDEV_DEV(netdev, &intf->dev);
+
+       rc = register_candev(dev->netdev);
+       if (rc) {
+               free_candev(dev->netdev);
+               dev_err(&intf->dev, "Couldn't register candev (err=%d)\n", rc);
+               return ERR_PTR(rc);
+       }
+
+       return dev;
+}
+
+static void gs_destroy_candev(struct gs_can *dev)
+{
+       unregister_candev(dev->netdev);
+       free_candev(dev->netdev);
+       usb_kill_anchored_urbs(&dev->tx_submitted);
+       kfree(dev);
+}
+
+static int gs_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+       struct gs_usb *dev;
+       int rc = -ENOMEM;
+       unsigned int icount, i;
+       struct gs_host_config *hconf;
+       struct gs_device_config *dconf;
+
+       hconf = kmalloc(sizeof(*hconf), GFP_KERNEL);
+       if (!hconf)
+               return -ENOMEM;
+
+       hconf->byte_order = 0x0000beef;
+
+       /* send host config */
+       rc = usb_control_msg(interface_to_usbdev(intf),
+                            usb_sndctrlpipe(interface_to_usbdev(intf), 0),
+                            GS_USB_BREQ_HOST_FORMAT,
+                            USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
+                            1,
+                            intf->altsetting[0].desc.bInterfaceNumber,
+                            hconf,
+                            sizeof(*hconf),
+                            1000);
+
+       kfree(hconf);
+
+       if (rc < 0) {
+               dev_err(&intf->dev, "Couldn't send data format (err=%d)\n",
+                       rc);
+               return rc;
+       }
+
+       dconf = kmalloc(sizeof(*dconf), GFP_KERNEL);
+       if (!dconf)
+               return -ENOMEM;
+
+       /* read device config */
+       rc = usb_control_msg(interface_to_usbdev(intf),
+                            usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
+                            GS_USB_BREQ_DEVICE_CONFIG,
+                            USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
+                            1,
+                            intf->altsetting[0].desc.bInterfaceNumber,
+                            dconf,
+                            sizeof(*dconf),
+                            1000);
+       if (rc < 0) {
+               dev_err(&intf->dev, "Couldn't get device config: (err=%d)\n",
+                       rc);
+
+               kfree(dconf);
+
+               return rc;
+       }
+
+       icount = dconf->icount+1;
+
+       kfree(dconf);
+
+       dev_info(&intf->dev, "Configuring for %d interfaces\n", icount);
+
+       if (icount > GS_MAX_INTF) {
+               dev_err(&intf->dev,
+                       "Driver cannot handle more that %d CAN interfaces\n",
+                       GS_MAX_INTF);
+               return -EINVAL;
+       }
+
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       init_usb_anchor(&dev->rx_submitted);
+
+       atomic_set(&dev->active_channels, 0);
+
+       usb_set_intfdata(intf, dev);
+       dev->udev = interface_to_usbdev(intf);
+
+       for (i = 0; i < icount; i++) {
+               dev->canch[i] = gs_make_candev(i, intf);
+               if (IS_ERR_OR_NULL(dev->canch[i])) {
+                       /* on failure destroy previously created candevs */
+                       icount = i;
+                       for (i = 0; i < icount; i++) {
+                               gs_destroy_candev(dev->canch[i]);
+                               dev->canch[i] = NULL;
+                       }
+                       kfree(dev);
+                       return rc;
+               }
+               dev->canch[i]->parent = dev;
+       }
+
+       return 0;
+}
+
+static void gs_usb_disconnect(struct usb_interface *intf)
+{
+       unsigned i;
+       struct gs_usb *dev = usb_get_intfdata(intf);
+       usb_set_intfdata(intf, NULL);
+
+       if (!dev) {
+               dev_err(&intf->dev, "Disconnect (nodata)\n");
+               return;
+       }
+
+       for (i = 0; i < GS_MAX_INTF; i++) {
+               struct gs_can *can = dev->canch[i];
+
+               if (!can)
+                       continue;
+
+               gs_destroy_candev(can);
+       }
+
+       usb_kill_anchored_urbs(&dev->rx_submitted);
+}
+
+static const struct usb_device_id gs_usb_table[] = {
+       {USB_DEVICE(USB_GSUSB_1_VENDOR_ID, USB_GSUSB_1_PRODUCT_ID)},
+       {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, gs_usb_table);
+
+static struct usb_driver gs_usb_driver = {
+       .name       = "gs_usb",
+       .probe      = gs_usb_probe,
+       .disconnect = gs_usb_disconnect,
+       .id_table   = gs_usb_table,
+};
+
+module_usb_driver(gs_usb_driver);
+
+MODULE_AUTHOR("Maximilian Schneider <mws@schneidersoft.net>");
+MODULE_DESCRIPTION(
+"Socket CAN device driver for Geschwister Schneider Technologie-, "
+"Entwicklungs- und Vertriebs UG. USB2.0 to CAN interfaces.");
+MODULE_LICENSE("GPL v2");
index 4ca46edc061d761169a85fc58937dee449a4776f..541fb7a05625aaf889089704ec155956629e77c8 100644 (file)
@@ -53,6 +53,8 @@
 #define USB_OEM_MERCURY_PRODUCT_ID     34
 #define USB_OEM_LEAF_PRODUCT_ID                35
 #define USB_CAN_R_PRODUCT_ID           39
+#define USB_LEAF_LITE_V2_PRODUCT_ID    288
+#define USB_MINI_PCIE_HS_PRODUCT_ID    289
 
 /* USB devices features */
 #define KVASER_HAS_SILENT_MODE         BIT(0)
@@ -356,6 +358,8 @@ static const struct usb_device_id kvaser_usb_table[] = {
                .driver_info = KVASER_HAS_TXRX_ERRORS },
        { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID),
                .driver_info = KVASER_HAS_TXRX_ERRORS },
+       { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_V2_PRODUCT_ID) },
+       { USB_DEVICE(KVASER_VENDOR_ID, USB_MINI_PCIE_HS_PRODUCT_ID) },
        { }
 };
 MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
@@ -379,38 +383,43 @@ static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id,
        void *buf;
        int actual_len;
        int err;
-       int pos = 0;
+       int pos;
+       unsigned long to = jiffies + msecs_to_jiffies(USB_RECV_TIMEOUT);
 
        buf = kzalloc(RX_BUFFER_SIZE, GFP_KERNEL);
        if (!buf)
                return -ENOMEM;
 
-       err = usb_bulk_msg(dev->udev,
-                          usb_rcvbulkpipe(dev->udev,
-                                          dev->bulk_in->bEndpointAddress),
-                          buf, RX_BUFFER_SIZE, &actual_len,
-                          USB_RECV_TIMEOUT);
-       if (err < 0)
-               goto end;
+       do {
+               err = usb_bulk_msg(dev->udev,
+                                  usb_rcvbulkpipe(dev->udev,
+                                       dev->bulk_in->bEndpointAddress),
+                                  buf, RX_BUFFER_SIZE, &actual_len,
+                                  USB_RECV_TIMEOUT);
+               if (err < 0)
+                       goto end;
 
-       while (pos <= actual_len - MSG_HEADER_LEN) {
-               tmp = buf + pos;
+               pos = 0;
+               while (pos <= actual_len - MSG_HEADER_LEN) {
+                       tmp = buf + pos;
 
-               if (!tmp->len)
-                       break;
+                       if (!tmp->len)
+                               break;
 
-               if (pos + tmp->len > actual_len) {
-                       dev_err(dev->udev->dev.parent, "Format error\n");
-                       break;
-               }
+                       if (pos + tmp->len > actual_len) {
+                               dev_err(dev->udev->dev.parent,
+                                       "Format error\n");
+                               break;
+                       }
 
-               if (tmp->id == id) {
-                       memcpy(msg, tmp, tmp->len);
-                       goto end;
-               }
+                       if (tmp->id == id) {
+                               memcpy(msg, tmp, tmp->len);
+                               goto end;
+                       }
 
-               pos += tmp->len;
-       }
+                       pos += tmp->len;
+               }
+       } while (time_before(jiffies, to));
 
        err = -EINVAL;
 
diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c
new file mode 100644 (file)
index 0000000..5e8b560
--- /dev/null
@@ -0,0 +1,1208 @@
+/* Xilinx CAN device driver
+ *
+ * Copyright (C) 2012 - 2014 Xilinx, Inc.
+ * Copyright (C) 2009 PetaLogix. All rights reserved.
+ *
+ * Description:
+ * This driver is developed for Axi CAN IP and for Zynq CANPS Controller.
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include <linux/can/led.h>
+
+#define DRIVER_NAME    "xilinx_can"
+
+/* CAN registers set */
+enum xcan_reg {
+       XCAN_SRR_OFFSET         = 0x00, /* Software reset */
+       XCAN_MSR_OFFSET         = 0x04, /* Mode select */
+       XCAN_BRPR_OFFSET        = 0x08, /* Baud rate prescaler */
+       XCAN_BTR_OFFSET         = 0x0C, /* Bit timing */
+       XCAN_ECR_OFFSET         = 0x10, /* Error counter */
+       XCAN_ESR_OFFSET         = 0x14, /* Error status */
+       XCAN_SR_OFFSET          = 0x18, /* Status */
+       XCAN_ISR_OFFSET         = 0x1C, /* Interrupt status */
+       XCAN_IER_OFFSET         = 0x20, /* Interrupt enable */
+       XCAN_ICR_OFFSET         = 0x24, /* Interrupt clear */
+       XCAN_TXFIFO_ID_OFFSET   = 0x30,/* TX FIFO ID */
+       XCAN_TXFIFO_DLC_OFFSET  = 0x34, /* TX FIFO DLC */
+       XCAN_TXFIFO_DW1_OFFSET  = 0x38, /* TX FIFO Data Word 1 */
+       XCAN_TXFIFO_DW2_OFFSET  = 0x3C, /* TX FIFO Data Word 2 */
+       XCAN_RXFIFO_ID_OFFSET   = 0x50, /* RX FIFO ID */
+       XCAN_RXFIFO_DLC_OFFSET  = 0x54, /* RX FIFO DLC */
+       XCAN_RXFIFO_DW1_OFFSET  = 0x58, /* RX FIFO Data Word 1 */
+       XCAN_RXFIFO_DW2_OFFSET  = 0x5C, /* RX FIFO Data Word 2 */
+};
+
+/* CAN register bit masks - XCAN_<REG>_<BIT>_MASK */
+#define XCAN_SRR_CEN_MASK              0x00000002 /* CAN enable */
+#define XCAN_SRR_RESET_MASK            0x00000001 /* Soft Reset the CAN core */
+#define XCAN_MSR_LBACK_MASK            0x00000002 /* Loop back mode select */
+#define XCAN_MSR_SLEEP_MASK            0x00000001 /* Sleep mode select */
+#define XCAN_BRPR_BRP_MASK             0x000000FF /* Baud rate prescaler */
+#define XCAN_BTR_SJW_MASK              0x00000180 /* Synchronous jump width */
+#define XCAN_BTR_TS2_MASK              0x00000070 /* Time segment 2 */
+#define XCAN_BTR_TS1_MASK              0x0000000F /* Time segment 1 */
+#define XCAN_ECR_REC_MASK              0x0000FF00 /* Receive error counter */
+#define XCAN_ECR_TEC_MASK              0x000000FF /* Transmit error counter */
+#define XCAN_ESR_ACKER_MASK            0x00000010 /* ACK error */
+#define XCAN_ESR_BERR_MASK             0x00000008 /* Bit error */
+#define XCAN_ESR_STER_MASK             0x00000004 /* Stuff error */
+#define XCAN_ESR_FMER_MASK             0x00000002 /* Form error */
+#define XCAN_ESR_CRCER_MASK            0x00000001 /* CRC error */
+#define XCAN_SR_TXFLL_MASK             0x00000400 /* TX FIFO is full */
+#define XCAN_SR_ESTAT_MASK             0x00000180 /* Error status */
+#define XCAN_SR_ERRWRN_MASK            0x00000040 /* Error warning */
+#define XCAN_SR_NORMAL_MASK            0x00000008 /* Normal mode */
+#define XCAN_SR_LBACK_MASK             0x00000002 /* Loop back mode */
+#define XCAN_SR_CONFIG_MASK            0x00000001 /* Configuration mode */
+#define XCAN_IXR_TXFEMP_MASK           0x00004000 /* TX FIFO Empty */
+#define XCAN_IXR_WKUP_MASK             0x00000800 /* Wake up interrupt */
+#define XCAN_IXR_SLP_MASK              0x00000400 /* Sleep interrupt */
+#define XCAN_IXR_BSOFF_MASK            0x00000200 /* Bus off interrupt */
+#define XCAN_IXR_ERROR_MASK            0x00000100 /* Error interrupt */
+#define XCAN_IXR_RXNEMP_MASK           0x00000080 /* RX FIFO NotEmpty intr */
+#define XCAN_IXR_RXOFLW_MASK           0x00000040 /* RX FIFO Overflow intr */
+#define XCAN_IXR_RXOK_MASK             0x00000010 /* Message received intr */
+#define XCAN_IXR_TXFLL_MASK            0x00000004 /* Tx FIFO Full intr */
+#define XCAN_IXR_TXOK_MASK             0x00000002 /* TX successful intr */
+#define XCAN_IXR_ARBLST_MASK           0x00000001 /* Arbitration lost intr */
+#define XCAN_IDR_ID1_MASK              0xFFE00000 /* Standard msg identifier */
+#define XCAN_IDR_SRR_MASK              0x00100000 /* Substitute remote TXreq */
+#define XCAN_IDR_IDE_MASK              0x00080000 /* Identifier extension */
+#define XCAN_IDR_ID2_MASK              0x0007FFFE /* Extended message ident */
+#define XCAN_IDR_RTR_MASK              0x00000001 /* Remote TX request */
+#define XCAN_DLCR_DLC_MASK             0xF0000000 /* Data length code */
+
+#define XCAN_INTR_ALL          (XCAN_IXR_TXOK_MASK | XCAN_IXR_BSOFF_MASK |\
+                                XCAN_IXR_WKUP_MASK | XCAN_IXR_SLP_MASK | \
+                                XCAN_IXR_RXNEMP_MASK | XCAN_IXR_ERROR_MASK | \
+                                XCAN_IXR_ARBLST_MASK | XCAN_IXR_RXOK_MASK)
+
+/* CAN register bit shift - XCAN_<REG>_<BIT>_SHIFT */
+#define XCAN_BTR_SJW_SHIFT             7  /* Synchronous jump width */
+#define XCAN_BTR_TS2_SHIFT             4  /* Time segment 2 */
+#define XCAN_IDR_ID1_SHIFT             21 /* Standard Messg Identifier */
+#define XCAN_IDR_ID2_SHIFT             1  /* Extended Message Identifier */
+#define XCAN_DLCR_DLC_SHIFT            28 /* Data length code */
+#define XCAN_ESR_REC_SHIFT             8  /* Rx Error Count */
+
+/* CAN frame length constants */
+#define XCAN_FRAME_MAX_DATA_LEN                8
+#define XCAN_TIMEOUT                   (1 * HZ)
+
+/**
+ * struct xcan_priv - This definition define CAN driver instance
+ * @can:                       CAN private data structure.
+ * @tx_head:                   Tx CAN packets ready to send on the queue
+ * @tx_tail:                   Tx CAN packets successfully sended on the queue
+ * @tx_max:                    Maximum number packets the driver can send
+ * @napi:                      NAPI structure
+ * @read_reg:                  For reading data from CAN registers
+ * @write_reg:                 For writing data to CAN registers
+ * @dev:                       Network device data structure
+ * @reg_base:                  Ioremapped address to registers
+ * @irq_flags:                 For request_irq()
+ * @bus_clk:                   Pointer to struct clk
+ * @can_clk:                   Pointer to struct clk
+ */
+struct xcan_priv {
+       struct can_priv can;
+       unsigned int tx_head;
+       unsigned int tx_tail;
+       unsigned int tx_max;
+       struct napi_struct napi;
+       u32 (*read_reg)(const struct xcan_priv *priv, enum xcan_reg reg);
+       void (*write_reg)(const struct xcan_priv *priv, enum xcan_reg reg,
+                       u32 val);
+       struct net_device *dev;
+       void __iomem *reg_base;
+       unsigned long irq_flags;
+       struct clk *bus_clk;
+       struct clk *can_clk;
+};
+
+/* CAN Bittiming constants as per Xilinx CAN specs */
+static const struct can_bittiming_const xcan_bittiming_const = {
+       .name = DRIVER_NAME,
+       .tseg1_min = 1,
+       .tseg1_max = 16,
+       .tseg2_min = 1,
+       .tseg2_max = 8,
+       .sjw_max = 4,
+       .brp_min = 1,
+       .brp_max = 256,
+       .brp_inc = 1,
+};
+
+/**
+ * xcan_write_reg_le - Write a value to the device register little endian
+ * @priv:      Driver private data structure
+ * @reg:       Register offset
+ * @val:       Value to write at the Register offset
+ *
+ * Write data to the paricular CAN register
+ */
+static void xcan_write_reg_le(const struct xcan_priv *priv, enum xcan_reg reg,
+                       u32 val)
+{
+       iowrite32(val, priv->reg_base + reg);
+}
+
+/**
+ * xcan_read_reg_le - Read a value from the device register little endian
+ * @priv:      Driver private data structure
+ * @reg:       Register offset
+ *
+ * Read data from the particular CAN register
+ * Return: value read from the CAN register
+ */
+static u32 xcan_read_reg_le(const struct xcan_priv *priv, enum xcan_reg reg)
+{
+       return ioread32(priv->reg_base + reg);
+}
+
+/**
+ * xcan_write_reg_be - Write a value to the device register big endian
+ * @priv:      Driver private data structure
+ * @reg:       Register offset
+ * @val:       Value to write at the Register offset
+ *
+ * Write data to the paricular CAN register
+ */
+static void xcan_write_reg_be(const struct xcan_priv *priv, enum xcan_reg reg,
+                       u32 val)
+{
+       iowrite32be(val, priv->reg_base + reg);
+}
+
+/**
+ * xcan_read_reg_be - Read a value from the device register big endian
+ * @priv:      Driver private data structure
+ * @reg:       Register offset
+ *
+ * Read data from the particular CAN register
+ * Return: value read from the CAN register
+ */
+static u32 xcan_read_reg_be(const struct xcan_priv *priv, enum xcan_reg reg)
+{
+       return ioread32be(priv->reg_base + reg);
+}
+
+/**
+ * set_reset_mode - Resets the CAN device mode
+ * @ndev:      Pointer to net_device structure
+ *
+ * This is the driver reset mode routine.The driver
+ * enters into configuration mode.
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int set_reset_mode(struct net_device *ndev)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+       unsigned long timeout;
+
+       priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
+
+       timeout = jiffies + XCAN_TIMEOUT;
+       while (!(priv->read_reg(priv, XCAN_SR_OFFSET) & XCAN_SR_CONFIG_MASK)) {
+               if (time_after(jiffies, timeout)) {
+                       netdev_warn(ndev, "timed out for config mode\n");
+                       return -ETIMEDOUT;
+               }
+               usleep_range(500, 10000);
+       }
+
+       return 0;
+}
+
+/**
+ * xcan_set_bittiming - CAN set bit timing routine
+ * @ndev:      Pointer to net_device structure
+ *
+ * This is the driver set bittiming  routine.
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_set_bittiming(struct net_device *ndev)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+       struct can_bittiming *bt = &priv->can.bittiming;
+       u32 btr0, btr1;
+       u32 is_config_mode;
+
+       /* Check whether Xilinx CAN is in configuration mode.
+        * It cannot set bit timing if Xilinx CAN is not in configuration mode.
+        */
+       is_config_mode = priv->read_reg(priv, XCAN_SR_OFFSET) &
+                               XCAN_SR_CONFIG_MASK;
+       if (!is_config_mode) {
+               netdev_alert(ndev,
+                    "BUG! Cannot set bittiming - CAN is not in config mode\n");
+               return -EPERM;
+       }
+
+       /* Setting Baud Rate prescalar value in BRPR Register */
+       btr0 = (bt->brp - 1);
+
+       /* Setting Time Segment 1 in BTR Register */
+       btr1 = (bt->prop_seg + bt->phase_seg1 - 1);
+
+       /* Setting Time Segment 2 in BTR Register */
+       btr1 |= (bt->phase_seg2 - 1) << XCAN_BTR_TS2_SHIFT;
+
+       /* Setting Synchronous jump width in BTR Register */
+       btr1 |= (bt->sjw - 1) << XCAN_BTR_SJW_SHIFT;
+
+       priv->write_reg(priv, XCAN_BRPR_OFFSET, btr0);
+       priv->write_reg(priv, XCAN_BTR_OFFSET, btr1);
+
+       netdev_dbg(ndev, "BRPR=0x%08x, BTR=0x%08x\n",
+                       priv->read_reg(priv, XCAN_BRPR_OFFSET),
+                       priv->read_reg(priv, XCAN_BTR_OFFSET));
+
+       return 0;
+}
+
+/**
+ * xcan_chip_start - This the drivers start routine
+ * @ndev:      Pointer to net_device structure
+ *
+ * This is the drivers start routine.
+ * Based on the State of the CAN device it puts
+ * the CAN device into a proper mode.
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_chip_start(struct net_device *ndev)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+       u32 err, reg_msr, reg_sr_mask;
+       unsigned long timeout;
+
+       /* Check if it is in reset mode */
+       err = set_reset_mode(ndev);
+       if (err < 0)
+               return err;
+
+       err = xcan_set_bittiming(ndev);
+       if (err < 0)
+               return err;
+
+       /* Enable interrupts */
+       priv->write_reg(priv, XCAN_IER_OFFSET, XCAN_INTR_ALL);
+
+       /* Check whether it is loopback mode or normal mode  */
+       if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
+               reg_msr = XCAN_MSR_LBACK_MASK;
+               reg_sr_mask = XCAN_SR_LBACK_MASK;
+       } else {
+               reg_msr = 0x0;
+               reg_sr_mask = XCAN_SR_NORMAL_MASK;
+       }
+
+       priv->write_reg(priv, XCAN_MSR_OFFSET, reg_msr);
+       priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_CEN_MASK);
+
+       timeout = jiffies + XCAN_TIMEOUT;
+       while (!(priv->read_reg(priv, XCAN_SR_OFFSET) & reg_sr_mask)) {
+               if (time_after(jiffies, timeout)) {
+                       netdev_warn(ndev,
+                               "timed out for correct mode\n");
+                       return -ETIMEDOUT;
+               }
+       }
+       netdev_dbg(ndev, "status:#x%08x\n",
+                       priv->read_reg(priv, XCAN_SR_OFFSET));
+
+       priv->can.state = CAN_STATE_ERROR_ACTIVE;
+       return 0;
+}
+
+/**
+ * xcan_do_set_mode - This sets the mode of the driver
+ * @ndev:      Pointer to net_device structure
+ * @mode:      Tells the mode of the driver
+ *
+ * This check the drivers state and calls the
+ * the corresponding modes to set.
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_do_set_mode(struct net_device *ndev, enum can_mode mode)
+{
+       int ret;
+
+       switch (mode) {
+       case CAN_MODE_START:
+               ret = xcan_chip_start(ndev);
+               if (ret < 0) {
+                       netdev_err(ndev, "xcan_chip_start failed!\n");
+                       return ret;
+               }
+               netif_wake_queue(ndev);
+               break;
+       default:
+               ret = -EOPNOTSUPP;
+               break;
+       }
+
+       return ret;
+}
+
+/**
+ * xcan_start_xmit - Starts the transmission
+ * @skb:       sk_buff pointer that contains data to be Txed
+ * @ndev:      Pointer to net_device structure
+ *
+ * This function is invoked from upper layers to initiate transmission. This
+ * function uses the next available free txbuff and populates their fields to
+ * start the transmission.
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+       struct net_device_stats *stats = &ndev->stats;
+       struct can_frame *cf = (struct can_frame *)skb->data;
+       u32 id, dlc, data[2] = {0, 0};
+
+       if (can_dropped_invalid_skb(ndev, skb))
+               return NETDEV_TX_OK;
+
+       /* Check if the TX buffer is full */
+       if (unlikely(priv->read_reg(priv, XCAN_SR_OFFSET) &
+                       XCAN_SR_TXFLL_MASK)) {
+               netif_stop_queue(ndev);
+               netdev_err(ndev, "BUG!, TX FIFO full when queue awake!\n");
+               return NETDEV_TX_BUSY;
+       }
+
+       /* Watch carefully on the bit sequence */
+       if (cf->can_id & CAN_EFF_FLAG) {
+               /* Extended CAN ID format */
+               id = ((cf->can_id & CAN_EFF_MASK) << XCAN_IDR_ID2_SHIFT) &
+                       XCAN_IDR_ID2_MASK;
+               id |= (((cf->can_id & CAN_EFF_MASK) >>
+                       (CAN_EFF_ID_BITS-CAN_SFF_ID_BITS)) <<
+                       XCAN_IDR_ID1_SHIFT) & XCAN_IDR_ID1_MASK;
+
+               /* The substibute remote TX request bit should be "1"
+                * for extended frames as in the Xilinx CAN datasheet
+                */
+               id |= XCAN_IDR_IDE_MASK | XCAN_IDR_SRR_MASK;
+
+               if (cf->can_id & CAN_RTR_FLAG)
+                       /* Extended frames remote TX request */
+                       id |= XCAN_IDR_RTR_MASK;
+       } else {
+               /* Standard CAN ID format */
+               id = ((cf->can_id & CAN_SFF_MASK) << XCAN_IDR_ID1_SHIFT) &
+                       XCAN_IDR_ID1_MASK;
+
+               if (cf->can_id & CAN_RTR_FLAG)
+                       /* Standard frames remote TX request */
+                       id |= XCAN_IDR_SRR_MASK;
+       }
+
+       dlc = cf->can_dlc << XCAN_DLCR_DLC_SHIFT;
+
+       if (cf->can_dlc > 0)
+               data[0] = be32_to_cpup((__be32 *)(cf->data + 0));
+       if (cf->can_dlc > 4)
+               data[1] = be32_to_cpup((__be32 *)(cf->data + 4));
+
+       can_put_echo_skb(skb, ndev, priv->tx_head % priv->tx_max);
+       priv->tx_head++;
+
+       /* Write the Frame to Xilinx CAN TX FIFO */
+       priv->write_reg(priv, XCAN_TXFIFO_ID_OFFSET, id);
+       /* If the CAN frame is RTR frame this write triggers tranmission */
+       priv->write_reg(priv, XCAN_TXFIFO_DLC_OFFSET, dlc);
+       if (!(cf->can_id & CAN_RTR_FLAG)) {
+               priv->write_reg(priv, XCAN_TXFIFO_DW1_OFFSET, data[0]);
+               /* If the CAN frame is Standard/Extended frame this
+                * write triggers tranmission
+                */
+               priv->write_reg(priv, XCAN_TXFIFO_DW2_OFFSET, data[1]);
+               stats->tx_bytes += cf->can_dlc;
+       }
+
+       /* Check if the TX buffer is full */
+       if ((priv->tx_head - priv->tx_tail) == priv->tx_max)
+               netif_stop_queue(ndev);
+
+       return NETDEV_TX_OK;
+}
+
+/**
+ * xcan_rx -  Is called from CAN isr to complete the received
+ *             frame  processing
+ * @ndev:      Pointer to net_device structure
+ *
+ * This function is invoked from the CAN isr(poll) to process the Rx frames. It
+ * does minimal processing and invokes "netif_receive_skb" to complete further
+ * processing.
+ * Return: 1 on success and 0 on failure.
+ */
+static int xcan_rx(struct net_device *ndev)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+       struct net_device_stats *stats = &ndev->stats;
+       struct can_frame *cf;
+       struct sk_buff *skb;
+       u32 id_xcan, dlc, data[2] = {0, 0};
+
+       skb = alloc_can_skb(ndev, &cf);
+       if (unlikely(!skb)) {
+               stats->rx_dropped++;
+               return 0;
+       }
+
+       /* Read a frame from Xilinx zynq CANPS */
+       id_xcan = priv->read_reg(priv, XCAN_RXFIFO_ID_OFFSET);
+       dlc = priv->read_reg(priv, XCAN_RXFIFO_DLC_OFFSET) >>
+                               XCAN_DLCR_DLC_SHIFT;
+
+       /* Change Xilinx CAN data length format to socketCAN data format */
+       cf->can_dlc = get_can_dlc(dlc);
+
+       /* Change Xilinx CAN ID format to socketCAN ID format */
+       if (id_xcan & XCAN_IDR_IDE_MASK) {
+               /* The received frame is an Extended format frame */
+               cf->can_id = (id_xcan & XCAN_IDR_ID1_MASK) >> 3;
+               cf->can_id |= (id_xcan & XCAN_IDR_ID2_MASK) >>
+                               XCAN_IDR_ID2_SHIFT;
+               cf->can_id |= CAN_EFF_FLAG;
+               if (id_xcan & XCAN_IDR_RTR_MASK)
+                       cf->can_id |= CAN_RTR_FLAG;
+       } else {
+               /* The received frame is a standard format frame */
+               cf->can_id = (id_xcan & XCAN_IDR_ID1_MASK) >>
+                               XCAN_IDR_ID1_SHIFT;
+               if (id_xcan & XCAN_IDR_SRR_MASK)
+                       cf->can_id |= CAN_RTR_FLAG;
+       }
+
+       if (!(id_xcan & XCAN_IDR_SRR_MASK)) {
+               data[0] = priv->read_reg(priv, XCAN_RXFIFO_DW1_OFFSET);
+               data[1] = priv->read_reg(priv, XCAN_RXFIFO_DW2_OFFSET);
+
+               /* Change Xilinx CAN data format to socketCAN data format */
+               if (cf->can_dlc > 0)
+                       *(__be32 *)(cf->data) = cpu_to_be32(data[0]);
+               if (cf->can_dlc > 4)
+                       *(__be32 *)(cf->data + 4) = cpu_to_be32(data[1]);
+       }
+
+       stats->rx_bytes += cf->can_dlc;
+       stats->rx_packets++;
+       netif_receive_skb(skb);
+
+       return 1;
+}
+
+/**
+ * xcan_err_interrupt - error frame Isr
+ * @ndev:      net_device pointer
+ * @isr:       interrupt status register value
+ *
+ * This is the CAN error interrupt and it will
+ * check the the type of error and forward the error
+ * frame to upper layers.
+ */
+static void xcan_err_interrupt(struct net_device *ndev, u32 isr)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+       struct net_device_stats *stats = &ndev->stats;
+       struct can_frame *cf;
+       struct sk_buff *skb;
+       u32 err_status, status, txerr = 0, rxerr = 0;
+
+       skb = alloc_can_err_skb(ndev, &cf);
+
+       err_status = priv->read_reg(priv, XCAN_ESR_OFFSET);
+       priv->write_reg(priv, XCAN_ESR_OFFSET, err_status);
+       txerr = priv->read_reg(priv, XCAN_ECR_OFFSET) & XCAN_ECR_TEC_MASK;
+       rxerr = ((priv->read_reg(priv, XCAN_ECR_OFFSET) &
+                       XCAN_ECR_REC_MASK) >> XCAN_ESR_REC_SHIFT);
+       status = priv->read_reg(priv, XCAN_SR_OFFSET);
+
+       if (isr & XCAN_IXR_BSOFF_MASK) {
+               priv->can.state = CAN_STATE_BUS_OFF;
+               priv->can.can_stats.bus_off++;
+               /* Leave device in Config Mode in bus-off state */
+               priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
+               can_bus_off(ndev);
+               if (skb)
+                       cf->can_id |= CAN_ERR_BUSOFF;
+       } else if ((status & XCAN_SR_ESTAT_MASK) == XCAN_SR_ESTAT_MASK) {
+               priv->can.state = CAN_STATE_ERROR_PASSIVE;
+               priv->can.can_stats.error_passive++;
+               if (skb) {
+                       cf->can_id |= CAN_ERR_CRTL;
+                       cf->data[1] = (rxerr > 127) ?
+                                       CAN_ERR_CRTL_RX_PASSIVE :
+                                       CAN_ERR_CRTL_TX_PASSIVE;
+                       cf->data[6] = txerr;
+                       cf->data[7] = rxerr;
+               }
+       } else if (status & XCAN_SR_ERRWRN_MASK) {
+               priv->can.state = CAN_STATE_ERROR_WARNING;
+               priv->can.can_stats.error_warning++;
+               if (skb) {
+                       cf->can_id |= CAN_ERR_CRTL;
+                       cf->data[1] |= (txerr > rxerr) ?
+                                       CAN_ERR_CRTL_TX_WARNING :
+                                       CAN_ERR_CRTL_RX_WARNING;
+                       cf->data[6] = txerr;
+                       cf->data[7] = rxerr;
+               }
+       }
+
+       /* Check for Arbitration lost interrupt */
+       if (isr & XCAN_IXR_ARBLST_MASK) {
+               priv->can.can_stats.arbitration_lost++;
+               if (skb) {
+                       cf->can_id |= CAN_ERR_LOSTARB;
+                       cf->data[0] = CAN_ERR_LOSTARB_UNSPEC;
+               }
+       }
+
+       /* Check for RX FIFO Overflow interrupt */
+       if (isr & XCAN_IXR_RXOFLW_MASK) {
+               stats->rx_over_errors++;
+               stats->rx_errors++;
+               priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
+               if (skb) {
+                       cf->can_id |= CAN_ERR_CRTL;
+                       cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+               }
+       }
+
+       /* Check for error interrupt */
+       if (isr & XCAN_IXR_ERROR_MASK) {
+               if (skb) {
+                       cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+                       cf->data[2] |= CAN_ERR_PROT_UNSPEC;
+               }
+
+               /* Check for Ack error interrupt */
+               if (err_status & XCAN_ESR_ACKER_MASK) {
+                       stats->tx_errors++;
+                       if (skb) {
+                               cf->can_id |= CAN_ERR_ACK;
+                               cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
+                       }
+               }
+
+               /* Check for Bit error interrupt */
+               if (err_status & XCAN_ESR_BERR_MASK) {
+                       stats->tx_errors++;
+                       if (skb) {
+                               cf->can_id |= CAN_ERR_PROT;
+                               cf->data[2] = CAN_ERR_PROT_BIT;
+                       }
+               }
+
+               /* Check for Stuff error interrupt */
+               if (err_status & XCAN_ESR_STER_MASK) {
+                       stats->rx_errors++;
+                       if (skb) {
+                               cf->can_id |= CAN_ERR_PROT;
+                               cf->data[2] = CAN_ERR_PROT_STUFF;
+                       }
+               }
+
+               /* Check for Form error interrupt */
+               if (err_status & XCAN_ESR_FMER_MASK) {
+                       stats->rx_errors++;
+                       if (skb) {
+                               cf->can_id |= CAN_ERR_PROT;
+                               cf->data[2] = CAN_ERR_PROT_FORM;
+                       }
+               }
+
+               /* Check for CRC error interrupt */
+               if (err_status & XCAN_ESR_CRCER_MASK) {
+                       stats->rx_errors++;
+                       if (skb) {
+                               cf->can_id |= CAN_ERR_PROT;
+                               cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ |
+                                               CAN_ERR_PROT_LOC_CRC_DEL;
+                       }
+               }
+                       priv->can.can_stats.bus_error++;
+       }
+
+       if (skb) {
+               stats->rx_packets++;
+               stats->rx_bytes += cf->can_dlc;
+               netif_rx(skb);
+       }
+
+       netdev_dbg(ndev, "%s: error status register:0x%x\n",
+                       __func__, priv->read_reg(priv, XCAN_ESR_OFFSET));
+}
+
+/**
+ * xcan_state_interrupt - It will check the state of the CAN device
+ * @ndev:      net_device pointer
+ * @isr:       interrupt status register value
+ *
+ * This will checks the state of the CAN device
+ * and puts the device into appropriate state.
+ */
+static void xcan_state_interrupt(struct net_device *ndev, u32 isr)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+
+       /* Check for Sleep interrupt if set put CAN device in sleep state */
+       if (isr & XCAN_IXR_SLP_MASK)
+               priv->can.state = CAN_STATE_SLEEPING;
+
+       /* Check for Wake up interrupt if set put CAN device in Active state */
+       if (isr & XCAN_IXR_WKUP_MASK)
+               priv->can.state = CAN_STATE_ERROR_ACTIVE;
+}
+
+/**
+ * xcan_rx_poll - Poll routine for rx packets (NAPI)
+ * @napi:      napi structure pointer
+ * @quota:     Max number of rx packets to be processed.
+ *
+ * This is the poll routine for rx part.
+ * It will process the packets maximux quota value.
+ *
+ * Return: number of packets received
+ */
+static int xcan_rx_poll(struct napi_struct *napi, int quota)
+{
+       struct net_device *ndev = napi->dev;
+       struct xcan_priv *priv = netdev_priv(ndev);
+       u32 isr, ier;
+       int work_done = 0;
+
+       isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
+       while ((isr & XCAN_IXR_RXNEMP_MASK) && (work_done < quota)) {
+               if (isr & XCAN_IXR_RXOK_MASK) {
+                       priv->write_reg(priv, XCAN_ICR_OFFSET,
+                               XCAN_IXR_RXOK_MASK);
+                       work_done += xcan_rx(ndev);
+               } else {
+                       priv->write_reg(priv, XCAN_ICR_OFFSET,
+                               XCAN_IXR_RXNEMP_MASK);
+                       break;
+               }
+               priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_RXNEMP_MASK);
+               isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
+       }
+
+       if (work_done)
+               can_led_event(ndev, CAN_LED_EVENT_RX);
+
+       if (work_done < quota) {
+               napi_complete(napi);
+               ier = priv->read_reg(priv, XCAN_IER_OFFSET);
+               ier |= (XCAN_IXR_RXOK_MASK | XCAN_IXR_RXNEMP_MASK);
+               priv->write_reg(priv, XCAN_IER_OFFSET, ier);
+       }
+       return work_done;
+}
+
+/**
+ * xcan_tx_interrupt - Tx Done Isr
+ * @ndev:      net_device pointer
+ * @isr:       Interrupt status register value
+ */
+static void xcan_tx_interrupt(struct net_device *ndev, u32 isr)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+       struct net_device_stats *stats = &ndev->stats;
+
+       while ((priv->tx_head - priv->tx_tail > 0) &&
+                       (isr & XCAN_IXR_TXOK_MASK)) {
+               priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_TXOK_MASK);
+               can_get_echo_skb(ndev, priv->tx_tail %
+                                       priv->tx_max);
+               priv->tx_tail++;
+               stats->tx_packets++;
+               isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
+       }
+       can_led_event(ndev, CAN_LED_EVENT_TX);
+       netif_wake_queue(ndev);
+}
+
+/**
+ * xcan_interrupt - CAN Isr
+ * @irq:       irq number
+ * @dev_id:    device id poniter
+ *
+ * This is the xilinx CAN Isr. It checks for the type of interrupt
+ * and invokes the corresponding ISR.
+ *
+ * Return:
+ * IRQ_NONE - If CAN device is in sleep mode, IRQ_HANDLED otherwise
+ */
+static irqreturn_t xcan_interrupt(int irq, void *dev_id)
+{
+       struct net_device *ndev = (struct net_device *)dev_id;
+       struct xcan_priv *priv = netdev_priv(ndev);
+       u32 isr, ier;
+
+       /* Get the interrupt status from Xilinx CAN */
+       isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
+       if (!isr)
+               return IRQ_NONE;
+
+       /* Check for the type of interrupt and Processing it */
+       if (isr & (XCAN_IXR_SLP_MASK | XCAN_IXR_WKUP_MASK)) {
+               priv->write_reg(priv, XCAN_ICR_OFFSET, (XCAN_IXR_SLP_MASK |
+                               XCAN_IXR_WKUP_MASK));
+               xcan_state_interrupt(ndev, isr);
+       }
+
+       /* Check for Tx interrupt and Processing it */
+       if (isr & XCAN_IXR_TXOK_MASK)
+               xcan_tx_interrupt(ndev, isr);
+
+       /* Check for the type of error interrupt and Processing it */
+       if (isr & (XCAN_IXR_ERROR_MASK | XCAN_IXR_RXOFLW_MASK |
+                       XCAN_IXR_BSOFF_MASK | XCAN_IXR_ARBLST_MASK)) {
+               priv->write_reg(priv, XCAN_ICR_OFFSET, (XCAN_IXR_ERROR_MASK |
+                               XCAN_IXR_RXOFLW_MASK | XCAN_IXR_BSOFF_MASK |
+                               XCAN_IXR_ARBLST_MASK));
+               xcan_err_interrupt(ndev, isr);
+       }
+
+       /* Check for the type of receive interrupt and Processing it */
+       if (isr & (XCAN_IXR_RXNEMP_MASK | XCAN_IXR_RXOK_MASK)) {
+               ier = priv->read_reg(priv, XCAN_IER_OFFSET);
+               ier &= ~(XCAN_IXR_RXNEMP_MASK | XCAN_IXR_RXOK_MASK);
+               priv->write_reg(priv, XCAN_IER_OFFSET, ier);
+               napi_schedule(&priv->napi);
+       }
+       return IRQ_HANDLED;
+}
+
+/**
+ * xcan_chip_stop - Driver stop routine
+ * @ndev:      Pointer to net_device structure
+ *
+ * This is the drivers stop routine. It will disable the
+ * interrupts and put the device into configuration mode.
+ */
+static void xcan_chip_stop(struct net_device *ndev)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+       u32 ier;
+
+       /* Disable interrupts and leave the can in configuration mode */
+       ier = priv->read_reg(priv, XCAN_IER_OFFSET);
+       ier &= ~XCAN_INTR_ALL;
+       priv->write_reg(priv, XCAN_IER_OFFSET, ier);
+       priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
+       priv->can.state = CAN_STATE_STOPPED;
+}
+
+/**
+ * xcan_open - Driver open routine
+ * @ndev:      Pointer to net_device structure
+ *
+ * This is the driver open routine.
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_open(struct net_device *ndev)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+       int ret;
+
+       ret = request_irq(ndev->irq, xcan_interrupt, priv->irq_flags,
+                       ndev->name, ndev);
+       if (ret < 0) {
+               netdev_err(ndev, "irq allocation for CAN failed\n");
+               goto err;
+       }
+
+       ret = clk_prepare_enable(priv->can_clk);
+       if (ret) {
+               netdev_err(ndev, "unable to enable device clock\n");
+               goto err_irq;
+       }
+
+       ret = clk_prepare_enable(priv->bus_clk);
+       if (ret) {
+               netdev_err(ndev, "unable to enable bus clock\n");
+               goto err_can_clk;
+       }
+
+       /* Set chip into reset mode */
+       ret = set_reset_mode(ndev);
+       if (ret < 0) {
+               netdev_err(ndev, "mode resetting failed!\n");
+               goto err_bus_clk;
+       }
+
+       /* Common open */
+       ret = open_candev(ndev);
+       if (ret)
+               goto err_bus_clk;
+
+       ret = xcan_chip_start(ndev);
+       if (ret < 0) {
+               netdev_err(ndev, "xcan_chip_start failed!\n");
+               goto err_candev;
+       }
+
+       can_led_event(ndev, CAN_LED_EVENT_OPEN);
+       napi_enable(&priv->napi);
+       netif_start_queue(ndev);
+
+       return 0;
+
+err_candev:
+       close_candev(ndev);
+err_bus_clk:
+       clk_disable_unprepare(priv->bus_clk);
+err_can_clk:
+       clk_disable_unprepare(priv->can_clk);
+err_irq:
+       free_irq(ndev->irq, ndev);
+err:
+       return ret;
+}
+
+/**
+ * xcan_close - Driver close routine
+ * @ndev:      Pointer to net_device structure
+ *
+ * Return: 0 always
+ */
+static int xcan_close(struct net_device *ndev)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+
+       netif_stop_queue(ndev);
+       napi_disable(&priv->napi);
+       xcan_chip_stop(ndev);
+       clk_disable_unprepare(priv->bus_clk);
+       clk_disable_unprepare(priv->can_clk);
+       free_irq(ndev->irq, ndev);
+       close_candev(ndev);
+
+       can_led_event(ndev, CAN_LED_EVENT_STOP);
+
+       return 0;
+}
+
+/**
+ * xcan_get_berr_counter - error counter routine
+ * @ndev:      Pointer to net_device structure
+ * @bec:       Pointer to can_berr_counter structure
+ *
+ * This is the driver error counter routine.
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_get_berr_counter(const struct net_device *ndev,
+                                       struct can_berr_counter *bec)
+{
+       struct xcan_priv *priv = netdev_priv(ndev);
+       int ret;
+
+       ret = clk_prepare_enable(priv->can_clk);
+       if (ret)
+               goto err;
+
+       ret = clk_prepare_enable(priv->bus_clk);
+       if (ret)
+               goto err_clk;
+
+       bec->txerr = priv->read_reg(priv, XCAN_ECR_OFFSET) & XCAN_ECR_TEC_MASK;
+       bec->rxerr = ((priv->read_reg(priv, XCAN_ECR_OFFSET) &
+                       XCAN_ECR_REC_MASK) >> XCAN_ESR_REC_SHIFT);
+
+       clk_disable_unprepare(priv->bus_clk);
+       clk_disable_unprepare(priv->can_clk);
+
+       return 0;
+
+err_clk:
+       clk_disable_unprepare(priv->can_clk);
+err:
+       return ret;
+}
+
+
+static const struct net_device_ops xcan_netdev_ops = {
+       .ndo_open       = xcan_open,
+       .ndo_stop       = xcan_close,
+       .ndo_start_xmit = xcan_start_xmit,
+};
+
+/**
+ * xcan_suspend - Suspend method for the driver
+ * @dev:       Address of the platform_device structure
+ *
+ * Put the driver into low power mode.
+ * Return: 0 always
+ */
+static int __maybe_unused xcan_suspend(struct device *dev)
+{
+       struct platform_device *pdev = dev_get_drvdata(dev);
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct xcan_priv *priv = netdev_priv(ndev);
+
+       if (netif_running(ndev)) {
+               netif_stop_queue(ndev);
+               netif_device_detach(ndev);
+       }
+
+       priv->write_reg(priv, XCAN_MSR_OFFSET, XCAN_MSR_SLEEP_MASK);
+       priv->can.state = CAN_STATE_SLEEPING;
+
+       clk_disable(priv->bus_clk);
+       clk_disable(priv->can_clk);
+
+       return 0;
+}
+
+/**
+ * xcan_resume - Resume from suspend
+ * @dev:       Address of the platformdevice structure
+ *
+ * Resume operation after suspend.
+ * Return: 0 on success and failure value on error
+ */
+static int __maybe_unused xcan_resume(struct device *dev)
+{
+       struct platform_device *pdev = dev_get_drvdata(dev);
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct xcan_priv *priv = netdev_priv(ndev);
+       int ret;
+
+       ret = clk_enable(priv->bus_clk);
+       if (ret) {
+               dev_err(dev, "Cannot enable clock.\n");
+               return ret;
+       }
+       ret = clk_enable(priv->can_clk);
+       if (ret) {
+               dev_err(dev, "Cannot enable clock.\n");
+               clk_disable_unprepare(priv->bus_clk);
+               return ret;
+       }
+
+       priv->write_reg(priv, XCAN_MSR_OFFSET, 0);
+       priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_CEN_MASK);
+       priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+       if (netif_running(ndev)) {
+               netif_device_attach(ndev);
+               netif_start_queue(ndev);
+       }
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(xcan_dev_pm_ops, xcan_suspend, xcan_resume);
+
+/**
+ * xcan_probe - Platform registration call
+ * @pdev:      Handle to the platform device structure
+ *
+ * This function does all the memory allocation and registration for the CAN
+ * device.
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_probe(struct platform_device *pdev)
+{
+       struct resource *res; /* IO mem resources */
+       struct net_device *ndev;
+       struct xcan_priv *priv;
+       void __iomem *addr;
+       int ret, rx_max, tx_max;
+
+       /* Get the virtual base address for the device */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       addr = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(addr)) {
+               ret = PTR_ERR(addr);
+               goto err;
+       }
+
+       ret = of_property_read_u32(pdev->dev.of_node, "tx-fifo-depth", &tx_max);
+       if (ret < 0)
+               goto err;
+
+       ret = of_property_read_u32(pdev->dev.of_node, "rx-fifo-depth", &rx_max);
+       if (ret < 0)
+               goto err;
+
+       /* Create a CAN device instance */
+       ndev = alloc_candev(sizeof(struct xcan_priv), tx_max);
+       if (!ndev)
+               return -ENOMEM;
+
+       priv = netdev_priv(ndev);
+       priv->dev = ndev;
+       priv->can.bittiming_const = &xcan_bittiming_const;
+       priv->can.do_set_mode = xcan_do_set_mode;
+       priv->can.do_get_berr_counter = xcan_get_berr_counter;
+       priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
+                                       CAN_CTRLMODE_BERR_REPORTING;
+       priv->reg_base = addr;
+       priv->tx_max = tx_max;
+
+       /* Get IRQ for the device */
+       ndev->irq = platform_get_irq(pdev, 0);
+       ndev->flags |= IFF_ECHO;        /* We support local echo */
+
+       platform_set_drvdata(pdev, ndev);
+       SET_NETDEV_DEV(ndev, &pdev->dev);
+       ndev->netdev_ops = &xcan_netdev_ops;
+
+       /* Getting the CAN can_clk info */
+       priv->can_clk = devm_clk_get(&pdev->dev, "can_clk");
+       if (IS_ERR(priv->can_clk)) {
+               dev_err(&pdev->dev, "Device clock not found.\n");
+               ret = PTR_ERR(priv->can_clk);
+               goto err_free;
+       }
+       /* Check for type of CAN device */
+       if (of_device_is_compatible(pdev->dev.of_node,
+                                   "xlnx,zynq-can-1.0")) {
+               priv->bus_clk = devm_clk_get(&pdev->dev, "pclk");
+               if (IS_ERR(priv->bus_clk)) {
+                       dev_err(&pdev->dev, "bus clock not found\n");
+                       ret = PTR_ERR(priv->bus_clk);
+                       goto err_free;
+               }
+       } else {
+               priv->bus_clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
+               if (IS_ERR(priv->bus_clk)) {
+                       dev_err(&pdev->dev, "bus clock not found\n");
+                       ret = PTR_ERR(priv->bus_clk);
+                       goto err_free;
+               }
+       }
+
+       ret = clk_prepare_enable(priv->can_clk);
+       if (ret) {
+               dev_err(&pdev->dev, "unable to enable device clock\n");
+               goto err_free;
+       }
+
+       ret = clk_prepare_enable(priv->bus_clk);
+       if (ret) {
+               dev_err(&pdev->dev, "unable to enable bus clock\n");
+               goto err_unprepare_disable_dev;
+       }
+
+       priv->write_reg = xcan_write_reg_le;
+       priv->read_reg = xcan_read_reg_le;
+
+       if (priv->read_reg(priv, XCAN_SR_OFFSET) != XCAN_SR_CONFIG_MASK) {
+               priv->write_reg = xcan_write_reg_be;
+               priv->read_reg = xcan_read_reg_be;
+       }
+
+       priv->can.clock.freq = clk_get_rate(priv->can_clk);
+
+       netif_napi_add(ndev, &priv->napi, xcan_rx_poll, rx_max);
+
+       ret = register_candev(ndev);
+       if (ret) {
+               dev_err(&pdev->dev, "fail to register failed (err=%d)\n", ret);
+               goto err_unprepare_disable_busclk;
+       }
+
+       devm_can_led_init(ndev);
+       clk_disable_unprepare(priv->bus_clk);
+       clk_disable_unprepare(priv->can_clk);
+       netdev_dbg(ndev, "reg_base=0x%p irq=%d clock=%d, tx fifo depth:%d\n",
+                       priv->reg_base, ndev->irq, priv->can.clock.freq,
+                       priv->tx_max);
+
+       return 0;
+
+err_unprepare_disable_busclk:
+       clk_disable_unprepare(priv->bus_clk);
+err_unprepare_disable_dev:
+       clk_disable_unprepare(priv->can_clk);
+err_free:
+       free_candev(ndev);
+err:
+       return ret;
+}
+
+/**
+ * xcan_remove - Unregister the device after releasing the resources
+ * @pdev:      Handle to the platform device structure
+ *
+ * This function frees all the resources allocated to the device.
+ * Return: 0 always
+ */
+static int xcan_remove(struct platform_device *pdev)
+{
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct xcan_priv *priv = netdev_priv(ndev);
+
+       if (set_reset_mode(ndev) < 0)
+               netdev_err(ndev, "mode resetting failed!\n");
+
+       unregister_candev(ndev);
+       netif_napi_del(&priv->napi);
+       free_candev(ndev);
+
+       return 0;
+}
+
+/* Match table for OF platform binding */
+static struct of_device_id xcan_of_match[] = {
+       { .compatible = "xlnx,zynq-can-1.0", },
+       { .compatible = "xlnx,axi-can-1.00.a", },
+       { /* end of list */ },
+};
+MODULE_DEVICE_TABLE(of, xcan_of_match);
+
+static struct platform_driver xcan_driver = {
+       .probe = xcan_probe,
+       .remove = xcan_remove,
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = DRIVER_NAME,
+               .pm = &xcan_dev_pm_ops,
+               .of_match_table = xcan_of_match,
+       },
+};
+
+module_platform_driver(xcan_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Xilinx Inc");
+MODULE_DESCRIPTION("Xilinx CAN interface");
index 41ee5b6ae91751239d81e4613f336aff02fbff84..69c42513dd724bb06ef40eaf4b5e4bbea800936d 100644 (file)
@@ -289,7 +289,7 @@ static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p)
 
 static int mv88e6123_61_65_setup(struct dsa_switch *ds)
 {
-       struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
        int i;
        int ret;
 
index dadfafba64e9ac0aad3de55d84e5bdea3ceb00a0..953bc6a49e594471342270e7281a8e263f0086de 100644 (file)
@@ -155,7 +155,7 @@ static int mv88e6131_setup_global(struct dsa_switch *ds)
 
 static int mv88e6131_setup_port(struct dsa_switch *ds, int p)
 {
-       struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
        int addr = REG_PORT(p);
        u16 val;
 
@@ -274,7 +274,7 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p)
 
 static int mv88e6131_setup(struct dsa_switch *ds)
 {
-       struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
        int i;
        int ret;
 
index 17314ed9456d32c961aea31db10a650a2ffb1392..9ce2146346b6cda7175229d0f493f004730ab1b1 100644 (file)
@@ -74,7 +74,7 @@ int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg)
 
 int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg)
 {
-       struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
        int ret;
 
        mutex_lock(&ps->smi_mutex);
@@ -118,7 +118,7 @@ int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
 
 int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val)
 {
-       struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
        int ret;
 
        mutex_lock(&ps->smi_mutex);
@@ -256,7 +256,7 @@ static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps)
 
 static int mv88e6xxx_ppu_access_get(struct dsa_switch *ds)
 {
-       struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
        int ret;
 
        mutex_lock(&ps->ppu_mutex);
@@ -283,7 +283,7 @@ static int mv88e6xxx_ppu_access_get(struct dsa_switch *ds)
 
 static void mv88e6xxx_ppu_access_put(struct dsa_switch *ds)
 {
-       struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
 
        /* Schedule a timer to re-enable the PHY polling unit. */
        mod_timer(&ps->ppu_timer, jiffies + msecs_to_jiffies(10));
@@ -292,7 +292,7 @@ static void mv88e6xxx_ppu_access_put(struct dsa_switch *ds)
 
 void mv88e6xxx_ppu_state_init(struct dsa_switch *ds)
 {
-       struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
 
        mutex_init(&ps->ppu_mutex);
        INIT_WORK(&ps->ppu_work, mv88e6xxx_ppu_reenable_work);
@@ -463,7 +463,7 @@ void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
                                 int nr_stats, struct mv88e6xxx_hw_stat *stats,
                                 int port, uint64_t *data)
 {
-       struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
+       struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
        int ret;
        int i;
 
index 35df0b9e6848b0f1bd964f7b93ed2f18988c6b08..a968654b631d28a860dcd83b8c9b728189c196b2 100644 (file)
@@ -534,7 +534,7 @@ static int el3_common_init(struct net_device *dev)
        /* The EL3-specific entries in the device structure. */
        dev->netdev_ops = &netdev_ops;
        dev->watchdog_timeo = TX_TIMEOUT;
-       SET_ETHTOOL_OPS(dev, &ethtool_ops);
+       dev->ethtool_ops = &ethtool_ops;
 
        err = register_netdev(dev);
        if (err) {
index 063557e037f21b8b43fa3d1dfe64a19c870d4789..f18647c2355990ba0e55b6a2690c0cdbb819da43 100644 (file)
@@ -218,7 +218,7 @@ static int tc589_probe(struct pcmcia_device *link)
        dev->netdev_ops = &el3_netdev_ops;
        dev->watchdog_timeo = TX_TIMEOUT;
 
-       SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
+       dev->ethtool_ops = &netdev_ethtool_ops;
 
        return tc589_config(link);
 }
index 465cc7108d8a5e5bbb2c34df44f7bd0329d02d6b..e13b04624dedcecb7285943787946f2dfadd610c 100644 (file)
@@ -2435,7 +2435,7 @@ typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        netif_napi_add(dev, &tp->napi, typhoon_poll, 16);
        dev->watchdog_timeo     = TX_TIMEOUT;
 
-       SET_ETHTOOL_OPS(dev, &typhoon_ethtool_ops);
+       dev->ethtool_ops = &typhoon_ethtool_ops;
 
        /* We can handle scatter gather, up to 16 entries, and
         * we can do IP checksumming (only version 4, doh...)
index 455d4c399b52168ce6f8691160991e500216ff54..1d162ccb473341af256279b29d466bc56429472b 100644 (file)
@@ -157,7 +157,7 @@ static void ax_reset_8390(struct net_device *dev)
 
        /* This check _should_not_ be necessary, omit eventually. */
        while ((ei_inb(addr + EN0_ISR) & ENISR_RESET) == 0) {
-               if (jiffies - reset_start_time > 2 * HZ / 100) {
+               if (time_after(jiffies, reset_start_time + 2 * HZ / 100)) {
                        netdev_warn(dev, "%s: did not complete.\n", __func__);
                        break;
                }
@@ -293,7 +293,7 @@ static void ax_block_output(struct net_device *dev, int count,
        dma_start = jiffies;
 
        while ((ei_inb(nic_base + EN0_ISR) & ENISR_RDC) == 0) {
-               if (jiffies - dma_start > 2 * HZ / 100) {               /* 20ms */
+               if (time_after(jiffies, dma_start + 2 * HZ / 100)) { /* 20ms */
                        netdev_warn(dev, "timeout waiting for Tx RDC.\n");
                        ax_reset_8390(dev);
                        ax_NS8390_init(dev, 1);
index 051349458462142081f10ca6b44f0ce02ba95036..edb718661850f350711e73ada6368d2e080fe486 100644 (file)
@@ -68,6 +68,7 @@ source "drivers/net/ethernet/neterion/Kconfig"
 source "drivers/net/ethernet/faraday/Kconfig"
 source "drivers/net/ethernet/freescale/Kconfig"
 source "drivers/net/ethernet/fujitsu/Kconfig"
+source "drivers/net/ethernet/hisilicon/Kconfig"
 source "drivers/net/ethernet/hp/Kconfig"
 source "drivers/net/ethernet/ibm/Kconfig"
 source "drivers/net/ethernet/intel/Kconfig"
index 35190e36c4568e6803279f878a6aa866685ca1be..58de3339ab3c7a443d591a37425bb032c5b93ab5 100644 (file)
@@ -31,6 +31,7 @@ obj-$(CONFIG_NET_VENDOR_EXAR) += neterion/
 obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
 obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
 obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/
+obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/
 obj-$(CONFIG_NET_VENDOR_HP) += hp/
 obj-$(CONFIG_NET_VENDOR_IBM) += ibm/
 obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
index 171d73c1d3c22de3209ca6c48b7978c35b2a38b0..40dbbf740331c49b13ac0d64ef977be6ce361993 100644 (file)
@@ -784,7 +784,7 @@ static int starfire_init_one(struct pci_dev *pdev,
 
        dev->netdev_ops = &netdev_ops;
        dev->watchdog_timeo = TX_TIMEOUT;
-       SET_ETHTOOL_OPS(dev, &ethtool_ops);
+       dev->ethtool_ops = &ethtool_ops;
 
        netif_napi_add(dev, &np->napi, netdev_poll, max_interrupt_work);
 
index 1517e9df5ba16c66f6f63bc10f8a0e1b460a95a3..9a6991be9749f75b4107d979cd31518cda837ec0 100644 (file)
@@ -476,7 +476,7 @@ static int acenic_probe_one(struct pci_dev *pdev,
        dev->watchdog_timeo = 5*HZ;
 
        dev->netdev_ops = &ace_netdev_ops;
-       SET_ETHTOOL_OPS(dev, &ace_ethtool_ops);
+       dev->ethtool_ops = &ace_ethtool_ops;
 
        /* we only display this string ONCE */
        if (!boards_found)
index 99cc56f451cf4886fbf0a5bca044dfa5cf033a2c..580553d42d34fd773139cda6076f00d7bc40cce1 100644 (file)
@@ -353,7 +353,6 @@ static int sgdma_async_read(struct altera_tse_private *priv)
 
        struct sgdma_descrip __iomem *cdesc = &descbase[0];
        struct sgdma_descrip __iomem *ndesc = &descbase[1];
-
        struct tse_buffer *rxbuffer = NULL;
 
        if (!sgdma_rxbusy(priv)) {
index 54c25eff795272661e2caabfd554ffcc7dd24e00..be72e1e6452522ba9b81e58a3268580c992cfa5f 100644 (file)
@@ -271,5 +271,5 @@ static const struct ethtool_ops tse_ethtool_ops = {
 
 void altera_tse_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &tse_ethtool_ops);
+       netdev->ethtool_ops = &tse_ethtool_ops;
 }
index 562df46e0a82e24968b8b89bc35a7b357982e6ec..bbaf36d9f5e1cfbb7649ec13d5eb00595543a5b4 100644 (file)
@@ -7,7 +7,7 @@ config NET_VENDOR_AMD
        default y
        depends on DIO || MACH_DECSTATION || MVME147 || ATARI || SUN3 || \
                   SUN3X || SBUS || PCI || ZORRO || (ISA && ISA_DMA_API) || \
-                  (ARM && ARCH_EBSA110) || ISA || EISA || PCMCIA
+                  (ARM && ARCH_EBSA110) || ISA || EISA || PCMCIA || ARM64
        ---help---
          If you have a network (Ethernet) chipset belonging to this class,
          say Y.
@@ -177,4 +177,16 @@ config SUNLANCE
          To compile this driver as a module, choose M here: the module
          will be called sunlance.
 
+config AMD_XGBE
+       tristate "AMD 10GbE Ethernet driver"
+       depends on OF_NET
+       select PHYLIB
+       select AMD_XGBE_PHY
+       ---help---
+         This driver supports the AMD 10GbE Ethernet device found on an
+         AMD SoC.
+
+         To compile this driver as a module, choose M here: the module
+         will be called amd-xgbe.
+
 endif # NET_VENDOR_AMD
index cdd4301a973dc388a6d7f1d2a7ed5325f87d6e6b..a38a2dce3eb329ecceabe2b0d560160d009e527c 100644 (file)
@@ -17,3 +17,4 @@ obj-$(CONFIG_NI65) += ni65.o
 obj-$(CONFIG_PCNET32) += pcnet32.o
 obj-$(CONFIG_SUN3LANCE) += sun3lance.o
 obj-$(CONFIG_SUNLANCE) += sunlance.o
+obj-$(CONFIG_AMD_XGBE) += xgbe/
index 26efaaa5e73fd292de512fc428e9af84126fcbb9..068dc7cad5fa3c511c34b034c006add6284bb097 100644 (file)
@@ -1900,7 +1900,7 @@ static int amd8111e_probe_one(struct pci_dev *pdev,
 
        /* Initialize driver entry points */
        dev->netdev_ops = &amd8111e_netdev_ops;
-       SET_ETHTOOL_OPS(dev, &ops);
+       dev->ethtool_ops = &ops;
        dev->irq =pdev->irq;
        dev->watchdog_timeo = AMD8111E_TX_TIMEOUT;
        netif_napi_add(dev, &lp->napi, amd8111e_rx_poll, 32);
index b08101b31b8bc547ffbfb486f87abc7a18f92686..968b7bfac8fcaa9af767c57c474a83b3229b09a0 100644 (file)
@@ -718,7 +718,6 @@ static int ariadne_init_one(struct zorro_dev *z,
        unsigned long mem_start = board + ARIADNE_RAM;
        struct resource *r1, *r2;
        struct net_device *dev;
-       struct ariadne_private *priv;
        u32 serial;
        int err;
 
@@ -738,8 +737,6 @@ static int ariadne_init_one(struct zorro_dev *z,
                return -ENOMEM;
        }
 
-       priv = netdev_priv(dev);
-
        r1->name = dev->name;
        r2->name = dev->name;
 
index a2bd91e3d302acce0d727cc3ef1eda27e56040cb..a78e4c13695980e295ead2c8d85fd6f6a351f5c8 100644 (file)
@@ -1229,7 +1229,7 @@ static int au1000_probe(struct platform_device *pdev)
        dev->base_addr = base->start;
        dev->irq = irq;
        dev->netdev_ops = &au1000_netdev_ops;
-       SET_ETHTOOL_OPS(dev, &au1000_ethtool_ops);
+       dev->ethtool_ops = &au1000_ethtool_ops;
        dev->watchdog_timeo = ETH_TX_TIMEOUT;
 
        /*
index 47ce57c2c8939fd458decbb74042d8179b31c558..6c9de117ffc68f2941d20f465161460f806f2d04 100644 (file)
@@ -27,9 +27,9 @@
 
 #include "hplance.h"
 
-/* We have 16834 bytes of RAM for the init block and buffers. This places
+/* We have 16392 bytes of RAM for the init block and buffers. This places
  * an upper limit on the number of buffers we can use. NetBSD uses 8 Rx
- * buffers and 2 Tx buffers.
+ * buffers and 2 Tx buffers, it takes (8 + 2) * 1544 bytes.
  */
 #define LANCE_LOG_TX_BUFFERS 1
 #define LANCE_LOG_RX_BUFFERS 3
index 0e8399dec0543736d8dc3ef732cb18817eca7892..0660ac5846bbef3e07d99c25a3c2d63f6eff9e12 100644 (file)
@@ -26,9 +26,9 @@
 #include <asm/pgtable.h>
 #include <asm/mvme147hw.h>
 
-/* We have 16834 bytes of RAM for the init block and buffers. This places
+/* We have 32K of RAM for the init block and buffers. This places
  * an upper limit on the number of buffers we can use. NetBSD uses 8 Rx
- * buffers and 2 Tx buffers.
+ * buffers and 2 Tx buffers, it takes (8 + 2) * 1544 bytes.
  */
 #define LANCE_LOG_TX_BUFFERS 1
 #define LANCE_LOG_RX_BUFFERS 3
@@ -111,7 +111,7 @@ struct net_device * __init mvme147lance_probe(int unit)
               dev->dev_addr);
 
        lp = netdev_priv(dev);
-       lp->ram = __get_dma_pages(GFP_ATOMIC, 3);       /* 16K */
+       lp->ram = __get_dma_pages(GFP_ATOMIC, 3);       /* 32K */
        if (!lp->ram) {
                printk("%s: No memory for LANCE buffers\n", dev->name);
                free_netdev(dev);
index 08569fe2b182c2bef930ef582320b9ccb9cfc952..abf3b1581c82a2eacbf7074b3c9db9ca12fbef19 100644 (file)
@@ -457,7 +457,7 @@ static int nmclan_probe(struct pcmcia_device *link)
     lp->tx_free_frames=AM2150_MAX_TX_FRAMES;
 
     dev->netdev_ops = &mace_netdev_ops;
-    SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
+    dev->ethtool_ops = &netdev_ethtool_ops;
     dev->watchdog_timeo = TX_TIMEOUT;
 
     return nmclan_config(link);
diff --git a/drivers/net/ethernet/amd/xgbe/Makefile b/drivers/net/ethernet/amd/xgbe/Makefile
new file mode 100644 (file)
index 0000000..26cf9af
--- /dev/null
@@ -0,0 +1,6 @@
+obj-$(CONFIG_AMD_XGBE) += amd-xgbe.o
+
+amd-xgbe-objs := xgbe-main.o xgbe-drv.o xgbe-dev.o \
+                xgbe-desc.o xgbe-ethtool.o xgbe-mdio.o
+
+amd-xgbe-$(CONFIG_DEBUG_FS) += xgbe-debugfs.o
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h
new file mode 100644 (file)
index 0000000..bf462ee
--- /dev/null
@@ -0,0 +1,1007 @@
+/*
+ * AMD 10Gb Ethernet driver
+ *
+ * This file is available to you under your choice of the following two
+ * licenses:
+ *
+ * License 1: GPLv2
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ *
+ * This file is free software; you may copy, redistribute 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 file 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, see <http://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and the Software, to deal in the Software
+ *     without restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ *     of the Software, and to permit persons to whom the Software is furnished
+ *     to do so, subject to the following conditions:
+ *
+ *     The above copyright notice and this permission notice shall be included
+ *     in all copies or substantial portions of the Software.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ *
+ *
+ * License 2: Modified BSD
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ * 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 of Advanced Micro Devices, Inc. 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 <COPYRIGHT HOLDER> 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.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and the Software, to deal in the Software
+ *     without restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ *     of the Software, and to permit persons to whom the Software is furnished
+ *     to do so, subject to the following conditions:
+ *
+ *     The above copyright notice and this permission notice shall be included
+ *     in all copies or substantial portions of the Software.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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 __XGBE_COMMON_H__
+#define __XGBE_COMMON_H__
+
+/* DMA register offsets */
+#define DMA_MR                         0x3000
+#define DMA_SBMR                       0x3004
+#define DMA_ISR                                0x3008
+#define DMA_AXIARCR                    0x3010
+#define DMA_AXIAWCR                    0x3018
+#define DMA_DSR0                       0x3020
+#define DMA_DSR1                       0x3024
+#define DMA_DSR2                       0x3028
+#define DMA_DSR3                       0x302c
+#define DMA_DSR4                       0x3030
+
+/* DMA register entry bit positions and sizes */
+#define DMA_AXIARCR_DRC_INDEX          0
+#define DMA_AXIARCR_DRC_WIDTH          4
+#define DMA_AXIARCR_DRD_INDEX          4
+#define DMA_AXIARCR_DRD_WIDTH          2
+#define DMA_AXIARCR_TEC_INDEX          8
+#define DMA_AXIARCR_TEC_WIDTH          4
+#define DMA_AXIARCR_TED_INDEX          12
+#define DMA_AXIARCR_TED_WIDTH          2
+#define DMA_AXIARCR_THC_INDEX          16
+#define DMA_AXIARCR_THC_WIDTH          4
+#define DMA_AXIARCR_THD_INDEX          20
+#define DMA_AXIARCR_THD_WIDTH          2
+#define DMA_AXIAWCR_DWC_INDEX          0
+#define DMA_AXIAWCR_DWC_WIDTH          4
+#define DMA_AXIAWCR_DWD_INDEX          4
+#define DMA_AXIAWCR_DWD_WIDTH          2
+#define DMA_AXIAWCR_RPC_INDEX          8
+#define DMA_AXIAWCR_RPC_WIDTH          4
+#define DMA_AXIAWCR_RPD_INDEX          12
+#define DMA_AXIAWCR_RPD_WIDTH          2
+#define DMA_AXIAWCR_RHC_INDEX          16
+#define DMA_AXIAWCR_RHC_WIDTH          4
+#define DMA_AXIAWCR_RHD_INDEX          20
+#define DMA_AXIAWCR_RHD_WIDTH          2
+#define DMA_AXIAWCR_TDC_INDEX          24
+#define DMA_AXIAWCR_TDC_WIDTH          4
+#define DMA_AXIAWCR_TDD_INDEX          28
+#define DMA_AXIAWCR_TDD_WIDTH          2
+#define DMA_DSR0_RPS_INDEX             8
+#define DMA_DSR0_RPS_WIDTH             4
+#define DMA_DSR0_TPS_INDEX             12
+#define DMA_DSR0_TPS_WIDTH             4
+#define DMA_ISR_MACIS_INDEX            17
+#define DMA_ISR_MACIS_WIDTH            1
+#define DMA_ISR_MTLIS_INDEX            16
+#define DMA_ISR_MTLIS_WIDTH            1
+#define DMA_MR_SWR_INDEX               0
+#define DMA_MR_SWR_WIDTH               1
+#define DMA_SBMR_EAME_INDEX            11
+#define DMA_SBMR_EAME_WIDTH            1
+#define DMA_SBMR_UNDEF_INDEX           0
+#define DMA_SBMR_UNDEF_WIDTH           1
+
+/* DMA channel register offsets
+ *   Multiple channels can be active.  The first channel has registers
+ *   that begin at 0x3100.  Each subsequent channel has registers that
+ *   are accessed using an offset of 0x80 from the previous channel.
+ */
+#define DMA_CH_BASE                    0x3100
+#define DMA_CH_INC                     0x80
+
+#define DMA_CH_CR                      0x00
+#define DMA_CH_TCR                     0x04
+#define DMA_CH_RCR                     0x08
+#define DMA_CH_TDLR_HI                 0x10
+#define DMA_CH_TDLR_LO                 0x14
+#define DMA_CH_RDLR_HI                 0x18
+#define DMA_CH_RDLR_LO                 0x1c
+#define DMA_CH_TDTR_LO                 0x24
+#define DMA_CH_RDTR_LO                 0x2c
+#define DMA_CH_TDRLR                   0x30
+#define DMA_CH_RDRLR                   0x34
+#define DMA_CH_IER                     0x38
+#define DMA_CH_RIWT                    0x3c
+#define DMA_CH_CATDR_LO                        0x44
+#define DMA_CH_CARDR_LO                        0x4c
+#define DMA_CH_CATBR_HI                        0x50
+#define DMA_CH_CATBR_LO                        0x54
+#define DMA_CH_CARBR_HI                        0x58
+#define DMA_CH_CARBR_LO                        0x5c
+#define DMA_CH_SR                      0x60
+
+/* DMA channel register entry bit positions and sizes */
+#define DMA_CH_CR_PBLX8_INDEX          16
+#define DMA_CH_CR_PBLX8_WIDTH          1
+#define DMA_CH_IER_AIE_INDEX           15
+#define DMA_CH_IER_AIE_WIDTH           1
+#define DMA_CH_IER_FBEE_INDEX          12
+#define DMA_CH_IER_FBEE_WIDTH          1
+#define DMA_CH_IER_NIE_INDEX           16
+#define DMA_CH_IER_NIE_WIDTH           1
+#define DMA_CH_IER_RBUE_INDEX          7
+#define DMA_CH_IER_RBUE_WIDTH          1
+#define DMA_CH_IER_RIE_INDEX           6
+#define DMA_CH_IER_RIE_WIDTH           1
+#define DMA_CH_IER_RSE_INDEX           8
+#define DMA_CH_IER_RSE_WIDTH           1
+#define DMA_CH_IER_TBUE_INDEX          2
+#define DMA_CH_IER_TBUE_WIDTH          1
+#define DMA_CH_IER_TIE_INDEX           0
+#define DMA_CH_IER_TIE_WIDTH           1
+#define DMA_CH_IER_TXSE_INDEX          1
+#define DMA_CH_IER_TXSE_WIDTH          1
+#define DMA_CH_RCR_PBL_INDEX           16
+#define DMA_CH_RCR_PBL_WIDTH           6
+#define DMA_CH_RCR_RBSZ_INDEX          1
+#define DMA_CH_RCR_RBSZ_WIDTH          14
+#define DMA_CH_RCR_SR_INDEX            0
+#define DMA_CH_RCR_SR_WIDTH            1
+#define DMA_CH_RIWT_RWT_INDEX          0
+#define DMA_CH_RIWT_RWT_WIDTH          8
+#define DMA_CH_SR_FBE_INDEX            12
+#define DMA_CH_SR_FBE_WIDTH            1
+#define DMA_CH_SR_RBU_INDEX            7
+#define DMA_CH_SR_RBU_WIDTH            1
+#define DMA_CH_SR_RI_INDEX             6
+#define DMA_CH_SR_RI_WIDTH             1
+#define DMA_CH_SR_RPS_INDEX            8
+#define DMA_CH_SR_RPS_WIDTH            1
+#define DMA_CH_SR_TBU_INDEX            2
+#define DMA_CH_SR_TBU_WIDTH            1
+#define DMA_CH_SR_TI_INDEX             0
+#define DMA_CH_SR_TI_WIDTH             1
+#define DMA_CH_SR_TPS_INDEX            1
+#define DMA_CH_SR_TPS_WIDTH            1
+#define DMA_CH_TCR_OSP_INDEX           4
+#define DMA_CH_TCR_OSP_WIDTH           1
+#define DMA_CH_TCR_PBL_INDEX           16
+#define DMA_CH_TCR_PBL_WIDTH           6
+#define DMA_CH_TCR_ST_INDEX            0
+#define DMA_CH_TCR_ST_WIDTH            1
+#define DMA_CH_TCR_TSE_INDEX           12
+#define DMA_CH_TCR_TSE_WIDTH           1
+
+/* DMA channel register values */
+#define DMA_OSP_DISABLE                        0x00
+#define DMA_OSP_ENABLE                 0x01
+#define DMA_PBL_1                      1
+#define DMA_PBL_2                      2
+#define DMA_PBL_4                      4
+#define DMA_PBL_8                      8
+#define DMA_PBL_16                     16
+#define DMA_PBL_32                     32
+#define DMA_PBL_64                     64      /* 8 x 8 */
+#define DMA_PBL_128                    128     /* 8 x 16 */
+#define DMA_PBL_256                    256     /* 8 x 32 */
+#define DMA_PBL_X8_DISABLE             0x00
+#define DMA_PBL_X8_ENABLE              0x01
+
+
+/* MAC register offsets */
+#define MAC_TCR                                0x0000
+#define MAC_RCR                                0x0004
+#define MAC_PFR                                0x0008
+#define MAC_WTR                                0x000c
+#define MAC_HTR0                       0x0010
+#define MAC_HTR1                       0x0014
+#define MAC_HTR2                       0x0018
+#define MAC_HTR3                       0x001c
+#define MAC_HTR4                       0x0020
+#define MAC_HTR5                       0x0024
+#define MAC_HTR6                       0x0028
+#define MAC_HTR7                       0x002c
+#define MAC_VLANTR                     0x0050
+#define MAC_VLANHTR                    0x0058
+#define MAC_VLANIR                     0x0060
+#define MAC_IVLANIR                    0x0064
+#define MAC_RETMR                      0x006c
+#define MAC_Q0TFCR                     0x0070
+#define MAC_RFCR                       0x0090
+#define MAC_RQC0R                      0x00a0
+#define MAC_RQC1R                      0x00a4
+#define MAC_RQC2R                      0x00a8
+#define MAC_RQC3R                      0x00ac
+#define MAC_ISR                                0x00b0
+#define MAC_IER                                0x00b4
+#define MAC_RTSR                       0x00b8
+#define MAC_PMTCSR                     0x00c0
+#define MAC_RWKPFR                     0x00c4
+#define MAC_LPICSR                     0x00d0
+#define MAC_LPITCR                     0x00d4
+#define MAC_VR                         0x0110
+#define MAC_DR                         0x0114
+#define MAC_HWF0R                      0x011c
+#define MAC_HWF1R                      0x0120
+#define MAC_HWF2R                      0x0124
+#define MAC_GPIOCR                     0x0278
+#define MAC_GPIOSR                     0x027c
+#define MAC_MACA0HR                    0x0300
+#define MAC_MACA0LR                    0x0304
+#define MAC_MACA1HR                    0x0308
+#define MAC_MACA1LR                    0x030c
+
+#define MAC_QTFCR_INC                  4
+#define MAC_MACA_INC                   4
+
+/* MAC register entry bit positions and sizes */
+#define MAC_HWF0R_ADDMACADRSEL_INDEX   18
+#define MAC_HWF0R_ADDMACADRSEL_WIDTH   5
+#define MAC_HWF0R_ARPOFFSEL_INDEX      9
+#define MAC_HWF0R_ARPOFFSEL_WIDTH      1
+#define MAC_HWF0R_EEESEL_INDEX         13
+#define MAC_HWF0R_EEESEL_WIDTH         1
+#define MAC_HWF0R_GMIISEL_INDEX                1
+#define MAC_HWF0R_GMIISEL_WIDTH                1
+#define MAC_HWF0R_MGKSEL_INDEX         7
+#define MAC_HWF0R_MGKSEL_WIDTH         1
+#define MAC_HWF0R_MMCSEL_INDEX         8
+#define MAC_HWF0R_MMCSEL_WIDTH         1
+#define MAC_HWF0R_RWKSEL_INDEX         6
+#define MAC_HWF0R_RWKSEL_WIDTH         1
+#define MAC_HWF0R_RXCOESEL_INDEX       16
+#define MAC_HWF0R_RXCOESEL_WIDTH       1
+#define MAC_HWF0R_SAVLANINS_INDEX      27
+#define MAC_HWF0R_SAVLANINS_WIDTH      1
+#define MAC_HWF0R_SMASEL_INDEX         5
+#define MAC_HWF0R_SMASEL_WIDTH         1
+#define MAC_HWF0R_TSSEL_INDEX          12
+#define MAC_HWF0R_TSSEL_WIDTH          1
+#define MAC_HWF0R_TSSTSSEL_INDEX       25
+#define MAC_HWF0R_TSSTSSEL_WIDTH       2
+#define MAC_HWF0R_TXCOESEL_INDEX       14
+#define MAC_HWF0R_TXCOESEL_WIDTH       1
+#define MAC_HWF0R_VLHASH_INDEX         4
+#define MAC_HWF0R_VLHASH_WIDTH         1
+#define MAC_HWF1R_ADVTHWORD_INDEX      13
+#define MAC_HWF1R_ADVTHWORD_WIDTH      1
+#define MAC_HWF1R_DBGMEMA_INDEX                19
+#define MAC_HWF1R_DBGMEMA_WIDTH                1
+#define MAC_HWF1R_DCBEN_INDEX          16
+#define MAC_HWF1R_DCBEN_WIDTH          1
+#define MAC_HWF1R_HASHTBLSZ_INDEX      24
+#define MAC_HWF1R_HASHTBLSZ_WIDTH      3
+#define MAC_HWF1R_L3L4FNUM_INDEX       27
+#define MAC_HWF1R_L3L4FNUM_WIDTH       4
+#define MAC_HWF1R_RSSEN_INDEX          20
+#define MAC_HWF1R_RSSEN_WIDTH          1
+#define MAC_HWF1R_RXFIFOSIZE_INDEX     0
+#define MAC_HWF1R_RXFIFOSIZE_WIDTH     5
+#define MAC_HWF1R_SPHEN_INDEX          17
+#define MAC_HWF1R_SPHEN_WIDTH          1
+#define MAC_HWF1R_TSOEN_INDEX          18
+#define MAC_HWF1R_TSOEN_WIDTH          1
+#define MAC_HWF1R_TXFIFOSIZE_INDEX     6
+#define MAC_HWF1R_TXFIFOSIZE_WIDTH     5
+#define MAC_HWF2R_AUXSNAPNUM_INDEX     28
+#define MAC_HWF2R_AUXSNAPNUM_WIDTH     3
+#define MAC_HWF2R_PPSOUTNUM_INDEX      24
+#define MAC_HWF2R_PPSOUTNUM_WIDTH      3
+#define MAC_HWF2R_RXCHCNT_INDEX                12
+#define MAC_HWF2R_RXCHCNT_WIDTH                4
+#define MAC_HWF2R_RXQCNT_INDEX         0
+#define MAC_HWF2R_RXQCNT_WIDTH         4
+#define MAC_HWF2R_TXCHCNT_INDEX                18
+#define MAC_HWF2R_TXCHCNT_WIDTH                4
+#define MAC_HWF2R_TXQCNT_INDEX         6
+#define MAC_HWF2R_TXQCNT_WIDTH         4
+#define MAC_ISR_MMCRXIS_INDEX          9
+#define MAC_ISR_MMCRXIS_WIDTH          1
+#define MAC_ISR_MMCTXIS_INDEX          10
+#define MAC_ISR_MMCTXIS_WIDTH          1
+#define MAC_ISR_PMTIS_INDEX            4
+#define MAC_ISR_PMTIS_WIDTH            1
+#define MAC_MACA1HR_AE_INDEX           31
+#define MAC_MACA1HR_AE_WIDTH           1
+#define MAC_PFR_HMC_INDEX              2
+#define MAC_PFR_HMC_WIDTH              1
+#define MAC_PFR_HUC_INDEX              1
+#define MAC_PFR_HUC_WIDTH              1
+#define MAC_PFR_PM_INDEX               4
+#define MAC_PFR_PM_WIDTH               1
+#define MAC_PFR_PR_INDEX               0
+#define MAC_PFR_PR_WIDTH               1
+#define MAC_PMTCSR_MGKPKTEN_INDEX      1
+#define MAC_PMTCSR_MGKPKTEN_WIDTH      1
+#define MAC_PMTCSR_PWRDWN_INDEX                0
+#define MAC_PMTCSR_PWRDWN_WIDTH                1
+#define MAC_PMTCSR_RWKFILTRST_INDEX    31
+#define MAC_PMTCSR_RWKFILTRST_WIDTH    1
+#define MAC_PMTCSR_RWKPKTEN_INDEX      2
+#define MAC_PMTCSR_RWKPKTEN_WIDTH      1
+#define MAC_Q0TFCR_PT_INDEX            16
+#define MAC_Q0TFCR_PT_WIDTH            16
+#define MAC_Q0TFCR_TFE_INDEX           1
+#define MAC_Q0TFCR_TFE_WIDTH           1
+#define MAC_RCR_ACS_INDEX              1
+#define MAC_RCR_ACS_WIDTH              1
+#define MAC_RCR_CST_INDEX              2
+#define MAC_RCR_CST_WIDTH              1
+#define MAC_RCR_DCRCC_INDEX            3
+#define MAC_RCR_DCRCC_WIDTH            1
+#define MAC_RCR_IPC_INDEX              9
+#define MAC_RCR_IPC_WIDTH              1
+#define MAC_RCR_JE_INDEX               8
+#define MAC_RCR_JE_WIDTH               1
+#define MAC_RCR_LM_INDEX               10
+#define MAC_RCR_LM_WIDTH               1
+#define MAC_RCR_RE_INDEX               0
+#define MAC_RCR_RE_WIDTH               1
+#define MAC_RFCR_RFE_INDEX             0
+#define MAC_RFCR_RFE_WIDTH             1
+#define MAC_RQC0R_RXQ0EN_INDEX         0
+#define MAC_RQC0R_RXQ0EN_WIDTH         2
+#define MAC_TCR_SS_INDEX               29
+#define MAC_TCR_SS_WIDTH               2
+#define MAC_TCR_TE_INDEX               0
+#define MAC_TCR_TE_WIDTH               1
+#define MAC_VLANTR_DOVLTC_INDEX                20
+#define MAC_VLANTR_DOVLTC_WIDTH                1
+#define MAC_VLANTR_ERSVLM_INDEX                19
+#define MAC_VLANTR_ERSVLM_WIDTH                1
+#define MAC_VLANTR_ESVL_INDEX          18
+#define MAC_VLANTR_ESVL_WIDTH          1
+#define MAC_VLANTR_EVLS_INDEX          21
+#define MAC_VLANTR_EVLS_WIDTH          2
+#define MAC_VLANTR_EVLRXS_INDEX                24
+#define MAC_VLANTR_EVLRXS_WIDTH                1
+#define MAC_VR_DEVID_INDEX             8
+#define MAC_VR_DEVID_WIDTH             8
+#define MAC_VR_SNPSVER_INDEX           0
+#define MAC_VR_SNPSVER_WIDTH           8
+#define MAC_VR_USERVER_INDEX           16
+#define MAC_VR_USERVER_WIDTH           8
+
+/* MMC register offsets */
+#define MMC_CR                         0x0800
+#define MMC_RISR                       0x0804
+#define MMC_TISR                       0x0808
+#define MMC_RIER                       0x080c
+#define MMC_TIER                       0x0810
+#define MMC_TXOCTETCOUNT_GB_LO         0x0814
+#define MMC_TXOCTETCOUNT_GB_HI         0x0818
+#define MMC_TXFRAMECOUNT_GB_LO         0x081c
+#define MMC_TXFRAMECOUNT_GB_HI         0x0820
+#define MMC_TXBROADCASTFRAMES_G_LO     0x0824
+#define MMC_TXBROADCASTFRAMES_G_HI     0x0828
+#define MMC_TXMULTICASTFRAMES_G_LO     0x082c
+#define MMC_TXMULTICASTFRAMES_G_HI     0x0830
+#define MMC_TX64OCTETS_GB_LO           0x0834
+#define MMC_TX64OCTETS_GB_HI           0x0838
+#define MMC_TX65TO127OCTETS_GB_LO      0x083c
+#define MMC_TX65TO127OCTETS_GB_HI      0x0840
+#define MMC_TX128TO255OCTETS_GB_LO     0x0844
+#define MMC_TX128TO255OCTETS_GB_HI     0x0848
+#define MMC_TX256TO511OCTETS_GB_LO     0x084c
+#define MMC_TX256TO511OCTETS_GB_HI     0x0850
+#define MMC_TX512TO1023OCTETS_GB_LO    0x0854
+#define MMC_TX512TO1023OCTETS_GB_HI    0x0858
+#define MMC_TX1024TOMAXOCTETS_GB_LO    0x085c
+#define MMC_TX1024TOMAXOCTETS_GB_HI    0x0860
+#define MMC_TXUNICASTFRAMES_GB_LO      0x0864
+#define MMC_TXUNICASTFRAMES_GB_HI      0x0868
+#define MMC_TXMULTICASTFRAMES_GB_LO    0x086c
+#define MMC_TXMULTICASTFRAMES_GB_HI    0x0870
+#define MMC_TXBROADCASTFRAMES_GB_LO    0x0874
+#define MMC_TXBROADCASTFRAMES_GB_HI    0x0878
+#define MMC_TXUNDERFLOWERROR_LO                0x087c
+#define MMC_TXUNDERFLOWERROR_HI                0x0880
+#define MMC_TXOCTETCOUNT_G_LO          0x0884
+#define MMC_TXOCTETCOUNT_G_HI          0x0888
+#define MMC_TXFRAMECOUNT_G_LO          0x088c
+#define MMC_TXFRAMECOUNT_G_HI          0x0890
+#define MMC_TXPAUSEFRAMES_LO           0x0894
+#define MMC_TXPAUSEFRAMES_HI           0x0898
+#define MMC_TXVLANFRAMES_G_LO          0x089c
+#define MMC_TXVLANFRAMES_G_HI          0x08a0
+#define MMC_RXFRAMECOUNT_GB_LO         0x0900
+#define MMC_RXFRAMECOUNT_GB_HI         0x0904
+#define MMC_RXOCTETCOUNT_GB_LO         0x0908
+#define MMC_RXOCTETCOUNT_GB_HI         0x090c
+#define MMC_RXOCTETCOUNT_G_LO          0x0910
+#define MMC_RXOCTETCOUNT_G_HI          0x0914
+#define MMC_RXBROADCASTFRAMES_G_LO     0x0918
+#define MMC_RXBROADCASTFRAMES_G_HI     0x091c
+#define MMC_RXMULTICASTFRAMES_G_LO     0x0920
+#define MMC_RXMULTICASTFRAMES_G_HI     0x0924
+#define MMC_RXCRCERROR_LO              0x0928
+#define MMC_RXCRCERROR_HI              0x092c
+#define MMC_RXRUNTERROR                        0x0930
+#define MMC_RXJABBERERROR              0x0934
+#define MMC_RXUNDERSIZE_G              0x0938
+#define MMC_RXOVERSIZE_G               0x093c
+#define MMC_RX64OCTETS_GB_LO           0x0940
+#define MMC_RX64OCTETS_GB_HI           0x0944
+#define MMC_RX65TO127OCTETS_GB_LO      0x0948
+#define MMC_RX65TO127OCTETS_GB_HI      0x094c
+#define MMC_RX128TO255OCTETS_GB_LO     0x0950
+#define MMC_RX128TO255OCTETS_GB_HI     0x0954
+#define MMC_RX256TO511OCTETS_GB_LO     0x0958
+#define MMC_RX256TO511OCTETS_GB_HI     0x095c
+#define MMC_RX512TO1023OCTETS_GB_LO    0x0960
+#define MMC_RX512TO1023OCTETS_GB_HI    0x0964
+#define MMC_RX1024TOMAXOCTETS_GB_LO    0x0968
+#define MMC_RX1024TOMAXOCTETS_GB_HI    0x096c
+#define MMC_RXUNICASTFRAMES_G_LO       0x0970
+#define MMC_RXUNICASTFRAMES_G_HI       0x0974
+#define MMC_RXLENGTHERROR_LO           0x0978
+#define MMC_RXLENGTHERROR_HI           0x097c
+#define MMC_RXOUTOFRANGETYPE_LO                0x0980
+#define MMC_RXOUTOFRANGETYPE_HI                0x0984
+#define MMC_RXPAUSEFRAMES_LO           0x0988
+#define MMC_RXPAUSEFRAMES_HI           0x098c
+#define MMC_RXFIFOOVERFLOW_LO          0x0990
+#define MMC_RXFIFOOVERFLOW_HI          0x0994
+#define MMC_RXVLANFRAMES_GB_LO         0x0998
+#define MMC_RXVLANFRAMES_GB_HI         0x099c
+#define MMC_RXWATCHDOGERROR            0x09a0
+
+/* MMC register entry bit positions and sizes */
+#define MMC_CR_CR_INDEX                                0
+#define MMC_CR_CR_WIDTH                                1
+#define MMC_CR_CSR_INDEX                       1
+#define MMC_CR_CSR_WIDTH                       1
+#define MMC_CR_ROR_INDEX                       2
+#define MMC_CR_ROR_WIDTH                       1
+#define MMC_CR_MCF_INDEX                       3
+#define MMC_CR_MCF_WIDTH                       1
+#define MMC_CR_MCT_INDEX                       4
+#define MMC_CR_MCT_WIDTH                       2
+#define MMC_RIER_ALL_INTERRUPTS_INDEX          0
+#define MMC_RIER_ALL_INTERRUPTS_WIDTH          23
+#define MMC_RISR_RXFRAMECOUNT_GB_INDEX         0
+#define MMC_RISR_RXFRAMECOUNT_GB_WIDTH         1
+#define MMC_RISR_RXOCTETCOUNT_GB_INDEX         1
+#define MMC_RISR_RXOCTETCOUNT_GB_WIDTH         1
+#define MMC_RISR_RXOCTETCOUNT_G_INDEX          2
+#define MMC_RISR_RXOCTETCOUNT_G_WIDTH          1
+#define MMC_RISR_RXBROADCASTFRAMES_G_INDEX     3
+#define MMC_RISR_RXBROADCASTFRAMES_G_WIDTH     1
+#define MMC_RISR_RXMULTICASTFRAMES_G_INDEX     4
+#define MMC_RISR_RXMULTICASTFRAMES_G_WIDTH     1
+#define MMC_RISR_RXCRCERROR_INDEX              5
+#define MMC_RISR_RXCRCERROR_WIDTH              1
+#define MMC_RISR_RXRUNTERROR_INDEX             6
+#define MMC_RISR_RXRUNTERROR_WIDTH             1
+#define MMC_RISR_RXJABBERERROR_INDEX           7
+#define MMC_RISR_RXJABBERERROR_WIDTH           1
+#define MMC_RISR_RXUNDERSIZE_G_INDEX           8
+#define MMC_RISR_RXUNDERSIZE_G_WIDTH           1
+#define MMC_RISR_RXOVERSIZE_G_INDEX            9
+#define MMC_RISR_RXOVERSIZE_G_WIDTH            1
+#define MMC_RISR_RX64OCTETS_GB_INDEX           10
+#define MMC_RISR_RX64OCTETS_GB_WIDTH           1
+#define MMC_RISR_RX65TO127OCTETS_GB_INDEX      11
+#define MMC_RISR_RX65TO127OCTETS_GB_WIDTH      1
+#define MMC_RISR_RX128TO255OCTETS_GB_INDEX     12
+#define MMC_RISR_RX128TO255OCTETS_GB_WIDTH     1
+#define MMC_RISR_RX256TO511OCTETS_GB_INDEX     13
+#define MMC_RISR_RX256TO511OCTETS_GB_WIDTH     1
+#define MMC_RISR_RX512TO1023OCTETS_GB_INDEX    14
+#define MMC_RISR_RX512TO1023OCTETS_GB_WIDTH    1
+#define MMC_RISR_RX1024TOMAXOCTETS_GB_INDEX    15
+#define MMC_RISR_RX1024TOMAXOCTETS_GB_WIDTH    1
+#define MMC_RISR_RXUNICASTFRAMES_G_INDEX       16
+#define MMC_RISR_RXUNICASTFRAMES_G_WIDTH       1
+#define MMC_RISR_RXLENGTHERROR_INDEX           17
+#define MMC_RISR_RXLENGTHERROR_WIDTH           1
+#define MMC_RISR_RXOUTOFRANGETYPE_INDEX                18
+#define MMC_RISR_RXOUTOFRANGETYPE_WIDTH                1
+#define MMC_RISR_RXPAUSEFRAMES_INDEX           19
+#define MMC_RISR_RXPAUSEFRAMES_WIDTH           1
+#define MMC_RISR_RXFIFOOVERFLOW_INDEX          20
+#define MMC_RISR_RXFIFOOVERFLOW_WIDTH          1
+#define MMC_RISR_RXVLANFRAMES_GB_INDEX         21
+#define MMC_RISR_RXVLANFRAMES_GB_WIDTH         1
+#define MMC_RISR_RXWATCHDOGERROR_INDEX         22
+#define MMC_RISR_RXWATCHDOGERROR_WIDTH         1
+#define MMC_TIER_ALL_INTERRUPTS_INDEX          0
+#define MMC_TIER_ALL_INTERRUPTS_WIDTH          18
+#define MMC_TISR_TXOCTETCOUNT_GB_INDEX         0
+#define MMC_TISR_TXOCTETCOUNT_GB_WIDTH         1
+#define MMC_TISR_TXFRAMECOUNT_GB_INDEX         1
+#define MMC_TISR_TXFRAMECOUNT_GB_WIDTH         1
+#define MMC_TISR_TXBROADCASTFRAMES_G_INDEX     2
+#define MMC_TISR_TXBROADCASTFRAMES_G_WIDTH     1
+#define MMC_TISR_TXMULTICASTFRAMES_G_INDEX     3
+#define MMC_TISR_TXMULTICASTFRAMES_G_WIDTH     1
+#define MMC_TISR_TX64OCTETS_GB_INDEX           4
+#define MMC_TISR_TX64OCTETS_GB_WIDTH           1
+#define MMC_TISR_TX65TO127OCTETS_GB_INDEX      5
+#define MMC_TISR_TX65TO127OCTETS_GB_WIDTH      1
+#define MMC_TISR_TX128TO255OCTETS_GB_INDEX     6
+#define MMC_TISR_TX128TO255OCTETS_GB_WIDTH     1
+#define MMC_TISR_TX256TO511OCTETS_GB_INDEX     7
+#define MMC_TISR_TX256TO511OCTETS_GB_WIDTH     1
+#define MMC_TISR_TX512TO1023OCTETS_GB_INDEX    8
+#define MMC_TISR_TX512TO1023OCTETS_GB_WIDTH    1
+#define MMC_TISR_TX1024TOMAXOCTETS_GB_INDEX    9
+#define MMC_TISR_TX1024TOMAXOCTETS_GB_WIDTH    1
+#define MMC_TISR_TXUNICASTFRAMES_GB_INDEX      10
+#define MMC_TISR_TXUNICASTFRAMES_GB_WIDTH      1
+#define MMC_TISR_TXMULTICASTFRAMES_GB_INDEX    11
+#define MMC_TISR_TXMULTICASTFRAMES_GB_WIDTH    1
+#define MMC_TISR_TXBROADCASTFRAMES_GB_INDEX    12
+#define MMC_TISR_TXBROADCASTFRAMES_GB_WIDTH    1
+#define MMC_TISR_TXUNDERFLOWERROR_INDEX                13
+#define MMC_TISR_TXUNDERFLOWERROR_WIDTH                1
+#define MMC_TISR_TXOCTETCOUNT_G_INDEX          14
+#define MMC_TISR_TXOCTETCOUNT_G_WIDTH          1
+#define MMC_TISR_TXFRAMECOUNT_G_INDEX          15
+#define MMC_TISR_TXFRAMECOUNT_G_WIDTH          1
+#define MMC_TISR_TXPAUSEFRAMES_INDEX           16
+#define MMC_TISR_TXPAUSEFRAMES_WIDTH           1
+#define MMC_TISR_TXVLANFRAMES_G_INDEX          17
+#define MMC_TISR_TXVLANFRAMES_G_WIDTH          1
+
+/* MTL register offsets */
+#define MTL_OMR                                0x1000
+#define MTL_FDCR                       0x1008
+#define MTL_FDSR                       0x100c
+#define MTL_FDDR                       0x1010
+#define MTL_ISR                                0x1020
+#define MTL_RQDCM0R                    0x1030
+#define MTL_TCPM0R                     0x1040
+#define MTL_TCPM1R                     0x1044
+
+#define MTL_RQDCM_INC                  4
+#define MTL_RQDCM_Q_PER_REG            4
+
+/* MTL register entry bit positions and sizes */
+#define MTL_OMR_ETSALG_INDEX           5
+#define MTL_OMR_ETSALG_WIDTH           2
+#define MTL_OMR_RAA_INDEX              2
+#define MTL_OMR_RAA_WIDTH              1
+
+/* MTL queue register offsets
+ *   Multiple queues can be active.  The first queue has registers
+ *   that begin at 0x1100.  Each subsequent queue has registers that
+ *   are accessed using an offset of 0x80 from the previous queue.
+ */
+#define MTL_Q_BASE                     0x1100
+#define MTL_Q_INC                      0x80
+
+#define MTL_Q_TQOMR                    0x00
+#define MTL_Q_TQUR                     0x04
+#define MTL_Q_TQDR                     0x08
+#define MTL_Q_TCECR                    0x10
+#define MTL_Q_TCESR                    0x14
+#define MTL_Q_TCQWR                    0x18
+#define MTL_Q_RQOMR                    0x40
+#define MTL_Q_RQMPOCR                  0x44
+#define MTL_Q_RQDR                     0x4c
+#define MTL_Q_IER                      0x70
+#define MTL_Q_ISR                      0x74
+
+/* MTL queue register entry bit positions and sizes */
+#define MTL_Q_TCQWR_QW_INDEX           0
+#define MTL_Q_TCQWR_QW_WIDTH           21
+#define MTL_Q_RQOMR_EHFC_INDEX         7
+#define MTL_Q_RQOMR_EHFC_WIDTH         1
+#define MTL_Q_RQOMR_RFA_INDEX          8
+#define MTL_Q_RQOMR_RFA_WIDTH          3
+#define MTL_Q_RQOMR_RFD_INDEX          13
+#define MTL_Q_RQOMR_RFD_WIDTH          3
+#define MTL_Q_RQOMR_RQS_INDEX          16
+#define MTL_Q_RQOMR_RQS_WIDTH          9
+#define MTL_Q_RQOMR_RSF_INDEX          5
+#define MTL_Q_RQOMR_RSF_WIDTH          1
+#define MTL_Q_RQOMR_RTC_INDEX          0
+#define MTL_Q_RQOMR_RTC_WIDTH          2
+#define MTL_Q_TQOMR_FTQ_INDEX          0
+#define MTL_Q_TQOMR_FTQ_WIDTH          1
+#define MTL_Q_TQOMR_TQS_INDEX          16
+#define MTL_Q_TQOMR_TQS_WIDTH          10
+#define MTL_Q_TQOMR_TSF_INDEX          1
+#define MTL_Q_TQOMR_TSF_WIDTH          1
+#define MTL_Q_TQOMR_TTC_INDEX          4
+#define MTL_Q_TQOMR_TTC_WIDTH          3
+#define MTL_Q_TQOMR_TXQEN_INDEX                2
+#define MTL_Q_TQOMR_TXQEN_WIDTH                2
+
+/* MTL queue register value */
+#define MTL_RSF_DISABLE                        0x00
+#define MTL_RSF_ENABLE                 0x01
+#define MTL_TSF_DISABLE                        0x00
+#define MTL_TSF_ENABLE                 0x01
+
+#define MTL_RX_THRESHOLD_64            0x00
+#define MTL_RX_THRESHOLD_96            0x02
+#define MTL_RX_THRESHOLD_128           0x03
+#define MTL_TX_THRESHOLD_32            0x01
+#define MTL_TX_THRESHOLD_64            0x00
+#define MTL_TX_THRESHOLD_96            0x02
+#define MTL_TX_THRESHOLD_128           0x03
+#define MTL_TX_THRESHOLD_192           0x04
+#define MTL_TX_THRESHOLD_256           0x05
+#define MTL_TX_THRESHOLD_384           0x06
+#define MTL_TX_THRESHOLD_512           0x07
+
+#define MTL_ETSALG_WRR                 0x00
+#define MTL_ETSALG_WFQ                 0x01
+#define MTL_ETSALG_DWRR                        0x02
+#define MTL_RAA_SP                     0x00
+#define MTL_RAA_WSP                    0x01
+
+#define MTL_Q_DISABLED                 0x00
+#define MTL_Q_ENABLED                  0x02
+
+
+/* MTL traffic class register offsets
+ *   Multiple traffic classes can be active.  The first class has registers
+ *   that begin at 0x1100.  Each subsequent queue has registers that
+ *   are accessed using an offset of 0x80 from the previous queue.
+ */
+#define MTL_TC_BASE                    MTL_Q_BASE
+#define MTL_TC_INC                     MTL_Q_INC
+
+#define MTL_TC_ETSCR                   0x10
+
+/* MTL traffic class register entry bit positions and sizes */
+#define MTL_TC_ETSCR_TSA_INDEX         0
+#define MTL_TC_ETSCR_TSA_WIDTH         2
+
+/* MTL traffic class register value */
+#define MTL_TSA_SP                     0x00
+#define MTL_TSA_ETS                    0x02
+
+
+/* PCS MMD select register offset
+ *  The MMD select register is used for accessing PCS registers
+ *  when the underlying APB3 interface is using indirect addressing.
+ *  Indirect addressing requires accessing registers in two phases,
+ *  an address phase and a data phase.  The address phases requires
+ *  writing an address selection value to the MMD select regiesters.
+ */
+#define PCS_MMD_SELECT                 0xff
+
+
+/* Descriptor/Packet entry bit positions and sizes */
+#define RX_PACKET_ERRORS_CRC_INDEX             2
+#define RX_PACKET_ERRORS_CRC_WIDTH             1
+#define RX_PACKET_ERRORS_FRAME_INDEX           3
+#define RX_PACKET_ERRORS_FRAME_WIDTH           1
+#define RX_PACKET_ERRORS_LENGTH_INDEX          0
+#define RX_PACKET_ERRORS_LENGTH_WIDTH          1
+#define RX_PACKET_ERRORS_OVERRUN_INDEX         1
+#define RX_PACKET_ERRORS_OVERRUN_WIDTH         1
+
+#define RX_PACKET_ATTRIBUTES_CSUM_DONE_INDEX   0
+#define RX_PACKET_ATTRIBUTES_CSUM_DONE_WIDTH   1
+#define RX_PACKET_ATTRIBUTES_VLAN_CTAG_INDEX   1
+#define RX_PACKET_ATTRIBUTES_VLAN_CTAG_WIDTH   1
+#define RX_PACKET_ATTRIBUTES_INCOMPLETE_INDEX  2
+#define RX_PACKET_ATTRIBUTES_INCOMPLETE_WIDTH  1
+
+#define RX_NORMAL_DESC0_OVT_INDEX              0
+#define RX_NORMAL_DESC0_OVT_WIDTH              16
+#define RX_NORMAL_DESC3_ES_INDEX               15
+#define RX_NORMAL_DESC3_ES_WIDTH               1
+#define RX_NORMAL_DESC3_ETLT_INDEX             16
+#define RX_NORMAL_DESC3_ETLT_WIDTH             4
+#define RX_NORMAL_DESC3_INTE_INDEX             30
+#define RX_NORMAL_DESC3_INTE_WIDTH             1
+#define RX_NORMAL_DESC3_LD_INDEX               28
+#define RX_NORMAL_DESC3_LD_WIDTH               1
+#define RX_NORMAL_DESC3_OWN_INDEX              31
+#define RX_NORMAL_DESC3_OWN_WIDTH              1
+#define RX_NORMAL_DESC3_PL_INDEX               0
+#define RX_NORMAL_DESC3_PL_WIDTH               14
+
+#define TX_PACKET_ATTRIBUTES_CSUM_ENABLE_INDEX 0
+#define TX_PACKET_ATTRIBUTES_CSUM_ENABLE_WIDTH 1
+#define TX_PACKET_ATTRIBUTES_TSO_ENABLE_INDEX  1
+#define TX_PACKET_ATTRIBUTES_TSO_ENABLE_WIDTH  1
+#define TX_PACKET_ATTRIBUTES_VLAN_CTAG_INDEX   2
+#define TX_PACKET_ATTRIBUTES_VLAN_CTAG_WIDTH   1
+
+#define TX_CONTEXT_DESC2_MSS_INDEX             0
+#define TX_CONTEXT_DESC2_MSS_WIDTH             15
+#define TX_CONTEXT_DESC3_CTXT_INDEX            30
+#define TX_CONTEXT_DESC3_CTXT_WIDTH            1
+#define TX_CONTEXT_DESC3_TCMSSV_INDEX          26
+#define TX_CONTEXT_DESC3_TCMSSV_WIDTH          1
+#define TX_CONTEXT_DESC3_VLTV_INDEX            16
+#define TX_CONTEXT_DESC3_VLTV_WIDTH            1
+#define TX_CONTEXT_DESC3_VT_INDEX              0
+#define TX_CONTEXT_DESC3_VT_WIDTH              16
+
+#define TX_NORMAL_DESC2_HL_B1L_INDEX           0
+#define TX_NORMAL_DESC2_HL_B1L_WIDTH           14
+#define TX_NORMAL_DESC2_IC_INDEX               31
+#define TX_NORMAL_DESC2_IC_WIDTH               1
+#define TX_NORMAL_DESC2_VTIR_INDEX             14
+#define TX_NORMAL_DESC2_VTIR_WIDTH             2
+#define TX_NORMAL_DESC3_CIC_INDEX              16
+#define TX_NORMAL_DESC3_CIC_WIDTH              2
+#define TX_NORMAL_DESC3_CPC_INDEX              26
+#define TX_NORMAL_DESC3_CPC_WIDTH              2
+#define TX_NORMAL_DESC3_CTXT_INDEX             30
+#define TX_NORMAL_DESC3_CTXT_WIDTH             1
+#define TX_NORMAL_DESC3_FD_INDEX               29
+#define TX_NORMAL_DESC3_FD_WIDTH               1
+#define TX_NORMAL_DESC3_FL_INDEX               0
+#define TX_NORMAL_DESC3_FL_WIDTH               15
+#define TX_NORMAL_DESC3_LD_INDEX               28
+#define TX_NORMAL_DESC3_LD_WIDTH               1
+#define TX_NORMAL_DESC3_OWN_INDEX              31
+#define TX_NORMAL_DESC3_OWN_WIDTH              1
+#define TX_NORMAL_DESC3_TCPHDRLEN_INDEX                19
+#define TX_NORMAL_DESC3_TCPHDRLEN_WIDTH                4
+#define TX_NORMAL_DESC3_TCPPL_INDEX            0
+#define TX_NORMAL_DESC3_TCPPL_WIDTH            18
+#define TX_NORMAL_DESC3_TSE_INDEX              18
+#define TX_NORMAL_DESC3_TSE_WIDTH              1
+
+#define TX_NORMAL_DESC2_VLAN_INSERT            0x2
+
+/* MDIO undefined or vendor specific registers */
+#ifndef MDIO_AN_COMP_STAT
+#define MDIO_AN_COMP_STAT              0x0030
+#endif
+
+
+/* Bit setting and getting macros
+ *  The get macro will extract the current bit field value from within
+ *  the variable
+ *
+ *  The set macro will clear the current bit field value within the
+ *  variable and then set the bit field of the variable to the
+ *  specified value
+ */
+#define GET_BITS(_var, _index, _width)                                 \
+       (((_var) >> (_index)) & ((0x1 << (_width)) - 1))
+
+#define SET_BITS(_var, _index, _width, _val)                           \
+do {                                                                   \
+       (_var) &= ~(((0x1 << (_width)) - 1) << (_index));               \
+       (_var) |= (((_val) & ((0x1 << (_width)) - 1)) << (_index));     \
+} while (0)
+
+#define GET_BITS_LE(_var, _index, _width)                              \
+       ((le32_to_cpu((_var)) >> (_index)) & ((0x1 << (_width)) - 1))
+
+#define SET_BITS_LE(_var, _index, _width, _val)                                \
+do {                                                                   \
+       (_var) &= cpu_to_le32(~(((0x1 << (_width)) - 1) << (_index)));  \
+       (_var) |= cpu_to_le32((((_val) &                                \
+                             ((0x1 << (_width)) - 1)) << (_index)));   \
+} while (0)
+
+
+/* Bit setting and getting macros based on register fields
+ *  The get macro uses the bit field definitions formed using the input
+ *  names to extract the current bit field value from within the
+ *  variable
+ *
+ *  The set macro uses the bit field definitions formed using the input
+ *  names to set the bit field of the variable to the specified value
+ */
+#define XGMAC_GET_BITS(_var, _prefix, _field)                          \
+       GET_BITS((_var),                                                \
+                _prefix##_##_field##_INDEX,                            \
+                _prefix##_##_field##_WIDTH)
+
+#define XGMAC_SET_BITS(_var, _prefix, _field, _val)                    \
+       SET_BITS((_var),                                                \
+                _prefix##_##_field##_INDEX,                            \
+                _prefix##_##_field##_WIDTH, (_val))
+
+#define XGMAC_GET_BITS_LE(_var, _prefix, _field)                       \
+       GET_BITS_LE((_var),                                             \
+                _prefix##_##_field##_INDEX,                            \
+                _prefix##_##_field##_WIDTH)
+
+#define XGMAC_SET_BITS_LE(_var, _prefix, _field, _val)                 \
+       SET_BITS_LE((_var),                                             \
+                _prefix##_##_field##_INDEX,                            \
+                _prefix##_##_field##_WIDTH, (_val))
+
+
+/* Macros for reading or writing registers
+ *  The ioread macros will get bit fields or full values using the
+ *  register definitions formed using the input names
+ *
+ *  The iowrite macros will set bit fields or full values using the
+ *  register definitions formed using the input names
+ */
+#define XGMAC_IOREAD(_pdata, _reg)                                     \
+       ioread32((_pdata)->xgmac_regs + _reg)
+
+#define XGMAC_IOREAD_BITS(_pdata, _reg, _field)                                \
+       GET_BITS(XGMAC_IOREAD((_pdata), _reg),                          \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH)
+
+#define XGMAC_IOWRITE(_pdata, _reg, _val)                              \
+       iowrite32((_val), (_pdata)->xgmac_regs + _reg)
+
+#define XGMAC_IOWRITE_BITS(_pdata, _reg, _field, _val)                 \
+do {                                                                   \
+       u32 reg_val = XGMAC_IOREAD((_pdata), _reg);                     \
+       SET_BITS(reg_val,                                               \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH, (_val));                      \
+       XGMAC_IOWRITE((_pdata), _reg, reg_val);                         \
+} while (0)
+
+
+/* Macros for reading or writing MTL queue or traffic class registers
+ *  Similar to the standard read and write macros except that the
+ *  base register value is calculated by the queue or traffic class number
+ */
+#define XGMAC_MTL_IOREAD(_pdata, _n, _reg)                             \
+       ioread32((_pdata)->xgmac_regs +                                 \
+                MTL_Q_BASE + ((_n) * MTL_Q_INC) + _reg)
+
+#define XGMAC_MTL_IOREAD_BITS(_pdata, _n, _reg, _field)                        \
+       GET_BITS(XGMAC_MTL_IOREAD((_pdata), (_n), _reg),                \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH)
+
+#define XGMAC_MTL_IOWRITE(_pdata, _n, _reg, _val)                      \
+       iowrite32((_val), (_pdata)->xgmac_regs +                        \
+                 MTL_Q_BASE + ((_n) * MTL_Q_INC) + _reg)
+
+#define XGMAC_MTL_IOWRITE_BITS(_pdata, _n, _reg, _field, _val)         \
+do {                                                                   \
+       u32 reg_val = XGMAC_MTL_IOREAD((_pdata), (_n), _reg);           \
+       SET_BITS(reg_val,                                               \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH, (_val));                      \
+       XGMAC_MTL_IOWRITE((_pdata), (_n), _reg, reg_val);               \
+} while (0)
+
+
+/* Macros for reading or writing DMA channel registers
+ *  Similar to the standard read and write macros except that the
+ *  base register value is obtained from the ring
+ */
+#define XGMAC_DMA_IOREAD(_channel, _reg)                               \
+       ioread32((_channel)->dma_regs + _reg)
+
+#define XGMAC_DMA_IOREAD_BITS(_channel, _reg, _field)                  \
+       GET_BITS(XGMAC_DMA_IOREAD((_channel), _reg),                    \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH)
+
+#define XGMAC_DMA_IOWRITE(_channel, _reg, _val)                                \
+       iowrite32((_val), (_channel)->dma_regs + _reg)
+
+#define XGMAC_DMA_IOWRITE_BITS(_channel, _reg, _field, _val)           \
+do {                                                                   \
+       u32 reg_val = XGMAC_DMA_IOREAD((_channel), _reg);               \
+       SET_BITS(reg_val,                                               \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH, (_val));                      \
+       XGMAC_DMA_IOWRITE((_channel), _reg, reg_val);                   \
+} while (0)
+
+
+/* Macros for building, reading or writing register values or bits
+ * within the register values of XPCS registers.
+ */
+#define XPCS_IOWRITE(_pdata, _off, _val)                               \
+       iowrite32(_val, (_pdata)->xpcs_regs + (_off))
+
+#define XPCS_IOREAD(_pdata, _off)                                      \
+       ioread32((_pdata)->xpcs_regs + (_off))
+
+
+/* Macros for building, reading or writing register values or bits
+ * using MDIO.  Different from above because of the use of standardized
+ * Linux include values.  No shifting is performed with the bit
+ * operations, everything works on mask values.
+ */
+#define XMDIO_READ(_pdata, _mmd, _reg)                                 \
+       ((_pdata)->hw_if.read_mmd_regs((_pdata), 0,                     \
+               MII_ADDR_C45 | (_mmd << 16) | ((_reg) & 0xffff)))
+
+#define XMDIO_READ_BITS(_pdata, _mmd, _reg, _mask)                     \
+       (XMDIO_READ((_pdata), _mmd, _reg) & _mask)
+
+#define XMDIO_WRITE(_pdata, _mmd, _reg, _val)                          \
+       ((_pdata)->hw_if.write_mmd_regs((_pdata), 0,                    \
+               MII_ADDR_C45 | (_mmd << 16) | ((_reg) & 0xffff), (_val)))
+
+#define XMDIO_WRITE_BITS(_pdata, _mmd, _reg, _mask, _val)              \
+do {                                                                   \
+       u32 mmd_val = XMDIO_READ((_pdata), _mmd, _reg);                 \
+       mmd_val &= ~_mask;                                              \
+       mmd_val |= (_val);                                              \
+       XMDIO_WRITE((_pdata), _mmd, _reg, mmd_val);                     \
+} while (0)
+
+#endif
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c
new file mode 100644 (file)
index 0000000..6bb76d5
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * AMD 10Gb Ethernet driver
+ *
+ * This file is available to you under your choice of the following two
+ * licenses:
+ *
+ * License 1: GPLv2
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ *
+ * This file is free software; you may copy, redistribute 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 file 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, see <http://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and the Software, to deal in the Software
+ *     without restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ *     of the Software, and to permit persons to whom the Software is furnished
+ *     to do so, subject to the following conditions:
+ *
+ *     The above copyright notice and this permission notice shall be included
+ *     in all copies or substantial portions of the Software.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ *
+ *
+ * License 2: Modified BSD
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ * 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 of Advanced Micro Devices, Inc. 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 <COPYRIGHT HOLDER> 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.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and the Software, to deal in the Software
+ *     without restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ *     of the Software, and to permit persons to whom the Software is furnished
+ *     to do so, subject to the following conditions:
+ *
+ *     The above copyright notice and this permission notice shall be included
+ *     in all copies or substantial portions of the Software.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "xgbe.h"
+#include "xgbe-common.h"
+
+
+static ssize_t xgbe_common_read(char __user *buffer, size_t count,
+                               loff_t *ppos, unsigned int value)
+{
+       char *buf;
+       ssize_t len;
+
+       if (*ppos != 0)
+               return 0;
+
+       buf = kasprintf(GFP_KERNEL, "0x%08x\n", value);
+       if (!buf)
+               return -ENOMEM;
+
+       if (count < strlen(buf)) {
+               kfree(buf);
+               return -ENOSPC;
+       }
+
+       len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
+       kfree(buf);
+
+       return len;
+}
+
+static ssize_t xgbe_common_write(const char __user *buffer, size_t count,
+                                loff_t *ppos, unsigned int *value)
+{
+       char workarea[32];
+       ssize_t len;
+       unsigned int scan_value;
+
+       if (*ppos != 0)
+               return 0;
+
+       if (count >= sizeof(workarea))
+               return -ENOSPC;
+
+       len = simple_write_to_buffer(workarea, sizeof(workarea) - 1, ppos,
+                                    buffer, count);
+       if (len < 0)
+               return len;
+
+       workarea[len] = '\0';
+       if (sscanf(workarea, "%x", &scan_value) == 1)
+               *value = scan_value;
+       else
+               return -EIO;
+
+       return len;
+}
+
+static ssize_t xgmac_reg_addr_read(struct file *filp, char __user *buffer,
+                                  size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+
+       return xgbe_common_read(buffer, count, ppos, pdata->debugfs_xgmac_reg);
+}
+
+static ssize_t xgmac_reg_addr_write(struct file *filp,
+                                   const char __user *buffer,
+                                   size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+
+       return xgbe_common_write(buffer, count, ppos,
+                                &pdata->debugfs_xgmac_reg);
+}
+
+static ssize_t xgmac_reg_value_read(struct file *filp, char __user *buffer,
+                                   size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+       unsigned int value;
+
+       value = XGMAC_IOREAD(pdata, pdata->debugfs_xgmac_reg);
+
+       return xgbe_common_read(buffer, count, ppos, value);
+}
+
+static ssize_t xgmac_reg_value_write(struct file *filp,
+                                    const char __user *buffer,
+                                    size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+       unsigned int value;
+       ssize_t len;
+
+       len = xgbe_common_write(buffer, count, ppos, &value);
+       if (len < 0)
+               return len;
+
+       XGMAC_IOWRITE(pdata, pdata->debugfs_xgmac_reg, value);
+
+       return len;
+}
+
+static const struct file_operations xgmac_reg_addr_fops = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .read =  xgmac_reg_addr_read,
+       .write = xgmac_reg_addr_write,
+};
+
+static const struct file_operations xgmac_reg_value_fops = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .read =  xgmac_reg_value_read,
+       .write = xgmac_reg_value_write,
+};
+
+static ssize_t xpcs_mmd_read(struct file *filp, char __user *buffer,
+                            size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+
+       return xgbe_common_read(buffer, count, ppos, pdata->debugfs_xpcs_mmd);
+}
+
+static ssize_t xpcs_mmd_write(struct file *filp, const char __user *buffer,
+                             size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+
+       return xgbe_common_write(buffer, count, ppos,
+                                &pdata->debugfs_xpcs_mmd);
+}
+
+static ssize_t xpcs_reg_addr_read(struct file *filp, char __user *buffer,
+                                 size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+
+       return xgbe_common_read(buffer, count, ppos, pdata->debugfs_xpcs_reg);
+}
+
+static ssize_t xpcs_reg_addr_write(struct file *filp, const char __user *buffer,
+                                  size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+
+       return xgbe_common_write(buffer, count, ppos,
+                                &pdata->debugfs_xpcs_reg);
+}
+
+static ssize_t xpcs_reg_value_read(struct file *filp, char __user *buffer,
+                                  size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+       unsigned int value;
+
+       value = pdata->hw_if.read_mmd_regs(pdata, pdata->debugfs_xpcs_mmd,
+                                          pdata->debugfs_xpcs_reg);
+
+       return xgbe_common_read(buffer, count, ppos, value);
+}
+
+static ssize_t xpcs_reg_value_write(struct file *filp,
+                                   const char __user *buffer,
+                                   size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+       unsigned int value;
+       ssize_t len;
+
+       len = xgbe_common_write(buffer, count, ppos, &value);
+       if (len < 0)
+               return len;
+
+       pdata->hw_if.write_mmd_regs(pdata, pdata->debugfs_xpcs_mmd,
+                                   pdata->debugfs_xpcs_reg, value);
+
+       return len;
+}
+
+static const struct file_operations xpcs_mmd_fops = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .read =  xpcs_mmd_read,
+       .write = xpcs_mmd_write,
+};
+
+static const struct file_operations xpcs_reg_addr_fops = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .read =  xpcs_reg_addr_read,
+       .write = xpcs_reg_addr_write,
+};
+
+static const struct file_operations xpcs_reg_value_fops = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .read =  xpcs_reg_value_read,
+       .write = xpcs_reg_value_write,
+};
+
+void xgbe_debugfs_init(struct xgbe_prv_data *pdata)
+{
+       struct dentry *pfile;
+       char *buf;
+
+       /* Set defaults */
+       pdata->debugfs_xgmac_reg = 0;
+       pdata->debugfs_xpcs_mmd = 1;
+       pdata->debugfs_xpcs_reg = 0;
+
+       buf = kasprintf(GFP_KERNEL, "amd-xgbe-%s", pdata->netdev->name);
+       pdata->xgbe_debugfs = debugfs_create_dir(buf, NULL);
+       if (pdata->xgbe_debugfs == NULL) {
+               netdev_err(pdata->netdev, "debugfs_create_dir failed\n");
+               return;
+       }
+
+       pfile = debugfs_create_file("xgmac_register", 0600,
+                                   pdata->xgbe_debugfs, pdata,
+                                   &xgmac_reg_addr_fops);
+       if (!pfile)
+               netdev_err(pdata->netdev, "debugfs_create_file failed\n");
+
+       pfile = debugfs_create_file("xgmac_register_value", 0600,
+                                   pdata->xgbe_debugfs, pdata,
+                                   &xgmac_reg_value_fops);
+       if (!pfile)
+               netdev_err(pdata->netdev, "debugfs_create_file failed\n");
+
+       pfile = debugfs_create_file("xpcs_mmd", 0600,
+                                   pdata->xgbe_debugfs, pdata,
+                                   &xpcs_mmd_fops);
+       if (!pfile)
+               netdev_err(pdata->netdev, "debugfs_create_file failed\n");
+
+       pfile = debugfs_create_file("xpcs_register", 0600,
+                                   pdata->xgbe_debugfs, pdata,
+                                   &xpcs_reg_addr_fops);
+       if (!pfile)
+               netdev_err(pdata->netdev, "debugfs_create_file failed\n");
+
+       pfile = debugfs_create_file("xpcs_register_value", 0600,
+                                   pdata->xgbe_debugfs, pdata,
+                                   &xpcs_reg_value_fops);
+       if (!pfile)
+               netdev_err(pdata->netdev, "debugfs_create_file failed\n");
+
+       kfree(buf);
+}
+
+void xgbe_debugfs_exit(struct xgbe_prv_data *pdata)
+{
+       debugfs_remove_recursive(pdata->xgbe_debugfs);
+       pdata->xgbe_debugfs = NULL;
+}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-desc.c b/drivers/net/ethernet/amd/xgbe/xgbe-desc.c
new file mode 100644 (file)
index 0000000..6f1c859
--- /dev/null
@@ -0,0 +1,556 @@
+/*
+ * AMD 10Gb Ethernet driver
+ *
+ * This file is available to you under your choice of the following two
+ * licenses:
+ *
+ * License 1: GPLv2
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ *
+ * This file is free software; you may copy, redistribute 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 file 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, see <http://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and the Software, to deal in the Software
+ *     without restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ *     of the Software, and to permit persons to whom the Software is furnished
+ *     to do so, subject to the following conditions:
+ *
+ *     The above copyright notice and this permission notice shall be included
+ *     in all copies or substantial portions of the Software.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ *
+ *
+ * License 2: Modified BSD
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ * 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 of Advanced Micro Devices, Inc. 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 <COPYRIGHT HOLDER> 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.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and the Software, to deal in the Software
+ *     without restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ *     of the Software, and to permit persons to whom the Software is furnished
+ *     to do so, subject to the following conditions:
+ *
+ *     The above copyright notice and this permission notice shall be included
+ *     in all copies or substantial portions of the Software.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ */
+
+#include "xgbe.h"
+#include "xgbe-common.h"
+
+
+static void xgbe_unmap_skb(struct xgbe_prv_data *, struct xgbe_ring_data *);
+
+static void xgbe_free_ring(struct xgbe_prv_data *pdata,
+                          struct xgbe_ring *ring)
+{
+       struct xgbe_ring_data *rdata;
+       unsigned int i;
+
+       if (!ring)
+               return;
+
+       if (ring->rdata) {
+               for (i = 0; i < ring->rdesc_count; i++) {
+                       rdata = GET_DESC_DATA(ring, i);
+                       xgbe_unmap_skb(pdata, rdata);
+               }
+
+               kfree(ring->rdata);
+               ring->rdata = NULL;
+       }
+
+       if (ring->rdesc) {
+               dma_free_coherent(pdata->dev,
+                                 (sizeof(struct xgbe_ring_desc) *
+                                  ring->rdesc_count),
+                                 ring->rdesc, ring->rdesc_dma);
+               ring->rdesc = NULL;
+       }
+}
+
+static void xgbe_free_ring_resources(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       DBGPR("-->xgbe_free_ring_resources\n");
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               xgbe_free_ring(pdata, channel->tx_ring);
+               xgbe_free_ring(pdata, channel->rx_ring);
+       }
+
+       DBGPR("<--xgbe_free_ring_resources\n");
+}
+
+static int xgbe_init_ring(struct xgbe_prv_data *pdata,
+                         struct xgbe_ring *ring, unsigned int rdesc_count)
+{
+       DBGPR("-->xgbe_init_ring\n");
+
+       if (!ring)
+               return 0;
+
+       /* Descriptors */
+       ring->rdesc_count = rdesc_count;
+       ring->rdesc = dma_alloc_coherent(pdata->dev,
+                                        (sizeof(struct xgbe_ring_desc) *
+                                         rdesc_count), &ring->rdesc_dma,
+                                        GFP_KERNEL);
+       if (!ring->rdesc)
+               return -ENOMEM;
+
+       /* Descriptor information */
+       ring->rdata = kcalloc(rdesc_count, sizeof(struct xgbe_ring_data),
+                             GFP_KERNEL);
+       if (!ring->rdata)
+               return -ENOMEM;
+
+       DBGPR("    rdesc=0x%p, rdesc_dma=0x%llx, rdata=0x%p\n",
+             ring->rdesc, ring->rdesc_dma, ring->rdata);
+
+       DBGPR("<--xgbe_init_ring\n");
+
+       return 0;
+}
+
+static int xgbe_alloc_ring_resources(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+       int ret;
+
+       DBGPR("-->xgbe_alloc_ring_resources\n");
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               DBGPR("  %s - tx_ring:\n", channel->name);
+               ret = xgbe_init_ring(pdata, channel->tx_ring,
+                                    pdata->tx_desc_count);
+               if (ret) {
+                       netdev_alert(pdata->netdev,
+                                    "error initializing Tx ring\n");
+                       goto err_ring;
+               }
+
+               DBGPR("  %s - rx_ring:\n", channel->name);
+               ret = xgbe_init_ring(pdata, channel->rx_ring,
+                                    pdata->rx_desc_count);
+               if (ret) {
+                       netdev_alert(pdata->netdev,
+                                    "error initializing Tx ring\n");
+                       goto err_ring;
+               }
+       }
+
+       DBGPR("<--xgbe_alloc_ring_resources\n");
+
+       return 0;
+
+err_ring:
+       xgbe_free_ring_resources(pdata);
+
+       return ret;
+}
+
+static void xgbe_wrapper_tx_descriptor_init(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct xgbe_channel *channel;
+       struct xgbe_ring *ring;
+       struct xgbe_ring_data *rdata;
+       struct xgbe_ring_desc *rdesc;
+       dma_addr_t rdesc_dma;
+       unsigned int i, j;
+
+       DBGPR("-->xgbe_wrapper_tx_descriptor_init\n");
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               ring = channel->tx_ring;
+               if (!ring)
+                       break;
+
+               rdesc = ring->rdesc;
+               rdesc_dma = ring->rdesc_dma;
+
+               for (j = 0; j < ring->rdesc_count; j++) {
+                       rdata = GET_DESC_DATA(ring, j);
+
+                       rdata->rdesc = rdesc;
+                       rdata->rdesc_dma = rdesc_dma;
+
+                       rdesc++;
+                       rdesc_dma += sizeof(struct xgbe_ring_desc);
+               }
+
+               ring->cur = 0;
+               ring->dirty = 0;
+               ring->tx.queue_stopped = 0;
+
+               hw_if->tx_desc_init(channel);
+       }
+
+       DBGPR("<--xgbe_wrapper_tx_descriptor_init\n");
+}
+
+static void xgbe_wrapper_rx_descriptor_init(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct xgbe_channel *channel;
+       struct xgbe_ring *ring;
+       struct xgbe_ring_desc *rdesc;
+       struct xgbe_ring_data *rdata;
+       dma_addr_t rdesc_dma, skb_dma;
+       struct sk_buff *skb = NULL;
+       unsigned int i, j;
+
+       DBGPR("-->xgbe_wrapper_rx_descriptor_init\n");
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               ring = channel->rx_ring;
+               if (!ring)
+                       break;
+
+               rdesc = ring->rdesc;
+               rdesc_dma = ring->rdesc_dma;
+
+               for (j = 0; j < ring->rdesc_count; j++) {
+                       rdata = GET_DESC_DATA(ring, j);
+
+                       rdata->rdesc = rdesc;
+                       rdata->rdesc_dma = rdesc_dma;
+
+                       /* Allocate skb & assign to each rdesc */
+                       skb = dev_alloc_skb(pdata->rx_buf_size);
+                       if (skb == NULL)
+                               break;
+                       skb_dma = dma_map_single(pdata->dev, skb->data,
+                                                pdata->rx_buf_size,
+                                                DMA_FROM_DEVICE);
+                       if (dma_mapping_error(pdata->dev, skb_dma)) {
+                               netdev_alert(pdata->netdev,
+                                            "failed to do the dma map\n");
+                               dev_kfree_skb_any(skb);
+                               break;
+                       }
+                       rdata->skb = skb;
+                       rdata->skb_dma = skb_dma;
+                       rdata->skb_dma_len = pdata->rx_buf_size;
+
+                       rdesc++;
+                       rdesc_dma += sizeof(struct xgbe_ring_desc);
+               }
+
+               ring->cur = 0;
+               ring->dirty = 0;
+               ring->rx.realloc_index = 0;
+               ring->rx.realloc_threshold = 0;
+
+               hw_if->rx_desc_init(channel);
+       }
+
+       DBGPR("<--xgbe_wrapper_rx_descriptor_init\n");
+}
+
+static void xgbe_unmap_skb(struct xgbe_prv_data *pdata,
+                          struct xgbe_ring_data *rdata)
+{
+       if (rdata->skb_dma) {
+               if (rdata->mapped_as_page) {
+                       dma_unmap_page(pdata->dev, rdata->skb_dma,
+                                      rdata->skb_dma_len, DMA_TO_DEVICE);
+               } else {
+                       dma_unmap_single(pdata->dev, rdata->skb_dma,
+                                        rdata->skb_dma_len, DMA_TO_DEVICE);
+               }
+               rdata->skb_dma = 0;
+               rdata->skb_dma_len = 0;
+       }
+
+       if (rdata->skb) {
+               dev_kfree_skb_any(rdata->skb);
+               rdata->skb = NULL;
+       }
+
+       rdata->tso_header = 0;
+       rdata->len = 0;
+       rdata->interrupt = 0;
+       rdata->mapped_as_page = 0;
+}
+
+static int xgbe_map_tx_skb(struct xgbe_channel *channel, struct sk_buff *skb)
+{
+       struct xgbe_prv_data *pdata = channel->pdata;
+       struct xgbe_ring *ring = channel->tx_ring;
+       struct xgbe_ring_data *rdata;
+       struct xgbe_packet_data *packet;
+       struct skb_frag_struct *frag;
+       dma_addr_t skb_dma;
+       unsigned int start_index, cur_index;
+       unsigned int offset, tso, vlan, datalen, len;
+       unsigned int i;
+
+       DBGPR("-->xgbe_map_tx_skb: cur = %d\n", ring->cur);
+
+       offset = 0;
+       start_index = ring->cur;
+       cur_index = ring->cur;
+
+       packet = &ring->packet_data;
+       packet->rdesc_count = 0;
+       packet->length = 0;
+
+       tso = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
+                            TSO_ENABLE);
+       vlan = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
+                             VLAN_CTAG);
+
+       /* Save space for a context descriptor if needed */
+       if ((tso && (packet->mss != ring->tx.cur_mss)) ||
+           (vlan && (packet->vlan_ctag != ring->tx.cur_vlan_ctag)))
+               cur_index++;
+       rdata = GET_DESC_DATA(ring, cur_index);
+
+       if (tso) {
+               DBGPR("  TSO packet\n");
+
+               /* Map the TSO header */
+               skb_dma = dma_map_single(pdata->dev, skb->data,
+                                        packet->header_len, DMA_TO_DEVICE);
+               if (dma_mapping_error(pdata->dev, skb_dma)) {
+                       netdev_alert(pdata->netdev, "dma_map_single failed\n");
+                       goto err_out;
+               }
+               rdata->skb_dma = skb_dma;
+               rdata->skb_dma_len = packet->header_len;
+               rdata->tso_header = 1;
+
+               offset = packet->header_len;
+
+               packet->length += packet->header_len;
+
+               cur_index++;
+               rdata = GET_DESC_DATA(ring, cur_index);
+       }
+
+       /* Map the (remainder of the) packet */
+       for (datalen = skb_headlen(skb) - offset; datalen; ) {
+               len = min_t(unsigned int, datalen, TX_MAX_BUF_SIZE);
+
+               skb_dma = dma_map_single(pdata->dev, skb->data + offset, len,
+                                        DMA_TO_DEVICE);
+               if (dma_mapping_error(pdata->dev, skb_dma)) {
+                       netdev_alert(pdata->netdev, "dma_map_single failed\n");
+                       goto err_out;
+               }
+               rdata->skb_dma = skb_dma;
+               rdata->skb_dma_len = len;
+               DBGPR("  skb data: index=%u, dma=0x%llx, len=%u\n",
+                     cur_index, skb_dma, len);
+
+               datalen -= len;
+               offset += len;
+
+               packet->length += len;
+
+               cur_index++;
+               rdata = GET_DESC_DATA(ring, cur_index);
+       }
+
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+               DBGPR("  mapping frag %u\n", i);
+
+               frag = &skb_shinfo(skb)->frags[i];
+               offset = 0;
+
+               for (datalen = skb_frag_size(frag); datalen; ) {
+                       len = min_t(unsigned int, datalen, TX_MAX_BUF_SIZE);
+
+                       skb_dma = skb_frag_dma_map(pdata->dev, frag, offset,
+                                                  len, DMA_TO_DEVICE);
+                       if (dma_mapping_error(pdata->dev, skb_dma)) {
+                               netdev_alert(pdata->netdev,
+                                            "skb_frag_dma_map failed\n");
+                               goto err_out;
+                       }
+                       rdata->skb_dma = skb_dma;
+                       rdata->skb_dma_len = len;
+                       rdata->mapped_as_page = 1;
+                       DBGPR("  skb data: index=%u, dma=0x%llx, len=%u\n",
+                             cur_index, skb_dma, len);
+
+                       datalen -= len;
+                       offset += len;
+
+                       packet->length += len;
+
+                       cur_index++;
+                       rdata = GET_DESC_DATA(ring, cur_index);
+               }
+       }
+
+       /* Save the skb address in the last entry */
+       rdata->skb = skb;
+
+       /* Save the number of descriptor entries used */
+       packet->rdesc_count = cur_index - start_index;
+
+       DBGPR("<--xgbe_map_tx_skb: count=%u\n", packet->rdesc_count);
+
+       return packet->rdesc_count;
+
+err_out:
+       while (start_index < cur_index) {
+               rdata = GET_DESC_DATA(ring, start_index++);
+               xgbe_unmap_skb(pdata, rdata);
+       }
+
+       DBGPR("<--xgbe_map_tx_skb: count=0\n");
+
+       return 0;
+}
+
+static void xgbe_realloc_skb(struct xgbe_channel *channel)
+{
+       struct xgbe_prv_data *pdata = channel->pdata;
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct xgbe_ring *ring = channel->rx_ring;
+       struct xgbe_ring_data *rdata;
+       struct sk_buff *skb = NULL;
+       dma_addr_t skb_dma;
+       int i;
+
+       DBGPR("-->xgbe_realloc_skb: rx_ring->rx.realloc_index = %u\n",
+             ring->rx.realloc_index);
+
+       for (i = 0; i < ring->dirty; i++) {
+               rdata = GET_DESC_DATA(ring, ring->rx.realloc_index);
+
+               /* Reset rdata values */
+               xgbe_unmap_skb(pdata, rdata);
+
+               /* Allocate skb & assign to each rdesc */
+               skb = dev_alloc_skb(pdata->rx_buf_size);
+               if (skb == NULL) {
+                       netdev_alert(pdata->netdev,
+                                    "failed to allocate skb\n");
+                       break;
+               }
+               skb_dma = dma_map_single(pdata->dev, skb->data,
+                                        pdata->rx_buf_size, DMA_FROM_DEVICE);
+               if (dma_mapping_error(pdata->dev, skb_dma)) {
+                       netdev_alert(pdata->netdev,
+                                    "failed to do the dma map\n");
+                       dev_kfree_skb_any(skb);
+                       break;
+               }
+               rdata->skb = skb;
+               rdata->skb_dma = skb_dma;
+               rdata->skb_dma_len = pdata->rx_buf_size;
+
+               hw_if->rx_desc_reset(rdata);
+
+               ring->rx.realloc_index++;
+       }
+       ring->dirty = 0;
+
+       DBGPR("<--xgbe_realloc_skb\n");
+}
+
+void xgbe_init_function_ptrs_desc(struct xgbe_desc_if *desc_if)
+{
+       DBGPR("-->xgbe_init_function_ptrs_desc\n");
+
+       desc_if->alloc_ring_resources = xgbe_alloc_ring_resources;
+       desc_if->free_ring_resources = xgbe_free_ring_resources;
+       desc_if->map_tx_skb = xgbe_map_tx_skb;
+       desc_if->realloc_skb = xgbe_realloc_skb;
+       desc_if->unmap_skb = xgbe_unmap_skb;
+       desc_if->wrapper_tx_desc_init = xgbe_wrapper_tx_descriptor_init;
+       desc_if->wrapper_rx_desc_init = xgbe_wrapper_rx_descriptor_init;
+
+       DBGPR("<--xgbe_init_function_ptrs_desc\n");
+}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
new file mode 100644 (file)
index 0000000..002293b
--- /dev/null
@@ -0,0 +1,2182 @@
+/*
+ * AMD 10Gb Ethernet driver
+ *
+ * This file is available to you under your choice of the following two
+ * licenses:
+ *
+ * License 1: GPLv2
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ *
+ * This file is free software; you may copy, redistribute 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 file 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, see <http://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and the Software, to deal in the Software
+ *     without restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ *     of the Software, and to permit persons to whom the Software is furnished
+ *     to do so, subject to the following conditions:
+ *
+ *     The above copyright notice and this permission notice shall be included
+ *     in all copies or substantial portions of the Software.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ *
+ *
+ * License 2: Modified BSD
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ * 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 of Advanced Micro Devices, Inc. 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 <COPYRIGHT HOLDER> 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.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and the Software, to deal in the Software
+ *     without restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ *     of the Software, and to permit persons to whom the Software is furnished
+ *     to do so, subject to the following conditions:
+ *
+ *     The above copyright notice and this permission notice shall be included
+ *     in all copies or substantial portions of the Software.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ */
+
+#include <linux/phy.h>
+#include <linux/clk.h>
+
+#include "xgbe.h"
+#include "xgbe-common.h"
+
+
+static unsigned int xgbe_usec_to_riwt(struct xgbe_prv_data *pdata,
+                                     unsigned int usec)
+{
+       unsigned long rate;
+       unsigned int ret;
+
+       DBGPR("-->xgbe_usec_to_riwt\n");
+
+       rate = clk_get_rate(pdata->sysclock);
+
+       /*
+        * Convert the input usec value to the watchdog timer value. Each
+        * watchdog timer value is equivalent to 256 clock cycles.
+        * Calculate the required value as:
+        *   ( usec * ( system_clock_mhz / 10^6 ) / 256
+        */
+       ret = (usec * (rate / 1000000)) / 256;
+
+       DBGPR("<--xgbe_usec_to_riwt\n");
+
+       return ret;
+}
+
+static unsigned int xgbe_riwt_to_usec(struct xgbe_prv_data *pdata,
+                                     unsigned int riwt)
+{
+       unsigned long rate;
+       unsigned int ret;
+
+       DBGPR("-->xgbe_riwt_to_usec\n");
+
+       rate = clk_get_rate(pdata->sysclock);
+
+       /*
+        * Convert the input watchdog timer value to the usec value. Each
+        * watchdog timer value is equivalent to 256 clock cycles.
+        * Calculate the required value as:
+        *   ( riwt * 256 ) / ( system_clock_mhz / 10^6 )
+        */
+       ret = (riwt * 256) / (rate / 1000000);
+
+       DBGPR("<--xgbe_riwt_to_usec\n");
+
+       return ret;
+}
+
+static int xgbe_config_pblx8(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++)
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_CR, PBLX8,
+                                      pdata->pblx8);
+
+       return 0;
+}
+
+static int xgbe_get_tx_pbl_val(struct xgbe_prv_data *pdata)
+{
+       return XGMAC_DMA_IOREAD_BITS(pdata->channel, DMA_CH_TCR, PBL);
+}
+
+static int xgbe_config_tx_pbl_val(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->tx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_TCR, PBL,
+                                      pdata->tx_pbl);
+       }
+
+       return 0;
+}
+
+static int xgbe_get_rx_pbl_val(struct xgbe_prv_data *pdata)
+{
+       return XGMAC_DMA_IOREAD_BITS(pdata->channel, DMA_CH_RCR, PBL);
+}
+
+static int xgbe_config_rx_pbl_val(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->rx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_RCR, PBL,
+                                      pdata->rx_pbl);
+       }
+
+       return 0;
+}
+
+static int xgbe_config_osp_mode(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->tx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_TCR, OSP,
+                                      pdata->tx_osp_mode);
+       }
+
+       return 0;
+}
+
+static int xgbe_config_rsf_mode(struct xgbe_prv_data *pdata, unsigned int val)
+{
+       unsigned int i;
+
+       for (i = 0; i < pdata->hw_feat.rx_q_cnt; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, RSF, val);
+
+       return 0;
+}
+
+static int xgbe_config_tsf_mode(struct xgbe_prv_data *pdata, unsigned int val)
+{
+       unsigned int i;
+
+       for (i = 0; i < pdata->hw_feat.tx_q_cnt; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_TQOMR, TSF, val);
+
+       return 0;
+}
+
+static int xgbe_config_rx_threshold(struct xgbe_prv_data *pdata,
+                                   unsigned int val)
+{
+       unsigned int i;
+
+       for (i = 0; i < pdata->hw_feat.rx_q_cnt; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, RTC, val);
+
+       return 0;
+}
+
+static int xgbe_config_tx_threshold(struct xgbe_prv_data *pdata,
+                                   unsigned int val)
+{
+       unsigned int i;
+
+       for (i = 0; i < pdata->hw_feat.tx_q_cnt; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_TQOMR, TTC, val);
+
+       return 0;
+}
+
+static int xgbe_config_rx_coalesce(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->rx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_RIWT, RWT,
+                                      pdata->rx_riwt);
+       }
+
+       return 0;
+}
+
+static int xgbe_config_tx_coalesce(struct xgbe_prv_data *pdata)
+{
+       return 0;
+}
+
+static void xgbe_config_rx_buffer_size(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->rx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_RCR, RBSZ,
+                                      pdata->rx_buf_size);
+       }
+}
+
+static void xgbe_config_tso_mode(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->tx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_TCR, TSE, 1);
+       }
+}
+
+static int xgbe_disable_tx_flow_control(struct xgbe_prv_data *pdata)
+{
+       unsigned int max_q_count, q_count;
+       unsigned int reg, reg_val;
+       unsigned int i;
+
+       /* Clear MTL flow control */
+       for (i = 0; i < pdata->hw_feat.rx_q_cnt; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, EHFC, 0);
+
+       /* Clear MAC flow control */
+       max_q_count = XGMAC_MAX_FLOW_CONTROL_QUEUES;
+       q_count = min_t(unsigned int, pdata->hw_feat.rx_q_cnt, max_q_count);
+       reg = MAC_Q0TFCR;
+       for (i = 0; i < q_count; i++) {
+               reg_val = XGMAC_IOREAD(pdata, reg);
+               XGMAC_SET_BITS(reg_val, MAC_Q0TFCR, TFE, 0);
+               XGMAC_IOWRITE(pdata, reg, reg_val);
+
+               reg += MAC_QTFCR_INC;
+       }
+
+       return 0;
+}
+
+static int xgbe_enable_tx_flow_control(struct xgbe_prv_data *pdata)
+{
+       unsigned int max_q_count, q_count;
+       unsigned int reg, reg_val;
+       unsigned int i;
+
+       /* Set MTL flow control */
+       for (i = 0; i < pdata->hw_feat.rx_q_cnt; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, EHFC, 1);
+
+       /* Set MAC flow control */
+       max_q_count = XGMAC_MAX_FLOW_CONTROL_QUEUES;
+       q_count = min_t(unsigned int, pdata->hw_feat.rx_q_cnt, max_q_count);
+       reg = MAC_Q0TFCR;
+       for (i = 0; i < q_count; i++) {
+               reg_val = XGMAC_IOREAD(pdata, reg);
+
+               /* Enable transmit flow control */
+               XGMAC_SET_BITS(reg_val, MAC_Q0TFCR, TFE, 1);
+               /* Set pause time */
+               XGMAC_SET_BITS(reg_val, MAC_Q0TFCR, PT, 0xffff);
+
+               XGMAC_IOWRITE(pdata, reg, reg_val);
+
+               reg += MAC_QTFCR_INC;
+       }
+
+       return 0;
+}
+
+static int xgbe_disable_rx_flow_control(struct xgbe_prv_data *pdata)
+{
+       XGMAC_IOWRITE_BITS(pdata, MAC_RFCR, RFE, 0);
+
+       return 0;
+}
+
+static int xgbe_enable_rx_flow_control(struct xgbe_prv_data *pdata)
+{
+       XGMAC_IOWRITE_BITS(pdata, MAC_RFCR, RFE, 1);
+
+       return 0;
+}
+
+static int xgbe_config_tx_flow_control(struct xgbe_prv_data *pdata)
+{
+       if (pdata->tx_pause)
+               xgbe_enable_tx_flow_control(pdata);
+       else
+               xgbe_disable_tx_flow_control(pdata);
+
+       return 0;
+}
+
+static int xgbe_config_rx_flow_control(struct xgbe_prv_data *pdata)
+{
+       if (pdata->rx_pause)
+               xgbe_enable_rx_flow_control(pdata);
+       else
+               xgbe_disable_rx_flow_control(pdata);
+
+       return 0;
+}
+
+static void xgbe_config_flow_control(struct xgbe_prv_data *pdata)
+{
+       xgbe_config_tx_flow_control(pdata);
+       xgbe_config_rx_flow_control(pdata);
+}
+
+static void xgbe_enable_dma_interrupts(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int dma_ch_isr, dma_ch_ier;
+       unsigned int i;
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               /* Clear all the interrupts which are set */
+               dma_ch_isr = XGMAC_DMA_IOREAD(channel, DMA_CH_SR);
+               XGMAC_DMA_IOWRITE(channel, DMA_CH_SR, dma_ch_isr);
+
+               /* Clear all interrupt enable bits */
+               dma_ch_ier = 0;
+
+               /* Enable following interrupts
+                *   NIE  - Normal Interrupt Summary Enable
+                *   AIE  - Abnormal Interrupt Summary Enable
+                *   FBEE - Fatal Bus Error Enable
+                */
+               XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, NIE, 1);
+               XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, AIE, 1);
+               XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, FBEE, 1);
+
+               if (channel->tx_ring) {
+                       /* Enable the following Tx interrupts
+                        *   TIE  - Transmit Interrupt Enable (unless polling)
+                        */
+                       XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, TIE, 1);
+               }
+               if (channel->rx_ring) {
+                       /* Enable following Rx interrupts
+                        *   RBUE - Receive Buffer Unavailable Enable
+                        *   RIE  - Receive Interrupt Enable
+                        */
+                       XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RBUE, 1);
+                       XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RIE, 1);
+               }
+
+               XGMAC_DMA_IOWRITE(channel, DMA_CH_IER, dma_ch_ier);
+       }
+}
+
+static void xgbe_enable_mtl_interrupts(struct xgbe_prv_data *pdata)
+{
+       unsigned int mtl_q_isr;
+       unsigned int q_count, i;
+
+       q_count = max(pdata->hw_feat.tx_q_cnt, pdata->hw_feat.rx_q_cnt);
+       for (i = 0; i < q_count; i++) {
+               /* Clear all the interrupts which are set */
+               mtl_q_isr = XGMAC_MTL_IOREAD(pdata, i, MTL_Q_ISR);
+               XGMAC_MTL_IOWRITE(pdata, i, MTL_Q_ISR, mtl_q_isr);
+
+               /* No MTL interrupts to be enabled */
+               XGMAC_MTL_IOWRITE(pdata, i, MTL_Q_ISR, 0);
+       }
+}
+
+static void xgbe_enable_mac_interrupts(struct xgbe_prv_data *pdata)
+{
+       /* No MAC interrupts to be enabled */
+       XGMAC_IOWRITE(pdata, MAC_IER, 0);
+
+       /* Enable all counter interrupts */
+       XGMAC_IOWRITE_BITS(pdata, MMC_RIER, ALL_INTERRUPTS, 0xff);
+       XGMAC_IOWRITE_BITS(pdata, MMC_TIER, ALL_INTERRUPTS, 0xff);
+}
+
+static int xgbe_set_gmii_speed(struct xgbe_prv_data *pdata)
+{
+       XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0x3);
+
+       return 0;
+}
+
+static int xgbe_set_gmii_2500_speed(struct xgbe_prv_data *pdata)
+{
+       XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0x2);
+
+       return 0;
+}
+
+static int xgbe_set_xgmii_speed(struct xgbe_prv_data *pdata)
+{
+       XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0);
+
+       return 0;
+}
+
+static int xgbe_set_promiscuous_mode(struct xgbe_prv_data *pdata,
+                                    unsigned int enable)
+{
+       unsigned int val = enable ? 1 : 0;
+
+       if (XGMAC_IOREAD_BITS(pdata, MAC_PFR, PR) == val)
+               return 0;
+
+       DBGPR("  %s promiscuous mode\n", enable ? "entering" : "leaving");
+       XGMAC_IOWRITE_BITS(pdata, MAC_PFR, PR, val);
+
+       return 0;
+}
+
+static int xgbe_set_all_multicast_mode(struct xgbe_prv_data *pdata,
+                                      unsigned int enable)
+{
+       unsigned int val = enable ? 1 : 0;
+
+       if (XGMAC_IOREAD_BITS(pdata, MAC_PFR, PM) == val)
+               return 0;
+
+       DBGPR("  %s allmulti mode\n", enable ? "entering" : "leaving");
+       XGMAC_IOWRITE_BITS(pdata, MAC_PFR, PM, val);
+
+       return 0;
+}
+
+static int xgbe_set_addn_mac_addrs(struct xgbe_prv_data *pdata,
+                                  unsigned int am_mode)
+{
+       struct netdev_hw_addr *ha;
+       unsigned int mac_reg;
+       unsigned int mac_addr_hi, mac_addr_lo;
+       u8 *mac_addr;
+       unsigned int i;
+
+       XGMAC_IOWRITE_BITS(pdata, MAC_PFR, HUC, 0);
+       XGMAC_IOWRITE_BITS(pdata, MAC_PFR, HMC, 0);
+
+       i = 0;
+       mac_reg = MAC_MACA1HR;
+
+       netdev_for_each_uc_addr(ha, pdata->netdev) {
+               mac_addr_lo = 0;
+               mac_addr_hi = 0;
+               mac_addr = (u8 *)&mac_addr_lo;
+               mac_addr[0] = ha->addr[0];
+               mac_addr[1] = ha->addr[1];
+               mac_addr[2] = ha->addr[2];
+               mac_addr[3] = ha->addr[3];
+               mac_addr = (u8 *)&mac_addr_hi;
+               mac_addr[0] = ha->addr[4];
+               mac_addr[1] = ha->addr[5];
+
+               DBGPR("  adding unicast address %pM at 0x%04x\n",
+                     ha->addr, mac_reg);
+
+               XGMAC_SET_BITS(mac_addr_hi, MAC_MACA1HR, AE, 1);
+
+               XGMAC_IOWRITE(pdata, mac_reg, mac_addr_hi);
+               mac_reg += MAC_MACA_INC;
+               XGMAC_IOWRITE(pdata, mac_reg, mac_addr_lo);
+               mac_reg += MAC_MACA_INC;
+
+               i++;
+       }
+
+       if (!am_mode) {
+               netdev_for_each_mc_addr(ha, pdata->netdev) {
+                       mac_addr_lo = 0;
+                       mac_addr_hi = 0;
+                       mac_addr = (u8 *)&mac_addr_lo;
+                       mac_addr[0] = ha->addr[0];
+                       mac_addr[1] = ha->addr[1];
+                       mac_addr[2] = ha->addr[2];
+                       mac_addr[3] = ha->addr[3];
+                       mac_addr = (u8 *)&mac_addr_hi;
+                       mac_addr[0] = ha->addr[4];
+                       mac_addr[1] = ha->addr[5];
+
+                       DBGPR("  adding multicast address %pM at 0x%04x\n",
+                             ha->addr, mac_reg);
+
+                       XGMAC_SET_BITS(mac_addr_hi, MAC_MACA1HR, AE, 1);
+
+                       XGMAC_IOWRITE(pdata, mac_reg, mac_addr_hi);
+                       mac_reg += MAC_MACA_INC;
+                       XGMAC_IOWRITE(pdata, mac_reg, mac_addr_lo);
+                       mac_reg += MAC_MACA_INC;
+
+                       i++;
+               }
+       }
+
+       /* Clear remaining additional MAC address entries */
+       for (; i < pdata->hw_feat.addn_mac; i++) {
+               XGMAC_IOWRITE(pdata, mac_reg, 0);
+               mac_reg += MAC_MACA_INC;
+               XGMAC_IOWRITE(pdata, mac_reg, 0);
+               mac_reg += MAC_MACA_INC;
+       }
+
+       return 0;
+}
+
+static int xgbe_set_mac_address(struct xgbe_prv_data *pdata, u8 *addr)
+{
+       unsigned int mac_addr_hi, mac_addr_lo;
+
+       mac_addr_hi = (addr[5] <<  8) | (addr[4] <<  0);
+       mac_addr_lo = (addr[3] << 24) | (addr[2] << 16) |
+                     (addr[1] <<  8) | (addr[0] <<  0);
+
+       XGMAC_IOWRITE(pdata, MAC_MACA0HR, mac_addr_hi);
+       XGMAC_IOWRITE(pdata, MAC_MACA0LR, mac_addr_lo);
+
+       return 0;
+}
+
+static int xgbe_read_mmd_regs(struct xgbe_prv_data *pdata, int prtad,
+                             int mmd_reg)
+{
+       unsigned int mmd_address;
+       int mmd_data;
+
+       if (mmd_reg & MII_ADDR_C45)
+               mmd_address = mmd_reg & ~MII_ADDR_C45;
+       else
+               mmd_address = (pdata->mdio_mmd << 16) | (mmd_reg & 0xffff);
+
+       /* The PCS registers are accessed using mmio. The underlying APB3
+        * management interface uses indirect addressing to access the MMD
+        * register sets. This requires accessing of the PCS register in two
+        * phases, an address phase and a data phase.
+        *
+        * The mmio interface is based on 32-bit offsets and values. All
+        * register offsets must therefore be adjusted by left shifting the
+        * offset 2 bits and reading 32 bits of data.
+        */
+       mutex_lock(&pdata->xpcs_mutex);
+       XPCS_IOWRITE(pdata, PCS_MMD_SELECT << 2, mmd_address >> 8);
+       mmd_data = XPCS_IOREAD(pdata, (mmd_address & 0xff) << 2);
+       mutex_unlock(&pdata->xpcs_mutex);
+
+       return mmd_data;
+}
+
+static void xgbe_write_mmd_regs(struct xgbe_prv_data *pdata, int prtad,
+                               int mmd_reg, int mmd_data)
+{
+       unsigned int mmd_address;
+
+       if (mmd_reg & MII_ADDR_C45)
+               mmd_address = mmd_reg & ~MII_ADDR_C45;
+       else
+               mmd_address = (pdata->mdio_mmd << 16) | (mmd_reg & 0xffff);
+
+       /* The PCS registers are accessed using mmio. The underlying APB3
+        * management interface uses indirect addressing to access the MMD
+        * register sets. This requires accessing of the PCS register in two
+        * phases, an address phase and a data phase.
+        *
+        * The mmio interface is based on 32-bit offsets and values. All
+        * register offsets must therefore be adjusted by left shifting the
+        * offset 2 bits and reading 32 bits of data.
+        */
+       mutex_lock(&pdata->xpcs_mutex);
+       XPCS_IOWRITE(pdata, PCS_MMD_SELECT << 2, mmd_address >> 8);
+       XPCS_IOWRITE(pdata, (mmd_address & 0xff) << 2, mmd_data);
+       mutex_unlock(&pdata->xpcs_mutex);
+}
+
+static int xgbe_tx_complete(struct xgbe_ring_desc *rdesc)
+{
+       return !XGMAC_GET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, OWN);
+}
+
+static int xgbe_disable_rx_csum(struct xgbe_prv_data *pdata)
+{
+       XGMAC_IOWRITE_BITS(pdata, MAC_RCR, IPC, 0);
+
+       return 0;
+}
+
+static int xgbe_enable_rx_csum(struct xgbe_prv_data *pdata)
+{
+       XGMAC_IOWRITE_BITS(pdata, MAC_RCR, IPC, 1);
+
+       return 0;
+}
+
+static int xgbe_enable_rx_vlan_stripping(struct xgbe_prv_data *pdata)
+{
+       /* Put the VLAN tag in the Rx descriptor */
+       XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, EVLRXS, 1);
+
+       /* Don't check the VLAN type */
+       XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, DOVLTC, 1);
+
+       /* Check only C-TAG (0x8100) packets */
+       XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, ERSVLM, 0);
+
+       /* Don't consider an S-TAG (0x88A8) packet as a VLAN packet */
+       XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, ESVL, 0);
+
+       /* Enable VLAN tag stripping */
+       XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, EVLS, 0x3);
+
+       return 0;
+}
+
+static int xgbe_disable_rx_vlan_stripping(struct xgbe_prv_data *pdata)
+{
+       XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, EVLS, 0);
+
+       return 0;
+}
+
+static void xgbe_tx_desc_reset(struct xgbe_ring_data *rdata)
+{
+       struct xgbe_ring_desc *rdesc = rdata->rdesc;
+
+       /* Reset the Tx descriptor
+        *   Set buffer 1 (lo) address to zero
+        *   Set buffer 1 (hi) address to zero
+        *   Reset all other control bits (IC, TTSE, B2L & B1L)
+        *   Reset all other control bits (OWN, CTXT, FD, LD, CPC, CIC, etc)
+        */
+       rdesc->desc0 = 0;
+       rdesc->desc1 = 0;
+       rdesc->desc2 = 0;
+       rdesc->desc3 = 0;
+}
+
+static void xgbe_tx_desc_init(struct xgbe_channel *channel)
+{
+       struct xgbe_ring *ring = channel->tx_ring;
+       struct xgbe_ring_data *rdata;
+       struct xgbe_ring_desc *rdesc;
+       int i;
+       int start_index = ring->cur;
+
+       DBGPR("-->tx_desc_init\n");
+
+       /* Initialze all descriptors */
+       for (i = 0; i < ring->rdesc_count; i++) {
+               rdata = GET_DESC_DATA(ring, i);
+               rdesc = rdata->rdesc;
+
+               /* Initialize Tx descriptor
+                *   Set buffer 1 (lo) address to zero
+                *   Set buffer 1 (hi) address to zero
+                *   Reset all other control bits (IC, TTSE, B2L & B1L)
+                *   Reset all other control bits (OWN, CTXT, FD, LD, CPC, CIC,
+                *     etc)
+                */
+               rdesc->desc0 = 0;
+               rdesc->desc1 = 0;
+               rdesc->desc2 = 0;
+               rdesc->desc3 = 0;
+       }
+
+       /* Make sure everything is written to the descriptor(s) before
+        * telling the device about them
+        */
+       wmb();
+
+       /* Update the total number of Tx descriptors */
+       XGMAC_DMA_IOWRITE(channel, DMA_CH_TDRLR, ring->rdesc_count - 1);
+
+       /* Update the starting address of descriptor ring */
+       rdata = GET_DESC_DATA(ring, start_index);
+       XGMAC_DMA_IOWRITE(channel, DMA_CH_TDLR_HI,
+                         upper_32_bits(rdata->rdesc_dma));
+       XGMAC_DMA_IOWRITE(channel, DMA_CH_TDLR_LO,
+                         lower_32_bits(rdata->rdesc_dma));
+
+       DBGPR("<--tx_desc_init\n");
+}
+
+static void xgbe_rx_desc_reset(struct xgbe_ring_data *rdata)
+{
+       struct xgbe_ring_desc *rdesc = rdata->rdesc;
+
+       /* Reset the Rx descriptor
+        *   Set buffer 1 (lo) address to dma address (lo)
+        *   Set buffer 1 (hi) address to dma address (hi)
+        *   Set buffer 2 (lo) address to zero
+        *   Set buffer 2 (hi) address to zero and set control bits
+        *     OWN and INTE
+        */
+       rdesc->desc0 = cpu_to_le32(lower_32_bits(rdata->skb_dma));
+       rdesc->desc1 = cpu_to_le32(upper_32_bits(rdata->skb_dma));
+       rdesc->desc2 = 0;
+
+       rdesc->desc3 = 0;
+       if (rdata->interrupt)
+               XGMAC_SET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, INTE, 1);
+
+       /* Since the Rx DMA engine is likely running, make sure everything
+        * is written to the descriptor(s) before setting the OWN bit
+        * for the descriptor
+        */
+       wmb();
+
+       XGMAC_SET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, OWN, 1);
+
+       /* Make sure ownership is written to the descriptor */
+       wmb();
+}
+
+static void xgbe_rx_desc_init(struct xgbe_channel *channel)
+{
+       struct xgbe_prv_data *pdata = channel->pdata;
+       struct xgbe_ring *ring = channel->rx_ring;
+       struct xgbe_ring_data *rdata;
+       struct xgbe_ring_desc *rdesc;
+       unsigned int start_index = ring->cur;
+       unsigned int rx_coalesce, rx_frames;
+       unsigned int i;
+
+       DBGPR("-->rx_desc_init\n");
+
+       rx_coalesce = (pdata->rx_riwt || pdata->rx_frames) ? 1 : 0;
+       rx_frames = pdata->rx_frames;
+
+       /* Initialize all descriptors */
+       for (i = 0; i < ring->rdesc_count; i++) {
+               rdata = GET_DESC_DATA(ring, i);
+               rdesc = rdata->rdesc;
+
+               /* Initialize Rx descriptor
+                *   Set buffer 1 (lo) address to dma address (lo)
+                *   Set buffer 1 (hi) address to dma address (hi)
+                *   Set buffer 2 (lo) address to zero
+                *   Set buffer 2 (hi) address to zero and set control
+                *     bits OWN and INTE appropriateley
+                */
+               rdesc->desc0 = cpu_to_le32(lower_32_bits(rdata->skb_dma));
+               rdesc->desc1 = cpu_to_le32(upper_32_bits(rdata->skb_dma));
+               rdesc->desc2 = 0;
+               rdesc->desc3 = 0;
+               XGMAC_SET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, OWN, 1);
+               XGMAC_SET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, INTE, 1);
+               rdata->interrupt = 1;
+               if (rx_coalesce && (!rx_frames || ((i + 1) % rx_frames))) {
+                       /* Clear interrupt on completion bit */
+                       XGMAC_SET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, INTE,
+                                         0);
+                       rdata->interrupt = 0;
+               }
+       }
+
+       /* Make sure everything is written to the descriptors before
+        * telling the device about them
+        */
+       wmb();
+
+       /* Update the total number of Rx descriptors */
+       XGMAC_DMA_IOWRITE(channel, DMA_CH_RDRLR, ring->rdesc_count - 1);
+
+       /* Update the starting address of descriptor ring */
+       rdata = GET_DESC_DATA(ring, start_index);
+       XGMAC_DMA_IOWRITE(channel, DMA_CH_RDLR_HI,
+                         upper_32_bits(rdata->rdesc_dma));
+       XGMAC_DMA_IOWRITE(channel, DMA_CH_RDLR_LO,
+                         lower_32_bits(rdata->rdesc_dma));
+
+       /* Update the Rx Descriptor Tail Pointer */
+       rdata = GET_DESC_DATA(ring, start_index + ring->rdesc_count - 1);
+       XGMAC_DMA_IOWRITE(channel, DMA_CH_RDTR_LO,
+                         lower_32_bits(rdata->rdesc_dma));
+
+       DBGPR("<--rx_desc_init\n");
+}
+
+static void xgbe_pre_xmit(struct xgbe_channel *channel)
+{
+       struct xgbe_prv_data *pdata = channel->pdata;
+       struct xgbe_ring *ring = channel->tx_ring;
+       struct xgbe_ring_data *rdata;
+       struct xgbe_ring_desc *rdesc;
+       struct xgbe_packet_data *packet = &ring->packet_data;
+       unsigned int csum, tso, vlan;
+       unsigned int tso_context, vlan_context;
+       unsigned int tx_coalesce, tx_frames;
+       int start_index = ring->cur;
+       int i;
+
+       DBGPR("-->xgbe_pre_xmit\n");
+
+       csum = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
+                             CSUM_ENABLE);
+       tso = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
+                            TSO_ENABLE);
+       vlan = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
+                             VLAN_CTAG);
+
+       if (tso && (packet->mss != ring->tx.cur_mss))
+               tso_context = 1;
+       else
+               tso_context = 0;
+
+       if (vlan && (packet->vlan_ctag != ring->tx.cur_vlan_ctag))
+               vlan_context = 1;
+       else
+               vlan_context = 0;
+
+       tx_coalesce = (pdata->tx_usecs || pdata->tx_frames) ? 1 : 0;
+       tx_frames = pdata->tx_frames;
+       if (tx_coalesce && !channel->tx_timer_active)
+               ring->coalesce_count = 0;
+
+       rdata = GET_DESC_DATA(ring, ring->cur);
+       rdesc = rdata->rdesc;
+
+       /* Create a context descriptor if this is a TSO packet */
+       if (tso_context || vlan_context) {
+               if (tso_context) {
+                       DBGPR("  TSO context descriptor, mss=%u\n",
+                             packet->mss);
+
+                       /* Set the MSS size */
+                       XGMAC_SET_BITS_LE(rdesc->desc2, TX_CONTEXT_DESC2,
+                                         MSS, packet->mss);
+
+                       /* Mark it as a CONTEXT descriptor */
+                       XGMAC_SET_BITS_LE(rdesc->desc3, TX_CONTEXT_DESC3,
+                                         CTXT, 1);
+
+                       /* Indicate this descriptor contains the MSS */
+                       XGMAC_SET_BITS_LE(rdesc->desc3, TX_CONTEXT_DESC3,
+                                         TCMSSV, 1);
+
+                       ring->tx.cur_mss = packet->mss;
+               }
+
+               if (vlan_context) {
+                       DBGPR("  VLAN context descriptor, ctag=%u\n",
+                             packet->vlan_ctag);
+
+                       /* Mark it as a CONTEXT descriptor */
+                       XGMAC_SET_BITS_LE(rdesc->desc3, TX_CONTEXT_DESC3,
+                                         CTXT, 1);
+
+                       /* Set the VLAN tag */
+                       XGMAC_SET_BITS_LE(rdesc->desc3, TX_CONTEXT_DESC3,
+                                         VT, packet->vlan_ctag);
+
+                       /* Indicate this descriptor contains the VLAN tag */
+                       XGMAC_SET_BITS_LE(rdesc->desc3, TX_CONTEXT_DESC3,
+                                         VLTV, 1);
+
+                       ring->tx.cur_vlan_ctag = packet->vlan_ctag;
+               }
+
+               ring->cur++;
+               rdata = GET_DESC_DATA(ring, ring->cur);
+               rdesc = rdata->rdesc;
+       }
+
+       /* Update buffer address (for TSO this is the header) */
+       rdesc->desc0 =  cpu_to_le32(lower_32_bits(rdata->skb_dma));
+       rdesc->desc1 =  cpu_to_le32(upper_32_bits(rdata->skb_dma));
+
+       /* Update the buffer length */
+       XGMAC_SET_BITS_LE(rdesc->desc2, TX_NORMAL_DESC2, HL_B1L,
+                         rdata->skb_dma_len);
+
+       /* VLAN tag insertion check */
+       if (vlan)
+               XGMAC_SET_BITS_LE(rdesc->desc2, TX_NORMAL_DESC2, VTIR,
+                                 TX_NORMAL_DESC2_VLAN_INSERT);
+
+       /* Set IC bit based on Tx coalescing settings */
+       XGMAC_SET_BITS_LE(rdesc->desc2, TX_NORMAL_DESC2, IC, 1);
+       if (tx_coalesce && (!tx_frames ||
+                           (++ring->coalesce_count % tx_frames)))
+               /* Clear IC bit */
+               XGMAC_SET_BITS_LE(rdesc->desc2, TX_NORMAL_DESC2, IC, 0);
+
+       /* Mark it as First Descriptor */
+       XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, FD, 1);
+
+       /* Mark it as a NORMAL descriptor */
+       XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, CTXT, 0);
+
+       /* Set OWN bit if not the first descriptor */
+       if (ring->cur != start_index)
+               XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, OWN, 1);
+
+       if (tso) {
+               /* Enable TSO */
+               XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, TSE, 1);
+               XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, TCPPL,
+                                 packet->tcp_payload_len);
+               XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, TCPHDRLEN,
+                                 packet->tcp_header_len / 4);
+       } else {
+               /* Enable CRC and Pad Insertion */
+               XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, CPC, 0);
+
+               /* Enable HW CSUM */
+               if (csum)
+                       XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3,
+                                         CIC, 0x3);
+
+               /* Set the total length to be transmitted */
+               XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, FL,
+                                 packet->length);
+       }
+
+       for (i = ring->cur - start_index + 1; i < packet->rdesc_count; i++) {
+               ring->cur++;
+               rdata = GET_DESC_DATA(ring, ring->cur);
+               rdesc = rdata->rdesc;
+
+               /* Update buffer address */
+               rdesc->desc0 = cpu_to_le32(lower_32_bits(rdata->skb_dma));
+               rdesc->desc1 = cpu_to_le32(upper_32_bits(rdata->skb_dma));
+
+               /* Update the buffer length */
+               XGMAC_SET_BITS_LE(rdesc->desc2, TX_NORMAL_DESC2, HL_B1L,
+                                 rdata->skb_dma_len);
+
+               /* Set IC bit based on Tx coalescing settings */
+               XGMAC_SET_BITS_LE(rdesc->desc2, TX_NORMAL_DESC2, IC, 1);
+               if (tx_coalesce && (!tx_frames ||
+                                   (++ring->coalesce_count % tx_frames)))
+                       /* Clear IC bit */
+                       XGMAC_SET_BITS_LE(rdesc->desc2, TX_NORMAL_DESC2, IC, 0);
+
+               /* Set OWN bit */
+               XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, OWN, 1);
+
+               /* Mark it as NORMAL descriptor */
+               XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, CTXT, 0);
+
+               /* Enable HW CSUM */
+               if (csum)
+                       XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3,
+                                         CIC, 0x3);
+       }
+
+       /* Set LAST bit for the last descriptor */
+       XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, LD, 1);
+
+       /* In case the Tx DMA engine is running, make sure everything
+        * is written to the descriptor(s) before setting the OWN bit
+        * for the first descriptor
+        */
+       wmb();
+
+       /* Set OWN bit for the first descriptor */
+       rdata = GET_DESC_DATA(ring, start_index);
+       rdesc = rdata->rdesc;
+       XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, OWN, 1);
+
+#ifdef XGMAC_ENABLE_TX_DESC_DUMP
+       xgbe_dump_tx_desc(ring, start_index, packet->rdesc_count, 1);
+#endif
+
+       /* Make sure ownership is written to the descriptor */
+       wmb();
+
+       /* Issue a poll command to Tx DMA by writing address
+        * of next immediate free descriptor */
+       ring->cur++;
+       rdata = GET_DESC_DATA(ring, ring->cur);
+       XGMAC_DMA_IOWRITE(channel, DMA_CH_TDTR_LO,
+                         lower_32_bits(rdata->rdesc_dma));
+
+       /* Start the Tx coalescing timer */
+       if (tx_coalesce && !channel->tx_timer_active) {
+               channel->tx_timer_active = 1;
+               hrtimer_start(&channel->tx_timer,
+                             ktime_set(0, pdata->tx_usecs * NSEC_PER_USEC),
+                             HRTIMER_MODE_REL);
+       }
+
+       DBGPR("  %s: descriptors %u to %u written\n",
+             channel->name, start_index & (ring->rdesc_count - 1),
+             (ring->cur - 1) & (ring->rdesc_count - 1));
+
+       DBGPR("<--xgbe_pre_xmit\n");
+}
+
+static int xgbe_dev_read(struct xgbe_channel *channel)
+{
+       struct xgbe_ring *ring = channel->rx_ring;
+       struct xgbe_ring_data *rdata;
+       struct xgbe_ring_desc *rdesc;
+       struct xgbe_packet_data *packet = &ring->packet_data;
+       unsigned int err, etlt;
+
+       DBGPR("-->xgbe_dev_read: cur = %d\n", ring->cur);
+
+       rdata = GET_DESC_DATA(ring, ring->cur);
+       rdesc = rdata->rdesc;
+
+       /* Check for data availability */
+       if (XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, OWN))
+               return 1;
+
+#ifdef XGMAC_ENABLE_RX_DESC_DUMP
+       xgbe_dump_rx_desc(ring, rdesc, ring->cur);
+#endif
+
+       /* Get the packet length */
+       rdata->len = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, PL);
+
+       if (!XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, LD)) {
+               /* Not all the data has been transferred for this packet */
+               XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
+                              INCOMPLETE, 1);
+               return 0;
+       }
+
+       /* This is the last of the data for this packet */
+       XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
+                      INCOMPLETE, 0);
+
+       /* Set checksum done indicator as appropriate */
+       if (channel->pdata->netdev->features & NETIF_F_RXCSUM)
+               XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
+                              CSUM_DONE, 1);
+
+       /* Check for errors (only valid in last descriptor) */
+       err = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, ES);
+       etlt = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, ETLT);
+       DBGPR("  err=%u, etlt=%#x\n", err, etlt);
+
+       if (!err || (err && !etlt)) {
+               if (etlt == 0x09) {
+                       XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
+                                      VLAN_CTAG, 1);
+                       packet->vlan_ctag = XGMAC_GET_BITS_LE(rdesc->desc0,
+                                                             RX_NORMAL_DESC0,
+                                                             OVT);
+                       DBGPR("  vlan-ctag=0x%04x\n", packet->vlan_ctag);
+               }
+       } else {
+               if ((etlt == 0x05) || (etlt == 0x06))
+                       XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
+                                      CSUM_DONE, 0);
+               else
+                       XGMAC_SET_BITS(packet->errors, RX_PACKET_ERRORS,
+                                      FRAME, 1);
+       }
+
+       DBGPR("<--xgbe_dev_read: %s - descriptor=%u (cur=%d)\n", channel->name,
+             ring->cur & (ring->rdesc_count - 1), ring->cur);
+
+       return 0;
+}
+
+static int xgbe_is_context_desc(struct xgbe_ring_desc *rdesc)
+{
+       /* Rx and Tx share CTXT bit, so check TDES3.CTXT bit */
+       return XGMAC_GET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, CTXT);
+}
+
+static int xgbe_is_last_desc(struct xgbe_ring_desc *rdesc)
+{
+       /* Rx and Tx share LD bit, so check TDES3.LD bit */
+       return XGMAC_GET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, LD);
+}
+
+static void xgbe_save_interrupt_status(struct xgbe_channel *channel,
+                                      enum xgbe_int_state int_state)
+{
+       unsigned int dma_ch_ier;
+
+       if (int_state == XGMAC_INT_STATE_SAVE) {
+               channel->saved_ier = XGMAC_DMA_IOREAD(channel, DMA_CH_IER);
+               channel->saved_ier &= DMA_INTERRUPT_MASK;
+       } else {
+               dma_ch_ier = XGMAC_DMA_IOREAD(channel, DMA_CH_IER);
+               dma_ch_ier |= channel->saved_ier;
+               XGMAC_DMA_IOWRITE(channel, DMA_CH_IER, dma_ch_ier);
+       }
+}
+
+static int xgbe_enable_int(struct xgbe_channel *channel,
+                          enum xgbe_int int_id)
+{
+       switch (int_id) {
+       case XGMAC_INT_DMA_ISR_DC0IS:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, TIE, 1);
+               break;
+       case XGMAC_INT_DMA_CH_SR_TI:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, TIE, 1);
+               break;
+       case XGMAC_INT_DMA_CH_SR_TPS:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, TXSE, 1);
+               break;
+       case XGMAC_INT_DMA_CH_SR_TBU:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, TBUE, 1);
+               break;
+       case XGMAC_INT_DMA_CH_SR_RI:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, RIE, 1);
+               break;
+       case XGMAC_INT_DMA_CH_SR_RBU:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, RBUE, 1);
+               break;
+       case XGMAC_INT_DMA_CH_SR_RPS:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, RSE, 1);
+               break;
+       case XGMAC_INT_DMA_CH_SR_FBE:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, FBEE, 1);
+               break;
+       case XGMAC_INT_DMA_ALL:
+               xgbe_save_interrupt_status(channel, XGMAC_INT_STATE_RESTORE);
+               break;
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
+static int xgbe_disable_int(struct xgbe_channel *channel,
+                           enum xgbe_int int_id)
+{
+       unsigned int dma_ch_ier;
+
+       switch (int_id) {
+       case XGMAC_INT_DMA_ISR_DC0IS:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, TIE, 0);
+               break;
+       case XGMAC_INT_DMA_CH_SR_TI:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, TIE, 0);
+               break;
+       case XGMAC_INT_DMA_CH_SR_TPS:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, TXSE, 0);
+               break;
+       case XGMAC_INT_DMA_CH_SR_TBU:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, TBUE, 0);
+               break;
+       case XGMAC_INT_DMA_CH_SR_RI:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, RIE, 0);
+               break;
+       case XGMAC_INT_DMA_CH_SR_RBU:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, RBUE, 0);
+               break;
+       case XGMAC_INT_DMA_CH_SR_RPS:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, RSE, 0);
+               break;
+       case XGMAC_INT_DMA_CH_SR_FBE:
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_IER, FBEE, 0);
+               break;
+       case XGMAC_INT_DMA_ALL:
+               xgbe_save_interrupt_status(channel, XGMAC_INT_STATE_SAVE);
+
+               dma_ch_ier = XGMAC_DMA_IOREAD(channel, DMA_CH_IER);
+               dma_ch_ier &= ~DMA_INTERRUPT_MASK;
+               XGMAC_DMA_IOWRITE(channel, DMA_CH_IER, dma_ch_ier);
+               break;
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
+static int xgbe_exit(struct xgbe_prv_data *pdata)
+{
+       unsigned int count = 2000;
+
+       DBGPR("-->xgbe_exit\n");
+
+       /* Issue a software reset */
+       XGMAC_IOWRITE_BITS(pdata, DMA_MR, SWR, 1);
+       usleep_range(10, 15);
+
+       /* Poll Until Poll Condition */
+       while (count-- && XGMAC_IOREAD_BITS(pdata, DMA_MR, SWR))
+               usleep_range(500, 600);
+
+       if (!count)
+               return -EBUSY;
+
+       DBGPR("<--xgbe_exit\n");
+
+       return 0;
+}
+
+static int xgbe_flush_tx_queues(struct xgbe_prv_data *pdata)
+{
+       unsigned int i, count;
+
+       for (i = 0; i < pdata->hw_feat.tx_q_cnt; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_TQOMR, FTQ, 1);
+
+       /* Poll Until Poll Condition */
+       for (i = 0; i < pdata->hw_feat.tx_q_cnt; i++) {
+               count = 2000;
+               while (count-- && XGMAC_MTL_IOREAD_BITS(pdata, i,
+                                                       MTL_Q_TQOMR, FTQ))
+                       usleep_range(500, 600);
+
+               if (!count)
+                       return -EBUSY;
+       }
+
+       return 0;
+}
+
+static void xgbe_config_dma_bus(struct xgbe_prv_data *pdata)
+{
+       /* Set enhanced addressing mode */
+       XGMAC_IOWRITE_BITS(pdata, DMA_SBMR, EAME, 1);
+
+       /* Set the System Bus mode */
+       XGMAC_IOWRITE_BITS(pdata, DMA_SBMR, UNDEF, 1);
+}
+
+static void xgbe_config_dma_cache(struct xgbe_prv_data *pdata)
+{
+       unsigned int arcache, awcache;
+
+       arcache = 0;
+       XGMAC_SET_BITS(arcache, DMA_AXIARCR, DRC, DMA_ARCACHE_SETTING);
+       XGMAC_SET_BITS(arcache, DMA_AXIARCR, DRD, DMA_ARDOMAIN_SETTING);
+       XGMAC_SET_BITS(arcache, DMA_AXIARCR, TEC, DMA_ARCACHE_SETTING);
+       XGMAC_SET_BITS(arcache, DMA_AXIARCR, TED, DMA_ARDOMAIN_SETTING);
+       XGMAC_SET_BITS(arcache, DMA_AXIARCR, THC, DMA_ARCACHE_SETTING);
+       XGMAC_SET_BITS(arcache, DMA_AXIARCR, THD, DMA_ARDOMAIN_SETTING);
+       XGMAC_IOWRITE(pdata, DMA_AXIARCR, arcache);
+
+       awcache = 0;
+       XGMAC_SET_BITS(awcache, DMA_AXIAWCR, DWC, DMA_AWCACHE_SETTING);
+       XGMAC_SET_BITS(awcache, DMA_AXIAWCR, DWD, DMA_AWDOMAIN_SETTING);
+       XGMAC_SET_BITS(awcache, DMA_AXIAWCR, RPC, DMA_AWCACHE_SETTING);
+       XGMAC_SET_BITS(awcache, DMA_AXIAWCR, RPD, DMA_AWDOMAIN_SETTING);
+       XGMAC_SET_BITS(awcache, DMA_AXIAWCR, RHC, DMA_AWCACHE_SETTING);
+       XGMAC_SET_BITS(awcache, DMA_AXIAWCR, RHD, DMA_AWDOMAIN_SETTING);
+       XGMAC_SET_BITS(awcache, DMA_AXIAWCR, TDC, DMA_AWCACHE_SETTING);
+       XGMAC_SET_BITS(awcache, DMA_AXIAWCR, TDD, DMA_AWDOMAIN_SETTING);
+       XGMAC_IOWRITE(pdata, DMA_AXIAWCR, awcache);
+}
+
+static void xgbe_config_mtl_mode(struct xgbe_prv_data *pdata)
+{
+       unsigned int i;
+
+       /* Set Tx to weighted round robin scheduling algorithm (when
+        * traffic class is using ETS algorithm)
+        */
+       XGMAC_IOWRITE_BITS(pdata, MTL_OMR, ETSALG, MTL_ETSALG_WRR);
+
+       /* Set Tx traffic classes to strict priority algorithm */
+       for (i = 0; i < XGBE_TC_CNT; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_TC_ETSCR, TSA, MTL_TSA_SP);
+
+       /* Set Rx to strict priority algorithm */
+       XGMAC_IOWRITE_BITS(pdata, MTL_OMR, RAA, MTL_RAA_SP);
+}
+
+static unsigned int xgbe_calculate_per_queue_fifo(unsigned long fifo_size,
+                                                 unsigned char queue_count)
+{
+       unsigned int q_fifo_size = 0;
+       enum xgbe_mtl_fifo_size p_fifo = XGMAC_MTL_FIFO_SIZE_256;
+
+       /* Calculate Tx/Rx fifo share per queue */
+       switch (fifo_size) {
+       case 0:
+               q_fifo_size = FIFO_SIZE_B(128);
+               break;
+       case 1:
+               q_fifo_size = FIFO_SIZE_B(256);
+               break;
+       case 2:
+               q_fifo_size = FIFO_SIZE_B(512);
+               break;
+       case 3:
+               q_fifo_size = FIFO_SIZE_KB(1);
+               break;
+       case 4:
+               q_fifo_size = FIFO_SIZE_KB(2);
+               break;
+       case 5:
+               q_fifo_size = FIFO_SIZE_KB(4);
+               break;
+       case 6:
+               q_fifo_size = FIFO_SIZE_KB(8);
+               break;
+       case 7:
+               q_fifo_size = FIFO_SIZE_KB(16);
+               break;
+       case 8:
+               q_fifo_size = FIFO_SIZE_KB(32);
+               break;
+       case 9:
+               q_fifo_size = FIFO_SIZE_KB(64);
+               break;
+       case 10:
+               q_fifo_size = FIFO_SIZE_KB(128);
+               break;
+       case 11:
+               q_fifo_size = FIFO_SIZE_KB(256);
+               break;
+       }
+       q_fifo_size = q_fifo_size / queue_count;
+
+       /* Set the queue fifo size programmable value */
+       if (q_fifo_size >= FIFO_SIZE_KB(256))
+               p_fifo = XGMAC_MTL_FIFO_SIZE_256K;
+       else if (q_fifo_size >= FIFO_SIZE_KB(128))
+               p_fifo = XGMAC_MTL_FIFO_SIZE_128K;
+       else if (q_fifo_size >= FIFO_SIZE_KB(64))
+               p_fifo = XGMAC_MTL_FIFO_SIZE_64K;
+       else if (q_fifo_size >= FIFO_SIZE_KB(32))
+               p_fifo = XGMAC_MTL_FIFO_SIZE_32K;
+       else if (q_fifo_size >= FIFO_SIZE_KB(16))
+               p_fifo = XGMAC_MTL_FIFO_SIZE_16K;
+       else if (q_fifo_size >= FIFO_SIZE_KB(8))
+               p_fifo = XGMAC_MTL_FIFO_SIZE_8K;
+       else if (q_fifo_size >= FIFO_SIZE_KB(4))
+               p_fifo = XGMAC_MTL_FIFO_SIZE_4K;
+       else if (q_fifo_size >= FIFO_SIZE_KB(2))
+               p_fifo = XGMAC_MTL_FIFO_SIZE_2K;
+       else if (q_fifo_size >= FIFO_SIZE_KB(1))
+               p_fifo = XGMAC_MTL_FIFO_SIZE_1K;
+       else if (q_fifo_size >= FIFO_SIZE_B(512))
+               p_fifo = XGMAC_MTL_FIFO_SIZE_512;
+       else if (q_fifo_size >= FIFO_SIZE_B(256))
+               p_fifo = XGMAC_MTL_FIFO_SIZE_256;
+
+       return p_fifo;
+}
+
+static void xgbe_config_tx_fifo_size(struct xgbe_prv_data *pdata)
+{
+       enum xgbe_mtl_fifo_size fifo_size;
+       unsigned int i;
+
+       fifo_size = xgbe_calculate_per_queue_fifo(pdata->hw_feat.tx_fifo_size,
+                                                 pdata->hw_feat.tx_q_cnt);
+
+       for (i = 0; i < pdata->hw_feat.tx_q_cnt; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_TQOMR, TQS, fifo_size);
+
+       netdev_notice(pdata->netdev, "%d Tx queues, %d byte fifo per queue\n",
+                     pdata->hw_feat.tx_q_cnt, ((fifo_size + 1) * 256));
+}
+
+static void xgbe_config_rx_fifo_size(struct xgbe_prv_data *pdata)
+{
+       enum xgbe_mtl_fifo_size fifo_size;
+       unsigned int i;
+
+       fifo_size = xgbe_calculate_per_queue_fifo(pdata->hw_feat.rx_fifo_size,
+                                                 pdata->hw_feat.rx_q_cnt);
+
+       for (i = 0; i < pdata->hw_feat.rx_q_cnt; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, RQS, fifo_size);
+
+       netdev_notice(pdata->netdev, "%d Rx queues, %d byte fifo per queue\n",
+                     pdata->hw_feat.rx_q_cnt, ((fifo_size + 1) * 256));
+}
+
+static void xgbe_config_rx_queue_mapping(struct xgbe_prv_data *pdata)
+{
+       unsigned int i, reg, reg_val;
+       unsigned int q_count = pdata->hw_feat.rx_q_cnt;
+
+       /* Select dynamic mapping of MTL Rx queue to DMA Rx channel */
+       reg = MTL_RQDCM0R;
+       reg_val = 0;
+       for (i = 0; i < q_count;) {
+               reg_val |= (0x80 << ((i++ % MTL_RQDCM_Q_PER_REG) << 3));
+
+               if ((i % MTL_RQDCM_Q_PER_REG) && (i != q_count))
+                       continue;
+
+               XGMAC_IOWRITE(pdata, reg, reg_val);
+
+               reg += MTL_RQDCM_INC;
+               reg_val = 0;
+       }
+}
+
+static void xgbe_config_flow_control_threshold(struct xgbe_prv_data *pdata)
+{
+       unsigned int i;
+
+       for (i = 0; i < pdata->hw_feat.rx_q_cnt; i++) {
+               /* Activate flow control when less than 4k left in fifo */
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, RFA, 2);
+
+               /* De-activate flow control when more than 6k left in fifo */
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, RFD, 4);
+       }
+}
+
+static void xgbe_config_mac_address(struct xgbe_prv_data *pdata)
+{
+       xgbe_set_mac_address(pdata, pdata->netdev->dev_addr);
+}
+
+static void xgbe_config_jumbo_enable(struct xgbe_prv_data *pdata)
+{
+       unsigned int val;
+
+       val = (pdata->netdev->mtu > XGMAC_STD_PACKET_MTU) ? 1 : 0;
+
+       XGMAC_IOWRITE_BITS(pdata, MAC_RCR, JE, val);
+}
+
+static void xgbe_config_checksum_offload(struct xgbe_prv_data *pdata)
+{
+       if (pdata->netdev->features & NETIF_F_RXCSUM)
+               xgbe_enable_rx_csum(pdata);
+       else
+               xgbe_disable_rx_csum(pdata);
+}
+
+static void xgbe_config_vlan_support(struct xgbe_prv_data *pdata)
+{
+       if (pdata->netdev->features & NETIF_F_HW_VLAN_CTAG_RX)
+               xgbe_enable_rx_vlan_stripping(pdata);
+       else
+               xgbe_disable_rx_vlan_stripping(pdata);
+}
+
+static void xgbe_tx_mmc_int(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_mmc_stats *stats = &pdata->mmc_stats;
+       unsigned int mmc_isr = XGMAC_IOREAD(pdata, MMC_TISR);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXOCTETCOUNT_GB))
+               stats->txoctetcount_gb +=
+                       XGMAC_IOREAD(pdata, MMC_TXOCTETCOUNT_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXFRAMECOUNT_GB))
+               stats->txframecount_gb +=
+                       XGMAC_IOREAD(pdata, MMC_TXFRAMECOUNT_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXBROADCASTFRAMES_G))
+               stats->txbroadcastframes_g +=
+                       XGMAC_IOREAD(pdata, MMC_TXBROADCASTFRAMES_G_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXMULTICASTFRAMES_G))
+               stats->txmulticastframes_g +=
+                       XGMAC_IOREAD(pdata, MMC_TXMULTICASTFRAMES_G_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TX64OCTETS_GB))
+               stats->tx64octets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_TX64OCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TX65TO127OCTETS_GB))
+               stats->tx65to127octets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_TX65TO127OCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TX128TO255OCTETS_GB))
+               stats->tx128to255octets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_TX128TO255OCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TX256TO511OCTETS_GB))
+               stats->tx256to511octets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_TX256TO511OCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TX512TO1023OCTETS_GB))
+               stats->tx512to1023octets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_TX512TO1023OCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TX1024TOMAXOCTETS_GB))
+               stats->tx1024tomaxoctets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_TX1024TOMAXOCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXUNICASTFRAMES_GB))
+               stats->txunicastframes_gb +=
+                       XGMAC_IOREAD(pdata, MMC_TXUNICASTFRAMES_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXMULTICASTFRAMES_GB))
+               stats->txmulticastframes_gb +=
+                       XGMAC_IOREAD(pdata, MMC_TXMULTICASTFRAMES_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXBROADCASTFRAMES_GB))
+               stats->txbroadcastframes_g +=
+                       XGMAC_IOREAD(pdata, MMC_TXBROADCASTFRAMES_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXUNDERFLOWERROR))
+               stats->txunderflowerror +=
+                       XGMAC_IOREAD(pdata, MMC_TXUNDERFLOWERROR_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXOCTETCOUNT_G))
+               stats->txoctetcount_g +=
+                       XGMAC_IOREAD(pdata, MMC_TXOCTETCOUNT_G_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXFRAMECOUNT_G))
+               stats->txframecount_g +=
+                       XGMAC_IOREAD(pdata, MMC_TXFRAMECOUNT_G_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXPAUSEFRAMES))
+               stats->txpauseframes +=
+                       XGMAC_IOREAD(pdata, MMC_TXPAUSEFRAMES_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_TISR, TXVLANFRAMES_G))
+               stats->txvlanframes_g +=
+                       XGMAC_IOREAD(pdata, MMC_TXVLANFRAMES_G_LO);
+}
+
+static void xgbe_rx_mmc_int(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_mmc_stats *stats = &pdata->mmc_stats;
+       unsigned int mmc_isr = XGMAC_IOREAD(pdata, MMC_RISR);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXFRAMECOUNT_GB))
+               stats->rxframecount_gb +=
+                       XGMAC_IOREAD(pdata, MMC_RXFRAMECOUNT_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXOCTETCOUNT_GB))
+               stats->rxoctetcount_gb +=
+                       XGMAC_IOREAD(pdata, MMC_RXOCTETCOUNT_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXOCTETCOUNT_G))
+               stats->rxoctetcount_g +=
+                       XGMAC_IOREAD(pdata, MMC_RXOCTETCOUNT_G_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXBROADCASTFRAMES_G))
+               stats->rxbroadcastframes_g +=
+                       XGMAC_IOREAD(pdata, MMC_RXBROADCASTFRAMES_G_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXMULTICASTFRAMES_G))
+               stats->rxmulticastframes_g +=
+                       XGMAC_IOREAD(pdata, MMC_RXMULTICASTFRAMES_G_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXCRCERROR))
+               stats->rxcrcerror +=
+                       XGMAC_IOREAD(pdata, MMC_RXCRCERROR_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXRUNTERROR))
+               stats->rxrunterror +=
+                       XGMAC_IOREAD(pdata, MMC_RXRUNTERROR);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXJABBERERROR))
+               stats->rxjabbererror +=
+                       XGMAC_IOREAD(pdata, MMC_RXJABBERERROR);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXUNDERSIZE_G))
+               stats->rxundersize_g +=
+                       XGMAC_IOREAD(pdata, MMC_RXUNDERSIZE_G);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXOVERSIZE_G))
+               stats->rxoversize_g +=
+                       XGMAC_IOREAD(pdata, MMC_RXOVERSIZE_G);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RX64OCTETS_GB))
+               stats->rx64octets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_RX64OCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RX65TO127OCTETS_GB))
+               stats->rx65to127octets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_RX65TO127OCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RX128TO255OCTETS_GB))
+               stats->rx128to255octets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_RX128TO255OCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RX256TO511OCTETS_GB))
+               stats->rx256to511octets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_RX256TO511OCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RX512TO1023OCTETS_GB))
+               stats->rx512to1023octets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_RX512TO1023OCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RX1024TOMAXOCTETS_GB))
+               stats->rx1024tomaxoctets_gb +=
+                       XGMAC_IOREAD(pdata, MMC_RX1024TOMAXOCTETS_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXUNICASTFRAMES_G))
+               stats->rxunicastframes_g +=
+                       XGMAC_IOREAD(pdata, MMC_RXUNICASTFRAMES_G_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXLENGTHERROR))
+               stats->rxlengtherror +=
+                       XGMAC_IOREAD(pdata, MMC_RXLENGTHERROR_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXOUTOFRANGETYPE))
+               stats->rxoutofrangetype +=
+                       XGMAC_IOREAD(pdata, MMC_RXOUTOFRANGETYPE_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXPAUSEFRAMES))
+               stats->rxpauseframes +=
+                       XGMAC_IOREAD(pdata, MMC_RXPAUSEFRAMES_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXFIFOOVERFLOW))
+               stats->rxfifooverflow +=
+                       XGMAC_IOREAD(pdata, MMC_RXFIFOOVERFLOW_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXVLANFRAMES_GB))
+               stats->rxvlanframes_gb +=
+                       XGMAC_IOREAD(pdata, MMC_RXVLANFRAMES_GB_LO);
+
+       if (XGMAC_GET_BITS(mmc_isr, MMC_RISR, RXWATCHDOGERROR))
+               stats->rxwatchdogerror +=
+                       XGMAC_IOREAD(pdata, MMC_RXWATCHDOGERROR);
+}
+
+static void xgbe_read_mmc_stats(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_mmc_stats *stats = &pdata->mmc_stats;
+
+       /* Freeze counters */
+       XGMAC_IOWRITE_BITS(pdata, MMC_CR, MCF, 1);
+
+       stats->txoctetcount_gb +=
+               XGMAC_IOREAD(pdata, MMC_TXOCTETCOUNT_GB_LO);
+
+       stats->txframecount_gb +=
+               XGMAC_IOREAD(pdata, MMC_TXFRAMECOUNT_GB_LO);
+
+       stats->txbroadcastframes_g +=
+               XGMAC_IOREAD(pdata, MMC_TXBROADCASTFRAMES_G_LO);
+
+       stats->txmulticastframes_g +=
+               XGMAC_IOREAD(pdata, MMC_TXMULTICASTFRAMES_G_LO);
+
+       stats->tx64octets_gb +=
+               XGMAC_IOREAD(pdata, MMC_TX64OCTETS_GB_LO);
+
+       stats->tx65to127octets_gb +=
+               XGMAC_IOREAD(pdata, MMC_TX65TO127OCTETS_GB_LO);
+
+       stats->tx128to255octets_gb +=
+               XGMAC_IOREAD(pdata, MMC_TX128TO255OCTETS_GB_LO);
+
+       stats->tx256to511octets_gb +=
+               XGMAC_IOREAD(pdata, MMC_TX256TO511OCTETS_GB_LO);
+
+       stats->tx512to1023octets_gb +=
+               XGMAC_IOREAD(pdata, MMC_TX512TO1023OCTETS_GB_LO);
+
+       stats->tx1024tomaxoctets_gb +=
+               XGMAC_IOREAD(pdata, MMC_TX1024TOMAXOCTETS_GB_LO);
+
+       stats->txunicastframes_gb +=
+               XGMAC_IOREAD(pdata, MMC_TXUNICASTFRAMES_GB_LO);
+
+       stats->txmulticastframes_gb +=
+               XGMAC_IOREAD(pdata, MMC_TXMULTICASTFRAMES_GB_LO);
+
+       stats->txbroadcastframes_g +=
+               XGMAC_IOREAD(pdata, MMC_TXBROADCASTFRAMES_GB_LO);
+
+       stats->txunderflowerror +=
+               XGMAC_IOREAD(pdata, MMC_TXUNDERFLOWERROR_LO);
+
+       stats->txoctetcount_g +=
+               XGMAC_IOREAD(pdata, MMC_TXOCTETCOUNT_G_LO);
+
+       stats->txframecount_g +=
+               XGMAC_IOREAD(pdata, MMC_TXFRAMECOUNT_G_LO);
+
+       stats->txpauseframes +=
+               XGMAC_IOREAD(pdata, MMC_TXPAUSEFRAMES_LO);
+
+       stats->txvlanframes_g +=
+               XGMAC_IOREAD(pdata, MMC_TXVLANFRAMES_G_LO);
+
+       stats->rxframecount_gb +=
+               XGMAC_IOREAD(pdata, MMC_RXFRAMECOUNT_GB_LO);
+
+       stats->rxoctetcount_gb +=
+               XGMAC_IOREAD(pdata, MMC_RXOCTETCOUNT_GB_LO);
+
+       stats->rxoctetcount_g +=
+               XGMAC_IOREAD(pdata, MMC_RXOCTETCOUNT_G_LO);
+
+       stats->rxbroadcastframes_g +=
+               XGMAC_IOREAD(pdata, MMC_RXBROADCASTFRAMES_G_LO);
+
+       stats->rxmulticastframes_g +=
+               XGMAC_IOREAD(pdata, MMC_RXMULTICASTFRAMES_G_LO);
+
+       stats->rxcrcerror +=
+               XGMAC_IOREAD(pdata, MMC_RXCRCERROR_LO);
+
+       stats->rxrunterror +=
+               XGMAC_IOREAD(pdata, MMC_RXRUNTERROR);
+
+       stats->rxjabbererror +=
+               XGMAC_IOREAD(pdata, MMC_RXJABBERERROR);
+
+       stats->rxundersize_g +=
+               XGMAC_IOREAD(pdata, MMC_RXUNDERSIZE_G);
+
+       stats->rxoversize_g +=
+               XGMAC_IOREAD(pdata, MMC_RXOVERSIZE_G);
+
+       stats->rx64octets_gb +=
+               XGMAC_IOREAD(pdata, MMC_RX64OCTETS_GB_LO);
+
+       stats->rx65to127octets_gb +=
+               XGMAC_IOREAD(pdata, MMC_RX65TO127OCTETS_GB_LO);
+
+       stats->rx128to255octets_gb +=
+               XGMAC_IOREAD(pdata, MMC_RX128TO255OCTETS_GB_LO);
+
+       stats->rx256to511octets_gb +=
+               XGMAC_IOREAD(pdata, MMC_RX256TO511OCTETS_GB_LO);
+
+       stats->rx512to1023octets_gb +=
+               XGMAC_IOREAD(pdata, MMC_RX512TO1023OCTETS_GB_LO);
+
+       stats->rx1024tomaxoctets_gb +=
+               XGMAC_IOREAD(pdata, MMC_RX1024TOMAXOCTETS_GB_LO);
+
+       stats->rxunicastframes_g +=
+               XGMAC_IOREAD(pdata, MMC_RXUNICASTFRAMES_G_LO);
+
+       stats->rxlengtherror +=
+               XGMAC_IOREAD(pdata, MMC_RXLENGTHERROR_LO);
+
+       stats->rxoutofrangetype +=
+               XGMAC_IOREAD(pdata, MMC_RXOUTOFRANGETYPE_LO);
+
+       stats->rxpauseframes +=
+               XGMAC_IOREAD(pdata, MMC_RXPAUSEFRAMES_LO);
+
+       stats->rxfifooverflow +=
+               XGMAC_IOREAD(pdata, MMC_RXFIFOOVERFLOW_LO);
+
+       stats->rxvlanframes_gb +=
+               XGMAC_IOREAD(pdata, MMC_RXVLANFRAMES_GB_LO);
+
+       stats->rxwatchdogerror +=
+               XGMAC_IOREAD(pdata, MMC_RXWATCHDOGERROR);
+
+       /* Un-freeze counters */
+       XGMAC_IOWRITE_BITS(pdata, MMC_CR, MCF, 0);
+}
+
+static void xgbe_config_mmc(struct xgbe_prv_data *pdata)
+{
+       /* Set counters to reset on read */
+       XGMAC_IOWRITE_BITS(pdata, MMC_CR, ROR, 1);
+
+       /* Reset the counters */
+       XGMAC_IOWRITE_BITS(pdata, MMC_CR, CR, 1);
+}
+
+static void xgbe_enable_tx(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       /* Enable each Tx DMA channel */
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->tx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_TCR, ST, 1);
+       }
+
+       /* Enable each Tx queue */
+       for (i = 0; i < pdata->hw_feat.tx_q_cnt; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_TQOMR, TXQEN,
+                                      MTL_Q_ENABLED);
+
+       /* Enable MAC Tx */
+       XGMAC_IOWRITE_BITS(pdata, MAC_TCR, TE, 1);
+}
+
+static void xgbe_disable_tx(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       /* Disable MAC Tx */
+       XGMAC_IOWRITE_BITS(pdata, MAC_TCR, TE, 0);
+
+       /* Disable each Tx queue */
+       for (i = 0; i < pdata->hw_feat.tx_q_cnt; i++)
+               XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_TQOMR, TXQEN, 0);
+
+       /* Disable each Tx DMA channel */
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->tx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_TCR, ST, 0);
+       }
+}
+
+static void xgbe_enable_rx(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int reg_val, i;
+
+       /* Enable each Rx DMA channel */
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->rx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_RCR, SR, 1);
+       }
+
+       /* Enable each Rx queue */
+       reg_val = 0;
+       for (i = 0; i < pdata->hw_feat.rx_q_cnt; i++)
+               reg_val |= (0x02 << (i << 1));
+       XGMAC_IOWRITE(pdata, MAC_RQC0R, reg_val);
+
+       /* Enable MAC Rx */
+       XGMAC_IOWRITE_BITS(pdata, MAC_RCR, DCRCC, 1);
+       XGMAC_IOWRITE_BITS(pdata, MAC_RCR, CST, 1);
+       XGMAC_IOWRITE_BITS(pdata, MAC_RCR, ACS, 1);
+       XGMAC_IOWRITE_BITS(pdata, MAC_RCR, RE, 1);
+}
+
+static void xgbe_disable_rx(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       /* Disable MAC Rx */
+       XGMAC_IOWRITE_BITS(pdata, MAC_RCR, DCRCC, 0);
+       XGMAC_IOWRITE_BITS(pdata, MAC_RCR, CST, 0);
+       XGMAC_IOWRITE_BITS(pdata, MAC_RCR, ACS, 0);
+       XGMAC_IOWRITE_BITS(pdata, MAC_RCR, RE, 0);
+
+       /* Disable each Rx queue */
+       XGMAC_IOWRITE(pdata, MAC_RQC0R, 0);
+
+       /* Disable each Rx DMA channel */
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->rx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_RCR, SR, 0);
+       }
+}
+
+static void xgbe_powerup_tx(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       /* Enable each Tx DMA channel */
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->tx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_TCR, ST, 1);
+       }
+
+       /* Enable MAC Tx */
+       XGMAC_IOWRITE_BITS(pdata, MAC_TCR, TE, 1);
+}
+
+static void xgbe_powerdown_tx(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       /* Disable MAC Tx */
+       XGMAC_IOWRITE_BITS(pdata, MAC_TCR, TE, 0);
+
+       /* Disable each Tx DMA channel */
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->tx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_TCR, ST, 0);
+       }
+}
+
+static void xgbe_powerup_rx(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       /* Enable each Rx DMA channel */
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->rx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_RCR, SR, 1);
+       }
+}
+
+static void xgbe_powerdown_rx(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       /* Disable each Rx DMA channel */
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->rx_ring)
+                       break;
+
+               XGMAC_DMA_IOWRITE_BITS(channel, DMA_CH_RCR, SR, 0);
+       }
+}
+
+static int xgbe_init(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_desc_if *desc_if = &pdata->desc_if;
+       int ret;
+
+       DBGPR("-->xgbe_init\n");
+
+       /* Flush Tx queues */
+       ret = xgbe_flush_tx_queues(pdata);
+       if (ret)
+               return ret;
+
+       /*
+        * Initialize DMA related features
+        */
+       xgbe_config_dma_bus(pdata);
+       xgbe_config_dma_cache(pdata);
+       xgbe_config_osp_mode(pdata);
+       xgbe_config_pblx8(pdata);
+       xgbe_config_tx_pbl_val(pdata);
+       xgbe_config_rx_pbl_val(pdata);
+       xgbe_config_rx_coalesce(pdata);
+       xgbe_config_tx_coalesce(pdata);
+       xgbe_config_rx_buffer_size(pdata);
+       xgbe_config_tso_mode(pdata);
+       desc_if->wrapper_tx_desc_init(pdata);
+       desc_if->wrapper_rx_desc_init(pdata);
+       xgbe_enable_dma_interrupts(pdata);
+
+       /*
+        * Initialize MTL related features
+        */
+       xgbe_config_mtl_mode(pdata);
+       xgbe_config_rx_queue_mapping(pdata);
+       /*TODO: Program the priorities mapped to the Selected Traffic Classes
+               in MTL_TC_Prty_Map0-3 registers */
+       xgbe_config_tsf_mode(pdata, pdata->tx_sf_mode);
+       xgbe_config_rsf_mode(pdata, pdata->rx_sf_mode);
+       xgbe_config_tx_threshold(pdata, pdata->tx_threshold);
+       xgbe_config_rx_threshold(pdata, pdata->rx_threshold);
+       xgbe_config_tx_fifo_size(pdata);
+       xgbe_config_rx_fifo_size(pdata);
+       xgbe_config_flow_control_threshold(pdata);
+       /*TODO: Queue to Traffic Class Mapping (Q2TCMAP) */
+       /*TODO: Error Packet and undersized good Packet forwarding enable
+               (FEP and FUP)
+        */
+       xgbe_enable_mtl_interrupts(pdata);
+
+       /* Transmit Class Weight */
+       XGMAC_IOWRITE_BITS(pdata, MTL_Q_TCQWR, QW, 0x10);
+
+       /*
+        * Initialize MAC related features
+        */
+       xgbe_config_mac_address(pdata);
+       xgbe_config_jumbo_enable(pdata);
+       xgbe_config_flow_control(pdata);
+       xgbe_config_checksum_offload(pdata);
+       xgbe_config_vlan_support(pdata);
+       xgbe_config_mmc(pdata);
+       xgbe_enable_mac_interrupts(pdata);
+
+       DBGPR("<--xgbe_init\n");
+
+       return 0;
+}
+
+void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *hw_if)
+{
+       DBGPR("-->xgbe_init_function_ptrs\n");
+
+       hw_if->tx_complete = xgbe_tx_complete;
+
+       hw_if->set_promiscuous_mode = xgbe_set_promiscuous_mode;
+       hw_if->set_all_multicast_mode = xgbe_set_all_multicast_mode;
+       hw_if->set_addn_mac_addrs = xgbe_set_addn_mac_addrs;
+       hw_if->set_mac_address = xgbe_set_mac_address;
+
+       hw_if->enable_rx_csum = xgbe_enable_rx_csum;
+       hw_if->disable_rx_csum = xgbe_disable_rx_csum;
+
+       hw_if->enable_rx_vlan_stripping = xgbe_enable_rx_vlan_stripping;
+       hw_if->disable_rx_vlan_stripping = xgbe_disable_rx_vlan_stripping;
+
+       hw_if->read_mmd_regs = xgbe_read_mmd_regs;
+       hw_if->write_mmd_regs = xgbe_write_mmd_regs;
+
+       hw_if->set_gmii_speed = xgbe_set_gmii_speed;
+       hw_if->set_gmii_2500_speed = xgbe_set_gmii_2500_speed;
+       hw_if->set_xgmii_speed = xgbe_set_xgmii_speed;
+
+       hw_if->enable_tx = xgbe_enable_tx;
+       hw_if->disable_tx = xgbe_disable_tx;
+       hw_if->enable_rx = xgbe_enable_rx;
+       hw_if->disable_rx = xgbe_disable_rx;
+
+       hw_if->powerup_tx = xgbe_powerup_tx;
+       hw_if->powerdown_tx = xgbe_powerdown_tx;
+       hw_if->powerup_rx = xgbe_powerup_rx;
+       hw_if->powerdown_rx = xgbe_powerdown_rx;
+
+       hw_if->pre_xmit = xgbe_pre_xmit;
+       hw_if->dev_read = xgbe_dev_read;
+       hw_if->enable_int = xgbe_enable_int;
+       hw_if->disable_int = xgbe_disable_int;
+       hw_if->init = xgbe_init;
+       hw_if->exit = xgbe_exit;
+
+       /* Descriptor related Sequences have to be initialized here */
+       hw_if->tx_desc_init = xgbe_tx_desc_init;
+       hw_if->rx_desc_init = xgbe_rx_desc_init;
+       hw_if->tx_desc_reset = xgbe_tx_desc_reset;
+       hw_if->rx_desc_reset = xgbe_rx_desc_reset;
+       hw_if->is_last_desc = xgbe_is_last_desc;
+       hw_if->is_context_desc = xgbe_is_context_desc;
+
+       /* For FLOW ctrl */
+       hw_if->config_tx_flow_control = xgbe_config_tx_flow_control;
+       hw_if->config_rx_flow_control = xgbe_config_rx_flow_control;
+
+       /* For RX coalescing */
+       hw_if->config_rx_coalesce = xgbe_config_rx_coalesce;
+       hw_if->config_tx_coalesce = xgbe_config_tx_coalesce;
+       hw_if->usec_to_riwt = xgbe_usec_to_riwt;
+       hw_if->riwt_to_usec = xgbe_riwt_to_usec;
+
+       /* For RX and TX threshold config */
+       hw_if->config_rx_threshold = xgbe_config_rx_threshold;
+       hw_if->config_tx_threshold = xgbe_config_tx_threshold;
+
+       /* For RX and TX Store and Forward Mode config */
+       hw_if->config_rsf_mode = xgbe_config_rsf_mode;
+       hw_if->config_tsf_mode = xgbe_config_tsf_mode;
+
+       /* For TX DMA Operating on Second Frame config */
+       hw_if->config_osp_mode = xgbe_config_osp_mode;
+
+       /* For RX and TX PBL config */
+       hw_if->config_rx_pbl_val = xgbe_config_rx_pbl_val;
+       hw_if->get_rx_pbl_val = xgbe_get_rx_pbl_val;
+       hw_if->config_tx_pbl_val = xgbe_config_tx_pbl_val;
+       hw_if->get_tx_pbl_val = xgbe_get_tx_pbl_val;
+       hw_if->config_pblx8 = xgbe_config_pblx8;
+
+       /* For MMC statistics support */
+       hw_if->tx_mmc_int = xgbe_tx_mmc_int;
+       hw_if->rx_mmc_int = xgbe_rx_mmc_int;
+       hw_if->read_mmc_stats = xgbe_read_mmc_stats;
+
+       DBGPR("<--xgbe_init_function_ptrs\n");
+}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
new file mode 100644 (file)
index 0000000..cfe3d93
--- /dev/null
@@ -0,0 +1,1351 @@
+/*
+ * AMD 10Gb Ethernet driver
+ *
+ * This file is available to you under your choice of the following two
+ * licenses:
+ *
+ * License 1: GPLv2
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ *
+ * This file is free software; you may copy, redistribute 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 file 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, see <http://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and the Software, to deal in the Software
+ *     without restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ *     of the Software, and to permit persons to whom the Software is furnished
+ *     to do so, subject to the following conditions:
+ *
+ *     The above copyright notice and this permission notice shall be included
+ *     in all copies or substantial portions of the Software.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ *
+ *
+ * License 2: Modified BSD
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ * 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 of Advanced Micro Devices, Inc. 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 <COPYRIGHT HOLDER> 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.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and the Software, to deal in the Software
+ *     without restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ *     of the Software, and to permit persons to whom the Software is furnished
+ *     to do so, subject to the following conditions:
+ *
+ *     The above copyright notice and this permission notice shall be included
+ *     in all copies or substantial portions of the Software.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ */
+
+#include <linux/spinlock.h>
+#include <linux/tcp.h>
+#include <linux/if_vlan.h>
+#include <linux/phy.h>
+#include <net/busy_poll.h>
+#include <linux/clk.h>
+#include <linux/if_ether.h>
+
+#include "xgbe.h"
+#include "xgbe-common.h"
+
+
+static int xgbe_poll(struct napi_struct *, int);
+static void xgbe_set_rx_mode(struct net_device *);
+
+static inline unsigned int xgbe_tx_avail_desc(struct xgbe_ring *ring)
+{
+       return (ring->rdesc_count - (ring->cur - ring->dirty));
+}
+
+static int xgbe_calc_rx_buf_size(struct net_device *netdev, unsigned int mtu)
+{
+       unsigned int rx_buf_size;
+
+       if (mtu > XGMAC_JUMBO_PACKET_MTU) {
+               netdev_alert(netdev, "MTU exceeds maximum supported value\n");
+               return -EINVAL;
+       }
+
+       rx_buf_size = mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
+       if (rx_buf_size < RX_MIN_BUF_SIZE)
+               rx_buf_size = RX_MIN_BUF_SIZE;
+       rx_buf_size = (rx_buf_size + RX_BUF_ALIGN - 1) & ~(RX_BUF_ALIGN - 1);
+
+       return rx_buf_size;
+}
+
+static void xgbe_enable_rx_tx_ints(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (channel->tx_ring)
+                       hw_if->enable_int(channel,
+                                         XGMAC_INT_DMA_CH_SR_TI);
+               if (channel->rx_ring)
+                       hw_if->enable_int(channel,
+                                         XGMAC_INT_DMA_CH_SR_RI);
+       }
+}
+
+static void xgbe_disable_rx_tx_ints(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (channel->tx_ring)
+                       hw_if->disable_int(channel,
+                                          XGMAC_INT_DMA_CH_SR_TI);
+               if (channel->rx_ring)
+                       hw_if->disable_int(channel,
+                                          XGMAC_INT_DMA_CH_SR_RI);
+       }
+}
+
+static irqreturn_t xgbe_isr(int irq, void *data)
+{
+       struct xgbe_prv_data *pdata = data;
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct xgbe_channel *channel;
+       unsigned int dma_isr, dma_ch_isr;
+       unsigned int mac_isr;
+       unsigned int i;
+
+       /* The DMA interrupt status register also reports MAC and MTL
+        * interrupts. So for polling mode, we just need to check for
+        * this register to be non-zero
+        */
+       dma_isr = XGMAC_IOREAD(pdata, DMA_ISR);
+       if (!dma_isr)
+               goto isr_done;
+
+       DBGPR("-->xgbe_isr\n");
+
+       DBGPR("  DMA_ISR = %08x\n", dma_isr);
+       DBGPR("  DMA_DS0 = %08x\n", XGMAC_IOREAD(pdata, DMA_DSR0));
+       DBGPR("  DMA_DS1 = %08x\n", XGMAC_IOREAD(pdata, DMA_DSR1));
+
+       for (i = 0; i < pdata->channel_count; i++) {
+               if (!(dma_isr & (1 << i)))
+                       continue;
+
+               channel = pdata->channel + i;
+
+               dma_ch_isr = XGMAC_DMA_IOREAD(channel, DMA_CH_SR);
+               DBGPR("  DMA_CH%u_ISR = %08x\n", i, dma_ch_isr);
+
+               if (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, TI) ||
+                   XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, RI)) {
+                       if (napi_schedule_prep(&pdata->napi)) {
+                               /* Disable Tx and Rx interrupts */
+                               xgbe_disable_rx_tx_ints(pdata);
+
+                               /* Turn on polling */
+                               __napi_schedule(&pdata->napi);
+                       }
+               }
+
+               /* Restart the device on a Fatal Bus Error */
+               if (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, FBE))
+                       schedule_work(&pdata->restart_work);
+
+               /* Clear all interrupt signals */
+               XGMAC_DMA_IOWRITE(channel, DMA_CH_SR, dma_ch_isr);
+       }
+
+       if (XGMAC_GET_BITS(dma_isr, DMA_ISR, MACIS)) {
+               mac_isr = XGMAC_IOREAD(pdata, MAC_ISR);
+
+               if (XGMAC_GET_BITS(mac_isr, MAC_ISR, MMCTXIS))
+                       hw_if->tx_mmc_int(pdata);
+
+               if (XGMAC_GET_BITS(mac_isr, MAC_ISR, MMCRXIS))
+                       hw_if->rx_mmc_int(pdata);
+       }
+
+       DBGPR("  DMA_ISR = %08x\n", XGMAC_IOREAD(pdata, DMA_ISR));
+
+       DBGPR("<--xgbe_isr\n");
+
+isr_done:
+       return IRQ_HANDLED;
+}
+
+static enum hrtimer_restart xgbe_tx_timer(struct hrtimer *timer)
+{
+       struct xgbe_channel *channel = container_of(timer,
+                                                   struct xgbe_channel,
+                                                   tx_timer);
+       struct xgbe_ring *ring = channel->tx_ring;
+       struct xgbe_prv_data *pdata = channel->pdata;
+       unsigned long flags;
+
+       DBGPR("-->xgbe_tx_timer\n");
+
+       spin_lock_irqsave(&ring->lock, flags);
+
+       if (napi_schedule_prep(&pdata->napi)) {
+               /* Disable Tx and Rx interrupts */
+               xgbe_disable_rx_tx_ints(pdata);
+
+               /* Turn on polling */
+               __napi_schedule(&pdata->napi);
+       }
+
+       channel->tx_timer_active = 0;
+
+       spin_unlock_irqrestore(&ring->lock, flags);
+
+       DBGPR("<--xgbe_tx_timer\n");
+
+       return HRTIMER_NORESTART;
+}
+
+static void xgbe_init_tx_timers(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       DBGPR("-->xgbe_init_tx_timers\n");
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->tx_ring)
+                       break;
+
+               DBGPR("  %s adding tx timer\n", channel->name);
+               hrtimer_init(&channel->tx_timer, CLOCK_MONOTONIC,
+                            HRTIMER_MODE_REL);
+               channel->tx_timer.function = xgbe_tx_timer;
+       }
+
+       DBGPR("<--xgbe_init_tx_timers\n");
+}
+
+static void xgbe_stop_tx_timers(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel;
+       unsigned int i;
+
+       DBGPR("-->xgbe_stop_tx_timers\n");
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               if (!channel->tx_ring)
+                       break;
+
+               DBGPR("  %s deleting tx timer\n", channel->name);
+               channel->tx_timer_active = 0;
+               hrtimer_cancel(&channel->tx_timer);
+       }
+
+       DBGPR("<--xgbe_stop_tx_timers\n");
+}
+
+void xgbe_get_all_hw_features(struct xgbe_prv_data *pdata)
+{
+       unsigned int mac_hfr0, mac_hfr1, mac_hfr2;
+       struct xgbe_hw_features *hw_feat = &pdata->hw_feat;
+
+       DBGPR("-->xgbe_get_all_hw_features\n");
+
+       mac_hfr0 = XGMAC_IOREAD(pdata, MAC_HWF0R);
+       mac_hfr1 = XGMAC_IOREAD(pdata, MAC_HWF1R);
+       mac_hfr2 = XGMAC_IOREAD(pdata, MAC_HWF2R);
+
+       memset(hw_feat, 0, sizeof(*hw_feat));
+
+       /* Hardware feature register 0 */
+       hw_feat->gmii        = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, GMIISEL);
+       hw_feat->vlhash      = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, VLHASH);
+       hw_feat->sma         = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, SMASEL);
+       hw_feat->rwk         = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, RWKSEL);
+       hw_feat->mgk         = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, MGKSEL);
+       hw_feat->mmc         = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, MMCSEL);
+       hw_feat->aoe         = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, ARPOFFSEL);
+       hw_feat->ts          = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, TSSEL);
+       hw_feat->eee         = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, EEESEL);
+       hw_feat->tx_coe      = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, TXCOESEL);
+       hw_feat->rx_coe      = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, RXCOESEL);
+       hw_feat->addn_mac    = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R,
+                                             ADDMACADRSEL);
+       hw_feat->ts_src      = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, TSSTSSEL);
+       hw_feat->sa_vlan_ins = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, SAVLANINS);
+
+       /* Hardware feature register 1 */
+       hw_feat->rx_fifo_size  = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R,
+                                               RXFIFOSIZE);
+       hw_feat->tx_fifo_size  = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R,
+                                               TXFIFOSIZE);
+       hw_feat->dcb           = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, DCBEN);
+       hw_feat->sph           = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, SPHEN);
+       hw_feat->tso           = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, TSOEN);
+       hw_feat->dma_debug     = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, DBGMEMA);
+       hw_feat->hash_table_size = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R,
+                                                 HASHTBLSZ);
+       hw_feat->l3l4_filter_num = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R,
+                                                 L3L4FNUM);
+
+       /* Hardware feature register 2 */
+       hw_feat->rx_q_cnt     = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, RXQCNT);
+       hw_feat->tx_q_cnt     = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, TXQCNT);
+       hw_feat->rx_ch_cnt    = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, RXCHCNT);
+       hw_feat->tx_ch_cnt    = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, TXCHCNT);
+       hw_feat->pps_out_num  = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, PPSOUTNUM);
+       hw_feat->aux_snap_num = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, AUXSNAPNUM);
+
+       /* The Queue and Channel counts are zero based so increment them
+        * to get the actual number
+        */
+       hw_feat->rx_q_cnt++;
+       hw_feat->tx_q_cnt++;
+       hw_feat->rx_ch_cnt++;
+       hw_feat->tx_ch_cnt++;
+
+       DBGPR("<--xgbe_get_all_hw_features\n");
+}
+
+static void xgbe_napi_enable(struct xgbe_prv_data *pdata, unsigned int add)
+{
+       if (add)
+               netif_napi_add(pdata->netdev, &pdata->napi, xgbe_poll,
+                              NAPI_POLL_WEIGHT);
+       napi_enable(&pdata->napi);
+}
+
+static void xgbe_napi_disable(struct xgbe_prv_data *pdata)
+{
+       napi_disable(&pdata->napi);
+}
+
+void xgbe_init_tx_coalesce(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+
+       DBGPR("-->xgbe_init_tx_coalesce\n");
+
+       pdata->tx_usecs = XGMAC_INIT_DMA_TX_USECS;
+       pdata->tx_frames = XGMAC_INIT_DMA_TX_FRAMES;
+
+       hw_if->config_tx_coalesce(pdata);
+
+       DBGPR("<--xgbe_init_tx_coalesce\n");
+}
+
+void xgbe_init_rx_coalesce(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+
+       DBGPR("-->xgbe_init_rx_coalesce\n");
+
+       pdata->rx_riwt = hw_if->usec_to_riwt(pdata, XGMAC_INIT_DMA_RX_USECS);
+       pdata->rx_frames = XGMAC_INIT_DMA_RX_FRAMES;
+
+       hw_if->config_rx_coalesce(pdata);
+
+       DBGPR("<--xgbe_init_rx_coalesce\n");
+}
+
+static void xgbe_free_tx_skbuff(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_desc_if *desc_if = &pdata->desc_if;
+       struct xgbe_channel *channel;
+       struct xgbe_ring *ring;
+       struct xgbe_ring_data *rdata;
+       unsigned int i, j;
+
+       DBGPR("-->xgbe_free_tx_skbuff\n");
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               ring = channel->tx_ring;
+               if (!ring)
+                       break;
+
+               for (j = 0; j < ring->rdesc_count; j++) {
+                       rdata = GET_DESC_DATA(ring, j);
+                       desc_if->unmap_skb(pdata, rdata);
+               }
+       }
+
+       DBGPR("<--xgbe_free_tx_skbuff\n");
+}
+
+static void xgbe_free_rx_skbuff(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_desc_if *desc_if = &pdata->desc_if;
+       struct xgbe_channel *channel;
+       struct xgbe_ring *ring;
+       struct xgbe_ring_data *rdata;
+       unsigned int i, j;
+
+       DBGPR("-->xgbe_free_rx_skbuff\n");
+
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++) {
+               ring = channel->rx_ring;
+               if (!ring)
+                       break;
+
+               for (j = 0; j < ring->rdesc_count; j++) {
+                       rdata = GET_DESC_DATA(ring, j);
+                       desc_if->unmap_skb(pdata, rdata);
+               }
+       }
+
+       DBGPR("<--xgbe_free_rx_skbuff\n");
+}
+
+int xgbe_powerdown(struct net_device *netdev, unsigned int caller)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       unsigned long flags;
+
+       DBGPR("-->xgbe_powerdown\n");
+
+       if (!netif_running(netdev) ||
+           (caller == XGMAC_IOCTL_CONTEXT && pdata->power_down)) {
+               netdev_alert(netdev, "Device is already powered down\n");
+               DBGPR("<--xgbe_powerdown\n");
+               return -EINVAL;
+       }
+
+       phy_stop(pdata->phydev);
+
+       spin_lock_irqsave(&pdata->lock, flags);
+
+       if (caller == XGMAC_DRIVER_CONTEXT)
+               netif_device_detach(netdev);
+
+       netif_tx_stop_all_queues(netdev);
+       xgbe_napi_disable(pdata);
+
+       /* Powerdown Tx/Rx */
+       hw_if->powerdown_tx(pdata);
+       hw_if->powerdown_rx(pdata);
+
+       pdata->power_down = 1;
+
+       spin_unlock_irqrestore(&pdata->lock, flags);
+
+       DBGPR("<--xgbe_powerdown\n");
+
+       return 0;
+}
+
+int xgbe_powerup(struct net_device *netdev, unsigned int caller)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       unsigned long flags;
+
+       DBGPR("-->xgbe_powerup\n");
+
+       if (!netif_running(netdev) ||
+           (caller == XGMAC_IOCTL_CONTEXT && !pdata->power_down)) {
+               netdev_alert(netdev, "Device is already powered up\n");
+               DBGPR("<--xgbe_powerup\n");
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&pdata->lock, flags);
+
+       pdata->power_down = 0;
+
+       phy_start(pdata->phydev);
+
+       /* Enable Tx/Rx */
+       hw_if->powerup_tx(pdata);
+       hw_if->powerup_rx(pdata);
+
+       if (caller == XGMAC_DRIVER_CONTEXT)
+               netif_device_attach(netdev);
+
+       xgbe_napi_enable(pdata, 0);
+       netif_tx_start_all_queues(netdev);
+
+       spin_unlock_irqrestore(&pdata->lock, flags);
+
+       DBGPR("<--xgbe_powerup\n");
+
+       return 0;
+}
+
+static int xgbe_start(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct net_device *netdev = pdata->netdev;
+
+       DBGPR("-->xgbe_start\n");
+
+       xgbe_set_rx_mode(netdev);
+
+       hw_if->init(pdata);
+
+       phy_start(pdata->phydev);
+
+       hw_if->enable_tx(pdata);
+       hw_if->enable_rx(pdata);
+
+       xgbe_init_tx_timers(pdata);
+
+       xgbe_napi_enable(pdata, 1);
+       netif_tx_start_all_queues(netdev);
+
+       DBGPR("<--xgbe_start\n");
+
+       return 0;
+}
+
+static void xgbe_stop(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct net_device *netdev = pdata->netdev;
+
+       DBGPR("-->xgbe_stop\n");
+
+       phy_stop(pdata->phydev);
+
+       netif_tx_stop_all_queues(netdev);
+       xgbe_napi_disable(pdata);
+
+       xgbe_stop_tx_timers(pdata);
+
+       hw_if->disable_tx(pdata);
+       hw_if->disable_rx(pdata);
+
+       DBGPR("<--xgbe_stop\n");
+}
+
+static void xgbe_restart_dev(struct xgbe_prv_data *pdata, unsigned int reset)
+{
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+
+       DBGPR("-->xgbe_restart_dev\n");
+
+       /* If not running, "restart" will happen on open */
+       if (!netif_running(pdata->netdev))
+               return;
+
+       xgbe_stop(pdata);
+       synchronize_irq(pdata->irq_number);
+
+       xgbe_free_tx_skbuff(pdata);
+       xgbe_free_rx_skbuff(pdata);
+
+       /* Issue software reset to device if requested */
+       if (reset)
+               hw_if->exit(pdata);
+
+       xgbe_start(pdata);
+
+       DBGPR("<--xgbe_restart_dev\n");
+}
+
+static void xgbe_restart(struct work_struct *work)
+{
+       struct xgbe_prv_data *pdata = container_of(work,
+                                                  struct xgbe_prv_data,
+                                                  restart_work);
+
+       rtnl_lock();
+
+       xgbe_restart_dev(pdata, 1);
+
+       rtnl_unlock();
+}
+
+static void xgbe_prep_vlan(struct sk_buff *skb, struct xgbe_packet_data *packet)
+{
+       if (vlan_tx_tag_present(skb))
+               packet->vlan_ctag = vlan_tx_tag_get(skb);
+}
+
+static int xgbe_prep_tso(struct sk_buff *skb, struct xgbe_packet_data *packet)
+{
+       int ret;
+
+       if (!XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
+                           TSO_ENABLE))
+               return 0;
+
+       ret = skb_cow_head(skb, 0);
+       if (ret)
+               return ret;
+
+       packet->header_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+       packet->tcp_header_len = tcp_hdrlen(skb);
+       packet->tcp_payload_len = skb->len - packet->header_len;
+       packet->mss = skb_shinfo(skb)->gso_size;
+       DBGPR("  packet->header_len=%u\n", packet->header_len);
+       DBGPR("  packet->tcp_header_len=%u, packet->tcp_payload_len=%u\n",
+             packet->tcp_header_len, packet->tcp_payload_len);
+       DBGPR("  packet->mss=%u\n", packet->mss);
+
+       return 0;
+}
+
+static int xgbe_is_tso(struct sk_buff *skb)
+{
+       if (skb->ip_summed != CHECKSUM_PARTIAL)
+               return 0;
+
+       if (!skb_is_gso(skb))
+               return 0;
+
+       DBGPR("  TSO packet to be processed\n");
+
+       return 1;
+}
+
+static void xgbe_packet_info(struct xgbe_ring *ring, struct sk_buff *skb,
+                            struct xgbe_packet_data *packet)
+{
+       struct skb_frag_struct *frag;
+       unsigned int context_desc;
+       unsigned int len;
+       unsigned int i;
+
+       context_desc = 0;
+       packet->rdesc_count = 0;
+
+       if (xgbe_is_tso(skb)) {
+               /* TSO requires an extra desriptor if mss is different */
+               if (skb_shinfo(skb)->gso_size != ring->tx.cur_mss) {
+                       context_desc = 1;
+                       packet->rdesc_count++;
+               }
+
+               /* TSO requires an extra desriptor for TSO header */
+               packet->rdesc_count++;
+
+               XGMAC_SET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
+                              TSO_ENABLE, 1);
+               XGMAC_SET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
+                              CSUM_ENABLE, 1);
+       } else if (skb->ip_summed == CHECKSUM_PARTIAL)
+               XGMAC_SET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
+                              CSUM_ENABLE, 1);
+
+       if (vlan_tx_tag_present(skb)) {
+               /* VLAN requires an extra descriptor if tag is different */
+               if (vlan_tx_tag_get(skb) != ring->tx.cur_vlan_ctag)
+                       /* We can share with the TSO context descriptor */
+                       if (!context_desc) {
+                               context_desc = 1;
+                               packet->rdesc_count++;
+                       }
+
+               XGMAC_SET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
+                              VLAN_CTAG, 1);
+       }
+
+       for (len = skb_headlen(skb); len;) {
+               packet->rdesc_count++;
+               len -= min_t(unsigned int, len, TX_MAX_BUF_SIZE);
+       }
+
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+               frag = &skb_shinfo(skb)->frags[i];
+               for (len = skb_frag_size(frag); len; ) {
+                       packet->rdesc_count++;
+                       len -= min_t(unsigned int, len, TX_MAX_BUF_SIZE);
+               }
+       }
+}
+
+static int xgbe_open(struct net_device *netdev)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct xgbe_desc_if *desc_if = &pdata->desc_if;
+       int ret;
+
+       DBGPR("-->xgbe_open\n");
+
+       /* Enable the clock */
+       ret = clk_prepare_enable(pdata->sysclock);
+       if (ret) {
+               netdev_alert(netdev, "clk_prepare_enable failed\n");
+               return ret;
+       }
+
+       /* Calculate the Rx buffer size before allocating rings */
+       ret = xgbe_calc_rx_buf_size(netdev, netdev->mtu);
+       if (ret < 0)
+               goto err_clk;
+       pdata->rx_buf_size = ret;
+
+       /* Allocate the ring descriptors and buffers */
+       ret = desc_if->alloc_ring_resources(pdata);
+       if (ret)
+               goto err_clk;
+
+       /* Initialize the device restart work struct */
+       INIT_WORK(&pdata->restart_work, xgbe_restart);
+
+       /* Request interrupts */
+       ret = devm_request_irq(pdata->dev, netdev->irq, xgbe_isr, 0,
+                              netdev->name, pdata);
+       if (ret) {
+               netdev_alert(netdev, "error requesting irq %d\n",
+                            pdata->irq_number);
+               goto err_irq;
+       }
+       pdata->irq_number = netdev->irq;
+
+       ret = xgbe_start(pdata);
+       if (ret)
+               goto err_start;
+
+       DBGPR("<--xgbe_open\n");
+
+       return 0;
+
+err_start:
+       hw_if->exit(pdata);
+
+       devm_free_irq(pdata->dev, pdata->irq_number, pdata);
+       pdata->irq_number = 0;
+
+err_irq:
+       desc_if->free_ring_resources(pdata);
+
+err_clk:
+       clk_disable_unprepare(pdata->sysclock);
+
+       return ret;
+}
+
+static int xgbe_close(struct net_device *netdev)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct xgbe_desc_if *desc_if = &pdata->desc_if;
+
+       DBGPR("-->xgbe_close\n");
+
+       /* Stop the device */
+       xgbe_stop(pdata);
+
+       /* Issue software reset to device */
+       hw_if->exit(pdata);
+
+       /* Free all the ring data */
+       desc_if->free_ring_resources(pdata);
+
+       /* Release the interrupt */
+       if (pdata->irq_number != 0) {
+               devm_free_irq(pdata->dev, pdata->irq_number, pdata);
+               pdata->irq_number = 0;
+       }
+
+       /* Disable the clock */
+       clk_disable_unprepare(pdata->sysclock);
+
+       DBGPR("<--xgbe_close\n");
+
+       return 0;
+}
+
+static int xgbe_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct xgbe_desc_if *desc_if = &pdata->desc_if;
+       struct xgbe_channel *channel;
+       struct xgbe_ring *ring;
+       struct xgbe_packet_data *packet;
+       unsigned long flags;
+       int ret;
+
+       DBGPR("-->xgbe_xmit: skb->len = %d\n", skb->len);
+
+       channel = pdata->channel + skb->queue_mapping;
+       ring = channel->tx_ring;
+       packet = &ring->packet_data;
+
+       ret = NETDEV_TX_OK;
+
+       spin_lock_irqsave(&ring->lock, flags);
+
+       if (skb->len == 0) {
+               netdev_err(netdev, "empty skb received from stack\n");
+               dev_kfree_skb_any(skb);
+               goto tx_netdev_return;
+       }
+
+       /* Calculate preliminary packet info */
+       memset(packet, 0, sizeof(*packet));
+       xgbe_packet_info(ring, skb, packet);
+
+       /* Check that there are enough descriptors available */
+       if (packet->rdesc_count > xgbe_tx_avail_desc(ring)) {
+               DBGPR("  Tx queue stopped, not enough descriptors available\n");
+               netif_stop_subqueue(netdev, channel->queue_index);
+               ring->tx.queue_stopped = 1;
+               ret = NETDEV_TX_BUSY;
+               goto tx_netdev_return;
+       }
+
+       ret = xgbe_prep_tso(skb, packet);
+       if (ret) {
+               netdev_err(netdev, "error processing TSO packet\n");
+               dev_kfree_skb_any(skb);
+               goto tx_netdev_return;
+       }
+       xgbe_prep_vlan(skb, packet);
+
+       if (!desc_if->map_tx_skb(channel, skb)) {
+               dev_kfree_skb_any(skb);
+               goto tx_netdev_return;
+       }
+
+       /* Configure required descriptor fields for transmission */
+       hw_if->pre_xmit(channel);
+
+#ifdef XGMAC_ENABLE_TX_PKT_DUMP
+       xgbe_print_pkt(netdev, skb, true);
+#endif
+
+tx_netdev_return:
+       spin_unlock_irqrestore(&ring->lock, flags);
+
+       DBGPR("<--xgbe_xmit\n");
+
+       return ret;
+}
+
+static void xgbe_set_rx_mode(struct net_device *netdev)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       unsigned int pr_mode, am_mode;
+
+       DBGPR("-->xgbe_set_rx_mode\n");
+
+       pr_mode = ((netdev->flags & IFF_PROMISC) != 0);
+       am_mode = ((netdev->flags & IFF_ALLMULTI) != 0);
+
+       if (netdev_uc_count(netdev) > pdata->hw_feat.addn_mac)
+               pr_mode = 1;
+       if (netdev_mc_count(netdev) > pdata->hw_feat.addn_mac)
+               am_mode = 1;
+       if ((netdev_uc_count(netdev) + netdev_mc_count(netdev)) >
+            pdata->hw_feat.addn_mac)
+               pr_mode = 1;
+
+       hw_if->set_promiscuous_mode(pdata, pr_mode);
+       hw_if->set_all_multicast_mode(pdata, am_mode);
+       if (!pr_mode)
+               hw_if->set_addn_mac_addrs(pdata, am_mode);
+
+       DBGPR("<--xgbe_set_rx_mode\n");
+}
+
+static int xgbe_set_mac_address(struct net_device *netdev, void *addr)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct sockaddr *saddr = addr;
+
+       DBGPR("-->xgbe_set_mac_address\n");
+
+       if (!is_valid_ether_addr(saddr->sa_data))
+               return -EADDRNOTAVAIL;
+
+       memcpy(netdev->dev_addr, saddr->sa_data, netdev->addr_len);
+
+       hw_if->set_mac_address(pdata, netdev->dev_addr);
+
+       DBGPR("<--xgbe_set_mac_address\n");
+
+       return 0;
+}
+
+static int xgbe_change_mtu(struct net_device *netdev, int mtu)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       int ret;
+
+       DBGPR("-->xgbe_change_mtu\n");
+
+       ret = xgbe_calc_rx_buf_size(netdev, mtu);
+       if (ret < 0)
+               return ret;
+
+       pdata->rx_buf_size = ret;
+       netdev->mtu = mtu;
+
+       xgbe_restart_dev(pdata, 0);
+
+       DBGPR("<--xgbe_change_mtu\n");
+
+       return 0;
+}
+
+static struct rtnl_link_stats64 *xgbe_get_stats64(struct net_device *netdev,
+                                                 struct rtnl_link_stats64 *s)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_mmc_stats *pstats = &pdata->mmc_stats;
+
+       DBGPR("-->%s\n", __func__);
+
+       pdata->hw_if.read_mmc_stats(pdata);
+
+       s->rx_packets = pstats->rxframecount_gb;
+       s->rx_bytes = pstats->rxoctetcount_gb;
+       s->rx_errors = pstats->rxframecount_gb -
+                      pstats->rxbroadcastframes_g -
+                      pstats->rxmulticastframes_g -
+                      pstats->rxunicastframes_g;
+       s->multicast = pstats->rxmulticastframes_g;
+       s->rx_length_errors = pstats->rxlengtherror;
+       s->rx_crc_errors = pstats->rxcrcerror;
+       s->rx_fifo_errors = pstats->rxfifooverflow;
+
+       s->tx_packets = pstats->txframecount_gb;
+       s->tx_bytes = pstats->txoctetcount_gb;
+       s->tx_errors = pstats->txframecount_gb - pstats->txframecount_g;
+       s->tx_dropped = netdev->stats.tx_dropped;
+
+       DBGPR("<--%s\n", __func__);
+
+       return s;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void xgbe_poll_controller(struct net_device *netdev)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+
+       DBGPR("-->xgbe_poll_controller\n");
+
+       disable_irq(pdata->irq_number);
+
+       xgbe_isr(pdata->irq_number, pdata);
+
+       enable_irq(pdata->irq_number);
+
+       DBGPR("<--xgbe_poll_controller\n");
+}
+#endif /* End CONFIG_NET_POLL_CONTROLLER */
+
+static int xgbe_set_features(struct net_device *netdev,
+                            netdev_features_t features)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       unsigned int rxcsum_enabled, rxvlan_enabled;
+
+       rxcsum_enabled = !!(pdata->netdev_features & NETIF_F_RXCSUM);
+       rxvlan_enabled = !!(pdata->netdev_features & NETIF_F_HW_VLAN_CTAG_RX);
+
+       if ((features & NETIF_F_RXCSUM) && !rxcsum_enabled) {
+               hw_if->enable_rx_csum(pdata);
+               netdev_alert(netdev, "state change - rxcsum enabled\n");
+       } else if (!(features & NETIF_F_RXCSUM) && rxcsum_enabled) {
+               hw_if->disable_rx_csum(pdata);
+               netdev_alert(netdev, "state change - rxcsum disabled\n");
+       }
+
+       if ((features & NETIF_F_HW_VLAN_CTAG_RX) && !rxvlan_enabled) {
+               hw_if->enable_rx_vlan_stripping(pdata);
+               netdev_alert(netdev, "state change - rxvlan enabled\n");
+       } else if (!(features & NETIF_F_HW_VLAN_CTAG_RX) && rxvlan_enabled) {
+               hw_if->disable_rx_vlan_stripping(pdata);
+               netdev_alert(netdev, "state change - rxvlan disabled\n");
+       }
+
+       pdata->netdev_features = features;
+
+       DBGPR("<--xgbe_set_features\n");
+
+       return 0;
+}
+
+static const struct net_device_ops xgbe_netdev_ops = {
+       .ndo_open               = xgbe_open,
+       .ndo_stop               = xgbe_close,
+       .ndo_start_xmit         = xgbe_xmit,
+       .ndo_set_rx_mode        = xgbe_set_rx_mode,
+       .ndo_set_mac_address    = xgbe_set_mac_address,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_change_mtu         = xgbe_change_mtu,
+       .ndo_get_stats64        = xgbe_get_stats64,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = xgbe_poll_controller,
+#endif
+       .ndo_set_features       = xgbe_set_features,
+};
+
+struct net_device_ops *xgbe_get_netdev_ops(void)
+{
+       return (struct net_device_ops *)&xgbe_netdev_ops;
+}
+
+static int xgbe_tx_poll(struct xgbe_channel *channel)
+{
+       struct xgbe_prv_data *pdata = channel->pdata;
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct xgbe_desc_if *desc_if = &pdata->desc_if;
+       struct xgbe_ring *ring = channel->tx_ring;
+       struct xgbe_ring_data *rdata;
+       struct xgbe_ring_desc *rdesc;
+       struct net_device *netdev = pdata->netdev;
+       unsigned long flags;
+       int processed = 0;
+
+       DBGPR("-->xgbe_tx_poll\n");
+
+       /* Nothing to do if there isn't a Tx ring for this channel */
+       if (!ring)
+               return 0;
+
+       spin_lock_irqsave(&ring->lock, flags);
+
+       while ((processed < TX_DESC_MAX_PROC) && (ring->dirty < ring->cur)) {
+               rdata = GET_DESC_DATA(ring, ring->dirty);
+               rdesc = rdata->rdesc;
+
+               if (!hw_if->tx_complete(rdesc))
+                       break;
+
+#ifdef XGMAC_ENABLE_TX_DESC_DUMP
+               xgbe_dump_tx_desc(ring, ring->dirty, 1, 0);
+#endif
+
+               /* Free the SKB and reset the descriptor for re-use */
+               desc_if->unmap_skb(pdata, rdata);
+               hw_if->tx_desc_reset(rdata);
+
+               processed++;
+               ring->dirty++;
+       }
+
+       if ((ring->tx.queue_stopped == 1) &&
+           (xgbe_tx_avail_desc(ring) > TX_DESC_MIN_FREE)) {
+               ring->tx.queue_stopped = 0;
+               netif_wake_subqueue(netdev, channel->queue_index);
+       }
+
+       DBGPR("<--xgbe_tx_poll: processed=%d\n", processed);
+
+       spin_unlock_irqrestore(&ring->lock, flags);
+
+       return processed;
+}
+
+static int xgbe_rx_poll(struct xgbe_channel *channel, int budget)
+{
+       struct xgbe_prv_data *pdata = channel->pdata;
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct xgbe_desc_if *desc_if = &pdata->desc_if;
+       struct xgbe_ring *ring = channel->rx_ring;
+       struct xgbe_ring_data *rdata;
+       struct xgbe_packet_data *packet;
+       struct net_device *netdev = pdata->netdev;
+       struct sk_buff *skb;
+       unsigned int incomplete, error;
+       unsigned int cur_len, put_len, max_len;
+       int received = 0;
+
+       DBGPR("-->xgbe_rx_poll: budget=%d\n", budget);
+
+       /* Nothing to do if there isn't a Rx ring for this channel */
+       if (!ring)
+               return 0;
+
+       packet = &ring->packet_data;
+       while (received < budget) {
+               DBGPR("  cur = %d\n", ring->cur);
+
+               /* Clear the packet data information */
+               memset(packet, 0, sizeof(*packet));
+               skb = NULL;
+               error = 0;
+               cur_len = 0;
+
+read_again:
+               rdata = GET_DESC_DATA(ring, ring->cur);
+
+               if (hw_if->dev_read(channel))
+                       break;
+
+               received++;
+               ring->cur++;
+               ring->dirty++;
+
+               dma_unmap_single(pdata->dev, rdata->skb_dma,
+                                rdata->skb_dma_len, DMA_FROM_DEVICE);
+               rdata->skb_dma = 0;
+
+               incomplete = XGMAC_GET_BITS(packet->attributes,
+                                           RX_PACKET_ATTRIBUTES,
+                                           INCOMPLETE);
+
+               /* Earlier error, just drain the remaining data */
+               if (incomplete && error)
+                       goto read_again;
+
+               if (error || packet->errors) {
+                       if (packet->errors)
+                               DBGPR("Error in received packet\n");
+                       dev_kfree_skb(skb);
+                       continue;
+               }
+
+               put_len = rdata->len - cur_len;
+               if (skb) {
+                       if (pskb_expand_head(skb, 0, put_len, GFP_ATOMIC)) {
+                               DBGPR("pskb_expand_head error\n");
+                               if (incomplete) {
+                                       error = 1;
+                                       goto read_again;
+                               }
+
+                               dev_kfree_skb(skb);
+                               continue;
+                       }
+                       memcpy(skb_tail_pointer(skb), rdata->skb->data,
+                              put_len);
+               } else {
+                       skb = rdata->skb;
+                       rdata->skb = NULL;
+               }
+               skb_put(skb, put_len);
+               cur_len += put_len;
+
+               if (incomplete)
+                       goto read_again;
+
+               /* Be sure we don't exceed the configured MTU */
+               max_len = netdev->mtu + ETH_HLEN;
+               if (!(netdev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
+                   (skb->protocol == htons(ETH_P_8021Q)))
+                       max_len += VLAN_HLEN;
+
+               if (skb->len > max_len) {
+                       DBGPR("packet length exceeds configured MTU\n");
+                       dev_kfree_skb(skb);
+                       continue;
+               }
+
+#ifdef XGMAC_ENABLE_RX_PKT_DUMP
+               xgbe_print_pkt(netdev, skb, false);
+#endif
+
+               skb_checksum_none_assert(skb);
+               if (XGMAC_GET_BITS(packet->attributes,
+                                  RX_PACKET_ATTRIBUTES, CSUM_DONE))
+                       skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+               if (XGMAC_GET_BITS(packet->attributes,
+                                  RX_PACKET_ATTRIBUTES, VLAN_CTAG))
+                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+                                              packet->vlan_ctag);
+
+               skb->dev = netdev;
+               skb->protocol = eth_type_trans(skb, netdev);
+               skb_record_rx_queue(skb, channel->queue_index);
+               skb_mark_napi_id(skb, &pdata->napi);
+
+               netdev->last_rx = jiffies;
+               napi_gro_receive(&pdata->napi, skb);
+       }
+
+       if (received) {
+               desc_if->realloc_skb(channel);
+
+               /* Update the Rx Tail Pointer Register with address of
+                * the last cleaned entry */
+               rdata = GET_DESC_DATA(ring, ring->rx.realloc_index - 1);
+               XGMAC_DMA_IOWRITE(channel, DMA_CH_RDTR_LO,
+                                 lower_32_bits(rdata->rdesc_dma));
+       }
+
+       DBGPR("<--xgbe_rx_poll: received = %d\n", received);
+
+       return received;
+}
+
+static int xgbe_poll(struct napi_struct *napi, int budget)
+{
+       struct xgbe_prv_data *pdata = container_of(napi, struct xgbe_prv_data,
+                                                  napi);
+       struct xgbe_channel *channel;
+       int processed;
+       unsigned int i;
+
+       DBGPR("-->xgbe_poll: budget=%d\n", budget);
+
+       /* Cleanup Tx ring first */
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++)
+               xgbe_tx_poll(channel);
+
+       /* Process Rx ring next */
+       processed = 0;
+       channel = pdata->channel;
+       for (i = 0; i < pdata->channel_count; i++, channel++)
+               processed += xgbe_rx_poll(channel, budget - processed);
+
+       /* If we processed everything, we are done */
+       if (processed < budget) {
+               /* Turn off polling */
+               napi_complete(napi);
+
+               /* Enable Tx and Rx interrupts */
+               xgbe_enable_rx_tx_ints(pdata);
+       }
+
+       DBGPR("<--xgbe_poll: received = %d\n", processed);
+
+       return processed;
+}
+
+void xgbe_dump_tx_desc(struct xgbe_ring *ring, unsigned int idx,
+                      unsigned int count, unsigned int flag)
+{
+       struct xgbe_ring_data *rdata;
+       struct xgbe_ring_desc *rdesc;
+
+       while (count--) {
+               rdata = GET_DESC_DATA(ring, idx);
+               rdesc = rdata->rdesc;
+               DBGPR("TX_NORMAL_DESC[%d %s] = %08x:%08x:%08x:%08x\n", idx,
+                     (flag == 1) ? "QUEUED FOR TX" : "TX BY DEVICE",
+                     le32_to_cpu(rdesc->desc0), le32_to_cpu(rdesc->desc1),
+                     le32_to_cpu(rdesc->desc2), le32_to_cpu(rdesc->desc3));
+               idx++;
+       }
+}
+
+void xgbe_dump_rx_desc(struct xgbe_ring *ring, struct xgbe_ring_desc *desc,
+                      unsigned int idx)
+{
+       DBGPR("RX_NORMAL_DESC[%d RX BY DEVICE] = %08x:%08x:%08x:%08x\n", idx,
+             le32_to_cpu(desc->desc0), le32_to_cpu(desc->desc1),
+             le32_to_cpu(desc->desc2), le32_to_cpu(desc->desc3));
+}
+
+void xgbe_print_pkt(struct net_device *netdev, struct sk_buff *skb, bool tx_rx)
+{
+       struct ethhdr *eth = (struct ethhdr *)skb->data;
+       unsigned char *buf = skb->data;
+       unsigned char buffer[128];
+       unsigned int i, j;
+
+       netdev_alert(netdev, "\n************** SKB dump ****************\n");
+
+       netdev_alert(netdev, "%s packet of %d bytes\n",
+                    (tx_rx ? "TX" : "RX"), skb->len);
+
+       netdev_alert(netdev, "Dst MAC addr: %pM\n", eth->h_dest);
+       netdev_alert(netdev, "Src MAC addr: %pM\n", eth->h_source);
+       netdev_alert(netdev, "Protocol: 0x%04hx\n", ntohs(eth->h_proto));
+
+       for (i = 0, j = 0; i < skb->len;) {
+               j += snprintf(buffer + j, sizeof(buffer) - j, "%02hhx",
+                             buf[i++]);
+
+               if ((i % 32) == 0) {
+                       netdev_alert(netdev, "  0x%04x: %s\n", i - 32, buffer);
+                       j = 0;
+               } else if ((i % 16) == 0) {
+                       buffer[j++] = ' ';
+                       buffer[j++] = ' ';
+               } else if ((i % 4) == 0) {
+                       buffer[j++] = ' ';
+               }
+       }
+       if (i % 32)
+               netdev_alert(netdev, "  0x%04x: %s\n", i - (i % 32), buffer);
+
+       netdev_alert(netdev, "\n************** SKB dump ****************\n");
+}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
new file mode 100644 (file)
index 0000000..8909f2b
--- /dev/null
@@ -0,0 +1,510 @@
+/*
+ * AMD 10Gb Ethernet driver
+ *
+ * This file is available to you under your choice of the following two
+ * licenses:
+ *
+ * License 1: GPLv2
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ *
+ * This file is free software; you may copy, redistribute 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 file 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, see <http://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and the Software, to deal in the Software
+ *     without restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ *     of the Software, and to permit persons to whom the Software is furnished
+ *     to do so, subject to the following conditions:
+ *
+ *     The above copyright notice and this permission notice shall be included
+ *     in all copies or substantial portions of the Software.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ *
+ *
+ * License 2: Modified BSD
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ * 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 of Advanced Micro Devices, Inc. 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 <COPYRIGHT HOLDER> 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.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and the Software, to deal in the Software
+ *     without restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ *     of the Software, and to permit persons to whom the Software is furnished
+ *     to do so, subject to the following conditions:
+ *
+ *     The above copyright notice and this permission notice shall be included
+ *     in all copies or substantial portions of the Software.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ */
+
+#include <linux/spinlock.h>
+#include <linux/phy.h>
+
+#include "xgbe.h"
+#include "xgbe-common.h"
+
+
+struct xgbe_stats {
+       char stat_string[ETH_GSTRING_LEN];
+       int stat_size;
+       int stat_offset;
+};
+
+#define XGMAC_MMC_STAT(_string, _var)                          \
+       { _string,                                              \
+         FIELD_SIZEOF(struct xgbe_mmc_stats, _var),            \
+         offsetof(struct xgbe_prv_data, mmc_stats._var),       \
+       }
+
+static const struct xgbe_stats xgbe_gstring_stats[] = {
+       XGMAC_MMC_STAT("tx_bytes", txoctetcount_gb),
+       XGMAC_MMC_STAT("tx_packets", txframecount_gb),
+       XGMAC_MMC_STAT("tx_unicast_packets", txunicastframes_gb),
+       XGMAC_MMC_STAT("tx_broadcast_packets", txbroadcastframes_gb),
+       XGMAC_MMC_STAT("tx_multicast_packets", txmulticastframes_gb),
+       XGMAC_MMC_STAT("tx_vlan_packets", txvlanframes_g),
+       XGMAC_MMC_STAT("tx_64_byte_packets", tx64octets_gb),
+       XGMAC_MMC_STAT("tx_65_to_127_byte_packets", tx65to127octets_gb),
+       XGMAC_MMC_STAT("tx_128_to_255_byte_packets", tx128to255octets_gb),
+       XGMAC_MMC_STAT("tx_256_to_511_byte_packets", tx256to511octets_gb),
+       XGMAC_MMC_STAT("tx_512_to_1023_byte_packets", tx512to1023octets_gb),
+       XGMAC_MMC_STAT("tx_1024_to_max_byte_packets", tx1024tomaxoctets_gb),
+       XGMAC_MMC_STAT("tx_underflow_errors", txunderflowerror),
+       XGMAC_MMC_STAT("tx_pause_frames", txpauseframes),
+
+       XGMAC_MMC_STAT("rx_bytes", rxoctetcount_gb),
+       XGMAC_MMC_STAT("rx_packets", rxframecount_gb),
+       XGMAC_MMC_STAT("rx_unicast_packets", rxunicastframes_g),
+       XGMAC_MMC_STAT("rx_broadcast_packets", rxbroadcastframes_g),
+       XGMAC_MMC_STAT("rx_multicast_packets", rxmulticastframes_g),
+       XGMAC_MMC_STAT("rx_vlan_packets", rxvlanframes_gb),
+       XGMAC_MMC_STAT("rx_64_byte_packets", rx64octets_gb),
+       XGMAC_MMC_STAT("rx_65_to_127_byte_packets", rx65to127octets_gb),
+       XGMAC_MMC_STAT("rx_128_to_255_byte_packets", rx128to255octets_gb),
+       XGMAC_MMC_STAT("rx_256_to_511_byte_packets", rx256to511octets_gb),
+       XGMAC_MMC_STAT("rx_512_to_1023_byte_packets", rx512to1023octets_gb),
+       XGMAC_MMC_STAT("rx_1024_to_max_byte_packets", rx1024tomaxoctets_gb),
+       XGMAC_MMC_STAT("rx_undersize_packets", rxundersize_g),
+       XGMAC_MMC_STAT("rx_oversize_packets", rxoversize_g),
+       XGMAC_MMC_STAT("rx_crc_errors", rxcrcerror),
+       XGMAC_MMC_STAT("rx_crc_errors_small_packets", rxrunterror),
+       XGMAC_MMC_STAT("rx_crc_errors_giant_packets", rxjabbererror),
+       XGMAC_MMC_STAT("rx_length_errors", rxlengtherror),
+       XGMAC_MMC_STAT("rx_out_of_range_errors", rxoutofrangetype),
+       XGMAC_MMC_STAT("rx_fifo_overflow_errors", rxfifooverflow),
+       XGMAC_MMC_STAT("rx_watchdog_errors", rxwatchdogerror),
+       XGMAC_MMC_STAT("rx_pause_frames", rxpauseframes),
+};
+#define XGBE_STATS_COUNT       ARRAY_SIZE(xgbe_gstring_stats)
+
+static void xgbe_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
+{
+       int i;
+
+       DBGPR("-->%s\n", __func__);
+
+       switch (stringset) {
+       case ETH_SS_STATS:
+               for (i = 0; i < XGBE_STATS_COUNT; i++) {
+                       memcpy(data, xgbe_gstring_stats[i].stat_string,
+                              ETH_GSTRING_LEN);
+                       data += ETH_GSTRING_LEN;
+               }
+               break;
+       }
+
+       DBGPR("<--%s\n", __func__);
+}
+
+static void xgbe_get_ethtool_stats(struct net_device *netdev,
+                                  struct ethtool_stats *stats, u64 *data)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       u8 *stat;
+       int i;
+
+       DBGPR("-->%s\n", __func__);
+
+       pdata->hw_if.read_mmc_stats(pdata);
+       for (i = 0; i < XGBE_STATS_COUNT; i++) {
+               stat = (u8 *)pdata + xgbe_gstring_stats[i].stat_offset;
+               *data++ = *(u64 *)stat;
+       }
+
+       DBGPR("<--%s\n", __func__);
+}
+
+static int xgbe_get_sset_count(struct net_device *netdev, int stringset)
+{
+       int ret;
+
+       DBGPR("-->%s\n", __func__);
+
+       switch (stringset) {
+       case ETH_SS_STATS:
+               ret = XGBE_STATS_COUNT;
+               break;
+
+       default:
+               ret = -EOPNOTSUPP;
+       }
+
+       DBGPR("<--%s\n", __func__);
+
+       return ret;
+}
+
+static void xgbe_get_pauseparam(struct net_device *netdev,
+                               struct ethtool_pauseparam *pause)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+
+       DBGPR("-->xgbe_get_pauseparam\n");
+
+       pause->autoneg = pdata->pause_autoneg;
+       pause->tx_pause = pdata->tx_pause;
+       pause->rx_pause = pdata->rx_pause;
+
+       DBGPR("<--xgbe_get_pauseparam\n");
+}
+
+static int xgbe_set_pauseparam(struct net_device *netdev,
+                              struct ethtool_pauseparam *pause)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct phy_device *phydev = pdata->phydev;
+       int ret = 0;
+
+       DBGPR("-->xgbe_set_pauseparam\n");
+
+       DBGPR("  autoneg = %d, tx_pause = %d, rx_pause = %d\n",
+             pause->autoneg, pause->tx_pause, pause->rx_pause);
+
+       pdata->pause_autoneg = pause->autoneg;
+       if (pause->autoneg) {
+               phydev->advertising |= ADVERTISED_Pause;
+               phydev->advertising |= ADVERTISED_Asym_Pause;
+
+       } else {
+               phydev->advertising &= ~ADVERTISED_Pause;
+               phydev->advertising &= ~ADVERTISED_Asym_Pause;
+
+               pdata->tx_pause = pause->tx_pause;
+               pdata->rx_pause = pause->rx_pause;
+       }
+
+       if (netif_running(netdev))
+               ret = phy_start_aneg(phydev);
+
+       DBGPR("<--xgbe_set_pauseparam\n");
+
+       return ret;
+}
+
+static int xgbe_get_settings(struct net_device *netdev,
+                            struct ethtool_cmd *cmd)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       int ret;
+
+       DBGPR("-->xgbe_get_settings\n");
+
+       if (!pdata->phydev)
+               return -ENODEV;
+
+       spin_lock_irq(&pdata->lock);
+
+       ret = phy_ethtool_gset(pdata->phydev, cmd);
+       cmd->transceiver = XCVR_EXTERNAL;
+
+       spin_unlock_irq(&pdata->lock);
+
+       DBGPR("<--xgbe_get_settings\n");
+
+       return ret;
+}
+
+static int xgbe_set_settings(struct net_device *netdev,
+                            struct ethtool_cmd *cmd)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct phy_device *phydev = pdata->phydev;
+       u32 speed;
+       int ret;
+
+       DBGPR("-->xgbe_set_settings\n");
+
+       if (!pdata->phydev)
+               return -ENODEV;
+
+       spin_lock_irq(&pdata->lock);
+
+       speed = ethtool_cmd_speed(cmd);
+
+       ret = -EINVAL;
+       if (cmd->phy_address != phydev->addr)
+               goto unlock;
+
+       if ((cmd->autoneg != AUTONEG_ENABLE) &&
+           (cmd->autoneg != AUTONEG_DISABLE))
+               goto unlock;
+
+       if ((cmd->autoneg == AUTONEG_DISABLE) &&
+           (((speed != SPEED_10000) && (speed != SPEED_1000)) ||
+            (cmd->duplex != DUPLEX_FULL)))
+               goto unlock;
+
+       if (cmd->autoneg == AUTONEG_ENABLE) {
+               /* Clear settings needed to force speeds */
+               phydev->supported &= ~SUPPORTED_1000baseT_Full;
+               phydev->supported &= ~SUPPORTED_10000baseT_Full;
+       } else {
+               /* Add settings needed to force speed */
+               phydev->supported |= SUPPORTED_1000baseT_Full;
+               phydev->supported |= SUPPORTED_10000baseT_Full;
+       }
+
+       cmd->advertising &= phydev->supported;
+       if ((cmd->autoneg == AUTONEG_ENABLE) && !cmd->advertising)
+               goto unlock;
+
+       ret = 0;
+       phydev->autoneg = cmd->autoneg;
+       phydev->speed = speed;
+       phydev->duplex = cmd->duplex;
+       phydev->advertising = cmd->advertising;
+
+       if (cmd->autoneg == AUTONEG_ENABLE)
+               phydev->advertising |= ADVERTISED_Autoneg;
+       else
+               phydev->advertising &= ~ADVERTISED_Autoneg;
+
+       if (netif_running(netdev))
+               ret = phy_start_aneg(phydev);
+
+unlock:
+       spin_unlock_irq(&pdata->lock);
+
+       DBGPR("<--xgbe_set_settings\n");
+
+       return ret;
+}
+
+static void xgbe_get_drvinfo(struct net_device *netdev,
+                            struct ethtool_drvinfo *drvinfo)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+
+       strlcpy(drvinfo->driver, XGBE_DRV_NAME, sizeof(drvinfo->driver));
+       strlcpy(drvinfo->version, XGBE_DRV_VERSION, sizeof(drvinfo->version));
+       strlcpy(drvinfo->bus_info, dev_name(pdata->dev),
+               sizeof(drvinfo->bus_info));
+       snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "%d.%d.%d",
+                XGMAC_IOREAD_BITS(pdata, MAC_VR, USERVER),
+                XGMAC_IOREAD_BITS(pdata, MAC_VR, DEVID),
+                XGMAC_IOREAD_BITS(pdata, MAC_VR, SNPSVER));
+       drvinfo->n_stats = XGBE_STATS_COUNT;
+}
+
+static int xgbe_get_coalesce(struct net_device *netdev,
+                            struct ethtool_coalesce *ec)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       unsigned int riwt;
+
+       DBGPR("-->xgbe_get_coalesce\n");
+
+       memset(ec, 0, sizeof(struct ethtool_coalesce));
+
+       riwt = pdata->rx_riwt;
+       ec->rx_coalesce_usecs = hw_if->riwt_to_usec(pdata, riwt);
+       ec->rx_max_coalesced_frames = pdata->rx_frames;
+
+       ec->tx_coalesce_usecs = pdata->tx_usecs;
+       ec->tx_max_coalesced_frames = pdata->tx_frames;
+
+       DBGPR("<--xgbe_get_coalesce\n");
+
+       return 0;
+}
+
+static int xgbe_set_coalesce(struct net_device *netdev,
+                            struct ethtool_coalesce *ec)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       unsigned int rx_frames, rx_riwt, rx_usecs;
+       unsigned int tx_frames, tx_usecs;
+
+       DBGPR("-->xgbe_set_coalesce\n");
+
+       /* Check for not supported parameters  */
+       if ((ec->rx_coalesce_usecs_irq) ||
+           (ec->rx_max_coalesced_frames_irq) ||
+           (ec->tx_coalesce_usecs_irq) ||
+           (ec->tx_max_coalesced_frames_irq) ||
+           (ec->stats_block_coalesce_usecs) ||
+           (ec->use_adaptive_rx_coalesce) ||
+           (ec->use_adaptive_tx_coalesce) ||
+           (ec->pkt_rate_low) ||
+           (ec->rx_coalesce_usecs_low) ||
+           (ec->rx_max_coalesced_frames_low) ||
+           (ec->tx_coalesce_usecs_low) ||
+           (ec->tx_max_coalesced_frames_low) ||
+           (ec->pkt_rate_high) ||
+           (ec->rx_coalesce_usecs_high) ||
+           (ec->rx_max_coalesced_frames_high) ||
+           (ec->tx_coalesce_usecs_high) ||
+           (ec->tx_max_coalesced_frames_high) ||
+           (ec->rate_sample_interval))
+               return -EOPNOTSUPP;
+
+       /* Can only change rx-frames when interface is down (see
+        * rx_descriptor_init in xgbe-dev.c)
+        */
+       rx_frames = pdata->rx_frames;
+       if (rx_frames != ec->rx_max_coalesced_frames && netif_running(netdev)) {
+               netdev_alert(netdev,
+                            "interface must be down to change rx-frames\n");
+               return -EINVAL;
+       }
+
+       rx_riwt = hw_if->usec_to_riwt(pdata, ec->rx_coalesce_usecs);
+       rx_frames = ec->rx_max_coalesced_frames;
+
+       /* Use smallest possible value if conversion resulted in zero */
+       if (ec->rx_coalesce_usecs && !rx_riwt)
+               rx_riwt = 1;
+
+       /* Check the bounds of values for Rx */
+       if (rx_riwt > XGMAC_MAX_DMA_RIWT) {
+               rx_usecs = hw_if->riwt_to_usec(pdata, XGMAC_MAX_DMA_RIWT);
+               netdev_alert(netdev, "rx-usec is limited to %d usecs\n",
+                            rx_usecs);
+               return -EINVAL;
+       }
+       if (rx_frames > pdata->channel->rx_ring->rdesc_count) {
+               netdev_alert(netdev, "rx-frames is limited to %d frames\n",
+                            pdata->channel->rx_ring->rdesc_count);
+               return -EINVAL;
+       }
+
+       tx_usecs = ec->tx_coalesce_usecs;
+       tx_frames = ec->tx_max_coalesced_frames;
+
+       /* Check the bounds of values for Tx */
+       if (tx_frames > pdata->channel->tx_ring->rdesc_count) {
+               netdev_alert(netdev, "tx-frames is limited to %d frames\n",
+                            pdata->channel->tx_ring->rdesc_count);
+               return -EINVAL;
+       }
+
+       pdata->rx_riwt = rx_riwt;
+       pdata->rx_frames = rx_frames;
+       hw_if->config_rx_coalesce(pdata);
+
+       pdata->tx_usecs = tx_usecs;
+       pdata->tx_frames = tx_frames;
+       hw_if->config_tx_coalesce(pdata);
+
+       DBGPR("<--xgbe_set_coalesce\n");
+
+       return 0;
+}
+
+static const struct ethtool_ops xgbe_ethtool_ops = {
+       .get_settings = xgbe_get_settings,
+       .set_settings = xgbe_set_settings,
+       .get_drvinfo = xgbe_get_drvinfo,
+       .get_link = ethtool_op_get_link,
+       .get_coalesce = xgbe_get_coalesce,
+       .set_coalesce = xgbe_set_coalesce,
+       .get_pauseparam = xgbe_get_pauseparam,
+       .set_pauseparam = xgbe_set_pauseparam,
+       .get_strings = xgbe_get_strings,
+       .get_ethtool_stats = xgbe_get_ethtool_stats,
+       .get_sset_count = xgbe_get_sset_count,
+};
+
+struct ethtool_ops *xgbe_get_ethtool_ops(void)
+{
+       return (struct ethtool_ops *)&xgbe_ethtool_ops;
+}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c
new file mode 100644 (file)
index 0000000..c83584a
--- /dev/null
@@ -0,0 +1,512 @@
+/*
+ * AMD 10Gb Ethernet driver
+ *
+ * This file is available to you under your choice of the following two
+ * licenses:
+ *
+ * License 1: GPLv2
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ *
+ * This file is free software; you may copy, redistribute 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 file 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, see <http://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and the Software, to deal in the Software
+ *     without restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ *     of the Software, and to permit persons to whom the Software is furnished
+ *     to do so, subject to the following conditions:
+ *
+ *     The above copyright notice and this permission notice shall be included
+ *     in all copies or substantial portions of the Software.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ *
+ *
+ * License 2: Modified BSD
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ * 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 of Advanced Micro Devices, Inc. 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 <COPYRIGHT HOLDER> 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.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and the Software, to deal in the Software
+ *     without restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ *     of the Software, and to permit persons to whom the Software is furnished
+ *     to do so, subject to the following conditions:
+ *
+ *     The above copyright notice and this permission notice shall be included
+ *     in all copies or substantial portions of the Software.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_net.h>
+#include <linux/clk.h>
+
+#include "xgbe.h"
+#include "xgbe-common.h"
+
+
+MODULE_AUTHOR("Tom Lendacky <thomas.lendacky@amd.com>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION(XGBE_DRV_VERSION);
+MODULE_DESCRIPTION(XGBE_DRV_DESC);
+
+static struct xgbe_channel *xgbe_alloc_rings(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_channel *channel_mem, *channel;
+       struct xgbe_ring *tx_ring, *rx_ring;
+       unsigned int count, i;
+
+       DBGPR("-->xgbe_alloc_rings\n");
+
+       count = max_t(unsigned int, pdata->tx_ring_count, pdata->rx_ring_count);
+
+       channel_mem = devm_kcalloc(pdata->dev, count,
+                                  sizeof(struct xgbe_channel), GFP_KERNEL);
+       if (!channel_mem)
+               return NULL;
+
+       tx_ring = devm_kcalloc(pdata->dev, pdata->tx_ring_count,
+                              sizeof(struct xgbe_ring), GFP_KERNEL);
+       if (!tx_ring)
+               return NULL;
+
+       rx_ring = devm_kcalloc(pdata->dev, pdata->rx_ring_count,
+                              sizeof(struct xgbe_ring), GFP_KERNEL);
+       if (!rx_ring)
+               return NULL;
+
+       for (i = 0, channel = channel_mem; i < count; i++, channel++) {
+               snprintf(channel->name, sizeof(channel->name), "channel-%d", i);
+               channel->pdata = pdata;
+               channel->queue_index = i;
+               channel->dma_regs = pdata->xgmac_regs + DMA_CH_BASE +
+                                   (DMA_CH_INC * i);
+
+               if (i < pdata->tx_ring_count) {
+                       spin_lock_init(&tx_ring->lock);
+                       channel->tx_ring = tx_ring++;
+               }
+
+               if (i < pdata->rx_ring_count) {
+                       spin_lock_init(&tx_ring->lock);
+                       channel->rx_ring = rx_ring++;
+               }
+
+               DBGPR("  %s - queue_index=%u, dma_regs=%p, tx=%p, rx=%p\n",
+                     channel->name, channel->queue_index, channel->dma_regs,
+                     channel->tx_ring, channel->rx_ring);
+       }
+
+       pdata->channel_count = count;
+
+       DBGPR("<--xgbe_alloc_rings\n");
+
+       return channel_mem;
+}
+
+static void xgbe_default_config(struct xgbe_prv_data *pdata)
+{
+       DBGPR("-->xgbe_default_config\n");
+
+       pdata->pblx8 = DMA_PBL_X8_ENABLE;
+       pdata->tx_sf_mode = MTL_TSF_ENABLE;
+       pdata->tx_threshold = MTL_TX_THRESHOLD_64;
+       pdata->tx_pbl = DMA_PBL_16;
+       pdata->tx_osp_mode = DMA_OSP_ENABLE;
+       pdata->rx_sf_mode = MTL_RSF_DISABLE;
+       pdata->rx_threshold = MTL_RX_THRESHOLD_64;
+       pdata->rx_pbl = DMA_PBL_16;
+       pdata->pause_autoneg = 1;
+       pdata->tx_pause = 1;
+       pdata->rx_pause = 1;
+       pdata->power_down = 0;
+       pdata->default_autoneg = AUTONEG_ENABLE;
+       pdata->default_speed = SPEED_10000;
+
+       DBGPR("<--xgbe_default_config\n");
+}
+
+static void xgbe_init_all_fptrs(struct xgbe_prv_data *pdata)
+{
+       xgbe_init_function_ptrs_dev(&pdata->hw_if);
+       xgbe_init_function_ptrs_desc(&pdata->desc_if);
+}
+
+static int xgbe_probe(struct platform_device *pdev)
+{
+       struct xgbe_prv_data *pdata;
+       struct xgbe_hw_if *hw_if;
+       struct xgbe_desc_if *desc_if;
+       struct net_device *netdev;
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       const u8 *mac_addr;
+       int ret;
+
+       DBGPR("--> xgbe_probe\n");
+
+       netdev = alloc_etherdev_mq(sizeof(struct xgbe_prv_data),
+                                  XGBE_MAX_DMA_CHANNELS);
+       if (!netdev) {
+               dev_err(dev, "alloc_etherdev failed\n");
+               ret = -ENOMEM;
+               goto err_alloc;
+       }
+       SET_NETDEV_DEV(netdev, dev);
+       pdata = netdev_priv(netdev);
+       pdata->netdev = netdev;
+       pdata->pdev = pdev;
+       pdata->dev = dev;
+       platform_set_drvdata(pdev, netdev);
+
+       spin_lock_init(&pdata->lock);
+       mutex_init(&pdata->xpcs_mutex);
+
+       /* Set and validate the number of descriptors for a ring */
+       BUILD_BUG_ON_NOT_POWER_OF_2(TX_DESC_CNT);
+       pdata->tx_desc_count = TX_DESC_CNT;
+       if (pdata->tx_desc_count & (pdata->tx_desc_count - 1)) {
+               dev_err(dev, "tx descriptor count (%d) is not valid\n",
+                       pdata->tx_desc_count);
+               ret = -EINVAL;
+               goto err_io;
+       }
+       BUILD_BUG_ON_NOT_POWER_OF_2(RX_DESC_CNT);
+       pdata->rx_desc_count = RX_DESC_CNT;
+       if (pdata->rx_desc_count & (pdata->rx_desc_count - 1)) {
+               dev_err(dev, "rx descriptor count (%d) is not valid\n",
+                       pdata->rx_desc_count);
+               ret = -EINVAL;
+               goto err_io;
+       }
+
+       /* Obtain the system clock setting */
+       pdata->sysclock = devm_clk_get(dev, NULL);
+       if (IS_ERR(pdata->sysclock)) {
+               dev_err(dev, "devm_clk_get failed\n");
+               ret = PTR_ERR(pdata->sysclock);
+               goto err_io;
+       }
+
+       /* Obtain the mmio areas for the device */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       pdata->xgmac_regs = devm_ioremap_resource(dev, res);
+       if (IS_ERR(pdata->xgmac_regs)) {
+               dev_err(dev, "xgmac ioremap failed\n");
+               ret = PTR_ERR(pdata->xgmac_regs);
+               goto err_io;
+       }
+       DBGPR("  xgmac_regs = %p\n", pdata->xgmac_regs);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       pdata->xpcs_regs = devm_ioremap_resource(dev, res);
+       if (IS_ERR(pdata->xpcs_regs)) {
+               dev_err(dev, "xpcs ioremap failed\n");
+               ret = PTR_ERR(pdata->xpcs_regs);
+               goto err_io;
+       }
+       DBGPR("  xpcs_regs  = %p\n", pdata->xpcs_regs);
+
+       /* Set the DMA mask */
+       if (!dev->dma_mask)
+               dev->dma_mask = &dev->coherent_dma_mask;
+       *(dev->dma_mask) = DMA_BIT_MASK(40);
+       dev->coherent_dma_mask = DMA_BIT_MASK(40);
+
+       ret = platform_get_irq(pdev, 0);
+       if (ret < 0) {
+               dev_err(dev, "platform_get_irq failed\n");
+               goto err_io;
+       }
+       netdev->irq = ret;
+       netdev->base_addr = (unsigned long)pdata->xgmac_regs;
+
+       /* Set all the function pointers */
+       xgbe_init_all_fptrs(pdata);
+       hw_if = &pdata->hw_if;
+       desc_if = &pdata->desc_if;
+
+       /* Issue software reset to device */
+       hw_if->exit(pdata);
+
+       /* Populate the hardware features */
+       xgbe_get_all_hw_features(pdata);
+
+       /* Retrieve the MAC address */
+       mac_addr = of_get_mac_address(dev->of_node);
+       if (!mac_addr) {
+               dev_err(dev, "invalid mac address for this device\n");
+               ret = -EINVAL;
+               goto err_io;
+       }
+       memcpy(netdev->dev_addr, mac_addr, netdev->addr_len);
+
+       /* Retrieve the PHY mode - it must be "xgmii" */
+       pdata->phy_mode = of_get_phy_mode(dev->of_node);
+       if (pdata->phy_mode != PHY_INTERFACE_MODE_XGMII) {
+               dev_err(dev, "invalid phy-mode specified for this device\n");
+               ret = -EINVAL;
+               goto err_io;
+       }
+
+       /* Set default configuration data */
+       xgbe_default_config(pdata);
+
+       /* Calculate the number of Tx and Rx rings to be created */
+       pdata->tx_ring_count = min_t(unsigned int, num_online_cpus(),
+                                    pdata->hw_feat.tx_ch_cnt);
+       if (netif_set_real_num_tx_queues(netdev, pdata->tx_ring_count)) {
+               dev_err(dev, "error setting real tx queue count\n");
+               goto err_io;
+       }
+
+       pdata->rx_ring_count = min_t(unsigned int,
+                                    netif_get_num_default_rss_queues(),
+                                    pdata->hw_feat.rx_ch_cnt);
+       ret = netif_set_real_num_rx_queues(netdev, pdata->rx_ring_count);
+       if (ret) {
+               dev_err(dev, "error setting real rx queue count\n");
+               goto err_io;
+       }
+
+       /* Allocate the rings for the DMA channels */
+       pdata->channel = xgbe_alloc_rings(pdata);
+       if (!pdata->channel) {
+               dev_err(dev, "ring allocation failed\n");
+               ret = -ENOMEM;
+               goto err_io;
+       }
+
+       /* Prepare to regsiter with MDIO */
+       pdata->mii_bus_id = kasprintf(GFP_KERNEL, "%s", pdev->name);
+       if (!pdata->mii_bus_id) {
+               dev_err(dev, "failed to allocate mii bus id\n");
+               ret = -ENOMEM;
+               goto err_io;
+       }
+       ret = xgbe_mdio_register(pdata);
+       if (ret)
+               goto err_bus_id;
+
+       /* Set network and ethtool operations */
+       netdev->netdev_ops = xgbe_get_netdev_ops();
+       netdev->ethtool_ops = xgbe_get_ethtool_ops();
+
+       /* Set device features */
+       netdev->hw_features = NETIF_F_SG |
+                             NETIF_F_IP_CSUM |
+                             NETIF_F_IPV6_CSUM |
+                             NETIF_F_RXCSUM |
+                             NETIF_F_TSO |
+                             NETIF_F_TSO6 |
+                             NETIF_F_GRO |
+                             NETIF_F_HW_VLAN_CTAG_RX |
+                             NETIF_F_HW_VLAN_CTAG_TX;
+
+       netdev->vlan_features |= NETIF_F_SG |
+                                NETIF_F_IP_CSUM |
+                                NETIF_F_IPV6_CSUM |
+                                NETIF_F_TSO |
+                                NETIF_F_TSO6;
+
+       netdev->features |= netdev->hw_features;
+       pdata->netdev_features = netdev->features;
+
+       xgbe_init_rx_coalesce(pdata);
+       xgbe_init_tx_coalesce(pdata);
+
+       netif_carrier_off(netdev);
+       ret = register_netdev(netdev);
+       if (ret) {
+               dev_err(dev, "net device registration failed\n");
+               goto err_reg_netdev;
+       }
+
+       xgbe_debugfs_init(pdata);
+
+       netdev_notice(netdev, "net device enabled\n");
+
+       DBGPR("<-- xgbe_probe\n");
+
+       return 0;
+
+err_reg_netdev:
+       xgbe_mdio_unregister(pdata);
+
+err_bus_id:
+       kfree(pdata->mii_bus_id);
+
+err_io:
+       free_netdev(netdev);
+
+err_alloc:
+       dev_notice(dev, "net device not enabled\n");
+
+       return ret;
+}
+
+static int xgbe_remove(struct platform_device *pdev)
+{
+       struct net_device *netdev = platform_get_drvdata(pdev);
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+
+       DBGPR("-->xgbe_remove\n");
+
+       xgbe_debugfs_exit(pdata);
+
+       unregister_netdev(netdev);
+
+       xgbe_mdio_unregister(pdata);
+
+       kfree(pdata->mii_bus_id);
+
+       free_netdev(netdev);
+
+       DBGPR("<--xgbe_remove\n");
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int xgbe_suspend(struct device *dev)
+{
+       struct net_device *netdev = dev_get_drvdata(dev);
+       int ret;
+
+       DBGPR("-->xgbe_suspend\n");
+
+       if (!netif_running(netdev)) {
+               DBGPR("<--xgbe_dev_suspend\n");
+               return -EINVAL;
+       }
+
+       ret = xgbe_powerdown(netdev, XGMAC_DRIVER_CONTEXT);
+
+       DBGPR("<--xgbe_suspend\n");
+
+       return ret;
+}
+
+static int xgbe_resume(struct device *dev)
+{
+       struct net_device *netdev = dev_get_drvdata(dev);
+       int ret;
+
+       DBGPR("-->xgbe_resume\n");
+
+       if (!netif_running(netdev)) {
+               DBGPR("<--xgbe_dev_resume\n");
+               return -EINVAL;
+       }
+
+       ret = xgbe_powerup(netdev, XGMAC_DRIVER_CONTEXT);
+
+       DBGPR("<--xgbe_resume\n");
+
+       return ret;
+}
+#endif /* CONFIG_PM */
+
+static const struct of_device_id xgbe_of_match[] = {
+       { .compatible = "amd,xgbe-seattle-v1a", },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, xgbe_of_match);
+static SIMPLE_DEV_PM_OPS(xgbe_pm_ops, xgbe_suspend, xgbe_resume);
+
+static struct platform_driver xgbe_driver = {
+       .driver = {
+               .name = "amd-xgbe",
+               .of_match_table = xgbe_of_match,
+               .pm = &xgbe_pm_ops,
+       },
+       .probe = xgbe_probe,
+       .remove = xgbe_remove,
+};
+
+module_platform_driver(xgbe_driver);
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
new file mode 100644 (file)
index 0000000..ea7a5d6
--- /dev/null
@@ -0,0 +1,433 @@
+/*
+ * AMD 10Gb Ethernet driver
+ *
+ * This file is available to you under your choice of the following two
+ * licenses:
+ *
+ * License 1: GPLv2
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ *
+ * This file is free software; you may copy, redistribute 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 file 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, see <http://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and the Software, to deal in the Software
+ *     without restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ *     of the Software, and to permit persons to whom the Software is furnished
+ *     to do so, subject to the following conditions:
+ *
+ *     The above copyright notice and this permission notice shall be included
+ *     in all copies or substantial portions of the Software.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ *
+ *
+ * License 2: Modified BSD
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ * 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 of Advanced Micro Devices, Inc. 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 <COPYRIGHT HOLDER> 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.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and the Software, to deal in the Software
+ *     without restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ *     of the Software, and to permit persons to whom the Software is furnished
+ *     to do so, subject to the following conditions:
+ *
+ *     The above copyright notice and this permission notice shall be included
+ *     in all copies or substantial portions of the Software.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ */
+
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/spinlock.h>
+#include <linux/mdio.h>
+#include <linux/phy.h>
+#include <linux/of.h>
+
+#include "xgbe.h"
+#include "xgbe-common.h"
+
+
+static int xgbe_mdio_read(struct mii_bus *mii, int prtad, int mmd_reg)
+{
+       struct xgbe_prv_data *pdata = mii->priv;
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       int mmd_data;
+
+       DBGPR_MDIO("-->xgbe_mdio_read: prtad=%#x mmd_reg=%#x\n",
+                  prtad, mmd_reg);
+
+       mmd_data = hw_if->read_mmd_regs(pdata, prtad, mmd_reg);
+
+       DBGPR_MDIO("<--xgbe_mdio_read: mmd_data=%#x\n", mmd_data);
+
+       return mmd_data;
+}
+
+static int xgbe_mdio_write(struct mii_bus *mii, int prtad, int mmd_reg,
+                          u16 mmd_val)
+{
+       struct xgbe_prv_data *pdata = mii->priv;
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       int mmd_data = mmd_val;
+
+       DBGPR_MDIO("-->xgbe_mdio_write: prtad=%#x mmd_reg=%#x mmd_data=%#x\n",
+                  prtad, mmd_reg, mmd_data);
+
+       hw_if->write_mmd_regs(pdata, prtad, mmd_reg, mmd_data);
+
+       DBGPR_MDIO("<--xgbe_mdio_write\n");
+
+       return 0;
+}
+
+static void xgbe_adjust_link(struct net_device *netdev)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_hw_if *hw_if = &pdata->hw_if;
+       struct phy_device *phydev = pdata->phydev;
+       unsigned long flags;
+       int new_state = 0;
+
+       if (phydev == NULL)
+               return;
+
+       DBGPR_MDIO("-->xgbe_adjust_link: address=%d, newlink=%d, curlink=%d\n",
+                  phydev->addr, phydev->link, pdata->phy_link);
+
+       spin_lock_irqsave(&pdata->lock, flags);
+
+       if (phydev->link) {
+               /* Flow control support */
+               if (pdata->pause_autoneg) {
+                       if (phydev->pause || phydev->asym_pause) {
+                               pdata->tx_pause = 1;
+                               pdata->rx_pause = 1;
+                       } else {
+                               pdata->tx_pause = 0;
+                               pdata->rx_pause = 0;
+                       }
+               }
+
+               if (pdata->tx_pause != pdata->phy_tx_pause) {
+                       hw_if->config_tx_flow_control(pdata);
+                       pdata->phy_tx_pause = pdata->tx_pause;
+               }
+
+               if (pdata->rx_pause != pdata->phy_rx_pause) {
+                       hw_if->config_rx_flow_control(pdata);
+                       pdata->phy_rx_pause = pdata->rx_pause;
+               }
+
+               /* Speed support */
+               if (phydev->speed != pdata->phy_speed) {
+                       new_state = 1;
+
+                       switch (phydev->speed) {
+                       case SPEED_10000:
+                               hw_if->set_xgmii_speed(pdata);
+                               break;
+
+                       case SPEED_2500:
+                               hw_if->set_gmii_2500_speed(pdata);
+                               break;
+
+                       case SPEED_1000:
+                               hw_if->set_gmii_speed(pdata);
+                               break;
+                       }
+                       pdata->phy_speed = phydev->speed;
+               }
+
+               if (phydev->link != pdata->phy_link) {
+                       new_state = 1;
+                       pdata->phy_link = 1;
+               }
+       } else if (pdata->phy_link) {
+               new_state = 1;
+               pdata->phy_link = 0;
+               pdata->phy_speed = SPEED_UNKNOWN;
+       }
+
+       if (new_state)
+               phy_print_status(phydev);
+
+       spin_unlock_irqrestore(&pdata->lock, flags);
+
+       DBGPR_MDIO("<--xgbe_adjust_link\n");
+}
+
+void xgbe_dump_phy_registers(struct xgbe_prv_data *pdata)
+{
+       struct device *dev = pdata->dev;
+       struct phy_device *phydev = pdata->mii->phy_map[XGBE_PRTAD];
+       int i;
+
+       dev_alert(dev, "\n************* PHY Reg dump **********************\n");
+
+       dev_alert(dev, "PCS Control Reg (%#04x) = %#04x\n", MDIO_CTRL1,
+                 XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1));
+       dev_alert(dev, "PCS Status Reg (%#04x) = %#04x\n", MDIO_STAT1,
+                 XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1));
+       dev_alert(dev, "Phy Id (PHYS ID 1 %#04x)= %#04x\n", MDIO_DEVID1,
+                 XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVID1));
+       dev_alert(dev, "Phy Id (PHYS ID 2 %#04x)= %#04x\n", MDIO_DEVID2,
+                 XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVID2));
+       dev_alert(dev, "Devices in Package (%#04x)= %#04x\n", MDIO_DEVS1,
+                 XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVS1));
+       dev_alert(dev, "Devices in Package (%#04x)= %#04x\n", MDIO_DEVS2,
+                 XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVS2));
+
+       dev_alert(dev, "Auto-Neg Control Reg (%#04x) = %#04x\n", MDIO_CTRL1,
+                 XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_CTRL1));
+       dev_alert(dev, "Auto-Neg Status Reg (%#04x) = %#04x\n", MDIO_STAT1,
+                 XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_STAT1));
+       dev_alert(dev, "Auto-Neg Ad Reg 1 (%#04x) = %#04x\n",
+                 MDIO_AN_ADVERTISE,
+                 XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE));
+       dev_alert(dev, "Auto-Neg Ad Reg 2 (%#04x) = %#04x\n",
+                 MDIO_AN_ADVERTISE + 1,
+                 XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1));
+       dev_alert(dev, "Auto-Neg Ad Reg 3 (%#04x) = %#04x\n",
+                 MDIO_AN_ADVERTISE + 2,
+                 XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2));
+       dev_alert(dev, "Auto-Neg Completion Reg (%#04x) = %#04x\n",
+                 MDIO_AN_COMP_STAT,
+                 XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_COMP_STAT));
+
+       dev_alert(dev, "MMD Device Mask = %#x\n",
+                 phydev->c45_ids.devices_in_package);
+       for (i = 0; i < ARRAY_SIZE(phydev->c45_ids.device_ids); i++)
+               dev_alert(dev, "  MMD %d: ID = %#08x\n", i,
+                         phydev->c45_ids.device_ids[i]);
+
+       dev_alert(dev, "\n*************************************************\n");
+}
+
+int xgbe_mdio_register(struct xgbe_prv_data *pdata)
+{
+       struct net_device *netdev = pdata->netdev;
+       struct device_node *phy_node;
+       struct mii_bus *mii;
+       struct phy_device *phydev;
+       int ret = 0;
+
+       DBGPR("-->xgbe_mdio_register\n");
+
+       /* Retrieve the phy-handle */
+       phy_node = of_parse_phandle(pdata->dev->of_node, "phy-handle", 0);
+       if (!phy_node) {
+               dev_err(pdata->dev, "unable to parse phy-handle\n");
+               return -EINVAL;
+       }
+
+       /* Register with the MDIO bus */
+       mii = mdiobus_alloc();
+       if (mii == NULL) {
+               dev_err(pdata->dev, "mdiobus_alloc failed\n");
+               ret = -ENOMEM;
+               goto err_node_get;
+       }
+
+       /* Register on the MDIO bus (don't probe any PHYs) */
+       mii->name = XGBE_PHY_NAME;
+       mii->read = xgbe_mdio_read;
+       mii->write = xgbe_mdio_write;
+       snprintf(mii->id, sizeof(mii->id), "%s", pdata->mii_bus_id);
+       mii->priv = pdata;
+       mii->phy_mask = ~0;
+       mii->parent = pdata->dev;
+       ret = mdiobus_register(mii);
+       if (ret) {
+               dev_err(pdata->dev, "mdiobus_register failed\n");
+               goto err_mdiobus_alloc;
+       }
+       DBGPR("  mdiobus_register succeeded for %s\n", pdata->mii_bus_id);
+
+       /* Probe the PCS using Clause 45 */
+       phydev = get_phy_device(mii, XGBE_PRTAD, true);
+       if (IS_ERR(phydev) || !phydev ||
+           !phydev->c45_ids.device_ids[MDIO_MMD_PCS]) {
+               dev_err(pdata->dev, "get_phy_device failed\n");
+               ret = phydev ? PTR_ERR(phydev) : -ENOLINK;
+               goto err_mdiobus_register;
+       }
+       request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT,
+                      MDIO_ID_ARGS(phydev->c45_ids.device_ids[MDIO_MMD_PCS]));
+
+       of_node_get(phy_node);
+       phydev->dev.of_node = phy_node;
+       ret = phy_device_register(phydev);
+       if (ret) {
+               dev_err(pdata->dev, "phy_device_register failed\n");
+               of_node_put(phy_node);
+               goto err_phy_device;
+       }
+
+       /* Add a reference to the PHY driver so it can't be unloaded */
+       pdata->phy_module = phydev->dev.driver ?
+                           phydev->dev.driver->owner : NULL;
+       if (!try_module_get(pdata->phy_module)) {
+               dev_err(pdata->dev, "try_module_get failed\n");
+               ret = -EIO;
+               goto err_phy_device;
+       }
+
+       pdata->mii = mii;
+       pdata->mdio_mmd = MDIO_MMD_PCS;
+
+       pdata->phy_link = -1;
+       pdata->phy_speed = SPEED_UNKNOWN;
+       pdata->phy_tx_pause = pdata->tx_pause;
+       pdata->phy_rx_pause = pdata->rx_pause;
+
+       ret = phy_connect_direct(netdev, phydev, &xgbe_adjust_link,
+                                pdata->phy_mode);
+       if (ret) {
+               netdev_err(netdev, "phy_connect_direct failed\n");
+               goto err_phy_device;
+       }
+
+       if (!phydev->drv || (phydev->drv->phy_id == 0)) {
+               netdev_err(netdev, "phy_id not valid\n");
+               ret = -ENODEV;
+               goto err_phy_connect;
+       }
+       DBGPR("  phy_connect_direct succeeded for PHY %s, link=%d\n",
+             dev_name(&phydev->dev), phydev->link);
+
+       phydev->autoneg = pdata->default_autoneg;
+       if (phydev->autoneg == AUTONEG_DISABLE) {
+               /* Add settings needed to force speed */
+               phydev->supported |= SUPPORTED_1000baseT_Full;
+               phydev->supported |= SUPPORTED_10000baseT_Full;
+
+               phydev->speed = pdata->default_speed;
+               phydev->duplex = DUPLEX_FULL;
+
+               phydev->advertising &= ~ADVERTISED_Autoneg;
+       }
+
+       pdata->phydev = phydev;
+
+       of_node_put(phy_node);
+
+       DBGPHY_REGS(pdata);
+
+       DBGPR("<--xgbe_mdio_register\n");
+
+       return 0;
+
+err_phy_connect:
+       phy_disconnect(phydev);
+
+err_phy_device:
+       phy_device_free(phydev);
+
+err_mdiobus_register:
+       mdiobus_unregister(mii);
+
+err_mdiobus_alloc:
+       mdiobus_free(mii);
+
+err_node_get:
+       of_node_put(phy_node);
+
+       return ret;
+}
+
+void xgbe_mdio_unregister(struct xgbe_prv_data *pdata)
+{
+       DBGPR("-->xgbe_mdio_unregister\n");
+
+       phy_disconnect(pdata->phydev);
+       pdata->phydev = NULL;
+
+       module_put(pdata->phy_module);
+       pdata->phy_module = NULL;
+
+       mdiobus_unregister(pdata->mii);
+       pdata->mii->priv = NULL;
+
+       mdiobus_free(pdata->mii);
+       pdata->mii = NULL;
+
+       DBGPR("<--xgbe_mdio_unregister\n");
+}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h
new file mode 100644 (file)
index 0000000..ab06271
--- /dev/null
@@ -0,0 +1,676 @@
+/*
+ * AMD 10Gb Ethernet driver
+ *
+ * This file is available to you under your choice of the following two
+ * licenses:
+ *
+ * License 1: GPLv2
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ *
+ * This file is free software; you may copy, redistribute 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 file 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, see <http://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and the Software, to deal in the Software
+ *     without restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ *     of the Software, and to permit persons to whom the Software is furnished
+ *     to do so, subject to the following conditions:
+ *
+ *     The above copyright notice and this permission notice shall be included
+ *     in all copies or substantial portions of the Software.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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.
+ *
+ *
+ * License 2: Modified BSD
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ * 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 of Advanced Micro Devices, Inc. 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 <COPYRIGHT HOLDER> 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.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and the Software, to deal in the Software
+ *     without restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ *     of the Software, and to permit persons to whom the Software is furnished
+ *     to do so, subject to the following conditions:
+ *
+ *     The above copyright notice and this permission notice shall be included
+ *     in all copies or substantial portions of the Software.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     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 __XGBE_H__
+#define __XGBE_H__
+
+#include <linux/dma-mapping.h>
+#include <linux/netdevice.h>
+#include <linux/workqueue.h>
+#include <linux/phy.h>
+
+
+#define XGBE_DRV_NAME          "amd-xgbe"
+#define XGBE_DRV_VERSION       "1.0.0-a"
+#define XGBE_DRV_DESC          "AMD 10 Gigabit Ethernet Driver"
+
+/* Descriptor related defines */
+#define TX_DESC_CNT            512
+#define TX_DESC_MIN_FREE       (TX_DESC_CNT >> 3)
+#define TX_DESC_MAX_PROC       (TX_DESC_CNT >> 1)
+#define RX_DESC_CNT            512
+
+#define TX_MAX_BUF_SIZE                (0x3fff & ~(64 - 1))
+
+#define RX_MIN_BUF_SIZE                (ETH_FRAME_LEN + ETH_FCS_LEN + VLAN_HLEN)
+#define RX_BUF_ALIGN           64
+
+#define XGBE_MAX_DMA_CHANNELS  16
+#define DMA_ARDOMAIN_SETTING   0x2
+#define DMA_ARCACHE_SETTING    0xb
+#define DMA_AWDOMAIN_SETTING   0x2
+#define DMA_AWCACHE_SETTING    0x7
+#define DMA_INTERRUPT_MASK     0x31c7
+
+#define XGMAC_MIN_PACKET       60
+#define XGMAC_STD_PACKET_MTU   1500
+#define XGMAC_MAX_STD_PACKET   1518
+#define XGMAC_JUMBO_PACKET_MTU 9000
+#define XGMAC_MAX_JUMBO_PACKET 9018
+
+#define MAX_MULTICAST_LIST     14
+#define TX_FLAGS_IP_PKT                0x00000001
+#define TX_FLAGS_TCP_PKT       0x00000002
+
+/* MDIO bus phy name */
+#define XGBE_PHY_NAME          "amd_xgbe_phy"
+#define XGBE_PRTAD             0
+
+/* Driver PMT macros */
+#define XGMAC_DRIVER_CONTEXT   1
+#define XGMAC_IOCTL_CONTEXT    2
+
+#define FIFO_SIZE_B(x)         (x)
+#define FIFO_SIZE_KB(x)                (x * 1024)
+
+#define XGBE_TC_CNT            2
+
+/* Helper macro for descriptor handling
+ *  Always use GET_DESC_DATA to access the descriptor data
+ *  since the index is free-running and needs to be and-ed
+ *  with the descriptor count value of the ring to index to
+ *  the proper descriptor data.
+ */
+#define GET_DESC_DATA(_ring, _idx)                             \
+       ((_ring)->rdata +                                       \
+        ((_idx) & ((_ring)->rdesc_count - 1)))
+
+
+/* Default coalescing parameters */
+#define XGMAC_INIT_DMA_TX_USECS                100
+#define XGMAC_INIT_DMA_TX_FRAMES       16
+
+#define XGMAC_MAX_DMA_RIWT             0xff
+#define XGMAC_INIT_DMA_RX_USECS                100
+#define XGMAC_INIT_DMA_RX_FRAMES       16
+
+/* Flow control queue count */
+#define XGMAC_MAX_FLOW_CONTROL_QUEUES  8
+
+
+struct xgbe_prv_data;
+
+struct xgbe_packet_data {
+       unsigned int attributes;
+
+       unsigned int errors;
+
+       unsigned int rdesc_count;
+       unsigned int length;
+
+       unsigned int header_len;
+       unsigned int tcp_header_len;
+       unsigned int tcp_payload_len;
+       unsigned short mss;
+
+       unsigned short vlan_ctag;
+};
+
+/* Common Rx and Tx descriptor mapping */
+struct xgbe_ring_desc {
+       unsigned int desc0;
+       unsigned int desc1;
+       unsigned int desc2;
+       unsigned int desc3;
+};
+
+/* Structure used to hold information related to the descriptor
+ * and the packet associated with the descriptor (always use
+ * use the GET_DESC_DATA macro to access this data from the ring)
+ */
+struct xgbe_ring_data {
+       struct xgbe_ring_desc *rdesc;   /* Virtual address of descriptor */
+       dma_addr_t rdesc_dma;           /* DMA address of descriptor */
+
+       struct sk_buff *skb;            /* Virtual address of SKB */
+       dma_addr_t skb_dma;             /* DMA address of SKB data */
+       unsigned int skb_dma_len;       /* Length of SKB DMA area */
+       unsigned int tso_header;        /* TSO header indicator */
+
+       unsigned short len;             /* Length of received Rx packet */
+
+       unsigned int interrupt;         /* Interrupt indicator */
+
+       unsigned int mapped_as_page;
+};
+
+struct xgbe_ring {
+       /* Ring lock - used just for TX rings at the moment */
+       spinlock_t lock;
+
+       /* Per packet related information */
+       struct xgbe_packet_data packet_data;
+
+       /* Virtual/DMA addresses and count of allocated descriptor memory */
+       struct xgbe_ring_desc *rdesc;
+       dma_addr_t rdesc_dma;
+       unsigned int rdesc_count;
+
+       /* Array of descriptor data corresponding the descriptor memory
+        * (always use the GET_DESC_DATA macro to access this data)
+        */
+       struct xgbe_ring_data *rdata;
+
+       /* Ring index values
+        *  cur   - Tx: index of descriptor to be used for current transfer
+        *          Rx: index of descriptor to check for packet availability
+        *  dirty - Tx: index of descriptor to check for transfer complete
+        *          Rx: count of descriptors in which a packet has been received
+        *              (used with skb_realloc_index to refresh the ring)
+        */
+       unsigned int cur;
+       unsigned int dirty;
+
+       /* Coalesce frame count used for interrupt bit setting */
+       unsigned int coalesce_count;
+
+       union {
+               struct {
+                       unsigned int queue_stopped;
+                       unsigned short cur_mss;
+                       unsigned short cur_vlan_ctag;
+               } tx;
+
+               struct {
+                       unsigned int realloc_index;
+                       unsigned int realloc_threshold;
+               } rx;
+       };
+} ____cacheline_aligned;
+
+/* Structure used to describe the descriptor rings associated with
+ * a DMA channel.
+ */
+struct xgbe_channel {
+       char name[16];
+
+       /* Address of private data area for device */
+       struct xgbe_prv_data *pdata;
+
+       /* Queue index and base address of queue's DMA registers */
+       unsigned int queue_index;
+       void __iomem *dma_regs;
+
+       unsigned int saved_ier;
+
+       unsigned int tx_timer_active;
+       struct hrtimer tx_timer;
+
+       struct xgbe_ring *tx_ring;
+       struct xgbe_ring *rx_ring;
+} ____cacheline_aligned;
+
+enum xgbe_int {
+       XGMAC_INT_DMA_ISR_DC0IS,
+       XGMAC_INT_DMA_CH_SR_TI,
+       XGMAC_INT_DMA_CH_SR_TPS,
+       XGMAC_INT_DMA_CH_SR_TBU,
+       XGMAC_INT_DMA_CH_SR_RI,
+       XGMAC_INT_DMA_CH_SR_RBU,
+       XGMAC_INT_DMA_CH_SR_RPS,
+       XGMAC_INT_DMA_CH_SR_FBE,
+       XGMAC_INT_DMA_ALL,
+};
+
+enum xgbe_int_state {
+       XGMAC_INT_STATE_SAVE,
+       XGMAC_INT_STATE_RESTORE,
+};
+
+enum xgbe_mtl_fifo_size {
+       XGMAC_MTL_FIFO_SIZE_256  = 0x00,
+       XGMAC_MTL_FIFO_SIZE_512  = 0x01,
+       XGMAC_MTL_FIFO_SIZE_1K   = 0x03,
+       XGMAC_MTL_FIFO_SIZE_2K   = 0x07,
+       XGMAC_MTL_FIFO_SIZE_4K   = 0x0f,
+       XGMAC_MTL_FIFO_SIZE_8K   = 0x1f,
+       XGMAC_MTL_FIFO_SIZE_16K  = 0x3f,
+       XGMAC_MTL_FIFO_SIZE_32K  = 0x7f,
+       XGMAC_MTL_FIFO_SIZE_64K  = 0xff,
+       XGMAC_MTL_FIFO_SIZE_128K = 0x1ff,
+       XGMAC_MTL_FIFO_SIZE_256K = 0x3ff,
+};
+
+struct xgbe_mmc_stats {
+       /* Tx Stats */
+       u64 txoctetcount_gb;
+       u64 txframecount_gb;
+       u64 txbroadcastframes_g;
+       u64 txmulticastframes_g;
+       u64 tx64octets_gb;
+       u64 tx65to127octets_gb;
+       u64 tx128to255octets_gb;
+       u64 tx256to511octets_gb;
+       u64 tx512to1023octets_gb;
+       u64 tx1024tomaxoctets_gb;
+       u64 txunicastframes_gb;
+       u64 txmulticastframes_gb;
+       u64 txbroadcastframes_gb;
+       u64 txunderflowerror;
+       u64 txoctetcount_g;
+       u64 txframecount_g;
+       u64 txpauseframes;
+       u64 txvlanframes_g;
+
+       /* Rx Stats */
+       u64 rxframecount_gb;
+       u64 rxoctetcount_gb;
+       u64 rxoctetcount_g;
+       u64 rxbroadcastframes_g;
+       u64 rxmulticastframes_g;
+       u64 rxcrcerror;
+       u64 rxrunterror;
+       u64 rxjabbererror;
+       u64 rxundersize_g;
+       u64 rxoversize_g;
+       u64 rx64octets_gb;
+       u64 rx65to127octets_gb;
+       u64 rx128to255octets_gb;
+       u64 rx256to511octets_gb;
+       u64 rx512to1023octets_gb;
+       u64 rx1024tomaxoctets_gb;
+       u64 rxunicastframes_g;
+       u64 rxlengtherror;
+       u64 rxoutofrangetype;
+       u64 rxpauseframes;
+       u64 rxfifooverflow;
+       u64 rxvlanframes_gb;
+       u64 rxwatchdogerror;
+};
+
+struct xgbe_hw_if {
+       int (*tx_complete)(struct xgbe_ring_desc *);
+
+       int (*set_promiscuous_mode)(struct xgbe_prv_data *, unsigned int);
+       int (*set_all_multicast_mode)(struct xgbe_prv_data *, unsigned int);
+       int (*set_addn_mac_addrs)(struct xgbe_prv_data *, unsigned int);
+       int (*set_mac_address)(struct xgbe_prv_data *, u8 *addr);
+
+       int (*enable_rx_csum)(struct xgbe_prv_data *);
+       int (*disable_rx_csum)(struct xgbe_prv_data *);
+
+       int (*enable_rx_vlan_stripping)(struct xgbe_prv_data *);
+       int (*disable_rx_vlan_stripping)(struct xgbe_prv_data *);
+
+       int (*read_mmd_regs)(struct xgbe_prv_data *, int, int);
+       void (*write_mmd_regs)(struct xgbe_prv_data *, int, int, int);
+       int (*set_gmii_speed)(struct xgbe_prv_data *);
+       int (*set_gmii_2500_speed)(struct xgbe_prv_data *);
+       int (*set_xgmii_speed)(struct xgbe_prv_data *);
+
+       void (*enable_tx)(struct xgbe_prv_data *);
+       void (*disable_tx)(struct xgbe_prv_data *);
+       void (*enable_rx)(struct xgbe_prv_data *);
+       void (*disable_rx)(struct xgbe_prv_data *);
+
+       void (*powerup_tx)(struct xgbe_prv_data *);
+       void (*powerdown_tx)(struct xgbe_prv_data *);
+       void (*powerup_rx)(struct xgbe_prv_data *);
+       void (*powerdown_rx)(struct xgbe_prv_data *);
+
+       int (*init)(struct xgbe_prv_data *);
+       int (*exit)(struct xgbe_prv_data *);
+
+       int (*enable_int)(struct xgbe_channel *, enum xgbe_int);
+       int (*disable_int)(struct xgbe_channel *, enum xgbe_int);
+       void (*pre_xmit)(struct xgbe_channel *);
+       int (*dev_read)(struct xgbe_channel *);
+       void (*tx_desc_init)(struct xgbe_channel *);
+       void (*rx_desc_init)(struct xgbe_channel *);
+       void (*rx_desc_reset)(struct xgbe_ring_data *);
+       void (*tx_desc_reset)(struct xgbe_ring_data *);
+       int (*is_last_desc)(struct xgbe_ring_desc *);
+       int (*is_context_desc)(struct xgbe_ring_desc *);
+
+       /* For FLOW ctrl */
+       int (*config_tx_flow_control)(struct xgbe_prv_data *);
+       int (*config_rx_flow_control)(struct xgbe_prv_data *);
+
+       /* For RX coalescing */
+       int (*config_rx_coalesce)(struct xgbe_prv_data *);
+       int (*config_tx_coalesce)(struct xgbe_prv_data *);
+       unsigned int (*usec_to_riwt)(struct xgbe_prv_data *, unsigned int);
+       unsigned int (*riwt_to_usec)(struct xgbe_prv_data *, unsigned int);
+
+       /* For RX and TX threshold config */
+       int (*config_rx_threshold)(struct xgbe_prv_data *, unsigned int);
+       int (*config_tx_threshold)(struct xgbe_prv_data *, unsigned int);
+
+       /* For RX and TX Store and Forward Mode config */
+       int (*config_rsf_mode)(struct xgbe_prv_data *, unsigned int);
+       int (*config_tsf_mode)(struct xgbe_prv_data *, unsigned int);
+
+       /* For TX DMA Operate on Second Frame config */
+       int (*config_osp_mode)(struct xgbe_prv_data *);
+
+       /* For RX and TX PBL config */
+       int (*config_rx_pbl_val)(struct xgbe_prv_data *);
+       int (*get_rx_pbl_val)(struct xgbe_prv_data *);
+       int (*config_tx_pbl_val)(struct xgbe_prv_data *);
+       int (*get_tx_pbl_val)(struct xgbe_prv_data *);
+       int (*config_pblx8)(struct xgbe_prv_data *);
+
+       /* For MMC statistics */
+       void (*rx_mmc_int)(struct xgbe_prv_data *);
+       void (*tx_mmc_int)(struct xgbe_prv_data *);
+       void (*read_mmc_stats)(struct xgbe_prv_data *);
+};
+
+struct xgbe_desc_if {
+       int (*alloc_ring_resources)(struct xgbe_prv_data *);
+       void (*free_ring_resources)(struct xgbe_prv_data *);
+       int (*map_tx_skb)(struct xgbe_channel *, struct sk_buff *);
+       void (*realloc_skb)(struct xgbe_channel *);
+       void (*unmap_skb)(struct xgbe_prv_data *, struct xgbe_ring_data *);
+       void (*wrapper_tx_desc_init)(struct xgbe_prv_data *);
+       void (*wrapper_rx_desc_init)(struct xgbe_prv_data *);
+};
+
+/* This structure contains flags that indicate what hardware features
+ * or configurations are present in the device.
+ */
+struct xgbe_hw_features {
+       /* HW Feature Register0 */
+       unsigned int gmii;              /* 1000 Mbps support */
+       unsigned int vlhash;            /* VLAN Hash Filter */
+       unsigned int sma;               /* SMA(MDIO) Interface */
+       unsigned int rwk;               /* PMT remote wake-up packet */
+       unsigned int mgk;               /* PMT magic packet */
+       unsigned int mmc;               /* RMON module */
+       unsigned int aoe;               /* ARP Offload */
+       unsigned int ts;                /* IEEE 1588-2008 Adavanced Timestamp */
+       unsigned int eee;               /* Energy Efficient Ethernet */
+       unsigned int tx_coe;            /* Tx Checksum Offload */
+       unsigned int rx_coe;            /* Rx Checksum Offload */
+       unsigned int addn_mac;          /* Additional MAC Addresses */
+       unsigned int ts_src;            /* Timestamp Source */
+       unsigned int sa_vlan_ins;       /* Source Address or VLAN Insertion */
+
+       /* HW Feature Register1 */
+       unsigned int rx_fifo_size;      /* MTL Receive FIFO Size */
+       unsigned int tx_fifo_size;      /* MTL Transmit FIFO Size */
+       unsigned int adv_ts_hi;         /* Advance Timestamping High Word */
+       unsigned int dcb;               /* DCB Feature */
+       unsigned int sph;               /* Split Header Feature */
+       unsigned int tso;               /* TCP Segmentation Offload */
+       unsigned int dma_debug;         /* DMA Debug Registers */
+       unsigned int rss;               /* Receive Side Scaling */
+       unsigned int hash_table_size;   /* Hash Table Size */
+       unsigned int l3l4_filter_num;   /* Number of L3-L4 Filters */
+
+       /* HW Feature Register2 */
+       unsigned int rx_q_cnt;          /* Number of MTL Receive Queues */
+       unsigned int tx_q_cnt;          /* Number of MTL Transmit Queues */
+       unsigned int rx_ch_cnt;         /* Number of DMA Receive Channels */
+       unsigned int tx_ch_cnt;         /* Number of DMA Transmit Channels */
+       unsigned int pps_out_num;       /* Number of PPS outputs */
+       unsigned int aux_snap_num;      /* Number of Aux snapshot inputs */
+};
+
+struct xgbe_prv_data {
+       struct net_device *netdev;
+       struct platform_device *pdev;
+       struct device *dev;
+
+       /* XGMAC/XPCS related mmio registers */
+       void __iomem *xgmac_regs;       /* XGMAC CSRs */
+       void __iomem *xpcs_regs;        /* XPCS MMD registers */
+
+       /* Overall device lock */
+       spinlock_t lock;
+
+       /* XPCS indirect addressing mutex */
+       struct mutex xpcs_mutex;
+
+       int irq_number;
+
+       struct xgbe_hw_if hw_if;
+       struct xgbe_desc_if desc_if;
+
+       /* Rings for Tx/Rx on a DMA channel */
+       struct xgbe_channel *channel;
+       unsigned int channel_count;
+       unsigned int tx_ring_count;
+       unsigned int tx_desc_count;
+       unsigned int rx_ring_count;
+       unsigned int rx_desc_count;
+
+       /* Tx/Rx common settings */
+       unsigned int pblx8;
+
+       /* Tx settings */
+       unsigned int tx_sf_mode;
+       unsigned int tx_threshold;
+       unsigned int tx_pbl;
+       unsigned int tx_osp_mode;
+
+       /* Rx settings */
+       unsigned int rx_sf_mode;
+       unsigned int rx_threshold;
+       unsigned int rx_pbl;
+
+       /* Tx coalescing settings */
+       unsigned int tx_usecs;
+       unsigned int tx_frames;
+
+       /* Rx coalescing settings */
+       unsigned int rx_riwt;
+       unsigned int rx_frames;
+
+       /* Current MTU */
+       unsigned int rx_buf_size;
+
+       /* Flow control settings */
+       unsigned int pause_autoneg;
+       unsigned int tx_pause;
+       unsigned int rx_pause;
+
+       /* MDIO settings */
+       struct module *phy_module;
+       char *mii_bus_id;
+       struct mii_bus *mii;
+       int mdio_mmd;
+       struct phy_device *phydev;
+       int default_autoneg;
+       int default_speed;
+
+       /* Current PHY settings */
+       phy_interface_t phy_mode;
+       int phy_link;
+       int phy_speed;
+       unsigned int phy_tx_pause;
+       unsigned int phy_rx_pause;
+
+       /* Netdev related settings */
+       netdev_features_t netdev_features;
+       struct napi_struct napi;
+       struct xgbe_mmc_stats mmc_stats;
+
+       /* System clock value used for Rx watchdog */
+       struct clk *sysclock;
+
+       /* Hardware features of the device */
+       struct xgbe_hw_features hw_feat;
+
+       /* Device restart work structure */
+       struct work_struct restart_work;
+
+       /* Keeps track of power mode */
+       unsigned int power_down;
+
+#ifdef CONFIG_DEBUG_FS
+       struct dentry *xgbe_debugfs;
+
+       unsigned int debugfs_xgmac_reg;
+
+       unsigned int debugfs_xpcs_mmd;
+       unsigned int debugfs_xpcs_reg;
+#endif
+};
+
+/* Function prototypes*/
+
+void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *);
+void xgbe_init_function_ptrs_desc(struct xgbe_desc_if *);
+struct net_device_ops *xgbe_get_netdev_ops(void);
+struct ethtool_ops *xgbe_get_ethtool_ops(void);
+
+int xgbe_mdio_register(struct xgbe_prv_data *);
+void xgbe_mdio_unregister(struct xgbe_prv_data *);
+void xgbe_dump_phy_registers(struct xgbe_prv_data *);
+void xgbe_dump_tx_desc(struct xgbe_ring *, unsigned int, unsigned int,
+                      unsigned int);
+void xgbe_dump_rx_desc(struct xgbe_ring *, struct xgbe_ring_desc *,
+                      unsigned int);
+void xgbe_print_pkt(struct net_device *, struct sk_buff *, bool);
+void xgbe_get_all_hw_features(struct xgbe_prv_data *);
+int xgbe_powerup(struct net_device *, unsigned int);
+int xgbe_powerdown(struct net_device *, unsigned int);
+void xgbe_init_rx_coalesce(struct xgbe_prv_data *);
+void xgbe_init_tx_coalesce(struct xgbe_prv_data *);
+
+#ifdef CONFIG_DEBUG_FS
+void xgbe_debugfs_init(struct xgbe_prv_data *);
+void xgbe_debugfs_exit(struct xgbe_prv_data *);
+#else
+static inline void xgbe_debugfs_init(struct xgbe_prv_data *pdata) {}
+static inline void xgbe_debugfs_exit(struct xgbe_prv_data *pdata) {}
+#endif /* CONFIG_DEBUG_FS */
+
+/* NOTE: Uncomment for TX and RX DESCRIPTOR DUMP in KERNEL LOG */
+#if 0
+#define XGMAC_ENABLE_TX_DESC_DUMP
+#define XGMAC_ENABLE_RX_DESC_DUMP
+#endif
+
+/* NOTE: Uncomment for TX and RX PACKET DUMP in KERNEL LOG */
+#if 0
+#define XGMAC_ENABLE_TX_PKT_DUMP
+#define XGMAC_ENABLE_RX_PKT_DUMP
+#endif
+
+/* NOTE: Uncomment for function trace log messages in KERNEL LOG */
+#if 0
+#define YDEBUG
+#define YDEBUG_MDIO
+#endif
+
+/* For debug prints */
+#ifdef YDEBUG
+#define DBGPR(x...) pr_alert(x)
+#define DBGPHY_REGS(x...) xgbe_dump_phy_registers(x)
+#else
+#define DBGPR(x...) do { } while (0)
+#define DBGPHY_REGS(x...) do { } while (0)
+#endif
+
+#ifdef YDEBUG_MDIO
+#define DBGPR_MDIO(x...) pr_alert(x)
+#else
+#define DBGPR_MDIO(x...) do { } while (0)
+#endif
+
+#endif
index d647a7d115acb2e7d40f72ee5010cd1e6ea79982..18e2faccebb0dcb98bc19bdc333561776aec6b95 100644 (file)
@@ -13,6 +13,7 @@
  *             Vineet Gupta
  */
 
+#include <linux/crc32.h>
 #include <linux/etherdevice.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
@@ -362,6 +363,15 @@ static irqreturn_t arc_emac_intr(int irq, void *dev_instance)
        return IRQ_HANDLED;
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void arc_emac_poll_controller(struct net_device *dev)
+{
+       disable_irq(dev->irq);
+       arc_emac_intr(dev->irq, dev);
+       enable_irq(dev->irq);
+}
+#endif
+
 /**
  * arc_emac_open - Open the network device.
  * @ndev:      Pointer to the network device.
@@ -450,6 +460,41 @@ static int arc_emac_open(struct net_device *ndev)
        return 0;
 }
 
+/**
+ * arc_emac_set_rx_mode - Change the receive filtering mode.
+ * @ndev:      Pointer to the network device.
+ *
+ * This function enables/disables promiscuous or all-multicast mode
+ * and updates the multicast filtering list of the network device.
+ */
+static void arc_emac_set_rx_mode(struct net_device *ndev)
+{
+       struct arc_emac_priv *priv = netdev_priv(ndev);
+
+       if (ndev->flags & IFF_PROMISC) {
+               arc_reg_or(priv, R_CTRL, PROM_MASK);
+       } else {
+               arc_reg_clr(priv, R_CTRL, PROM_MASK);
+
+               if (ndev->flags & IFF_ALLMULTI) {
+                       arc_reg_set(priv, R_LAFL, ~0);
+                       arc_reg_set(priv, R_LAFH, ~0);
+               } else {
+                       struct netdev_hw_addr *ha;
+                       unsigned int filter[2] = { 0, 0 };
+                       int bit;
+
+                       netdev_for_each_mc_addr(ha, ndev) {
+                               bit = ether_crc_le(ETH_ALEN, ha->addr) >> 26;
+                               filter[bit >> 5] |= 1 << (bit & 31);
+                       }
+
+                       arc_reg_set(priv, R_LAFL, filter[0]);
+                       arc_reg_set(priv, R_LAFH, filter[1]);
+               }
+       }
+}
+
 /**
  * arc_emac_stop - Close the network device.
  * @ndev:      Pointer to the network device.
@@ -620,6 +665,10 @@ static const struct net_device_ops arc_emac_netdev_ops = {
        .ndo_start_xmit         = arc_emac_tx,
        .ndo_set_mac_address    = arc_emac_set_address,
        .ndo_get_stats          = arc_emac_stats,
+       .ndo_set_rx_mode        = arc_emac_set_rx_mode,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = arc_emac_poll_controller,
+#endif
 };
 
 static int arc_emac_probe(struct platform_device *pdev)
index 17bb9ce96260df20eba44a9f215778a62c28373e..49faa97a30c364185f7e988a7e64be5529a62382 100644 (file)
@@ -1302,7 +1302,7 @@ static int alx_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        }
 
        netdev->netdev_ops = &alx_netdev_ops;
-       SET_ETHTOOL_OPS(netdev, &alx_ethtool_ops);
+       netdev->ethtool_ops = &alx_ethtool_ops;
        netdev->irq = pdev->irq;
        netdev->watchdog_timeo = ALX_WATCHDOG_TIME;
 
index 859ea844ba0ff7c292994a59446951680a2e7b79..48694c239d5cee024055731d2aa586e57fe529e5 100644 (file)
@@ -56,8 +56,8 @@ static int atl1c_get_settings(struct net_device *netdev,
                else
                        ecmd->duplex = DUPLEX_HALF;
        } else {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
 
        ecmd->autoneg = AUTONEG_ENABLE;
@@ -305,5 +305,5 @@ static const struct ethtool_ops atl1c_ethtool_ops = {
 
 void atl1c_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &atl1c_ethtool_ops);
+       netdev->ethtool_ops = &atl1c_ethtool_ops;
 }
index 82b23861bf5598698f3a21232e37eaf693f83c6e..1be072f4afc2a261b5504302db5dba50b81ad268 100644 (file)
@@ -57,8 +57,8 @@ static int atl1e_get_settings(struct net_device *netdev,
                else
                        ecmd->duplex = DUPLEX_HALF;
        } else {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
 
        ecmd->autoneg = AUTONEG_ENABLE;
@@ -388,5 +388,5 @@ static const struct ethtool_ops atl1e_ethtool_ops = {
 
 void atl1e_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &atl1e_ethtool_ops);
+       netdev->ethtool_ops = &atl1e_ethtool_ops;
 }
index dfd0e91fa726852818b48b2853e9d60533d01dd7..b460db7919a28866c5f37db08c9a30b71a857840 100644 (file)
@@ -3258,8 +3258,8 @@ static int atl1_get_settings(struct net_device *netdev,
                else
                        ecmd->duplex = DUPLEX_HALF;
        } else {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
        if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
            hw->media_type == MEDIA_TYPE_1000M_FULL)
index 78befb522a528268fae32c68649ead0a01263366..6746bd7171460100a4d224e55bdcbe62a4efcecf 100644 (file)
@@ -1396,7 +1396,7 @@ static int atl2_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        atl2_setup_pcicmd(pdev);
 
        netdev->netdev_ops = &atl2_netdev_ops;
-       SET_ETHTOOL_OPS(netdev, &atl2_ethtool_ops);
+       netdev->ethtool_ops = &atl2_ethtool_ops;
        netdev->watchdog_timeo = 5 * HZ;
        strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1);
 
@@ -1769,8 +1769,8 @@ static int atl2_get_settings(struct net_device *netdev,
                else
                        ecmd->duplex = DUPLEX_HALF;
        } else {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
 
        ecmd->autoneg = AUTONEG_ENABLE;
index 85dbddd03722b20a861f53cba7fe00b6cb66f3db..3e488094b0731811459c66dcb0517d00cb7dfbbe 100644 (file)
@@ -150,4 +150,15 @@ config BGMAC
          In case of using this driver on BCM4706 it's also requires to enable
          BCMA_DRIVER_GMAC_CMN to make it work.
 
+config SYSTEMPORT
+       tristate "Broadcom SYSTEMPORT internal MAC support"
+       depends on OF
+       select MII
+       select PHYLIB
+       select FIXED_PHY if SYSTEMPORT=y
+       help
+         This driver supports the built-in Ethernet MACs found in the
+         Broadcom BCM7xxx Set Top Box family chipset using an internal
+         Ethernet switch.
+
 endif # NET_VENDOR_BROADCOM
index fd639a0d4c7d64b2b7db5eb084087502e3c6d63a..e2a958a657e0bb8f816d205e0792d3fdfbfc70a4 100644 (file)
@@ -11,3 +11,4 @@ obj-$(CONFIG_BNX2X) += bnx2x/
 obj-$(CONFIG_SB1250_MAC) += sb1250-mac.o
 obj-$(CONFIG_TIGON3) += tg3.o
 obj-$(CONFIG_BGMAC) += bgmac.o
+obj-$(CONFIG_SYSTEMPORT) += bcmsysport.o
index 05ba6258901794ab51842ddc8d630f4d1286798d..ca5a20a48b14cdb48ccd75aaa1c34715a8dcfee3 100644 (file)
@@ -2380,7 +2380,7 @@ static int b44_init_one(struct ssb_device *sdev,
        netif_napi_add(dev, &bp->napi, b44_poll, 64);
        dev->watchdog_timeo = B44_TX_TIMEOUT;
        dev->irq = sdev->irq;
-       SET_ETHTOOL_OPS(dev, &b44_ethtool_ops);
+       dev->ethtool_ops = &b44_ethtool_ops;
 
        err = ssb_bus_powerup(sdev->bus, 0);
        if (err) {
index a7d11f5565d69342ad296471a9a5d44f8d7c51d5..3e8d1a88ed3d7b597298100798e5286449ffafe8 100644 (file)
@@ -1315,8 +1315,7 @@ static const struct bcm_enet_stats bcm_enet_gstrings_stats[] = {
 
 };
 
-#define BCM_ENET_STATS_LEN     \
-       (sizeof(bcm_enet_gstrings_stats) / sizeof(struct bcm_enet_stats))
+#define BCM_ENET_STATS_LEN     ARRAY_SIZE(bcm_enet_gstrings_stats)
 
 static const u32 unused_mib_regs[] = {
        ETH_MIB_TX_ALL_OCTETS,
@@ -1898,7 +1897,7 @@ static int bcm_enet_probe(struct platform_device *pdev)
        dev->netdev_ops = &bcm_enet_ops;
        netif_napi_add(dev, &priv->napi, bcm_enet_poll, 16);
 
-       SET_ETHTOOL_OPS(dev, &bcm_enet_ethtool_ops);
+       dev->ethtool_ops = &bcm_enet_ethtool_ops;
        SET_NETDEV_DEV(dev, &pdev->dev);
 
        ret = register_netdev(dev);
@@ -2784,7 +2783,7 @@ static int bcm_enetsw_probe(struct platform_device *pdev)
        /* register netdevice */
        dev->netdev_ops = &bcm_enetsw_ops;
        netif_napi_add(dev, &priv->napi, bcm_enet_poll, 16);
-       SET_ETHTOOL_OPS(dev, &bcm_enetsw_ethtool_ops);
+       dev->ethtool_ops = &bcm_enetsw_ethtool_ops;
        SET_NETDEV_DEV(dev, &pdev->dev);
 
        spin_lock_init(&priv->enetsw_mdio_lock);
diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c
new file mode 100644 (file)
index 0000000..141160e
--- /dev/null
@@ -0,0 +1,1654 @@
+/*
+ * Broadcom BCM7xxx System Port Ethernet MAC driver
+ *
+ * Copyright (C) 2014 Broadcom 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.
+ */
+
+#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_net.h>
+#include <linux/of_mdio.h>
+#include <linux/phy.h>
+#include <linux/phy_fixed.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+
+#include "bcmsysport.h"
+
+/* I/O accessors register helpers */
+#define BCM_SYSPORT_IO_MACRO(name, offset) \
+static inline u32 name##_readl(struct bcm_sysport_priv *priv, u32 off) \
+{                                                                      \
+       u32 reg = __raw_readl(priv->base + offset + off);               \
+       return reg;                                                     \
+}                                                                      \
+static inline void name##_writel(struct bcm_sysport_priv *priv,                \
+                                 u32 val, u32 off)                     \
+{                                                                      \
+       __raw_writel(val, priv->base + offset + off);                   \
+}                                                                      \
+
+BCM_SYSPORT_IO_MACRO(intrl2_0, SYS_PORT_INTRL2_0_OFFSET);
+BCM_SYSPORT_IO_MACRO(intrl2_1, SYS_PORT_INTRL2_1_OFFSET);
+BCM_SYSPORT_IO_MACRO(umac, SYS_PORT_UMAC_OFFSET);
+BCM_SYSPORT_IO_MACRO(tdma, SYS_PORT_TDMA_OFFSET);
+BCM_SYSPORT_IO_MACRO(rdma, SYS_PORT_RDMA_OFFSET);
+BCM_SYSPORT_IO_MACRO(rxchk, SYS_PORT_RXCHK_OFFSET);
+BCM_SYSPORT_IO_MACRO(txchk, SYS_PORT_TXCHK_OFFSET);
+BCM_SYSPORT_IO_MACRO(rbuf, SYS_PORT_RBUF_OFFSET);
+BCM_SYSPORT_IO_MACRO(tbuf, SYS_PORT_TBUF_OFFSET);
+BCM_SYSPORT_IO_MACRO(topctrl, SYS_PORT_TOPCTRL_OFFSET);
+
+/* L2-interrupt masking/unmasking helpers, does automatic saving of the applied
+ * mask in a software copy to avoid CPU_MASK_STATUS reads in hot-paths.
+  */
+#define BCM_SYSPORT_INTR_L2(which)     \
+static inline void intrl2_##which##_mask_clear(struct bcm_sysport_priv *priv, \
+                                               u32 mask)               \
+{                                                                      \
+       intrl2_##which##_writel(priv, mask, INTRL2_CPU_MASK_CLEAR);     \
+       priv->irq##which##_mask &= ~(mask);                             \
+}                                                                      \
+static inline void intrl2_##which##_mask_set(struct bcm_sysport_priv *priv, \
+                                               u32 mask)               \
+{                                                                      \
+       intrl2_## which##_writel(priv, mask, INTRL2_CPU_MASK_SET);      \
+       priv->irq##which##_mask |= (mask);                              \
+}                                                                      \
+
+BCM_SYSPORT_INTR_L2(0)
+BCM_SYSPORT_INTR_L2(1)
+
+/* Register accesses to GISB/RBUS registers are expensive (few hundred
+ * nanoseconds), so keep the check for 64-bits explicit here to save
+ * one register write per-packet on 32-bits platforms.
+ */
+static inline void dma_desc_set_addr(struct bcm_sysport_priv *priv,
+                                    void __iomem *d,
+                                    dma_addr_t addr)
+{
+#ifdef CONFIG_PHYS_ADDR_T_64BIT
+       __raw_writel(upper_32_bits(addr) & DESC_ADDR_HI_MASK,
+                       d + DESC_ADDR_HI_STATUS_LEN);
+#endif
+       __raw_writel(lower_32_bits(addr), d + DESC_ADDR_LO);
+}
+
+static inline void tdma_port_write_desc_addr(struct bcm_sysport_priv *priv,
+                                               struct dma_desc *desc,
+                                               unsigned int port)
+{
+       /* Ports are latched, so write upper address first */
+       tdma_writel(priv, desc->addr_status_len, TDMA_WRITE_PORT_HI(port));
+       tdma_writel(priv, desc->addr_lo, TDMA_WRITE_PORT_LO(port));
+}
+
+/* Ethtool operations */
+static int bcm_sysport_set_settings(struct net_device *dev,
+                                   struct ethtool_cmd *cmd)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+
+       if (!netif_running(dev))
+               return -EINVAL;
+
+       return phy_ethtool_sset(priv->phydev, cmd);
+}
+
+static int bcm_sysport_get_settings(struct net_device *dev,
+                                       struct ethtool_cmd *cmd)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+
+       if (!netif_running(dev))
+               return -EINVAL;
+
+       return phy_ethtool_gset(priv->phydev, cmd);
+}
+
+static int bcm_sysport_set_rx_csum(struct net_device *dev,
+                                       netdev_features_t wanted)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+       u32 reg;
+
+       priv->rx_csum_en = !!(wanted & NETIF_F_RXCSUM);
+       reg = rxchk_readl(priv, RXCHK_CONTROL);
+       if (priv->rx_csum_en)
+               reg |= RXCHK_EN;
+       else
+               reg &= ~RXCHK_EN;
+
+       /* If UniMAC forwards CRC, we need to skip over it to get
+        * a valid CHK bit to be set in the per-packet status word
+        */
+       if (priv->rx_csum_en && priv->crc_fwd)
+               reg |= RXCHK_SKIP_FCS;
+       else
+               reg &= ~RXCHK_SKIP_FCS;
+
+       rxchk_writel(priv, reg, RXCHK_CONTROL);
+
+       return 0;
+}
+
+static int bcm_sysport_set_tx_csum(struct net_device *dev,
+                                       netdev_features_t wanted)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+       u32 reg;
+
+       /* Hardware transmit checksum requires us to enable the Transmit status
+        * block prepended to the packet contents
+        */
+       priv->tsb_en = !!(wanted & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM));
+       reg = tdma_readl(priv, TDMA_CONTROL);
+       if (priv->tsb_en)
+               reg |= TSB_EN;
+       else
+               reg &= ~TSB_EN;
+       tdma_writel(priv, reg, TDMA_CONTROL);
+
+       return 0;
+}
+
+static int bcm_sysport_set_features(struct net_device *dev,
+                                       netdev_features_t features)
+{
+       netdev_features_t changed = features ^ dev->features;
+       netdev_features_t wanted = dev->wanted_features;
+       int ret = 0;
+
+       if (changed & NETIF_F_RXCSUM)
+               ret = bcm_sysport_set_rx_csum(dev, wanted);
+       if (changed & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))
+               ret = bcm_sysport_set_tx_csum(dev, wanted);
+
+       return ret;
+}
+
+/* Hardware counters must be kept in sync because the order/offset
+ * is important here (order in structure declaration = order in hardware)
+ */
+static const struct bcm_sysport_stats bcm_sysport_gstrings_stats[] = {
+       /* general stats */
+       STAT_NETDEV(rx_packets),
+       STAT_NETDEV(tx_packets),
+       STAT_NETDEV(rx_bytes),
+       STAT_NETDEV(tx_bytes),
+       STAT_NETDEV(rx_errors),
+       STAT_NETDEV(tx_errors),
+       STAT_NETDEV(rx_dropped),
+       STAT_NETDEV(tx_dropped),
+       STAT_NETDEV(multicast),
+       /* UniMAC RSV counters */
+       STAT_MIB_RX("rx_64_octets", mib.rx.pkt_cnt.cnt_64),
+       STAT_MIB_RX("rx_65_127_oct", mib.rx.pkt_cnt.cnt_127),
+       STAT_MIB_RX("rx_128_255_oct", mib.rx.pkt_cnt.cnt_255),
+       STAT_MIB_RX("rx_256_511_oct", mib.rx.pkt_cnt.cnt_511),
+       STAT_MIB_RX("rx_512_1023_oct", mib.rx.pkt_cnt.cnt_1023),
+       STAT_MIB_RX("rx_1024_1518_oct", mib.rx.pkt_cnt.cnt_1518),
+       STAT_MIB_RX("rx_vlan_1519_1522_oct", mib.rx.pkt_cnt.cnt_mgv),
+       STAT_MIB_RX("rx_1522_2047_oct", mib.rx.pkt_cnt.cnt_2047),
+       STAT_MIB_RX("rx_2048_4095_oct", mib.rx.pkt_cnt.cnt_4095),
+       STAT_MIB_RX("rx_4096_9216_oct", mib.rx.pkt_cnt.cnt_9216),
+       STAT_MIB_RX("rx_pkts", mib.rx.pkt),
+       STAT_MIB_RX("rx_bytes", mib.rx.bytes),
+       STAT_MIB_RX("rx_multicast", mib.rx.mca),
+       STAT_MIB_RX("rx_broadcast", mib.rx.bca),
+       STAT_MIB_RX("rx_fcs", mib.rx.fcs),
+       STAT_MIB_RX("rx_control", mib.rx.cf),
+       STAT_MIB_RX("rx_pause", mib.rx.pf),
+       STAT_MIB_RX("rx_unknown", mib.rx.uo),
+       STAT_MIB_RX("rx_align", mib.rx.aln),
+       STAT_MIB_RX("rx_outrange", mib.rx.flr),
+       STAT_MIB_RX("rx_code", mib.rx.cde),
+       STAT_MIB_RX("rx_carrier", mib.rx.fcr),
+       STAT_MIB_RX("rx_oversize", mib.rx.ovr),
+       STAT_MIB_RX("rx_jabber", mib.rx.jbr),
+       STAT_MIB_RX("rx_mtu_err", mib.rx.mtue),
+       STAT_MIB_RX("rx_good_pkts", mib.rx.pok),
+       STAT_MIB_RX("rx_unicast", mib.rx.uc),
+       STAT_MIB_RX("rx_ppp", mib.rx.ppp),
+       STAT_MIB_RX("rx_crc", mib.rx.rcrc),
+       /* UniMAC TSV counters */
+       STAT_MIB_TX("tx_64_octets", mib.tx.pkt_cnt.cnt_64),
+       STAT_MIB_TX("tx_65_127_oct", mib.tx.pkt_cnt.cnt_127),
+       STAT_MIB_TX("tx_128_255_oct", mib.tx.pkt_cnt.cnt_255),
+       STAT_MIB_TX("tx_256_511_oct", mib.tx.pkt_cnt.cnt_511),
+       STAT_MIB_TX("tx_512_1023_oct", mib.tx.pkt_cnt.cnt_1023),
+       STAT_MIB_TX("tx_1024_1518_oct", mib.tx.pkt_cnt.cnt_1518),
+       STAT_MIB_TX("tx_vlan_1519_1522_oct", mib.tx.pkt_cnt.cnt_mgv),
+       STAT_MIB_TX("tx_1522_2047_oct", mib.tx.pkt_cnt.cnt_2047),
+       STAT_MIB_TX("tx_2048_4095_oct", mib.tx.pkt_cnt.cnt_4095),
+       STAT_MIB_TX("tx_4096_9216_oct", mib.tx.pkt_cnt.cnt_9216),
+       STAT_MIB_TX("tx_pkts", mib.tx.pkts),
+       STAT_MIB_TX("tx_multicast", mib.tx.mca),
+       STAT_MIB_TX("tx_broadcast", mib.tx.bca),
+       STAT_MIB_TX("tx_pause", mib.tx.pf),
+       STAT_MIB_TX("tx_control", mib.tx.cf),
+       STAT_MIB_TX("tx_fcs_err", mib.tx.fcs),
+       STAT_MIB_TX("tx_oversize", mib.tx.ovr),
+       STAT_MIB_TX("tx_defer", mib.tx.drf),
+       STAT_MIB_TX("tx_excess_defer", mib.tx.edf),
+       STAT_MIB_TX("tx_single_col", mib.tx.scl),
+       STAT_MIB_TX("tx_multi_col", mib.tx.mcl),
+       STAT_MIB_TX("tx_late_col", mib.tx.lcl),
+       STAT_MIB_TX("tx_excess_col", mib.tx.ecl),
+       STAT_MIB_TX("tx_frags", mib.tx.frg),
+       STAT_MIB_TX("tx_total_col", mib.tx.ncl),
+       STAT_MIB_TX("tx_jabber", mib.tx.jbr),
+       STAT_MIB_TX("tx_bytes", mib.tx.bytes),
+       STAT_MIB_TX("tx_good_pkts", mib.tx.pok),
+       STAT_MIB_TX("tx_unicast", mib.tx.uc),
+       /* UniMAC RUNT counters */
+       STAT_RUNT("rx_runt_pkts", mib.rx_runt_cnt),
+       STAT_RUNT("rx_runt_valid_fcs", mib.rx_runt_fcs),
+       STAT_RUNT("rx_runt_inval_fcs_align", mib.rx_runt_fcs_align),
+       STAT_RUNT("rx_runt_bytes", mib.rx_runt_bytes),
+       /* RXCHK misc statistics */
+       STAT_RXCHK("rxchk_bad_csum", mib.rxchk_bad_csum, RXCHK_BAD_CSUM_CNTR),
+       STAT_RXCHK("rxchk_other_pkt_disc", mib.rxchk_other_pkt_disc,
+                       RXCHK_OTHER_DISC_CNTR),
+       /* RBUF misc statistics */
+       STAT_RBUF("rbuf_ovflow_cnt", mib.rbuf_ovflow_cnt, RBUF_OVFL_DISC_CNTR),
+       STAT_RBUF("rbuf_err_cnt", mib.rbuf_err_cnt, RBUF_ERR_PKT_CNTR),
+};
+
+#define BCM_SYSPORT_STATS_LEN  ARRAY_SIZE(bcm_sysport_gstrings_stats)
+
+static void bcm_sysport_get_drvinfo(struct net_device *dev,
+                                       struct ethtool_drvinfo *info)
+{
+       strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
+       strlcpy(info->version, "0.1", sizeof(info->version));
+       strlcpy(info->bus_info, "platform", sizeof(info->bus_info));
+       info->n_stats = BCM_SYSPORT_STATS_LEN;
+}
+
+static u32 bcm_sysport_get_msglvl(struct net_device *dev)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+
+       return priv->msg_enable;
+}
+
+static void bcm_sysport_set_msglvl(struct net_device *dev, u32 enable)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+
+       priv->msg_enable = enable;
+}
+
+static int bcm_sysport_get_sset_count(struct net_device *dev, int string_set)
+{
+       switch (string_set) {
+       case ETH_SS_STATS:
+               return BCM_SYSPORT_STATS_LEN;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static void bcm_sysport_get_strings(struct net_device *dev,
+                                       u32 stringset, u8 *data)
+{
+       int i;
+
+       switch (stringset) {
+       case ETH_SS_STATS:
+               for (i = 0; i < BCM_SYSPORT_STATS_LEN; i++) {
+                       memcpy(data + i * ETH_GSTRING_LEN,
+                               bcm_sysport_gstrings_stats[i].stat_string,
+                               ETH_GSTRING_LEN);
+               }
+               break;
+       default:
+               break;
+       }
+}
+
+static void bcm_sysport_update_mib_counters(struct bcm_sysport_priv *priv)
+{
+       int i, j = 0;
+
+       for (i = 0; i < BCM_SYSPORT_STATS_LEN; i++) {
+               const struct bcm_sysport_stats *s;
+               u8 offset = 0;
+               u32 val = 0;
+               char *p;
+
+               s = &bcm_sysport_gstrings_stats[i];
+               switch (s->type) {
+               case BCM_SYSPORT_STAT_NETDEV:
+                       continue;
+               case BCM_SYSPORT_STAT_MIB_RX:
+               case BCM_SYSPORT_STAT_MIB_TX:
+               case BCM_SYSPORT_STAT_RUNT:
+                       if (s->type != BCM_SYSPORT_STAT_MIB_RX)
+                               offset = UMAC_MIB_STAT_OFFSET;
+                       val = umac_readl(priv, UMAC_MIB_START + j + offset);
+                       break;
+               case BCM_SYSPORT_STAT_RXCHK:
+                       val = rxchk_readl(priv, s->reg_offset);
+                       if (val == ~0)
+                               rxchk_writel(priv, 0, s->reg_offset);
+                       break;
+               case BCM_SYSPORT_STAT_RBUF:
+                       val = rbuf_readl(priv, s->reg_offset);
+                       if (val == ~0)
+                               rbuf_writel(priv, 0, s->reg_offset);
+                       break;
+               }
+
+               j += s->stat_sizeof;
+               p = (char *)priv + s->stat_offset;
+               *(u32 *)p = val;
+       }
+
+       netif_dbg(priv, hw, priv->netdev, "updated MIB counters\n");
+}
+
+static void bcm_sysport_get_stats(struct net_device *dev,
+                                       struct ethtool_stats *stats, u64 *data)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+       int i;
+
+       if (netif_running(dev))
+               bcm_sysport_update_mib_counters(priv);
+
+       for (i =  0; i < BCM_SYSPORT_STATS_LEN; i++) {
+               const struct bcm_sysport_stats *s;
+               char *p;
+
+               s = &bcm_sysport_gstrings_stats[i];
+               if (s->type == BCM_SYSPORT_STAT_NETDEV)
+                       p = (char *)&dev->stats;
+               else
+                       p = (char *)priv;
+               p += s->stat_offset;
+               data[i] = *(u32 *)p;
+       }
+}
+
+static void bcm_sysport_free_cb(struct bcm_sysport_cb *cb)
+{
+       dev_kfree_skb_any(cb->skb);
+       cb->skb = NULL;
+       dma_unmap_addr_set(cb, dma_addr, 0);
+}
+
+static int bcm_sysport_rx_refill(struct bcm_sysport_priv *priv,
+                                struct bcm_sysport_cb *cb)
+{
+       struct device *kdev = &priv->pdev->dev;
+       struct net_device *ndev = priv->netdev;
+       dma_addr_t mapping;
+       int ret;
+
+       cb->skb = netdev_alloc_skb(priv->netdev, RX_BUF_LENGTH);
+       if (!cb->skb) {
+               netif_err(priv, rx_err, ndev, "SKB alloc failed\n");
+               return -ENOMEM;
+       }
+
+       mapping = dma_map_single(kdev, cb->skb->data,
+                               RX_BUF_LENGTH, DMA_FROM_DEVICE);
+       ret = dma_mapping_error(kdev, mapping);
+       if (ret) {
+               bcm_sysport_free_cb(cb);
+               netif_err(priv, rx_err, ndev, "DMA mapping failure\n");
+               return ret;
+       }
+
+       dma_unmap_addr_set(cb, dma_addr, mapping);
+       dma_desc_set_addr(priv, priv->rx_bd_assign_ptr, mapping);
+
+       priv->rx_bd_assign_index++;
+       priv->rx_bd_assign_index &= (priv->num_rx_bds - 1);
+       priv->rx_bd_assign_ptr = priv->rx_bds +
+               (priv->rx_bd_assign_index * DESC_SIZE);
+
+       netif_dbg(priv, rx_status, ndev, "RX refill\n");
+
+       return 0;
+}
+
+static int bcm_sysport_alloc_rx_bufs(struct bcm_sysport_priv *priv)
+{
+       struct bcm_sysport_cb *cb;
+       int ret = 0;
+       unsigned int i;
+
+       for (i = 0; i < priv->num_rx_bds; i++) {
+               cb = &priv->rx_cbs[priv->rx_bd_assign_index];
+               if (cb->skb)
+                       continue;
+
+               ret = bcm_sysport_rx_refill(priv, cb);
+               if (ret)
+                       break;
+       }
+
+       return ret;
+}
+
+/* Poll the hardware for up to budget packets to process */
+static unsigned int bcm_sysport_desc_rx(struct bcm_sysport_priv *priv,
+                                       unsigned int budget)
+{
+       struct device *kdev = &priv->pdev->dev;
+       struct net_device *ndev = priv->netdev;
+       unsigned int processed = 0, to_process;
+       struct bcm_sysport_cb *cb;
+       struct sk_buff *skb;
+       unsigned int p_index;
+       u16 len, status;
+       struct bcm_rsb *rsb;
+
+       /* Determine how much we should process since last call */
+       p_index = rdma_readl(priv, RDMA_PROD_INDEX);
+       p_index &= RDMA_PROD_INDEX_MASK;
+
+       if (p_index < priv->rx_c_index)
+               to_process = (RDMA_CONS_INDEX_MASK + 1) -
+                       priv->rx_c_index + p_index;
+       else
+               to_process = p_index - priv->rx_c_index;
+
+       netif_dbg(priv, rx_status, ndev,
+                       "p_index=%d rx_c_index=%d to_process=%d\n",
+                       p_index, priv->rx_c_index, to_process);
+
+       while ((processed < to_process) &&
+               (processed < budget)) {
+
+               cb = &priv->rx_cbs[priv->rx_read_ptr];
+               skb = cb->skb;
+               dma_unmap_single(kdev, dma_unmap_addr(cb, dma_addr),
+                               RX_BUF_LENGTH, DMA_FROM_DEVICE);
+
+               /* Extract the Receive Status Block prepended */
+               rsb = (struct bcm_rsb *)skb->data;
+               len = (rsb->rx_status_len >> DESC_LEN_SHIFT) & DESC_LEN_MASK;
+               status = (rsb->rx_status_len >> DESC_STATUS_SHIFT) &
+                       DESC_STATUS_MASK;
+
+               processed++;
+               priv->rx_read_ptr++;
+               if (priv->rx_read_ptr == priv->num_rx_bds)
+                       priv->rx_read_ptr = 0;
+
+               netif_dbg(priv, rx_status, ndev,
+                               "p=%d, c=%d, rd_ptr=%d, len=%d, flag=0x%04x\n",
+                               p_index, priv->rx_c_index, priv->rx_read_ptr,
+                               len, status);
+
+               if (unlikely(!skb)) {
+                       netif_err(priv, rx_err, ndev, "out of memory!\n");
+                       ndev->stats.rx_dropped++;
+                       ndev->stats.rx_errors++;
+                       goto refill;
+               }
+
+               if (unlikely(!(status & DESC_EOP) || !(status & DESC_SOP))) {
+                       netif_err(priv, rx_status, ndev, "fragmented packet!\n");
+                       ndev->stats.rx_dropped++;
+                       ndev->stats.rx_errors++;
+                       bcm_sysport_free_cb(cb);
+                       goto refill;
+               }
+
+               if (unlikely(status & (RX_STATUS_ERR | RX_STATUS_OVFLOW))) {
+                       netif_err(priv, rx_err, ndev, "error packet\n");
+                       if (status & RX_STATUS_OVFLOW)
+                               ndev->stats.rx_over_errors++;
+                       ndev->stats.rx_dropped++;
+                       ndev->stats.rx_errors++;
+                       bcm_sysport_free_cb(cb);
+                       goto refill;
+               }
+
+               skb_put(skb, len);
+
+               /* Hardware validated our checksum */
+               if (likely(status & DESC_L4_CSUM))
+                       skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+               /* Hardware pre-pends packets with 2bytes before Ethernet
+                * header plus we have the Receive Status Block, strip off all
+                * of this from the SKB.
+                */
+               skb_pull(skb, sizeof(*rsb) + 2);
+               len -= (sizeof(*rsb) + 2);
+
+               /* UniMAC may forward CRC */
+               if (priv->crc_fwd) {
+                       skb_trim(skb, len - ETH_FCS_LEN);
+                       len -= ETH_FCS_LEN;
+               }
+
+               skb->protocol = eth_type_trans(skb, ndev);
+               ndev->stats.rx_packets++;
+               ndev->stats.rx_bytes += len;
+
+               napi_gro_receive(&priv->napi, skb);
+refill:
+               bcm_sysport_rx_refill(priv, cb);
+       }
+
+       return processed;
+}
+
+static void bcm_sysport_tx_reclaim_one(struct bcm_sysport_priv *priv,
+                                       struct bcm_sysport_cb *cb,
+                                       unsigned int *bytes_compl,
+                                       unsigned int *pkts_compl)
+{
+       struct device *kdev = &priv->pdev->dev;
+       struct net_device *ndev = priv->netdev;
+
+       if (cb->skb) {
+               ndev->stats.tx_bytes += cb->skb->len;
+               *bytes_compl += cb->skb->len;
+               dma_unmap_single(kdev, dma_unmap_addr(cb, dma_addr),
+                               dma_unmap_len(cb, dma_len),
+                               DMA_TO_DEVICE);
+               ndev->stats.tx_packets++;
+               (*pkts_compl)++;
+               bcm_sysport_free_cb(cb);
+       /* SKB fragment */
+       } else if (dma_unmap_addr(cb, dma_addr)) {
+               ndev->stats.tx_bytes += dma_unmap_len(cb, dma_len);
+               dma_unmap_page(kdev, dma_unmap_addr(cb, dma_addr),
+                               dma_unmap_len(cb, dma_len), DMA_TO_DEVICE);
+               dma_unmap_addr_set(cb, dma_addr, 0);
+       }
+}
+
+/* Reclaim queued SKBs for transmission completion, lockless version */
+static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv,
+                                            struct bcm_sysport_tx_ring *ring)
+{
+       struct net_device *ndev = priv->netdev;
+       unsigned int c_index, last_c_index, last_tx_cn, num_tx_cbs;
+       unsigned int pkts_compl = 0, bytes_compl = 0;
+       struct bcm_sysport_cb *cb;
+       struct netdev_queue *txq;
+       u32 hw_ind;
+
+       txq = netdev_get_tx_queue(ndev, ring->index);
+
+       /* Compute how many descriptors have been processed since last call */
+       hw_ind = tdma_readl(priv, TDMA_DESC_RING_PROD_CONS_INDEX(ring->index));
+       c_index = (hw_ind >> RING_CONS_INDEX_SHIFT) & RING_CONS_INDEX_MASK;
+       ring->p_index = (hw_ind & RING_PROD_INDEX_MASK);
+
+       last_c_index = ring->c_index;
+       num_tx_cbs = ring->size;
+
+       c_index &= (num_tx_cbs - 1);
+
+       if (c_index >= last_c_index)
+               last_tx_cn = c_index - last_c_index;
+       else
+               last_tx_cn = num_tx_cbs - last_c_index + c_index;
+
+       netif_dbg(priv, tx_done, ndev,
+                       "ring=%d c_index=%d last_tx_cn=%d last_c_index=%d\n",
+                       ring->index, c_index, last_tx_cn, last_c_index);
+
+       while (last_tx_cn-- > 0) {
+               cb = ring->cbs + last_c_index;
+               bcm_sysport_tx_reclaim_one(priv, cb, &bytes_compl, &pkts_compl);
+
+               ring->desc_count++;
+               last_c_index++;
+               last_c_index &= (num_tx_cbs - 1);
+       }
+
+       ring->c_index = c_index;
+
+       if (netif_tx_queue_stopped(txq) && pkts_compl)
+               netif_tx_wake_queue(txq);
+
+       netif_dbg(priv, tx_done, ndev,
+                       "ring=%d c_index=%d pkts_compl=%d, bytes_compl=%d\n",
+                       ring->index, ring->c_index, pkts_compl, bytes_compl);
+
+       return pkts_compl;
+}
+
+/* Locked version of the per-ring TX reclaim routine */
+static unsigned int bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv,
+                                          struct bcm_sysport_tx_ring *ring)
+{
+       unsigned int released;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ring->lock, flags);
+       released = __bcm_sysport_tx_reclaim(priv, ring);
+       spin_unlock_irqrestore(&ring->lock, flags);
+
+       return released;
+}
+
+static int bcm_sysport_tx_poll(struct napi_struct *napi, int budget)
+{
+       struct bcm_sysport_tx_ring *ring =
+               container_of(napi, struct bcm_sysport_tx_ring, napi);
+       unsigned int work_done = 0;
+
+       work_done = bcm_sysport_tx_reclaim(ring->priv, ring);
+
+       if (work_done < budget) {
+               napi_complete(napi);
+               /* re-enable TX interrupt */
+               intrl2_1_mask_clear(ring->priv, BIT(ring->index));
+       }
+
+       return work_done;
+}
+
+static void bcm_sysport_tx_reclaim_all(struct bcm_sysport_priv *priv)
+{
+       unsigned int q;
+
+       for (q = 0; q < priv->netdev->num_tx_queues; q++)
+               bcm_sysport_tx_reclaim(priv, &priv->tx_rings[q]);
+}
+
+static int bcm_sysport_poll(struct napi_struct *napi, int budget)
+{
+       struct bcm_sysport_priv *priv =
+               container_of(napi, struct bcm_sysport_priv, napi);
+       unsigned int work_done = 0;
+
+       work_done = bcm_sysport_desc_rx(priv, budget);
+
+       priv->rx_c_index += work_done;
+       priv->rx_c_index &= RDMA_CONS_INDEX_MASK;
+       rdma_writel(priv, priv->rx_c_index, RDMA_CONS_INDEX);
+
+       if (work_done < budget) {
+               napi_complete(napi);
+               /* re-enable RX interrupts */
+               intrl2_0_mask_clear(priv, INTRL2_0_RDMA_MBDONE);
+       }
+
+       return work_done;
+}
+
+
+/* RX and misc interrupt routine */
+static irqreturn_t bcm_sysport_rx_isr(int irq, void *dev_id)
+{
+       struct net_device *dev = dev_id;
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+
+       priv->irq0_stat = intrl2_0_readl(priv, INTRL2_CPU_STATUS) &
+                         ~intrl2_0_readl(priv, INTRL2_CPU_MASK_STATUS);
+       intrl2_0_writel(priv, priv->irq0_stat, INTRL2_CPU_CLEAR);
+
+       if (unlikely(priv->irq0_stat == 0)) {
+               netdev_warn(priv->netdev, "spurious RX interrupt\n");
+               return IRQ_NONE;
+       }
+
+       if (priv->irq0_stat & INTRL2_0_RDMA_MBDONE) {
+               if (likely(napi_schedule_prep(&priv->napi))) {
+                       /* disable RX interrupts */
+                       intrl2_0_mask_set(priv, INTRL2_0_RDMA_MBDONE);
+                       __napi_schedule(&priv->napi);
+               }
+       }
+
+       /* TX ring is full, perform a full reclaim since we do not know
+        * which one would trigger this interrupt
+        */
+       if (priv->irq0_stat & INTRL2_0_TX_RING_FULL)
+               bcm_sysport_tx_reclaim_all(priv);
+
+       return IRQ_HANDLED;
+}
+
+/* TX interrupt service routine */
+static irqreturn_t bcm_sysport_tx_isr(int irq, void *dev_id)
+{
+       struct net_device *dev = dev_id;
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+       struct bcm_sysport_tx_ring *txr;
+       unsigned int ring;
+
+       priv->irq1_stat = intrl2_1_readl(priv, INTRL2_CPU_STATUS) &
+                               ~intrl2_1_readl(priv, INTRL2_CPU_MASK_STATUS);
+       intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR);
+
+       if (unlikely(priv->irq1_stat == 0)) {
+               netdev_warn(priv->netdev, "spurious TX interrupt\n");
+               return IRQ_NONE;
+       }
+
+       for (ring = 0; ring < dev->num_tx_queues; ring++) {
+               if (!(priv->irq1_stat & BIT(ring)))
+                       continue;
+
+               txr = &priv->tx_rings[ring];
+
+               if (likely(napi_schedule_prep(&txr->napi))) {
+                       intrl2_1_mask_set(priv, BIT(ring));
+                       __napi_schedule(&txr->napi);
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int bcm_sysport_insert_tsb(struct sk_buff *skb, struct net_device *dev)
+{
+       struct sk_buff *nskb;
+       struct bcm_tsb *tsb;
+       u32 csum_info;
+       u8 ip_proto;
+       u16 csum_start;
+       u16 ip_ver;
+
+       /* Re-allocate SKB if needed */
+       if (unlikely(skb_headroom(skb) < sizeof(*tsb))) {
+               nskb = skb_realloc_headroom(skb, sizeof(*tsb));
+               dev_kfree_skb(skb);
+               if (!nskb) {
+                       dev->stats.tx_errors++;
+                       dev->stats.tx_dropped++;
+                       return -ENOMEM;
+               }
+               skb = nskb;
+       }
+
+       tsb = (struct bcm_tsb *)skb_push(skb, sizeof(*tsb));
+       /* Zero-out TSB by default */
+       memset(tsb, 0, sizeof(*tsb));
+
+       if (skb->ip_summed == CHECKSUM_PARTIAL) {
+               ip_ver = htons(skb->protocol);
+               switch (ip_ver) {
+               case ETH_P_IP:
+                       ip_proto = ip_hdr(skb)->protocol;
+                       break;
+               case ETH_P_IPV6:
+                       ip_proto = ipv6_hdr(skb)->nexthdr;
+                       break;
+               default:
+                       return 0;
+               }
+
+               /* Get the checksum offset and the L4 (transport) offset */
+               csum_start = skb_checksum_start_offset(skb) - sizeof(*tsb);
+               csum_info = (csum_start + skb->csum_offset) & L4_CSUM_PTR_MASK;
+               csum_info |= (csum_start << L4_PTR_SHIFT);
+
+               if (ip_proto == IPPROTO_TCP || ip_proto == IPPROTO_UDP) {
+                       csum_info |= L4_LENGTH_VALID;
+                       if (ip_proto == IPPROTO_UDP && ip_ver == ETH_P_IP)
+                               csum_info |= L4_UDP;
+               } else
+                       csum_info = 0;
+
+               tsb->l4_ptr_dest_map = csum_info;
+       }
+
+       return 0;
+}
+
+static netdev_tx_t bcm_sysport_xmit(struct sk_buff *skb,
+                                   struct net_device *dev)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+       struct device *kdev = &priv->pdev->dev;
+       struct bcm_sysport_tx_ring *ring;
+       struct bcm_sysport_cb *cb;
+       struct netdev_queue *txq;
+       struct dma_desc *desc;
+       unsigned int skb_len;
+       unsigned long flags;
+       dma_addr_t mapping;
+       u32 len_status;
+       u16 queue;
+       int ret;
+
+       queue = skb_get_queue_mapping(skb);
+       txq = netdev_get_tx_queue(dev, queue);
+       ring = &priv->tx_rings[queue];
+
+       /* lock against tx reclaim in BH context and TX ring full interrupt */
+       spin_lock_irqsave(&ring->lock, flags);
+       if (unlikely(ring->desc_count == 0)) {
+               netif_tx_stop_queue(txq);
+               netdev_err(dev, "queue %d awake and ring full!\n", queue);
+               ret = NETDEV_TX_BUSY;
+               goto out;
+       }
+
+       /* Insert TSB and checksum infos */
+       if (priv->tsb_en) {
+               ret = bcm_sysport_insert_tsb(skb, dev);
+               if (ret) {
+                       ret = NETDEV_TX_OK;
+                       goto out;
+               }
+       }
+
+       /* The Ethernet switch we are interfaced with needs packets to be at
+        * least 64 bytes (including FCS) otherwise they will be discarded when
+        * they enter the switch port logic. When Broadcom tags are enabled, we
+        * need to make sure that packets are at least 68 bytes
+        * (including FCS and tag) because the length verification is done after
+        * the Broadcom tag is stripped off the ingress packet.
+        */
+       if (skb_padto(skb, ETH_ZLEN + ENET_BRCM_TAG_LEN)) {
+               ret = NETDEV_TX_OK;
+               goto out;
+       }
+
+       skb_len = skb->len < ETH_ZLEN + ENET_BRCM_TAG_LEN ?
+                       ETH_ZLEN + ENET_BRCM_TAG_LEN : skb->len;
+
+       mapping = dma_map_single(kdev, skb->data, skb_len, DMA_TO_DEVICE);
+       if (dma_mapping_error(kdev, mapping)) {
+               netif_err(priv, tx_err, dev, "DMA map failed at %p (len=%d)\n",
+                               skb->data, skb_len);
+               ret = NETDEV_TX_OK;
+               goto out;
+       }
+
+       /* Remember the SKB for future freeing */
+       cb = &ring->cbs[ring->curr_desc];
+       cb->skb = skb;
+       dma_unmap_addr_set(cb, dma_addr, mapping);
+       dma_unmap_len_set(cb, dma_len, skb_len);
+
+       /* Fetch a descriptor entry from our pool */
+       desc = ring->desc_cpu;
+
+       desc->addr_lo = lower_32_bits(mapping);
+       len_status = upper_32_bits(mapping) & DESC_ADDR_HI_MASK;
+       len_status |= (skb_len << DESC_LEN_SHIFT);
+       len_status |= (DESC_SOP | DESC_EOP | TX_STATUS_APP_CRC) <<
+                       DESC_STATUS_SHIFT;
+       if (skb->ip_summed == CHECKSUM_PARTIAL)
+               len_status |= (DESC_L4_CSUM << DESC_STATUS_SHIFT);
+
+       ring->curr_desc++;
+       if (ring->curr_desc == ring->size)
+               ring->curr_desc = 0;
+       ring->desc_count--;
+
+       /* Ensure write completion of the descriptor status/length
+        * in DRAM before the System Port WRITE_PORT register latches
+        * the value
+        */
+       wmb();
+       desc->addr_status_len = len_status;
+       wmb();
+
+       /* Write this descriptor address to the RING write port */
+       tdma_port_write_desc_addr(priv, desc, ring->index);
+
+       /* Check ring space and update SW control flow */
+       if (ring->desc_count == 0)
+               netif_tx_stop_queue(txq);
+
+       netif_dbg(priv, tx_queued, dev, "ring=%d desc_count=%d, curr_desc=%d\n",
+                       ring->index, ring->desc_count, ring->curr_desc);
+
+       ret = NETDEV_TX_OK;
+out:
+       spin_unlock_irqrestore(&ring->lock, flags);
+       return ret;
+}
+
+static void bcm_sysport_tx_timeout(struct net_device *dev)
+{
+       netdev_warn(dev, "transmit timeout!\n");
+
+       dev->trans_start = jiffies;
+       dev->stats.tx_errors++;
+
+       netif_tx_wake_all_queues(dev);
+}
+
+/* phylib adjust link callback */
+static void bcm_sysport_adj_link(struct net_device *dev)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+       struct phy_device *phydev = priv->phydev;
+       unsigned int changed = 0;
+       u32 cmd_bits = 0, reg;
+
+       if (priv->old_link != phydev->link) {
+               changed = 1;
+               priv->old_link = phydev->link;
+       }
+
+       if (priv->old_duplex != phydev->duplex) {
+               changed = 1;
+               priv->old_duplex = phydev->duplex;
+       }
+
+       switch (phydev->speed) {
+       case SPEED_2500:
+               cmd_bits = CMD_SPEED_2500;
+               break;
+       case SPEED_1000:
+               cmd_bits = CMD_SPEED_1000;
+               break;
+       case SPEED_100:
+               cmd_bits = CMD_SPEED_100;
+               break;
+       case SPEED_10:
+               cmd_bits = CMD_SPEED_10;
+               break;
+       default:
+               break;
+       }
+       cmd_bits <<= CMD_SPEED_SHIFT;
+
+       if (phydev->duplex == DUPLEX_HALF)
+               cmd_bits |= CMD_HD_EN;
+
+       if (priv->old_pause != phydev->pause) {
+               changed = 1;
+               priv->old_pause = phydev->pause;
+       }
+
+       if (!phydev->pause)
+               cmd_bits |= CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE;
+
+       if (changed) {
+               reg = umac_readl(priv, UMAC_CMD);
+               reg &= ~((CMD_SPEED_MASK << CMD_SPEED_SHIFT) |
+                       CMD_HD_EN | CMD_RX_PAUSE_IGNORE |
+                       CMD_TX_PAUSE_IGNORE);
+               reg |= cmd_bits;
+               umac_writel(priv, reg, UMAC_CMD);
+
+               phy_print_status(priv->phydev);
+       }
+}
+
+static int bcm_sysport_init_tx_ring(struct bcm_sysport_priv *priv,
+                                   unsigned int index)
+{
+       struct bcm_sysport_tx_ring *ring = &priv->tx_rings[index];
+       struct device *kdev = &priv->pdev->dev;
+       size_t size;
+       void *p;
+       u32 reg;
+
+       /* Simple descriptors partitioning for now */
+       size = 256;
+
+       /* We just need one DMA descriptor which is DMA-able, since writing to
+        * the port will allocate a new descriptor in its internal linked-list
+        */
+       p = dma_zalloc_coherent(kdev, 1, &ring->desc_dma, GFP_KERNEL);
+       if (!p) {
+               netif_err(priv, hw, priv->netdev, "DMA alloc failed\n");
+               return -ENOMEM;
+       }
+
+       ring->cbs = kzalloc(sizeof(struct bcm_sysport_cb) * size, GFP_KERNEL);
+       if (!ring->cbs) {
+               netif_err(priv, hw, priv->netdev, "CB allocation failed\n");
+               return -ENOMEM;
+       }
+
+       /* Initialize SW view of the ring */
+       spin_lock_init(&ring->lock);
+       ring->priv = priv;
+       netif_napi_add(priv->netdev, &ring->napi, bcm_sysport_tx_poll, 64);
+       ring->index = index;
+       ring->size = size;
+       ring->alloc_size = ring->size;
+       ring->desc_cpu = p;
+       ring->desc_count = ring->size;
+       ring->curr_desc = 0;
+
+       /* Initialize HW ring */
+       tdma_writel(priv, RING_EN, TDMA_DESC_RING_HEAD_TAIL_PTR(index));
+       tdma_writel(priv, 0, TDMA_DESC_RING_COUNT(index));
+       tdma_writel(priv, 1, TDMA_DESC_RING_INTR_CONTROL(index));
+       tdma_writel(priv, 0, TDMA_DESC_RING_PROD_CONS_INDEX(index));
+       tdma_writel(priv, RING_IGNORE_STATUS, TDMA_DESC_RING_MAPPING(index));
+       tdma_writel(priv, 0, TDMA_DESC_RING_PCP_DEI_VID(index));
+
+       /* Program the number of descriptors as MAX_THRESHOLD and half of
+        * its size for the hysteresis trigger
+        */
+       tdma_writel(priv, ring->size |
+                       1 << RING_HYST_THRESH_SHIFT,
+                       TDMA_DESC_RING_MAX_HYST(index));
+
+       /* Enable the ring queue in the arbiter */
+       reg = tdma_readl(priv, TDMA_TIER1_ARB_0_QUEUE_EN);
+       reg |= (1 << index);
+       tdma_writel(priv, reg, TDMA_TIER1_ARB_0_QUEUE_EN);
+
+       napi_enable(&ring->napi);
+
+       netif_dbg(priv, hw, priv->netdev,
+                       "TDMA cfg, size=%d, desc_cpu=%p\n",
+                       ring->size, ring->desc_cpu);
+
+       return 0;
+}
+
+static void bcm_sysport_fini_tx_ring(struct bcm_sysport_priv *priv,
+                                       unsigned int index)
+{
+       struct bcm_sysport_tx_ring *ring = &priv->tx_rings[index];
+       struct device *kdev = &priv->pdev->dev;
+       u32 reg;
+
+       /* Caller should stop the TDMA engine */
+       reg = tdma_readl(priv, TDMA_STATUS);
+       if (!(reg & TDMA_DISABLED))
+               netdev_warn(priv->netdev, "TDMA not stopped!\n");
+
+       napi_disable(&ring->napi);
+       netif_napi_del(&ring->napi);
+
+       bcm_sysport_tx_reclaim(priv, ring);
+
+       kfree(ring->cbs);
+       ring->cbs = NULL;
+
+       if (ring->desc_dma) {
+               dma_free_coherent(kdev, 1, ring->desc_cpu, ring->desc_dma);
+               ring->desc_dma = 0;
+       }
+       ring->size = 0;
+       ring->alloc_size = 0;
+
+       netif_dbg(priv, hw, priv->netdev, "TDMA fini done\n");
+}
+
+/* RDMA helper */
+static inline int rdma_enable_set(struct bcm_sysport_priv *priv,
+                                       unsigned int enable)
+{
+       unsigned int timeout = 1000;
+       u32 reg;
+
+       reg = rdma_readl(priv, RDMA_CONTROL);
+       if (enable)
+               reg |= RDMA_EN;
+       else
+               reg &= ~RDMA_EN;
+       rdma_writel(priv, reg, RDMA_CONTROL);
+
+       /* Poll for RMDA disabling completion */
+       do {
+               reg = rdma_readl(priv, RDMA_STATUS);
+               if (!!(reg & RDMA_DISABLED) == !enable)
+                       return 0;
+               usleep_range(1000, 2000);
+       } while (timeout-- > 0);
+
+       netdev_err(priv->netdev, "timeout waiting for RDMA to finish\n");
+
+       return -ETIMEDOUT;
+}
+
+/* TDMA helper */
+static inline int tdma_enable_set(struct bcm_sysport_priv *priv,
+                                       unsigned int enable)
+{
+       unsigned int timeout = 1000;
+       u32 reg;
+
+       reg = tdma_readl(priv, TDMA_CONTROL);
+       if (enable)
+               reg |= TDMA_EN;
+       else
+               reg &= ~TDMA_EN;
+       tdma_writel(priv, reg, TDMA_CONTROL);
+
+       /* Poll for TMDA disabling completion */
+       do {
+               reg = tdma_readl(priv, TDMA_STATUS);
+               if (!!(reg & TDMA_DISABLED) == !enable)
+                       return 0;
+
+               usleep_range(1000, 2000);
+       } while (timeout-- > 0);
+
+       netdev_err(priv->netdev, "timeout waiting for TDMA to finish\n");
+
+       return -ETIMEDOUT;
+}
+
+static int bcm_sysport_init_rx_ring(struct bcm_sysport_priv *priv)
+{
+       u32 reg;
+       int ret;
+
+       /* Initialize SW view of the RX ring */
+       priv->num_rx_bds = NUM_RX_DESC;
+       priv->rx_bds = priv->base + SYS_PORT_RDMA_OFFSET;
+       priv->rx_bd_assign_ptr = priv->rx_bds;
+       priv->rx_bd_assign_index = 0;
+       priv->rx_c_index = 0;
+       priv->rx_read_ptr = 0;
+       priv->rx_cbs = kzalloc(priv->num_rx_bds *
+                               sizeof(struct bcm_sysport_cb), GFP_KERNEL);
+       if (!priv->rx_cbs) {
+               netif_err(priv, hw, priv->netdev, "CB allocation failed\n");
+               return -ENOMEM;
+       }
+
+       ret = bcm_sysport_alloc_rx_bufs(priv);
+       if (ret) {
+               netif_err(priv, hw, priv->netdev, "SKB allocation failed\n");
+               return ret;
+       }
+
+       /* Initialize HW, ensure RDMA is disabled */
+       reg = rdma_readl(priv, RDMA_STATUS);
+       if (!(reg & RDMA_DISABLED))
+               rdma_enable_set(priv, 0);
+
+       rdma_writel(priv, 0, RDMA_WRITE_PTR_LO);
+       rdma_writel(priv, 0, RDMA_WRITE_PTR_HI);
+       rdma_writel(priv, 0, RDMA_PROD_INDEX);
+       rdma_writel(priv, 0, RDMA_CONS_INDEX);
+       rdma_writel(priv, priv->num_rx_bds << RDMA_RING_SIZE_SHIFT |
+                         RX_BUF_LENGTH, RDMA_RING_BUF_SIZE);
+       /* Operate the queue in ring mode */
+       rdma_writel(priv, 0, RDMA_START_ADDR_HI);
+       rdma_writel(priv, 0, RDMA_START_ADDR_LO);
+       rdma_writel(priv, 0, RDMA_END_ADDR_HI);
+       rdma_writel(priv, NUM_HW_RX_DESC_WORDS - 1, RDMA_END_ADDR_LO);
+
+       rdma_writel(priv, 1, RDMA_MBDONE_INTR);
+
+       netif_dbg(priv, hw, priv->netdev,
+                       "RDMA cfg, num_rx_bds=%d, rx_bds=%p\n",
+                       priv->num_rx_bds, priv->rx_bds);
+
+       return 0;
+}
+
+static void bcm_sysport_fini_rx_ring(struct bcm_sysport_priv *priv)
+{
+       struct bcm_sysport_cb *cb;
+       unsigned int i;
+       u32 reg;
+
+       /* Caller should ensure RDMA is disabled */
+       reg = rdma_readl(priv, RDMA_STATUS);
+       if (!(reg & RDMA_DISABLED))
+               netdev_warn(priv->netdev, "RDMA not stopped!\n");
+
+       for (i = 0; i < priv->num_rx_bds; i++) {
+               cb = &priv->rx_cbs[i];
+               if (dma_unmap_addr(cb, dma_addr))
+                       dma_unmap_single(&priv->pdev->dev,
+                                       dma_unmap_addr(cb, dma_addr),
+                                       RX_BUF_LENGTH, DMA_FROM_DEVICE);
+               bcm_sysport_free_cb(cb);
+       }
+
+       kfree(priv->rx_cbs);
+       priv->rx_cbs = NULL;
+
+       netif_dbg(priv, hw, priv->netdev, "RDMA fini done\n");
+}
+
+static void bcm_sysport_set_rx_mode(struct net_device *dev)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+       u32 reg;
+
+       reg = umac_readl(priv, UMAC_CMD);
+       if (dev->flags & IFF_PROMISC)
+               reg |= CMD_PROMISC;
+       else
+               reg &= ~CMD_PROMISC;
+       umac_writel(priv, reg, UMAC_CMD);
+
+       /* No support for ALLMULTI */
+       if (dev->flags & IFF_ALLMULTI)
+               return;
+}
+
+static inline void umac_enable_set(struct bcm_sysport_priv *priv,
+                                       unsigned int enable)
+{
+       u32 reg;
+
+       reg = umac_readl(priv, UMAC_CMD);
+       if (enable)
+               reg |= CMD_RX_EN | CMD_TX_EN;
+       else
+               reg &= ~(CMD_RX_EN | CMD_TX_EN);
+       umac_writel(priv, reg, UMAC_CMD);
+
+       /* UniMAC stops on a packet boundary, wait for a full-sized packet
+        * to be processed (1 msec).
+        */
+       if (enable == 0)
+               usleep_range(1000, 2000);
+}
+
+static inline int umac_reset(struct bcm_sysport_priv *priv)
+{
+       unsigned int timeout = 0;
+       u32 reg;
+       int ret = 0;
+
+       umac_writel(priv, 0, UMAC_CMD);
+       while (timeout++ < 1000) {
+               reg = umac_readl(priv, UMAC_CMD);
+               if (!(reg & CMD_SW_RESET))
+                       break;
+
+               udelay(1);
+       }
+
+       if (timeout == 1000) {
+               dev_err(&priv->pdev->dev,
+                       "timeout waiting for MAC to come out of reset\n");
+               ret = -ETIMEDOUT;
+       }
+
+       return ret;
+}
+
+static void umac_set_hw_addr(struct bcm_sysport_priv *priv,
+                               unsigned char *addr)
+{
+       umac_writel(priv, (addr[0] << 24) | (addr[1] << 16) |
+                       (addr[2] << 8) | addr[3], UMAC_MAC0);
+       umac_writel(priv, (addr[4] << 8) | addr[5], UMAC_MAC1);
+}
+
+static void topctrl_flush(struct bcm_sysport_priv *priv)
+{
+       topctrl_writel(priv, RX_FLUSH, RX_FLUSH_CNTL);
+       topctrl_writel(priv, TX_FLUSH, TX_FLUSH_CNTL);
+       mdelay(1);
+       topctrl_writel(priv, 0, RX_FLUSH_CNTL);
+       topctrl_writel(priv, 0, TX_FLUSH_CNTL);
+}
+
+static int bcm_sysport_open(struct net_device *dev)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+       unsigned int i;
+       u32 reg;
+       int ret;
+
+       /* Reset UniMAC */
+       ret = umac_reset(priv);
+       if (ret) {
+               netdev_err(dev, "UniMAC reset failed\n");
+               return ret;
+       }
+
+       /* Flush TX and RX FIFOs at TOPCTRL level */
+       topctrl_flush(priv);
+
+       /* Disable the UniMAC RX/TX */
+       umac_enable_set(priv, 0);
+
+       /* Enable RBUF 2bytes alignment and Receive Status Block */
+       reg = rbuf_readl(priv, RBUF_CONTROL);
+       reg |= RBUF_4B_ALGN | RBUF_RSB_EN;
+       rbuf_writel(priv, reg, RBUF_CONTROL);
+
+       /* Set maximum frame length */
+       umac_writel(priv, UMAC_MAX_MTU_SIZE, UMAC_MAX_FRAME_LEN);
+
+       /* Set MAC address */
+       umac_set_hw_addr(priv, dev->dev_addr);
+
+       /* Read CRC forward */
+       priv->crc_fwd = !!(umac_readl(priv, UMAC_CMD) & CMD_CRC_FWD);
+
+       priv->phydev = of_phy_connect(dev, priv->phy_dn, bcm_sysport_adj_link,
+                                       0, priv->phy_interface);
+       if (!priv->phydev) {
+               netdev_err(dev, "could not attach to PHY\n");
+               return -ENODEV;
+       }
+
+       /* Reset house keeping link status */
+       priv->old_duplex = -1;
+       priv->old_link = -1;
+       priv->old_pause = -1;
+
+       /* mask all interrupts and request them */
+       intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET);
+       intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR);
+       intrl2_0_writel(priv, 0, INTRL2_CPU_MASK_CLEAR);
+       intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET);
+       intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR);
+       intrl2_1_writel(priv, 0, INTRL2_CPU_MASK_CLEAR);
+
+       ret = request_irq(priv->irq0, bcm_sysport_rx_isr, 0, dev->name, dev);
+       if (ret) {
+               netdev_err(dev, "failed to request RX interrupt\n");
+               goto out_phy_disconnect;
+       }
+
+       ret = request_irq(priv->irq1, bcm_sysport_tx_isr, 0, dev->name, dev);
+       if (ret) {
+               netdev_err(dev, "failed to request TX interrupt\n");
+               goto out_free_irq0;
+       }
+
+       /* Initialize both hardware and software ring */
+       for (i = 0; i < dev->num_tx_queues; i++) {
+               ret = bcm_sysport_init_tx_ring(priv, i);
+               if (ret) {
+                       netdev_err(dev, "failed to initialize TX ring %d\n",
+                                       i);
+                       goto out_free_tx_ring;
+               }
+       }
+
+       /* Initialize linked-list */
+       tdma_writel(priv, TDMA_LL_RAM_INIT_BUSY, TDMA_STATUS);
+
+       /* Initialize RX ring */
+       ret = bcm_sysport_init_rx_ring(priv);
+       if (ret) {
+               netdev_err(dev, "failed to initialize RX ring\n");
+               goto out_free_rx_ring;
+       }
+
+       /* Turn on RDMA */
+       ret = rdma_enable_set(priv, 1);
+       if (ret)
+               goto out_free_rx_ring;
+
+       /* Enable RX interrupt and TX ring full interrupt */
+       intrl2_0_mask_clear(priv, INTRL2_0_RDMA_MBDONE | INTRL2_0_TX_RING_FULL);
+
+       /* Turn on TDMA */
+       ret = tdma_enable_set(priv, 1);
+       if (ret)
+               goto out_clear_rx_int;
+
+       /* Enable NAPI */
+       napi_enable(&priv->napi);
+
+       /* Turn on UniMAC TX/RX */
+       umac_enable_set(priv, 1);
+
+       phy_start(priv->phydev);
+
+       /* Enable TX interrupts for the 32 TXQs */
+       intrl2_1_mask_clear(priv, 0xffffffff);
+
+       /* Last call before we start the real business */
+       netif_tx_start_all_queues(dev);
+
+       return 0;
+
+out_clear_rx_int:
+       intrl2_0_mask_set(priv, INTRL2_0_RDMA_MBDONE | INTRL2_0_TX_RING_FULL);
+out_free_rx_ring:
+       bcm_sysport_fini_rx_ring(priv);
+out_free_tx_ring:
+       for (i = 0; i < dev->num_tx_queues; i++)
+               bcm_sysport_fini_tx_ring(priv, i);
+       free_irq(priv->irq1, dev);
+out_free_irq0:
+       free_irq(priv->irq0, dev);
+out_phy_disconnect:
+       phy_disconnect(priv->phydev);
+       return ret;
+}
+
+static int bcm_sysport_stop(struct net_device *dev)
+{
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
+       unsigned int i;
+       u32 reg;
+       int ret;
+
+       /* stop all software from updating hardware */
+       netif_tx_stop_all_queues(dev);
+       napi_disable(&priv->napi);
+       phy_stop(priv->phydev);
+
+       /* mask all interrupts */
+       intrl2_0_mask_set(priv, 0xffffffff);
+       intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR);
+       intrl2_1_mask_set(priv, 0xffffffff);
+       intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR);
+
+       /* Disable UniMAC RX */
+       reg = umac_readl(priv, UMAC_CMD);
+       reg &= ~CMD_RX_EN;
+       umac_writel(priv, reg, UMAC_CMD);
+
+       ret = tdma_enable_set(priv, 0);
+       if (ret) {
+               netdev_err(dev, "timeout disabling RDMA\n");
+               return ret;
+       }
+
+       /* Wait for a maximum packet size to be drained */
+       usleep_range(2000, 3000);
+
+       ret = rdma_enable_set(priv, 0);
+       if (ret) {
+               netdev_err(dev, "timeout disabling TDMA\n");
+               return ret;
+       }
+
+       /* Disable UniMAC TX */
+       reg = umac_readl(priv, UMAC_CMD);
+       reg &= ~CMD_TX_EN;
+       umac_writel(priv, reg, UMAC_CMD);
+
+       /* Free RX/TX rings SW structures */
+       for (i = 0; i < dev->num_tx_queues; i++)
+               bcm_sysport_fini_tx_ring(priv, i);
+       bcm_sysport_fini_rx_ring(priv);
+
+       free_irq(priv->irq0, dev);
+       free_irq(priv->irq1, dev);
+
+       /* Disconnect from PHY */
+       phy_disconnect(priv->phydev);
+
+       return 0;
+}
+
+static struct ethtool_ops bcm_sysport_ethtool_ops = {
+       .get_settings           = bcm_sysport_get_settings,
+       .set_settings           = bcm_sysport_set_settings,
+       .get_drvinfo            = bcm_sysport_get_drvinfo,
+       .get_msglevel           = bcm_sysport_get_msglvl,
+       .set_msglevel           = bcm_sysport_set_msglvl,
+       .get_link               = ethtool_op_get_link,
+       .get_strings            = bcm_sysport_get_strings,
+       .get_ethtool_stats      = bcm_sysport_get_stats,
+       .get_sset_count         = bcm_sysport_get_sset_count,
+};
+
+static const struct net_device_ops bcm_sysport_netdev_ops = {
+       .ndo_start_xmit         = bcm_sysport_xmit,
+       .ndo_tx_timeout         = bcm_sysport_tx_timeout,
+       .ndo_open               = bcm_sysport_open,
+       .ndo_stop               = bcm_sysport_stop,
+       .ndo_set_features       = bcm_sysport_set_features,
+       .ndo_set_rx_mode        = bcm_sysport_set_rx_mode,
+};
+
+#define REV_FMT        "v%2x.%02x"
+
+static int bcm_sysport_probe(struct platform_device *pdev)
+{
+       struct bcm_sysport_priv *priv;
+       struct device_node *dn;
+       struct net_device *dev;
+       const void *macaddr;
+       struct resource *r;
+       u32 txq, rxq;
+       int ret;
+
+       dn = pdev->dev.of_node;
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       /* Read the Transmit/Receive Queue properties */
+       if (of_property_read_u32(dn, "systemport,num-txq", &txq))
+               txq = TDMA_NUM_RINGS;
+       if (of_property_read_u32(dn, "systemport,num-rxq", &rxq))
+               rxq = 1;
+
+       dev = alloc_etherdev_mqs(sizeof(*priv), txq, rxq);
+       if (!dev)
+               return -ENOMEM;
+
+       /* Initialize private members */
+       priv = netdev_priv(dev);
+
+       priv->irq0 = platform_get_irq(pdev, 0);
+       priv->irq1 = platform_get_irq(pdev, 1);
+       if (priv->irq0 <= 0 || priv->irq1 <= 0) {
+               dev_err(&pdev->dev, "invalid interrupts\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       priv->base = devm_ioremap_resource(&pdev->dev, r);
+       if (IS_ERR(priv->base)) {
+               ret = PTR_ERR(priv->base);
+               goto err;
+       }
+
+       priv->netdev = dev;
+       priv->pdev = pdev;
+
+       priv->phy_interface = of_get_phy_mode(dn);
+       /* Default to GMII interface mode */
+       if (priv->phy_interface < 0)
+               priv->phy_interface = PHY_INTERFACE_MODE_GMII;
+
+       /* In the case of a fixed PHY, the DT node associated
+        * to the PHY is the Ethernet MAC DT node.
+        */
+       if (of_phy_is_fixed_link(dn)) {
+               ret = of_phy_register_fixed_link(dn);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to register fixed PHY\n");
+                       goto err;
+               }
+
+               priv->phy_dn = dn;
+       }
+
+       /* Initialize netdevice members */
+       macaddr = of_get_mac_address(dn);
+       if (!macaddr || !is_valid_ether_addr(macaddr)) {
+               dev_warn(&pdev->dev, "using random Ethernet MAC\n");
+               random_ether_addr(dev->dev_addr);
+       } else {
+               ether_addr_copy(dev->dev_addr, macaddr);
+       }
+
+       SET_NETDEV_DEV(dev, &pdev->dev);
+       dev_set_drvdata(&pdev->dev, dev);
+       dev->ethtool_ops = &bcm_sysport_ethtool_ops;
+       dev->netdev_ops = &bcm_sysport_netdev_ops;
+       netif_napi_add(dev, &priv->napi, bcm_sysport_poll, 64);
+
+       /* HW supported features, none enabled by default */
+       dev->hw_features |= NETIF_F_RXCSUM | NETIF_F_HIGHDMA |
+                               NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
+
+       /* Set the needed headroom once and for all */
+       BUILD_BUG_ON(sizeof(struct bcm_tsb) != 8);
+       dev->needed_headroom += sizeof(struct bcm_tsb);
+
+       /* We are interfaced to a switch which handles the multicast
+        * filtering for us, so we do not support programming any
+        * multicast hash table in this Ethernet MAC.
+        */
+       dev->flags &= ~IFF_MULTICAST;
+
+       /* libphy will adjust the link state accordingly */
+       netif_carrier_off(dev);
+
+       ret = register_netdev(dev);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to register net_device\n");
+               goto err;
+       }
+
+       priv->rev = topctrl_readl(priv, REV_CNTL) & REV_MASK;
+       dev_info(&pdev->dev,
+               "Broadcom SYSTEMPORT" REV_FMT
+               " at 0x%p (irqs: %d, %d, TXQs: %d, RXQs: %d)\n",
+               (priv->rev >> 8) & 0xff, priv->rev & 0xff,
+               priv->base, priv->irq0, priv->irq1, txq, rxq);
+
+       return 0;
+err:
+       free_netdev(dev);
+       return ret;
+}
+
+static int bcm_sysport_remove(struct platform_device *pdev)
+{
+       struct net_device *dev = dev_get_drvdata(&pdev->dev);
+
+       /* Not much to do, ndo_close has been called
+        * and we use managed allocations
+        */
+       unregister_netdev(dev);
+       free_netdev(dev);
+       dev_set_drvdata(&pdev->dev, NULL);
+
+       return 0;
+}
+
+static const struct of_device_id bcm_sysport_of_match[] = {
+       { .compatible = "brcm,systemport-v1.00" },
+       { .compatible = "brcm,systemport" },
+       { /* sentinel */ }
+};
+
+static struct platform_driver bcm_sysport_driver = {
+       .probe  = bcm_sysport_probe,
+       .remove = bcm_sysport_remove,
+       .driver =  {
+               .name = "brcm-systemport",
+               .owner = THIS_MODULE,
+               .of_match_table = bcm_sysport_of_match,
+       },
+};
+module_platform_driver(bcm_sysport_driver);
+
+MODULE_AUTHOR("Broadcom Corporation");
+MODULE_DESCRIPTION("Broadcom System Port Ethernet MAC driver");
+MODULE_ALIAS("platform:brcm-systemport");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/broadcom/bcmsysport.h b/drivers/net/ethernet/broadcom/bcmsysport.h
new file mode 100644 (file)
index 0000000..281c082
--- /dev/null
@@ -0,0 +1,678 @@
+/*
+ * Broadcom BCM7xxx System Port Ethernet MAC driver
+ *
+ * Copyright (C) 2014 Broadcom 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.
+ */
+
+#ifndef __BCM_SYSPORT_H
+#define __BCM_SYSPORT_H
+
+#include <linux/if_vlan.h>
+
+/* Receive/transmit descriptor format */
+#define DESC_ADDR_HI_STATUS_LEN        0x00
+#define  DESC_ADDR_HI_SHIFT    0
+#define  DESC_ADDR_HI_MASK     0xff
+#define  DESC_STATUS_SHIFT     8
+#define  DESC_STATUS_MASK      0x3ff
+#define  DESC_LEN_SHIFT                18
+#define  DESC_LEN_MASK         0x7fff
+#define DESC_ADDR_LO           0x04
+
+/* HW supports 40-bit addressing hence the */
+#define DESC_SIZE              (WORDS_PER_DESC * sizeof(u32))
+
+/* Default RX buffer allocation size */
+#define RX_BUF_LENGTH          2048
+
+/* Body(1500) + EH_SIZE(14) + VLANTAG(4) + BRCMTAG(4) + FCS(4) = 1526.
+ * 1536 is multiple of 256 bytes
+ */
+#define ENET_BRCM_TAG_LEN      4
+#define ENET_PAD               10
+#define UMAC_MAX_MTU_SIZE      (ETH_DATA_LEN + ETH_HLEN + VLAN_HLEN + \
+                                ENET_BRCM_TAG_LEN + ETH_FCS_LEN + ENET_PAD)
+
+/* Transmit status block */
+struct bcm_tsb {
+       u32 pcp_dei_vid;
+#define PCP_DEI_MASK           0xf
+#define VID_SHIFT              4
+#define VID_MASK               0xfff
+       u32 l4_ptr_dest_map;
+#define L4_CSUM_PTR_MASK       0x1ff
+#define L4_PTR_SHIFT           9
+#define L4_PTR_MASK            0x1ff
+#define L4_UDP                 (1 << 18)
+#define L4_LENGTH_VALID                (1 << 19)
+#define DEST_MAP_SHIFT         20
+#define DEST_MAP_MASK          0x1ff
+};
+
+/* Receive status block uses the same
+ * definitions as the DMA descriptor
+ */
+struct bcm_rsb {
+       u32 rx_status_len;
+       u32 brcm_egress_tag;
+};
+
+/* Common Receive/Transmit status bits */
+#define DESC_L4_CSUM           (1 << 7)
+#define DESC_SOP               (1 << 8)
+#define DESC_EOP               (1 << 9)
+
+/* Receive Status bits */
+#define RX_STATUS_UCAST                        0
+#define RX_STATUS_BCAST                        0x04
+#define RX_STATUS_MCAST                        0x08
+#define RX_STATUS_L2_MCAST             0x0c
+#define RX_STATUS_ERR                  (1 << 4)
+#define RX_STATUS_OVFLOW               (1 << 5)
+#define RX_STATUS_PARSE_FAIL           (1 << 6)
+
+/* Transmit Status bits */
+#define TX_STATUS_VLAN_NO_ACT          0x00
+#define TX_STATUS_VLAN_PCP_TSB         0x01
+#define TX_STATUS_VLAN_QUEUE           0x02
+#define TX_STATUS_VLAN_VID_TSB         0x03
+#define TX_STATUS_OWR_CRC              (1 << 2)
+#define TX_STATUS_APP_CRC              (1 << 3)
+#define TX_STATUS_BRCM_TAG_NO_ACT      0
+#define TX_STATUS_BRCM_TAG_ZERO                0x10
+#define TX_STATUS_BRCM_TAG_ONE_QUEUE   0x20
+#define TX_STATUS_BRCM_TAG_ONE_TSB     0x30
+#define TX_STATUS_SKIP_BYTES           (1 << 6)
+
+/* Specific register definitions */
+#define SYS_PORT_TOPCTRL_OFFSET                0
+#define REV_CNTL                       0x00
+#define  REV_MASK                      0xffff
+
+#define RX_FLUSH_CNTL                  0x04
+#define  RX_FLUSH                      (1 << 0)
+
+#define TX_FLUSH_CNTL                  0x08
+#define  TX_FLUSH                      (1 << 0)
+
+#define MISC_CNTL                      0x0c
+#define  SYS_CLK_SEL                   (1 << 0)
+#define  TDMA_EOP_SEL                  (1 << 1)
+
+/* Level-2 Interrupt controller offsets and defines */
+#define SYS_PORT_INTRL2_0_OFFSET       0x200
+#define SYS_PORT_INTRL2_1_OFFSET       0x240
+#define INTRL2_CPU_STATUS              0x00
+#define INTRL2_CPU_SET                 0x04
+#define INTRL2_CPU_CLEAR               0x08
+#define INTRL2_CPU_MASK_STATUS         0x0c
+#define INTRL2_CPU_MASK_SET            0x10
+#define INTRL2_CPU_MASK_CLEAR          0x14
+
+/* Level-2 instance 0 interrupt bits */
+#define INTRL2_0_GISB_ERR              (1 << 0)
+#define INTRL2_0_RBUF_OVFLOW           (1 << 1)
+#define INTRL2_0_TBUF_UNDFLOW          (1 << 2)
+#define INTRL2_0_MPD                   (1 << 3)
+#define INTRL2_0_BRCM_MATCH_TAG                (1 << 4)
+#define INTRL2_0_RDMA_MBDONE           (1 << 5)
+#define INTRL2_0_OVER_MAX_THRESH       (1 << 6)
+#define INTRL2_0_BELOW_HYST_THRESH     (1 << 7)
+#define INTRL2_0_FREE_LIST_EMPTY       (1 << 8)
+#define INTRL2_0_TX_RING_FULL          (1 << 9)
+#define INTRL2_0_DESC_ALLOC_ERR                (1 << 10)
+#define INTRL2_0_UNEXP_PKTSIZE_ACK     (1 << 11)
+
+/* RXCHK offset and defines */
+#define SYS_PORT_RXCHK_OFFSET          0x300
+
+#define RXCHK_CONTROL                  0x00
+#define  RXCHK_EN                      (1 << 0)
+#define  RXCHK_SKIP_FCS                        (1 << 1)
+#define  RXCHK_BAD_CSUM_DIS            (1 << 2)
+#define  RXCHK_BRCM_TAG_EN             (1 << 3)
+#define  RXCHK_BRCM_TAG_MATCH_SHIFT    4
+#define  RXCHK_BRCM_TAG_MATCH_MASK     0xff
+#define  RXCHK_PARSE_TNL               (1 << 12)
+#define  RXCHK_VIOL_EN                 (1 << 13)
+#define  RXCHK_VIOL_DIS                        (1 << 14)
+#define  RXCHK_INCOM_PKT               (1 << 15)
+#define  RXCHK_V6_DUPEXT_EN            (1 << 16)
+#define  RXCHK_V6_DUPEXT_DIS           (1 << 17)
+#define  RXCHK_ETHERTYPE_DIS           (1 << 18)
+#define  RXCHK_L2_HDR_DIS              (1 << 19)
+#define  RXCHK_L3_HDR_DIS              (1 << 20)
+#define  RXCHK_MAC_RX_ERR_DIS          (1 << 21)
+#define  RXCHK_PARSE_AUTH              (1 << 22)
+
+#define RXCHK_BRCM_TAG0                        0x04
+#define RXCHK_BRCM_TAG(i)              ((i) * RXCHK_BRCM_TAG0)
+#define RXCHK_BRCM_TAG0_MASK           0x24
+#define RXCHK_BRCM_TAG_MASK(i)         ((i) * RXCHK_BRCM_TAG0_MASK)
+#define RXCHK_BRCM_TAG_MATCH_STATUS    0x44
+#define RXCHK_ETHERTYPE                        0x48
+#define RXCHK_BAD_CSUM_CNTR            0x4C
+#define RXCHK_OTHER_DISC_CNTR          0x50
+
+/* TXCHCK offsets and defines */
+#define SYS_PORT_TXCHK_OFFSET          0x380
+#define TXCHK_PKT_RDY_THRESH           0x00
+
+/* Receive buffer offset and defines */
+#define SYS_PORT_RBUF_OFFSET           0x400
+
+#define RBUF_CONTROL                   0x00
+#define  RBUF_RSB_EN                   (1 << 0)
+#define  RBUF_4B_ALGN                  (1 << 1)
+#define  RBUF_BRCM_TAG_STRIP           (1 << 2)
+#define  RBUF_BAD_PKT_DISC             (1 << 3)
+#define  RBUF_RESUME_THRESH_SHIFT      4
+#define  RBUF_RESUME_THRESH_MASK       0xff
+#define  RBUF_OK_TO_SEND_SHIFT         12
+#define  RBUF_OK_TO_SEND_MASK          0xff
+#define  RBUF_CRC_REPLACE              (1 << 20)
+#define  RBUF_OK_TO_SEND_MODE          (1 << 21)
+#define  RBUF_RSB_SWAP                 (1 << 22)
+#define  RBUF_ACPI_EN                  (1 << 23)
+
+#define RBUF_PKT_RDY_THRESH            0x04
+
+#define RBUF_STATUS                    0x08
+#define  RBUF_WOL_MODE                 (1 << 0)
+#define  RBUF_MPD                      (1 << 1)
+#define  RBUF_ACPI                     (1 << 2)
+
+#define RBUF_OVFL_DISC_CNTR            0x0c
+#define RBUF_ERR_PKT_CNTR              0x10
+
+/* Transmit buffer offset and defines */
+#define SYS_PORT_TBUF_OFFSET           0x600
+
+#define TBUF_CONTROL                   0x00
+#define  TBUF_BP_EN                    (1 << 0)
+#define  TBUF_MAX_PKT_THRESH_SHIFT     1
+#define  TBUF_MAX_PKT_THRESH_MASK      0x1f
+#define  TBUF_FULL_THRESH_SHIFT                8
+#define  TBUF_FULL_THRESH_MASK         0x1f
+
+/* UniMAC offset and defines */
+#define SYS_PORT_UMAC_OFFSET           0x800
+
+#define UMAC_CMD                       0x008
+#define  CMD_TX_EN                     (1 << 0)
+#define  CMD_RX_EN                     (1 << 1)
+#define  CMD_SPEED_SHIFT               2
+#define  CMD_SPEED_10                  0
+#define  CMD_SPEED_100                 1
+#define  CMD_SPEED_1000                        2
+#define  CMD_SPEED_2500                        3
+#define  CMD_SPEED_MASK                        3
+#define  CMD_PROMISC                   (1 << 4)
+#define  CMD_PAD_EN                    (1 << 5)
+#define  CMD_CRC_FWD                   (1 << 6)
+#define  CMD_PAUSE_FWD                 (1 << 7)
+#define  CMD_RX_PAUSE_IGNORE           (1 << 8)
+#define  CMD_TX_ADDR_INS               (1 << 9)
+#define  CMD_HD_EN                     (1 << 10)
+#define  CMD_SW_RESET                  (1 << 13)
+#define  CMD_LCL_LOOP_EN               (1 << 15)
+#define  CMD_AUTO_CONFIG               (1 << 22)
+#define  CMD_CNTL_FRM_EN               (1 << 23)
+#define  CMD_NO_LEN_CHK                        (1 << 24)
+#define  CMD_RMT_LOOP_EN               (1 << 25)
+#define  CMD_PRBL_EN                   (1 << 27)
+#define  CMD_TX_PAUSE_IGNORE           (1 << 28)
+#define  CMD_TX_RX_EN                  (1 << 29)
+#define  CMD_RUNT_FILTER_DIS           (1 << 30)
+
+#define UMAC_MAC0                      0x00c
+#define UMAC_MAC1                      0x010
+#define UMAC_MAX_FRAME_LEN             0x014
+
+#define UMAC_TX_FLUSH                  0x334
+
+#define UMAC_MIB_START                 0x400
+
+/* There is a 0xC gap between the end of RX and beginning of TX stats and then
+ * between the end of TX stats and the beginning of the RX RUNT
+ */
+#define UMAC_MIB_STAT_OFFSET           0xc
+
+#define UMAC_MIB_CTRL                  0x580
+#define  MIB_RX_CNT_RST                        (1 << 0)
+#define  MIB_RUNT_CNT_RST              (1 << 1)
+#define  MIB_TX_CNT_RST                        (1 << 2)
+#define UMAC_MDF_CTRL                  0x650
+#define UMAC_MDF_ADDR                  0x654
+
+/* Receive DMA offset and defines */
+#define SYS_PORT_RDMA_OFFSET           0x2000
+
+#define RDMA_CONTROL                   0x1000
+#define  RDMA_EN                       (1 << 0)
+#define  RDMA_RING_CFG                 (1 << 1)
+#define  RDMA_DISC_EN                  (1 << 2)
+#define  RDMA_BUF_DATA_OFFSET_SHIFT    4
+#define  RDMA_BUF_DATA_OFFSET_MASK     0x3ff
+
+#define RDMA_STATUS                    0x1004
+#define  RDMA_DISABLED                 (1 << 0)
+#define  RDMA_DESC_RAM_INIT_BUSY       (1 << 1)
+#define  RDMA_BP_STATUS                        (1 << 2)
+
+#define RDMA_SCB_BURST_SIZE            0x1008
+
+#define RDMA_RING_BUF_SIZE             0x100c
+#define  RDMA_RING_SIZE_SHIFT          16
+
+#define RDMA_WRITE_PTR_HI              0x1010
+#define RDMA_WRITE_PTR_LO              0x1014
+#define RDMA_PROD_INDEX                        0x1018
+#define  RDMA_PROD_INDEX_MASK          0xffff
+
+#define RDMA_CONS_INDEX                        0x101c
+#define  RDMA_CONS_INDEX_MASK          0xffff
+
+#define RDMA_START_ADDR_HI             0x1020
+#define RDMA_START_ADDR_LO             0x1024
+#define RDMA_END_ADDR_HI               0x1028
+#define RDMA_END_ADDR_LO               0x102c
+
+#define RDMA_MBDONE_INTR               0x1030
+#define  RDMA_INTR_THRESH_MASK         0xff
+#define  RDMA_TIMEOUT_SHIFT            16
+#define  RDMA_TIMEOUT_MASK             0xffff
+
+#define RDMA_XON_XOFF_THRESH           0x1034
+#define  RDMA_XON_XOFF_THRESH_MASK     0xffff
+#define  RDMA_XOFF_THRESH_SHIFT                16
+
+#define RDMA_READ_PTR_HI               0x1038
+#define RDMA_READ_PTR_LO               0x103c
+
+#define RDMA_OVERRIDE                  0x1040
+#define  RDMA_LE_MODE                  (1 << 0)
+#define  RDMA_REG_MODE                 (1 << 1)
+
+#define RDMA_TEST                      0x1044
+#define  RDMA_TP_OUT_SEL               (1 << 0)
+#define  RDMA_MEM_SEL                  (1 << 1)
+
+#define RDMA_DEBUG                     0x1048
+
+/* Transmit DMA offset and defines */
+#define TDMA_NUM_RINGS                 32      /* rings = queues */
+#define TDMA_PORT_SIZE                 DESC_SIZE /* two 32-bits words */
+
+#define SYS_PORT_TDMA_OFFSET           0x4000
+#define TDMA_WRITE_PORT_OFFSET         0x0000
+#define TDMA_WRITE_PORT_HI(i)          (TDMA_WRITE_PORT_OFFSET + \
+                                       (i) * TDMA_PORT_SIZE)
+#define TDMA_WRITE_PORT_LO(i)          (TDMA_WRITE_PORT_OFFSET + \
+                                       sizeof(u32) + (i) * TDMA_PORT_SIZE)
+
+#define TDMA_READ_PORT_OFFSET          (TDMA_WRITE_PORT_OFFSET + \
+                                       (TDMA_NUM_RINGS * TDMA_PORT_SIZE))
+#define TDMA_READ_PORT_HI(i)           (TDMA_READ_PORT_OFFSET + \
+                                       (i) * TDMA_PORT_SIZE)
+#define TDMA_READ_PORT_LO(i)           (TDMA_READ_PORT_OFFSET + \
+                                       sizeof(u32) + (i) * TDMA_PORT_SIZE)
+
+#define TDMA_READ_PORT_CMD_OFFSET      (TDMA_READ_PORT_OFFSET + \
+                                       (TDMA_NUM_RINGS * TDMA_PORT_SIZE))
+#define TDMA_READ_PORT_CMD(i)          (TDMA_READ_PORT_CMD_OFFSET + \
+                                       (i) * sizeof(u32))
+
+#define TDMA_DESC_RING_00_BASE         (TDMA_READ_PORT_CMD_OFFSET + \
+                                       (TDMA_NUM_RINGS * sizeof(u32)))
+
+/* Register offsets and defines relatives to a specific ring number */
+#define RING_HEAD_TAIL_PTR             0x00
+#define  RING_HEAD_MASK                        0x7ff
+#define  RING_TAIL_SHIFT               11
+#define  RING_TAIL_MASK                        0x7ff
+#define  RING_FLUSH                    (1 << 24)
+#define  RING_EN                       (1 << 25)
+
+#define RING_COUNT                     0x04
+#define  RING_COUNT_MASK               0x7ff
+#define  RING_BUFF_DONE_SHIFT          11
+#define  RING_BUFF_DONE_MASK           0x7ff
+
+#define RING_MAX_HYST                  0x08
+#define  RING_MAX_THRESH_MASK          0x7ff
+#define  RING_HYST_THRESH_SHIFT                11
+#define  RING_HYST_THRESH_MASK         0x7ff
+
+#define RING_INTR_CONTROL              0x0c
+#define  RING_INTR_THRESH_MASK         0x7ff
+#define  RING_EMPTY_INTR_EN            (1 << 15)
+#define  RING_TIMEOUT_SHIFT            16
+#define  RING_TIMEOUT_MASK             0xffff
+
+#define RING_PROD_CONS_INDEX           0x10
+#define  RING_PROD_INDEX_MASK          0xffff
+#define  RING_CONS_INDEX_SHIFT         16
+#define  RING_CONS_INDEX_MASK          0xffff
+
+#define RING_MAPPING                   0x14
+#define  RING_QID_MASK                 0x3
+#define  RING_PORT_ID_SHIFT            3
+#define  RING_PORT_ID_MASK             0x7
+#define  RING_IGNORE_STATUS            (1 << 6)
+#define  RING_FAILOVER_EN              (1 << 7)
+#define  RING_CREDIT_SHIFT             8
+#define  RING_CREDIT_MASK              0xffff
+
+#define RING_PCP_DEI_VID               0x18
+#define  RING_VID_MASK                 0x7ff
+#define  RING_DEI                      (1 << 12)
+#define  RING_PCP_SHIFT                        13
+#define  RING_PCP_MASK                 0x7
+#define  RING_PKT_SIZE_ADJ_SHIFT       16
+#define  RING_PKT_SIZE_ADJ_MASK                0xf
+
+#define TDMA_DESC_RING_SIZE            28
+
+/* Defininition for a given TX ring base address */
+#define TDMA_DESC_RING_BASE(i)         (TDMA_DESC_RING_00_BASE + \
+                                       ((i) * TDMA_DESC_RING_SIZE))
+
+/* Ring indexed register addreses */
+#define TDMA_DESC_RING_HEAD_TAIL_PTR(i)        (TDMA_DESC_RING_BASE(i) + \
+                                       RING_HEAD_TAIL_PTR)
+#define TDMA_DESC_RING_COUNT(i)                (TDMA_DESC_RING_BASE(i) + \
+                                       RING_COUNT)
+#define TDMA_DESC_RING_MAX_HYST(i)     (TDMA_DESC_RING_BASE(i) + \
+                                       RING_MAX_HYST)
+#define TDMA_DESC_RING_INTR_CONTROL(i) (TDMA_DESC_RING_BASE(i) + \
+                                       RING_INTR_CONTROL)
+#define TDMA_DESC_RING_PROD_CONS_INDEX(i) \
+                                       (TDMA_DESC_RING_BASE(i) + \
+                                       RING_PROD_CONS_INDEX)
+#define TDMA_DESC_RING_MAPPING(i)      (TDMA_DESC_RING_BASE(i) + \
+                                       RING_MAPPING)
+#define TDMA_DESC_RING_PCP_DEI_VID(i)  (TDMA_DESC_RING_BASE(i) + \
+                                       RING_PCP_DEI_VID)
+
+#define TDMA_CONTROL                   0x600
+#define  TDMA_EN                       (1 << 0)
+#define  TSB_EN                                (1 << 1)
+#define  TSB_SWAP                      (1 << 2)
+#define  ACB_ALGO                      (1 << 3)
+#define  BUF_DATA_OFFSET_SHIFT         4
+#define  BUF_DATA_OFFSET_MASK          0x3ff
+#define  VLAN_EN                       (1 << 14)
+#define  SW_BRCM_TAG                   (1 << 15)
+#define  WNC_KPT_SIZE_UPDATE           (1 << 16)
+#define  SYNC_PKT_SIZE                 (1 << 17)
+#define  ACH_TXDONE_DELAY_SHIFT                18
+#define  ACH_TXDONE_DELAY_MASK         0xff
+
+#define TDMA_STATUS                    0x604
+#define  TDMA_DISABLED                 (1 << 0)
+#define  TDMA_LL_RAM_INIT_BUSY         (1 << 1)
+
+#define TDMA_SCB_BURST_SIZE            0x608
+#define TDMA_OVER_MAX_THRESH_STATUS    0x60c
+#define TDMA_OVER_HYST_THRESH_STATUS   0x610
+#define TDMA_TPID                      0x614
+
+#define TDMA_FREE_LIST_HEAD_TAIL_PTR   0x618
+#define  TDMA_FREE_HEAD_MASK           0x7ff
+#define  TDMA_FREE_TAIL_SHIFT          11
+#define  TDMA_FREE_TAIL_MASK           0x7ff
+
+#define TDMA_FREE_LIST_COUNT           0x61c
+#define  TDMA_FREE_LIST_COUNT_MASK     0x7ff
+
+#define TDMA_TIER2_ARB_CTRL            0x620
+#define  TDMA_ARB_MODE_RR              0
+#define  TDMA_ARB_MODE_WEIGHT_RR       0x1
+#define  TDMA_ARB_MODE_STRICT          0x2
+#define  TDMA_ARB_MODE_DEFICIT_RR      0x3
+#define  TDMA_CREDIT_SHIFT             4
+#define  TDMA_CREDIT_MASK              0xffff
+
+#define TDMA_TIER1_ARB_0_CTRL          0x624
+#define  TDMA_ARB_EN                   (1 << 0)
+
+#define TDMA_TIER1_ARB_0_QUEUE_EN      0x628
+#define TDMA_TIER1_ARB_1_CTRL          0x62c
+#define TDMA_TIER1_ARB_1_QUEUE_EN      0x630
+#define TDMA_TIER1_ARB_2_CTRL          0x634
+#define TDMA_TIER1_ARB_2_QUEUE_EN      0x638
+#define TDMA_TIER1_ARB_3_CTRL          0x63c
+#define TDMA_TIER1_ARB_3_QUEUE_EN      0x640
+
+#define TDMA_SCB_ENDIAN_OVERRIDE       0x644
+#define  TDMA_LE_MODE                  (1 << 0)
+#define  TDMA_REG_MODE                 (1 << 1)
+
+#define TDMA_TEST                      0x648
+#define  TDMA_TP_OUT_SEL               (1 << 0)
+#define  TDMA_MEM_TM                   (1 << 1)
+
+#define TDMA_DEBUG                     0x64c
+
+/* Transmit/Receive descriptor */
+struct dma_desc {
+       u32     addr_status_len;
+       u32     addr_lo;
+};
+
+/* Number of Receive hardware descriptor words */
+#define NUM_HW_RX_DESC_WORDS           1024
+/* Real number of usable descriptors */
+#define NUM_RX_DESC                    (NUM_HW_RX_DESC_WORDS / WORDS_PER_DESC)
+
+/* Internal linked-list RAM has up to 1536 entries */
+#define NUM_TX_DESC                    1536
+
+#define WORDS_PER_DESC                 (sizeof(struct dma_desc) / sizeof(u32))
+
+/* Rx/Tx common counter group.*/
+struct bcm_sysport_pkt_counters {
+       u32     cnt_64;         /* RO Received/Transmited 64 bytes packet */
+       u32     cnt_127;        /* RO Rx/Tx 127 bytes packet */
+       u32     cnt_255;        /* RO Rx/Tx 65-255 bytes packet */
+       u32     cnt_511;        /* RO Rx/Tx 256-511 bytes packet */
+       u32     cnt_1023;       /* RO Rx/Tx 512-1023 bytes packet */
+       u32     cnt_1518;       /* RO Rx/Tx 1024-1518 bytes packet */
+       u32     cnt_mgv;        /* RO Rx/Tx 1519-1522 good VLAN packet */
+       u32     cnt_2047;       /* RO Rx/Tx 1522-2047 bytes packet*/
+       u32     cnt_4095;       /* RO Rx/Tx 2048-4095 bytes packet*/
+       u32     cnt_9216;       /* RO Rx/Tx 4096-9216 bytes packet*/
+};
+
+/* RSV, Receive Status Vector */
+struct bcm_sysport_rx_counters {
+       struct  bcm_sysport_pkt_counters pkt_cnt;
+       u32     pkt;            /* RO (0x428) Received pkt count*/
+       u32     bytes;          /* RO Received byte count */
+       u32     mca;            /* RO # of Received multicast pkt */
+       u32     bca;            /* RO # of Receive broadcast pkt */
+       u32     fcs;            /* RO # of Received FCS error  */
+       u32     cf;             /* RO # of Received control frame pkt*/
+       u32     pf;             /* RO # of Received pause frame pkt */
+       u32     uo;             /* RO # of unknown op code pkt */
+       u32     aln;            /* RO # of alignment error count */
+       u32     flr;            /* RO # of frame length out of range count */
+       u32     cde;            /* RO # of code error pkt */
+       u32     fcr;            /* RO # of carrier sense error pkt */
+       u32     ovr;            /* RO # of oversize pkt*/
+       u32     jbr;            /* RO # of jabber count */
+       u32     mtue;           /* RO # of MTU error pkt*/
+       u32     pok;            /* RO # of Received good pkt */
+       u32     uc;             /* RO # of unicast pkt */
+       u32     ppp;            /* RO # of PPP pkt */
+       u32     rcrc;           /* RO (0x470),# of CRC match pkt */
+};
+
+/* TSV, Transmit Status Vector */
+struct bcm_sysport_tx_counters {
+       struct bcm_sysport_pkt_counters pkt_cnt;
+       u32     pkts;           /* RO (0x4a8) Transmited pkt */
+       u32     mca;            /* RO # of xmited multicast pkt */
+       u32     bca;            /* RO # of xmited broadcast pkt */
+       u32     pf;             /* RO # of xmited pause frame count */
+       u32     cf;             /* RO # of xmited control frame count */
+       u32     fcs;            /* RO # of xmited FCS error count */
+       u32     ovr;            /* RO # of xmited oversize pkt */
+       u32     drf;            /* RO # of xmited deferral pkt */
+       u32     edf;            /* RO # of xmited Excessive deferral pkt*/
+       u32     scl;            /* RO # of xmited single collision pkt */
+       u32     mcl;            /* RO # of xmited multiple collision pkt*/
+       u32     lcl;            /* RO # of xmited late collision pkt */
+       u32     ecl;            /* RO # of xmited excessive collision pkt*/
+       u32     frg;            /* RO # of xmited fragments pkt*/
+       u32     ncl;            /* RO # of xmited total collision count */
+       u32     jbr;            /* RO # of xmited jabber count*/
+       u32     bytes;          /* RO # of xmited byte count */
+       u32     pok;            /* RO # of xmited good pkt */
+       u32     uc;             /* RO (0x0x4f0)# of xmited unitcast pkt */
+};
+
+struct bcm_sysport_mib {
+       struct bcm_sysport_rx_counters rx;
+       struct bcm_sysport_tx_counters tx;
+       u32 rx_runt_cnt;
+       u32 rx_runt_fcs;
+       u32 rx_runt_fcs_align;
+       u32 rx_runt_bytes;
+       u32 rxchk_bad_csum;
+       u32 rxchk_other_pkt_disc;
+       u32 rbuf_ovflow_cnt;
+       u32 rbuf_err_cnt;
+};
+
+/* HW maintains a large list of counters */
+enum bcm_sysport_stat_type {
+       BCM_SYSPORT_STAT_NETDEV = -1,
+       BCM_SYSPORT_STAT_MIB_RX,
+       BCM_SYSPORT_STAT_MIB_TX,
+       BCM_SYSPORT_STAT_RUNT,
+       BCM_SYSPORT_STAT_RXCHK,
+       BCM_SYSPORT_STAT_RBUF,
+};
+
+/* Macros to help define ethtool statistics */
+#define STAT_NETDEV(m) { \
+       .stat_string = __stringify(m), \
+       .stat_sizeof = sizeof(((struct net_device_stats *)0)->m), \
+       .stat_offset = offsetof(struct net_device_stats, m), \
+       .type = BCM_SYSPORT_STAT_NETDEV, \
+}
+
+#define STAT_MIB(str, m, _type) { \
+       .stat_string = str, \
+       .stat_sizeof = sizeof(((struct bcm_sysport_priv *)0)->m), \
+       .stat_offset = offsetof(struct bcm_sysport_priv, m), \
+       .type = _type, \
+}
+
+#define STAT_MIB_RX(str, m) STAT_MIB(str, m, BCM_SYSPORT_STAT_MIB_RX)
+#define STAT_MIB_TX(str, m) STAT_MIB(str, m, BCM_SYSPORT_STAT_MIB_TX)
+#define STAT_RUNT(str, m) STAT_MIB(str, m, BCM_SYSPORT_STAT_RUNT)
+
+#define STAT_RXCHK(str, m, ofs) { \
+       .stat_string = str, \
+       .stat_sizeof = sizeof(((struct bcm_sysport_priv *)0)->m), \
+       .stat_offset = offsetof(struct bcm_sysport_priv, m), \
+       .type = BCM_SYSPORT_STAT_RXCHK, \
+       .reg_offset = ofs, \
+}
+
+#define STAT_RBUF(str, m, ofs) { \
+       .stat_string = str, \
+       .stat_sizeof = sizeof(((struct bcm_sysport_priv *)0)->m), \
+       .stat_offset = offsetof(struct bcm_sysport_priv, m), \
+       .type = BCM_SYSPORT_STAT_RBUF, \
+       .reg_offset = ofs, \
+}
+
+struct bcm_sysport_stats {
+       char stat_string[ETH_GSTRING_LEN];
+       int stat_sizeof;
+       int stat_offset;
+       enum bcm_sysport_stat_type type;
+       /* reg offset from UMAC base for misc counters */
+       u16 reg_offset;
+};
+
+/* Software house keeping helper structure */
+struct bcm_sysport_cb {
+       struct sk_buff  *skb;           /* SKB for RX packets */
+       void __iomem    *bd_addr;       /* Buffer descriptor PHYS addr */
+
+       DEFINE_DMA_UNMAP_ADDR(dma_addr);
+       DEFINE_DMA_UNMAP_LEN(dma_len);
+};
+
+/* Software view of the TX ring */
+struct bcm_sysport_tx_ring {
+       spinlock_t      lock;           /* Ring lock for tx reclaim/xmit */
+       struct napi_struct napi;        /* NAPI per tx queue */
+       dma_addr_t      desc_dma;       /* DMA cookie */
+       unsigned int    index;          /* Ring index */
+       unsigned int    size;           /* Ring current size */
+       unsigned int    alloc_size;     /* Ring one-time allocated size */
+       unsigned int    desc_count;     /* Number of descriptors */
+       unsigned int    curr_desc;      /* Current descriptor */
+       unsigned int    c_index;        /* Last consumer index */
+       unsigned int    p_index;        /* Current producer index */
+       struct bcm_sysport_cb *cbs;     /* Transmit control blocks */
+       struct dma_desc *desc_cpu;      /* CPU view of the descriptor */
+       struct bcm_sysport_priv *priv;  /* private context backpointer */
+};
+
+/* Driver private structure */
+struct bcm_sysport_priv {
+       void __iomem            *base;
+       u32                     irq0_stat;
+       u32                     irq0_mask;
+       u32                     irq1_stat;
+       u32                     irq1_mask;
+       struct napi_struct      napi ____cacheline_aligned;
+       struct net_device       *netdev;
+       struct platform_device  *pdev;
+       int                     irq0;
+       int                     irq1;
+
+       /* Transmit rings */
+       struct bcm_sysport_tx_ring tx_rings[TDMA_NUM_RINGS];
+
+       /* Receive queue */
+       void __iomem            *rx_bds;
+       void __iomem            *rx_bd_assign_ptr;
+       unsigned int            rx_bd_assign_index;
+       struct bcm_sysport_cb   *rx_cbs;
+       unsigned int            num_rx_bds;
+       unsigned int            rx_read_ptr;
+       unsigned int            rx_c_index;
+
+       /* PHY device */
+       struct device_node      *phy_dn;
+       struct phy_device       *phydev;
+       phy_interface_t         phy_interface;
+       int                     old_pause;
+       int                     old_link;
+       int                     old_duplex;
+
+       /* Misc fields */
+       unsigned int            rx_csum_en:1;
+       unsigned int            tsb_en:1;
+       unsigned int            crc_fwd:1;
+       u16                     rev;
+
+       /* MIB related fields */
+       struct bcm_sysport_mib  mib;
+
+       /* Ethtool */
+       u32                     msg_enable;
+};
+#endif /* __BCM_SYSPORT_H */
index 0297a79a38e16312c7fe24bfd7d1992b980a8f85..05c6af6c418fa45690d085885b0cca330b58c210 100644 (file)
@@ -1436,7 +1436,7 @@ static int bgmac_probe(struct bcma_device *core)
                return -ENOMEM;
        net_dev->netdev_ops = &bgmac_netdev_ops;
        net_dev->irq = core->irq;
-       SET_ETHTOOL_OPS(net_dev, &bgmac_ethtool_ops);
+       net_dev->ethtool_ops = &bgmac_ethtool_ops;
        bgmac = netdev_priv(net_dev);
        bgmac->net_dev = net_dev;
        bgmac->core = core;
index 0ab83708b6a11a66f1b3d98873d59071d578fcdb..67d2b00473718eec10b9f1fbdbf4f6a981aa02d0 100644 (file)
@@ -6916,8 +6916,8 @@ bnx2_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
                }
        }
        else {
-               ethtool_cmd_speed_set(cmd, -1);
-               cmd->duplex = -1;
+               ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
+               cmd->duplex = DUPLEX_UNKNOWN;
        }
        spin_unlock_bh(&bp->phy_lock);
 
index 4d8f8aba0ea5d93be5e288a31da7fc12903af71f..4cab09d3f80729a2bd843cf6b0b6490f0a8e95ad 100644 (file)
@@ -6,7 +6,7 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Eliezer Tamir
  * Based on code from Michael Chan's bnx2 driver
  */
index dd57c7c5a3da8e011a83e257832a05326147b64e..47c5814114e1764145f18c9c2088e8395214602e 100644 (file)
@@ -6,7 +6,7 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Eliezer Tamir
  * Based on code from Michael Chan's bnx2 driver
  * UDP CSUM errata workaround by Arik Gendelman
@@ -906,6 +906,18 @@ static int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget)
                bd_prod = RX_BD(bd_prod);
                bd_cons = RX_BD(bd_cons);
 
+               /* A rmb() is required to ensure that the CQE is not read
+                * before it is written by the adapter DMA.  PCI ordering
+                * rules will make sure the other fields are written before
+                * the marker at the end of struct eth_fast_path_rx_cqe
+                * but without rmb() a weakly ordered processor can process
+                * stale data.  Without the barrier TPA state-machine might
+                * enter inconsistent state and kernel stack might be
+                * provided with incorrect packet description - these lead
+                * to various kernel crashed.
+                */
+               rmb();
+
                cqe_fp_flags = cqe_fp->type_error_flags;
                cqe_fp_type = cqe_fp_flags & ETH_FAST_PATH_RX_CQE_TYPE;
 
index 3448cc033ca55c33d4f8b002b2828d88b6185cd5..571427c7226b11f4c22669ce8d8f14c81950a10d 100644 (file)
@@ -6,7 +6,7 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Eliezer Tamir
  * Based on code from Michael Chan's bnx2 driver
  * UDP CSUM errata workaround by Arik Gendelman
index 97ea5421dd96f41bc3daf7a899ef9f1ddaab0931..51a952c51cb1a5fda4fbb3807fc9d73da8710087 100644 (file)
@@ -12,7 +12,7 @@
  * license other than the GPL, without Broadcom's express prior written
  * consent.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Dmitry Kravkov
  *
  */
index 804b8f64463e80a1fcb45f51bda976b4d8544062..c6939ecb02c572fd41fa5d2c814ed5ad932c26e7 100644 (file)
@@ -12,7 +12,7 @@
  * license other than the GPL, without Broadcom's express prior written
  * consent.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Dmitry Kravkov
  *
  */
index b6de05e3149b5604d818d5496cbbc23ab7bf64e8..bd0600cf72660f3fbc5ac9b2ad37b481fa0969a0 100644 (file)
@@ -6,7 +6,7 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Eliezer Tamir
  * Based on code from Michael Chan's bnx2 driver
  * UDP CSUM errata workaround by Arik Gendelman
@@ -3316,7 +3316,7 @@ static u32 bnx2x_get_rxfh_indir_size(struct net_device *dev)
        return T_ETH_INDIRECTION_TABLE_SIZE;
 }
 
-static int bnx2x_get_rxfh_indir(struct net_device *dev, u32 *indir)
+static int bnx2x_get_rxfh(struct net_device *dev, u32 *indir, u8 *key)
 {
        struct bnx2x *bp = netdev_priv(dev);
        u8 ind_table[T_ETH_INDIRECTION_TABLE_SIZE] = {0};
@@ -3340,14 +3340,15 @@ static int bnx2x_get_rxfh_indir(struct net_device *dev, u32 *indir)
        return 0;
 }
 
-static int bnx2x_set_rxfh_indir(struct net_device *dev, const u32 *indir)
+static int bnx2x_set_rxfh(struct net_device *dev, const u32 *indir,
+                         const u8 *key)
 {
        struct bnx2x *bp = netdev_priv(dev);
        size_t i;
 
        for (i = 0; i < T_ETH_INDIRECTION_TABLE_SIZE; i++) {
                /*
-                * The same as in bnx2x_get_rxfh_indir: we can't use a memcpy()
+                * The same as in bnx2x_get_rxfh: we can't use a memcpy()
                 * as an internal storage of an indirection table is a u8 array
                 * while indir->ring_index points to an array of u32.
                 *
@@ -3471,8 +3472,8 @@ static const struct ethtool_ops bnx2x_ethtool_ops = {
        .get_rxnfc              = bnx2x_get_rxnfc,
        .set_rxnfc              = bnx2x_set_rxnfc,
        .get_rxfh_indir_size    = bnx2x_get_rxfh_indir_size,
-       .get_rxfh_indir         = bnx2x_get_rxfh_indir,
-       .set_rxfh_indir         = bnx2x_set_rxfh_indir,
+       .get_rxfh               = bnx2x_get_rxfh,
+       .set_rxfh               = bnx2x_set_rxfh,
        .get_channels           = bnx2x_get_channels,
        .set_channels           = bnx2x_set_channels,
        .get_module_info        = bnx2x_get_module_info,
@@ -3498,16 +3499,14 @@ static const struct ethtool_ops bnx2x_vf_ethtool_ops = {
        .get_rxnfc              = bnx2x_get_rxnfc,
        .set_rxnfc              = bnx2x_set_rxnfc,
        .get_rxfh_indir_size    = bnx2x_get_rxfh_indir_size,
-       .get_rxfh_indir         = bnx2x_get_rxfh_indir,
-       .set_rxfh_indir         = bnx2x_set_rxfh_indir,
+       .get_rxfh               = bnx2x_get_rxfh,
+       .set_rxfh               = bnx2x_set_rxfh,
        .get_channels           = bnx2x_get_channels,
        .set_channels           = bnx2x_set_channels,
 };
 
 void bnx2x_set_ethtool_ops(struct bnx2x *bp, struct net_device *netdev)
 {
-       if (IS_PF(bp))
-               SET_ETHTOOL_OPS(netdev, &bnx2x_ethtool_ops);
-       else /* vf */
-               SET_ETHTOOL_OPS(netdev, &bnx2x_vf_ethtool_ops);
+       netdev->ethtool_ops = (IS_PF(bp)) ?
+               &bnx2x_ethtool_ops : &bnx2x_vf_ethtool_ops;
 }
index f572ae164fce4d49317ca752e2cd0eaac1895ae3..8aafd9b5d6a2b107f67a8cce344b4bb479ccc85c 100644 (file)
@@ -6,8 +6,8 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
- * Written by: Vladislav Zolotarov <vladz@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
+ * Written by: Vladislav Zolotarov
  * Based on the original idea of John Wright <john.wright@hp.com>.
  */
 
index c2dfea7968f452defdca6fd4b1ea68758381038e..bd90e50bd8e662d4731b61f82ba9fe06071429a1 100644 (file)
@@ -7,9 +7,9 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Eliezer Tamir
- * Modified by: Vladislav Zolotarov <vladz@broadcom.com>
+ * Modified by: Vladislav Zolotarov
  */
 
 #ifndef BNX2X_INIT_H
index 8ab0dd90096085b33f4ee831f6487c3766b164af..5669ed2e87d0039ab02c3bc70636359e49c811e6 100644 (file)
@@ -8,8 +8,8 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
- * Written by: Vladislav Zolotarov <vladz@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
+ * Written by: Vladislav Zolotarov
  */
 
 #ifndef BNX2X_INIT_OPS_H
index 9b6b3d7304b6ac451f33633853b7ba1eb5c2b787..53fb4fa61b405aa540239492fef2d5a4f94fa9bd 100644 (file)
@@ -2218,7 +2218,6 @@ int bnx2x_update_pfc(struct link_params *params,
         */
        u32 val;
        struct bnx2x *bp = params->bp;
-       int bnx2x_status = 0;
        u8 bmac_loopback = (params->loopback_mode == LOOPBACK_BMAC);
 
        if (params->feature_config_flags & FEATURE_CONFIG_PFC_ENABLED)
@@ -2232,7 +2231,7 @@ int bnx2x_update_pfc(struct link_params *params,
        bnx2x_update_pfc_nig(params, vars, pfc_params);
 
        if (!vars->link_up)
-               return bnx2x_status;
+               return 0;
 
        DP(NETIF_MSG_LINK, "About to update PFC in BMAC\n");
 
@@ -2246,7 +2245,7 @@ int bnx2x_update_pfc(struct link_params *params,
                    == 0) {
                        DP(NETIF_MSG_LINK, "About to update PFC in EMAC\n");
                        bnx2x_emac_enable(params, vars, 0);
-                       return bnx2x_status;
+                       return 0;
                }
                if (CHIP_IS_E2(bp))
                        bnx2x_update_pfc_bmac2(params, vars, bmac_loopback);
@@ -2260,7 +2259,7 @@ int bnx2x_update_pfc(struct link_params *params,
                        val = 1;
                REG_WR(bp, NIG_REG_BMAC0_PAUSE_OUT_EN + params->port*4, val);
        }
-       return bnx2x_status;
+       return 0;
 }
 
 static int bnx2x_bmac1_enable(struct link_params *params,
@@ -3703,7 +3702,8 @@ static void bnx2x_warpcore_restart_AN_KR(struct bnx2x_phy *phy,
 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 lane, i, cl72_ctrl, an_adv = 0, val;
+       u32 wc_lane_config;
        struct bnx2x *bp = params->bp;
        static struct bnx2x_reg_set reg_set[] = {
                {MDIO_WC_DEVAD, MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2, 0x7},
@@ -3822,15 +3822,27 @@ static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy,
                /* Enable Auto-Detect to support 1G over CL37 as well */
                bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
                                 MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X1, 0x10);
-
+               wc_lane_config = REG_RD(bp, params->shmem_base +
+                                       offsetof(struct shmem_region, dev_info.
+                                       shared_hw_config.wc_lane_config));
+               bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
+                               MDIO_WC_REG_RX0_PCI_CTRL + (lane << 4), &val);
                /* Force cl48 sync_status LOW to avoid getting stuck in CL73
                 * parallel-detect loop when CL73 and CL37 are enabled.
                 */
-               CL22_WR_OVER_CL45(bp, phy, MDIO_REG_BANK_AER_BLOCK,
-                                 MDIO_AER_BLOCK_AER_REG, 0);
+               val |= 1 << 11;
+
+               /* Restore Polarity settings in case it was run over by
+                * previous link owner
+                */
+               if (wc_lane_config &
+                   (SHARED_HW_CFG_RX_LANE0_POL_FLIP_ENABLED << lane))
+                       val |= 3 << 2;
+               else
+                       val &= ~(3 << 2);
                bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
-                                MDIO_WC_REG_RXB_ANA_RX_CONTROL_PCI, 0x0800);
-               bnx2x_set_aer_mmd(params, phy);
+                                MDIO_WC_REG_RX0_PCI_CTRL + (lane << 4),
+                                val);
 
                bnx2x_disable_kr2(params, vars, phy);
        }
@@ -6473,7 +6485,6 @@ int bnx2x_test_link(struct link_params *params, struct link_vars *vars,
 static int bnx2x_link_initialize(struct link_params *params,
                                 struct link_vars *vars)
 {
-       int rc = 0;
        u8 phy_index, non_ext_phy;
        struct bnx2x *bp = params->bp;
        /* In case of external phy existence, the line speed would be the
@@ -6546,7 +6557,7 @@ static int bnx2x_link_initialize(struct link_params *params,
                        NIG_STATUS_XGXS0_LINK_STATUS |
                        NIG_STATUS_SERDES0_LINK_STATUS |
                        NIG_MASK_MI_INT));
-       return rc;
+       return 0;
 }
 
 static void bnx2x_int_link_reset(struct bnx2x_phy *phy,
@@ -12461,6 +12472,7 @@ static int bnx2x_avoid_link_flap(struct link_params *params,
        u32 dont_clear_stat, lfa_sts;
        struct bnx2x *bp = params->bp;
 
+       bnx2x_set_mdio_emac_per_phy(bp, params);
        /* Sync the link parameters */
        bnx2x_link_status_update(params, vars);
 
index 3a8e51ed5beca4a35e29dff59bfd1ac9e5064083..2887034523e065a362dded7cf025742de29539b3 100644 (file)
@@ -6,7 +6,7 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Eliezer Tamir
  * Based on code from Michael Chan's bnx2 driver
  * UDP CSUM errata workaround by Arik Gendelman
@@ -10053,6 +10053,24 @@ static void bnx2x_prev_unload_close_mac(struct bnx2x *bp,
 #define BCM_5710_UNDI_FW_MF_VERS       (0x05)
 #define BNX2X_PREV_UNDI_MF_PORT(p) (BAR_TSTRORM_INTMEM + 0x150c + ((p) << 4))
 #define BNX2X_PREV_UNDI_MF_FUNC(f) (BAR_TSTRORM_INTMEM + 0x184c + ((f) << 4))
+
+static bool bnx2x_prev_is_after_undi(struct bnx2x *bp)
+{
+       /* UNDI marks its presence in DORQ -
+        * it initializes CID offset for normal bell to 0x7
+        */
+       if (!(REG_RD(bp, MISC_REG_RESET_REG_1) &
+           MISC_REGISTERS_RESET_REG_1_RST_DORQ))
+               return false;
+
+       if (REG_RD(bp, DORQ_REG_NORM_CID_OFST) == 0x7) {
+               BNX2X_DEV_INFO("UNDI previously loaded\n");
+               return true;
+       }
+
+       return false;
+}
+
 static bool bnx2x_prev_unload_undi_fw_supports_mf(struct bnx2x *bp)
 {
        u8 major, minor, version;
@@ -10302,6 +10320,10 @@ static int bnx2x_prev_unload_uncommon(struct bnx2x *bp)
 
        BNX2X_DEV_INFO("Path is unmarked\n");
 
+       /* Cannot proceed with FLR if UNDI is loaded, since FW does not match */
+       if (bnx2x_prev_is_after_undi(bp))
+               goto out;
+
        /* If function has FLR capabilities, and existing FW version matches
         * the one required, then FLR will be sufficient to clean any residue
         * left by previous driver
@@ -10322,6 +10344,7 @@ static int bnx2x_prev_unload_uncommon(struct bnx2x *bp)
 
        BNX2X_DEV_INFO("Could not FLR\n");
 
+out:
        /* Close the MCP request, return failure*/
        rc = bnx2x_prev_mcp_done(bp);
        if (!rc)
@@ -10360,19 +10383,13 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp)
                /* close LLH filters towards the BRB */
                bnx2x_set_rx_filter(&bp->link_params, 0);
 
-               /* Check if the UNDI driver was previously loaded
-                * UNDI driver initializes CID offset for normal bell to 0x7
-                */
-               if (reset_reg & MISC_REGISTERS_RESET_REG_1_RST_DORQ) {
-                       tmp_reg = REG_RD(bp, DORQ_REG_NORM_CID_OFST);
-                       if (tmp_reg == 0x7) {
-                               BNX2X_DEV_INFO("UNDI previously loaded\n");
-                               prev_undi = true;
-                               /* clear the UNDI indication */
-                               REG_WR(bp, DORQ_REG_NORM_CID_OFST, 0);
-                               /* clear possible idle check errors */
-                               REG_RD(bp, NIG_REG_NIG_INT_STS_CLR_0);
-                       }
+               /* Check if the UNDI driver was previously loaded */
+               if (bnx2x_prev_is_after_undi(bp)) {
+                       prev_undi = true;
+                       /* clear the UNDI indication */
+                       REG_WR(bp, DORQ_REG_NORM_CID_OFST, 0);
+                       /* clear possible idle check errors */
+                       REG_RD(bp, NIG_REG_NIG_INT_STS_CLR_0);
                }
                if (!CHIP_IS_E1x(bp))
                        /* block FW from writing to host */
@@ -13283,8 +13300,8 @@ static int bnx2x_eeh_nic_unload(struct bnx2x *bp)
        netdev_reset_tc(bp->dev);
 
        del_timer_sync(&bp->timer);
-       cancel_delayed_work(&bp->sp_task);
-       cancel_delayed_work(&bp->period_task);
+       cancel_delayed_work_sync(&bp->sp_task);
+       cancel_delayed_work_sync(&bp->period_task);
 
        spin_lock_bh(&bp->stats_lock);
        bp->stats_state = STATS_STATE_DISABLED;
index d725317c42773822ee5f945960df8a66e0f3df66..b1936044767aca73bc494609095ae2cc4fef55f0 100644 (file)
@@ -12,7 +12,7 @@
  * license other than the GPL, without Broadcom's express prior written
  * consent.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Vladislav Zolotarov
  *
  */
index 80f6c790ed88097ed17b3c3f259179e86451eff2..718ecd2946616195cc92d53bd2f6498d525f14fe 100644 (file)
@@ -12,7 +12,7 @@
  * license other than the GPL, without Broadcom's express prior written
  * consent.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Vladislav Zolotarov
  *
  */
index faf01488d26eb9d793a245ba8d31f611739c4695..eda8583f6fc0506c2c4d64fa1f7737645b36e277 100644 (file)
@@ -12,9 +12,9 @@
  * license other than the GPL, without Broadcom's express prior written
  * consent.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
- * Written by: Shmulik Ravid <shmulikr@broadcom.com>
- *            Ariel Elior <ariele@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
+ * Written by: Shmulik Ravid
+ *            Ariel Elior <ariel.elior@qlogic.com>
  *
  */
 #include "bnx2x.h"
@@ -1071,8 +1071,10 @@ void bnx2x_iov_init_dq(struct bnx2x *bp)
        REG_WR(bp, DORQ_REG_VF_TYPE_MIN_MCID_0, 0);
        REG_WR(bp, DORQ_REG_VF_TYPE_MAX_MCID_0, 0x1ffff);
 
-       /* set the VF doorbell threshold */
-       REG_WR(bp, DORQ_REG_VF_USAGE_CT_LIMIT, 4);
+       /* set the VF doorbell threshold. This threshold represents the amount
+        * of doorbells allowed in the main DORQ fifo for a specific VF.
+        */
+       REG_WR(bp, DORQ_REG_VF_USAGE_CT_LIMIT, 64);
 }
 
 void bnx2x_iov_init_dmae(struct bnx2x *bp)
@@ -2576,7 +2578,8 @@ int bnx2x_get_vf_config(struct net_device *dev, int vfidx,
 
        ivi->vf = vfidx;
        ivi->qos = 0;
-       ivi->tx_rate = 10000; /* always 10G. TBA take from link struct */
+       ivi->max_tx_rate = 10000; /* always 10G. TBA take from link struct */
+       ivi->min_tx_rate = 0;
        ivi->spoofchk = 1; /*always enabled */
        if (vf->state == VF_ENABLED) {
                /* mac and vlan are in vlan_mac objects */
index 6929adba52f97029cf1b0e4fa44b0838bef7046d..96c575e147a5b14da2b67e9ea25a044a4a882d07 100644 (file)
@@ -12,9 +12,9 @@
  * license other than the GPL, without Broadcom's express prior written
  * consent.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
- * Written by: Shmulik Ravid <shmulikr@broadcom.com>
- *            Ariel Elior <ariele@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
+ * Written by: Shmulik Ravid
+ *            Ariel Elior <ariel.elior@qlogic.com>
  */
 #ifndef BNX2X_SRIOV_H
 #define BNX2X_SRIOV_H
@@ -571,7 +571,7 @@ static inline void __iomem *bnx2x_vf_doorbells(struct bnx2x *bp)
        return NULL;
 }
 
-static inline void bnx2x_vf_pci_dealloc(struct bnx2 *bp) {return 0; }
+static inline void bnx2x_vf_pci_dealloc(struct bnx2x *bp) {}
 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; }
index 3b75070411aab83136ac2c245ba9c65682aefebe..ca47665f94bf76fd6c9b2c1eb920b5aba7743124 100644 (file)
@@ -6,7 +6,7 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Eliezer Tamir
  * Based on code from Michael Chan's bnx2 driver
  * UDP CSUM errata workaround by Arik Gendelman
index f35845006cdd8a74c57d99d069939548188256bb..2beceaefdeea7aa5ac53f6a3028cd0fbcacbc51a 100644 (file)
@@ -6,7 +6,7 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
  * Written by: Eliezer Tamir
  * Based on code from Michael Chan's bnx2 driver
  * UDP CSUM errata workaround by Arik Gendelman
index 784c7155b98a1977f2b809efe4ee5a45b23dc44b..d712d0ddd719bd4dd3a37b25736dfdccf156084c 100644 (file)
@@ -12,9 +12,9 @@
  * license other than the GPL, without Broadcom's express prior written
  * consent.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
- * Written by: Shmulik Ravid <shmulikr@broadcom.com>
- *            Ariel Elior <ariele@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
+ * Written by: Shmulik Ravid
+ *            Ariel Elior <ariel.elior@qlogic.com>
  */
 
 #include "bnx2x.h"
index c922b81170e5bc20c4ff69d34d16d40f853c9d8a..e21e706762c9964ad917e69b7149700a458f74fc 100644 (file)
@@ -12,8 +12,8 @@
  * license other than the GPL, without Broadcom's express prior written
  * consent.
  *
- * Maintained by: Eilon Greenstein <eilong@broadcom.com>
- * Written by: Ariel Elior <ariele@broadcom.com>
+ * Maintained by: Ariel Elior <ariel.elior@qlogic.com>
+ * Written by: Ariel Elior <ariel.elior@qlogic.com>
  */
 #ifndef VF_PF_IF_H
 #define VF_PF_IF_H
index 4dd48d2fa804324c40c3989c6f29fdc939418f46..8244e2b14bb44ccef6b14f13dae25ea717f03a74 100644 (file)
@@ -608,6 +608,10 @@ static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type)
                pr_err("%s: Bad type %d\n", __func__, ulp_type);
                return -EINVAL;
        }
+
+       if (ulp_type == CNIC_ULP_ISCSI)
+               cnic_send_nlmsg(cp, ISCSI_KEVENT_IF_DOWN, NULL);
+
        mutex_lock(&cnic_lock);
        if (rcu_dereference(cp->ulp_ops[ulp_type])) {
                RCU_INIT_POINTER(cp->ulp_ops[ulp_type], NULL);
@@ -620,9 +624,7 @@ static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type)
        }
        mutex_unlock(&cnic_lock);
 
-       if (ulp_type == CNIC_ULP_ISCSI)
-               cnic_send_nlmsg(cp, ISCSI_KEVENT_IF_DOWN, NULL);
-       else if (ulp_type == CNIC_ULP_FCOE)
+       if (ulp_type == CNIC_ULP_FCOE)
                dev->fcoe_cap = NULL;
 
        synchronize_rcu();
@@ -1039,21 +1041,17 @@ static int cnic_alloc_uio_rings(struct cnic_dev *dev, int pages)
        struct cnic_local *cp = dev->cnic_priv;
        struct cnic_uio_dev *udev;
 
-       read_lock(&cnic_dev_lock);
        list_for_each_entry(udev, &cnic_udev_list, list) {
                if (udev->pdev == dev->pcidev) {
                        udev->dev = dev;
                        if (__cnic_alloc_uio_rings(udev, pages)) {
                                udev->dev = NULL;
-                               read_unlock(&cnic_dev_lock);
                                return -ENOMEM;
                        }
                        cp->udev = udev;
-                       read_unlock(&cnic_dev_lock);
                        return 0;
                }
        }
-       read_unlock(&cnic_dev_lock);
 
        udev = kzalloc(sizeof(struct cnic_uio_dev), GFP_ATOMIC);
        if (!udev)
@@ -1067,9 +1065,7 @@ static int cnic_alloc_uio_rings(struct cnic_dev *dev, int pages)
        if (__cnic_alloc_uio_rings(udev, pages))
                goto err_udev;
 
-       write_lock(&cnic_dev_lock);
        list_add(&udev->list, &cnic_udev_list);
-       write_unlock(&cnic_dev_lock);
 
        pci_dev_get(udev->pdev);
 
@@ -5624,20 +5620,27 @@ static void cnic_rcv_netevent(struct cnic_local *cp, unsigned long event,
 {
        int if_type;
 
-       rcu_read_lock();
        for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) {
                struct cnic_ulp_ops *ulp_ops;
                void *ctx;
 
-               ulp_ops = rcu_dereference(cp->ulp_ops[if_type]);
-               if (!ulp_ops || !ulp_ops->indicate_netevent)
+               mutex_lock(&cnic_lock);
+               ulp_ops = rcu_dereference_protected(cp->ulp_ops[if_type],
+                                               lockdep_is_held(&cnic_lock));
+               if (!ulp_ops || !ulp_ops->indicate_netevent) {
+                       mutex_unlock(&cnic_lock);
                        continue;
+               }
 
                ctx = cp->ulp_handle[if_type];
 
+               set_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]);
+               mutex_unlock(&cnic_lock);
+
                ulp_ops->indicate_netevent(ctx, event, vlan_id);
+
+               clear_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]);
        }
-       rcu_read_unlock();
 }
 
 /* netdev event handler */
index 0966bd04375f1aa0384d4196b368bdf9345dc1f9..5ba1cfbd60da3555878fa8fd467c3a9a36c03642 100644 (file)
@@ -2481,7 +2481,7 @@ static int bcmgenet_probe(struct platform_device *pdev)
        dev_set_drvdata(&pdev->dev, dev);
        ether_addr_copy(dev->dev_addr, macaddr);
        dev->watchdog_timeo = 2 * HZ;
-       SET_ETHTOOL_OPS(dev, &bcmgenet_ethtool_ops);
+       dev->ethtool_ops = &bcmgenet_ethtool_ops;
        dev->netdev_ops = &bcmgenet_netdev_ops;
        netif_napi_add(dev, &priv->napi, bcmgenet_poll, 64);
 
index 4608673beaff9f7682d669e4cb7a540fed0c0f04..add8d8596084054ca1e059a360be4a1d24501122 100644 (file)
@@ -298,6 +298,7 @@ int bcmgenet_mii_config(struct net_device *dev)
 static int bcmgenet_mii_probe(struct net_device *dev)
 {
        struct bcmgenet_priv *priv = netdev_priv(dev);
+       struct device_node *dn = priv->pdev->dev.of_node;
        struct phy_device *phydev;
        unsigned int phy_flags;
        int ret;
@@ -307,15 +308,19 @@ static int bcmgenet_mii_probe(struct net_device *dev)
                return 0;
        }
 
-       if (priv->phy_dn)
-               phydev = of_phy_connect(dev, priv->phy_dn,
-                                       bcmgenet_mii_setup, 0,
-                                       priv->phy_interface);
-       else
-               phydev = of_phy_connect_fixed_link(dev,
-                                       bcmgenet_mii_setup,
-                                       priv->phy_interface);
+       /* In the case of a fixed PHY, the DT node associated
+        * to the PHY is the Ethernet MAC DT node.
+        */
+       if (of_phy_is_fixed_link(dn)) {
+               ret = of_phy_register_fixed_link(dn);
+               if (ret)
+                       return ret;
+
+               priv->phy_dn = dn;
+       }
 
+       phydev = of_phy_connect(dev, priv->phy_dn, bcmgenet_mii_setup, 0,
+                               priv->phy_interface);
        if (!phydev) {
                pr_err("could not attach to PHY\n");
                return -ENODEV;
index e5d95c5ce1ad8df29075dcc243650a0e15aa896d..df2792d8383d83568886f5047b16abfa8894ce7e 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (C) 2001, 2002, 2003, 2004 David S. Miller (davem@redhat.com)
  * Copyright (C) 2001, 2002, 2003 Jeff Garzik (jgarzik@pobox.com)
  * Copyright (C) 2004 Sun Microsystems Inc.
- * Copyright (C) 2005-2013 Broadcom Corporation.
+ * Copyright (C) 2005-2014 Broadcom Corporation.
  *
  * Firmware is:
  *     Derived from proprietary unpublished source code,
@@ -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                    136
+#define TG3_MIN_NUM                    137
 #define DRV_MODULE_VERSION     \
        __stringify(TG3_MAJ_NUM) "." __stringify(TG3_MIN_NUM)
-#define DRV_MODULE_RELDATE     "Jan 03, 2014"
+#define DRV_MODULE_RELDATE     "May 11, 2014"
 
 #define RESET_KIND_SHUTDOWN    0
 #define RESET_KIND_INIT                1
@@ -3224,7 +3224,7 @@ static int tg3_nvram_read_using_eeprom(struct tg3 *tp,
        return 0;
 }
 
-#define NVRAM_CMD_TIMEOUT 10000
+#define NVRAM_CMD_TIMEOUT 100
 
 static int tg3_nvram_exec_cmd(struct tg3 *tp, u32 nvram_cmd)
 {
@@ -7871,9 +7871,7 @@ static int tg3_tso_bug(struct tg3 *tp, struct sk_buff *skb)
        return NETDEV_TX_OK;
 }
 
-/* hard_start_xmit for devices that have the 4G bug and/or 40-bit bug and
- * support TG3_FLAG_HW_TSO_1 or firmware TSO only.
- */
+/* hard_start_xmit for all devices */
 static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct tg3 *tp = netdev_priv(dev);
@@ -7884,6 +7882,10 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
        struct tg3_napi *tnapi;
        struct netdev_queue *txq;
        unsigned int last;
+       struct iphdr *iph = NULL;
+       struct tcphdr *tcph = NULL;
+       __sum16 tcp_csum = 0, ip_csum = 0;
+       __be16 ip_tot_len = 0;
 
        txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb));
        tnapi = &tp->napi[skb_get_queue_mapping(skb)];
@@ -7915,7 +7917,6 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
 
        mss = skb_shinfo(skb)->gso_size;
        if (mss) {
-               struct iphdr *iph;
                u32 tcp_opt_len, hdr_len;
 
                if (skb_cow_head(skb, 0))
@@ -7927,27 +7928,31 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
                hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb) - ETH_HLEN;
 
                if (!skb_is_gso_v6(skb)) {
+                       if (unlikely((ETH_HLEN + hdr_len) > 80) &&
+                           tg3_flag(tp, TSO_BUG))
+                               return tg3_tso_bug(tp, skb);
+
+                       ip_csum = iph->check;
+                       ip_tot_len = iph->tot_len;
                        iph->check = 0;
                        iph->tot_len = htons(mss + hdr_len);
                }
 
-               if (unlikely((ETH_HLEN + hdr_len) > 80) &&
-                   tg3_flag(tp, TSO_BUG))
-                       return tg3_tso_bug(tp, skb);
-
                base_flags |= (TXD_FLAG_CPU_PRE_DMA |
                               TXD_FLAG_CPU_POST_DMA);
 
+               tcph = tcp_hdr(skb);
+               tcp_csum = tcph->check;
+
                if (tg3_flag(tp, HW_TSO_1) ||
                    tg3_flag(tp, HW_TSO_2) ||
                    tg3_flag(tp, HW_TSO_3)) {
-                       tcp_hdr(skb)->check = 0;
+                       tcph->check = 0;
                        base_flags &= ~TXD_FLAG_TCPUDP_CSUM;
-               } else
-                       tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr,
-                                                                iph->daddr, 0,
-                                                                IPPROTO_TCP,
-                                                                0);
+               } else {
+                       tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
+                                                        0, IPPROTO_TCP, 0);
+               }
 
                if (tg3_flag(tp, HW_TSO_3)) {
                        mss |= (hdr_len & 0xc) << 12;
@@ -8047,6 +8052,18 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
        if (would_hit_hwbug) {
                tg3_tx_skb_unmap(tnapi, tnapi->tx_prod, i);
 
+               if (mss) {
+                       /* If it's a TSO packet, do GSO instead of
+                        * allocating and copying to a large linear SKB
+                        */
+                       if (ip_tot_len) {
+                               iph->check = ip_csum;
+                               iph->tot_len = ip_tot_len;
+                       }
+                       tcph->check = tcp_csum;
+                       return tg3_tso_bug(tp, skb);
+               }
+
                /* If the workaround fails due to memory/mapping
                 * failure, silently drop this packet.
                 */
@@ -11876,9 +11893,9 @@ static int tg3_get_eeprom_len(struct net_device *dev)
 static int tg3_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *data)
 {
        struct tg3 *tp = netdev_priv(dev);
-       int ret;
+       int ret, cpmu_restore = 0;
        u8  *pd;
-       u32 i, offset, len, b_offset, b_count;
+       u32 i, offset, len, b_offset, b_count, cpmu_val = 0;
        __be32 val;
 
        if (tg3_flag(tp, NO_NVRAM))
@@ -11890,6 +11907,19 @@ static int tg3_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
 
        eeprom->magic = TG3_EEPROM_MAGIC;
 
+       /* Override clock, link aware and link idle modes */
+       if (tg3_flag(tp, CPMU_PRESENT)) {
+               cpmu_val = tr32(TG3_CPMU_CTRL);
+               if (cpmu_val & (CPMU_CTRL_LINK_AWARE_MODE |
+                               CPMU_CTRL_LINK_IDLE_MODE)) {
+                       tw32(TG3_CPMU_CTRL, cpmu_val &
+                                           ~(CPMU_CTRL_LINK_AWARE_MODE |
+                                            CPMU_CTRL_LINK_IDLE_MODE));
+                       cpmu_restore = 1;
+               }
+       }
+       tg3_override_clk(tp);
+
        if (offset & 3) {
                /* adjustments to start on required 4 byte boundary */
                b_offset = offset & 3;
@@ -11900,7 +11930,7 @@ static int tg3_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
                }
                ret = tg3_nvram_read_be32(tp, offset-b_offset, &val);
                if (ret)
-                       return ret;
+                       goto eeprom_done;
                memcpy(data, ((char *)&val) + b_offset, b_count);
                len -= b_count;
                offset += b_count;
@@ -11912,10 +11942,20 @@ static int tg3_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
        for (i = 0; i < (len - (len & 3)); i += 4) {
                ret = tg3_nvram_read_be32(tp, offset + i, &val);
                if (ret) {
+                       if (i)
+                               i -= 4;
                        eeprom->len += i;
-                       return ret;
+                       goto eeprom_done;
                }
                memcpy(pd + i, &val, 4);
+               if (need_resched()) {
+                       if (signal_pending(current)) {
+                               eeprom->len += i;
+                               ret = -EINTR;
+                               goto eeprom_done;
+                       }
+                       cond_resched();
+               }
        }
        eeprom->len += i;
 
@@ -11926,11 +11966,19 @@ static int tg3_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
                b_offset = offset + len - b_count;
                ret = tg3_nvram_read_be32(tp, b_offset, &val);
                if (ret)
-                       return ret;
+                       goto eeprom_done;
                memcpy(pd, &val, b_count);
                eeprom->len += b_count;
        }
-       return 0;
+       ret = 0;
+
+eeprom_done:
+       /* Restore clock, link aware and link idle modes */
+       tg3_restore_clk(tp);
+       if (cpmu_restore)
+               tw32(TG3_CPMU_CTRL, cpmu_val);
+
+       return ret;
 }
 
 static int tg3_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *data)
@@ -12484,7 +12532,7 @@ static u32 tg3_get_rxfh_indir_size(struct net_device *dev)
        return size;
 }
 
-static int tg3_get_rxfh_indir(struct net_device *dev, u32 *indir)
+static int tg3_get_rxfh(struct net_device *dev, u32 *indir, u8 *key)
 {
        struct tg3 *tp = netdev_priv(dev);
        int i;
@@ -12495,7 +12543,7 @@ static int tg3_get_rxfh_indir(struct net_device *dev, u32 *indir)
        return 0;
 }
 
-static int tg3_set_rxfh_indir(struct net_device *dev, const u32 *indir)
+static int tg3_set_rxfh(struct net_device *dev, const u32 *indir, const u8 *key)
 {
        struct tg3 *tp = netdev_priv(dev);
        size_t i;
@@ -14027,8 +14075,8 @@ static const struct ethtool_ops tg3_ethtool_ops = {
        .get_sset_count         = tg3_get_sset_count,
        .get_rxnfc              = tg3_get_rxnfc,
        .get_rxfh_indir_size    = tg3_get_rxfh_indir_size,
-       .get_rxfh_indir         = tg3_get_rxfh_indir,
-       .set_rxfh_indir         = tg3_set_rxfh_indir,
+       .get_rxfh               = tg3_get_rxfh,
+       .set_rxfh               = tg3_set_rxfh,
        .get_channels           = tg3_get_channels,
        .set_channels           = tg3_set_channels,
        .get_ts_info            = tg3_get_ts_info,
index 04321e5a356e45a0f7fc642f3817035d983dbd90..461accaf0aa40242c3756880dd6659371cdfe5f0 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (C) 2001, 2002, 2003, 2004 David S. Miller (davem@redhat.com)
  * Copyright (C) 2001 Jeff Garzik (jgarzik@pobox.com)
  * Copyright (C) 2004 Sun Microsystems Inc.
- * Copyright (C) 2007-2013 Broadcom Corporation.
+ * Copyright (C) 2007-2014 Broadcom Corporation.
  */
 
 #ifndef _T3_H
index f9e150825bb58bf0ef9e3568a1084cb8ebc61d37..882cad71ad620004aff54832e6dd9758ec025a2c 100644 (file)
@@ -266,8 +266,8 @@ bnad_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
                ethtool_cmd_speed_set(cmd, SPEED_10000);
                cmd->duplex = DUPLEX_FULL;
        } else {
-               ethtool_cmd_speed_set(cmd, -1);
-               cmd->duplex = -1;
+               ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
+               cmd->duplex = DUPLEX_UNKNOWN;
        }
        cmd->transceiver = XCVR_EXTERNAL;
        cmd->maxtxpkt = 0;
@@ -1137,5 +1137,5 @@ static const struct ethtool_ops bnad_ethtool_ops = {
 void
 bnad_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &bnad_ethtool_ops);
+       netdev->ethtool_ops = &bnad_ethtool_ops;
 }
index 521dfea44b837d57bc7a7297ac973cd4d8098d3e..25d6b2a10e4e6f7fb6b09d25706e11e2783bbea4 100644 (file)
@@ -1737,7 +1737,7 @@ static int xgmac_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, ndev);
        ether_setup(ndev);
        ndev->netdev_ops = &xgmac_netdev_ops;
-       SET_ETHTOOL_OPS(ndev, &xgmac_ethtool_ops);
+       ndev->ethtool_ops = &xgmac_ethtool_ops;
        spin_lock_init(&priv->stats_lock);
        INIT_WORK(&priv->tx_timeout_work, xgmac_tx_timeout_work);
 
index 05613a85ce617aeb48e33ed333cb5282075189f2..186566bfdbc8c579ada4136f0d71a9242e9b7629 100644 (file)
@@ -580,8 +580,8 @@ static int get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
                ethtool_cmd_speed_set(cmd, p->link_config.speed);
                cmd->duplex = p->link_config.duplex;
        } else {
-               ethtool_cmd_speed_set(cmd, -1);
-               cmd->duplex = -1;
+               ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
+               cmd->duplex = DUPLEX_UNKNOWN;
        }
 
        cmd->port = (cmd->supported & SUPPORTED_TP) ? PORT_TP : PORT_FIBRE;
@@ -1100,7 +1100,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
                netif_napi_add(netdev, &adapter->napi, t1_poll, 64);
 
-               SET_ETHTOOL_OPS(netdev, &t1_ethtool_ops);
+               netdev->ethtool_ops = &t1_ethtool_ops;
        }
 
        if (t1_init_sw_modules(adapter, bi) < 0) {
index 07bbb711b7e5a716aba3e8d2e3e958e2ce8fa506..5d9cce053cc9a90df1608d049212f639bf559e10 100644 (file)
@@ -1809,8 +1809,8 @@ static int get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
                ethtool_cmd_speed_set(cmd, p->link_config.speed);
                cmd->duplex = p->link_config.duplex;
        } else {
-               ethtool_cmd_speed_set(cmd, -1);
-               cmd->duplex = -1;
+               ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
+               cmd->duplex = DUPLEX_UNKNOWN;
        }
 
        cmd->port = (cmd->supported & SUPPORTED_TP) ? PORT_TP : PORT_FIBRE;
@@ -3291,7 +3291,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                        netdev->features |= NETIF_F_HIGHDMA;
 
                netdev->netdev_ops = &cxgb_netdev_ops;
-               SET_ETHTOOL_OPS(netdev, &cxgb_ethtool_ops);
+               netdev->ethtool_ops = &cxgb_ethtool_ops;
        }
 
        pci_set_drvdata(pdev, adapter);
index c0a9dd55f4e55215bb0e42902c12c97b241d0d3f..b0cbb2b7fd484f95ec36574feef7981753a552e9 100644 (file)
@@ -185,7 +185,7 @@ static struct net_device *get_iff_from_mac(struct adapter *adapter,
                if (ether_addr_equal(dev->dev_addr, mac)) {
                        rcu_read_lock();
                        if (vlan && vlan != VLAN_VID_MASK) {
-                               dev = __vlan_find_dev_deep(dev, htons(ETH_P_8021Q), vlan);
+                               dev = __vlan_find_dev_deep_rcu(dev, htons(ETH_P_8021Q), vlan);
                        } else if (netif_is_bond_slave(dev)) {
                                struct net_device *upper_dev;
 
index 32db37709263bc14e596056bc7a9df80cc65bb62..f503dce4ab173ca951a89db27482ac61b1f1d808 100644 (file)
@@ -357,11 +357,17 @@ enum {
        MAX_OFLD_QSETS = 16,          /* # of offload Tx/Rx queue sets */
        MAX_CTRL_QUEUES = NCHAN,      /* # of control Tx queues */
        MAX_RDMA_QUEUES = NCHAN,      /* # of streaming RDMA Rx queues */
+       MAX_RDMA_CIQS = NCHAN,        /* # of  RDMA concentrator IQs */
+       MAX_ISCSI_QUEUES = NCHAN,     /* # of streaming iSCSI Rx queues */
 };
 
 enum {
-       MAX_EGRQ = 128,         /* max # of egress queues, including FLs */
-       MAX_INGQ = 64           /* max # of interrupt-capable ingress queues */
+       INGQ_EXTRAS = 2,        /* firmware event queue and */
+                               /*   forwarded interrupts */
+       MAX_EGRQ = MAX_ETH_QSETS*2 + MAX_OFLD_QSETS*2
+                  + MAX_CTRL_QUEUES + MAX_RDMA_QUEUES + MAX_ISCSI_QUEUES,
+       MAX_INGQ = MAX_ETH_QSETS + MAX_OFLD_QSETS + MAX_RDMA_QUEUES
+                  + MAX_RDMA_CIQS + MAX_ISCSI_QUEUES + INGQ_EXTRAS,
 };
 
 struct adapter;
@@ -538,6 +544,7 @@ struct sge {
        struct sge_eth_rxq ethrxq[MAX_ETH_QSETS];
        struct sge_ofld_rxq ofldrxq[MAX_OFLD_QSETS];
        struct sge_ofld_rxq rdmarxq[MAX_RDMA_QUEUES];
+       struct sge_ofld_rxq rdmaciq[MAX_RDMA_CIQS];
        struct sge_rspq fw_evtq ____cacheline_aligned_in_smp;
 
        struct sge_rspq intrq ____cacheline_aligned_in_smp;
@@ -548,8 +555,10 @@ struct sge {
        u16 ethtxq_rover;           /* Tx queue to clean up next */
        u16 ofldqsets;              /* # of active offload queue sets */
        u16 rdmaqs;                 /* # of available RDMA Rx queues */
+       u16 rdmaciqs;               /* # of available RDMA concentrator IQs */
        u16 ofld_rxq[MAX_OFLD_QSETS];
        u16 rdma_rxq[NCHAN];
+       u16 rdma_ciq[NCHAN];
        u16 timer_val[SGE_NTIMERS];
        u8 counter_val[SGE_NCOUNTERS];
        u32 fl_pg_order;            /* large page allocation size */
@@ -577,6 +586,7 @@ struct sge {
 #define for_each_ethrxq(sge, i) for (i = 0; i < (sge)->ethqsets; i++)
 #define for_each_ofldrxq(sge, i) for (i = 0; i < (sge)->ofldqsets; i++)
 #define for_each_rdmarxq(sge, i) for (i = 0; i < (sge)->rdmaqs; i++)
+#define for_each_rdmaciq(sge, i) for (i = 0; i < (sge)->rdmaciqs; i++)
 
 struct l2t_data;
 
index 24e16e3301e086d6cff3f28181d3c97a153bf343..2f8d6b9103838d2a602718f388b45ce6fc8ba259 100644 (file)
@@ -818,12 +818,17 @@ static void name_msix_vecs(struct adapter *adap)
        for_each_rdmarxq(&adap->sge, i)
                snprintf(adap->msix_info[msi_idx++].desc, n, "%s-rdma%d",
                         adap->port[0]->name, i);
+
+       for_each_rdmaciq(&adap->sge, i)
+               snprintf(adap->msix_info[msi_idx++].desc, n, "%s-rdma-ciq%d",
+                        adap->port[0]->name, i);
 }
 
 static int request_msix_queue_irqs(struct adapter *adap)
 {
        struct sge *s = &adap->sge;
-       int err, ethqidx, ofldqidx = 0, rdmaqidx = 0, msi_index = 2;
+       int err, ethqidx, ofldqidx = 0, rdmaqidx = 0, rdmaciqqidx = 0;
+       int msi_index = 2;
 
        err = request_irq(adap->msix_info[1].vec, t4_sge_intr_msix, 0,
                          adap->msix_info[1].desc, &s->fw_evtq);
@@ -857,9 +862,21 @@ static int request_msix_queue_irqs(struct adapter *adap)
                        goto unwind;
                msi_index++;
        }
+       for_each_rdmaciq(s, rdmaciqqidx) {
+               err = request_irq(adap->msix_info[msi_index].vec,
+                                 t4_sge_intr_msix, 0,
+                                 adap->msix_info[msi_index].desc,
+                                 &s->rdmaciq[rdmaciqqidx].rspq);
+               if (err)
+                       goto unwind;
+               msi_index++;
+       }
        return 0;
 
 unwind:
+       while (--rdmaciqqidx >= 0)
+               free_irq(adap->msix_info[--msi_index].vec,
+                        &s->rdmaciq[rdmaciqqidx].rspq);
        while (--rdmaqidx >= 0)
                free_irq(adap->msix_info[--msi_index].vec,
                         &s->rdmarxq[rdmaqidx].rspq);
@@ -885,6 +902,8 @@ static void free_msix_queue_irqs(struct adapter *adap)
                free_irq(adap->msix_info[msi_index++].vec, &s->ofldrxq[i].rspq);
        for_each_rdmarxq(s, i)
                free_irq(adap->msix_info[msi_index++].vec, &s->rdmarxq[i].rspq);
+       for_each_rdmaciq(s, i)
+               free_irq(adap->msix_info[msi_index++].vec, &s->rdmaciq[i].rspq);
 }
 
 /**
@@ -1047,7 +1066,8 @@ freeout:  t4_free_sge_resources(adap);
                if (msi_idx > 0)
                        msi_idx++;
                err = t4_sge_alloc_rxq(adap, &q->rspq, false, dev, msi_idx,
-                                      &q->fl, uldrx_handler);
+                                      q->fl.size ? &q->fl : NULL,
+                                      uldrx_handler);
                if (err)
                        goto freeout;
                memset(&q->stats, 0, sizeof(q->stats));
@@ -1064,13 +1084,28 @@ freeout:        t4_free_sge_resources(adap);
                if (msi_idx > 0)
                        msi_idx++;
                err = t4_sge_alloc_rxq(adap, &q->rspq, false, adap->port[i],
-                                      msi_idx, &q->fl, uldrx_handler);
+                                      msi_idx, q->fl.size ? &q->fl : NULL,
+                                      uldrx_handler);
                if (err)
                        goto freeout;
                memset(&q->stats, 0, sizeof(q->stats));
                s->rdma_rxq[i] = q->rspq.abs_id;
        }
 
+       for_each_rdmaciq(s, i) {
+               struct sge_ofld_rxq *q = &s->rdmaciq[i];
+
+               if (msi_idx > 0)
+                       msi_idx++;
+               err = t4_sge_alloc_rxq(adap, &q->rspq, false, adap->port[i],
+                                      msi_idx, q->fl.size ? &q->fl : NULL,
+                                      uldrx_handler);
+               if (err)
+                       goto freeout;
+               memset(&q->stats, 0, sizeof(q->stats));
+               s->rdma_ciq[i] = q->rspq.abs_id;
+       }
+
        for_each_port(adap, i) {
                /*
                 * Note that ->rdmarxq[i].rspq.cntxt_id below is 0 if we don't
@@ -2252,12 +2287,19 @@ static int get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
        else if (p->port_type == FW_PORT_TYPE_FIBER_XFI ||
                 p->port_type == FW_PORT_TYPE_FIBER_XAUI)
                cmd->port = PORT_FIBRE;
-       else if (p->port_type == FW_PORT_TYPE_SFP) {
-               if (p->mod_type == FW_PORT_MOD_TYPE_TWINAX_PASSIVE ||
-                   p->mod_type == FW_PORT_MOD_TYPE_TWINAX_ACTIVE)
+       else if (p->port_type == FW_PORT_TYPE_SFP ||
+                p->port_type == FW_PORT_TYPE_QSFP_10G ||
+                p->port_type == FW_PORT_TYPE_QSFP) {
+               if (p->mod_type == FW_PORT_MOD_TYPE_LR ||
+                   p->mod_type == FW_PORT_MOD_TYPE_SR ||
+                   p->mod_type == FW_PORT_MOD_TYPE_ER ||
+                   p->mod_type == FW_PORT_MOD_TYPE_LRM)
+                       cmd->port = PORT_FIBRE;
+               else if (p->mod_type == FW_PORT_MOD_TYPE_TWINAX_PASSIVE ||
+                        p->mod_type == FW_PORT_MOD_TYPE_TWINAX_ACTIVE)
                        cmd->port = PORT_DA;
                else
-                       cmd->port = PORT_FIBRE;
+                       cmd->port = PORT_OTHER;
        } else
                cmd->port = PORT_OTHER;
 
@@ -2461,8 +2503,7 @@ static unsigned int qtimer_val(const struct adapter *adap,
 }
 
 /**
- *     set_rxq_intr_params - set a queue's interrupt holdoff parameters
- *     @adap: the adapter
+ *     set_rspq_intr_params - set a queue's interrupt holdoff parameters
  *     @q: the Rx queue
  *     @us: the hold-off time in us, or 0 to disable timer
  *     @cnt: the hold-off packet count, or 0 to disable counter
@@ -2470,9 +2511,11 @@ static unsigned int qtimer_val(const struct adapter *adap,
  *     Sets an Rx queue's interrupt hold-off time and packet count.  At least
  *     one of the two needs to be enabled for the queue to generate interrupts.
  */
-static int set_rxq_intr_params(struct adapter *adap, struct sge_rspq *q,
-                              unsigned int us, unsigned int cnt)
+static int set_rspq_intr_params(struct sge_rspq *q,
+                               unsigned int us, unsigned int cnt)
 {
+       struct adapter *adap = q->adap;
+
        if ((us | cnt) == 0)
                cnt = 1;
 
@@ -2499,24 +2542,34 @@ static int set_rxq_intr_params(struct adapter *adap, struct sge_rspq *q,
        return 0;
 }
 
-static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
+/**
+ * set_rx_intr_params - set a net devices's RX interrupt holdoff paramete!
+ * @dev: the network device
+ * @us: the hold-off time in us, or 0 to disable timer
+ * @cnt: the hold-off packet count, or 0 to disable counter
+ *
+ * Set the RX interrupt hold-off parameters for a network device.
+ */
+static int set_rx_intr_params(struct net_device *dev,
+                             unsigned int us, unsigned int cnt)
 {
-       const struct port_info *pi = netdev_priv(dev);
+       int i, err;
+       struct port_info *pi = netdev_priv(dev);
        struct adapter *adap = pi->adapter;
-       struct sge_rspq *q;
-       int i;
-       int r = 0;
-
-       for (i = pi->first_qset; i < pi->first_qset + pi->nqsets; i++) {
-               q = &adap->sge.ethrxq[i].rspq;
-               r = set_rxq_intr_params(adap, q, c->rx_coalesce_usecs,
-                       c->rx_max_coalesced_frames);
-               if (r) {
-                       dev_err(&dev->dev, "failed to set coalesce %d\n", r);
-                       break;
-               }
+       struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset];
+
+       for (i = 0; i < pi->nqsets; i++, q++) {
+               err = set_rspq_intr_params(&q->rspq, us, cnt);
+               if (err)
+                       return err;
        }
-       return r;
+       return 0;
+}
+
+static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
+{
+       return set_rx_intr_params(dev, c->rx_coalesce_usecs,
+                                 c->rx_max_coalesced_frames);
 }
 
 static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
@@ -2732,7 +2785,7 @@ static u32 get_rss_table_size(struct net_device *dev)
        return pi->rss_size;
 }
 
-static int get_rss_table(struct net_device *dev, u32 *p)
+static int get_rss_table(struct net_device *dev, u32 *p, u8 *key)
 {
        const struct port_info *pi = netdev_priv(dev);
        unsigned int n = pi->rss_size;
@@ -2742,7 +2795,7 @@ static int get_rss_table(struct net_device *dev, u32 *p)
        return 0;
 }
 
-static int set_rss_table(struct net_device *dev, const u32 *p)
+static int set_rss_table(struct net_device *dev, const u32 *p, const u8 *key)
 {
        unsigned int i;
        struct port_info *pi = netdev_priv(dev);
@@ -2844,8 +2897,8 @@ static const struct ethtool_ops cxgb_ethtool_ops = {
        .set_wol           = set_wol,
        .get_rxnfc         = get_rxnfc,
        .get_rxfh_indir_size = get_rss_table_size,
-       .get_rxfh_indir    = get_rss_table,
-       .set_rxfh_indir    = set_rss_table,
+       .get_rxfh          = get_rss_table,
+       .set_rxfh          = set_rss_table,
        .flash_device      = set_flash,
 };
 
@@ -3385,6 +3438,77 @@ unsigned int cxgb4_best_mtu(const unsigned short *mtus, unsigned short mtu,
 }
 EXPORT_SYMBOL(cxgb4_best_mtu);
 
+/**
+ *     cxgb4_best_aligned_mtu - find best MTU, [hopefully] data size aligned
+ *     @mtus: the HW MTU table
+ *     @header_size: Header Size
+ *     @data_size_max: maximum Data Segment Size
+ *     @data_size_align: desired Data Segment Size Alignment (2^N)
+ *     @mtu_idxp: HW MTU Table Index return value pointer (possibly NULL)
+ *
+ *     Similar to cxgb4_best_mtu() but instead of searching the Hardware
+ *     MTU Table based solely on a Maximum MTU parameter, we break that
+ *     parameter up into a Header Size and Maximum Data Segment Size, and
+ *     provide a desired Data Segment Size Alignment.  If we find an MTU in
+ *     the Hardware MTU Table which will result in a Data Segment Size with
+ *     the requested alignment _and_ that MTU isn't "too far" from the
+ *     closest MTU, then we'll return that rather than the closest MTU.
+ */
+unsigned int cxgb4_best_aligned_mtu(const unsigned short *mtus,
+                                   unsigned short header_size,
+                                   unsigned short data_size_max,
+                                   unsigned short data_size_align,
+                                   unsigned int *mtu_idxp)
+{
+       unsigned short max_mtu = header_size + data_size_max;
+       unsigned short data_size_align_mask = data_size_align - 1;
+       int mtu_idx, aligned_mtu_idx;
+
+       /* Scan the MTU Table till we find an MTU which is larger than our
+        * Maximum MTU or we reach the end of the table.  Along the way,
+        * record the last MTU found, if any, which will result in a Data
+        * Segment Length matching the requested alignment.
+        */
+       for (mtu_idx = 0, aligned_mtu_idx = -1; mtu_idx < NMTUS; mtu_idx++) {
+               unsigned short data_size = mtus[mtu_idx] - header_size;
+
+               /* If this MTU minus the Header Size would result in a
+                * Data Segment Size of the desired alignment, remember it.
+                */
+               if ((data_size & data_size_align_mask) == 0)
+                       aligned_mtu_idx = mtu_idx;
+
+               /* If we're not at the end of the Hardware MTU Table and the
+                * next element is larger than our Maximum MTU, drop out of
+                * the loop.
+                */
+               if (mtu_idx+1 < NMTUS && mtus[mtu_idx+1] > max_mtu)
+                       break;
+       }
+
+       /* If we fell out of the loop because we ran to the end of the table,
+        * then we just have to use the last [largest] entry.
+        */
+       if (mtu_idx == NMTUS)
+               mtu_idx--;
+
+       /* If we found an MTU which resulted in the requested Data Segment
+        * Length alignment and that's "not far" from the largest MTU which is
+        * less than or equal to the maximum MTU, then use that.
+        */
+       if (aligned_mtu_idx >= 0 &&
+           mtu_idx - aligned_mtu_idx <= 1)
+               mtu_idx = aligned_mtu_idx;
+
+       /* If the caller has passed in an MTU Index pointer, pass the
+        * MTU Index back.  Return the MTU value.
+        */
+       if (mtu_idxp)
+               *mtu_idxp = mtu_idx;
+       return mtus[mtu_idx];
+}
+EXPORT_SYMBOL(cxgb4_best_aligned_mtu);
+
 /**
  *     cxgb4_port_chan - get the HW channel of a port
  *     @dev: the net device for the port
@@ -3782,7 +3906,9 @@ static void uld_attach(struct adapter *adap, unsigned int uld)
        lli.mtus = adap->params.mtus;
        if (uld == CXGB4_ULD_RDMA) {
                lli.rxq_ids = adap->sge.rdma_rxq;
+               lli.ciq_ids = adap->sge.rdma_ciq;
                lli.nrxq = adap->sge.rdmaqs;
+               lli.nciq = adap->sge.rdmaciqs;
        } else if (uld == CXGB4_ULD_ISCSI) {
                lli.rxq_ids = adap->sge.ofld_rxq;
                lli.nrxq = adap->sge.ofldqsets;
@@ -4061,7 +4187,7 @@ static int update_root_dev_clip(struct net_device *dev)
 
        /* Parse all bond and vlan devices layered on top of the physical dev */
        for (i = 0; i < VLAN_N_VID; i++) {
-               root_dev = __vlan_find_dev_deep(dev, htons(ETH_P_8021Q), i);
+               root_dev = __vlan_find_dev_deep_rcu(dev, htons(ETH_P_8021Q), i);
                if (!root_dev)
                        continue;
 
@@ -5528,13 +5654,41 @@ static int adap_init0(struct adapter *adap)
 #undef FW_PARAM_PFVF
 #undef FW_PARAM_DEV
 
-       /*
-        * These are finalized by FW initialization, load their values now.
+       /* The MTU/MSS Table is initialized by now, so load their values.  If
+        * we're initializing the adapter, then we'll make any modifications
+        * we want to the MTU/MSS Table and also initialize the congestion
+        * parameters.
         */
        t4_read_mtu_tbl(adap, adap->params.mtus, NULL);
-       t4_load_mtus(adap, adap->params.mtus, adap->params.a_wnd,
-                    adap->params.b_wnd);
+       if (state != DEV_STATE_INIT) {
+               int i;
+
+               /* The default MTU Table contains values 1492 and 1500.
+                * However, for TCP, it's better to have two values which are
+                * a multiple of 8 +/- 4 bytes apart near this popular MTU.
+                * This allows us to have a TCP Data Payload which is a
+                * multiple of 8 regardless of what combination of TCP Options
+                * are in use (always a multiple of 4 bytes) which is
+                * important for performance reasons.  For instance, if no
+                * options are in use, then we have a 20-byte IP header and a
+                * 20-byte TCP header.  In this case, a 1500-byte MSS would
+                * result in a TCP Data Payload of 1500 - 40 == 1460 bytes
+                * which is not a multiple of 8.  So using an MSS of 1488 in
+                * this case results in a TCP Data Payload of 1448 bytes which
+                * is a multiple of 8.  On the other hand, if 12-byte TCP Time
+                * Stamps have been negotiated, then an MTU of 1500 bytes
+                * results in a TCP Data Payload of 1448 bytes which, as
+                * above, is a multiple of 8 bytes ...
+                */
+               for (i = 0; i < NMTUS; i++)
+                       if (adap->params.mtus[i] == 1492) {
+                               adap->params.mtus[i] = 1488;
+                               break;
+                       }
 
+               t4_load_mtus(adap, adap->params.mtus, adap->params.a_wnd,
+                            adap->params.b_wnd);
+       }
        t4_init_tp_params(adap);
        adap->flags |= FW_OK;
        return 0;
@@ -5669,12 +5823,12 @@ static inline bool is_x_10g_port(const struct link_config *lc)
               (lc->supported & FW_PORT_CAP_SPEED_40G) != 0;
 }
 
-static inline void init_rspq(struct sge_rspq *q, u8 timer_idx, u8 pkt_cnt_idx,
+static inline void init_rspq(struct adapter *adap, struct sge_rspq *q,
+                            unsigned int us, unsigned int cnt,
                             unsigned int size, unsigned int iqe_size)
 {
-       q->intr_params = QINTR_TIMER_IDX(timer_idx) |
-                        (pkt_cnt_idx < SGE_NCOUNTERS ? QINTR_CNT_EN : 0);
-       q->pktcnt_idx = pkt_cnt_idx < SGE_NCOUNTERS ? pkt_cnt_idx : 0;
+       q->adap = adap;
+       set_rspq_intr_params(q, us, cnt);
        q->iqe_len = iqe_size;
        q->size = size;
 }
@@ -5688,6 +5842,7 @@ static void cfg_queues(struct adapter *adap)
 {
        struct sge *s = &adap->sge;
        int i, q10g = 0, n10g = 0, qidx = 0;
+       int ciq_size;
 
        for_each_port(adap, i)
                n10g += is_x_10g_port(&adap2pinfo(adap, i)->link_cfg);
@@ -5726,12 +5881,13 @@ static void cfg_queues(struct adapter *adap)
                        s->ofldqsets = adap->params.nports;
                /* For RDMA one Rx queue per channel suffices */
                s->rdmaqs = adap->params.nports;
+               s->rdmaciqs = adap->params.nports;
        }
 
        for (i = 0; i < ARRAY_SIZE(s->ethrxq); i++) {
                struct sge_eth_rxq *r = &s->ethrxq[i];
 
-               init_rspq(&r->rspq, 0, 0, 1024, 64);
+               init_rspq(adap, &r->rspq, 5, 10, 1024, 64);
                r->fl.size = 72;
        }
 
@@ -5747,7 +5903,7 @@ static void cfg_queues(struct adapter *adap)
        for (i = 0; i < ARRAY_SIZE(s->ofldrxq); i++) {
                struct sge_ofld_rxq *r = &s->ofldrxq[i];
 
-               init_rspq(&r->rspq, 0, 0, 1024, 64);
+               init_rspq(adap, &r->rspq, 5, 1, 1024, 64);
                r->rspq.uld = CXGB4_ULD_ISCSI;
                r->fl.size = 72;
        }
@@ -5755,13 +5911,26 @@ static void cfg_queues(struct adapter *adap)
        for (i = 0; i < ARRAY_SIZE(s->rdmarxq); i++) {
                struct sge_ofld_rxq *r = &s->rdmarxq[i];
 
-               init_rspq(&r->rspq, 0, 0, 511, 64);
+               init_rspq(adap, &r->rspq, 5, 1, 511, 64);
                r->rspq.uld = CXGB4_ULD_RDMA;
                r->fl.size = 72;
        }
 
-       init_rspq(&s->fw_evtq, 6, 0, 512, 64);
-       init_rspq(&s->intrq, 6, 0, 2 * MAX_INGQ, 64);
+       ciq_size = 64 + adap->vres.cq.size + adap->tids.nftids;
+       if (ciq_size > SGE_MAX_IQ_SIZE) {
+               CH_WARN(adap, "CIQ size too small for available IQs\n");
+               ciq_size = SGE_MAX_IQ_SIZE;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(s->rdmaciq); i++) {
+               struct sge_ofld_rxq *r = &s->rdmaciq[i];
+
+               init_rspq(adap, &r->rspq, 5, 1, ciq_size, 64);
+               r->rspq.uld = CXGB4_ULD_RDMA;
+       }
+
+       init_rspq(adap, &s->fw_evtq, 0, 1, 1024, 64);
+       init_rspq(adap, &s->intrq, 0, 1, 2 * MAX_INGQ, 64);
 }
 
 /*
@@ -5808,9 +5977,9 @@ static int enable_msix(struct adapter *adap)
 
        want = s->max_ethqsets + EXTRA_VECS;
        if (is_offload(adap)) {
-               want += s->rdmaqs + s->ofldqsets;
+               want += s->rdmaqs + s->rdmaciqs + s->ofldqsets;
                /* need nchan for each possible ULD */
-               ofld_need = 2 * nchan;
+               ofld_need = 3 * nchan;
        }
        need = adap->params.nports + EXTRA_VECS + ofld_need;
 
@@ -6076,7 +6245,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                netdev->priv_flags |= IFF_UNICAST_FLT;
 
                netdev->netdev_ops = &cxgb4_netdev_ops;
-               SET_ETHTOOL_OPS(netdev, &cxgb_ethtool_ops);
+               netdev->ethtool_ops = &cxgb_ethtool_ops;
        }
 
        pci_set_drvdata(pdev, adapter);
index e274a047528fca6ed11c9773db2d0a188e16da14..55e9daf7f9d47f95ab19a83b363637b4c549990f 100644 (file)
@@ -232,8 +232,10 @@ struct cxgb4_lld_info {
        const struct cxgb4_virt_res *vr;     /* assorted HW resources */
        const unsigned short *mtus;          /* MTU table */
        const unsigned short *rxq_ids;       /* the ULD's Rx queue ids */
+       const unsigned short *ciq_ids;       /* the ULD's concentrator IQ ids */
        unsigned short nrxq;                 /* # of Rx queues */
        unsigned short ntxq;                 /* # of Tx queues */
+       unsigned short nciq;                 /* # of concentrator IQ */
        unsigned char nchan:4;               /* # of channels */
        unsigned char nports:4;              /* # of ports */
        unsigned char wr_cred;               /* WR 16-byte credits */
@@ -274,6 +276,11 @@ unsigned int cxgb4_port_viid(const struct net_device *dev);
 unsigned int cxgb4_port_idx(const struct net_device *dev);
 unsigned int cxgb4_best_mtu(const unsigned short *mtus, unsigned short mtu,
                            unsigned int *idx);
+unsigned int cxgb4_best_aligned_mtu(const unsigned short *mtus,
+                                   unsigned short header_size,
+                                   unsigned short data_size_max,
+                                   unsigned short data_size_align,
+                                   unsigned int *mtu_idxp);
 void cxgb4_get_tcp_stats(struct pci_dev *pdev, struct tp_tcp_stats *v4,
                         struct tp_tcp_stats *v6);
 void cxgb4_iscsi_init(struct net_device *dev, unsigned int tag_mask,
index e249528c8e60bbce6bdc53c9e0c65a2f8c95d836..dd4355d248e4c4134313b637154ced4efdf3c96f 100644 (file)
@@ -1697,7 +1697,8 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp,
                return handle_trace_pkt(q->adap, si);
 
        pkt = (const struct cpl_rx_pkt *)rsp;
-       csum_ok = pkt->csum_calc && !pkt->err_vec;
+       csum_ok = pkt->csum_calc && !pkt->err_vec &&
+                 (q->netdev->features & NETIF_F_RXCSUM);
        if ((pkt->l2info & htonl(RXF_TCP)) &&
            (q->netdev->features & NETIF_F_GRO) && csum_ok && !pkt->ip_frag) {
                do_gro(rxq, si, pkt);
@@ -1720,8 +1721,7 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp,
 
        rxq->stats.pkts++;
 
-       if (csum_ok && (q->netdev->features & NETIF_F_RXCSUM) &&
-           (pkt->l2info & htonl(RXF_UDP | RXF_TCP))) {
+       if (csum_ok && (pkt->l2info & htonl(RXF_UDP | RXF_TCP))) {
                if (!pkt->ip_frag) {
                        skb->ip_summed = CHECKSUM_UNNECESSARY;
                        rxq->stats.rx_cso++;
@@ -2215,7 +2215,6 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq,
        iq->cntxt_id = ntohs(c.iqid);
        iq->abs_id = ntohs(c.physiqid);
        iq->size--;                           /* subtract status entry */
-       iq->adap = adap;
        iq->netdev = dev;
        iq->handler = hnd;
 
@@ -2515,6 +2514,10 @@ void t4_free_sge_resources(struct adapter *adap)
                if (oq->rspq.desc)
                        free_rspq_fl(adap, &oq->rspq, &oq->fl);
        }
+       for (i = 0, oq = adap->sge.rdmaciq; i < adap->sge.rdmaciqs; i++, oq++) {
+               if (oq->rspq.desc)
+                       free_rspq_fl(adap, &oq->rspq, &oq->fl);
+       }
 
        /* clean up offload Tx queues */
        for (i = 0; i < ARRAY_SIZE(adap->sge.ofldtxq); i++) {
index 1d1623be9f1e9814a19ee2b1859597c2cd8ebef5..71b799b5b0f499d050237c82788903b87dbb63bb 100644 (file)
@@ -68,6 +68,7 @@ enum {
        SGE_MAX_WR_LEN = 512,     /* max WR size in bytes */
        SGE_NTIMERS = 6,          /* # of interrupt holdoff timer values */
        SGE_NCOUNTERS = 4,        /* # of interrupt packet counter values */
+       SGE_MAX_IQ_SIZE = 65520,
 
        SGE_TIMER_RSTRT_CNTR = 6, /* restart RX packet threshold counter */
        SGE_TIMER_UPD_CIDX = 7,   /* update cidx only */
index f2738c7107898f2cf9112c74e33edc2f5ab16053..973eb11aa98a06dbcc66f343c96063954ae9b90a 100644 (file)
@@ -227,6 +227,7 @@ struct cpl_pass_open_req {
 #define DELACK(x)     ((x) << 5)
 #define ULP_MODE(x)   ((x) << 8)
 #define RCV_BUFSIZ(x) ((x) << 12)
+#define RCV_BUFSIZ_MASK 0x3FFU
 #define DSCP(x)       ((x) << 22)
 #define SMAC_SEL(x)   ((u64)(x) << 28)
 #define L2T_IDX(x)    ((u64)(x) << 36)
@@ -278,6 +279,15 @@ struct cpl_pass_accept_rpl {
        __be64 opt0;
 };
 
+struct cpl_t5_pass_accept_rpl {
+       WR_HDR;
+       union opcode_tid ot;
+       __be32 opt2;
+       __be64 opt0;
+       __be32 iss;
+       __be32 rsvd;
+};
+
 struct cpl_act_open_req {
        WR_HDR;
        union opcode_tid ot;
index 52859288de7b4d5b8c550c9a1e53fe5845225da0..ff1cdd1788b5f62efdf03ffd2f501b65054383a9 100644 (file)
@@ -2664,7 +2664,7 @@ static int cxgb4vf_pci_probe(struct pci_dev *pdev,
                netdev->priv_flags |= IFF_UNICAST_FLT;
 
                netdev->netdev_ops = &cxgb4vf_netdev_ops;
-               SET_ETHTOOL_OPS(netdev, &cxgb4vf_ethtool_ops);
+               netdev->ethtool_ops = &cxgb4vf_ethtool_ops;
 
                /*
                 * Initialize the hardware/software state for the port.
index 9d88c1d50b49ba23c2539de80b3309e08943d060..bdfa80ca5e317cee72c925a4112bdeaaf2551799 100644 (file)
@@ -1510,7 +1510,8 @@ int t4vf_ethrx_handler(struct sge_rspq *rspq, const __be64 *rsp,
 {
        struct sk_buff *skb;
        const struct cpl_rx_pkt *pkt = (void *)rsp;
-       bool csum_ok = pkt->csum_calc && !pkt->err_vec;
+       bool csum_ok = pkt->csum_calc && !pkt->err_vec &&
+                      (rspq->netdev->features & NETIF_F_RXCSUM);
        struct sge_eth_rxq *rxq = container_of(rspq, struct sge_eth_rxq, rspq);
 
        /*
@@ -1538,8 +1539,8 @@ int t4vf_ethrx_handler(struct sge_rspq *rspq, const __be64 *rsp,
        skb_record_rx_queue(skb, rspq->idx);
        rxq->stats.pkts++;
 
-       if (csum_ok && (rspq->netdev->features & NETIF_F_RXCSUM) &&
-           !pkt->err_vec && (be32_to_cpu(pkt->l2info) & (RXF_UDP|RXF_TCP))) {
+       if (csum_ok && !pkt->err_vec &&
+           (be32_to_cpu(pkt->l2info) & (RXF_UDP|RXF_TCP))) {
                if (!pkt->ip_frag)
                        skb->ip_summed = CHECKSUM_UNNECESSARY;
                else {
index e35c8e0202adda08dec6d53586bdd9f7da89a2e9..14f465f239d65d8c791618c3961b03cd6ef79fed 100644 (file)
@@ -43,6 +43,8 @@
 #define ENIC_CQ_MAX            (ENIC_WQ_MAX + ENIC_RQ_MAX)
 #define ENIC_INTR_MAX          (ENIC_CQ_MAX + 2)
 
+#define ENIC_AIC_LARGE_PKT_DIFF        3
+
 struct enic_msix_entry {
        int requested;
        char devname[IFNAMSIZ];
@@ -50,6 +52,33 @@ struct enic_msix_entry {
        void *devid;
 };
 
+/* Store only the lower range.  Higher range is given by fw. */
+struct enic_intr_mod_range {
+       u32 small_pkt_range_start;
+       u32 large_pkt_range_start;
+};
+
+struct enic_intr_mod_table {
+       u32 rx_rate;
+       u32 range_percent;
+};
+
+#define ENIC_MAX_LINK_SPEEDS           3
+#define ENIC_LINK_SPEED_10G            10000
+#define ENIC_LINK_SPEED_4G             4000
+#define ENIC_LINK_40G_INDEX            2
+#define ENIC_LINK_10G_INDEX            1
+#define ENIC_LINK_4G_INDEX             0
+#define ENIC_RX_COALESCE_RANGE_END     125
+#define ENIC_AIC_TS_BREAK              100
+
+struct enic_rx_coal {
+       u32 small_pkt_range_start;
+       u32 large_pkt_range_start;
+       u32 range_end;
+       u32 use_adaptive_rx_coalesce;
+};
+
 /* priv_flags */
 #define ENIC_SRIOV_ENABLED             (1 << 0)
 
@@ -85,13 +114,12 @@ struct enic {
        u32 msg_enable;
        spinlock_t devcmd_lock;
        u8 mac_addr[ETH_ALEN];
-       u8 mc_addr[ENIC_MULTICAST_PERFECT_FILTERS][ETH_ALEN];
-       u8 uc_addr[ENIC_UNICAST_PERFECT_FILTERS][ETH_ALEN];
        unsigned int flags;
        unsigned int priv_flags;
        unsigned int mc_count;
        unsigned int uc_count;
        u32 port_mtu;
+       struct enic_rx_coal rx_coalesce_setting;
        u32 rx_coalesce_usecs;
        u32 tx_coalesce_usecs;
 #ifdef CONFIG_PCI_IOV
index 4b6e5695b263995be898f7fbd6f44108063755d5..3e27df522847def5fd3e2c2634f8edbc98eccf49 100644 (file)
@@ -88,7 +88,7 @@ int enic_dev_packet_filter(struct enic *enic, int directed, int multicast,
        return err;
 }
 
-int enic_dev_add_addr(struct enic *enic, u8 *addr)
+int enic_dev_add_addr(struct enic *enic, const u8 *addr)
 {
        int err;
 
@@ -99,7 +99,7 @@ int enic_dev_add_addr(struct enic *enic, u8 *addr)
        return err;
 }
 
-int enic_dev_del_addr(struct enic *enic, u8 *addr)
+int enic_dev_del_addr(struct enic *enic, const u8 *addr)
 {
        int err;
 
index 129b14a4efb088ef4c83212090a4c5a4125bee1a..36ea1ab25f6aa374cfc8ef9c6db4b69112592cb0 100644 (file)
@@ -45,8 +45,8 @@ int enic_dev_add_station_addr(struct enic *enic);
 int enic_dev_del_station_addr(struct enic *enic);
 int enic_dev_packet_filter(struct enic *enic, int directed, int multicast,
        int broadcast, int promisc, int allmulti);
-int enic_dev_add_addr(struct enic *enic, u8 *addr);
-int enic_dev_del_addr(struct enic *enic, u8 *addr);
+int enic_dev_add_addr(struct enic *enic, const u8 *addr);
+int enic_dev_del_addr(struct enic *enic, const u8 *addr);
 int enic_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid);
 int enic_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid);
 int enic_dev_notify_unset(struct enic *enic);
index 47e3562f48667232ad16be0e57cda9c618fac430..2e50b5489d204158e89b93dfdb7be09cfba2d8c9 100644 (file)
@@ -79,6 +79,17 @@ static const struct enic_stat enic_rx_stats[] = {
 static const unsigned int enic_n_tx_stats = ARRAY_SIZE(enic_tx_stats);
 static const unsigned int enic_n_rx_stats = ARRAY_SIZE(enic_rx_stats);
 
+void enic_intr_coal_set_rx(struct enic *enic, u32 timer)
+{
+       int i;
+       int intr;
+
+       for (i = 0; i < enic->rq_count; i++) {
+               intr = enic_msix_rq_intr(enic, i);
+               vnic_intr_coalescing_timer_set(&enic->intr[intr], timer);
+       }
+}
+
 static int enic_get_settings(struct net_device *netdev,
        struct ethtool_cmd *ecmd)
 {
@@ -93,8 +104,8 @@ static int enic_get_settings(struct net_device *netdev,
                ethtool_cmd_speed_set(ecmd, vnic_dev_port_speed(enic->vdev));
                ecmd->duplex = DUPLEX_FULL;
        } else {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
 
        ecmd->autoneg = AUTONEG_DISABLE;
@@ -178,9 +189,14 @@ static int enic_get_coalesce(struct net_device *netdev,
        struct ethtool_coalesce *ecmd)
 {
        struct enic *enic = netdev_priv(netdev);
+       struct enic_rx_coal *rxcoal = &enic->rx_coalesce_setting;
 
        ecmd->tx_coalesce_usecs = enic->tx_coalesce_usecs;
        ecmd->rx_coalesce_usecs = enic->rx_coalesce_usecs;
+       if (rxcoal->use_adaptive_rx_coalesce)
+               ecmd->use_adaptive_rx_coalesce = 1;
+       ecmd->rx_coalesce_usecs_low = rxcoal->small_pkt_range_start;
+       ecmd->rx_coalesce_usecs_high = rxcoal->range_end;
 
        return 0;
 }
@@ -191,17 +207,31 @@ static int enic_set_coalesce(struct net_device *netdev,
        struct enic *enic = netdev_priv(netdev);
        u32 tx_coalesce_usecs;
        u32 rx_coalesce_usecs;
+       u32 rx_coalesce_usecs_low;
+       u32 rx_coalesce_usecs_high;
+       u32 coalesce_usecs_max;
        unsigned int i, intr;
+       struct enic_rx_coal *rxcoal = &enic->rx_coalesce_setting;
 
+       coalesce_usecs_max = vnic_dev_get_intr_coal_timer_max(enic->vdev);
        tx_coalesce_usecs = min_t(u32, ecmd->tx_coalesce_usecs,
-               vnic_dev_get_intr_coal_timer_max(enic->vdev));
+                                 coalesce_usecs_max);
        rx_coalesce_usecs = min_t(u32, ecmd->rx_coalesce_usecs,
-               vnic_dev_get_intr_coal_timer_max(enic->vdev));
+                                 coalesce_usecs_max);
+
+       rx_coalesce_usecs_low = min_t(u32, ecmd->rx_coalesce_usecs_low,
+                                     coalesce_usecs_max);
+       rx_coalesce_usecs_high = min_t(u32, ecmd->rx_coalesce_usecs_high,
+                                      coalesce_usecs_max);
 
        switch (vnic_dev_get_intr_mode(enic->vdev)) {
        case VNIC_DEV_INTR_MODE_INTX:
                if (tx_coalesce_usecs != rx_coalesce_usecs)
                        return -EINVAL;
+               if (ecmd->use_adaptive_rx_coalesce      ||
+                   ecmd->rx_coalesce_usecs_low         ||
+                   ecmd->rx_coalesce_usecs_high)
+                       return -EOPNOTSUPP;
 
                intr = enic_legacy_io_intr();
                vnic_intr_coalescing_timer_set(&enic->intr[intr],
@@ -210,6 +240,10 @@ static int enic_set_coalesce(struct net_device *netdev,
        case VNIC_DEV_INTR_MODE_MSI:
                if (tx_coalesce_usecs != rx_coalesce_usecs)
                        return -EINVAL;
+               if (ecmd->use_adaptive_rx_coalesce      ||
+                   ecmd->rx_coalesce_usecs_low         ||
+                   ecmd->rx_coalesce_usecs_high)
+                       return -EOPNOTSUPP;
 
                vnic_intr_coalescing_timer_set(&enic->intr[0],
                        tx_coalesce_usecs);
@@ -221,12 +255,27 @@ static int enic_set_coalesce(struct net_device *netdev,
                                tx_coalesce_usecs);
                }
 
-               for (i = 0; i < enic->rq_count; i++) {
-                       intr = enic_msix_rq_intr(enic, i);
-                       vnic_intr_coalescing_timer_set(&enic->intr[intr],
-                               rx_coalesce_usecs);
+               if (rxcoal->use_adaptive_rx_coalesce) {
+                       if (!ecmd->use_adaptive_rx_coalesce) {
+                               rxcoal->use_adaptive_rx_coalesce = 0;
+                               enic_intr_coal_set_rx(enic, rx_coalesce_usecs);
+                       }
+               } else {
+                       if (ecmd->use_adaptive_rx_coalesce)
+                               rxcoal->use_adaptive_rx_coalesce = 1;
+                       else
+                               enic_intr_coal_set_rx(enic, rx_coalesce_usecs);
                }
 
+               if (ecmd->rx_coalesce_usecs_high) {
+                       if (rx_coalesce_usecs_high <
+                           (rx_coalesce_usecs_low + ENIC_AIC_LARGE_PKT_DIFF))
+                               return -EINVAL;
+                       rxcoal->range_end = rx_coalesce_usecs_high;
+                       rxcoal->small_pkt_range_start = rx_coalesce_usecs_low;
+                       rxcoal->large_pkt_range_start = rx_coalesce_usecs_low +
+                                                       ENIC_AIC_LARGE_PKT_DIFF;
+               }
                break;
        default:
                break;
@@ -253,5 +302,5 @@ static const struct ethtool_ops enic_ethtool_ops = {
 
 void enic_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &enic_ethtool_ops);
+       netdev->ethtool_ops = &enic_ethtool_ops;
 }
index 2945718ce8068e4355628852ed200d539c9c2273..f32f828b7f3dc31490179210f86c72c5968d84f7 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/rtnetlink.h>
 #include <linux/prefetch.h>
 #include <net/ip6_checksum.h>
+#include <linux/ktime.h>
 
 #include "cq_enet_desc.h"
 #include "vnic_dev.h"
@@ -72,6 +73,35 @@ MODULE_LICENSE("GPL");
 MODULE_VERSION(DRV_VERSION);
 MODULE_DEVICE_TABLE(pci, enic_id_table);
 
+#define ENIC_LARGE_PKT_THRESHOLD               1000
+#define ENIC_MAX_COALESCE_TIMERS               10
+/*  Interrupt moderation table, which will be used to decide the
+ *  coalescing timer values
+ *  {rx_rate in Mbps, mapping percentage of the range}
+ */
+struct enic_intr_mod_table mod_table[ENIC_MAX_COALESCE_TIMERS + 1] = {
+       {4000,  0},
+       {4400, 10},
+       {5060, 20},
+       {5230, 30},
+       {5540, 40},
+       {5820, 50},
+       {6120, 60},
+       {6435, 70},
+       {6745, 80},
+       {7000, 90},
+       {0xFFFFFFFF, 100}
+};
+
+/* This table helps the driver to pick different ranges for rx coalescing
+ * timer depending on the link speed.
+ */
+struct enic_intr_mod_range mod_range[ENIC_MAX_LINK_SPEEDS] = {
+       {0,  0}, /* 0  - 4  Gbps */
+       {0,  3}, /* 4  - 10 Gbps */
+       {3,  6}, /* 10 - 40 Gbps */
+};
+
 int enic_is_dynamic(struct enic *enic)
 {
        return enic->pdev->device == PCI_DEVICE_ID_CISCO_VIC_ENET_DYN;
@@ -586,8 +616,71 @@ static struct rtnl_link_stats64 *enic_get_stats(struct net_device *netdev,
        return net_stats;
 }
 
+static int enic_mc_sync(struct net_device *netdev, const u8 *mc_addr)
+{
+       struct enic *enic = netdev_priv(netdev);
+
+       if (enic->mc_count == ENIC_MULTICAST_PERFECT_FILTERS) {
+               unsigned int mc_count = netdev_mc_count(netdev);
+
+               netdev_warn(netdev, "Registering only %d out of %d multicast addresses\n",
+                           ENIC_MULTICAST_PERFECT_FILTERS, mc_count);
+
+               return -ENOSPC;
+       }
+
+       enic_dev_add_addr(enic, mc_addr);
+       enic->mc_count++;
+
+       return 0;
+}
+
+static int enic_mc_unsync(struct net_device *netdev, const u8 *mc_addr)
+{
+       struct enic *enic = netdev_priv(netdev);
+
+       enic_dev_del_addr(enic, mc_addr);
+       enic->mc_count--;
+
+       return 0;
+}
+
+static int enic_uc_sync(struct net_device *netdev, const u8 *uc_addr)
+{
+       struct enic *enic = netdev_priv(netdev);
+
+       if (enic->uc_count == ENIC_UNICAST_PERFECT_FILTERS) {
+               unsigned int uc_count = netdev_uc_count(netdev);
+
+               netdev_warn(netdev, "Registering only %d out of %d unicast addresses\n",
+                           ENIC_UNICAST_PERFECT_FILTERS, uc_count);
+
+               return -ENOSPC;
+       }
+
+       enic_dev_add_addr(enic, uc_addr);
+       enic->uc_count++;
+
+       return 0;
+}
+
+static int enic_uc_unsync(struct net_device *netdev, const u8 *uc_addr)
+{
+       struct enic *enic = netdev_priv(netdev);
+
+       enic_dev_del_addr(enic, uc_addr);
+       enic->uc_count--;
+
+       return 0;
+}
+
 void enic_reset_addr_lists(struct enic *enic)
 {
+       struct net_device *netdev = enic->netdev;
+
+       __dev_uc_unsync(netdev, NULL);
+       __dev_mc_unsync(netdev, NULL);
+
        enic->mc_count = 0;
        enic->uc_count = 0;
        enic->flags = 0;
@@ -654,112 +747,6 @@ static int enic_set_mac_address(struct net_device *netdev, void *p)
        return enic_dev_add_station_addr(enic);
 }
 
-static void enic_update_multicast_addr_list(struct enic *enic)
-{
-       struct net_device *netdev = enic->netdev;
-       struct netdev_hw_addr *ha;
-       unsigned int mc_count = netdev_mc_count(netdev);
-       u8 mc_addr[ENIC_MULTICAST_PERFECT_FILTERS][ETH_ALEN];
-       unsigned int i, j;
-
-       if (mc_count > ENIC_MULTICAST_PERFECT_FILTERS) {
-               netdev_warn(netdev, "Registering only %d out of %d "
-                       "multicast addresses\n",
-                       ENIC_MULTICAST_PERFECT_FILTERS, mc_count);
-               mc_count = ENIC_MULTICAST_PERFECT_FILTERS;
-       }
-
-       /* Is there an easier way?  Trying to minimize to
-        * calls to add/del multicast addrs.  We keep the
-        * addrs from the last call in enic->mc_addr and
-        * look for changes to add/del.
-        */
-
-       i = 0;
-       netdev_for_each_mc_addr(ha, netdev) {
-               if (i == mc_count)
-                       break;
-               memcpy(mc_addr[i++], ha->addr, ETH_ALEN);
-       }
-
-       for (i = 0; i < enic->mc_count; i++) {
-               for (j = 0; j < mc_count; j++)
-                       if (ether_addr_equal(enic->mc_addr[i], mc_addr[j]))
-                               break;
-               if (j == mc_count)
-                       enic_dev_del_addr(enic, enic->mc_addr[i]);
-       }
-
-       for (i = 0; i < mc_count; i++) {
-               for (j = 0; j < enic->mc_count; j++)
-                       if (ether_addr_equal(mc_addr[i], enic->mc_addr[j]))
-                               break;
-               if (j == enic->mc_count)
-                       enic_dev_add_addr(enic, mc_addr[i]);
-       }
-
-       /* Save the list to compare against next time
-        */
-
-       for (i = 0; i < mc_count; i++)
-               memcpy(enic->mc_addr[i], mc_addr[i], ETH_ALEN);
-
-       enic->mc_count = mc_count;
-}
-
-static void enic_update_unicast_addr_list(struct enic *enic)
-{
-       struct net_device *netdev = enic->netdev;
-       struct netdev_hw_addr *ha;
-       unsigned int uc_count = netdev_uc_count(netdev);
-       u8 uc_addr[ENIC_UNICAST_PERFECT_FILTERS][ETH_ALEN];
-       unsigned int i, j;
-
-       if (uc_count > ENIC_UNICAST_PERFECT_FILTERS) {
-               netdev_warn(netdev, "Registering only %d out of %d "
-                       "unicast addresses\n",
-                       ENIC_UNICAST_PERFECT_FILTERS, uc_count);
-               uc_count = ENIC_UNICAST_PERFECT_FILTERS;
-       }
-
-       /* Is there an easier way?  Trying to minimize to
-        * calls to add/del unicast addrs.  We keep the
-        * addrs from the last call in enic->uc_addr and
-        * look for changes to add/del.
-        */
-
-       i = 0;
-       netdev_for_each_uc_addr(ha, netdev) {
-               if (i == uc_count)
-                       break;
-               memcpy(uc_addr[i++], ha->addr, ETH_ALEN);
-       }
-
-       for (i = 0; i < enic->uc_count; i++) {
-               for (j = 0; j < uc_count; j++)
-                       if (ether_addr_equal(enic->uc_addr[i], uc_addr[j]))
-                               break;
-               if (j == uc_count)
-                       enic_dev_del_addr(enic, enic->uc_addr[i]);
-       }
-
-       for (i = 0; i < uc_count; i++) {
-               for (j = 0; j < enic->uc_count; j++)
-                       if (ether_addr_equal(uc_addr[i], enic->uc_addr[j]))
-                               break;
-               if (j == enic->uc_count)
-                       enic_dev_add_addr(enic, uc_addr[i]);
-       }
-
-       /* Save the list to compare against next time
-        */
-
-       for (i = 0; i < uc_count; i++)
-               memcpy(enic->uc_addr[i], uc_addr[i], ETH_ALEN);
-
-       enic->uc_count = uc_count;
-}
-
 /* netif_tx_lock held, BHs disabled */
 static void enic_set_rx_mode(struct net_device *netdev)
 {
@@ -782,9 +769,9 @@ static void enic_set_rx_mode(struct net_device *netdev)
        }
 
        if (!promisc) {
-               enic_update_unicast_addr_list(enic);
+               __dev_uc_sync(netdev, enic_uc_sync, enic_uc_unsync);
                if (!allmulti)
-                       enic_update_multicast_addr_list(enic);
+                       __dev_mc_sync(netdev, enic_mc_sync, enic_mc_unsync);
        }
 }
 
@@ -979,6 +966,15 @@ static int enic_rq_alloc_buf(struct vnic_rq *rq)
        return 0;
 }
 
+static void enic_intr_update_pkt_size(struct vnic_rx_bytes_counter *pkt_size,
+                                     u32 pkt_len)
+{
+       if (ENIC_LARGE_PKT_THRESHOLD <= pkt_len)
+               pkt_size->large_pkt_bytes_cnt += pkt_len;
+       else
+               pkt_size->small_pkt_bytes_cnt += pkt_len;
+}
+
 static void enic_rq_indicate_buf(struct vnic_rq *rq,
        struct cq_desc *cq_desc, struct vnic_rq_buf *buf,
        int skipped, void *opaque)
@@ -986,6 +982,7 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq,
        struct enic *enic = vnic_dev_priv(rq->vdev);
        struct net_device *netdev = enic->netdev;
        struct sk_buff *skb;
+       struct vnic_cq *cq = &enic->cq[enic_cq_rq(enic, rq->index)];
 
        u8 type, color, eop, sop, ingress_port, vlan_stripped;
        u8 fcoe, fcoe_sof, fcoe_fc_crc_ok, fcoe_enc_error, fcoe_eof;
@@ -1056,6 +1053,9 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq,
                        napi_gro_receive(&enic->napi[q_number], skb);
                else
                        netif_receive_skb(skb);
+               if (enic->rx_coalesce_setting.use_adaptive_rx_coalesce)
+                       enic_intr_update_pkt_size(&cq->pkt_size_counter,
+                                                 bytes_written);
        } else {
 
                /* Buffer overflow
@@ -1134,6 +1134,64 @@ static int enic_poll(struct napi_struct *napi, int budget)
        return rq_work_done;
 }
 
+static void enic_set_int_moderation(struct enic *enic, struct vnic_rq *rq)
+{
+       unsigned int intr = enic_msix_rq_intr(enic, rq->index);
+       struct vnic_cq *cq = &enic->cq[enic_cq_rq(enic, rq->index)];
+       u32 timer = cq->tobe_rx_coal_timeval;
+
+       if (cq->tobe_rx_coal_timeval != cq->cur_rx_coal_timeval) {
+               vnic_intr_coalescing_timer_set(&enic->intr[intr], timer);
+               cq->cur_rx_coal_timeval = cq->tobe_rx_coal_timeval;
+       }
+}
+
+static void enic_calc_int_moderation(struct enic *enic, struct vnic_rq *rq)
+{
+       struct enic_rx_coal *rx_coal = &enic->rx_coalesce_setting;
+       struct vnic_cq *cq = &enic->cq[enic_cq_rq(enic, rq->index)];
+       struct vnic_rx_bytes_counter *pkt_size_counter = &cq->pkt_size_counter;
+       int index;
+       u32 timer;
+       u32 range_start;
+       u32 traffic;
+       u64 delta;
+       ktime_t now = ktime_get();
+
+       delta = ktime_us_delta(now, cq->prev_ts);
+       if (delta < ENIC_AIC_TS_BREAK)
+               return;
+       cq->prev_ts = now;
+
+       traffic = pkt_size_counter->large_pkt_bytes_cnt +
+                 pkt_size_counter->small_pkt_bytes_cnt;
+       /* The table takes Mbps
+        * traffic *= 8    => bits
+        * traffic *= (10^6 / delta)    => bps
+        * traffic /= 10^6     => Mbps
+        *
+        * Combining, traffic *= (8 / delta)
+        */
+
+       traffic <<= 3;
+       traffic = delta > UINT_MAX ? 0 : traffic / (u32)delta;
+
+       for (index = 0; index < ENIC_MAX_COALESCE_TIMERS; index++)
+               if (traffic < mod_table[index].rx_rate)
+                       break;
+       range_start = (pkt_size_counter->small_pkt_bytes_cnt >
+                      pkt_size_counter->large_pkt_bytes_cnt << 1) ?
+                     rx_coal->small_pkt_range_start :
+                     rx_coal->large_pkt_range_start;
+       timer = range_start + ((rx_coal->range_end - range_start) *
+                              mod_table[index].range_percent / 100);
+       /* Damping */
+       cq->tobe_rx_coal_timeval = (timer + cq->tobe_rx_coal_timeval) >> 1;
+
+       pkt_size_counter->large_pkt_bytes_cnt = 0;
+       pkt_size_counter->small_pkt_bytes_cnt = 0;
+}
+
 static int enic_poll_msix(struct napi_struct *napi, int budget)
 {
        struct net_device *netdev = napi->dev;
@@ -1171,6 +1229,13 @@ static int enic_poll_msix(struct napi_struct *napi, int budget)
 
        if (err)
                work_done = work_to_do;
+       if (enic->rx_coalesce_setting.use_adaptive_rx_coalesce)
+               /* Call the function which refreshes
+                * the intr coalescing timer value based on
+                * the traffic.  This is supported only in
+                * the case of MSI-x mode
+                */
+               enic_calc_int_moderation(enic, &enic->rq[rq]);
 
        if (work_done < work_to_do) {
 
@@ -1179,6 +1244,8 @@ static int enic_poll_msix(struct napi_struct *napi, int budget)
                 */
 
                napi_complete(napi);
+               if (enic->rx_coalesce_setting.use_adaptive_rx_coalesce)
+                       enic_set_int_moderation(enic, &enic->rq[rq]);
                vnic_intr_unmask(&enic->intr[intr]);
        }
 
@@ -1314,6 +1381,42 @@ static void enic_synchronize_irqs(struct enic *enic)
        }
 }
 
+static void enic_set_rx_coal_setting(struct enic *enic)
+{
+       unsigned int speed;
+       int index = -1;
+       struct enic_rx_coal *rx_coal = &enic->rx_coalesce_setting;
+
+       /* If intr mode is not MSIX, do not do adaptive coalescing */
+       if (VNIC_DEV_INTR_MODE_MSIX != vnic_dev_get_intr_mode(enic->vdev)) {
+               netdev_info(enic->netdev, "INTR mode is not MSIX, Not initializing adaptive coalescing");
+               return;
+       }
+
+       /* 1. Read the link speed from fw
+        * 2. Pick the default range for the speed
+        * 3. Update it in enic->rx_coalesce_setting
+        */
+       speed = vnic_dev_port_speed(enic->vdev);
+       if (ENIC_LINK_SPEED_10G < speed)
+               index = ENIC_LINK_40G_INDEX;
+       else if (ENIC_LINK_SPEED_4G < speed)
+               index = ENIC_LINK_10G_INDEX;
+       else
+               index = ENIC_LINK_4G_INDEX;
+
+       rx_coal->small_pkt_range_start = mod_range[index].small_pkt_range_start;
+       rx_coal->large_pkt_range_start = mod_range[index].large_pkt_range_start;
+       rx_coal->range_end = ENIC_RX_COALESCE_RANGE_END;
+
+       /* Start with the value provided by UCSM */
+       for (index = 0; index < enic->rq_count; index++)
+               enic->cq[index].cur_rx_coal_timeval =
+                               enic->config.intr_timer_usec;
+
+       rx_coal->use_adaptive_rx_coalesce = 1;
+}
+
 static int enic_dev_notify_set(struct enic *enic)
 {
        int err;
@@ -2231,6 +2334,7 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        enic->notify_timer.function = enic_notify_timer;
        enic->notify_timer.data = (unsigned long)enic;
 
+       enic_set_rx_coal_setting(enic);
        INIT_WORK(&enic->reset, enic_reset);
        INIT_WORK(&enic->change_mtu_work, enic_change_mtu_work);
 
@@ -2250,6 +2354,9 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        }
 
        enic->tx_coalesce_usecs = enic->config.intr_timer_usec;
+       /* rx coalesce time already got initialized. This gets used
+        * if adaptive coal is turned off
+        */
        enic->rx_coalesce_usecs = enic->tx_coalesce_usecs;
 
        if (enic_is_dynamic(enic) || enic_is_sriov_vf(enic))
index 579315cbe803c91130c978b5e21538240aff3018..4e6aa65857f70a0b7236decf382c3018c137517c 100644 (file)
@@ -50,6 +50,11 @@ struct vnic_cq_ctrl {
        u32 pad10;
 };
 
+struct vnic_rx_bytes_counter {
+       unsigned int small_pkt_bytes_cnt;
+       unsigned int large_pkt_bytes_cnt;
+};
+
 struct vnic_cq {
        unsigned int index;
        struct vnic_dev *vdev;
@@ -58,6 +63,10 @@ struct vnic_cq {
        unsigned int to_clean;
        unsigned int last_color;
        unsigned int interrupt_offset;
+       struct vnic_rx_bytes_counter pkt_size_counter;
+       unsigned int cur_rx_coal_timeval;
+       unsigned int tobe_rx_coal_timeval;
+       ktime_t prev_ts;
 };
 
 static inline unsigned int vnic_cq_service(struct vnic_cq *cq,
index 69dd92598b7e672ebc56a45dc1d16efc69c9929f..e86a45cb9e682980b25e5f6ebfe1772afb3a377b 100644 (file)
@@ -657,7 +657,7 @@ int vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast,
        return err;
 }
 
-int vnic_dev_add_addr(struct vnic_dev *vdev, u8 *addr)
+int vnic_dev_add_addr(struct vnic_dev *vdev, const u8 *addr)
 {
        u64 a0 = 0, a1 = 0;
        int wait = 1000;
@@ -674,7 +674,7 @@ int vnic_dev_add_addr(struct vnic_dev *vdev, u8 *addr)
        return err;
 }
 
-int vnic_dev_del_addr(struct vnic_dev *vdev, u8 *addr)
+int vnic_dev_del_addr(struct vnic_dev *vdev, const u8 *addr)
 {
        u64 a0 = 0, a1 = 0;
        int wait = 1000;
index e670029862a1bc900ca3868d12cf5436a55346d1..1f3b301f822517d89ea881014b56152d3110c918 100644 (file)
@@ -95,8 +95,8 @@ int vnic_dev_stats_dump(struct vnic_dev *vdev, struct vnic_stats **stats);
 int vnic_dev_hang_notify(struct vnic_dev *vdev);
 int vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast,
        int broadcast, int promisc, int allmulti);
-int vnic_dev_add_addr(struct vnic_dev *vdev, u8 *addr);
-int vnic_dev_del_addr(struct vnic_dev *vdev, u8 *addr);
+int vnic_dev_add_addr(struct vnic_dev *vdev, const u8 *addr);
+int vnic_dev_del_addr(struct vnic_dev *vdev, const u8 *addr);
 int vnic_dev_get_mac_addr(struct vnic_dev *vdev, u8 *mac_addr);
 int vnic_dev_notify_set(struct vnic_dev *vdev, u16 intr);
 int vnic_dev_notify_unset(struct vnic_dev *vdev);
index 8c4b93be333bc704a736fa8333977f7b76800aad..13723c96d1a272896dffac2f95a1c0b18204674e 100644 (file)
@@ -109,6 +109,7 @@ typedef struct board_info {
        u8              imr_all;
 
        unsigned int    flags;
+       unsigned int    in_timeout:1;
        unsigned int    in_suspend:1;
        unsigned int    wake_supported:1;
 
@@ -187,13 +188,13 @@ dm9000_reset(board_info_t *db)
         * 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);
+       iow(db, DM9000_NCR, NCR_RST | NCR_MAC_LBK);
        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);
+       iow(db, DM9000_NCR, NCR_RST | NCR_MAC_LBK);
        udelay(100);
        if (ior(db, DM9000_NCR) & 1)
                dev_err(db->dev, "dm9000 did not respond to second reset\n");
@@ -273,7 +274,7 @@ static void dm9000_dumpblk_32bit(void __iomem *reg, int count)
  */
 static void dm9000_msleep(board_info_t *db, unsigned int ms)
 {
-       if (db->in_suspend)
+       if (db->in_suspend || db->in_timeout)
                mdelay(ms);
        else
                msleep(ms);
@@ -334,7 +335,8 @@ dm9000_phy_write(struct net_device *dev,
        unsigned long reg_save;
 
        dm9000_dbg(db, 5, "phy_write[%02x] = %04x\n", reg, value);
-       mutex_lock(&db->addr_lock);
+       if (!db->in_timeout)
+               mutex_lock(&db->addr_lock);
 
        spin_lock_irqsave(&db->lock, flags);
 
@@ -365,7 +367,8 @@ dm9000_phy_write(struct net_device *dev,
        writeb(reg_save, db->io_addr);
 
        spin_unlock_irqrestore(&db->lock, flags);
-       mutex_unlock(&db->addr_lock);
+       if (!db->in_timeout)
+               mutex_unlock(&db->addr_lock);
 }
 
 /* dm9000_set_io
@@ -882,6 +885,18 @@ dm9000_hash_table(struct net_device *dev)
        spin_unlock_irqrestore(&db->lock, flags);
 }
 
+static void
+dm9000_mask_interrupts(board_info_t *db)
+{
+       iow(db, DM9000_IMR, IMR_PAR);
+}
+
+static void
+dm9000_unmask_interrupts(board_info_t *db)
+{
+       iow(db, DM9000_IMR, db->imr_all);
+}
+
 /*
  * Initialize dm9000 board
  */
@@ -894,6 +909,9 @@ dm9000_init_dm9000(struct net_device *dev)
 
        dm9000_dbg(db, 1, "entering %s\n", __func__);
 
+       dm9000_reset(db);
+       dm9000_mask_interrupts(db);
+
        /* I/O mode */
        db->io_mode = ior(db, DM9000_ISR) >> 6; /* ISR bit7:6 keeps I/O mode */
 
@@ -941,9 +959,6 @@ dm9000_init_dm9000(struct net_device *dev)
 
        db->imr_all = imr;
 
-       /* Enable TX/RX interrupt mask */
-       iow(db, DM9000_IMR, imr);
-
        /* Init Driver variable */
        db->tx_pkt_cnt = 0;
        db->queue_pkt_len = 0;
@@ -959,17 +974,19 @@ static void dm9000_timeout(struct net_device *dev)
 
        /* Save previous register address */
        spin_lock_irqsave(&db->lock, flags);
+       db->in_timeout = 1;
        reg_save = readb(db->io_addr);
 
        netif_stop_queue(dev);
-       dm9000_reset(db);
        dm9000_init_dm9000(dev);
+       dm9000_unmask_interrupts(db);
        /* We can accept TX packets again */
        dev->trans_start = jiffies; /* prevent tx timeout */
        netif_wake_queue(dev);
 
        /* Restore previous register address */
        writeb(reg_save, db->io_addr);
+       db->in_timeout = 0;
        spin_unlock_irqrestore(&db->lock, flags);
 }
 
@@ -1093,7 +1110,6 @@ dm9000_rx(struct net_device *dev)
                if (rxbyte & DM9000_PKT_ERR) {
                        dev_warn(db->dev, "status check fail: %d\n", rxbyte);
                        iow(db, DM9000_RCR, 0x00);      /* Stop Device */
-                       iow(db, DM9000_ISR, IMR_PAR);   /* Stop INT request */
                        return;
                }
 
@@ -1193,9 +1209,7 @@ static irqreturn_t dm9000_interrupt(int irq, void *dev_id)
        /* Save previous register address */
        reg_save = readb(db->io_addr);
 
-       /* Disable all interrupts */
-       iow(db, DM9000_IMR, IMR_PAR);
-
+       dm9000_mask_interrupts(db);
        /* Got DM9000 interrupt status */
        int_status = ior(db, DM9000_ISR);       /* Got ISR */
        iow(db, DM9000_ISR, int_status);        /* Clear ISR status */
@@ -1218,9 +1232,7 @@ static irqreturn_t dm9000_interrupt(int irq, void *dev_id)
                }
        }
 
-       /* Re-enable interrupt mask */
-       iow(db, DM9000_IMR, db->imr_all);
-
+       dm9000_unmask_interrupts(db);
        /* Restore previous register address */
        writeb(reg_save, db->io_addr);
 
@@ -1291,6 +1303,9 @@ dm9000_open(struct net_device *dev)
        /* If there is no IRQ type specified, default to something that
         * may work, and tell the user that this is a problem */
 
+       if (irqflags == IRQF_TRIGGER_NONE)
+               irqflags = irq_get_trigger_type(dev->irq);
+
        if (irqflags == IRQF_TRIGGER_NONE)
                dev_warn(db->dev, "WARNING: no IRQ resource flags set.\n");
 
@@ -1301,11 +1316,14 @@ dm9000_open(struct net_device *dev)
        mdelay(1); /* delay needs by DM9000B */
 
        /* Initialize DM9000 board */
-       dm9000_reset(db);
        dm9000_init_dm9000(dev);
 
        if (request_irq(dev->irq, dm9000_interrupt, irqflags, dev->name, dev))
                return -EAGAIN;
+       /* Now that we have an interrupt handler hooked up we can unmask
+        * our interrupts
+        */
+       dm9000_unmask_interrupts(db);
 
        /* Init driver variable */
        db->dbug_cnt = 0;
@@ -1313,7 +1331,8 @@ dm9000_open(struct net_device *dev)
        mii_check_media(&db->mii, netif_msg_link(db), 1);
        netif_start_queue(dev);
 
-       dm9000_schedule_poll(db);
+       /* Poll initial link status */
+       schedule_delayed_work(&db->phy_poll, 1);
 
        return 0;
 }
@@ -1326,7 +1345,7 @@ dm9000_shutdown(struct net_device *dev)
        /* RESET device */
        dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET); /* PHY RESET */
        iow(db, DM9000_GPR, 0x01);      /* Power-Down PHY */
-       iow(db, DM9000_IMR, IMR_PAR);   /* Disable all interrupt */
+       dm9000_mask_interrupts(db);
        iow(db, DM9000_RCR, 0x00);      /* Disable RX */
 }
 
@@ -1547,12 +1566,7 @@ dm9000_probe(struct platform_device *pdev)
        db->flags |= DM9000_PLATF_SIMPLE_PHY;
 #endif
 
-       /* Fixing bug on dm9000_probe, takeover dm9000_reset(db),
-        * Need 'NCR_MAC_LBK' bit to indeed stable our DM9000 fifo
-        * while probe stage.
-        */
-
-       iow(db, DM9000_NCR, NCR_MAC_LBK | NCR_RST);
+       dm9000_reset(db);
 
        /* try multiple times, DM9000 sometimes gets the read wrong */
        for (i = 0; i < 8; i++) {
@@ -1695,8 +1709,8 @@ dm9000_drv_resume(struct device *dev)
                        /* reset if we were not in wake mode to ensure if
                         * the device was powered off it is in a known state */
                        if (!db->wake_state) {
-                               dm9000_reset(db);
                                dm9000_init_dm9000(ndev);
+                               dm9000_unmask_interrupts(db);
                        }
 
                        netif_device_attach(ndev);
index 1642de78aac84c86b51cbae27c16d9748d70fb19..861660841ce281c23a5790e62942a779998b5209 100644 (file)
@@ -1703,7 +1703,7 @@ static int tulip_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 #ifdef CONFIG_TULIP_NAPI
        netif_napi_add(dev, &tp->napi, tulip_poll, 16);
 #endif
-       SET_ETHTOOL_OPS(dev, &ops);
+       dev->ethtool_ops = &ops;
 
        if (register_netdev(dev))
                goto err_out_free_ring;
index aa801a6af7b9904752c7f3acebc2d01940527128..80afec335a117fcee3645a8d8198b5f9c7df08d6 100644 (file)
@@ -962,8 +962,8 @@ ULi_ethtool_gset(struct uli526x_board_info *db, struct ethtool_cmd *ecmd)
        }
        if(db->link_failed)
        {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
 
        if (db->media_mode & ULI526X_AUTO)
index 4fb756d219f700bfb82a37d62e9cb078fca22024..1274b6fdac8aace34559bb1fd1cae3ccca314a2e 100644 (file)
@@ -227,7 +227,7 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent)
        }
        dev->netdev_ops = &netdev_ops;
        dev->watchdog_timeo = TX_TIMEOUT;
-       SET_ETHTOOL_OPS(dev, &ethtool_ops);
+       dev->ethtool_ops = &ethtool_ops;
 #if 0
        dev->features = NETIF_F_IP_CSUM;
 #endif
@@ -1185,8 +1185,8 @@ static int rio_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
                ethtool_cmd_speed_set(cmd, np->speed);
                cmd->duplex = np->full_duplex ? DUPLEX_FULL : DUPLEX_HALF;
        } else {
-               ethtool_cmd_speed_set(cmd, -1);
-               cmd->duplex = -1;
+               ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
+               cmd->duplex = DUPLEX_UNKNOWN;
        }
        if ( np->an_enable)
                cmd->autoneg = AUTONEG_ENABLE;
index d9e5ca0d48c125c88e55b319975fcab5e0a4ad99..433c1e18544250bd76dddb3e1e243d8b90ce3b73 100644 (file)
@@ -577,7 +577,7 @@ static int sundance_probe1(struct pci_dev *pdev,
 
        /* The chip-specific entries in the device structure. */
        dev->netdev_ops = &netdev_ops;
-       SET_ETHTOOL_OPS(dev, &ethtool_ops);
+       dev->ethtool_ops = &ethtool_ops;
        dev->watchdog_timeo = TX_TIMEOUT;
 
        pci_set_drvdata(pdev, dev);
index 4884205e56eef8b938e43b0d87307dc02b50d339..056b44b934773cb0084d5126dd7946d08bb24c46 100644 (file)
@@ -134,17 +134,17 @@ struct ec_bhf_priv {
 
        struct pci_dev *dev;
 
-       void * __iomem io;
-       void * __iomem dma_io;
+       void __iomem *io;
+       void __iomem *dma_io;
 
        struct hrtimer hrtimer;
 
        int tx_dma_chan;
        int rx_dma_chan;
-       void * __iomem ec_io;
-       void * __iomem fifo_io;
-       void * __iomem mii_io;
-       void * __iomem mac_io;
+       void __iomem *ec_io;
+       void __iomem *fifo_io;
+       void __iomem *mii_io;
+       void __iomem *mac_io;
 
        struct bhf_dma rx_buf;
        struct rx_desc *rx_descs;
@@ -297,7 +297,7 @@ static int ec_bhf_setup_offsets(struct ec_bhf_priv *priv)
 {
        struct device *dev = PRIV_TO_DEV(priv);
        unsigned block_count, i;
-       void * __iomem ec_info;
+       void __iomem *ec_info;
 
        dev_dbg(dev, "Info block:\n");
        dev_dbg(dev, "Type of function: %x\n", (unsigned)ioread16(priv->io));
@@ -569,8 +569,8 @@ static int ec_bhf_probe(struct pci_dev *dev, const struct pci_device_id *id)
 {
        struct net_device *net_dev;
        struct ec_bhf_priv *priv;
-       void * __iomem dma_io;
-       void * __iomem io;
+       void __iomem *dma_io;
+       void __iomem *io;
        int err = 0;
 
        err = pci_enable_device(dev);
@@ -615,7 +615,7 @@ static int ec_bhf_probe(struct pci_dev *dev, const struct pci_device_id *id)
        }
 
        net_dev = alloc_etherdev(sizeof(struct ec_bhf_priv));
-       if (net_dev == 0) {
+       if (net_dev == NULL) {
                err = -ENOMEM;
                goto err_unmap_dma_io;
        }
index 97db5a7179df1c6c733bed5e25e5be3d44d39fa1..2e7c5553955e739ba717568325a2c1be67a15c47 100644 (file)
@@ -120,6 +120,9 @@ static inline char *nic_name(struct pci_dev *pdev)
 #define MAX_VFS                        30 /* Max VFs supported by BE3 FW */
 #define FW_VER_LEN             32
 
+#define        RSS_INDIR_TABLE_LEN     128
+#define RSS_HASH_KEY_LEN       40
+
 struct be_dma_mem {
        void *va;
        dma_addr_t dma;
@@ -371,6 +374,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_MCAST_PROMISC                 (1 << 5)
 #define BE_FLAGS_NAPI_ENABLED                  (1 << 9)
 #define BE_FLAGS_QNQ_ASYNC_EVT_RCVD            (1 << 11)
 #define BE_FLAGS_VXLAN_OFFLOADS                        (1 << 12)
@@ -409,6 +413,13 @@ struct be_resources {
        u32 if_cap_flags;
 };
 
+struct rss_info {
+       u64 rss_flags;
+       u8 rsstable[RSS_INDIR_TABLE_LEN];
+       u8 rss_queue[RSS_INDIR_TABLE_LEN];
+       u8 rss_hkey[RSS_HASH_KEY_LEN];
+};
+
 struct be_adapter {
        struct pci_dev *pdev;
        struct net_device *netdev;
@@ -445,7 +456,7 @@ struct be_adapter {
        struct be_drv_stats drv_stats;
        struct be_aic_obj aic_obj[MAX_EVT_QS];
        u16 vlans_added;
-       u8 vlan_tag[VLAN_N_VID];
+       unsigned long vids[BITS_TO_LONGS(VLAN_N_VID)];
        u8 vlan_prio_bmap;      /* Available Priority BitMap */
        u16 recommended_prio;   /* Recommended Priority */
        struct be_dma_mem rx_filter; /* Cmd DMA mem for rx-filter */
@@ -507,7 +518,7 @@ struct be_adapter {
        u32 msg_enable;
        int be_get_temp_freq;
        u8 pf_number;
-       u64 rss_flags;
+       struct rss_info rss_info;
 };
 
 #define be_physfn(adapter)             (!adapter->virtfn)
index d1ec15af0d2482f46c425bb7d2f6c99976c16329..f4ea3490f44657f3e90974065fea9a0eee649cd0 100644 (file)
@@ -52,8 +52,7 @@ static struct be_cmd_priv_map cmd_priv_map[] = {
        }
 };
 
-static bool be_cmd_allowed(struct be_adapter *adapter, u8 opcode,
-                          u8 subsystem)
+static bool be_cmd_allowed(struct be_adapter *adapter, u8 opcode, u8 subsystem)
 {
        int i;
        int num_entries = sizeof(cmd_priv_map)/sizeof(struct be_cmd_priv_map);
@@ -120,21 +119,28 @@ static struct be_cmd_resp_hdr *be_decode_resp_hdr(u32 tag0, u32 tag1)
        return (void *)addr;
 }
 
-static int be_mcc_compl_process(struct be_adapter *adapter,
-                               struct be_mcc_compl *compl)
+static bool be_skip_err_log(u8 opcode, u16 base_status, u16 addl_status)
 {
-       u16 compl_status, extd_status;
-       struct be_cmd_resp_hdr *resp_hdr;
-       u8 opcode = 0, subsystem = 0;
-
-       /* Just swap the status to host endian; mcc tag is opaquely copied
-        * from mcc_wrb */
-       be_dws_le_to_cpu(compl, 4);
-
-       compl_status = (compl->status >> CQE_STATUS_COMPL_SHIFT) &
-                               CQE_STATUS_COMPL_MASK;
+       if (base_status == MCC_STATUS_NOT_SUPPORTED ||
+           base_status == MCC_STATUS_ILLEGAL_REQUEST ||
+           addl_status == MCC_ADDL_STATUS_TOO_MANY_INTERFACES ||
+           (opcode == OPCODE_COMMON_WRITE_FLASHROM &&
+           (base_status == MCC_STATUS_ILLEGAL_FIELD ||
+            addl_status == MCC_ADDL_STATUS_FLASH_IMAGE_CRC_MISMATCH)))
+               return true;
+       else
+               return false;
+}
 
-       resp_hdr = be_decode_resp_hdr(compl->tag0, compl->tag1);
+/* Place holder for all the async MCC cmds wherein the caller is not in a busy
+ * loop (has not issued be_mcc_notify_wait())
+ */
+static void be_async_cmd_process(struct be_adapter *adapter,
+                                struct be_mcc_compl *compl,
+                                struct be_cmd_resp_hdr *resp_hdr)
+{
+       enum mcc_base_status base_status = base_status(compl->status);
+       u8 opcode = 0, subsystem = 0;
 
        if (resp_hdr) {
                opcode = resp_hdr->opcode;
@@ -144,61 +150,86 @@ static int be_mcc_compl_process(struct be_adapter *adapter,
        if (opcode == OPCODE_LOWLEVEL_LOOPBACK_TEST &&
            subsystem == CMD_SUBSYSTEM_LOWLEVEL) {
                complete(&adapter->et_cmd_compl);
-               return 0;
+               return;
        }
 
-       if (((opcode == OPCODE_COMMON_WRITE_FLASHROM) ||
-            (opcode == OPCODE_COMMON_WRITE_OBJECT)) &&
-           (subsystem == CMD_SUBSYSTEM_COMMON)) {
-               adapter->flash_status = compl_status;
+       if ((opcode == OPCODE_COMMON_WRITE_FLASHROM ||
+            opcode == OPCODE_COMMON_WRITE_OBJECT) &&
+           subsystem == CMD_SUBSYSTEM_COMMON) {
+               adapter->flash_status = compl->status;
                complete(&adapter->et_cmd_compl);
+               return;
        }
 
-       if (compl_status == MCC_STATUS_SUCCESS) {
-               if (((opcode == OPCODE_ETH_GET_STATISTICS) ||
-                    (opcode == OPCODE_ETH_GET_PPORT_STATS)) &&
-                   (subsystem == CMD_SUBSYSTEM_ETH)) {
-                       be_parse_stats(adapter);
-                       adapter->stats_cmd_sent = false;
-               }
-               if (opcode == OPCODE_COMMON_GET_CNTL_ADDITIONAL_ATTRIBUTES &&
-                   subsystem == CMD_SUBSYSTEM_COMMON) {
+       if ((opcode == OPCODE_ETH_GET_STATISTICS ||
+            opcode == OPCODE_ETH_GET_PPORT_STATS) &&
+           subsystem == CMD_SUBSYSTEM_ETH &&
+           base_status == MCC_STATUS_SUCCESS) {
+               be_parse_stats(adapter);
+               adapter->stats_cmd_sent = false;
+               return;
+       }
+
+       if (opcode == OPCODE_COMMON_GET_CNTL_ADDITIONAL_ATTRIBUTES &&
+           subsystem == CMD_SUBSYSTEM_COMMON) {
+               if (base_status == MCC_STATUS_SUCCESS) {
                        struct be_cmd_resp_get_cntl_addnl_attribs *resp =
-                               (void *)resp_hdr;
+                                                       (void *)resp_hdr;
                        adapter->drv_stats.be_on_die_temperature =
-                               resp->on_die_temperature;
-               }
-       } else {
-               if (opcode == OPCODE_COMMON_GET_CNTL_ADDITIONAL_ATTRIBUTES)
+                                               resp->on_die_temperature;
+               } else {
                        adapter->be_get_temp_freq = 0;
+               }
+               return;
+       }
+}
+
+static int be_mcc_compl_process(struct be_adapter *adapter,
+                               struct be_mcc_compl *compl)
+{
+       enum mcc_base_status base_status;
+       enum mcc_addl_status addl_status;
+       struct be_cmd_resp_hdr *resp_hdr;
+       u8 opcode = 0, subsystem = 0;
+
+       /* Just swap the status to host endian; mcc tag is opaquely copied
+        * from mcc_wrb */
+       be_dws_le_to_cpu(compl, 4);
+
+       base_status = base_status(compl->status);
+       addl_status = addl_status(compl->status);
+
+       resp_hdr = be_decode_resp_hdr(compl->tag0, compl->tag1);
+       if (resp_hdr) {
+               opcode = resp_hdr->opcode;
+               subsystem = resp_hdr->subsystem;
+       }
+
+       be_async_cmd_process(adapter, compl, resp_hdr);
 
-               if (compl_status == MCC_STATUS_NOT_SUPPORTED ||
-                       compl_status == MCC_STATUS_ILLEGAL_REQUEST)
-                       goto done;
+       if (base_status != MCC_STATUS_SUCCESS &&
+           !be_skip_err_log(opcode, base_status, addl_status)) {
 
-               if (compl_status == MCC_STATUS_UNAUTHORIZED_REQUEST) {
+               if (base_status == MCC_STATUS_UNAUTHORIZED_REQUEST) {
                        dev_warn(&adapter->pdev->dev,
                                 "VF is not privileged to issue opcode %d-%d\n",
                                 opcode, subsystem);
                } else {
-                       extd_status = (compl->status >> CQE_STATUS_EXTD_SHIFT) &
-                                       CQE_STATUS_EXTD_MASK;
                        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;
+                               opcode, subsystem, base_status, addl_status);
                }
        }
-done:
-       return compl_status;
+       return compl->status;
 }
 
 /* Link state evt is a string of bytes; no need for endian swapping */
 static void be_async_link_state_process(struct be_adapter *adapter,
-               struct be_async_event_link_state *evt)
+                                       struct be_mcc_compl *compl)
 {
+       struct be_async_event_link_state *evt =
+                       (struct be_async_event_link_state *)compl;
+
        /* When link status changes, link speed must be re-queried from FW */
        adapter->phy.link_speed = -1;
 
@@ -221,8 +252,11 @@ static void be_async_link_state_process(struct be_adapter *adapter,
 
 /* Grp5 CoS Priority evt */
 static void be_async_grp5_cos_priority_process(struct be_adapter *adapter,
-               struct be_async_event_grp5_cos_priority *evt)
+                                              struct be_mcc_compl *compl)
 {
+       struct be_async_event_grp5_cos_priority *evt =
+                       (struct be_async_event_grp5_cos_priority *)compl;
+
        if (evt->valid) {
                adapter->vlan_prio_bmap = evt->available_priority_bmap;
                adapter->recommended_prio &= ~VLAN_PRIO_MASK;
@@ -233,8 +267,11 @@ static void be_async_grp5_cos_priority_process(struct be_adapter *adapter,
 
 /* Grp5 QOS Speed evt: qos_link_speed is in units of 10 Mbps */
 static void be_async_grp5_qos_speed_process(struct be_adapter *adapter,
-               struct be_async_event_grp5_qos_link_speed *evt)
+                                           struct be_mcc_compl *compl)
 {
+       struct be_async_event_grp5_qos_link_speed *evt =
+                       (struct be_async_event_grp5_qos_link_speed *)compl;
+
        if (adapter->phy.link_speed >= 0 &&
            evt->physical_port == adapter->port_num)
                adapter->phy.link_speed = le16_to_cpu(evt->qos_link_speed) * 10;
@@ -242,8 +279,11 @@ static void be_async_grp5_qos_speed_process(struct be_adapter *adapter,
 
 /*Grp5 PVID evt*/
 static void be_async_grp5_pvid_state_process(struct be_adapter *adapter,
-               struct be_async_event_grp5_pvid_state *evt)
+                                            struct be_mcc_compl *compl)
 {
+       struct be_async_event_grp5_pvid_state *evt =
+                       (struct be_async_event_grp5_pvid_state *)compl;
+
        if (evt->enabled) {
                adapter->pvid = le16_to_cpu(evt->tag) & VLAN_VID_MASK;
                dev_info(&adapter->pdev->dev, "LPVID: %d\n", adapter->pvid);
@@ -253,26 +293,21 @@ static void be_async_grp5_pvid_state_process(struct be_adapter *adapter,
 }
 
 static void be_async_grp5_evt_process(struct be_adapter *adapter,
-               u32 trailer, struct be_mcc_compl *evt)
+                                     struct be_mcc_compl *compl)
 {
-       u8 event_type = 0;
-
-       event_type = (trailer >> ASYNC_TRAILER_EVENT_TYPE_SHIFT) &
-               ASYNC_TRAILER_EVENT_TYPE_MASK;
+       u8 event_type = (compl->flags >> ASYNC_EVENT_TYPE_SHIFT) &
+                               ASYNC_EVENT_TYPE_MASK;
 
        switch (event_type) {
        case ASYNC_EVENT_COS_PRIORITY:
-               be_async_grp5_cos_priority_process(adapter,
-               (struct be_async_event_grp5_cos_priority *)evt);
-       break;
+               be_async_grp5_cos_priority_process(adapter, compl);
+               break;
        case ASYNC_EVENT_QOS_SPEED:
-               be_async_grp5_qos_speed_process(adapter,
-               (struct be_async_event_grp5_qos_link_speed *)evt);
-       break;
+               be_async_grp5_qos_speed_process(adapter, compl);
+               break;
        case ASYNC_EVENT_PVID_STATE:
-               be_async_grp5_pvid_state_process(adapter,
-               (struct be_async_event_grp5_pvid_state *)evt);
-       break;
+               be_async_grp5_pvid_state_process(adapter, compl);
+               break;
        default:
                dev_warn(&adapter->pdev->dev, "Unknown grp5 event 0x%x!\n",
                         event_type);
@@ -281,13 +316,13 @@ static void be_async_grp5_evt_process(struct be_adapter *adapter,
 }
 
 static void be_async_dbg_evt_process(struct be_adapter *adapter,
-               u32 trailer, struct be_mcc_compl *cmp)
+                                    struct be_mcc_compl *cmp)
 {
        u8 event_type = 0;
        struct be_async_event_qnq *evt = (struct be_async_event_qnq *) cmp;
 
-       event_type = (trailer >> ASYNC_TRAILER_EVENT_TYPE_SHIFT) &
-               ASYNC_TRAILER_EVENT_TYPE_MASK;
+       event_type = (cmp->flags >> ASYNC_EVENT_TYPE_SHIFT) &
+                       ASYNC_EVENT_TYPE_MASK;
 
        switch (event_type) {
        case ASYNC_DEBUG_EVENT_TYPE_QNQ:
@@ -302,25 +337,33 @@ static void be_async_dbg_evt_process(struct be_adapter *adapter,
        }
 }
 
-static inline bool is_link_state_evt(u32 trailer)
+static inline bool is_link_state_evt(u32 flags)
 {
-       return ((trailer >> ASYNC_TRAILER_EVENT_CODE_SHIFT) &
-               ASYNC_TRAILER_EVENT_CODE_MASK) ==
-                               ASYNC_EVENT_CODE_LINK_STATE;
+       return ((flags >> ASYNC_EVENT_CODE_SHIFT) & ASYNC_EVENT_CODE_MASK) ==
+                       ASYNC_EVENT_CODE_LINK_STATE;
 }
 
-static inline bool is_grp5_evt(u32 trailer)
+static inline bool is_grp5_evt(u32 flags)
 {
-       return (((trailer >> ASYNC_TRAILER_EVENT_CODE_SHIFT) &
-               ASYNC_TRAILER_EVENT_CODE_MASK) ==
-                               ASYNC_EVENT_CODE_GRP_5);
+       return ((flags >> ASYNC_EVENT_CODE_SHIFT) & ASYNC_EVENT_CODE_MASK) ==
+                       ASYNC_EVENT_CODE_GRP_5;
 }
 
-static inline bool is_dbg_evt(u32 trailer)
+static inline bool is_dbg_evt(u32 flags)
 {
-       return (((trailer >> ASYNC_TRAILER_EVENT_CODE_SHIFT) &
-               ASYNC_TRAILER_EVENT_CODE_MASK) ==
-                               ASYNC_EVENT_CODE_QNQ);
+       return ((flags >> ASYNC_EVENT_CODE_SHIFT) & ASYNC_EVENT_CODE_MASK) ==
+                       ASYNC_EVENT_CODE_QNQ;
+}
+
+static void be_mcc_event_process(struct be_adapter *adapter,
+                                struct be_mcc_compl *compl)
+{
+       if (is_link_state_evt(compl->flags))
+               be_async_link_state_process(adapter, compl);
+       else if (is_grp5_evt(compl->flags))
+               be_async_grp5_evt_process(adapter, compl);
+       else if (is_dbg_evt(compl->flags))
+               be_async_dbg_evt_process(adapter, compl);
 }
 
 static struct be_mcc_compl *be_mcc_compl_get(struct be_adapter *adapter)
@@ -362,21 +405,13 @@ int be_process_mcc(struct be_adapter *adapter)
        struct be_mcc_obj *mcc_obj = &adapter->mcc_obj;
 
        spin_lock(&adapter->mcc_cq_lock);
+
        while ((compl = be_mcc_compl_get(adapter))) {
                if (compl->flags & CQE_FLAGS_ASYNC_MASK) {
-                       /* Interpret flags as an async trailer */
-                       if (is_link_state_evt(compl->flags))
-                               be_async_link_state_process(adapter,
-                               (struct be_async_event_link_state *) compl);
-                       else if (is_grp5_evt(compl->flags))
-                               be_async_grp5_evt_process(adapter,
-                               compl->flags, compl);
-                       else if (is_dbg_evt(compl->flags))
-                               be_async_dbg_evt_process(adapter,
-                               compl->flags, compl);
+                       be_mcc_event_process(adapter, compl);
                } else if (compl->flags & CQE_FLAGS_COMPLETED_MASK) {
-                               status = be_mcc_compl_process(adapter, compl);
-                               atomic_dec(&mcc_obj->q.used);
+                       status = be_mcc_compl_process(adapter, compl);
+                       atomic_dec(&mcc_obj->q.used);
                }
                be_mcc_compl_use(compl);
                num++;
@@ -436,7 +471,9 @@ static int be_mcc_notify_wait(struct be_adapter *adapter)
        if (status == -EIO)
                goto out;
 
-       status = resp->status;
+       status = (resp->base_status |
+                 ((resp->addl_status & CQE_ADDL_STATUS_MASK) <<
+                  CQE_ADDL_STATUS_SHIFT));
 out:
        return status;
 }
@@ -560,10 +597,8 @@ static bool lancer_provisioning_error(struct be_adapter *adapter)
        u32 sliport_status = 0, sliport_err1 = 0, sliport_err2 = 0;
        sliport_status = ioread32(adapter->db + SLIPORT_STATUS_OFFSET);
        if (sliport_status & SLIPORT_STATUS_ERR_MASK) {
-               sliport_err1 = ioread32(adapter->db +
-                                       SLIPORT_ERROR1_OFFSET);
-               sliport_err2 = ioread32(adapter->db +
-                                       SLIPORT_ERROR2_OFFSET);
+               sliport_err1 = ioread32(adapter->db + SLIPORT_ERROR1_OFFSET);
+               sliport_err2 = ioread32(adapter->db + SLIPORT_ERROR2_OFFSET);
 
                if (sliport_err1 == SLIPORT_ERROR_NO_RESOURCE1 &&
                    sliport_err2 == SLIPORT_ERROR_NO_RESOURCE2)
@@ -630,8 +665,7 @@ int be_fw_wait_ready(struct be_adapter *adapter)
                if (stage == POST_STAGE_ARMFW_RDY)
                        return 0;
 
-               dev_info(dev, "Waiting for POST, %ds elapsed\n",
-                        timeout);
+               dev_info(dev, "Waiting for POST, %ds elapsed\n", timeout);
                if (msleep_interruptible(2000)) {
                        dev_err(dev, "Waiting for POST aborted\n");
                        return -EINTR;
@@ -649,8 +683,7 @@ static inline struct be_sge *nonembedded_sgl(struct be_mcc_wrb *wrb)
        return &wrb->payload.sgl[0];
 }
 
-static inline void fill_wrb_tags(struct be_mcc_wrb *wrb,
-                                unsigned long addr)
+static inline void fill_wrb_tags(struct be_mcc_wrb *wrb, unsigned long addr)
 {
        wrb->tag0 = addr & 0xFFFFFFFF;
        wrb->tag1 = upper_32_bits(addr);
@@ -659,8 +692,9 @@ static inline void fill_wrb_tags(struct be_mcc_wrb *wrb,
 /* Don't touch the hdr after it's prepared */
 /* mem will be NULL for embedded commands */
 static void be_wrb_cmd_hdr_prepare(struct be_cmd_req_hdr *req_hdr,
-                               u8 subsystem, u8 opcode, int cmd_len,
-                               struct be_mcc_wrb *wrb, struct be_dma_mem *mem)
+                                  u8 subsystem, u8 opcode, int cmd_len,
+                                  struct be_mcc_wrb *wrb,
+                                  struct be_dma_mem *mem)
 {
        struct be_sge *sge;
 
@@ -683,7 +717,7 @@ static void be_wrb_cmd_hdr_prepare(struct be_cmd_req_hdr *req_hdr,
 }
 
 static void be_cmd_page_addrs_prepare(struct phys_addr *pages, u32 max_pages,
-                       struct be_dma_mem *mem)
+                                     struct be_dma_mem *mem)
 {
        int i, buf_pages = min(PAGES_4K_SPANNED(mem->va, mem->size), max_pages);
        u64 dma = (u64)mem->dma;
@@ -868,7 +902,8 @@ int be_cmd_eq_create(struct be_adapter *adapter, struct be_eq_obj *eqo)
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_EQ_CREATE, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_EQ_CREATE, sizeof(*req), wrb,
+                              NULL);
 
        /* Support for EQ_CREATEv2 available only SH-R onwards */
        if (!(BEx_chip(adapter) || lancer_chip(adapter)))
@@ -917,7 +952,8 @@ int be_cmd_mac_addr_query(struct be_adapter *adapter, u8 *mac_addr,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_NTWK_MAC_QUERY, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_NTWK_MAC_QUERY, sizeof(*req), wrb,
+                              NULL);
        req->type = MAC_ADDRESS_TYPE_NETWORK;
        if (permanent) {
                req->permanent = 1;
@@ -940,7 +976,7 @@ int be_cmd_mac_addr_query(struct be_adapter *adapter, u8 *mac_addr,
 
 /* Uses synchronous MCCQ */
 int be_cmd_pmac_add(struct be_adapter *adapter, u8 *mac_addr,
-               u32 if_id, u32 *pmac_id, u32 domain)
+                   u32 if_id, u32 *pmac_id, u32 domain)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_pmac_add *req;
@@ -956,7 +992,8 @@ int be_cmd_pmac_add(struct be_adapter *adapter, u8 *mac_addr,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_NTWK_PMAC_ADD, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_NTWK_PMAC_ADD, sizeof(*req), wrb,
+                              NULL);
 
        req->hdr.domain = domain;
        req->if_id = cpu_to_le32(if_id);
@@ -1012,7 +1049,7 @@ int be_cmd_pmac_del(struct be_adapter *adapter, u32 if_id, int pmac_id, u32 dom)
 
 /* Uses Mbox */
 int be_cmd_cq_create(struct be_adapter *adapter, struct be_queue_info *cq,
-               struct be_queue_info *eq, bool no_delay, int coalesce_wm)
+                    struct be_queue_info *eq, bool no_delay, int coalesce_wm)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_cq_create *req;
@@ -1028,17 +1065,18 @@ int be_cmd_cq_create(struct be_adapter *adapter, struct be_queue_info *cq,
        ctxt = &req->context;
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_CQ_CREATE, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_CQ_CREATE, sizeof(*req), wrb,
+                              NULL);
 
        req->num_pages =  cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size));
 
        if (BEx_chip(adapter)) {
                AMAP_SET_BITS(struct amap_cq_context_be, coalescwm, ctxt,
-                                                               coalesce_wm);
+                             coalesce_wm);
                AMAP_SET_BITS(struct amap_cq_context_be, nodelay,
-                                                               ctxt, no_delay);
+                             ctxt, no_delay);
                AMAP_SET_BITS(struct amap_cq_context_be, count, ctxt,
-                                               __ilog2_u32(cq->len/256));
+                             __ilog2_u32(cq->len / 256));
                AMAP_SET_BITS(struct amap_cq_context_be, valid, ctxt, 1);
                AMAP_SET_BITS(struct amap_cq_context_be, eventable, ctxt, 1);
                AMAP_SET_BITS(struct amap_cq_context_be, eqid, ctxt, eq->id);
@@ -1053,14 +1091,12 @@ int be_cmd_cq_create(struct be_adapter *adapter, struct be_queue_info *cq,
                        AMAP_SET_BITS(struct amap_cq_context_v2, coalescwm,
                                      ctxt, coalesce_wm);
                AMAP_SET_BITS(struct amap_cq_context_v2, nodelay, ctxt,
-                                                               no_delay);
+                             no_delay);
                AMAP_SET_BITS(struct amap_cq_context_v2, count, ctxt,
-                                               __ilog2_u32(cq->len/256));
+                             __ilog2_u32(cq->len / 256));
                AMAP_SET_BITS(struct amap_cq_context_v2, valid, ctxt, 1);
-               AMAP_SET_BITS(struct amap_cq_context_v2, eventable,
-                                                               ctxt, 1);
-               AMAP_SET_BITS(struct amap_cq_context_v2, eqid,
-                                                               ctxt, eq->id);
+               AMAP_SET_BITS(struct amap_cq_context_v2, eventable, ctxt, 1);
+               AMAP_SET_BITS(struct amap_cq_context_v2, eqid, ctxt, eq->id);
        }
 
        be_dws_cpu_to_le(ctxt, sizeof(req->context));
@@ -1088,8 +1124,8 @@ static u32 be_encoded_q_len(int q_len)
 }
 
 static int be_cmd_mccq_ext_create(struct be_adapter *adapter,
-                               struct be_queue_info *mccq,
-                               struct be_queue_info *cq)
+                                 struct be_queue_info *mccq,
+                                 struct be_queue_info *cq)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_mcc_ext_create *req;
@@ -1105,13 +1141,14 @@ static int be_cmd_mccq_ext_create(struct be_adapter *adapter,
        ctxt = &req->context;
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                       OPCODE_COMMON_MCC_CREATE_EXT, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_MCC_CREATE_EXT, sizeof(*req), wrb,
+                              NULL);
 
        req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size));
        if (BEx_chip(adapter)) {
                AMAP_SET_BITS(struct amap_mcc_context_be, valid, ctxt, 1);
                AMAP_SET_BITS(struct amap_mcc_context_be, ring_size, ctxt,
-                                               be_encoded_q_len(mccq->len));
+                             be_encoded_q_len(mccq->len));
                AMAP_SET_BITS(struct amap_mcc_context_be, cq_id, ctxt, cq->id);
        } else {
                req->hdr.version = 1;
@@ -1145,8 +1182,8 @@ static int be_cmd_mccq_ext_create(struct be_adapter *adapter,
 }
 
 static int be_cmd_mccq_org_create(struct be_adapter *adapter,
-                               struct be_queue_info *mccq,
-                               struct be_queue_info *cq)
+                                 struct be_queue_info *mccq,
+                                 struct be_queue_info *cq)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_mcc_create *req;
@@ -1162,13 +1199,14 @@ static int be_cmd_mccq_org_create(struct be_adapter *adapter,
        ctxt = &req->context;
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                       OPCODE_COMMON_MCC_CREATE, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_MCC_CREATE, sizeof(*req), wrb,
+                              NULL);
 
        req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size));
 
        AMAP_SET_BITS(struct amap_mcc_context_be, valid, ctxt, 1);
        AMAP_SET_BITS(struct amap_mcc_context_be, ring_size, ctxt,
-                       be_encoded_q_len(mccq->len));
+                     be_encoded_q_len(mccq->len));
        AMAP_SET_BITS(struct amap_mcc_context_be, cq_id, ctxt, cq->id);
 
        be_dws_cpu_to_le(ctxt, sizeof(req->context));
@@ -1187,8 +1225,7 @@ static int be_cmd_mccq_org_create(struct be_adapter *adapter,
 }
 
 int be_cmd_mccq_create(struct be_adapter *adapter,
-                       struct be_queue_info *mccq,
-                       struct be_queue_info *cq)
+                      struct be_queue_info *mccq, struct be_queue_info *cq)
 {
        int status;
 
@@ -1213,7 +1250,7 @@ int be_cmd_txq_create(struct be_adapter *adapter, struct be_tx_obj *txo)
 
        req = embedded_payload(&wrb);
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
-                               OPCODE_ETH_TX_CREATE, sizeof(*req), &wrb, NULL);
+                              OPCODE_ETH_TX_CREATE, sizeof(*req), &wrb, NULL);
 
        if (lancer_chip(adapter)) {
                req->hdr.version = 1;
@@ -1250,8 +1287,8 @@ int be_cmd_txq_create(struct be_adapter *adapter, struct be_tx_obj *txo)
 
 /* Uses MCC */
 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)
+                     struct be_queue_info *rxq, u16 cq_id, u16 frag_size,
+                     u32 if_id, u32 rss, u8 *rss_id)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_eth_rx_create *req;
@@ -1268,7 +1305,7 @@ int be_cmd_rxq_create(struct be_adapter *adapter,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
-                               OPCODE_ETH_RX_CREATE, sizeof(*req), wrb, NULL);
+                              OPCODE_ETH_RX_CREATE, sizeof(*req), wrb, NULL);
 
        req->cq_id = cpu_to_le16(cq_id);
        req->frag_size = fls(frag_size) - 1;
@@ -1295,7 +1332,7 @@ int be_cmd_rxq_create(struct be_adapter *adapter,
  * Uses Mbox
  */
 int be_cmd_q_destroy(struct be_adapter *adapter, struct be_queue_info *q,
-               int queue_type)
+                    int queue_type)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_q_destroy *req;
@@ -1334,7 +1371,7 @@ int be_cmd_q_destroy(struct be_adapter *adapter, struct be_queue_info *q,
        }
 
        be_wrb_cmd_hdr_prepare(&req->hdr, subsys, opcode, sizeof(*req), wrb,
-                               NULL);
+                              NULL);
        req->id = cpu_to_le16(q->id);
 
        status = be_mbox_notify_wait(adapter);
@@ -1361,7 +1398,7 @@ int be_cmd_rxq_destroy(struct be_adapter *adapter, struct be_queue_info *q)
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
-                       OPCODE_ETH_RX_DESTROY, sizeof(*req), wrb, NULL);
+                              OPCODE_ETH_RX_DESTROY, sizeof(*req), wrb, NULL);
        req->id = cpu_to_le16(q->id);
 
        status = be_mcc_notify_wait(adapter);
@@ -1384,7 +1421,8 @@ int be_cmd_if_create(struct be_adapter *adapter, u32 cap_flags, u32 en_flags,
 
        req = embedded_payload(&wrb);
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_NTWK_INTERFACE_CREATE, sizeof(*req), &wrb, NULL);
+                              OPCODE_COMMON_NTWK_INTERFACE_CREATE,
+                              sizeof(*req), &wrb, NULL);
        req->hdr.domain = domain;
        req->capability_flags = cpu_to_le32(cap_flags);
        req->enable_flags = cpu_to_le32(en_flags);
@@ -1422,7 +1460,8 @@ int be_cmd_if_destroy(struct be_adapter *adapter, int interface_id, u32 domain)
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_NTWK_INTERFACE_DESTROY, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_NTWK_INTERFACE_DESTROY,
+                              sizeof(*req), wrb, NULL);
        req->hdr.domain = domain;
        req->interface_id = cpu_to_le32(interface_id);
 
@@ -1452,7 +1491,8 @@ int be_cmd_get_stats(struct be_adapter *adapter, struct be_dma_mem *nonemb_cmd)
        hdr = nonemb_cmd->va;
 
        be_wrb_cmd_hdr_prepare(hdr, CMD_SUBSYSTEM_ETH,
-               OPCODE_ETH_GET_STATISTICS, nonemb_cmd->size, wrb, 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))
@@ -1472,7 +1512,7 @@ int be_cmd_get_stats(struct be_adapter *adapter, struct be_dma_mem *nonemb_cmd)
 
 /* Lancer Stats */
 int lancer_cmd_get_pport_stats(struct be_adapter *adapter,
-                               struct be_dma_mem *nonemb_cmd)
+                              struct be_dma_mem *nonemb_cmd)
 {
 
        struct be_mcc_wrb *wrb;
@@ -1493,8 +1533,8 @@ int lancer_cmd_get_pport_stats(struct be_adapter *adapter,
        req = nonemb_cmd->va;
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
-                       OPCODE_ETH_GET_PPORT_STATS, nonemb_cmd->size, wrb,
-                       nonemb_cmd);
+                              OPCODE_ETH_GET_PPORT_STATS, nonemb_cmd->size,
+                              wrb, nonemb_cmd);
 
        req->cmd_params.params.pport_num = cpu_to_le16(adapter->hba_port_num);
        req->cmd_params.params.reset_stats = 0;
@@ -1553,7 +1593,8 @@ int be_cmd_link_status_query(struct be_adapter *adapter, u16 *link_speed,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_NTWK_LINK_STATUS_QUERY, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_NTWK_LINK_STATUS_QUERY,
+                              sizeof(*req), wrb, NULL);
 
        /* version 1 of the cmd is not supported only by BE2 */
        if (!BE2_chip(adapter))
@@ -1598,8 +1639,8 @@ int be_cmd_get_die_temperature(struct be_adapter *adapter)
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_GET_CNTL_ADDITIONAL_ATTRIBUTES, sizeof(*req),
-               wrb, NULL);
+                              OPCODE_COMMON_GET_CNTL_ADDITIONAL_ATTRIBUTES,
+                              sizeof(*req), wrb, NULL);
 
        be_mcc_notify(adapter);
 
@@ -1625,7 +1666,8 @@ int be_cmd_get_reg_len(struct be_adapter *adapter, u32 *log_size)
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_MANAGE_FAT, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_MANAGE_FAT, sizeof(*req), wrb,
+                              NULL);
        req->fat_operation = cpu_to_le32(QUERY_FAT);
        status = be_mcc_notify_wait(adapter);
        if (!status) {
@@ -1655,8 +1697,8 @@ void be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf)
 
        get_fat_cmd.size = sizeof(struct be_cmd_req_get_fat) + 60*1024;
        get_fat_cmd.va = pci_alloc_consistent(adapter->pdev,
-                       get_fat_cmd.size,
-                       &get_fat_cmd.dma);
+                                             get_fat_cmd.size,
+                                             &get_fat_cmd.dma);
        if (!get_fat_cmd.va) {
                status = -ENOMEM;
                dev_err(&adapter->pdev->dev,
@@ -1679,8 +1721,8 @@ void be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf)
 
                payload_len = sizeof(struct be_cmd_req_get_fat) + buf_size;
                be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                               OPCODE_COMMON_MANAGE_FAT, payload_len, wrb,
-                               &get_fat_cmd);
+                                      OPCODE_COMMON_MANAGE_FAT, payload_len,
+                                      wrb, &get_fat_cmd);
 
                req->fat_operation = cpu_to_le32(RETRIEVE_FAT);
                req->read_log_offset = cpu_to_le32(log_offset);
@@ -1691,8 +1733,8 @@ void be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf)
                if (!status) {
                        struct be_cmd_resp_get_fat *resp = get_fat_cmd.va;
                        memcpy(buf + offset,
-                               resp->data_buffer,
-                               le32_to_cpu(resp->read_log_length));
+                              resp->data_buffer,
+                              le32_to_cpu(resp->read_log_length));
                } else {
                        dev_err(&adapter->pdev->dev, "FAT Table Retrieve error\n");
                        goto err;
@@ -1702,14 +1744,13 @@ void be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf)
        }
 err:
        pci_free_consistent(adapter->pdev, get_fat_cmd.size,
-                       get_fat_cmd.va,
-                       get_fat_cmd.dma);
+                           get_fat_cmd.va, get_fat_cmd.dma);
        spin_unlock_bh(&adapter->mcc_lock);
 }
 
 /* Uses synchronous mcc */
 int be_cmd_get_fw_ver(struct be_adapter *adapter, char *fw_ver,
-                       char *fw_on_flash)
+                     char *fw_on_flash)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_get_fw_version *req;
@@ -1726,7 +1767,8 @@ int be_cmd_get_fw_ver(struct be_adapter *adapter, char *fw_ver,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_GET_FW_VERSION, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_GET_FW_VERSION, sizeof(*req), wrb,
+                              NULL);
        status = be_mcc_notify_wait(adapter);
        if (!status) {
                struct be_cmd_resp_get_fw_version *resp = embedded_payload(wrb);
@@ -1759,7 +1801,8 @@ int be_cmd_modify_eqd(struct be_adapter *adapter, struct be_set_eqd *set_eqd,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_MODIFY_EQ_DELAY, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_MODIFY_EQ_DELAY, sizeof(*req), wrb,
+                              NULL);
 
        req->num_eq = cpu_to_le32(num);
        for (i = 0; i < num; i++) {
@@ -1777,7 +1820,7 @@ int be_cmd_modify_eqd(struct be_adapter *adapter, struct be_set_eqd *set_eqd,
 
 /* Uses sycnhronous mcc */
 int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, u16 *vtag_array,
-                      u32 num, bool promiscuous)
+                      u32 num)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_vlan_config *req;
@@ -1793,19 +1836,16 @@ int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, u16 *vtag_array,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_NTWK_VLAN_CONFIG, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_NTWK_VLAN_CONFIG, sizeof(*req),
+                              wrb, NULL);
 
        req->interface_id = if_id;
-       req->promiscuous = promiscuous;
        req->untagged = BE_IF_FLAGS_UNTAGGED & be_if_cap_flags(adapter) ? 1 : 0;
        req->num_vlan = num;
-       if (!promiscuous) {
-               memcpy(req->normal_vlan, vtag_array,
-                       req->num_vlan * sizeof(vtag_array[0]));
-       }
+       memcpy(req->normal_vlan, vtag_array,
+              req->num_vlan * sizeof(vtag_array[0]));
 
        status = be_mcc_notify_wait(adapter);
-
 err:
        spin_unlock_bh(&adapter->mcc_lock);
        return status;
@@ -1827,18 +1867,19 @@ int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 value)
        }
        memset(req, 0, sizeof(*req));
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                               OPCODE_COMMON_NTWK_RX_FILTER, sizeof(*req),
-                               wrb, mem);
+                              OPCODE_COMMON_NTWK_RX_FILTER, sizeof(*req),
+                              wrb, mem);
 
        req->if_id = cpu_to_le32(adapter->if_handle);
        if (flags & IFF_PROMISC) {
                req->if_flags_mask = cpu_to_le32(BE_IF_FLAGS_PROMISCUOUS |
-                                       BE_IF_FLAGS_VLAN_PROMISCUOUS |
-                                       BE_IF_FLAGS_MCAST_PROMISCUOUS);
+                                                BE_IF_FLAGS_VLAN_PROMISCUOUS |
+                                                BE_IF_FLAGS_MCAST_PROMISCUOUS);
                if (value == ON)
-                       req->if_flags = cpu_to_le32(BE_IF_FLAGS_PROMISCUOUS |
-                                               BE_IF_FLAGS_VLAN_PROMISCUOUS |
-                                               BE_IF_FLAGS_MCAST_PROMISCUOUS);
+                       req->if_flags =
+                               cpu_to_le32(BE_IF_FLAGS_PROMISCUOUS |
+                                           BE_IF_FLAGS_VLAN_PROMISCUOUS |
+                                           BE_IF_FLAGS_MCAST_PROMISCUOUS);
        } else if (flags & IFF_ALLMULTI) {
                req->if_flags_mask = req->if_flags =
                                cpu_to_le32(BE_IF_FLAGS_MCAST_PROMISCUOUS);
@@ -1867,7 +1908,7 @@ int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 value)
        }
 
        if ((req->if_flags_mask & cpu_to_le32(be_if_cap_flags(adapter))) !=
-            req->if_flags_mask) {
+           req->if_flags_mask) {
                dev_warn(&adapter->pdev->dev,
                         "Cannot set rx filter flags 0x%x\n",
                         req->if_flags_mask);
@@ -1905,7 +1946,8 @@ int be_cmd_set_flow_control(struct be_adapter *adapter, u32 tx_fc, u32 rx_fc)
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_SET_FLOW_CONTROL, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_SET_FLOW_CONTROL, sizeof(*req),
+                              wrb, NULL);
 
        req->tx_flow_control = cpu_to_le16((u16)tx_fc);
        req->rx_flow_control = cpu_to_le16((u16)rx_fc);
@@ -1938,7 +1980,8 @@ int be_cmd_get_flow_control(struct be_adapter *adapter, u32 *tx_fc, u32 *rx_fc)
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_GET_FLOW_CONTROL, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_GET_FLOW_CONTROL, sizeof(*req),
+                              wrb, NULL);
 
        status = be_mcc_notify_wait(adapter);
        if (!status) {
@@ -1968,7 +2011,8 @@ int be_cmd_query_fw_cfg(struct be_adapter *adapter, u32 *port_num,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_QUERY_FIRMWARE_CONFIG, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_QUERY_FIRMWARE_CONFIG,
+                              sizeof(*req), wrb, NULL);
 
        status = be_mbox_notify_wait(adapter);
        if (!status) {
@@ -2011,7 +2055,8 @@ int be_cmd_reset_function(struct be_adapter *adapter)
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(req, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_FUNCTION_RESET, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_FUNCTION_RESET, sizeof(*req), wrb,
+                              NULL);
 
        status = be_mbox_notify_wait(adapter);
 
@@ -2020,47 +2065,47 @@ 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)
+                     u32 rss_hash_opts, u16 table_size, const u8 *rss_hkey)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_rss_config *req;
-       u32 myhash[10] = {0x15d43fa5, 0x2534685a, 0x5f87693a, 0x5668494e,
-                       0x33cf6a53, 0x383334c6, 0x76ac4257, 0x59b242b2,
-                       0x3ea83c02, 0x4a110304};
        int status;
 
        if (!(be_if_cap_flags(adapter) & BE_IF_FLAGS_RSS))
                return 0;
 
-       if (mutex_lock_interruptible(&adapter->mbox_lock))
-               return -1;
+       spin_lock_bh(&adapter->mcc_lock);
 
-       wrb = wrb_from_mbox(adapter);
+       wrb = wrb_from_mccq(adapter);
+       if (!wrb) {
+               status = -EBUSY;
+               goto err;
+       }
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
-               OPCODE_ETH_RSS_CONFIG, sizeof(*req), wrb, NULL);
+                              OPCODE_ETH_RSS_CONFIG, sizeof(*req), wrb, NULL);
 
        req->if_id = cpu_to_le32(adapter->if_handle);
        req->enable_rss = cpu_to_le16(rss_hash_opts);
        req->cpu_table_size_log2 = cpu_to_le16(fls(table_size) - 1);
 
-       if (lancer_chip(adapter) || skyhawk_chip(adapter))
+       if (!BEx_chip(adapter))
                req->hdr.version = 1;
 
        memcpy(req->cpu_table, rsstable, table_size);
-       memcpy(req->hash, myhash, sizeof(myhash));
+       memcpy(req->hash, rss_hkey, RSS_HASH_KEY_LEN);
        be_dws_cpu_to_le(req->hash, sizeof(req->hash));
 
-       status = be_mbox_notify_wait(adapter);
-
-       mutex_unlock(&adapter->mbox_lock);
+       status = be_mcc_notify_wait(adapter);
+err:
+       spin_unlock_bh(&adapter->mcc_lock);
        return status;
 }
 
 /* Uses sync mcc */
 int be_cmd_set_beacon_state(struct be_adapter *adapter, u8 port_num,
-                       u8 bcn, u8 sts, u8 state)
+                           u8 bcn, u8 sts, u8 state)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_enable_disable_beacon *req;
@@ -2076,7 +2121,8 @@ int be_cmd_set_beacon_state(struct be_adapter *adapter, u8 port_num,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_ENABLE_DISABLE_BEACON, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_ENABLE_DISABLE_BEACON,
+                              sizeof(*req), wrb, NULL);
 
        req->port_num = port_num;
        req->beacon_state = state;
@@ -2107,7 +2153,8 @@ int be_cmd_get_beacon_state(struct be_adapter *adapter, u8 port_num, u32 *state)
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_GET_BEACON_STATE, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_GET_BEACON_STATE, sizeof(*req),
+                              wrb, NULL);
 
        req->port_num = port_num;
 
@@ -2146,20 +2193,20 @@ int lancer_cmd_write_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                               OPCODE_COMMON_WRITE_OBJECT,
-                               sizeof(struct lancer_cmd_req_write_object), wrb,
-                               NULL);
+                              OPCODE_COMMON_WRITE_OBJECT,
+                              sizeof(struct lancer_cmd_req_write_object), wrb,
+                              NULL);
 
        ctxt = &req->context;
        AMAP_SET_BITS(struct amap_lancer_write_obj_context,
-                       write_length, ctxt, data_size);
+                     write_length, ctxt, data_size);
 
        if (data_size == 0)
                AMAP_SET_BITS(struct amap_lancer_write_obj_context,
-                               eof, ctxt, 1);
+                             eof, ctxt, 1);
        else
                AMAP_SET_BITS(struct amap_lancer_write_obj_context,
-                               eof, ctxt, 0);
+                             eof, ctxt, 0);
 
        be_dws_cpu_to_le(ctxt, sizeof(req->context));
        req->write_offset = cpu_to_le32(data_offset);
@@ -2167,8 +2214,8 @@ int lancer_cmd_write_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
        req->descriptor_count = cpu_to_le32(1);
        req->buf_len = cpu_to_le32(data_size);
        req->addr_low = cpu_to_le32((cmd->dma +
-                               sizeof(struct lancer_cmd_req_write_object))
-                               & 0xFFFFFFFF);
+                                    sizeof(struct lancer_cmd_req_write_object))
+                                   & 0xFFFFFFFF);
        req->addr_high = cpu_to_le32(upper_32_bits(cmd->dma +
                                sizeof(struct lancer_cmd_req_write_object)));
 
@@ -2197,8 +2244,8 @@ int lancer_cmd_write_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
 }
 
 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)
 {
        struct be_mcc_wrb *wrb;
        struct lancer_cmd_req_read_object *req;
@@ -2216,9 +2263,9 @@ int lancer_cmd_read_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                       OPCODE_COMMON_READ_OBJECT,
-                       sizeof(struct lancer_cmd_req_read_object), wrb,
-                       NULL);
+                              OPCODE_COMMON_READ_OBJECT,
+                              sizeof(struct lancer_cmd_req_read_object), wrb,
+                              NULL);
 
        req->desired_read_len = cpu_to_le32(data_size);
        req->read_offset = cpu_to_le32(data_offset);
@@ -2244,7 +2291,7 @@ int lancer_cmd_read_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
 }
 
 int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd,
-                       u32 flash_type, u32 flash_opcode, u32 buf_size)
+                         u32 flash_type, u32 flash_opcode, u32 buf_size)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_write_flashrom *req;
@@ -2261,7 +2308,8 @@ int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd,
        req = cmd->va;
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_WRITE_FLASHROM, cmd->size, wrb, cmd);
+                              OPCODE_COMMON_WRITE_FLASHROM, cmd->size, wrb,
+                              cmd);
 
        req->params.op_type = cpu_to_le32(flash_type);
        req->params.op_code = cpu_to_le32(flash_opcode);
@@ -2284,7 +2332,7 @@ int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd,
 }
 
 int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc,
-                        int offset)
+                         u16 optype, int offset)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_read_flash_crc *req;
@@ -2303,7 +2351,7 @@ int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc,
                               OPCODE_COMMON_READ_FLASHROM, sizeof(*req),
                               wrb, NULL);
 
-       req->params.op_type = cpu_to_le32(OPTYPE_REDBOOT);
+       req->params.op_type = cpu_to_le32(optype);
        req->params.op_code = cpu_to_le32(FLASHROM_OPER_REPORT);
        req->params.offset = cpu_to_le32(offset);
        req->params.data_buf_size = cpu_to_le32(0x4);
@@ -2318,7 +2366,7 @@ int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc,
 }
 
 int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac,
-                               struct be_dma_mem *nonemb_cmd)
+                           struct be_dma_mem *nonemb_cmd)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_acpi_wol_magic_config *req;
@@ -2334,8 +2382,8 @@ int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac,
        req = nonemb_cmd->va;
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
-               OPCODE_ETH_ACPI_WOL_MAGIC_CONFIG, sizeof(*req), wrb,
-               nonemb_cmd);
+                              OPCODE_ETH_ACPI_WOL_MAGIC_CONFIG, sizeof(*req),
+                              wrb, nonemb_cmd);
        memcpy(req->magic_mac, mac, ETH_ALEN);
 
        status = be_mcc_notify_wait(adapter);
@@ -2363,8 +2411,8 @@ int be_cmd_set_loopback(struct be_adapter *adapter, u8 port_num,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_LOWLEVEL,
-                       OPCODE_LOWLEVEL_SET_LOOPBACK_MODE, sizeof(*req), wrb,
-                       NULL);
+                              OPCODE_LOWLEVEL_SET_LOOPBACK_MODE, sizeof(*req),
+                              wrb, NULL);
 
        req->src_port = port_num;
        req->dest_port = port_num;
@@ -2378,7 +2426,8 @@ int be_cmd_set_loopback(struct be_adapter *adapter, u8 port_num,
 }
 
 int be_cmd_loopback_test(struct be_adapter *adapter, u32 port_num,
-               u32 loopback_type, u32 pkt_size, u32 num_pkts, u64 pattern)
+                        u32 loopback_type, u32 pkt_size, u32 num_pkts,
+                        u64 pattern)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_loopback_test *req;
@@ -2396,7 +2445,8 @@ int be_cmd_loopback_test(struct be_adapter *adapter, u32 port_num,
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_LOWLEVEL,
-                       OPCODE_LOWLEVEL_LOOPBACK_TEST, sizeof(*req), wrb, NULL);
+                              OPCODE_LOWLEVEL_LOOPBACK_TEST, sizeof(*req), wrb,
+                              NULL);
 
        req->hdr.timeout = cpu_to_le32(15);
        req->pattern = cpu_to_le64(pattern);
@@ -2421,7 +2471,7 @@ int be_cmd_loopback_test(struct be_adapter *adapter, u32 port_num,
 }
 
 int be_cmd_ddr_dma_test(struct be_adapter *adapter, u64 pattern,
-                               u32 byte_cnt, struct be_dma_mem *cmd)
+                       u32 byte_cnt, struct be_dma_mem *cmd)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_ddrdma_test *req;
@@ -2437,7 +2487,8 @@ int be_cmd_ddr_dma_test(struct be_adapter *adapter, u64 pattern,
        }
        req = cmd->va;
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_LOWLEVEL,
-                       OPCODE_LOWLEVEL_HOST_DDR_DMA, cmd->size, wrb, cmd);
+                              OPCODE_LOWLEVEL_HOST_DDR_DMA, cmd->size, wrb,
+                              cmd);
 
        req->pattern = cpu_to_le64(pattern);
        req->byte_count = cpu_to_le32(byte_cnt);
@@ -2465,7 +2516,7 @@ int be_cmd_ddr_dma_test(struct be_adapter *adapter, u64 pattern,
 }
 
 int be_cmd_get_seeprom_data(struct be_adapter *adapter,
-                               struct be_dma_mem *nonemb_cmd)
+                           struct be_dma_mem *nonemb_cmd)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_seeprom_read *req;
@@ -2481,8 +2532,8 @@ int be_cmd_get_seeprom_data(struct be_adapter *adapter,
        req = nonemb_cmd->va;
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                       OPCODE_COMMON_SEEPROM_READ, sizeof(*req), wrb,
-                       nonemb_cmd);
+                              OPCODE_COMMON_SEEPROM_READ, sizeof(*req), wrb,
+                              nonemb_cmd);
 
        status = be_mcc_notify_wait(adapter);
 
@@ -2510,8 +2561,7 @@ int be_cmd_get_phy_info(struct be_adapter *adapter)
                goto err;
        }
        cmd.size = sizeof(struct be_cmd_req_get_phy_info);
-       cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size,
-                                       &cmd.dma);
+       cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size, &cmd.dma);
        if (!cmd.va) {
                dev_err(&adapter->pdev->dev, "Memory alloc failure\n");
                status = -ENOMEM;
@@ -2521,8 +2571,8 @@ int be_cmd_get_phy_info(struct be_adapter *adapter)
        req = cmd.va;
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                       OPCODE_COMMON_GET_PHY_DETAILS, sizeof(*req),
-                       wrb, &cmd);
+                              OPCODE_COMMON_GET_PHY_DETAILS, sizeof(*req),
+                              wrb, &cmd);
 
        status = be_mcc_notify_wait(adapter);
        if (!status) {
@@ -2544,8 +2594,7 @@ int be_cmd_get_phy_info(struct be_adapter *adapter)
                                BE_SUPPORTED_SPEED_1GBPS;
                }
        }
-       pci_free_consistent(adapter->pdev, cmd.size,
-                               cmd.va, cmd.dma);
+       pci_free_consistent(adapter->pdev, cmd.size, cmd.va, cmd.dma);
 err:
        spin_unlock_bh(&adapter->mcc_lock);
        return status;
@@ -2568,7 +2617,7 @@ int be_cmd_set_qos(struct be_adapter *adapter, u32 bps, u32 domain)
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                       OPCODE_COMMON_SET_QOS, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_SET_QOS, sizeof(*req), wrb, NULL);
 
        req->hdr.domain = domain;
        req->valid_bits = cpu_to_le32(BE_QOS_BITS_NIC);
@@ -2597,10 +2646,9 @@ int be_cmd_get_cntl_attributes(struct be_adapter *adapter)
        memset(&attribs_cmd, 0, sizeof(struct be_dma_mem));
        attribs_cmd.size = sizeof(struct be_cmd_resp_cntl_attribs);
        attribs_cmd.va = pci_alloc_consistent(adapter->pdev, attribs_cmd.size,
-                                               &attribs_cmd.dma);
+                                             &attribs_cmd.dma);
        if (!attribs_cmd.va) {
-               dev_err(&adapter->pdev->dev,
-                               "Memory allocation failure\n");
+               dev_err(&adapter->pdev->dev, "Memory allocation failure\n");
                status = -ENOMEM;
                goto err;
        }
@@ -2613,8 +2661,8 @@ int be_cmd_get_cntl_attributes(struct be_adapter *adapter)
        req = attribs_cmd.va;
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                        OPCODE_COMMON_GET_CNTL_ATTRIBUTES, payload_len, wrb,
-                       &attribs_cmd);
+                              OPCODE_COMMON_GET_CNTL_ATTRIBUTES, payload_len,
+                              wrb, &attribs_cmd);
 
        status = be_mbox_notify_wait(adapter);
        if (!status) {
@@ -2649,7 +2697,8 @@ int be_cmd_req_native_mode(struct be_adapter *adapter)
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-               OPCODE_COMMON_SET_DRIVER_FUNCTION_CAP, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_SET_DRIVER_FUNCTION_CAP,
+                              sizeof(*req), wrb, NULL);
 
        req->valid_cap_flags = cpu_to_le32(CAPABILITY_SW_TIMESTAMPS |
                                CAPABILITY_BE3_NATIVE_ERX_API);
@@ -2762,12 +2811,12 @@ int be_cmd_get_mac_from_list(struct be_adapter *adapter, u8 *mac,
        memset(&get_mac_list_cmd, 0, sizeof(struct be_dma_mem));
        get_mac_list_cmd.size = sizeof(struct be_cmd_resp_get_mac_list);
        get_mac_list_cmd.va = pci_alloc_consistent(adapter->pdev,
-                       get_mac_list_cmd.size,
-                       &get_mac_list_cmd.dma);
+                                                  get_mac_list_cmd.size,
+                                                  &get_mac_list_cmd.dma);
 
        if (!get_mac_list_cmd.va) {
                dev_err(&adapter->pdev->dev,
-                               "Memory allocation failure during GET_MAC_LIST\n");
+                       "Memory allocation failure during GET_MAC_LIST\n");
                return -ENOMEM;
        }
 
@@ -2831,18 +2880,18 @@ int be_cmd_get_mac_from_list(struct be_adapter *adapter, u8 *mac,
                /* If no active mac_id found, return first mac addr */
                *pmac_id_valid = false;
                memcpy(mac, resp->macaddr_list[0].mac_addr_id.macaddr,
-                                                               ETH_ALEN);
+                      ETH_ALEN);
        }
 
 out:
        spin_unlock_bh(&adapter->mcc_lock);
        pci_free_consistent(adapter->pdev, get_mac_list_cmd.size,
-                       get_mac_list_cmd.va, get_mac_list_cmd.dma);
+                           get_mac_list_cmd.va, get_mac_list_cmd.dma);
        return status;
 }
 
-int be_cmd_get_active_mac(struct be_adapter *adapter, u32 curr_pmac_id, u8 *mac,
-                         u32 if_handle, bool active, u32 domain)
+int be_cmd_get_active_mac(struct be_adapter *adapter, u32 curr_pmac_id,
+                         u8 *mac, u32 if_handle, bool active, u32 domain)
 {
 
        if (!active)
@@ -2892,7 +2941,7 @@ int be_cmd_set_mac_list(struct be_adapter *adapter, u8 *mac_array,
        memset(&cmd, 0, sizeof(struct be_dma_mem));
        cmd.size = sizeof(struct be_cmd_req_set_mac_list);
        cmd.va = dma_alloc_coherent(&adapter->pdev->dev, cmd.size,
-                       &cmd.dma, GFP_KERNEL);
+                                   &cmd.dma, GFP_KERNEL);
        if (!cmd.va)
                return -ENOMEM;
 
@@ -2906,8 +2955,8 @@ int be_cmd_set_mac_list(struct be_adapter *adapter, u8 *mac_array,
 
        req = cmd.va;
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                               OPCODE_COMMON_SET_MAC_LIST, sizeof(*req),
-                               wrb, &cmd);
+                              OPCODE_COMMON_SET_MAC_LIST, sizeof(*req),
+                              wrb, &cmd);
 
        req->hdr.domain = domain;
        req->mac_count = mac_count;
@@ -2917,8 +2966,7 @@ int be_cmd_set_mac_list(struct be_adapter *adapter, u8 *mac_array,
        status = be_mcc_notify_wait(adapter);
 
 err:
-       dma_free_coherent(&adapter->pdev->dev, cmd.size,
-                               cmd.va, cmd.dma);
+       dma_free_coherent(&adapter->pdev->dev, cmd.size, cmd.va, cmd.dma);
        spin_unlock_bh(&adapter->mcc_lock);
        return status;
 }
@@ -2963,7 +3011,8 @@ int be_cmd_set_hsw_config(struct be_adapter *adapter, u16 pvid,
        ctxt = &req->context;
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                       OPCODE_COMMON_SET_HSW_CONFIG, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_SET_HSW_CONFIG, sizeof(*req), wrb,
+                              NULL);
 
        req->hdr.domain = domain;
        AMAP_SET_BITS(struct amap_set_hsw_context, interface_id, ctxt, intf_id);
@@ -3009,7 +3058,8 @@ int be_cmd_get_hsw_config(struct be_adapter *adapter, u16 *pvid,
        ctxt = &req->context;
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
-                       OPCODE_COMMON_GET_HSW_CONFIG, sizeof(*req), wrb, NULL);
+                              OPCODE_COMMON_GET_HSW_CONFIG, sizeof(*req), wrb,
+                              NULL);
 
        req->hdr.domain = domain;
        AMAP_SET_BITS(struct amap_get_hsw_req_context, interface_id,
@@ -3027,10 +3077,9 @@ int be_cmd_get_hsw_config(struct be_adapter *adapter, u16 *pvid,
        if (!status) {
                struct be_cmd_resp_get_hsw_config *resp =
                                                embedded_payload(wrb);
-               be_dws_le_to_cpu(&resp->context,
-                                               sizeof(resp->context));
+               be_dws_le_to_cpu(&resp->context, sizeof(resp->context));
                vid = AMAP_GET_BITS(struct amap_get_hsw_resp_context,
-                                                       pvid, &resp->context);
+                                   pvid, &resp->context);
                if (pvid)
                        *pvid = le16_to_cpu(vid);
                if (mode)
@@ -3062,11 +3111,9 @@ int be_cmd_get_acpi_wol_cap(struct be_adapter *adapter)
 
        memset(&cmd, 0, sizeof(struct be_dma_mem));
        cmd.size = sizeof(struct be_cmd_resp_acpi_wol_magic_config_v1);
-       cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size,
-                                              &cmd.dma);
+       cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size, &cmd.dma);
        if (!cmd.va) {
-               dev_err(&adapter->pdev->dev,
-                               "Memory allocation failure\n");
+               dev_err(&adapter->pdev->dev, "Memory allocation failure\n");
                status = -ENOMEM;
                goto err;
        }
@@ -3349,8 +3396,7 @@ int be_cmd_get_func_config(struct be_adapter *adapter, struct be_resources *res)
 
        memset(&cmd, 0, sizeof(struct be_dma_mem));
        cmd.size = sizeof(struct be_cmd_resp_get_func_config);
-       cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size,
-                                     &cmd.dma);
+       cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size, &cmd.dma);
        if (!cmd.va) {
                dev_err(&adapter->pdev->dev, "Memory alloc failure\n");
                status = -ENOMEM;
@@ -3396,7 +3442,7 @@ int be_cmd_get_func_config(struct be_adapter *adapter, struct be_resources *res)
 
 /* Uses mbox */
 static int be_cmd_get_profile_config_mbox(struct be_adapter *adapter,
-                                       u8 domain, struct be_dma_mem *cmd)
+                                         u8 domain, struct be_dma_mem *cmd)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_get_profile_config *req;
@@ -3424,7 +3470,7 @@ static int be_cmd_get_profile_config_mbox(struct be_adapter *adapter,
 
 /* Uses sync mcc */
 static int be_cmd_get_profile_config_mccq(struct be_adapter *adapter,
-                                       u8 domain, struct be_dma_mem *cmd)
+                                         u8 domain, struct be_dma_mem *cmd)
 {
        struct be_mcc_wrb *wrb;
        struct be_cmd_req_get_profile_config *req;
@@ -3484,8 +3530,8 @@ int be_cmd_get_profile_config(struct be_adapter *adapter,
        resp = cmd.va;
        desc_count = le32_to_cpu(resp->desc_count);
 
-       pcie =  be_get_pcie_desc(adapter->pdev->devfn, resp->func_param,
-                                desc_count);
+       pcie = be_get_pcie_desc(adapter->pdev->devfn, resp->func_param,
+                               desc_count);
        if (pcie)
                res->max_vfs = le16_to_cpu(pcie->num_vfs);
 
@@ -3548,33 +3594,47 @@ void be_reset_nic_desc(struct be_nic_res_desc *nic)
        nic->cq_count = 0xFFFF;
        nic->toe_conn_count = 0xFFFF;
        nic->eq_count = 0xFFFF;
+       nic->iface_count = 0xFFFF;
        nic->link_param = 0xFF;
+       nic->channel_id_param = cpu_to_le16(0xF000);
        nic->acpi_params = 0xFF;
        nic->wol_param = 0x0F;
-       nic->bw_min = 0xFFFFFFFF;
+       nic->tunnel_iface_count = 0xFFFF;
+       nic->direct_tenant_iface_count = 0xFFFF;
        nic->bw_max = 0xFFFFFFFF;
 }
 
-int be_cmd_config_qos(struct be_adapter *adapter, u32 bps, u8 domain)
+int be_cmd_config_qos(struct be_adapter *adapter, u32 max_rate, u16 link_speed,
+                     u8 domain)
 {
-       if (lancer_chip(adapter)) {
-               struct be_nic_res_desc nic_desc;
+       struct be_nic_res_desc nic_desc;
+       u32 bw_percent;
+       u16 version = 0;
+
+       if (BE3_chip(adapter))
+               return be_cmd_set_qos(adapter, max_rate / 10, domain);
 
-               be_reset_nic_desc(&nic_desc);
+       be_reset_nic_desc(&nic_desc);
+       nic_desc.pf_num = adapter->pf_number;
+       nic_desc.vf_num = domain;
+       if (lancer_chip(adapter)) {
                nic_desc.hdr.desc_type = NIC_RESOURCE_DESC_TYPE_V0;
                nic_desc.hdr.desc_len = RESOURCE_DESC_SIZE_V0;
                nic_desc.flags = (1 << QUN_SHIFT) | (1 << IMM_SHIFT) |
                                        (1 << NOSV_SHIFT);
-               nic_desc.pf_num = adapter->pf_number;
-               nic_desc.vf_num = domain;
-               nic_desc.bw_max = cpu_to_le32(bps);
-
-               return be_cmd_set_profile_config(adapter, &nic_desc,
-                                                RESOURCE_DESC_SIZE_V0,
-                                                0, domain);
+               nic_desc.bw_max = cpu_to_le32(max_rate / 10);
        } else {
-               return be_cmd_set_qos(adapter, bps, domain);
+               version = 1;
+               nic_desc.hdr.desc_type = NIC_RESOURCE_DESC_TYPE_V1;
+               nic_desc.hdr.desc_len = RESOURCE_DESC_SIZE_V1;
+               nic_desc.flags = (1 << IMM_SHIFT) | (1 << NOSV_SHIFT);
+               bw_percent = max_rate ? (max_rate * 100) / link_speed : 100;
+               nic_desc.bw_max = cpu_to_le32(bw_percent);
        }
+
+       return be_cmd_set_profile_config(adapter, &nic_desc,
+                                        nic_desc.hdr.desc_len,
+                                        version, domain);
 }
 
 int be_cmd_manage_iface(struct be_adapter *adapter, u32 iface, u8 op)
@@ -3859,7 +3919,7 @@ int be_cmd_set_logical_link_config(struct be_adapter *adapter,
 }
 
 int be_roce_mcc_cmd(void *netdev_handle, void *wrb_payload,
-                       int wrb_payload_size, u16 *cmd_status, u16 *ext_status)
+                   int wrb_payload_size, u16 *cmd_status, u16 *ext_status)
 {
        struct be_adapter *adapter = netdev_priv(netdev_handle);
        struct be_mcc_wrb *wrb;
index b60e4d53c1c9a9f29be9d1c2c07099dab28b378a..3e0a6b243806d3bed3d7388899a76bcbd360ec1b 100644 (file)
@@ -50,7 +50,7 @@ struct be_mcc_wrb {
 #define CQE_FLAGS_CONSUMED_MASK        (1 << 27)
 
 /* Completion Status */
-enum {
+enum mcc_base_status {
        MCC_STATUS_SUCCESS = 0,
        MCC_STATUS_FAILED = 1,
        MCC_STATUS_ILLEGAL_REQUEST = 2,
@@ -60,12 +60,25 @@ enum {
        MCC_STATUS_NOT_SUPPORTED = 66
 };
 
-#define MCC_ADDL_STS_INSUFFICIENT_RESOURCES    0x16
+/* Additional status */
+enum mcc_addl_status {
+       MCC_ADDL_STATUS_INSUFFICIENT_RESOURCES = 0x16,
+       MCC_ADDL_STATUS_FLASH_IMAGE_CRC_MISMATCH = 0x4d,
+       MCC_ADDL_STATUS_TOO_MANY_INTERFACES = 0x4a
+};
+
+#define CQE_BASE_STATUS_MASK           0xFFFF
+#define CQE_BASE_STATUS_SHIFT          0       /* bits 0 - 15 */
+#define CQE_ADDL_STATUS_MASK           0xFF
+#define CQE_ADDL_STATUS_SHIFT          16      /* bits 16 - 31 */
 
-#define CQE_STATUS_COMPL_MASK          0xFFFF
-#define CQE_STATUS_COMPL_SHIFT         0       /* bits 0 - 15 */
-#define CQE_STATUS_EXTD_MASK           0xFFFF
-#define CQE_STATUS_EXTD_SHIFT          16      /* bits 16 - 31 */
+#define base_status(status)            \
+               ((enum mcc_base_status) \
+                       (status > 0 ? (status & CQE_BASE_STATUS_MASK) : 0))
+#define addl_status(status)            \
+               ((enum mcc_addl_status) \
+                       (status > 0 ? (status >> CQE_ADDL_STATUS_SHIFT) & \
+                                       CQE_ADDL_STATUS_MASK : 0))
 
 struct be_mcc_compl {
        u32 status;             /* dword 0 */
@@ -74,13 +87,13 @@ struct be_mcc_compl {
        u32 flags;              /* dword 3 */
 };
 
-/* When the async bit of mcc_compl is set, the last 4 bytes of
- * mcc_compl is interpreted as follows:
+/* When the async bit of mcc_compl flags is set, flags
+ * is interpreted as follows:
  */
-#define ASYNC_TRAILER_EVENT_CODE_SHIFT 8       /* bits 8 - 15 */
-#define ASYNC_TRAILER_EVENT_CODE_MASK  0xFF
-#define ASYNC_TRAILER_EVENT_TYPE_SHIFT 16
-#define ASYNC_TRAILER_EVENT_TYPE_MASK  0xFF
+#define ASYNC_EVENT_CODE_SHIFT         8       /* bits 8 - 15 */
+#define ASYNC_EVENT_CODE_MASK          0xFF
+#define ASYNC_EVENT_TYPE_SHIFT         16
+#define ASYNC_EVENT_TYPE_MASK          0xFF
 #define ASYNC_EVENT_CODE_LINK_STATE    0x1
 #define ASYNC_EVENT_CODE_GRP_5         0x5
 #define ASYNC_EVENT_QOS_SPEED          0x1
@@ -89,10 +102,6 @@ struct be_mcc_compl {
 #define ASYNC_EVENT_CODE_QNQ           0x6
 #define ASYNC_DEBUG_EVENT_TYPE_QNQ     1
 
-struct be_async_event_trailer {
-       u32 code;
-};
-
 enum {
        LINK_DOWN       = 0x0,
        LINK_UP         = 0x1
@@ -100,7 +109,7 @@ enum {
 #define LINK_STATUS_MASK                       0x1
 #define LOGICAL_LINK_STATUS_MASK               0x2
 
-/* When the event code of an async trailer is link-state, the mcc_compl
+/* When the event code of compl->flags is link-state, the mcc_compl
  * must be interpreted as follows
  */
 struct be_async_event_link_state {
@@ -110,10 +119,10 @@ struct be_async_event_link_state {
        u8 port_speed;
        u8 port_fault;
        u8 rsvd0[7];
-       struct be_async_event_trailer trailer;
+       u32 flags;
 } __packed;
 
-/* When the event code of an async trailer is GRP-5 and event_type is QOS_SPEED
+/* When the event code of compl->flags is GRP-5 and event_type is QOS_SPEED
  * the mcc_compl must be interpreted as follows
  */
 struct be_async_event_grp5_qos_link_speed {
@@ -121,10 +130,10 @@ struct be_async_event_grp5_qos_link_speed {
        u8 rsvd[5];
        u16 qos_link_speed;
        u32 event_tag;
-       struct be_async_event_trailer trailer;
+       u32 flags;
 } __packed;
 
-/* When the event code of an async trailer is GRP5 and event type is
+/* When the event code of compl->flags is GRP5 and event type is
  * CoS-Priority, the mcc_compl must be interpreted as follows
  */
 struct be_async_event_grp5_cos_priority {
@@ -134,10 +143,10 @@ struct be_async_event_grp5_cos_priority {
        u8 valid;
        u8 rsvd0;
        u8 event_tag;
-       struct be_async_event_trailer trailer;
+       u32 flags;
 } __packed;
 
-/* When the event code of an async trailer is GRP5 and event type is
+/* When the event code of compl->flags is GRP5 and event type is
  * PVID state, the mcc_compl must be interpreted as follows
  */
 struct be_async_event_grp5_pvid_state {
@@ -146,7 +155,7 @@ struct be_async_event_grp5_pvid_state {
        u16 tag;
        u32 event_tag;
        u32 rsvd1;
-       struct be_async_event_trailer trailer;
+       u32 flags;
 } __packed;
 
 /* async event indicating outer VLAN tag in QnQ */
@@ -156,7 +165,7 @@ struct be_async_event_qnq {
        u16 vlan_tag;
        u32 event_tag;
        u8 rsvd1[4];
-       struct be_async_event_trailer trailer;
+       u32 flags;
 } __packed;
 
 struct be_mcc_mailbox {
@@ -258,8 +267,8 @@ struct be_cmd_resp_hdr {
        u8 opcode;              /* dword 0 */
        u8 subsystem;           /* dword 0 */
        u8 rsvd[2];             /* dword 0 */
-       u8 status;              /* dword 1 */
-       u8 add_status;          /* dword 1 */
+       u8 base_status;         /* dword 1 */
+       u8 addl_status;         /* dword 1 */
        u8 rsvd1[2];            /* dword 1 */
        u32 response_length;    /* dword 2 */
        u32 actual_resp_len;    /* dword 3 */
@@ -1186,7 +1195,8 @@ struct be_cmd_read_flash_crc {
        struct flashrom_params params;
        u8 crc[4];
        u8 rsvd[4];
-};
+} __packed;
+
 /**************** Lancer Firmware Flash ************/
 struct amap_lancer_write_obj_context {
        u8 write_length[24];
@@ -1891,16 +1901,20 @@ struct be_nic_res_desc {
        u16 cq_count;
        u16 toe_conn_count;
        u16 eq_count;
-       u32 rsvd5;
+       u16 vlan_id;
+       u16 iface_count;
        u32 cap_flags;
        u8 link_param;
-       u8 rsvd6[3];
+       u8 rsvd6;
+       u16 channel_id_param;
        u32 bw_min;
        u32 bw_max;
        u8 acpi_params;
        u8 wol_param;
        u16 rsvd7;
-       u32 rsvd8[7];
+       u16 tunnel_iface_count;
+       u16 direct_tenant_iface_count;
+       u32 rsvd8[6];
 } __packed;
 
 /************ Multi-Channel type ***********/
@@ -2060,7 +2074,7 @@ 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 promiscuous);
+                      u32 num);
 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);
@@ -2068,7 +2082,7 @@ int be_cmd_query_fw_cfg(struct be_adapter *adapter, u32 *port_num,
                        u32 *function_mode, u32 *function_caps, u16 *asic_rev);
 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);
+                     u32 rss_hash_opts, u16 table_size, const u8 *rss_hkey);
 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);
@@ -2084,7 +2098,7 @@ 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);
 int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc,
-                        int offset);
+                         u16 optype, 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);
@@ -2101,7 +2115,8 @@ int be_cmd_get_seeprom_data(struct be_adapter *adapter,
 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_config_qos(struct be_adapter *adapter, u32 bps, u8 domain);
+int be_cmd_config_qos(struct be_adapter *adapter, u32 max_rate,
+                     u16 link_speed, u8 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);
index 15ba96cba65df1ba051cc3b2e49b72640d92c217..e2da4d20dd3de7441f8137f1be8a9ecdf5cec615 100644 (file)
@@ -132,6 +132,7 @@ static const struct be_ethtool_stat et_rx_stats[] = {
        {DRVSTAT_RX_INFO(rx_bytes)},/* If moving this member see above note */
        {DRVSTAT_RX_INFO(rx_pkts)}, /* If moving this member see above note */
        {DRVSTAT_RX_INFO(rx_compl)},
+       {DRVSTAT_RX_INFO(rx_compl_err)},
        {DRVSTAT_RX_INFO(rx_mcast_pkts)},
        /* Number of page allocation failures while posting receive buffers
         * to HW.
@@ -181,7 +182,7 @@ static const char et_self_tests[][ETH_GSTRING_LEN] = {
 #define BE_NO_LOOPBACK 0xff
 
 static void be_get_drvinfo(struct net_device *netdev,
-                               struct ethtool_drvinfo *drvinfo)
+                          struct ethtool_drvinfo *drvinfo)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
 
@@ -201,8 +202,7 @@ static void be_get_drvinfo(struct net_device *netdev,
        drvinfo->eedump_len = 0;
 }
 
-static u32
-lancer_cmd_get_file_len(struct be_adapter *adapter, u8 *file_name)
+static u32 lancer_cmd_get_file_len(struct be_adapter *adapter, u8 *file_name)
 {
        u32 data_read = 0, eof;
        u8 addn_status;
@@ -212,14 +212,14 @@ lancer_cmd_get_file_len(struct be_adapter *adapter, u8 *file_name)
        memset(&data_len_cmd, 0, sizeof(data_len_cmd));
        /* data_offset and data_size should be 0 to get reg len */
        status = lancer_cmd_read_object(adapter, &data_len_cmd, 0, 0,
-                               file_name, &data_read, &eof, &addn_status);
+                                       file_name, &data_read, &eof,
+                                       &addn_status);
 
        return data_read;
 }
 
-static int
-lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name,
-               u32 buf_len, void *buf)
+static int lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name,
+                               u32 buf_len, void *buf)
 {
        struct be_dma_mem read_cmd;
        u32 read_len = 0, total_read_len = 0, chunk_size;
@@ -229,11 +229,11 @@ lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name,
 
        read_cmd.size = LANCER_READ_FILE_CHUNK;
        read_cmd.va = pci_alloc_consistent(adapter->pdev, read_cmd.size,
-                       &read_cmd.dma);
+                                          &read_cmd.dma);
 
        if (!read_cmd.va) {
                dev_err(&adapter->pdev->dev,
-                               "Memory allocation failure while reading dump\n");
+                       "Memory allocation failure while reading dump\n");
                return -ENOMEM;
        }
 
@@ -242,8 +242,8 @@ lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name,
                                LANCER_READ_FILE_CHUNK);
                chunk_size = ALIGN(chunk_size, 4);
                status = lancer_cmd_read_object(adapter, &read_cmd, chunk_size,
-                               total_read_len, file_name, &read_len,
-                               &eof, &addn_status);
+                                               total_read_len, file_name,
+                                               &read_len, &eof, &addn_status);
                if (!status) {
                        memcpy(buf + total_read_len, read_cmd.va, read_len);
                        total_read_len += read_len;
@@ -254,13 +254,12 @@ lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name,
                }
        }
        pci_free_consistent(adapter->pdev, read_cmd.size, read_cmd.va,
-                       read_cmd.dma);
+                           read_cmd.dma);
 
        return status;
 }
 
-static int
-be_get_reg_len(struct net_device *netdev)
+static int be_get_reg_len(struct net_device *netdev)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
        u32 log_size = 0;
@@ -271,7 +270,7 @@ be_get_reg_len(struct net_device *netdev)
        if (be_physfn(adapter)) {
                if (lancer_chip(adapter))
                        log_size = lancer_cmd_get_file_len(adapter,
-                                       LANCER_FW_DUMP_FILE);
+                                                          LANCER_FW_DUMP_FILE);
                else
                        be_cmd_get_reg_len(adapter, &log_size);
        }
@@ -287,7 +286,7 @@ be_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *buf)
                memset(buf, 0, regs->len);
                if (lancer_chip(adapter))
                        lancer_cmd_read_file(adapter, LANCER_FW_DUMP_FILE,
-                                       regs->len, buf);
+                                            regs->len, buf);
                else
                        be_cmd_get_regs(adapter, regs->len, buf);
        }
@@ -337,9 +336,8 @@ static int be_set_coalesce(struct net_device *netdev,
        return 0;
 }
 
-static void
-be_get_ethtool_stats(struct net_device *netdev,
-               struct ethtool_stats *stats, uint64_t *data)
+static void be_get_ethtool_stats(struct net_device *netdev,
+                                struct ethtool_stats *stats, uint64_t *data)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
        struct be_rx_obj *rxo;
@@ -390,9 +388,8 @@ be_get_ethtool_stats(struct net_device *netdev,
        }
 }
 
-static void
-be_get_stat_strings(struct net_device *netdev, uint32_t stringset,
-               uint8_t *data)
+static void be_get_stat_strings(struct net_device *netdev, uint32_t stringset,
+                               uint8_t *data)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
        int i, j;
@@ -642,16 +639,15 @@ be_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *ecmd)
        adapter->rx_fc = ecmd->rx_pause;
 
        status = be_cmd_set_flow_control(adapter,
-                                       adapter->tx_fc, adapter->rx_fc);
+                                        adapter->tx_fc, adapter->rx_fc);
        if (status)
                dev_warn(&adapter->pdev->dev, "Pause param set failed.\n");
 
        return status;
 }
 
-static int
-be_set_phys_id(struct net_device *netdev,
-              enum ethtool_phys_id_state state)
+static int be_set_phys_id(struct net_device *netdev,
+                         enum ethtool_phys_id_state state)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
 
@@ -708,8 +704,7 @@ static int be_set_dump(struct net_device *netdev, struct ethtool_dump *dump)
        return status;
 }
 
-static void
-be_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
+static void be_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
 
@@ -723,8 +718,7 @@ be_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
        memset(&wol->sopass, 0, sizeof(wol->sopass));
 }
 
-static int
-be_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
+static int be_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
 
@@ -744,8 +738,7 @@ be_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
        return 0;
 }
 
-static int
-be_test_ddr_dma(struct be_adapter *adapter)
+static int be_test_ddr_dma(struct be_adapter *adapter)
 {
        int ret, i;
        struct be_dma_mem ddrdma_cmd;
@@ -761,7 +754,7 @@ be_test_ddr_dma(struct be_adapter *adapter)
 
        for (i = 0; i < 2; i++) {
                ret = be_cmd_ddr_dma_test(adapter, pattern[i],
-                                       4096, &ddrdma_cmd);
+                                         4096, &ddrdma_cmd);
                if (ret != 0)
                        goto err;
        }
@@ -773,20 +766,17 @@ be_test_ddr_dma(struct be_adapter *adapter)
 }
 
 static u64 be_loopback_test(struct be_adapter *adapter, u8 loopback_type,
-                               u64 *status)
+                           u64 *status)
 {
-       be_cmd_set_loopback(adapter, adapter->hba_port_num,
-                               loopback_type, 1);
+       be_cmd_set_loopback(adapter, adapter->hba_port_num, loopback_type, 1);
        *status = be_cmd_loopback_test(adapter, adapter->hba_port_num,
-                               loopback_type, 1500,
-                               2, 0xabc);
-       be_cmd_set_loopback(adapter, adapter->hba_port_num,
-                               BE_NO_LOOPBACK, 1);
+                                      loopback_type, 1500, 2, 0xabc);
+       be_cmd_set_loopback(adapter, adapter->hba_port_num, BE_NO_LOOPBACK, 1);
        return *status;
 }
 
-static void
-be_self_test(struct net_device *netdev, struct ethtool_test *test, u64 *data)
+static void be_self_test(struct net_device *netdev, struct ethtool_test *test,
+                        u64 *data)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
        int status;
@@ -801,12 +791,10 @@ be_self_test(struct net_device *netdev, struct ethtool_test *test, u64 *data)
        memset(data, 0, sizeof(u64) * ETHTOOL_TESTS_NUM);
 
        if (test->flags & ETH_TEST_FL_OFFLINE) {
-               if (be_loopback_test(adapter, BE_MAC_LOOPBACK,
-                                    &data[0]) != 0)
+               if (be_loopback_test(adapter, BE_MAC_LOOPBACK, &data[0]) != 0)
                        test->flags |= ETH_TEST_FL_FAILED;
 
-               if (be_loopback_test(adapter, BE_PHY_LOOPBACK,
-                                    &data[1]) != 0)
+               if (be_loopback_test(adapter, BE_PHY_LOOPBACK, &data[1]) != 0)
                        test->flags |= ETH_TEST_FL_FAILED;
 
                if (test->flags & ETH_TEST_FL_EXTERNAL_LB) {
@@ -832,16 +820,14 @@ be_self_test(struct net_device *netdev, struct ethtool_test *test, u64 *data)
        }
 }
 
-static int
-be_do_flash(struct net_device *netdev, struct ethtool_flash *efl)
+static int be_do_flash(struct net_device *netdev, struct ethtool_flash *efl)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
 
        return be_load_fw(adapter, efl->data);
 }
 
-static int
-be_get_eeprom_len(struct net_device *netdev)
+static int be_get_eeprom_len(struct net_device *netdev)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
 
@@ -851,18 +837,17 @@ be_get_eeprom_len(struct net_device *netdev)
        if (lancer_chip(adapter)) {
                if (be_physfn(adapter))
                        return lancer_cmd_get_file_len(adapter,
-                                       LANCER_VPD_PF_FILE);
+                                                      LANCER_VPD_PF_FILE);
                else
                        return lancer_cmd_get_file_len(adapter,
-                                       LANCER_VPD_VF_FILE);
+                                                      LANCER_VPD_VF_FILE);
        } else {
                return BE_READ_SEEPROM_LEN;
        }
 }
 
-static int
-be_read_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom,
-                       uint8_t *data)
+static int be_read_eeprom(struct net_device *netdev,
+                         struct ethtool_eeprom *eeprom, uint8_t *data)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
        struct be_dma_mem eeprom_cmd;
@@ -875,10 +860,10 @@ be_read_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom,
        if (lancer_chip(adapter)) {
                if (be_physfn(adapter))
                        return lancer_cmd_read_file(adapter, LANCER_VPD_PF_FILE,
-                                       eeprom->len, data);
+                                                   eeprom->len, data);
                else
                        return lancer_cmd_read_file(adapter, LANCER_VPD_VF_FILE,
-                                       eeprom->len, data);
+                                                   eeprom->len, data);
        }
 
        eeprom->magic = BE_VENDOR_ID | (adapter->pdev->device<<16);
@@ -933,27 +918,27 @@ static u64 be_get_rss_hash_opts(struct be_adapter *adapter, u64 flow_type)
 
        switch (flow_type) {
        case TCP_V4_FLOW:
-               if (adapter->rss_flags & RSS_ENABLE_IPV4)
+               if (adapter->rss_info.rss_flags & RSS_ENABLE_IPV4)
                        data |= RXH_IP_DST | RXH_IP_SRC;
-               if (adapter->rss_flags & RSS_ENABLE_TCP_IPV4)
+               if (adapter->rss_info.rss_flags & RSS_ENABLE_TCP_IPV4)
                        data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
                break;
        case UDP_V4_FLOW:
-               if (adapter->rss_flags & RSS_ENABLE_IPV4)
+               if (adapter->rss_info.rss_flags & RSS_ENABLE_IPV4)
                        data |= RXH_IP_DST | RXH_IP_SRC;
-               if (adapter->rss_flags & RSS_ENABLE_UDP_IPV4)
+               if (adapter->rss_info.rss_flags & RSS_ENABLE_UDP_IPV4)
                        data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
                break;
        case TCP_V6_FLOW:
-               if (adapter->rss_flags & RSS_ENABLE_IPV6)
+               if (adapter->rss_info.rss_flags & RSS_ENABLE_IPV6)
                        data |= RXH_IP_DST | RXH_IP_SRC;
-               if (adapter->rss_flags & RSS_ENABLE_TCP_IPV6)
+               if (adapter->rss_info.rss_flags & RSS_ENABLE_TCP_IPV6)
                        data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
                break;
        case UDP_V6_FLOW:
-               if (adapter->rss_flags & RSS_ENABLE_IPV6)
+               if (adapter->rss_info.rss_flags & RSS_ENABLE_IPV6)
                        data |= RXH_IP_DST | RXH_IP_SRC;
-               if (adapter->rss_flags & RSS_ENABLE_UDP_IPV6)
+               if (adapter->rss_info.rss_flags & RSS_ENABLE_UDP_IPV6)
                        data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
                break;
        }
@@ -962,7 +947,7 @@ static u64 be_get_rss_hash_opts(struct be_adapter *adapter, u64 flow_type)
 }
 
 static int be_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
-                     u32 *rule_locs)
+                       u32 *rule_locs)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
 
@@ -992,7 +977,7 @@ static int be_set_rss_hash_opts(struct be_adapter *adapter,
        struct be_rx_obj *rxo;
        int status = 0, i, j;
        u8 rsstable[128];
-       u32 rss_flags = adapter->rss_flags;
+       u32 rss_flags = adapter->rss_info.rss_flags;
 
        if (cmd->data != L3_RSS_FLAGS &&
            cmd->data != (L3_RSS_FLAGS | L4_RSS_FLAGS))
@@ -1039,7 +1024,7 @@ static int be_set_rss_hash_opts(struct be_adapter *adapter,
                return -EINVAL;
        }
 
-       if (rss_flags == adapter->rss_flags)
+       if (rss_flags == adapter->rss_info.rss_flags)
                return status;
 
        if (be_multi_rxq(adapter)) {
@@ -1051,9 +1036,11 @@ static int be_set_rss_hash_opts(struct be_adapter *adapter,
                        }
                }
        }
-       status = be_cmd_rss_config(adapter, rsstable, rss_flags, 128);
+
+       status = be_cmd_rss_config(adapter, adapter->rss_info.rsstable,
+                                  rss_flags, 128, adapter->rss_info.rss_hkey);
        if (!status)
-               adapter->rss_flags = rss_flags;
+               adapter->rss_info.rss_flags = rss_flags;
 
        return status;
 }
@@ -1103,6 +1090,69 @@ static int be_set_channels(struct net_device  *netdev,
        return be_update_queues(adapter);
 }
 
+static u32 be_get_rxfh_indir_size(struct net_device *netdev)
+{
+       return RSS_INDIR_TABLE_LEN;
+}
+
+static u32 be_get_rxfh_key_size(struct net_device *netdev)
+{
+       return RSS_HASH_KEY_LEN;
+}
+
+static int be_get_rxfh(struct net_device *netdev, u32 *indir, u8 *hkey)
+{
+       struct be_adapter *adapter = netdev_priv(netdev);
+       int i;
+       struct rss_info *rss = &adapter->rss_info;
+
+       if (indir) {
+               for (i = 0; i < RSS_INDIR_TABLE_LEN; i++)
+                       indir[i] = rss->rss_queue[i];
+       }
+
+       if (hkey)
+               memcpy(hkey, rss->rss_hkey, RSS_HASH_KEY_LEN);
+
+       return 0;
+}
+
+static int be_set_rxfh(struct net_device *netdev, const u32 *indir,
+                      const u8 *hkey)
+{
+       int rc = 0, i, j;
+       struct be_adapter *adapter = netdev_priv(netdev);
+       u8 rsstable[RSS_INDIR_TABLE_LEN];
+
+       if (indir) {
+               struct be_rx_obj *rxo;
+               for (i = 0; i < RSS_INDIR_TABLE_LEN; i++) {
+                       j = indir[i];
+                       rxo = &adapter->rx_obj[j];
+                       rsstable[i] = rxo->rss_id;
+                       adapter->rss_info.rss_queue[i] = j;
+               }
+       } else {
+               memcpy(rsstable, adapter->rss_info.rsstable,
+                      RSS_INDIR_TABLE_LEN);
+       }
+
+       if (!hkey)
+               hkey =  adapter->rss_info.rss_hkey;
+
+       rc = be_cmd_rss_config(adapter, rsstable,
+                       adapter->rss_info.rss_flags,
+                       RSS_INDIR_TABLE_LEN, hkey);
+       if (rc) {
+               adapter->rss_info.rss_flags = RSS_ENABLE_NONE;
+               return -EIO;
+       }
+       memcpy(adapter->rss_info.rss_hkey, hkey, RSS_HASH_KEY_LEN);
+       memcpy(adapter->rss_info.rsstable, rsstable,
+              RSS_INDIR_TABLE_LEN);
+       return 0;
+}
+
 const struct ethtool_ops be_ethtool_ops = {
        .get_settings = be_get_settings,
        .get_drvinfo = be_get_drvinfo,
@@ -1129,6 +1179,10 @@ const struct ethtool_ops be_ethtool_ops = {
        .self_test = be_self_test,
        .get_rxnfc = be_get_rxnfc,
        .set_rxnfc = be_set_rxnfc,
+       .get_rxfh_indir_size = be_get_rxfh_indir_size,
+       .get_rxfh_key_size = be_get_rxfh_key_size,
+       .get_rxfh = be_get_rxfh,
+       .set_rxfh = be_set_rxfh,
        .get_channels = be_get_channels,
        .set_channels = be_set_channels
 };
index 3bd198550edbb95a64602e28535e8eb56758a220..8840c64aaeca7daca310d0a5a38dfdbed790fb12 100644 (file)
 #define OPTYPE_FCOE_FW_ACTIVE          10
 #define OPTYPE_FCOE_FW_BACKUP          11
 #define OPTYPE_NCSI_FW                 13
+#define OPTYPE_REDBOOT_DIR             18
+#define OPTYPE_REDBOOT_CONFIG          19
+#define OPTYPE_SH_PHY_FW               21
+#define OPTYPE_FLASHISM_JUMPVECTOR     22
+#define OPTYPE_UFI_DIR                 23
 #define OPTYPE_PHY_FW                  99
 #define TN_8022                                13
 
-#define ILLEGAL_IOCTL_REQ              2
 #define FLASHROM_OPER_PHY_FLASH                9
 #define FLASHROM_OPER_PHY_SAVE         10
 #define FLASHROM_OPER_FLASH            1
 #define IMAGE_FIRMWARE_BACKUP_FCoE     178
 #define IMAGE_FIRMWARE_BACKUP_COMP_FCoE 179
 #define IMAGE_FIRMWARE_PHY             192
+#define IMAGE_REDBOOT_DIR              208
+#define IMAGE_REDBOOT_CONFIG           209
+#define IMAGE_UFI_DIR                  210
 #define IMAGE_BOOT_CODE                        224
 
 /************* Rx Packet Type Encoding **************/
@@ -534,7 +541,8 @@ struct flash_section_entry {
        u32 image_size;
        u32 cksum;
        u32 entry_point;
-       u32 rsvd0;
+       u16 optype;
+       u16 rsvd0;
        u32 rsvd1;
        u8 ver_data[32];
 } __packed;
index dc19bc5dec7732c02220e009ad08ffc393aa1c2c..6822b3d76d85960f3c9b48b3cd4d8c4bad44d87b 100644 (file)
@@ -134,7 +134,7 @@ static void be_queue_free(struct be_adapter *adapter, struct be_queue_info *q)
 }
 
 static int be_queue_alloc(struct be_adapter *adapter, struct be_queue_info *q,
-               u16 len, u16 entry_size)
+                         u16 len, u16 entry_size)
 {
        struct be_dma_mem *mem = &q->dma_mem;
 
@@ -154,7 +154,7 @@ static void be_reg_intr_set(struct be_adapter *adapter, bool enable)
        u32 reg, enabled;
 
        pci_read_config_dword(adapter->pdev, PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET,
-                               &reg);
+                             &reg);
        enabled = reg & MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK;
 
        if (!enabled && enable)
@@ -165,7 +165,7 @@ static void be_reg_intr_set(struct be_adapter *adapter, bool enable)
                return;
 
        pci_write_config_dword(adapter->pdev,
-                       PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET, reg);
+                              PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET, reg);
 }
 
 static void be_intr_set(struct be_adapter *adapter, bool enable)
@@ -206,12 +206,11 @@ static void be_txq_notify(struct be_adapter *adapter, struct be_tx_obj *txo,
 }
 
 static void be_eq_notify(struct be_adapter *adapter, u16 qid,
-               bool arm, bool clear_int, u16 num_popped)
+                        bool arm, bool clear_int, u16 num_popped)
 {
        u32 val = 0;
        val |= qid & DB_EQ_RING_ID_MASK;
-       val |= ((qid & DB_EQ_RING_ID_EXT_MASK) <<
-                       DB_EQ_RING_ID_EXT_MASK_SHIFT);
+       val |= ((qid & DB_EQ_RING_ID_EXT_MASK) << DB_EQ_RING_ID_EXT_MASK_SHIFT);
 
        if (adapter->eeh_error)
                return;
@@ -477,7 +476,7 @@ static void populate_be_v2_stats(struct be_adapter *adapter)
        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))  {
+       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;
@@ -491,8 +490,7 @@ static void populate_lancer_stats(struct be_adapter *adapter)
 {
 
        struct be_drv_stats *drvs = &adapter->drv_stats;
-       struct lancer_pport_stats *pport_stats =
-                                       pport_stats_from_cmd(adapter);
+       struct lancer_pport_stats *pport_stats = pport_stats_from_cmd(adapter);
 
        be_dws_le_to_cpu(pport_stats, sizeof(*pport_stats));
        drvs->rx_pause_frames = pport_stats->rx_pause_frames_lo;
@@ -539,8 +537,7 @@ static void accumulate_16bit_val(u32 *acc, u16 val)
 }
 
 static void populate_erx_stats(struct be_adapter *adapter,
-                       struct be_rx_obj *rxo,
-                       u32 erx_stat)
+                              struct be_rx_obj *rxo, u32 erx_stat)
 {
        if (!BEx_chip(adapter))
                rx_stats(rxo)->rx_drops_no_frags = erx_stat;
@@ -579,7 +576,7 @@ void be_parse_stats(struct be_adapter *adapter)
 }
 
 static struct rtnl_link_stats64 *be_get_stats64(struct net_device *netdev,
-                                       struct rtnl_link_stats64 *stats)
+                                               struct rtnl_link_stats64 *stats)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
        struct be_drv_stats *drvs = &adapter->drv_stats;
@@ -660,7 +657,8 @@ void be_link_status_update(struct be_adapter *adapter, u8 link_status)
 }
 
 static void be_tx_stats_update(struct be_tx_obj *txo,
-                       u32 wrb_cnt, u32 copied, u32 gso_segs, bool stopped)
+                              u32 wrb_cnt, u32 copied, u32 gso_segs,
+                              bool stopped)
 {
        struct be_tx_stats *stats = tx_stats(txo);
 
@@ -676,7 +674,7 @@ static void be_tx_stats_update(struct be_tx_obj *txo,
 
 /* Determine number of WRB entries needed to xmit data in an skb */
 static u32 wrb_cnt_for_skb(struct be_adapter *adapter, struct sk_buff *skb,
-                                                               bool *dummy)
+                          bool *dummy)
 {
        int cnt = (skb->len > skb->data_len);
 
@@ -704,7 +702,7 @@ static inline void wrb_fill(struct be_eth_wrb *wrb, u64 addr, int len)
 }
 
 static inline u16 be_get_tx_vlan_tag(struct be_adapter *adapter,
-                                       struct sk_buff *skb)
+                                    struct sk_buff *skb)
 {
        u8 vlan_prio;
        u16 vlan_tag;
@@ -733,7 +731,8 @@ static u16 skb_ip_proto(struct sk_buff *skb)
 }
 
 static void wrb_fill_hdr(struct be_adapter *adapter, struct be_eth_hdr_wrb *hdr,
-               struct sk_buff *skb, u32 wrb_cnt, u32 len, bool skip_hw_vlan)
+                        struct sk_buff *skb, u32 wrb_cnt, u32 len,
+                        bool skip_hw_vlan)
 {
        u16 vlan_tag, proto;
 
@@ -774,7 +773,7 @@ static void wrb_fill_hdr(struct be_adapter *adapter, struct be_eth_hdr_wrb *hdr,
 }
 
 static void unmap_tx_frag(struct device *dev, struct be_eth_wrb *wrb,
-               bool unmap_single)
+                         bool unmap_single)
 {
        dma_addr_t dma;
 
@@ -791,8 +790,8 @@ static void unmap_tx_frag(struct device *dev, struct be_eth_wrb *wrb,
 }
 
 static int make_tx_wrbs(struct be_adapter *adapter, struct be_queue_info *txq,
-               struct sk_buff *skb, u32 wrb_cnt, bool dummy_wrb,
-               bool skip_hw_vlan)
+                       struct sk_buff *skb, u32 wrb_cnt, bool dummy_wrb,
+                       bool skip_hw_vlan)
 {
        dma_addr_t busaddr;
        int i, copied = 0;
@@ -821,8 +820,7 @@ static int make_tx_wrbs(struct be_adapter *adapter, struct be_queue_info *txq,
        }
 
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
-               const struct skb_frag_struct *frag =
-                       &skb_shinfo(skb)->frags[i];
+               const struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i];
                busaddr = skb_frag_dma_map(dev, frag, 0,
                                           skb_frag_size(frag), DMA_TO_DEVICE);
                if (dma_mapping_error(dev, busaddr))
@@ -927,8 +925,7 @@ static int be_vlan_tag_tx_chk(struct be_adapter *adapter, struct sk_buff *skb)
        return vlan_tx_tag_present(skb) || adapter->pvid || adapter->qnq_vid;
 }
 
-static int be_ipv6_tx_stall_chk(struct be_adapter *adapter,
-                               struct sk_buff *skb)
+static int be_ipv6_tx_stall_chk(struct be_adapter *adapter, struct sk_buff *skb)
 {
        return BE3_chip(adapter) && be_ipv6_exthdr_check(skb);
 }
@@ -959,7 +956,7 @@ static struct sk_buff *be_lancer_xmit_workarounds(struct be_adapter *adapter,
         */
        if (be_pvid_tagging_enabled(adapter) &&
            veh->h_vlan_proto == htons(ETH_P_8021Q))
-                       *skip_hw_vlan = true;
+               *skip_hw_vlan = true;
 
        /* HW has a bug wherein it will calculate CSUM for VLAN
         * pkts even though it is disabled.
@@ -1077,16 +1074,15 @@ static int be_change_mtu(struct net_device *netdev, int new_mtu)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
        if (new_mtu < BE_MIN_MTU ||
-                       new_mtu > (BE_MAX_JUMBO_FRAME_SIZE -
-                                       (ETH_HLEN + ETH_FCS_LEN))) {
+           new_mtu > (BE_MAX_JUMBO_FRAME_SIZE - (ETH_HLEN + ETH_FCS_LEN))) {
                dev_info(&adapter->pdev->dev,
-                       "MTU must be between %d and %d bytes\n",
-                       BE_MIN_MTU,
-                       (BE_MAX_JUMBO_FRAME_SIZE - (ETH_HLEN + ETH_FCS_LEN)));
+                        "MTU must be between %d and %d bytes\n",
+                        BE_MIN_MTU,
+                        (BE_MAX_JUMBO_FRAME_SIZE - (ETH_HLEN + ETH_FCS_LEN)));
                return -EINVAL;
        }
        dev_info(&adapter->pdev->dev, "MTU changed from %d to %d bytes\n",
-                       netdev->mtu, new_mtu);
+                netdev->mtu, new_mtu);
        netdev->mtu = new_mtu;
        return 0;
 }
@@ -1098,7 +1094,7 @@ static int be_change_mtu(struct net_device *netdev, int new_mtu)
 static int be_vid_config(struct be_adapter *adapter)
 {
        u16 vids[BE_NUM_VLANS_SUPPORTED];
-       u16 num = 0, i;
+       u16 num = 0, i = 0;
        int status = 0;
 
        /* No need to further configure vids if in promiscuous mode */
@@ -1109,16 +1105,14 @@ static int be_vid_config(struct be_adapter *adapter)
                goto set_vlan_promisc;
 
        /* Construct VLAN Table to give to HW */
-       for (i = 0; i < VLAN_N_VID; i++)
-               if (adapter->vlan_tag[i])
-                       vids[num++] = cpu_to_le16(i);
-
-       status = be_cmd_vlan_config(adapter, adapter->if_handle,
-                                   vids, num, 0);
+       for_each_set_bit(i, adapter->vids, VLAN_N_VID)
+               vids[num++] = cpu_to_le16(i);
 
+       status = be_cmd_vlan_config(adapter, adapter->if_handle, vids, num);
        if (status) {
                /* Set to VLAN promisc mode as setting VLAN filter failed */
-               if (status == MCC_ADDL_STS_INSUFFICIENT_RESOURCES)
+               if (addl_status(status) ==
+                               MCC_ADDL_STATUS_INSUFFICIENT_RESOURCES)
                        goto set_vlan_promisc;
                dev_err(&adapter->pdev->dev,
                        "Setting HW VLAN filtering failed.\n");
@@ -1160,16 +1154,16 @@ static int be_vlan_add_vid(struct net_device *netdev, __be16 proto, u16 vid)
        if (lancer_chip(adapter) && vid == 0)
                return status;
 
-       if (adapter->vlan_tag[vid])
+       if (test_bit(vid, adapter->vids))
                return status;
 
-       adapter->vlan_tag[vid] = 1;
+       set_bit(vid, adapter->vids);
        adapter->vlans_added++;
 
        status = be_vid_config(adapter);
        if (status) {
                adapter->vlans_added--;
-               adapter->vlan_tag[vid] = 0;
+               clear_bit(vid, adapter->vids);
        }
 
        return status;
@@ -1184,12 +1178,12 @@ static int be_vlan_rem_vid(struct net_device *netdev, __be16 proto, u16 vid)
        if (lancer_chip(adapter) && vid == 0)
                goto ret;
 
-       adapter->vlan_tag[vid] = 0;
+       clear_bit(vid, adapter->vids);
        status = be_vid_config(adapter);
        if (!status)
                adapter->vlans_added--;
        else
-               adapter->vlan_tag[vid] = 1;
+               set_bit(vid, adapter->vids);
 ret:
        return status;
 }
@@ -1197,7 +1191,7 @@ static int be_vlan_rem_vid(struct net_device *netdev, __be16 proto, u16 vid)
 static void be_clear_promisc(struct be_adapter *adapter)
 {
        adapter->promiscuous = false;
-       adapter->flags &= ~BE_FLAGS_VLAN_PROMISC;
+       adapter->flags &= ~(BE_FLAGS_VLAN_PROMISC | BE_FLAGS_MCAST_PROMISC);
 
        be_cmd_rx_filter(adapter, IFF_PROMISC, OFF);
 }
@@ -1222,10 +1216,8 @@ static void be_set_rx_mode(struct net_device *netdev)
 
        /* Enable multicast promisc if num configured exceeds what we support */
        if (netdev->flags & IFF_ALLMULTI ||
-           netdev_mc_count(netdev) > be_max_mc(adapter)) {
-               be_cmd_rx_filter(adapter, IFF_ALLMULTI, ON);
-               goto done;
-       }
+           netdev_mc_count(netdev) > be_max_mc(adapter))
+               goto set_mcast_promisc;
 
        if (netdev_uc_count(netdev) != adapter->uc_macs) {
                struct netdev_hw_addr *ha;
@@ -1251,13 +1243,22 @@ static void be_set_rx_mode(struct net_device *netdev)
        }
 
        status = be_cmd_rx_filter(adapter, IFF_MULTICAST, ON);
-
-       /* Set to MCAST promisc mode if setting MULTICAST address fails */
-       if (status) {
-               dev_info(&adapter->pdev->dev, "Exhausted multicast HW filters.\n");
-               dev_info(&adapter->pdev->dev, "Disabling HW multicast filtering.\n");
-               be_cmd_rx_filter(adapter, IFF_ALLMULTI, ON);
+       if (!status) {
+               if (adapter->flags & BE_FLAGS_MCAST_PROMISC)
+                       adapter->flags &= ~BE_FLAGS_MCAST_PROMISC;
+               goto done;
        }
+
+set_mcast_promisc:
+       if (adapter->flags & BE_FLAGS_MCAST_PROMISC)
+               return;
+
+       /* Set to MCAST promisc mode if setting MULTICAST address fails
+        * or if num configured exceeds what we support
+        */
+       status = be_cmd_rx_filter(adapter, IFF_ALLMULTI, ON);
+       if (!status)
+               adapter->flags |= BE_FLAGS_MCAST_PROMISC;
 done:
        return;
 }
@@ -1287,7 +1288,7 @@ static int be_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
 
        if (status)
                dev_err(&adapter->pdev->dev, "MAC %pM set on VF %d Failed\n",
-                               mac, vf);
+                       mac, vf);
        else
                memcpy(vf_cfg->mac_addr, mac, ETH_ALEN);
 
@@ -1295,7 +1296,7 @@ static int be_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
 }
 
 static int be_get_vf_config(struct net_device *netdev, int vf,
-                       struct ifla_vf_info *vi)
+                           struct ifla_vf_info *vi)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
        struct be_vf_cfg *vf_cfg = &adapter->vf_cfg[vf];
@@ -1307,7 +1308,8 @@ static int be_get_vf_config(struct net_device *netdev, int vf,
                return -EINVAL;
 
        vi->vf = vf;
-       vi->tx_rate = vf_cfg->tx_rate;
+       vi->max_tx_rate = vf_cfg->tx_rate;
+       vi->min_tx_rate = 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);
@@ -1316,8 +1318,7 @@ static int be_get_vf_config(struct net_device *netdev, int vf,
        return 0;
 }
 
-static int be_set_vf_vlan(struct net_device *netdev,
-                       int vf, u16 vlan, u8 qos)
+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];
@@ -1348,11 +1349,14 @@ static int be_set_vf_vlan(struct net_device *netdev,
        return status;
 }
 
-static int be_set_vf_tx_rate(struct net_device *netdev,
-                       int vf, int rate)
+static int be_set_vf_tx_rate(struct net_device *netdev, int vf,
+                            int min_tx_rate, int max_tx_rate)
 {
        struct be_adapter *adapter = netdev_priv(netdev);
-       int status = 0;
+       struct device *dev = &adapter->pdev->dev;
+       int percent_rate, status = 0;
+       u16 link_speed = 0;
+       u8 link_status;
 
        if (!sriov_enabled(adapter))
                return -EPERM;
@@ -1360,18 +1364,50 @@ static int be_set_vf_tx_rate(struct net_device *netdev,
        if (vf >= adapter->num_vfs)
                return -EINVAL;
 
-       if (rate < 100 || rate > 10000) {
-               dev_err(&adapter->pdev->dev,
-                       "tx rate must be between 100 and 10000 Mbps\n");
+       if (min_tx_rate)
                return -EINVAL;
+
+       if (!max_tx_rate)
+               goto config_qos;
+
+       status = be_cmd_link_status_query(adapter, &link_speed,
+                                         &link_status, 0);
+       if (status)
+               goto err;
+
+       if (!link_status) {
+               dev_err(dev, "TX-rate setting not allowed when link is down\n");
+               status = -EPERM;
+               goto err;
+       }
+
+       if (max_tx_rate < 100 || max_tx_rate > link_speed) {
+               dev_err(dev, "TX-rate must be between 100 and %d Mbps\n",
+                       link_speed);
+               status = -EINVAL;
+               goto err;
+       }
+
+       /* On Skyhawk the QOS setting must be done only as a % value */
+       percent_rate = link_speed / 100;
+       if (skyhawk_chip(adapter) && (max_tx_rate % percent_rate)) {
+               dev_err(dev, "TX-rate must be a multiple of %d Mbps\n",
+                       percent_rate);
+               status = -EINVAL;
+               goto err;
        }
 
-       status = be_cmd_config_qos(adapter, rate / 10, vf + 1);
+config_qos:
+       status = be_cmd_config_qos(adapter, max_tx_rate, link_speed, vf + 1);
        if (status)
-               dev_err(&adapter->pdev->dev,
-                               "tx rate %d on VF %d failed\n", rate, vf);
-       else
-               adapter->vf_cfg[vf].tx_rate = rate;
+               goto err;
+
+       adapter->vf_cfg[vf].tx_rate = max_tx_rate;
+       return 0;
+
+err:
+       dev_err(dev, "TX-rate setting of %dMbps on VF%d failed\n",
+               max_tx_rate, vf);
        return status;
 }
 static int be_set_vf_link_state(struct net_device *netdev, int vf,
@@ -1469,7 +1505,7 @@ static void be_eqd_update(struct be_adapter *adapter)
 }
 
 static void be_rx_stats_update(struct be_rx_obj *rxo,
-               struct be_rx_compl_info *rxcp)
+                              struct be_rx_compl_info *rxcp)
 {
        struct be_rx_stats *stats = rx_stats(rxo);
 
@@ -1566,7 +1602,8 @@ static void skb_fill_rx_data(struct be_rx_obj *rxo, struct sk_buff *skb,
                skb_frag_set_page(skb, 0, page_info->page);
                skb_shinfo(skb)->frags[0].page_offset =
                                        page_info->page_offset + hdr_len;
-               skb_frag_size_set(&skb_shinfo(skb)->frags[0], curr_frag_len - hdr_len);
+               skb_frag_size_set(&skb_shinfo(skb)->frags[0],
+                                 curr_frag_len - hdr_len);
                skb->data_len = curr_frag_len - hdr_len;
                skb->truesize += rx_frag_size;
                skb->tail += hdr_len;
@@ -1725,8 +1762,8 @@ static void be_parse_rx_compl_v1(struct be_eth_rx_compl *compl,
        if (rxcp->vlanf) {
                rxcp->qnq = AMAP_GET_BITS(struct amap_eth_rx_compl_v1, qnq,
                                          compl);
-               rxcp->vlan_tag = AMAP_GET_BITS(struct amap_eth_rx_compl_v1, vlan_tag,
-                                              compl);
+               rxcp->vlan_tag = AMAP_GET_BITS(struct amap_eth_rx_compl_v1,
+                                              vlan_tag, compl);
        }
        rxcp->port = AMAP_GET_BITS(struct amap_eth_rx_compl_v1, port, compl);
        rxcp->tunneled =
@@ -1757,8 +1794,8 @@ static void be_parse_rx_compl_v0(struct be_eth_rx_compl *compl,
        if (rxcp->vlanf) {
                rxcp->qnq = AMAP_GET_BITS(struct amap_eth_rx_compl_v0, qnq,
                                          compl);
-               rxcp->vlan_tag = AMAP_GET_BITS(struct amap_eth_rx_compl_v0, vlan_tag,
-                                              compl);
+               rxcp->vlan_tag = AMAP_GET_BITS(struct amap_eth_rx_compl_v0,
+                                              vlan_tag, compl);
        }
        rxcp->port = AMAP_GET_BITS(struct amap_eth_rx_compl_v0, port, compl);
        rxcp->ip_frag = AMAP_GET_BITS(struct amap_eth_rx_compl_v0,
@@ -1799,7 +1836,7 @@ static struct be_rx_compl_info *be_rx_compl_get(struct be_rx_obj *rxo)
                        rxcp->vlan_tag = swab16(rxcp->vlan_tag);
 
                if (adapter->pvid == (rxcp->vlan_tag & VLAN_VID_MASK) &&
-                   !adapter->vlan_tag[rxcp->vlan_tag])
+                   !test_bit(rxcp->vlan_tag, adapter->vids))
                        rxcp->vlanf = 0;
        }
 
@@ -1915,7 +1952,7 @@ static struct be_eth_tx_compl *be_tx_compl_get(struct be_queue_info *tx_cq)
 }
 
 static u16 be_tx_compl_process(struct be_adapter *adapter,
-               struct be_tx_obj *txo, u16 last_index)
+                              struct be_tx_obj *txo, u16 last_index)
 {
        struct be_queue_info *txq = &txo->q;
        struct be_eth_wrb *wrb;
@@ -2122,7 +2159,7 @@ static int be_evt_queues_create(struct be_adapter *adapter)
 
                eq = &eqo->q;
                rc = be_queue_alloc(adapter, eq, EVNT_Q_LEN,
-                                       sizeof(struct be_eq_entry));
+                                   sizeof(struct be_eq_entry));
                if (rc)
                        return rc;
 
@@ -2155,7 +2192,7 @@ static int be_mcc_queues_create(struct be_adapter *adapter)
 
        cq = &adapter->mcc_obj.cq;
        if (be_queue_alloc(adapter, cq, MCC_CQ_LEN,
-                       sizeof(struct be_mcc_compl)))
+                          sizeof(struct be_mcc_compl)))
                goto err;
 
        /* Use the default EQ for MCC completions */
@@ -2275,7 +2312,7 @@ static int be_rx_cqs_create(struct be_adapter *adapter)
                rxo->adapter = adapter;
                cq = &rxo->cq;
                rc = be_queue_alloc(adapter, cq, RX_CQ_LEN,
-                               sizeof(struct be_eth_rx_compl));
+                                   sizeof(struct be_eth_rx_compl));
                if (rc)
                        return rc;
 
@@ -2339,7 +2376,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 polling)
+                        int budget, int polling)
 {
        struct be_adapter *adapter = rxo->adapter;
        struct be_queue_info *rx_cq = &rxo->cq;
@@ -2365,7 +2402,7 @@ static int be_process_rx(struct be_rx_obj *rxo, struct napi_struct *napi,
                 * promiscuous mode on some skews
                 */
                if (unlikely(rxcp->port != adapter->port_num &&
-                               !lancer_chip(adapter))) {
+                            !lancer_chip(adapter))) {
                        be_rx_compl_discard(rxo, rxcp);
                        goto loop_continue;
                }
@@ -2405,8 +2442,9 @@ static bool be_process_tx(struct be_adapter *adapter, struct be_tx_obj *txo,
                if (!txcp)
                        break;
                num_wrbs += be_tx_compl_process(adapter, txo,
-                               AMAP_GET_BITS(struct amap_eth_tx_compl,
-                                       wrb_index, txcp));
+                                               AMAP_GET_BITS(struct
+                                                             amap_eth_tx_compl,
+                                                             wrb_index, txcp));
        }
 
        if (work_done) {
@@ -2416,7 +2454,7 @@ static bool be_process_tx(struct be_adapter *adapter, struct be_tx_obj *txo,
                /* As Tx wrbs have been freed up, wake up netdev queue
                 * if it was stopped due to lack of tx wrbs.  */
                if (__netif_subqueue_stopped(adapter->netdev, idx) &&
-                       atomic_read(&txo->q.used) < txo->q.len / 2) {
+                   atomic_read(&txo->q.used) < txo->q.len / 2) {
                        netif_wake_subqueue(adapter->netdev, idx);
                }
 
@@ -2510,9 +2548,9 @@ void be_detect_error(struct be_adapter *adapter)
                sliport_status = ioread32(adapter->db + SLIPORT_STATUS_OFFSET);
                if (sliport_status & SLIPORT_STATUS_ERR_MASK) {
                        sliport_err1 = ioread32(adapter->db +
-                                       SLIPORT_ERROR1_OFFSET);
+                                               SLIPORT_ERROR1_OFFSET);
                        sliport_err2 = ioread32(adapter->db +
-                                       SLIPORT_ERROR2_OFFSET);
+                                               SLIPORT_ERROR2_OFFSET);
                        adapter->hw_error = true;
                        /* Do not log error messages if its a FW reset */
                        if (sliport_err1 == SLIPORT_ERROR_FW_RESET1 &&
@@ -2531,13 +2569,13 @@ void be_detect_error(struct be_adapter *adapter)
                }
        } else {
                pci_read_config_dword(adapter->pdev,
-                               PCICFG_UE_STATUS_LOW, &ue_lo);
+                                     PCICFG_UE_STATUS_LOW, &ue_lo);
                pci_read_config_dword(adapter->pdev,
-                               PCICFG_UE_STATUS_HIGH, &ue_hi);
+                                     PCICFG_UE_STATUS_HIGH, &ue_hi);
                pci_read_config_dword(adapter->pdev,
-                               PCICFG_UE_STATUS_LOW_MASK, &ue_lo_mask);
+                                     PCICFG_UE_STATUS_LOW_MASK, &ue_lo_mask);
                pci_read_config_dword(adapter->pdev,
-                               PCICFG_UE_STATUS_HI_MASK, &ue_hi_mask);
+                                     PCICFG_UE_STATUS_HI_MASK, &ue_hi_mask);
 
                ue_lo = (ue_lo & ~ue_lo_mask);
                ue_hi = (ue_hi & ~ue_hi_mask);
@@ -2624,7 +2662,7 @@ static int be_msix_enable(struct be_adapter *adapter)
 }
 
 static inline int be_msix_vec_get(struct be_adapter *adapter,
-                               struct be_eq_obj *eqo)
+                                 struct be_eq_obj *eqo)
 {
        return adapter->msix_entries[eqo->msix_idx].vector;
 }
@@ -2648,7 +2686,7 @@ static int be_msix_register(struct be_adapter *adapter)
        for (i--, eqo = &adapter->eq_obj[i]; i >= 0; i--, eqo--)
                free_irq(be_msix_vec_get(adapter, eqo), eqo);
        dev_warn(&adapter->pdev->dev, "MSIX Request IRQ failed - err %d\n",
-               status);
+                status);
        be_msix_disable(adapter);
        return status;
 }
@@ -2774,7 +2812,8 @@ static int be_rx_qs_create(struct be_adapter *adapter)
 {
        struct be_rx_obj *rxo;
        int rc, i, j;
-       u8 rsstable[128];
+       u8 rss_hkey[RSS_HASH_KEY_LEN];
+       struct rss_info *rss = &adapter->rss_info;
 
        for_all_rx_queues(adapter, rxo, i) {
                rc = be_queue_alloc(adapter, &rxo->q, RX_Q_LEN,
@@ -2799,31 +2838,36 @@ static int be_rx_qs_create(struct be_adapter *adapter)
        }
 
        if (be_multi_rxq(adapter)) {
-               for (j = 0; j < 128; j += adapter->num_rx_qs - 1) {
+               for (j = 0; j < RSS_INDIR_TABLE_LEN;
+                       j += adapter->num_rx_qs - 1) {
                        for_all_rss_queues(adapter, rxo, i) {
-                               if ((j + i) >= 128)
+                               if ((j + i) >= RSS_INDIR_TABLE_LEN)
                                        break;
-                               rsstable[j + i] = rxo->rss_id;
+                               rss->rsstable[j + i] = rxo->rss_id;
+                               rss->rss_queue[j + i] = i;
                        }
                }
-               adapter->rss_flags = RSS_ENABLE_TCP_IPV4 | RSS_ENABLE_IPV4 |
-                                       RSS_ENABLE_TCP_IPV6 | RSS_ENABLE_IPV6;
+               rss->rss_flags = RSS_ENABLE_TCP_IPV4 | RSS_ENABLE_IPV4 |
+                       RSS_ENABLE_TCP_IPV6 | RSS_ENABLE_IPV6;
 
                if (!BEx_chip(adapter))
-                       adapter->rss_flags |= RSS_ENABLE_UDP_IPV4 |
-                                               RSS_ENABLE_UDP_IPV6;
+                       rss->rss_flags |= RSS_ENABLE_UDP_IPV4 |
+                               RSS_ENABLE_UDP_IPV6;
        } else {
                /* Disable RSS, if only default RX Q is created */
-               adapter->rss_flags = RSS_ENABLE_NONE;
+               rss->rss_flags = RSS_ENABLE_NONE;
        }
 
-       rc = be_cmd_rss_config(adapter, rsstable, adapter->rss_flags,
-                              128);
+       get_random_bytes(rss_hkey, RSS_HASH_KEY_LEN);
+       rc = be_cmd_rss_config(adapter, rss->rsstable, rss->rss_flags,
+                              128, rss_hkey);
        if (rc) {
-               adapter->rss_flags = RSS_ENABLE_NONE;
+               rss->rss_flags = RSS_ENABLE_NONE;
                return rc;
        }
 
+       memcpy(rss->rss_hkey, rss_hkey, RSS_HASH_KEY_LEN);
+
        /* First time posting */
        for_all_rx_queues(adapter, rxo, i)
                be_post_rx_frags(rxo, GFP_KERNEL);
@@ -2896,7 +2940,8 @@ static int be_setup_wol(struct be_adapter *adapter, bool enable)
 
        if (enable) {
                status = pci_write_config_dword(adapter->pdev,
-                       PCICFG_PM_CONTROL_OFFSET, PCICFG_PM_CONTROL_MASK);
+                                               PCICFG_PM_CONTROL_OFFSET,
+                                               PCICFG_PM_CONTROL_MASK);
                if (status) {
                        dev_err(&adapter->pdev->dev,
                                "Could not enable Wake-on-lan\n");
@@ -2905,7 +2950,8 @@ static int be_setup_wol(struct be_adapter *adapter, bool enable)
                        return status;
                }
                status = be_cmd_enable_magic_wol(adapter,
-                               adapter->netdev->dev_addr, &cmd);
+                                                adapter->netdev->dev_addr,
+                                                &cmd);
                pci_enable_wake(adapter->pdev, PCI_D3hot, 1);
                pci_enable_wake(adapter->pdev, PCI_D3cold, 1);
        } else {
@@ -2944,7 +2990,8 @@ static int be_vf_eth_addr_config(struct be_adapter *adapter)
 
                if (status)
                        dev_err(&adapter->pdev->dev,
-                       "Mac address assignment failed for VF %d\n", vf);
+                               "Mac address assignment failed for VF %d\n",
+                               vf);
                else
                        memcpy(vf_cfg->mac_addr, mac, ETH_ALEN);
 
@@ -3086,9 +3133,11 @@ static int be_vfs_if_create(struct be_adapter *adapter)
 
                /* If a FW profile exists, then cap_flags are updated */
                en_flags = cap_flags & (BE_IF_FLAGS_UNTAGGED |
-                          BE_IF_FLAGS_BROADCAST | BE_IF_FLAGS_MULTICAST);
-               status = be_cmd_if_create(adapter, cap_flags, en_flags,
-                                         &vf_cfg->if_handle, vf + 1);
+                                       BE_IF_FLAGS_BROADCAST |
+                                       BE_IF_FLAGS_MULTICAST);
+               status =
+                   be_cmd_if_create(adapter, cap_flags, en_flags,
+                                    &vf_cfg->if_handle, vf + 1);
                if (status)
                        goto err;
        }
@@ -3119,7 +3168,6 @@ static int be_vf_setup(struct be_adapter *adapter)
        struct be_vf_cfg *vf_cfg;
        int status, old_vfs, vf;
        u32 privileges;
-       u16 lnk_speed;
 
        old_vfs = pci_num_vf(adapter->pdev);
        if (old_vfs) {
@@ -3175,16 +3223,9 @@ static int be_vf_setup(struct be_adapter *adapter)
                                         vf);
                }
 
-               /* BE3 FW, by default, caps VF TX-rate to 100mbps.
-                * Allow full available bandwidth
-                */
-               if (BE3_chip(adapter) && !old_vfs)
-                       be_cmd_config_qos(adapter, 1000, vf + 1);
-
-               status = be_cmd_link_status_query(adapter, &lnk_speed,
-                                                 NULL, vf + 1);
-               if (!status)
-                       vf_cfg->tx_rate = lnk_speed;
+               /* Allow full available bandwidth */
+               if (!old_vfs)
+                       be_cmd_config_qos(adapter, 0, 0, vf + 1);
 
                if (!old_vfs) {
                        be_cmd_enable_vf(adapter, vf + 1);
@@ -3590,35 +3631,7 @@ static void be_netpoll(struct net_device *netdev)
 }
 #endif
 
-#define FW_FILE_HDR_SIGN       "ServerEngines Corp. "
-static char flash_cookie[2][16] =      {"*** SE FLAS", "H DIRECTORY *** "};
-
-static bool be_flash_redboot(struct be_adapter *adapter,
-                       const u8 *p, u32 img_start, int image_size,
-                       int hdr_size)
-{
-       u32 crc_offset;
-       u8 flashed_crc[4];
-       int status;
-
-       crc_offset = hdr_size + img_start + image_size - 4;
-
-       p += crc_offset;
-
-       status = be_cmd_get_flash_crc(adapter, flashed_crc,
-                       (image_size - 4));
-       if (status) {
-               dev_err(&adapter->pdev->dev,
-               "could not get crc from flash, not flashing redboot\n");
-               return false;
-       }
-
-       /*update redboot only if crc does not match*/
-       if (!memcmp(flashed_crc, p, 4))
-               return false;
-       else
-               return true;
-}
+static char flash_cookie[2][16] = {"*** SE FLAS", "H DIRECTORY *** "};
 
 static bool phy_flashing_required(struct be_adapter *adapter)
 {
@@ -3649,8 +3662,8 @@ static bool is_comp_in_ufi(struct be_adapter *adapter,
 }
 
 static struct flash_section_info *get_fsec_info(struct be_adapter *adapter,
-                                        int header_size,
-                                        const struct firmware *fw)
+                                               int header_size,
+                                               const struct firmware *fw)
 {
        struct flash_section_info *fsec = NULL;
        const u8 *p = fw->data;
@@ -3665,12 +3678,35 @@ static struct flash_section_info *get_fsec_info(struct be_adapter *adapter,
        return NULL;
 }
 
+static int be_check_flash_crc(struct be_adapter *adapter, const u8 *p,
+                             u32 img_offset, u32 img_size, int hdr_size,
+                             u16 img_optype, bool *crc_match)
+{
+       u32 crc_offset;
+       int status;
+       u8 crc[4];
+
+       status = be_cmd_get_flash_crc(adapter, crc, img_optype, img_size - 4);
+       if (status)
+               return status;
+
+       crc_offset = hdr_size + img_offset + img_size - 4;
+
+       /* Skip flashing, if crc of flashed region matches */
+       if (!memcmp(crc, p + crc_offset, 4))
+               *crc_match = true;
+       else
+               *crc_match = false;
+
+       return status;
+}
+
 static int be_flash(struct be_adapter *adapter, const u8 *img,
-               struct be_dma_mem *flash_cmd, int optype, int img_size)
+                   struct be_dma_mem *flash_cmd, int optype, int img_size)
 {
-       u32 total_bytes = 0, flash_op, num_bytes = 0;
-       int status = 0;
        struct be_cmd_write_flashrom *req = flash_cmd->va;
+       u32 total_bytes, flash_op, num_bytes;
+       int status;
 
        total_bytes = img_size;
        while (total_bytes) {
@@ -3693,32 +3729,28 @@ static int be_flash(struct be_adapter *adapter, const u8 *img,
                memcpy(req->data_buf, img, num_bytes);
                img += num_bytes;
                status = be_cmd_write_flashrom(adapter, flash_cmd, optype,
-                                               flash_op, num_bytes);
-               if (status) {
-                       if (status == ILLEGAL_IOCTL_REQ &&
-                           optype == OPTYPE_PHY_FW)
-                               break;
-                       dev_err(&adapter->pdev->dev,
-                               "cmd to write to flash rom failed.\n");
+                                              flash_op, num_bytes);
+               if (base_status(status) == MCC_STATUS_ILLEGAL_REQUEST &&
+                   optype == OPTYPE_PHY_FW)
+                       break;
+               else if (status)
                        return status;
-               }
        }
        return 0;
 }
 
 /* For BE2, BE3 and BE3-R */
 static int be_flash_BEx(struct be_adapter *adapter,
-                        const struct firmware *fw,
-                        struct be_dma_mem *flash_cmd,
-                        int num_of_images)
-
+                       const struct firmware *fw,
+                       struct be_dma_mem *flash_cmd, int num_of_images)
 {
-       int status = 0, i, filehdr_size = 0;
        int img_hdrs_size = (num_of_images * sizeof(struct image_hdr));
-       const u8 *p = fw->data;
-       const struct flash_comp *pflashcomp;
-       int num_comp, redboot;
+       struct device *dev = &adapter->pdev->dev;
        struct flash_section_info *fsec = NULL;
+       int status, i, filehdr_size, num_comp;
+       const struct flash_comp *pflashcomp;
+       bool crc_match;
+       const u8 *p;
 
        struct flash_comp gen3_flash_types[] = {
                { FLASH_iSCSI_PRIMARY_IMAGE_START_g3, OPTYPE_ISCSI_ACTIVE,
@@ -3775,8 +3807,7 @@ static int be_flash_BEx(struct be_adapter *adapter,
        /* Get flash section info*/
        fsec = get_fsec_info(adapter, filehdr_size + img_hdrs_size, fw);
        if (!fsec) {
-               dev_err(&adapter->pdev->dev,
-                       "Invalid Cookie. UFI corrupted ?\n");
+               dev_err(dev, "Invalid Cookie. FW image may be corrupted\n");
                return -1;
        }
        for (i = 0; i < num_comp; i++) {
@@ -3792,23 +3823,32 @@ static int be_flash_BEx(struct be_adapter *adapter,
                                continue;
 
                if (pflashcomp[i].optype == OPTYPE_REDBOOT) {
-                       redboot = be_flash_redboot(adapter, fw->data,
-                               pflashcomp[i].offset, pflashcomp[i].size,
-                               filehdr_size + img_hdrs_size);
-                       if (!redboot)
+                       status = be_check_flash_crc(adapter, fw->data,
+                                                   pflashcomp[i].offset,
+                                                   pflashcomp[i].size,
+                                                   filehdr_size +
+                                                   img_hdrs_size,
+                                                   OPTYPE_REDBOOT, &crc_match);
+                       if (status) {
+                               dev_err(dev,
+                                       "Could not get CRC for 0x%x region\n",
+                                       pflashcomp[i].optype);
+                               continue;
+                       }
+
+                       if (crc_match)
                                continue;
                }
 
-               p = fw->data;
-               p += filehdr_size + pflashcomp[i].offset + img_hdrs_size;
+               p = fw->data + filehdr_size + pflashcomp[i].offset +
+                       img_hdrs_size;
                if (p + pflashcomp[i].size > fw->data + fw->size)
                        return -1;
 
                status = be_flash(adapter, p, flash_cmd, pflashcomp[i].optype,
-                                       pflashcomp[i].size);
+                                 pflashcomp[i].size);
                if (status) {
-                       dev_err(&adapter->pdev->dev,
-                               "Flashing section type %d failed.\n",
+                       dev_err(dev, "Flashing section type 0x%x failed\n",
                                pflashcomp[i].img_type);
                        return status;
                }
@@ -3816,80 +3856,142 @@ static int be_flash_BEx(struct be_adapter *adapter,
        return 0;
 }
 
+static u16 be_get_img_optype(struct flash_section_entry fsec_entry)
+{
+       u32 img_type = le32_to_cpu(fsec_entry.type);
+       u16 img_optype = le16_to_cpu(fsec_entry.optype);
+
+       if (img_optype != 0xFFFF)
+               return img_optype;
+
+       switch (img_type) {
+       case IMAGE_FIRMWARE_iSCSI:
+               img_optype = OPTYPE_ISCSI_ACTIVE;
+               break;
+       case IMAGE_BOOT_CODE:
+               img_optype = OPTYPE_REDBOOT;
+               break;
+       case IMAGE_OPTION_ROM_ISCSI:
+               img_optype = OPTYPE_BIOS;
+               break;
+       case IMAGE_OPTION_ROM_PXE:
+               img_optype = OPTYPE_PXE_BIOS;
+               break;
+       case IMAGE_OPTION_ROM_FCoE:
+               img_optype = OPTYPE_FCOE_BIOS;
+               break;
+       case IMAGE_FIRMWARE_BACKUP_iSCSI:
+               img_optype = OPTYPE_ISCSI_BACKUP;
+               break;
+       case IMAGE_NCSI:
+               img_optype = OPTYPE_NCSI_FW;
+               break;
+       case IMAGE_FLASHISM_JUMPVECTOR:
+               img_optype = OPTYPE_FLASHISM_JUMPVECTOR;
+               break;
+       case IMAGE_FIRMWARE_PHY:
+               img_optype = OPTYPE_SH_PHY_FW;
+               break;
+       case IMAGE_REDBOOT_DIR:
+               img_optype = OPTYPE_REDBOOT_DIR;
+               break;
+       case IMAGE_REDBOOT_CONFIG:
+               img_optype = OPTYPE_REDBOOT_CONFIG;
+               break;
+       case IMAGE_UFI_DIR:
+               img_optype = OPTYPE_UFI_DIR;
+               break;
+       default:
+               break;
+       }
+
+       return img_optype;
+}
+
 static int be_flash_skyhawk(struct be_adapter *adapter,
-               const struct firmware *fw,
-               struct be_dma_mem *flash_cmd, int num_of_images)
+                           const struct firmware *fw,
+                           struct be_dma_mem *flash_cmd, int num_of_images)
 {
-       int status = 0, i, filehdr_size = 0;
-       int img_offset, img_size, img_optype, redboot;
        int img_hdrs_size = num_of_images * sizeof(struct image_hdr);
-       const u8 *p = fw->data;
+       struct device *dev = &adapter->pdev->dev;
        struct flash_section_info *fsec = NULL;
+       u32 img_offset, img_size, img_type;
+       int status, i, filehdr_size;
+       bool crc_match, old_fw_img;
+       u16 img_optype;
+       const u8 *p;
 
        filehdr_size = sizeof(struct flash_file_hdr_g3);
        fsec = get_fsec_info(adapter, filehdr_size + img_hdrs_size, fw);
        if (!fsec) {
-               dev_err(&adapter->pdev->dev,
-                       "Invalid Cookie. UFI corrupted ?\n");
+               dev_err(dev, "Invalid Cookie. FW image may be corrupted\n");
                return -1;
        }
 
        for (i = 0; i < le32_to_cpu(fsec->fsec_hdr.num_images); i++) {
                img_offset = le32_to_cpu(fsec->fsec_entry[i].offset);
                img_size   = le32_to_cpu(fsec->fsec_entry[i].pad_size);
+               img_type   = le32_to_cpu(fsec->fsec_entry[i].type);
+               img_optype = be_get_img_optype(fsec->fsec_entry[i]);
+               old_fw_img = fsec->fsec_entry[i].optype == 0xFFFF;
 
-               switch (le32_to_cpu(fsec->fsec_entry[i].type)) {
-               case IMAGE_FIRMWARE_iSCSI:
-                       img_optype = OPTYPE_ISCSI_ACTIVE;
-                       break;
-               case IMAGE_BOOT_CODE:
-                       img_optype = OPTYPE_REDBOOT;
-                       break;
-               case IMAGE_OPTION_ROM_ISCSI:
-                       img_optype = OPTYPE_BIOS;
-                       break;
-               case IMAGE_OPTION_ROM_PXE:
-                       img_optype = OPTYPE_PXE_BIOS;
-                       break;
-               case IMAGE_OPTION_ROM_FCoE:
-                       img_optype = OPTYPE_FCOE_BIOS;
-                       break;
-               case IMAGE_FIRMWARE_BACKUP_iSCSI:
-                       img_optype = OPTYPE_ISCSI_BACKUP;
-                       break;
-               case IMAGE_NCSI:
-                       img_optype = OPTYPE_NCSI_FW;
-                       break;
-               default:
+               if (img_optype == 0xFFFF)
                        continue;
+               /* Don't bother verifying CRC if an old FW image is being
+                * flashed
+                */
+               if (old_fw_img)
+                       goto flash;
+
+               status = be_check_flash_crc(adapter, fw->data, img_offset,
+                                           img_size, filehdr_size +
+                                           img_hdrs_size, img_optype,
+                                           &crc_match);
+               /* The current FW image on the card does not recognize the new
+                * FLASH op_type. The FW download is partially complete.
+                * Reboot the server now to enable FW image to recognize the
+                * new FLASH op_type. To complete the remaining process,
+                * download the same FW again after the reboot.
+                */
+               if (base_status(status) == MCC_STATUS_ILLEGAL_REQUEST ||
+                   base_status(status) == MCC_STATUS_ILLEGAL_FIELD) {
+                       dev_err(dev, "Flash incomplete. Reset the server\n");
+                       dev_err(dev, "Download FW image again after reset\n");
+                       return -EAGAIN;
+               } else if (status) {
+                       dev_err(dev, "Could not get CRC for 0x%x region\n",
+                               img_optype);
+                       return -EFAULT;
                }
 
-               if (img_optype == OPTYPE_REDBOOT) {
-                       redboot = be_flash_redboot(adapter, fw->data,
-                                       img_offset, img_size,
-                                       filehdr_size + img_hdrs_size);
-                       if (!redboot)
-                               continue;
-               }
+               if (crc_match)
+                       continue;
 
-               p = fw->data;
-               p += filehdr_size + img_offset + img_hdrs_size;
+flash:
+               p = fw->data + filehdr_size + img_offset + img_hdrs_size;
                if (p + img_size > fw->data + fw->size)
                        return -1;
 
                status = be_flash(adapter, p, flash_cmd, img_optype, img_size);
-               if (status) {
-                       dev_err(&adapter->pdev->dev,
-                               "Flashing section type %d failed.\n",
-                               fsec->fsec_entry[i].type);
-                       return status;
+               /* For old FW images ignore ILLEGAL_FIELD error or errors on
+                * UFI_DIR region
+                */
+               if (old_fw_img &&
+                   (base_status(status) == MCC_STATUS_ILLEGAL_FIELD ||
+                    (img_optype == OPTYPE_UFI_DIR &&
+                     base_status(status) == MCC_STATUS_FAILED))) {
+                       continue;
+               } else if (status) {
+                       dev_err(dev, "Flashing section type 0x%x failed\n",
+                               img_type);
+                       return -EFAULT;
                }
        }
        return 0;
 }
 
 static int lancer_fw_download(struct be_adapter *adapter,
-                               const struct firmware *fw)
+                             const struct firmware *fw)
 {
 #define LANCER_FW_DOWNLOAD_CHUNK      (32 * 1024)
 #define LANCER_FW_DOWNLOAD_LOCATION   "/prg"
@@ -3955,7 +4057,7 @@ static int lancer_fw_download(struct be_adapter *adapter,
        }
 
        dma_free_coherent(&adapter->pdev->dev, flash_cmd.size, flash_cmd.va,
-                               flash_cmd.dma);
+                         flash_cmd.dma);
        if (status) {
                dev_err(&adapter->pdev->dev,
                        "Firmware load error. "
@@ -3976,9 +4078,8 @@ static int lancer_fw_download(struct be_adapter *adapter,
                        goto lancer_fw_exit;
                }
        } else if (change_status != LANCER_NO_RESET_NEEDED) {
-                       dev_err(&adapter->pdev->dev,
-                               "System reboot required for new FW"
-                               " to be active\n");
+               dev_err(&adapter->pdev->dev,
+                       "System reboot required for new FW to be active\n");
        }
 
        dev_info(&adapter->pdev->dev, "Firmware flashed successfully\n");
@@ -4042,7 +4143,7 @@ static int be_fw_download(struct be_adapter *adapter, const struct firmware* fw)
                        switch (ufi_type) {
                        case UFI_TYPE4:
                                status = be_flash_skyhawk(adapter, fw,
-                                                       &flash_cmd, num_imgs);
+                                                         &flash_cmd, num_imgs);
                                break;
                        case UFI_TYPE3R:
                                status = be_flash_BEx(adapter, fw, &flash_cmd,
@@ -4112,8 +4213,7 @@ int be_load_fw(struct be_adapter *adapter, u8 *fw_file)
        return status;
 }
 
-static int be_ndo_bridge_setlink(struct net_device *dev,
-                                   struct nlmsghdr *nlh)
+static int be_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh)
 {
        struct be_adapter *adapter = netdev_priv(dev);
        struct nlattr *attr, *br_spec;
@@ -4155,8 +4255,7 @@ static int be_ndo_bridge_setlink(struct net_device *dev,
 }
 
 static int be_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
-                                   struct net_device *dev,
-                                   u32 filter_mask)
+                                struct net_device *dev, u32 filter_mask)
 {
        struct be_adapter *adapter = netdev_priv(dev);
        int status = 0;
@@ -4254,7 +4353,7 @@ static const struct net_device_ops be_netdev_ops = {
        .ndo_vlan_rx_kill_vid   = be_vlan_rem_vid,
        .ndo_set_vf_mac         = be_set_vf_mac,
        .ndo_set_vf_vlan        = be_set_vf_vlan,
-       .ndo_set_vf_tx_rate     = be_set_vf_tx_rate,
+       .ndo_set_vf_rate        = be_set_vf_tx_rate,
        .ndo_get_vf_config      = be_get_vf_config,
        .ndo_set_vf_link_state  = be_set_vf_link_state,
 #ifdef CONFIG_NET_POLL_CONTROLLER
@@ -4301,7 +4400,7 @@ static void be_netdev_init(struct net_device *netdev)
 
        netdev->netdev_ops = &be_netdev_ops;
 
-       SET_ETHTOOL_OPS(netdev, &be_ethtool_ops);
+       netdev->ethtool_ops = &be_ethtool_ops;
 }
 
 static void be_unmap_pci_bars(struct be_adapter *adapter)
@@ -4870,7 +4969,7 @@ static void be_shutdown(struct pci_dev *pdev)
 }
 
 static pci_ers_result_t be_eeh_err_detected(struct pci_dev *pdev,
-                               pci_channel_state_t state)
+                                           pci_channel_state_t state)
 {
        struct be_adapter *adapter = pci_get_drvdata(pdev);
        struct net_device *netdev =  adapter->netdev;
index 8b70ca7e342b6f16a8d6624293ee1b940bcff3d5..f3658bdb64cc4265de972cfc2bbd25157e95a680 100644 (file)
@@ -769,11 +769,6 @@ static int ethoc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
        return phy_mii_ioctl(phy, ifr, cmd);
 }
 
-static int ethoc_config(struct net_device *dev, struct ifmap *map)
-{
-       return -ENOSYS;
-}
-
 static void ethoc_do_set_mac_address(struct net_device *dev)
 {
        struct ethoc *priv = netdev_priv(dev);
@@ -995,7 +990,6 @@ static const struct net_device_ops ethoc_netdev_ops = {
        .ndo_open = ethoc_open,
        .ndo_stop = ethoc_stop,
        .ndo_do_ioctl = ethoc_ioctl,
-       .ndo_set_config = ethoc_config,
        .ndo_set_mac_address = ethoc_set_mac_address,
        .ndo_set_rx_mode = ethoc_set_multicast_list,
        .ndo_change_mtu = ethoc_change_mtu,
index 68069eabc4f855c0636d12b69854e44919ffb863..c77fa4a6984458648a97960deeef2d0a54b6637c 100644 (file)
@@ -1210,7 +1210,7 @@ static int ftgmac100_probe(struct platform_device *pdev)
 
        SET_NETDEV_DEV(netdev, &pdev->dev);
 
-       SET_ETHTOOL_OPS(netdev, &ftgmac100_ethtool_ops);
+       netdev->ethtool_ops = &ftgmac100_ethtool_ops;
        netdev->netdev_ops = &ftgmac100_netdev_ops;
        netdev->features = NETIF_F_IP_CSUM | NETIF_F_GRO;
 
index 8be5b40c0a121331f1d1bbc0712499eb4bec8160..4ff1adc6bfcab9132b1dbf673626f868430bbcc3 100644 (file)
@@ -1085,7 +1085,7 @@ static int ftmac100_probe(struct platform_device *pdev)
        }
 
        SET_NETDEV_DEV(netdev, &pdev->dev);
-       SET_ETHTOOL_OPS(netdev, &ftmac100_ethtool_ops);
+       netdev->ethtool_ops = &ftmac100_ethtool_ops;
        netdev->netdev_ops = &ftmac100_netdev_ops;
 
        platform_set_drvdata(pdev, netdev);
index 6048dc8604ee6139235f45f2736d9cd5b385dfa0..270308315d437e8ace4dc8da93af7789b7a499f9 100644 (file)
@@ -67,6 +67,7 @@ config FSL_XGMAC_MDIO
        tristate "Freescale XGMAC MDIO"
        depends on FSL_SOC
        select PHYLIB
+       select OF_MDIO
        ---help---
          This driver supports the MDIO bus on the Fman 10G Ethernet MACs.
 
index 3b8d6d19ff0595885570713d7f866c732695a0db..671d080105a7e08c5e20456a5ad38b29f6704e19 100644 (file)
@@ -221,7 +221,7 @@ struct bufdesc_ex {
 #define BD_ENET_TX_RCMASK       ((ushort)0x003c)
 #define BD_ENET_TX_UN           ((ushort)0x0002)
 #define BD_ENET_TX_CSL          ((ushort)0x0001)
-#define BD_ENET_TX_STATS        ((ushort)0x03ff)        /* All status bits */
+#define BD_ENET_TX_STATS        ((ushort)0x0fff)        /* All status bits */
 
 /*enhanced buffer descriptor control/status used by Ethernet transmit*/
 #define BD_ENET_TX_INT          0x40000000
@@ -246,8 +246,8 @@ struct bufdesc_ex {
 #define RX_RING_SIZE           (FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES)
 #define FEC_ENET_TX_FRSIZE     2048
 #define FEC_ENET_TX_FRPPG      (PAGE_SIZE / FEC_ENET_TX_FRSIZE)
-#define TX_RING_SIZE           16      /* Must be power of two */
-#define TX_RING_MOD_MASK       15      /*   for this to work */
+#define TX_RING_SIZE           512     /* Must be power of two */
+#define TX_RING_MOD_MASK       511     /*   for this to work */
 
 #define BD_ENET_RX_INT          0x00800000
 #define BD_ENET_RX_PTP          ((ushort)0x0400)
@@ -296,8 +296,15 @@ struct fec_enet_private {
        /* The ring entries to be free()ed */
        struct bufdesc  *dirty_tx;
 
+       unsigned short bufdesc_size;
        unsigned short tx_ring_size;
        unsigned short rx_ring_size;
+       unsigned short tx_stop_threshold;
+       unsigned short tx_wake_threshold;
+
+       /* Software TSO */
+       char *tso_hdrs;
+       dma_addr_t tso_hdrs_dma;
 
        struct  platform_device *pdev;
 
index 8d69e439f0c518d4b3e46c9ae21d85e4013b7e06..38d9d276ab8b8c006fe13f1aa76eac2e55a2d775 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/in.h>
 #include <linux/ip.h>
 #include <net/ip.h>
+#include <net/tso.h>
 #include <linux/tcp.h>
 #include <linux/udp.h>
 #include <linux/icmp.h>
@@ -54,6 +55,7 @@
 #include <linux/of_net.h>
 #include <linux/regulator/consumer.h>
 #include <linux/if_vlan.h>
+#include <linux/pinctrl/consumer.h>
 
 #include <asm/cacheflush.h>
 
@@ -172,10 +174,6 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
 #endif
 #endif /* CONFIG_M5272 */
 
-#if (((RX_RING_SIZE + TX_RING_SIZE) * 32) > PAGE_SIZE)
-#error "FEC: descriptor ring size constants too large"
-#endif
-
 /* Interrupt events/masks. */
 #define FEC_ENET_HBERR ((uint)0x80000000)      /* Heartbeat error */
 #define FEC_ENET_BABR  ((uint)0x40000000)      /* Babbling receiver */
@@ -231,6 +229,15 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
 #define FEC_PAUSE_FLAG_AUTONEG 0x1
 #define FEC_PAUSE_FLAG_ENABLE  0x2
 
+#define TSO_HEADER_SIZE                128
+/* Max number of allowed TCP segments for software TSO */
+#define FEC_MAX_TSO_SEGS       100
+#define FEC_MAX_SKB_DESCS      (FEC_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS)
+
+#define IS_TSO_HEADER(txq, addr) \
+       ((addr >= txq->tso_hdrs_dma) && \
+       (addr < txq->tso_hdrs_dma + txq->tx_ring_size * TSO_HEADER_SIZE))
+
 static int mii_cnt;
 
 static inline
@@ -286,6 +293,22 @@ struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, struct fec_enet_priva
                return (new_bd < base) ? (new_bd + ring_size) : new_bd;
 }
 
+static int fec_enet_get_bd_index(struct bufdesc *base, struct bufdesc *bdp,
+                               struct fec_enet_private *fep)
+{
+       return ((const char *)bdp - (const char *)base) / fep->bufdesc_size;
+}
+
+static int fec_enet_get_free_txdesc_num(struct fec_enet_private *fep)
+{
+       int entries;
+
+       entries = ((const char *)fep->dirty_tx -
+                       (const char *)fep->cur_tx) / fep->bufdesc_size - 1;
+
+       return entries > 0 ? entries : entries + fep->tx_ring_size;
+}
+
 static void *swap_buffer(void *bufaddr, int len)
 {
        int i;
@@ -307,33 +330,133 @@ fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev)
        if (unlikely(skb_cow_head(skb, 0)))
                return -1;
 
+       ip_hdr(skb)->check = 0;
        *(__sum16 *)(skb->head + skb->csum_start + skb->csum_offset) = 0;
 
        return 0;
 }
 
-static netdev_tx_t
-fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+static void
+fec_enet_submit_work(struct bufdesc *bdp, struct fec_enet_private *fep)
+{
+       const struct platform_device_id *id_entry =
+                               platform_get_device_id(fep->pdev);
+       struct bufdesc *bdp_pre;
+
+       bdp_pre = fec_enet_get_prevdesc(bdp, fep);
+       if ((id_entry->driver_data & FEC_QUIRK_ERR006358) &&
+           !(bdp_pre->cbd_sc & BD_ENET_TX_READY)) {
+               fep->delay_work.trig_tx = true;
+               schedule_delayed_work(&(fep->delay_work.delay_work),
+                                       msecs_to_jiffies(1));
+       }
+}
+
+static int
+fec_enet_txq_submit_frag_skb(struct sk_buff *skb, struct net_device *ndev)
 {
        struct fec_enet_private *fep = netdev_priv(ndev);
        const struct platform_device_id *id_entry =
                                platform_get_device_id(fep->pdev);
-       struct bufdesc *bdp, *bdp_pre;
-       void *bufaddr;
-       unsigned short  status;
+       struct bufdesc *bdp = fep->cur_tx;
+       struct bufdesc_ex *ebdp;
+       int nr_frags = skb_shinfo(skb)->nr_frags;
+       int frag, frag_len;
+       unsigned short status;
+       unsigned int estatus = 0;
+       skb_frag_t *this_frag;
        unsigned int index;
+       void *bufaddr;
+       int i;
 
-       /* Fill in a Tx ring entry */
+       for (frag = 0; frag < nr_frags; frag++) {
+               this_frag = &skb_shinfo(skb)->frags[frag];
+               bdp = fec_enet_get_nextdesc(bdp, fep);
+               ebdp = (struct bufdesc_ex *)bdp;
+
+               status = bdp->cbd_sc;
+               status &= ~BD_ENET_TX_STATS;
+               status |= (BD_ENET_TX_TC | BD_ENET_TX_READY);
+               frag_len = skb_shinfo(skb)->frags[frag].size;
+
+               /* Handle the last BD specially */
+               if (frag == nr_frags - 1) {
+                       status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST);
+                       if (fep->bufdesc_ex) {
+                               estatus |= BD_ENET_TX_INT;
+                               if (unlikely(skb_shinfo(skb)->tx_flags &
+                                       SKBTX_HW_TSTAMP && fep->hwts_tx_en))
+                                       estatus |= BD_ENET_TX_TS;
+                       }
+               }
+
+               if (fep->bufdesc_ex) {
+                       if (skb->ip_summed == CHECKSUM_PARTIAL)
+                               estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS;
+                       ebdp->cbd_bdu = 0;
+                       ebdp->cbd_esc = estatus;
+               }
+
+               bufaddr = page_address(this_frag->page.p) + this_frag->page_offset;
+
+               index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep);
+               if (((unsigned long) bufaddr) & FEC_ALIGNMENT ||
+                       id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
+                       memcpy(fep->tx_bounce[index], bufaddr, frag_len);
+                       bufaddr = fep->tx_bounce[index];
+
+                       if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
+                               swap_buffer(bufaddr, frag_len);
+               }
+
+               bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, bufaddr,
+                                               frag_len, DMA_TO_DEVICE);
+               if (dma_mapping_error(&fep->pdev->dev, bdp->cbd_bufaddr)) {
+                       dev_kfree_skb_any(skb);
+                       if (net_ratelimit())
+                               netdev_err(ndev, "Tx DMA memory map failed\n");
+                       goto dma_mapping_error;
+               }
+
+               bdp->cbd_datlen = frag_len;
+               bdp->cbd_sc = status;
+       }
+
+       fep->cur_tx = bdp;
+
+       return 0;
+
+dma_mapping_error:
        bdp = fep->cur_tx;
+       for (i = 0; i < frag; i++) {
+               bdp = fec_enet_get_nextdesc(bdp, fep);
+               dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
+                               bdp->cbd_datlen, DMA_TO_DEVICE);
+       }
+       return NETDEV_TX_OK;
+}
 
-       status = bdp->cbd_sc;
+static int fec_enet_txq_submit_skb(struct sk_buff *skb, struct net_device *ndev)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       const struct platform_device_id *id_entry =
+                               platform_get_device_id(fep->pdev);
+       int nr_frags = skb_shinfo(skb)->nr_frags;
+       struct bufdesc *bdp, *last_bdp;
+       void *bufaddr;
+       unsigned short status;
+       unsigned short buflen;
+       unsigned int estatus = 0;
+       unsigned int index;
+       int entries_free;
+       int ret;
 
-       if (status & BD_ENET_TX_READY) {
-               /* Ooops.  All transmit buffers are full.  Bail out.
-                * This should not happen, since ndev->tbusy should be set.
-                */
-               netdev_err(ndev, "tx queue full!\n");
-               return NETDEV_TX_BUSY;
+       entries_free = fec_enet_get_free_txdesc_num(fep);
+       if (entries_free < MAX_SKB_FRAGS + 1) {
+               dev_kfree_skb_any(skb);
+               if (net_ratelimit())
+                       netdev_err(ndev, "NOT enough BD for SG!\n");
+               return NETDEV_TX_OK;
        }
 
        /* Protocol checksum off-load for TCP and UDP. */
@@ -342,102 +465,300 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
                return NETDEV_TX_OK;
        }
 
-       /* Clear all of the status flags */
+       /* Fill in a Tx ring entry */
+       bdp = fep->cur_tx;
+       status = bdp->cbd_sc;
        status &= ~BD_ENET_TX_STATS;
 
        /* Set buffer length and buffer pointer */
        bufaddr = skb->data;
-       bdp->cbd_datlen = skb->len;
-
-       /*
-        * On some FEC implementations data must be aligned on
-        * 4-byte boundaries. Use bounce buffers to copy data
-        * and get it aligned. Ugh.
-        */
-       if (fep->bufdesc_ex)
-               index = (struct bufdesc_ex *)bdp -
-                       (struct bufdesc_ex *)fep->tx_bd_base;
-       else
-               index = bdp - fep->tx_bd_base;
+       buflen = skb_headlen(skb);
 
-       if (((unsigned long) bufaddr) & FEC_ALIGNMENT) {
-               memcpy(fep->tx_bounce[index], skb->data, skb->len);
+       index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep);
+       if (((unsigned long) bufaddr) & FEC_ALIGNMENT ||
+               id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
+               memcpy(fep->tx_bounce[index], skb->data, buflen);
                bufaddr = fep->tx_bounce[index];
-       }
 
-       /*
-        * Some design made an incorrect assumption on endian mode of
-        * the system that it's running on. As the result, driver has to
-        * swap every frame going to and coming from the controller.
-        */
-       if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
-               swap_buffer(bufaddr, skb->len);
-
-       /* Save skb pointer */
-       fep->tx_skbuff[index] = skb;
+               if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
+                       swap_buffer(bufaddr, buflen);
+       }
 
        /* Push the data cache so the CPM does not get stale memory
         * data.
         */
        bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, bufaddr,
-                       skb->len, DMA_TO_DEVICE);
+                                       buflen, DMA_TO_DEVICE);
        if (dma_mapping_error(&fep->pdev->dev, bdp->cbd_bufaddr)) {
-               bdp->cbd_bufaddr = 0;
-               fep->tx_skbuff[index] = NULL;
                dev_kfree_skb_any(skb);
                if (net_ratelimit())
                        netdev_err(ndev, "Tx DMA memory map failed\n");
                return NETDEV_TX_OK;
        }
 
+       if (nr_frags) {
+               ret = fec_enet_txq_submit_frag_skb(skb, ndev);
+               if (ret)
+                       return ret;
+       } else {
+               status |= (BD_ENET_TX_INTR | BD_ENET_TX_LAST);
+               if (fep->bufdesc_ex) {
+                       estatus = BD_ENET_TX_INT;
+                       if (unlikely(skb_shinfo(skb)->tx_flags &
+                               SKBTX_HW_TSTAMP && fep->hwts_tx_en))
+                               estatus |= BD_ENET_TX_TS;
+               }
+       }
+
        if (fep->bufdesc_ex) {
 
                struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
-               ebdp->cbd_bdu = 0;
+
                if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
-                       fep->hwts_tx_en)) {
-                       ebdp->cbd_esc = (BD_ENET_TX_TS | BD_ENET_TX_INT);
+                       fep->hwts_tx_en))
                        skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
-               } else {
-                       ebdp->cbd_esc = BD_ENET_TX_INT;
 
-                       /* Enable protocol checksum flags
-                        * We do not bother with the IP Checksum bits as they
-                        * are done by the kernel
-                        */
-                       if (skb->ip_summed == CHECKSUM_PARTIAL)
-                               ebdp->cbd_esc |= BD_ENET_TX_PINS;
-               }
+               if (skb->ip_summed == CHECKSUM_PARTIAL)
+                       estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS;
+
+               ebdp->cbd_bdu = 0;
+               ebdp->cbd_esc = estatus;
        }
 
+       last_bdp = fep->cur_tx;
+       index = fec_enet_get_bd_index(fep->tx_bd_base, last_bdp, fep);
+       /* Save skb pointer */
+       fep->tx_skbuff[index] = skb;
+
+       bdp->cbd_datlen = buflen;
+
        /* Send it on its way.  Tell FEC it's ready, interrupt when done,
         * it's the last BD of the frame, and to put the CRC on the end.
         */
-       status |= (BD_ENET_TX_READY | BD_ENET_TX_INTR
-                       | BD_ENET_TX_LAST | BD_ENET_TX_TC);
+       status |= (BD_ENET_TX_READY | BD_ENET_TX_TC);
        bdp->cbd_sc = status;
 
-       bdp_pre = fec_enet_get_prevdesc(bdp, fep);
-       if ((id_entry->driver_data & FEC_QUIRK_ERR006358) &&
-           !(bdp_pre->cbd_sc & BD_ENET_TX_READY)) {
-               fep->delay_work.trig_tx = true;
-               schedule_delayed_work(&(fep->delay_work.delay_work),
-                                       msecs_to_jiffies(1));
-       }
+       fec_enet_submit_work(bdp, fep);
 
        /* If this was the last BD in the ring, start at the beginning again. */
-       bdp = fec_enet_get_nextdesc(bdp, fep);
+       bdp = fec_enet_get_nextdesc(last_bdp, fep);
 
        skb_tx_timestamp(skb);
 
        fep->cur_tx = bdp;
 
-       if (fep->cur_tx == fep->dirty_tx)
-               netif_stop_queue(ndev);
+       /* Trigger transmission start */
+       writel(0, fep->hwp + FEC_X_DES_ACTIVE);
+
+       return 0;
+}
+
+static int
+fec_enet_txq_put_data_tso(struct sk_buff *skb, struct net_device *ndev,
+                       struct bufdesc *bdp, int index, char *data,
+                       int size, bool last_tcp, bool is_last)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       const struct platform_device_id *id_entry =
+                               platform_get_device_id(fep->pdev);
+       struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
+       unsigned short status;
+       unsigned int estatus = 0;
+
+       status = bdp->cbd_sc;
+       status &= ~BD_ENET_TX_STATS;
+
+       status |= (BD_ENET_TX_TC | BD_ENET_TX_READY);
+       bdp->cbd_datlen = size;
+
+       if (((unsigned long) data) & FEC_ALIGNMENT ||
+               id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
+               memcpy(fep->tx_bounce[index], data, size);
+               data = fep->tx_bounce[index];
+
+               if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
+                       swap_buffer(data, size);
+       }
+
+       bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, data,
+                                       size, DMA_TO_DEVICE);
+       if (dma_mapping_error(&fep->pdev->dev, bdp->cbd_bufaddr)) {
+               dev_kfree_skb_any(skb);
+               if (net_ratelimit())
+                       netdev_err(ndev, "Tx DMA memory map failed\n");
+               return NETDEV_TX_BUSY;
+       }
+
+       if (fep->bufdesc_ex) {
+               if (skb->ip_summed == CHECKSUM_PARTIAL)
+                       estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS;
+               ebdp->cbd_bdu = 0;
+               ebdp->cbd_esc = estatus;
+       }
+
+       /* Handle the last BD specially */
+       if (last_tcp)
+               status |= (BD_ENET_TX_LAST | BD_ENET_TX_TC);
+       if (is_last) {
+               status |= BD_ENET_TX_INTR;
+               if (fep->bufdesc_ex)
+                       ebdp->cbd_esc |= BD_ENET_TX_INT;
+       }
+
+       bdp->cbd_sc = status;
+
+       return 0;
+}
+
+static int
+fec_enet_txq_put_hdr_tso(struct sk_buff *skb, struct net_device *ndev,
+                       struct bufdesc *bdp, int index)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       const struct platform_device_id *id_entry =
+                               platform_get_device_id(fep->pdev);
+       int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+       struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
+       void *bufaddr;
+       unsigned long dmabuf;
+       unsigned short status;
+       unsigned int estatus = 0;
+
+       status = bdp->cbd_sc;
+       status &= ~BD_ENET_TX_STATS;
+       status |= (BD_ENET_TX_TC | BD_ENET_TX_READY);
+
+       bufaddr = fep->tso_hdrs + index * TSO_HEADER_SIZE;
+       dmabuf = fep->tso_hdrs_dma + index * TSO_HEADER_SIZE;
+       if (((unsigned long) bufaddr) & FEC_ALIGNMENT ||
+               id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) {
+               memcpy(fep->tx_bounce[index], skb->data, hdr_len);
+               bufaddr = fep->tx_bounce[index];
+
+               if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
+                       swap_buffer(bufaddr, hdr_len);
+
+               dmabuf = dma_map_single(&fep->pdev->dev, bufaddr,
+                                       hdr_len, DMA_TO_DEVICE);
+               if (dma_mapping_error(&fep->pdev->dev, dmabuf)) {
+                       dev_kfree_skb_any(skb);
+                       if (net_ratelimit())
+                               netdev_err(ndev, "Tx DMA memory map failed\n");
+                       return NETDEV_TX_BUSY;
+               }
+       }
+
+       bdp->cbd_bufaddr = dmabuf;
+       bdp->cbd_datlen = hdr_len;
+
+       if (fep->bufdesc_ex) {
+               if (skb->ip_summed == CHECKSUM_PARTIAL)
+                       estatus |= BD_ENET_TX_PINS | BD_ENET_TX_IINS;
+               ebdp->cbd_bdu = 0;
+               ebdp->cbd_esc = estatus;
+       }
+
+       bdp->cbd_sc = status;
+
+       return 0;
+}
+
+static int fec_enet_txq_submit_tso(struct sk_buff *skb, struct net_device *ndev)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+       int total_len, data_left;
+       struct bufdesc *bdp = fep->cur_tx;
+       struct tso_t tso;
+       unsigned int index = 0;
+       int ret;
+
+       if (tso_count_descs(skb) >= fec_enet_get_free_txdesc_num(fep)) {
+               dev_kfree_skb_any(skb);
+               if (net_ratelimit())
+                       netdev_err(ndev, "NOT enough BD for TSO!\n");
+               return NETDEV_TX_OK;
+       }
+
+       /* Protocol checksum off-load for TCP and UDP. */
+       if (fec_enet_clear_csum(skb, ndev)) {
+               dev_kfree_skb_any(skb);
+               return NETDEV_TX_OK;
+       }
+
+       /* Initialize the TSO handler, and prepare the first payload */
+       tso_start(skb, &tso);
+
+       total_len = skb->len - hdr_len;
+       while (total_len > 0) {
+               char *hdr;
+
+               index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep);
+               data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len);
+               total_len -= data_left;
+
+               /* prepare packet headers: MAC + IP + TCP */
+               hdr = fep->tso_hdrs + index * TSO_HEADER_SIZE;
+               tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0);
+               ret = fec_enet_txq_put_hdr_tso(skb, ndev, bdp, index);
+               if (ret)
+                       goto err_release;
+
+               while (data_left > 0) {
+                       int size;
+
+                       size = min_t(int, tso.size, data_left);
+                       bdp = fec_enet_get_nextdesc(bdp, fep);
+                       index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep);
+                       ret = fec_enet_txq_put_data_tso(skb, ndev, bdp, index, tso.data,
+                                                       size, size == data_left,
+                                                       total_len == 0);
+                       if (ret)
+                               goto err_release;
+
+                       data_left -= size;
+                       tso_build_data(skb, &tso, size);
+               }
+
+               bdp = fec_enet_get_nextdesc(bdp, fep);
+       }
+
+       /* Save skb pointer */
+       fep->tx_skbuff[index] = skb;
+
+       fec_enet_submit_work(bdp, fep);
+
+       skb_tx_timestamp(skb);
+       fep->cur_tx = bdp;
 
        /* Trigger transmission start */
        writel(0, fep->hwp + FEC_X_DES_ACTIVE);
 
+       return 0;
+
+err_release:
+       /* TODO: Release all used data descriptors for TSO */
+       return ret;
+}
+
+static netdev_tx_t
+fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       int entries_free;
+       int ret;
+
+       if (skb_is_gso(skb))
+               ret = fec_enet_txq_submit_tso(skb, ndev);
+       else
+               ret = fec_enet_txq_submit_skb(skb, ndev);
+       if (ret)
+               return ret;
+
+       entries_free = fec_enet_get_free_txdesc_num(fep);
+       if (entries_free <= fep->tx_stop_threshold)
+               netif_stop_queue(ndev);
+
        return NETDEV_TX_OK;
 }
 
@@ -756,6 +1077,7 @@ fec_enet_tx(struct net_device *ndev)
        unsigned short status;
        struct  sk_buff *skb;
        int     index = 0;
+       int     entries_free;
 
        fep = netdev_priv(ndev);
        bdp = fep->dirty_tx;
@@ -769,16 +1091,17 @@ fec_enet_tx(struct net_device *ndev)
                if (bdp == fep->cur_tx)
                        break;
 
-               if (fep->bufdesc_ex)
-                       index = (struct bufdesc_ex *)bdp -
-                               (struct bufdesc_ex *)fep->tx_bd_base;
-               else
-                       index = bdp - fep->tx_bd_base;
+               index = fec_enet_get_bd_index(fep->tx_bd_base, bdp, fep);
 
                skb = fep->tx_skbuff[index];
-               dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, skb->len,
-                               DMA_TO_DEVICE);
+               if (!IS_TSO_HEADER(fep, bdp->cbd_bufaddr))
+                       dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
+                                       bdp->cbd_datlen, DMA_TO_DEVICE);
                bdp->cbd_bufaddr = 0;
+               if (!skb) {
+                       bdp = fec_enet_get_nextdesc(bdp, fep);
+                       continue;
+               }
 
                /* Check for errors. */
                if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC |
@@ -797,7 +1120,7 @@ fec_enet_tx(struct net_device *ndev)
                                ndev->stats.tx_carrier_errors++;
                } else {
                        ndev->stats.tx_packets++;
-                       ndev->stats.tx_bytes += bdp->cbd_datlen;
+                       ndev->stats.tx_bytes += skb->len;
                }
 
                if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) &&
@@ -834,15 +1157,15 @@ fec_enet_tx(struct net_device *ndev)
 
                /* Since we have freed up a buffer, the ring is no longer full
                 */
-               if (fep->dirty_tx != fep->cur_tx) {
-                       if (netif_queue_stopped(ndev))
+               if (netif_queue_stopped(ndev)) {
+                       entries_free = fec_enet_get_free_txdesc_num(fep);
+                       if (entries_free >= fep->tx_wake_threshold)
                                netif_wake_queue(ndev);
                }
        }
        return;
 }
 
-
 /* During a receive, the cur_rx points to the current incoming buffer.
  * When we update through the ring, if the next incoming buffer has
  * not been given to the system, we just set the empty indicator,
@@ -920,11 +1243,7 @@ fec_enet_rx(struct net_device *ndev, int budget)
                pkt_len = bdp->cbd_datlen;
                ndev->stats.rx_bytes += pkt_len;
 
-               if (fep->bufdesc_ex)
-                       index = (struct bufdesc_ex *)bdp -
-                               (struct bufdesc_ex *)fep->rx_bd_base;
-               else
-                       index = bdp - fep->rx_bd_base;
+               index = fec_enet_get_bd_index(fep->rx_bd_base, bdp, fep);
                data = fep->rx_skbuff[index]->data;
                dma_sync_single_for_cpu(&fep->pdev->dev, bdp->cbd_bufaddr,
                                        FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
@@ -1255,6 +1574,49 @@ static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
        return 0;
 }
 
+static int fec_enet_clk_enable(struct net_device *ndev, bool enable)
+{
+       struct fec_enet_private *fep = netdev_priv(ndev);
+       int ret;
+
+       if (enable) {
+               ret = clk_prepare_enable(fep->clk_ahb);
+               if (ret)
+                       return ret;
+               ret = clk_prepare_enable(fep->clk_ipg);
+               if (ret)
+                       goto failed_clk_ipg;
+               if (fep->clk_enet_out) {
+                       ret = clk_prepare_enable(fep->clk_enet_out);
+                       if (ret)
+                               goto failed_clk_enet_out;
+               }
+               if (fep->clk_ptp) {
+                       ret = clk_prepare_enable(fep->clk_ptp);
+                       if (ret)
+                               goto failed_clk_ptp;
+               }
+       } else {
+               clk_disable_unprepare(fep->clk_ahb);
+               clk_disable_unprepare(fep->clk_ipg);
+               if (fep->clk_enet_out)
+                       clk_disable_unprepare(fep->clk_enet_out);
+               if (fep->clk_ptp)
+                       clk_disable_unprepare(fep->clk_ptp);
+       }
+
+       return 0;
+failed_clk_ptp:
+       if (fep->clk_enet_out)
+               clk_disable_unprepare(fep->clk_enet_out);
+failed_clk_enet_out:
+               clk_disable_unprepare(fep->clk_ipg);
+failed_clk_ipg:
+               clk_disable_unprepare(fep->clk_ahb);
+
+       return ret;
+}
+
 static int fec_enet_mii_probe(struct net_device *ndev)
 {
        struct fec_enet_private *fep = netdev_priv(ndev);
@@ -1364,7 +1726,7 @@ static int fec_enet_mii_init(struct platform_device *pdev)
         * Reference Manual has an error on this, and gets fixed on i.MX6Q
         * document.
         */
-       fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ahb), 5000000);
+       fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 5000000);
        if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
                fep->phy_speed--;
        fep->phy_speed <<= 1;
@@ -1773,6 +2135,11 @@ fec_enet_open(struct net_device *ndev)
        struct fec_enet_private *fep = netdev_priv(ndev);
        int ret;
 
+       pinctrl_pm_select_default_state(&fep->pdev->dev);
+       ret = fec_enet_clk_enable(ndev, true);
+       if (ret)
+               return ret;
+
        /* I should reset the ring buffers here, but I don't yet know
         * a simple way to do that.
         */
@@ -1811,6 +2178,8 @@ fec_enet_close(struct net_device *ndev)
                phy_disconnect(fep->phy_dev);
        }
 
+       fec_enet_clk_enable(ndev, false);
+       pinctrl_pm_select_sleep_state(&fep->pdev->dev);
        fec_enet_free_buffers(ndev);
 
        return 0;
@@ -1988,13 +2357,35 @@ static int fec_enet_init(struct net_device *ndev)
        const struct platform_device_id *id_entry =
                                platform_get_device_id(fep->pdev);
        struct bufdesc *cbd_base;
+       int bd_size;
+
+       /* init the tx & rx ring size */
+       fep->tx_ring_size = TX_RING_SIZE;
+       fep->rx_ring_size = RX_RING_SIZE;
+
+       fep->tx_stop_threshold = FEC_MAX_SKB_DESCS;
+       fep->tx_wake_threshold = (fep->tx_ring_size - fep->tx_stop_threshold) / 2;
+
+       if (fep->bufdesc_ex)
+               fep->bufdesc_size = sizeof(struct bufdesc_ex);
+       else
+               fep->bufdesc_size = sizeof(struct bufdesc);
+       bd_size = (fep->tx_ring_size + fep->rx_ring_size) *
+                       fep->bufdesc_size;
 
        /* Allocate memory for buffer descriptors. */
-       cbd_base = dma_alloc_coherent(NULL, PAGE_SIZE, &fep->bd_dma,
+       cbd_base = dma_alloc_coherent(NULL, bd_size, &fep->bd_dma,
                                      GFP_KERNEL);
        if (!cbd_base)
                return -ENOMEM;
 
+       fep->tso_hdrs = dma_alloc_coherent(NULL, fep->tx_ring_size * TSO_HEADER_SIZE,
+                                               &fep->tso_hdrs_dma, GFP_KERNEL);
+       if (!fep->tso_hdrs) {
+               dma_free_coherent(NULL, bd_size, cbd_base, fep->bd_dma);
+               return -ENOMEM;
+       }
+
        memset(cbd_base, 0, PAGE_SIZE);
 
        fep->netdev = ndev;
@@ -2004,10 +2395,6 @@ static int fec_enet_init(struct net_device *ndev)
        /* make sure MAC we just acquired is programmed into the hw */
        fec_set_mac_address(ndev, NULL);
 
-       /* init the tx & rx ring size */
-       fep->tx_ring_size = TX_RING_SIZE;
-       fep->rx_ring_size = RX_RING_SIZE;
-
        /* Set receive and transmit descriptor base. */
        fep->rx_bd_base = cbd_base;
        if (fep->bufdesc_ex)
@@ -2024,21 +2411,21 @@ static int fec_enet_init(struct net_device *ndev)
        writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK);
        netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, NAPI_POLL_WEIGHT);
 
-       if (id_entry->driver_data & FEC_QUIRK_HAS_VLAN) {
+       if (id_entry->driver_data & FEC_QUIRK_HAS_VLAN)
                /* enable hw VLAN support */
                ndev->features |= NETIF_F_HW_VLAN_CTAG_RX;
-               ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
-       }
 
        if (id_entry->driver_data & FEC_QUIRK_HAS_CSUM) {
+               ndev->gso_max_segs = FEC_MAX_TSO_SEGS;
+
                /* enable hw accelerator */
                ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM
-                               | NETIF_F_RXCSUM);
-               ndev->hw_features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM
-                               | NETIF_F_RXCSUM);
+                               | NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO);
                fep->csum_flags |= FLAG_RX_CSUM_ENABLED;
        }
 
+       ndev->hw_features = ndev->features;
+
        fec_restart(ndev, 0);
 
        return 0;
@@ -2114,6 +2501,9 @@ fec_probe(struct platform_device *pdev)
                fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG;
 #endif
 
+       /* Select default pin state */
+       pinctrl_pm_select_default_state(&pdev->dev);
+
        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        fep->hwp = devm_ioremap_resource(&pdev->dev, r);
        if (IS_ERR(fep->hwp)) {
@@ -2164,26 +2554,10 @@ fec_probe(struct platform_device *pdev)
                fep->bufdesc_ex = 0;
        }
 
-       ret = clk_prepare_enable(fep->clk_ahb);
+       ret = fec_enet_clk_enable(ndev, true);
        if (ret)
                goto failed_clk;
 
-       ret = clk_prepare_enable(fep->clk_ipg);
-       if (ret)
-               goto failed_clk_ipg;
-
-       if (fep->clk_enet_out) {
-               ret = clk_prepare_enable(fep->clk_enet_out);
-               if (ret)
-                       goto failed_clk_enet_out;
-       }
-
-       if (fep->clk_ptp) {
-               ret = clk_prepare_enable(fep->clk_ptp);
-               if (ret)
-                       goto failed_clk_ptp;
-       }
-
        fep->reg_phy = devm_regulator_get(&pdev->dev, "phy");
        if (!IS_ERR(fep->reg_phy)) {
                ret = regulator_enable(fep->reg_phy);
@@ -2225,6 +2599,8 @@ fec_probe(struct platform_device *pdev)
 
        /* Carrier starts down, phylib will bring it up */
        netif_carrier_off(ndev);
+       fec_enet_clk_enable(ndev, false);
+       pinctrl_pm_select_sleep_state(&pdev->dev);
 
        ret = register_netdev(ndev);
        if (ret)
@@ -2244,15 +2620,7 @@ fec_probe(struct platform_device *pdev)
        if (fep->reg_phy)
                regulator_disable(fep->reg_phy);
 failed_regulator:
-       if (fep->clk_ptp)
-               clk_disable_unprepare(fep->clk_ptp);
-failed_clk_ptp:
-       if (fep->clk_enet_out)
-               clk_disable_unprepare(fep->clk_enet_out);
-failed_clk_enet_out:
-       clk_disable_unprepare(fep->clk_ipg);
-failed_clk_ipg:
-       clk_disable_unprepare(fep->clk_ahb);
+       fec_enet_clk_enable(ndev, false);
 failed_clk:
 failed_ioremap:
        free_netdev(ndev);
@@ -2272,14 +2640,9 @@ fec_drv_remove(struct platform_device *pdev)
        del_timer_sync(&fep->time_keep);
        if (fep->reg_phy)
                regulator_disable(fep->reg_phy);
-       if (fep->clk_ptp)
-               clk_disable_unprepare(fep->clk_ptp);
        if (fep->ptp_clock)
                ptp_clock_unregister(fep->ptp_clock);
-       if (fep->clk_enet_out)
-               clk_disable_unprepare(fep->clk_enet_out);
-       clk_disable_unprepare(fep->clk_ipg);
-       clk_disable_unprepare(fep->clk_ahb);
+       fec_enet_clk_enable(ndev, false);
        free_netdev(ndev);
 
        return 0;
@@ -2296,12 +2659,8 @@ fec_suspend(struct device *dev)
                fec_stop(ndev);
                netif_device_detach(ndev);
        }
-       if (fep->clk_ptp)
-               clk_disable_unprepare(fep->clk_ptp);
-       if (fep->clk_enet_out)
-               clk_disable_unprepare(fep->clk_enet_out);
-       clk_disable_unprepare(fep->clk_ipg);
-       clk_disable_unprepare(fep->clk_ahb);
+       fec_enet_clk_enable(ndev, false);
+       pinctrl_pm_select_sleep_state(&fep->pdev->dev);
 
        if (fep->reg_phy)
                regulator_disable(fep->reg_phy);
@@ -2322,25 +2681,10 @@ fec_resume(struct device *dev)
                        return ret;
        }
 
-       ret = clk_prepare_enable(fep->clk_ahb);
+       pinctrl_pm_select_default_state(&fep->pdev->dev);
+       ret = fec_enet_clk_enable(ndev, true);
        if (ret)
-               goto failed_clk_ahb;
-
-       ret = clk_prepare_enable(fep->clk_ipg);
-       if (ret)
-               goto failed_clk_ipg;
-
-       if (fep->clk_enet_out) {
-               ret = clk_prepare_enable(fep->clk_enet_out);
-               if (ret)
-                       goto failed_clk_enet_out;
-       }
-
-       if (fep->clk_ptp) {
-               ret = clk_prepare_enable(fep->clk_ptp);
-               if (ret)
-                       goto failed_clk_ptp;
-       }
+               goto failed_clk;
 
        if (netif_running(ndev)) {
                fec_restart(ndev, fep->full_duplex);
@@ -2349,14 +2693,7 @@ fec_resume(struct device *dev)
 
        return 0;
 
-failed_clk_ptp:
-       if (fep->clk_enet_out)
-               clk_disable_unprepare(fep->clk_enet_out);
-failed_clk_enet_out:
-       clk_disable_unprepare(fep->clk_ipg);
-failed_clk_ipg:
-       clk_disable_unprepare(fep->clk_ahb);
-failed_clk_ahb:
+failed_clk:
        if (fep->reg_phy)
                regulator_disable(fep->reg_phy);
        return ret;
index dc80db41d6b3397388b0210283c4c7fd3ce07680..cfaf17b70f3fc5d6ab7a11a81266d67267646f33 100644 (file)
@@ -791,10 +791,6 @@ static int fs_init_phy(struct net_device *dev)
 
        phydev = of_phy_connect(dev, fep->fpi->phy_node, &fs_adjust_link, 0,
                                iface);
-       if (!phydev) {
-               phydev = of_phy_connect_fixed_link(dev, &fs_adjust_link,
-                                                  iface);
-       }
        if (!phydev) {
                dev_err(&dev->dev, "Could not attach to PHY\n");
                return -ENODEV;
@@ -1029,9 +1025,16 @@ static int fs_enet_probe(struct platform_device *ofdev)
        fpi->use_napi = 1;
        fpi->napi_weight = 17;
        fpi->phy_node = of_parse_phandle(ofdev->dev.of_node, "phy-handle", 0);
-       if ((!fpi->phy_node) && (!of_get_property(ofdev->dev.of_node, "fixed-link",
-                                                 NULL)))
-               goto out_free_fpi;
+       if (!fpi->phy_node && of_phy_is_fixed_link(ofdev->dev.of_node)) {
+               err = of_phy_register_fixed_link(ofdev->dev.of_node);
+               if (err)
+                       goto out_free_fpi;
+
+               /* In the case of a fixed PHY, the DT node associated
+                * to the PHY is the Ethernet MAC DT node.
+                */
+               fpi->phy_node = ofdev->dev.of_node;
+       }
 
        if (of_device_is_compatible(ofdev->dev.of_node, "fsl,mpc5125-fec")) {
                phy_connection_type = of_get_property(ofdev->dev.of_node,
index ee6ddbd4f252789c0adc76007053b34c5468e665..a6cf40e62f3a9c531f2997c34590fbe89d0ddc80 100644 (file)
@@ -889,6 +889,17 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
 
        priv->phy_node = of_parse_phandle(np, "phy-handle", 0);
 
+       /* In the case of a fixed PHY, the DT node associated
+        * to the PHY is the Ethernet MAC DT node.
+        */
+       if (of_phy_is_fixed_link(np)) {
+               err = of_phy_register_fixed_link(np);
+               if (err)
+                       goto err_grp_init;
+
+               priv->phy_node = np;
+       }
+
        /* Find the TBI PHY.  If it's not there, we don't support SGMII */
        priv->tbi_node = of_parse_phandle(np, "tbi-handle", 0);
 
@@ -1231,7 +1242,7 @@ static void gfar_hw_init(struct gfar_private *priv)
                gfar_write_isrg(priv);
 }
 
-static void __init gfar_init_addr_hash_table(struct gfar_private *priv)
+static void gfar_init_addr_hash_table(struct gfar_private *priv)
 {
        struct gfar __iomem *regs = priv->gfargrp[0].regs;
 
@@ -1373,6 +1384,9 @@ static int gfar_probe(struct platform_device *ofdev)
 
        gfar_hw_init(priv);
 
+       /* Carrier starts down, phylib will bring it up */
+       netif_carrier_off(dev);
+
        err = register_netdev(dev);
 
        if (err) {
@@ -1380,9 +1394,6 @@ static int gfar_probe(struct platform_device *ofdev)
                goto register_fail;
        }
 
-       /* Carrier starts down, phylib will bring it up */
-       netif_carrier_off(dev);
-
        device_init_wakeup(&dev->dev,
                           priv->device_flags &
                           FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
@@ -1660,9 +1671,6 @@ static int init_phy(struct net_device *dev)
 
        priv->phydev = of_phy_connect(dev, priv->phy_node, &adjust_link, 0,
                                      interface);
-       if (!priv->phydev)
-               priv->phydev = of_phy_connect_fixed_link(dev, &adjust_link,
-                                                        interface);
        if (!priv->phydev) {
                dev_err(&dev->dev, "could not attach to PHY\n");
                return -ENODEV;
index c8299c31b21f9f5c52dc380f9b867597c1b8bcdf..fab39e2954410106f9c26304f73c29169c1d35a1 100644 (file)
@@ -1728,9 +1728,6 @@ static int init_phy(struct net_device *dev)
 
        phydev = of_phy_connect(dev, ug_info->phy_node, &adjust_link, 0,
                                priv->phy_interface);
-       if (!phydev)
-               phydev = of_phy_connect_fixed_link(dev, &adjust_link,
-                                                  priv->phy_interface);
        if (!phydev) {
                dev_err(&dev->dev, "Could not attach to PHY\n");
                return -ENODEV;
@@ -3790,6 +3787,17 @@ static int ucc_geth_probe(struct platform_device* ofdev)
        ug_info->uf_info.irq = irq_of_parse_and_map(np, 0);
 
        ug_info->phy_node = of_parse_phandle(np, "phy-handle", 0);
+       if (!ug_info->phy_node) {
+               /* In the case of a fixed PHY, the DT node associated
+                * to the PHY is the Ethernet MAC DT node.
+                */
+               if (of_phy_is_fixed_link(np)) {
+                       err = of_phy_register_fixed_link(np);
+                       if (err)
+                               return err;
+               }
+               ug_info->phy_node = np;
+       }
 
        /* Find the TBI PHY node.  If it's not there, we don't support SGMII */
        ug_info->tbi_node = of_parse_phandle(np, "tbi-handle", 0);
index 413329eff2ffc05f80197d05d16a9c4332821b6b..cc83350d56ba1c05aa7eb619b607635f76de6400 100644 (file)
@@ -417,5 +417,5 @@ static const struct ethtool_ops uec_ethtool_ops = {
 
 void uec_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &uec_ethtool_ops);
+       netdev->ethtool_ops = &uec_ethtool_ops;
 }
index d449fcb901999cce522a0301a26692ff6df34b0f..0c9d55c862aeb40bb8251bc10825549ac01aa6b3 100644 (file)
@@ -162,7 +162,9 @@ static int xgmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
 
        /* Return all Fs if nothing was there */
        if (in_be32(&regs->mdio_stat) & MDIO_STAT_RD_ER) {
-               dev_err(&bus->dev, "MDIO read error\n");
+               dev_err(&bus->dev,
+                       "Error while reading PHY%d reg at %d.%d\n",
+                       phy_id, dev_addr, regnum);
                return 0xffff;
        }
 
index 7becab1aa3e43b41c8f4b942cce18d9c584152fd..cfe7a74317307f8ef1ef39acd2c78c97e903d423 100644 (file)
@@ -256,7 +256,7 @@ static int fmvj18x_probe(struct pcmcia_device *link)
     dev->netdev_ops = &fjn_netdev_ops;
     dev->watchdog_timeo = TX_TIMEOUT;
 
-    SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
+    dev->ethtool_ops = &netdev_ethtool_ops;
 
     return fmvj18x_config(link);
 } /* fmvj18x_attach */
diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig
new file mode 100644 (file)
index 0000000..e942173
--- /dev/null
@@ -0,0 +1,27 @@
+#
+# HISILICON device configuration
+#
+
+config NET_VENDOR_HISILICON
+       bool "Hisilicon devices"
+       default y
+       depends on ARM
+       ---help---
+         If you have a network (Ethernet) card belonging to this class, say Y
+         and read the Ethernet-HOWTO, available from
+         <http://www.tldp.org/docs.html#howto>.
+
+         Note that the answer to this question doesn't directly affect the
+         kernel: saying N will just cause the configurator to skip all
+         the questions about Hisilicon devices. If you say Y, you will be asked
+         for your specific card in the following questions.
+
+if NET_VENDOR_HISILICON
+
+config HIX5HD2_GMAC
+       tristate "Hisilicon HIX5HD2 Family Network Device Support"
+       select PHYLIB
+       help
+         This selects the hix5hd2 mac family network device.
+
+endif # NET_VENDOR_HISILICON
diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile
new file mode 100644 (file)
index 0000000..9175e84
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Makefile for the HISILICON network device drivers.
+#
+
+obj-$(CONFIG_HIX5HD2_GMAC) += hix5hd2_gmac.o
diff --git a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c
new file mode 100644 (file)
index 0000000..0ffdcd3
--- /dev/null
@@ -0,0 +1,1066 @@
+/* Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * 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/interrupt.h>
+#include <linux/etherdevice.h>
+#include <linux/platform_device.h>
+#include <linux/of_net.h>
+#include <linux/of_mdio.h>
+#include <linux/clk.h>
+#include <linux/circ_buf.h>
+
+#define STATION_ADDR_LOW               0x0000
+#define STATION_ADDR_HIGH              0x0004
+#define MAC_DUPLEX_HALF_CTRL           0x0008
+#define MAX_FRM_SIZE                   0x003c
+#define PORT_MODE                      0x0040
+#define PORT_EN                                0x0044
+#define BITS_TX_EN                     BIT(2)
+#define BITS_RX_EN                     BIT(1)
+#define REC_FILT_CONTROL               0x0064
+#define BIT_CRC_ERR_PASS               BIT(5)
+#define BIT_PAUSE_FRM_PASS             BIT(4)
+#define BIT_VLAN_DROP_EN               BIT(3)
+#define BIT_BC_DROP_EN                 BIT(2)
+#define BIT_MC_MATCH_EN                        BIT(1)
+#define BIT_UC_MATCH_EN                        BIT(0)
+#define PORT_MC_ADDR_LOW               0x0068
+#define PORT_MC_ADDR_HIGH              0x006C
+#define CF_CRC_STRIP                   0x01b0
+#define MODE_CHANGE_EN                 0x01b4
+#define BIT_MODE_CHANGE_EN             BIT(0)
+#define COL_SLOT_TIME                  0x01c0
+#define RECV_CONTROL                   0x01e0
+#define BIT_STRIP_PAD_EN               BIT(3)
+#define BIT_RUNT_PKT_EN                        BIT(4)
+#define CONTROL_WORD                   0x0214
+#define MDIO_SINGLE_CMD                        0x03c0
+#define MDIO_SINGLE_DATA               0x03c4
+#define MDIO_CTRL                      0x03cc
+#define MDIO_RDATA_STATUS              0x03d0
+
+#define MDIO_START                     BIT(20)
+#define MDIO_R_VALID                   BIT(0)
+#define MDIO_READ                      (BIT(17) | MDIO_START)
+#define MDIO_WRITE                     (BIT(16) | MDIO_START)
+
+#define RX_FQ_START_ADDR               0x0500
+#define RX_FQ_DEPTH                    0x0504
+#define RX_FQ_WR_ADDR                  0x0508
+#define RX_FQ_RD_ADDR                  0x050c
+#define RX_FQ_VLDDESC_CNT              0x0510
+#define RX_FQ_ALEMPTY_TH               0x0514
+#define RX_FQ_REG_EN                   0x0518
+#define BITS_RX_FQ_START_ADDR_EN       BIT(2)
+#define BITS_RX_FQ_DEPTH_EN            BIT(1)
+#define BITS_RX_FQ_RD_ADDR_EN          BIT(0)
+#define RX_FQ_ALFULL_TH                        0x051c
+#define RX_BQ_START_ADDR               0x0520
+#define RX_BQ_DEPTH                    0x0524
+#define RX_BQ_WR_ADDR                  0x0528
+#define RX_BQ_RD_ADDR                  0x052c
+#define RX_BQ_FREE_DESC_CNT            0x0530
+#define RX_BQ_ALEMPTY_TH               0x0534
+#define RX_BQ_REG_EN                   0x0538
+#define BITS_RX_BQ_START_ADDR_EN       BIT(2)
+#define BITS_RX_BQ_DEPTH_EN            BIT(1)
+#define BITS_RX_BQ_WR_ADDR_EN          BIT(0)
+#define RX_BQ_ALFULL_TH                        0x053c
+#define TX_BQ_START_ADDR               0x0580
+#define TX_BQ_DEPTH                    0x0584
+#define TX_BQ_WR_ADDR                  0x0588
+#define TX_BQ_RD_ADDR                  0x058c
+#define TX_BQ_VLDDESC_CNT              0x0590
+#define TX_BQ_ALEMPTY_TH               0x0594
+#define TX_BQ_REG_EN                   0x0598
+#define BITS_TX_BQ_START_ADDR_EN       BIT(2)
+#define BITS_TX_BQ_DEPTH_EN            BIT(1)
+#define BITS_TX_BQ_RD_ADDR_EN          BIT(0)
+#define TX_BQ_ALFULL_TH                        0x059c
+#define TX_RQ_START_ADDR               0x05a0
+#define TX_RQ_DEPTH                    0x05a4
+#define TX_RQ_WR_ADDR                  0x05a8
+#define TX_RQ_RD_ADDR                  0x05ac
+#define TX_RQ_FREE_DESC_CNT            0x05b0
+#define TX_RQ_ALEMPTY_TH               0x05b4
+#define TX_RQ_REG_EN                   0x05b8
+#define BITS_TX_RQ_START_ADDR_EN       BIT(2)
+#define BITS_TX_RQ_DEPTH_EN            BIT(1)
+#define BITS_TX_RQ_WR_ADDR_EN          BIT(0)
+#define TX_RQ_ALFULL_TH                        0x05bc
+#define RAW_PMU_INT                    0x05c0
+#define ENA_PMU_INT                    0x05c4
+#define STATUS_PMU_INT                 0x05c8
+#define MAC_FIFO_ERR_IN                        BIT(30)
+#define TX_RQ_IN_TIMEOUT_INT           BIT(29)
+#define RX_BQ_IN_TIMEOUT_INT           BIT(28)
+#define TXOUTCFF_FULL_INT              BIT(27)
+#define TXOUTCFF_EMPTY_INT             BIT(26)
+#define TXCFF_FULL_INT                 BIT(25)
+#define TXCFF_EMPTY_INT                        BIT(24)
+#define RXOUTCFF_FULL_INT              BIT(23)
+#define RXOUTCFF_EMPTY_INT             BIT(22)
+#define RXCFF_FULL_INT                 BIT(21)
+#define RXCFF_EMPTY_INT                        BIT(20)
+#define TX_RQ_IN_INT                   BIT(19)
+#define TX_BQ_OUT_INT                  BIT(18)
+#define RX_BQ_IN_INT                   BIT(17)
+#define RX_FQ_OUT_INT                  BIT(16)
+#define TX_RQ_EMPTY_INT                        BIT(15)
+#define TX_RQ_FULL_INT                 BIT(14)
+#define TX_RQ_ALEMPTY_INT              BIT(13)
+#define TX_RQ_ALFULL_INT               BIT(12)
+#define TX_BQ_EMPTY_INT                        BIT(11)
+#define TX_BQ_FULL_INT                 BIT(10)
+#define TX_BQ_ALEMPTY_INT              BIT(9)
+#define TX_BQ_ALFULL_INT               BIT(8)
+#define RX_BQ_EMPTY_INT                        BIT(7)
+#define RX_BQ_FULL_INT                 BIT(6)
+#define RX_BQ_ALEMPTY_INT              BIT(5)
+#define RX_BQ_ALFULL_INT               BIT(4)
+#define RX_FQ_EMPTY_INT                        BIT(3)
+#define RX_FQ_FULL_INT                 BIT(2)
+#define RX_FQ_ALEMPTY_INT              BIT(1)
+#define RX_FQ_ALFULL_INT               BIT(0)
+
+#define DEF_INT_MASK                   (RX_BQ_IN_INT | RX_BQ_IN_TIMEOUT_INT | \
+                                       TX_RQ_IN_INT | TX_RQ_IN_TIMEOUT_INT)
+
+#define DESC_WR_RD_ENA                 0x05cc
+#define IN_QUEUE_TH                    0x05d8
+#define OUT_QUEUE_TH                   0x05dc
+#define QUEUE_TX_BQ_SHIFT              16
+#define RX_BQ_IN_TIMEOUT_TH            0x05e0
+#define TX_RQ_IN_TIMEOUT_TH            0x05e4
+#define STOP_CMD                       0x05e8
+#define BITS_TX_STOP                   BIT(1)
+#define BITS_RX_STOP                   BIT(0)
+#define FLUSH_CMD                      0x05eC
+#define BITS_TX_FLUSH_CMD              BIT(5)
+#define BITS_RX_FLUSH_CMD              BIT(4)
+#define BITS_TX_FLUSH_FLAG_DOWN                BIT(3)
+#define BITS_TX_FLUSH_FLAG_UP          BIT(2)
+#define BITS_RX_FLUSH_FLAG_DOWN                BIT(1)
+#define BITS_RX_FLUSH_FLAG_UP          BIT(0)
+#define RX_CFF_NUM_REG                 0x05f0
+#define PMU_FSM_REG                    0x05f8
+#define RX_FIFO_PKT_IN_NUM             0x05fc
+#define RX_FIFO_PKT_OUT_NUM            0x0600
+
+#define RGMII_SPEED_1000               0x2c
+#define RGMII_SPEED_100                        0x2f
+#define RGMII_SPEED_10                 0x2d
+#define MII_SPEED_100                  0x0f
+#define MII_SPEED_10                   0x0d
+#define GMAC_SPEED_1000                        0x05
+#define GMAC_SPEED_100                 0x01
+#define GMAC_SPEED_10                  0x00
+#define GMAC_FULL_DUPLEX               BIT(4)
+
+#define RX_BQ_INT_THRESHOLD            0x01
+#define TX_RQ_INT_THRESHOLD            0x01
+#define RX_BQ_IN_TIMEOUT               0x10000
+#define TX_RQ_IN_TIMEOUT               0x50000
+
+#define MAC_MAX_FRAME_SIZE             1600
+#define DESC_SIZE                      32
+#define RX_DESC_NUM                    1024
+#define TX_DESC_NUM                    1024
+
+#define DESC_VLD_FREE                  0
+#define DESC_VLD_BUSY                  0x80000000
+#define DESC_FL_MID                    0
+#define DESC_FL_LAST                   0x20000000
+#define DESC_FL_FIRST                  0x40000000
+#define DESC_FL_FULL                   0x60000000
+#define DESC_DATA_LEN_OFF              16
+#define DESC_BUFF_LEN_OFF              0
+#define DESC_DATA_MASK                 0x7ff
+
+/* DMA descriptor ring helpers */
+#define dma_ring_incr(n, s)            (((n) + 1) & ((s) - 1))
+#define dma_cnt(n)                     ((n) >> 5)
+#define dma_byte(n)                    ((n) << 5)
+
+struct hix5hd2_desc {
+       __le32 buff_addr;
+       __le32 cmd;
+} __aligned(32);
+
+struct hix5hd2_desc_sw {
+       struct hix5hd2_desc *desc;
+       dma_addr_t      phys_addr;
+       unsigned int    count;
+       unsigned int    size;
+};
+
+#define QUEUE_NUMS     4
+struct hix5hd2_priv {
+       struct hix5hd2_desc_sw pool[QUEUE_NUMS];
+#define rx_fq          pool[0]
+#define rx_bq          pool[1]
+#define tx_bq          pool[2]
+#define tx_rq          pool[3]
+
+       void __iomem *base;
+       void __iomem *ctrl_base;
+
+       struct sk_buff *tx_skb[TX_DESC_NUM];
+       struct sk_buff *rx_skb[RX_DESC_NUM];
+
+       struct device *dev;
+       struct net_device *netdev;
+
+       struct phy_device *phy;
+       struct device_node *phy_node;
+       phy_interface_t phy_mode;
+
+       unsigned int speed;
+       unsigned int duplex;
+
+       struct clk *clk;
+       struct mii_bus *bus;
+       struct napi_struct napi;
+       struct work_struct tx_timeout_task;
+};
+
+static void hix5hd2_config_port(struct net_device *dev, u32 speed, u32 duplex)
+{
+       struct hix5hd2_priv *priv = netdev_priv(dev);
+       u32 val;
+
+       priv->speed = speed;
+       priv->duplex = duplex;
+
+       switch (priv->phy_mode) {
+       case PHY_INTERFACE_MODE_RGMII:
+               if (speed == SPEED_1000)
+                       val = RGMII_SPEED_1000;
+               else if (speed == SPEED_100)
+                       val = RGMII_SPEED_100;
+               else
+                       val = RGMII_SPEED_10;
+               break;
+       case PHY_INTERFACE_MODE_MII:
+               if (speed == SPEED_100)
+                       val = MII_SPEED_100;
+               else
+                       val = MII_SPEED_10;
+               break;
+       default:
+               netdev_warn(dev, "not supported mode\n");
+               val = MII_SPEED_10;
+               break;
+       }
+
+       if (duplex)
+               val |= GMAC_FULL_DUPLEX;
+       writel_relaxed(val, priv->ctrl_base);
+
+       writel_relaxed(BIT_MODE_CHANGE_EN, priv->base + MODE_CHANGE_EN);
+       if (speed == SPEED_1000)
+               val = GMAC_SPEED_1000;
+       else if (speed == SPEED_100)
+               val = GMAC_SPEED_100;
+       else
+               val = GMAC_SPEED_10;
+       writel_relaxed(val, priv->base + PORT_MODE);
+       writel_relaxed(0, priv->base + MODE_CHANGE_EN);
+       writel_relaxed(duplex, priv->base + MAC_DUPLEX_HALF_CTRL);
+}
+
+static void hix5hd2_set_desc_depth(struct hix5hd2_priv *priv, int rx, int tx)
+{
+       writel_relaxed(BITS_RX_FQ_DEPTH_EN, priv->base + RX_FQ_REG_EN);
+       writel_relaxed(rx << 3, priv->base + RX_FQ_DEPTH);
+       writel_relaxed(0, priv->base + RX_FQ_REG_EN);
+
+       writel_relaxed(BITS_RX_BQ_DEPTH_EN, priv->base + RX_BQ_REG_EN);
+       writel_relaxed(rx << 3, priv->base + RX_BQ_DEPTH);
+       writel_relaxed(0, priv->base + RX_BQ_REG_EN);
+
+       writel_relaxed(BITS_TX_BQ_DEPTH_EN, priv->base + TX_BQ_REG_EN);
+       writel_relaxed(tx << 3, priv->base + TX_BQ_DEPTH);
+       writel_relaxed(0, priv->base + TX_BQ_REG_EN);
+
+       writel_relaxed(BITS_TX_RQ_DEPTH_EN, priv->base + TX_RQ_REG_EN);
+       writel_relaxed(tx << 3, priv->base + TX_RQ_DEPTH);
+       writel_relaxed(0, priv->base + TX_RQ_REG_EN);
+}
+
+static void hix5hd2_set_rx_fq(struct hix5hd2_priv *priv, dma_addr_t phy_addr)
+{
+       writel_relaxed(BITS_RX_FQ_START_ADDR_EN, priv->base + RX_FQ_REG_EN);
+       writel_relaxed(phy_addr, priv->base + RX_FQ_START_ADDR);
+       writel_relaxed(0, priv->base + RX_FQ_REG_EN);
+}
+
+static void hix5hd2_set_rx_bq(struct hix5hd2_priv *priv, dma_addr_t phy_addr)
+{
+       writel_relaxed(BITS_RX_BQ_START_ADDR_EN, priv->base + RX_BQ_REG_EN);
+       writel_relaxed(phy_addr, priv->base + RX_BQ_START_ADDR);
+       writel_relaxed(0, priv->base + RX_BQ_REG_EN);
+}
+
+static void hix5hd2_set_tx_bq(struct hix5hd2_priv *priv, dma_addr_t phy_addr)
+{
+       writel_relaxed(BITS_TX_BQ_START_ADDR_EN, priv->base + TX_BQ_REG_EN);
+       writel_relaxed(phy_addr, priv->base + TX_BQ_START_ADDR);
+       writel_relaxed(0, priv->base + TX_BQ_REG_EN);
+}
+
+static void hix5hd2_set_tx_rq(struct hix5hd2_priv *priv, dma_addr_t phy_addr)
+{
+       writel_relaxed(BITS_TX_RQ_START_ADDR_EN, priv->base + TX_RQ_REG_EN);
+       writel_relaxed(phy_addr, priv->base + TX_RQ_START_ADDR);
+       writel_relaxed(0, priv->base + TX_RQ_REG_EN);
+}
+
+static void hix5hd2_set_desc_addr(struct hix5hd2_priv *priv)
+{
+       hix5hd2_set_rx_fq(priv, priv->rx_fq.phys_addr);
+       hix5hd2_set_rx_bq(priv, priv->rx_bq.phys_addr);
+       hix5hd2_set_tx_rq(priv, priv->tx_rq.phys_addr);
+       hix5hd2_set_tx_bq(priv, priv->tx_bq.phys_addr);
+}
+
+static void hix5hd2_hw_init(struct hix5hd2_priv *priv)
+{
+       u32 val;
+
+       /* disable and clear all interrupts */
+       writel_relaxed(0, priv->base + ENA_PMU_INT);
+       writel_relaxed(~0, priv->base + RAW_PMU_INT);
+
+       writel_relaxed(BIT_CRC_ERR_PASS, priv->base + REC_FILT_CONTROL);
+       writel_relaxed(MAC_MAX_FRAME_SIZE, priv->base + CONTROL_WORD);
+       writel_relaxed(0, priv->base + COL_SLOT_TIME);
+
+       val = RX_BQ_INT_THRESHOLD | TX_RQ_INT_THRESHOLD << QUEUE_TX_BQ_SHIFT;
+       writel_relaxed(val, priv->base + IN_QUEUE_TH);
+
+       writel_relaxed(RX_BQ_IN_TIMEOUT, priv->base + RX_BQ_IN_TIMEOUT_TH);
+       writel_relaxed(TX_RQ_IN_TIMEOUT, priv->base + TX_RQ_IN_TIMEOUT_TH);
+
+       hix5hd2_set_desc_depth(priv, RX_DESC_NUM, TX_DESC_NUM);
+       hix5hd2_set_desc_addr(priv);
+}
+
+static void hix5hd2_irq_enable(struct hix5hd2_priv *priv)
+{
+       writel_relaxed(DEF_INT_MASK, priv->base + ENA_PMU_INT);
+}
+
+static void hix5hd2_irq_disable(struct hix5hd2_priv *priv)
+{
+       writel_relaxed(0, priv->base + ENA_PMU_INT);
+}
+
+static void hix5hd2_port_enable(struct hix5hd2_priv *priv)
+{
+       writel_relaxed(0xf, priv->base + DESC_WR_RD_ENA);
+       writel_relaxed(BITS_RX_EN | BITS_TX_EN, priv->base + PORT_EN);
+}
+
+static void hix5hd2_port_disable(struct hix5hd2_priv *priv)
+{
+       writel_relaxed(~(BITS_RX_EN | BITS_TX_EN), priv->base + PORT_EN);
+       writel_relaxed(0, priv->base + DESC_WR_RD_ENA);
+}
+
+static void hix5hd2_hw_set_mac_addr(struct net_device *dev)
+{
+       struct hix5hd2_priv *priv = netdev_priv(dev);
+       unsigned char *mac = dev->dev_addr;
+       u32 val;
+
+       val = mac[1] | (mac[0] << 8);
+       writel_relaxed(val, priv->base + STATION_ADDR_HIGH);
+
+       val = mac[5] | (mac[4] << 8) | (mac[3] << 16) | (mac[2] << 24);
+       writel_relaxed(val, priv->base + STATION_ADDR_LOW);
+}
+
+static int hix5hd2_net_set_mac_address(struct net_device *dev, void *p)
+{
+       int ret;
+
+       ret = eth_mac_addr(dev, p);
+       if (!ret)
+               hix5hd2_hw_set_mac_addr(dev);
+
+       return ret;
+}
+
+static void hix5hd2_adjust_link(struct net_device *dev)
+{
+       struct hix5hd2_priv *priv = netdev_priv(dev);
+       struct phy_device *phy = priv->phy;
+
+       if ((priv->speed != phy->speed) || (priv->duplex != phy->duplex)) {
+               hix5hd2_config_port(dev, phy->speed, phy->duplex);
+               phy_print_status(phy);
+       }
+}
+
+static void hix5hd2_rx_refill(struct hix5hd2_priv *priv)
+{
+       struct hix5hd2_desc *desc;
+       struct sk_buff *skb;
+       u32 start, end, num, pos, i;
+       u32 len = MAC_MAX_FRAME_SIZE;
+       dma_addr_t addr;
+
+       /* software write pointer */
+       start = dma_cnt(readl_relaxed(priv->base + RX_FQ_WR_ADDR));
+       /* logic read pointer */
+       end = dma_cnt(readl_relaxed(priv->base + RX_FQ_RD_ADDR));
+       num = CIRC_SPACE(start, end, RX_DESC_NUM);
+
+       for (i = 0, pos = start; i < num; i++) {
+               if (priv->rx_skb[pos]) {
+                       break;
+               } else {
+                       skb = netdev_alloc_skb_ip_align(priv->netdev, len);
+                       if (unlikely(skb == NULL))
+                               break;
+               }
+
+               addr = dma_map_single(priv->dev, skb->data, len, DMA_FROM_DEVICE);
+               if (dma_mapping_error(priv->dev, addr)) {
+                       dev_kfree_skb_any(skb);
+                       break;
+               }
+
+               desc = priv->rx_fq.desc + pos;
+               desc->buff_addr = cpu_to_le32(addr);
+               priv->rx_skb[pos] = skb;
+               desc->cmd = cpu_to_le32(DESC_VLD_FREE |
+                                       (len - 1) << DESC_BUFF_LEN_OFF);
+               pos = dma_ring_incr(pos, RX_DESC_NUM);
+       }
+
+       /* ensure desc updated */
+       wmb();
+
+       if (pos != start)
+               writel_relaxed(dma_byte(pos), priv->base + RX_FQ_WR_ADDR);
+}
+
+static int hix5hd2_rx(struct net_device *dev, int limit)
+{
+       struct hix5hd2_priv *priv = netdev_priv(dev);
+       struct sk_buff *skb;
+       struct hix5hd2_desc *desc;
+       dma_addr_t addr;
+       u32 start, end, num, pos, i, len;
+
+       /* software read pointer */
+       start = dma_cnt(readl_relaxed(priv->base + RX_BQ_RD_ADDR));
+       /* logic write pointer */
+       end = dma_cnt(readl_relaxed(priv->base + RX_BQ_WR_ADDR));
+       num = CIRC_CNT(end, start, RX_DESC_NUM);
+       if (num > limit)
+               num = limit;
+
+       /* ensure get updated desc */
+       rmb();
+       for (i = 0, pos = start; i < num; i++) {
+               skb = priv->rx_skb[pos];
+               if (unlikely(!skb)) {
+                       netdev_err(dev, "inconsistent rx_skb\n");
+                       break;
+               }
+               priv->rx_skb[pos] = NULL;
+
+               desc = priv->rx_bq.desc + pos;
+               len = (le32_to_cpu(desc->cmd) >> DESC_DATA_LEN_OFF) &
+                      DESC_DATA_MASK;
+               addr = le32_to_cpu(desc->buff_addr);
+               dma_unmap_single(priv->dev, addr, MAC_MAX_FRAME_SIZE,
+                                DMA_FROM_DEVICE);
+
+               skb_put(skb, len);
+               if (skb->len > MAC_MAX_FRAME_SIZE) {
+                       netdev_err(dev, "rcv len err, len = %d\n", skb->len);
+                       dev->stats.rx_errors++;
+                       dev->stats.rx_length_errors++;
+                       dev_kfree_skb_any(skb);
+                       goto next;
+               }
+
+               skb->protocol = eth_type_trans(skb, dev);
+               napi_gro_receive(&priv->napi, skb);
+               dev->stats.rx_packets++;
+               dev->stats.rx_bytes += skb->len;
+               dev->last_rx = jiffies;
+next:
+               pos = dma_ring_incr(pos, RX_DESC_NUM);
+       }
+
+       if (pos != start)
+               writel_relaxed(dma_byte(pos), priv->base + RX_BQ_RD_ADDR);
+
+       hix5hd2_rx_refill(priv);
+
+       return num;
+}
+
+static void hix5hd2_xmit_reclaim(struct net_device *dev)
+{
+       struct sk_buff *skb;
+       struct hix5hd2_desc *desc;
+       struct hix5hd2_priv *priv = netdev_priv(dev);
+       unsigned int bytes_compl = 0, pkts_compl = 0;
+       u32 start, end, num, pos, i;
+       dma_addr_t addr;
+
+       netif_tx_lock(dev);
+
+       /* software read */
+       start = dma_cnt(readl_relaxed(priv->base + TX_RQ_RD_ADDR));
+       /* logic write */
+       end = dma_cnt(readl_relaxed(priv->base + TX_RQ_WR_ADDR));
+       num = CIRC_CNT(end, start, TX_DESC_NUM);
+
+       for (i = 0, pos = start; i < num; i++) {
+               skb = priv->tx_skb[pos];
+               if (unlikely(!skb)) {
+                       netdev_err(dev, "inconsistent tx_skb\n");
+                       break;
+               }
+
+               pkts_compl++;
+               bytes_compl += skb->len;
+               desc = priv->tx_rq.desc + pos;
+               addr = le32_to_cpu(desc->buff_addr);
+               dma_unmap_single(priv->dev, addr, skb->len, DMA_TO_DEVICE);
+               priv->tx_skb[pos] = NULL;
+               dev_consume_skb_any(skb);
+               pos = dma_ring_incr(pos, TX_DESC_NUM);
+       }
+
+       if (pos != start)
+               writel_relaxed(dma_byte(pos), priv->base + TX_RQ_RD_ADDR);
+
+       netif_tx_unlock(dev);
+
+       if (pkts_compl || bytes_compl)
+               netdev_completed_queue(dev, pkts_compl, bytes_compl);
+
+       if (unlikely(netif_queue_stopped(priv->netdev)) && pkts_compl)
+               netif_wake_queue(priv->netdev);
+}
+
+static int hix5hd2_poll(struct napi_struct *napi, int budget)
+{
+       struct hix5hd2_priv *priv = container_of(napi,
+                               struct hix5hd2_priv, napi);
+       struct net_device *dev = priv->netdev;
+       int work_done = 0, task = budget;
+       int ints, num;
+
+       do {
+               hix5hd2_xmit_reclaim(dev);
+               num = hix5hd2_rx(dev, task);
+               work_done += num;
+               task -= num;
+               if ((work_done >= budget) || (num == 0))
+                       break;
+
+               ints = readl_relaxed(priv->base + RAW_PMU_INT);
+               writel_relaxed(ints, priv->base + RAW_PMU_INT);
+       } while (ints & DEF_INT_MASK);
+
+       if (work_done < budget) {
+               napi_complete(napi);
+               hix5hd2_irq_enable(priv);
+       }
+
+       return work_done;
+}
+
+static irqreturn_t hix5hd2_interrupt(int irq, void *dev_id)
+{
+       struct net_device *dev = (struct net_device *)dev_id;
+       struct hix5hd2_priv *priv = netdev_priv(dev);
+       int ints = readl_relaxed(priv->base + RAW_PMU_INT);
+
+       writel_relaxed(ints, priv->base + RAW_PMU_INT);
+       if (likely(ints & DEF_INT_MASK)) {
+               hix5hd2_irq_disable(priv);
+               napi_schedule(&priv->napi);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int hix5hd2_net_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct hix5hd2_priv *priv = netdev_priv(dev);
+       struct hix5hd2_desc *desc;
+       dma_addr_t addr;
+       u32 pos;
+
+       /* software write pointer */
+       pos = dma_cnt(readl_relaxed(priv->base + TX_BQ_WR_ADDR));
+       if (unlikely(priv->tx_skb[pos])) {
+               dev->stats.tx_dropped++;
+               dev->stats.tx_fifo_errors++;
+               netif_stop_queue(dev);
+               return NETDEV_TX_BUSY;
+       }
+
+       addr = dma_map_single(priv->dev, skb->data, skb->len, DMA_TO_DEVICE);
+       if (dma_mapping_error(priv->dev, addr)) {
+               dev_kfree_skb_any(skb);
+               return NETDEV_TX_OK;
+       }
+
+       desc = priv->tx_bq.desc + pos;
+       desc->buff_addr = cpu_to_le32(addr);
+       priv->tx_skb[pos] = skb;
+       desc->cmd = cpu_to_le32(DESC_VLD_BUSY | DESC_FL_FULL |
+                               (skb->len & DESC_DATA_MASK) << DESC_DATA_LEN_OFF |
+                               (skb->len & DESC_DATA_MASK) << DESC_BUFF_LEN_OFF);
+
+       /* ensure desc updated */
+       wmb();
+
+       pos = dma_ring_incr(pos, TX_DESC_NUM);
+       writel_relaxed(dma_byte(pos), priv->base + TX_BQ_WR_ADDR);
+
+       dev->trans_start = jiffies;
+       dev->stats.tx_packets++;
+       dev->stats.tx_bytes += skb->len;
+       netdev_sent_queue(dev, skb->len);
+
+       return NETDEV_TX_OK;
+}
+
+static void hix5hd2_free_dma_desc_rings(struct hix5hd2_priv *priv)
+{
+       struct hix5hd2_desc *desc;
+       dma_addr_t addr;
+       int i;
+
+       for (i = 0; i < RX_DESC_NUM; i++) {
+               struct sk_buff *skb = priv->rx_skb[i];
+               if (skb == NULL)
+                       continue;
+
+               desc = priv->rx_fq.desc + i;
+               addr = le32_to_cpu(desc->buff_addr);
+               dma_unmap_single(priv->dev, addr,
+                                MAC_MAX_FRAME_SIZE, DMA_FROM_DEVICE);
+               dev_kfree_skb_any(skb);
+               priv->rx_skb[i] = NULL;
+       }
+
+       for (i = 0; i < TX_DESC_NUM; i++) {
+               struct sk_buff *skb = priv->tx_skb[i];
+               if (skb == NULL)
+                       continue;
+
+               desc = priv->tx_rq.desc + i;
+               addr = le32_to_cpu(desc->buff_addr);
+               dma_unmap_single(priv->dev, addr, skb->len, DMA_TO_DEVICE);
+               dev_kfree_skb_any(skb);
+               priv->tx_skb[i] = NULL;
+       }
+}
+
+static int hix5hd2_net_open(struct net_device *dev)
+{
+       struct hix5hd2_priv *priv = netdev_priv(dev);
+       int ret;
+
+       ret = clk_prepare_enable(priv->clk);
+       if (ret < 0) {
+               netdev_err(dev, "failed to enable clk %d\n", ret);
+               return ret;
+       }
+
+       priv->phy = of_phy_connect(dev, priv->phy_node,
+                                  &hix5hd2_adjust_link, 0, priv->phy_mode);
+       if (!priv->phy)
+               return -ENODEV;
+
+       phy_start(priv->phy);
+       hix5hd2_hw_init(priv);
+       hix5hd2_rx_refill(priv);
+
+       netdev_reset_queue(dev);
+       netif_start_queue(dev);
+       napi_enable(&priv->napi);
+
+       hix5hd2_port_enable(priv);
+       hix5hd2_irq_enable(priv);
+
+       return 0;
+}
+
+static int hix5hd2_net_close(struct net_device *dev)
+{
+       struct hix5hd2_priv *priv = netdev_priv(dev);
+
+       hix5hd2_port_disable(priv);
+       hix5hd2_irq_disable(priv);
+       napi_disable(&priv->napi);
+       netif_stop_queue(dev);
+       hix5hd2_free_dma_desc_rings(priv);
+
+       if (priv->phy) {
+               phy_stop(priv->phy);
+               phy_disconnect(priv->phy);
+       }
+
+       clk_disable_unprepare(priv->clk);
+
+       return 0;
+}
+
+static void hix5hd2_tx_timeout_task(struct work_struct *work)
+{
+       struct hix5hd2_priv *priv;
+
+       priv = container_of(work, struct hix5hd2_priv, tx_timeout_task);
+       hix5hd2_net_close(priv->netdev);
+       hix5hd2_net_open(priv->netdev);
+}
+
+static void hix5hd2_net_timeout(struct net_device *dev)
+{
+       struct hix5hd2_priv *priv = netdev_priv(dev);
+
+       schedule_work(&priv->tx_timeout_task);
+}
+
+static const struct net_device_ops hix5hd2_netdev_ops = {
+       .ndo_open               = hix5hd2_net_open,
+       .ndo_stop               = hix5hd2_net_close,
+       .ndo_start_xmit         = hix5hd2_net_xmit,
+       .ndo_tx_timeout         = hix5hd2_net_timeout,
+       .ndo_set_mac_address    = hix5hd2_net_set_mac_address,
+};
+
+static int hix5hd2_get_settings(struct net_device *net_dev,
+                               struct ethtool_cmd *cmd)
+{
+       struct hix5hd2_priv *priv = netdev_priv(net_dev);
+
+       if (!priv->phy)
+               return -ENODEV;
+
+       return phy_ethtool_gset(priv->phy, cmd);
+}
+
+static int hix5hd2_set_settings(struct net_device *net_dev,
+                               struct ethtool_cmd *cmd)
+{
+       struct hix5hd2_priv *priv = netdev_priv(net_dev);
+
+       if (!priv->phy)
+               return -ENODEV;
+
+       return phy_ethtool_sset(priv->phy, cmd);
+}
+
+static struct ethtool_ops hix5hd2_ethtools_ops = {
+       .get_link               = ethtool_op_get_link,
+       .get_settings           = hix5hd2_get_settings,
+       .set_settings           = hix5hd2_set_settings,
+};
+
+static int hix5hd2_mdio_wait_ready(struct mii_bus *bus)
+{
+       struct hix5hd2_priv *priv = bus->priv;
+       void __iomem *base = priv->base;
+       int i, timeout = 10000;
+
+       for (i = 0; readl_relaxed(base + MDIO_SINGLE_CMD) & MDIO_START; i++) {
+               if (i == timeout)
+                       return -ETIMEDOUT;
+               usleep_range(10, 20);
+       }
+
+       return 0;
+}
+
+static int hix5hd2_mdio_read(struct mii_bus *bus, int phy, int reg)
+{
+       struct hix5hd2_priv *priv = bus->priv;
+       void __iomem *base = priv->base;
+       int val, ret;
+
+       ret = hix5hd2_mdio_wait_ready(bus);
+       if (ret < 0)
+               goto out;
+
+       writel_relaxed(MDIO_READ | phy << 8 | reg, base + MDIO_SINGLE_CMD);
+       ret = hix5hd2_mdio_wait_ready(bus);
+       if (ret < 0)
+               goto out;
+
+       val = readl_relaxed(base + MDIO_RDATA_STATUS);
+       if (val & MDIO_R_VALID) {
+               dev_err(bus->parent, "SMI bus read not valid\n");
+               ret = -ENODEV;
+               goto out;
+       }
+
+       val = readl_relaxed(priv->base + MDIO_SINGLE_DATA);
+       ret = (val >> 16) & 0xFFFF;
+out:
+       return ret;
+}
+
+static int hix5hd2_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
+{
+       struct hix5hd2_priv *priv = bus->priv;
+       void __iomem *base = priv->base;
+       int ret;
+
+       ret = hix5hd2_mdio_wait_ready(bus);
+       if (ret < 0)
+               goto out;
+
+       writel_relaxed(val, base + MDIO_SINGLE_DATA);
+       writel_relaxed(MDIO_WRITE | phy << 8 | reg, base + MDIO_SINGLE_CMD);
+       ret = hix5hd2_mdio_wait_ready(bus);
+out:
+       return ret;
+}
+
+static void hix5hd2_destroy_hw_desc_queue(struct hix5hd2_priv *priv)
+{
+       int i;
+
+       for (i = 0; i < QUEUE_NUMS; i++) {
+               if (priv->pool[i].desc) {
+                       dma_free_coherent(priv->dev, priv->pool[i].size,
+                                         priv->pool[i].desc,
+                                         priv->pool[i].phys_addr);
+                       priv->pool[i].desc = NULL;
+               }
+       }
+}
+
+static int hix5hd2_init_hw_desc_queue(struct hix5hd2_priv *priv)
+{
+       struct device *dev = priv->dev;
+       struct hix5hd2_desc *virt_addr;
+       dma_addr_t phys_addr;
+       int size, i;
+
+       priv->rx_fq.count = RX_DESC_NUM;
+       priv->rx_bq.count = RX_DESC_NUM;
+       priv->tx_bq.count = TX_DESC_NUM;
+       priv->tx_rq.count = TX_DESC_NUM;
+
+       for (i = 0; i < QUEUE_NUMS; i++) {
+               size = priv->pool[i].count * sizeof(struct hix5hd2_desc);
+               virt_addr = dma_alloc_coherent(dev, size, &phys_addr,
+                                              GFP_KERNEL);
+               if (virt_addr == NULL)
+                       goto error_free_pool;
+
+               memset(virt_addr, 0, size);
+               priv->pool[i].size = size;
+               priv->pool[i].desc = virt_addr;
+               priv->pool[i].phys_addr = phys_addr;
+       }
+       return 0;
+
+error_free_pool:
+       hix5hd2_destroy_hw_desc_queue(priv);
+
+       return -ENOMEM;
+}
+
+static int hix5hd2_dev_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *node = dev->of_node;
+       struct net_device *ndev;
+       struct hix5hd2_priv *priv;
+       struct resource *res;
+       struct mii_bus *bus;
+       const char *mac_addr;
+       int ret;
+
+       ndev = alloc_etherdev(sizeof(struct hix5hd2_priv));
+       if (!ndev)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, ndev);
+
+       priv = netdev_priv(ndev);
+       priv->dev = dev;
+       priv->netdev = ndev;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       priv->base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(priv->base)) {
+               ret = PTR_ERR(priv->base);
+               goto out_free_netdev;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       priv->ctrl_base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(priv->ctrl_base)) {
+               ret = PTR_ERR(priv->ctrl_base);
+               goto out_free_netdev;
+       }
+
+       priv->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(priv->clk)) {
+               netdev_err(ndev, "failed to get clk\n");
+               ret = -ENODEV;
+               goto out_free_netdev;
+       }
+
+       ret = clk_prepare_enable(priv->clk);
+       if (ret < 0) {
+               netdev_err(ndev, "failed to enable clk %d\n", ret);
+               goto out_free_netdev;
+       }
+
+       bus = mdiobus_alloc();
+       if (bus == NULL) {
+               ret = -ENOMEM;
+               goto out_free_netdev;
+       }
+
+       bus->priv = priv;
+       bus->name = "hix5hd2_mii_bus";
+       bus->read = hix5hd2_mdio_read;
+       bus->write = hix5hd2_mdio_write;
+       bus->parent = &pdev->dev;
+       snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
+       priv->bus = bus;
+
+       ret = of_mdiobus_register(bus, node);
+       if (ret)
+               goto err_free_mdio;
+
+       priv->phy_mode = of_get_phy_mode(node);
+       if (priv->phy_mode < 0) {
+               netdev_err(ndev, "not find phy-mode\n");
+               ret = -EINVAL;
+               goto err_mdiobus;
+       }
+
+       priv->phy_node = of_parse_phandle(node, "phy-handle", 0);
+       if (!priv->phy_node) {
+               netdev_err(ndev, "not find phy-handle\n");
+               ret = -EINVAL;
+               goto err_mdiobus;
+       }
+
+       ndev->irq = platform_get_irq(pdev, 0);
+       if (ndev->irq <= 0) {
+               netdev_err(ndev, "No irq resource\n");
+               ret = -EINVAL;
+               goto out_phy_node;
+       }
+
+       ret = devm_request_irq(dev, ndev->irq, hix5hd2_interrupt,
+                              0, pdev->name, ndev);
+       if (ret) {
+               netdev_err(ndev, "devm_request_irq failed\n");
+               goto out_phy_node;
+       }
+
+       mac_addr = of_get_mac_address(node);
+       if (mac_addr)
+               ether_addr_copy(ndev->dev_addr, mac_addr);
+       if (!is_valid_ether_addr(ndev->dev_addr)) {
+               eth_hw_addr_random(ndev);
+               netdev_warn(ndev, "using random MAC address %pM\n",
+                           ndev->dev_addr);
+       }
+
+       INIT_WORK(&priv->tx_timeout_task, hix5hd2_tx_timeout_task);
+       ndev->watchdog_timeo = 6 * HZ;
+       ndev->priv_flags |= IFF_UNICAST_FLT;
+       ndev->netdev_ops = &hix5hd2_netdev_ops;
+       ndev->ethtool_ops = &hix5hd2_ethtools_ops;
+       SET_NETDEV_DEV(ndev, dev);
+
+       ret = hix5hd2_init_hw_desc_queue(priv);
+       if (ret)
+               goto out_phy_node;
+
+       netif_napi_add(ndev, &priv->napi, hix5hd2_poll, NAPI_POLL_WEIGHT);
+       ret = register_netdev(priv->netdev);
+       if (ret) {
+               netdev_err(ndev, "register_netdev failed!");
+               goto out_destroy_queue;
+       }
+
+       clk_disable_unprepare(priv->clk);
+
+       return ret;
+
+out_destroy_queue:
+       netif_napi_del(&priv->napi);
+       hix5hd2_destroy_hw_desc_queue(priv);
+out_phy_node:
+       of_node_put(priv->phy_node);
+err_mdiobus:
+       mdiobus_unregister(bus);
+err_free_mdio:
+       mdiobus_free(bus);
+out_free_netdev:
+       free_netdev(ndev);
+
+       return ret;
+}
+
+static int hix5hd2_dev_remove(struct platform_device *pdev)
+{
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct hix5hd2_priv *priv = netdev_priv(ndev);
+
+       netif_napi_del(&priv->napi);
+       unregister_netdev(ndev);
+       mdiobus_unregister(priv->bus);
+       mdiobus_free(priv->bus);
+
+       hix5hd2_destroy_hw_desc_queue(priv);
+       of_node_put(priv->phy_node);
+       cancel_work_sync(&priv->tx_timeout_task);
+       free_netdev(ndev);
+
+       return 0;
+}
+
+static const struct of_device_id hix5hd2_of_match[] = {
+       {.compatible = "hisilicon,hix5hd2-gmac",},
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, hix5hd2_of_match);
+
+static struct platform_driver hix5hd2_dev_driver = {
+       .driver = {
+               .name = "hix5hd2-gmac",
+               .of_match_table = hix5hd2_of_match,
+       },
+       .probe = hix5hd2_dev_probe,
+       .remove = hix5hd2_dev_remove,
+};
+
+module_platform_driver(hix5hd2_dev_driver);
+
+MODULE_DESCRIPTION("HISILICON HIX5HD2 Ethernet driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:hix5hd2-gmac");
index 95837b99a464865a7ca47273cb49ce9b3200136b..85a3866459cf4a3bebc6bf6ee3fbe3ff254d48a4 100644 (file)
@@ -63,8 +63,8 @@ static int ehea_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
                cmd->duplex = port->full_duplex == 1 ?
                                                     DUPLEX_FULL : DUPLEX_HALF;
        } else {
-               speed = ~0;
-               cmd->duplex = -1;
+               speed = SPEED_UNKNOWN;
+               cmd->duplex = DUPLEX_UNKNOWN;
        }
        ethtool_cmd_speed_set(cmd, speed);
 
@@ -278,5 +278,5 @@ static const struct ethtool_ops ehea_ethtool_ops = {
 
 void ehea_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &ehea_ethtool_ops);
+       netdev->ethtool_ops = &ehea_ethtool_ops;
 }
index 538903bf13bce736161a96af324a2b9ebc9aecd9..a0b418e007a0d09303a32838d8acf1a06ca9d061 100644 (file)
@@ -28,6 +28,7 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include <linux/device.h>
 #include <linux/in.h>
 #include <linux/ip.h>
 #include <linux/tcp.h>
@@ -3273,7 +3274,7 @@ static int ehea_probe_adapter(struct platform_device *dev)
                return -EINVAL;
        }
 
-       adapter = kzalloc(sizeof(*adapter), GFP_KERNEL);
+       adapter = devm_kzalloc(&dev->dev, sizeof(*adapter), GFP_KERNEL);
        if (!adapter) {
                ret = -ENOMEM;
                dev_err(&dev->dev, "no mem for ehea_adapter\n");
@@ -3359,7 +3360,6 @@ static int ehea_probe_adapter(struct platform_device *dev)
 
 out_free_ad:
        list_del(&adapter->list);
-       kfree(adapter);
 
 out:
        ehea_update_firmware_handles();
@@ -3386,7 +3386,6 @@ static int ehea_remove(struct platform_device *dev)
        ehea_destroy_eq(adapter->neq);
        ehea_remove_adapter_mr(adapter);
        list_del(&adapter->list);
-       kfree(adapter);
 
        ehea_update_firmware_handles();
 
index 9b03033bb5576f52d7c6f687f9e1a99c8e7a21a5..a0820f72b25c88bf9141354231813864c8159fec 100644 (file)
@@ -103,12 +103,14 @@ static int hw_queue_ctor(struct hw_queue *queue, const u32 nr_of_pages,
 
 static void hw_queue_dtor(struct hw_queue *queue)
 {
-       int pages_per_kpage = PAGE_SIZE / queue->pagesize;
+       int pages_per_kpage;
        int i, nr_pages;
 
        if (!queue || !queue->queue_pages)
                return;
 
+       pages_per_kpage = PAGE_SIZE / queue->pagesize;
+
        nr_pages = queue->queue_length / queue->pagesize;
 
        for (i = 0; i < nr_pages; i += pages_per_kpage)
index ae342fdb42c8e79853507007ad2ea6c8d3681fad..87bd953cc2eeaef7f6af65902841645e98f7eda5 100644 (file)
@@ -2879,7 +2879,7 @@ static int emac_probe(struct platform_device *ofdev)
                dev->commac.ops = &emac_commac_sg_ops;
        } else
                ndev->netdev_ops = &emac_netdev_ops;
-       SET_ETHTOOL_OPS(ndev, &emac_ethtool_ops);
+       ndev->ethtool_ops = &emac_ethtool_ops;
 
        netif_carrier_off(ndev);
 
index 25045ae071711f03cee9ccabf9898cf2abebbc21..5727779a7df27477bee22c8bad9ac8253b083aea 100644 (file)
@@ -2245,7 +2245,7 @@ static int ipg_probe(struct pci_dev *pdev, const struct pci_device_id *id)
         */
        dev->netdev_ops = &ipg_netdev_ops;
        SET_NETDEV_DEV(dev, &pdev->dev);
-       SET_ETHTOOL_OPS(dev, &ipg_ethtool_ops);
+       dev->ethtool_ops = &ipg_ethtool_ops;
 
        rc = pci_request_regions(pdev, DRV_NAME);
        if (rc)
index b56461ce674c7832152cdab4c0b4c3b27789d9a4..9d979d7debef0fd1cb3ec80ca3dd8d908b6cd33b 100644 (file)
@@ -2854,7 +2854,7 @@ static int e100_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        netdev->hw_features |= NETIF_F_RXALL;
 
        netdev->netdev_ops = &e100_netdev_ops;
-       SET_ETHTOOL_OPS(netdev, &e100_ethtool_ops);
+       netdev->ethtool_ops = &e100_ethtool_ops;
        netdev->watchdog_timeo = E100_WATCHDOG_PERIOD;
        strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1);
 
index 73a8aeefb92a46d13a4c73be6cade2c5d694e00f..d50f78afb56d8715d8b90d936216d22bacaaa70e 100644 (file)
@@ -168,8 +168,8 @@ static int e1000_get_settings(struct net_device *netdev,
                else
                        ecmd->duplex = DUPLEX_HALF;
        } else {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
 
        ecmd->autoneg = ((hw->media_type == e1000_media_type_fiber) ||
@@ -1460,7 +1460,8 @@ static int e1000_run_loopback_test(struct e1000_adapter *adapter)
                         * enough time to complete the receives, if it's
                         * exceeded, break and error off
                         */
-               } while (good_cnt < 64 && jiffies < (time + 20));
+               } while (good_cnt < 64 && time_after(time + 20, jiffies));
+
                if (good_cnt != 64) {
                        ret_val = 13; /* ret_val is the same as mis-compare */
                        break;
@@ -1905,5 +1906,5 @@ static const struct ethtool_ops e1000_ethtool_ops = {
 
 void e1000_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &e1000_ethtool_ops);
+       netdev->ethtool_ops = &e1000_ethtool_ops;
 }
index c1d3fdb296a05e4f1cd86205bce12669e41ceb6b..e9b07ccc0ebaebc0b1b0993625485cae93b0b447 100644 (file)
@@ -4877,10 +4877,10 @@ void e1000_tbi_adjust_stats(struct e1000_hw *hw, struct e1000_hw_stats *stats,
         * since the test for a multicast frame will test positive on
         * a broadcast frame.
         */
-       if ((mac_addr[0] == (u8) 0xff) && (mac_addr[1] == (u8) 0xff))
+       if (is_broadcast_ether_addr(mac_addr))
                /* Broadcast packet */
                stats->bprc++;
-       else if (*mac_addr & 0x01)
+       else if (is_multicast_ether_addr(mac_addr))
                /* Multicast packet */
                stats->mprc++;
 
index 27058dfe418bee10f0c85ecf971fc419c346582f..660971f304b242f03e49d4cfb910fde35df4651a 100644 (file)
@@ -3105,11 +3105,6 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb,
         */
        tx_ring = adapter->tx_ring;
 
-       if (unlikely(skb->len <= 0)) {
-               dev_kfree_skb_any(skb);
-               return NETDEV_TX_OK;
-       }
-
        /* On PCI/PCI-X HW, if packet size is less than ETH_ZLEN,
         * packets may get corrupted during padding by HW.
         * To WA this issue, pad all small packets manually.
index a5f6b11d6992e63aa9af8da9f52540697455b2d1..08f22f348800ddd37aaf2d2d87eb4ce0e9c7f3b3 100644 (file)
@@ -1365,6 +1365,7 @@ static const struct e1000_mac_operations es2_mac_ops = {
        .setup_led              = e1000e_setup_led_generic,
        .config_collision_dist  = e1000e_config_collision_dist_generic,
        .rar_set                = e1000e_rar_set_generic,
+       .rar_get_count          = e1000e_rar_get_count_generic,
 };
 
 static const struct e1000_phy_operations es2_phy_ops = {
index e0aa7f1efb08ceb50d9049128dcbdb66f442b17d..218481e509f99f4e32deba736ae4117365c650e3 100644 (file)
@@ -1896,6 +1896,7 @@ static const struct e1000_mac_operations e82571_mac_ops = {
        .config_collision_dist  = e1000e_config_collision_dist_generic,
        .read_mac_addr          = e1000_read_mac_addr_82571,
        .rar_set                = e1000e_rar_set_generic,
+       .rar_get_count          = e1000e_rar_get_count_generic,
 };
 
 static const struct e1000_phy_operations e82_phy_ops_igp = {
index 1471c5464a89e72d87aa571d4a1b15d791a3f015..7785240a0da1a1b409cfbc3d4b18ff63b5f2966a 100644 (file)
@@ -265,10 +265,10 @@ struct e1000_adapter {
        u32 tx_hwtstamp_timeouts;
 
        /* Rx */
-       bool (*clean_rx) (struct e1000_ring *ring, int *work_done,
-                         int work_to_do) ____cacheline_aligned_in_smp;
-       void (*alloc_rx_buf) (struct e1000_ring *ring, int cleaned_count,
-                             gfp_t gfp);
+       bool (*clean_rx)(struct e1000_ring *ring, int *work_done,
+                        int work_to_do) ____cacheline_aligned_in_smp;
+       void (*alloc_rx_buf)(struct e1000_ring *ring, int cleaned_count,
+                            gfp_t gfp);
        struct e1000_ring *rx_ring;
 
        u32 rx_int_delay;
@@ -391,6 +391,8 @@ s32 e1000e_get_base_timinca(struct e1000_adapter *adapter, u32 *timinca);
  * 25MHz       46-bit  2^46 / 10^9 / 3600 = 19.55 hours
  */
 #define E1000_SYSTIM_OVERFLOW_PERIOD   (HZ * 60 * 60 * 4)
+#define E1000_MAX_82574_SYSTIM_REREADS 50
+#define E1000_82574_SYSTIM_EPSILON     (1ULL << 35ULL)
 
 /* hardware capability, feature, and workaround flags */
 #define FLAG_HAS_AMT                      (1 << 0)
@@ -573,35 +575,8 @@ static inline u32 __er32(struct e1000_hw *hw, unsigned long reg)
 
 #define er32(reg)      __er32(hw, E1000_##reg)
 
-/**
- * __ew32_prepare - prepare to write to MAC CSR register on certain parts
- * @hw: pointer to the HW structure
- *
- * When updating the MAC CSR registers, the Manageability Engine (ME) could
- * be accessing the registers at the same time.  Normally, this is handled in
- * h/w by an arbiter but on some parts there is a bug that acknowledges Host
- * accesses later than it should which could result in the register to have
- * an incorrect value.  Workaround this by checking the FWSM register which
- * has bit 24 set while ME is accessing MAC CSR registers, wait if it is set
- * and try again a number of times.
- **/
-static inline s32 __ew32_prepare(struct e1000_hw *hw)
-{
-       s32 i = E1000_ICH_FWSM_PCIM2PCI_COUNT;
-
-       while ((er32(FWSM) & E1000_ICH_FWSM_PCIM2PCI) && --i)
-               udelay(50);
-
-       return i;
-}
-
-static inline void __ew32(struct e1000_hw *hw, unsigned long reg, u32 val)
-{
-       if (hw->adapter->flags2 & FLAG2_PCIM2PCI_ARBITER_WA)
-               __ew32_prepare(hw);
-
-       writel(val, hw->hw_addr + reg);
-}
+s32 __ew32_prepare(struct e1000_hw *hw);
+void __ew32(struct e1000_hw *hw, unsigned long reg, u32 val);
 
 #define ew32(reg, val) __ew32(hw, E1000_##reg, (val))
 
index cad250bc1b99fc81d51fb8956eee74c9acc3bc7e..815e26c6d34b85a54c554de5863e200302d6eacd 100644 (file)
@@ -159,8 +159,8 @@ static int e1000_get_settings(struct net_device *netdev,
                ecmd->transceiver = XCVR_EXTERNAL;
        }
 
-       speed = -1;
-       ecmd->duplex = -1;
+       speed = SPEED_UNKNOWN;
+       ecmd->duplex = DUPLEX_UNKNOWN;
 
        if (netif_running(netdev)) {
                if (netif_carrier_ok(netdev)) {
@@ -169,6 +169,7 @@ static int e1000_get_settings(struct net_device *netdev,
                }
        } else if (!pm_runtime_suspended(netdev->dev.parent)) {
                u32 status = er32(STATUS);
+
                if (status & E1000_STATUS_LU) {
                        if (status & E1000_STATUS_SPEED_1000)
                                speed = SPEED_1000;
@@ -783,25 +784,26 @@ static bool reg_pattern_test(struct e1000_adapter *adapter, u64 *data,
                              reg + (offset << 2), val,
                              (test[pat] & write & mask));
                        *data = reg;
-                       return 1;
+                       return true;
                }
        }
-       return 0;
+       return false;
 }
 
 static bool reg_set_and_check(struct e1000_adapter *adapter, u64 *data,
                              int reg, u32 mask, u32 write)
 {
        u32 val;
+
        __ew32(&adapter->hw, reg, write & mask);
        val = __er32(&adapter->hw, reg);
        if ((write & mask) != (val & mask)) {
                e_err("set/check test failed (reg 0x%05X): got 0x%08X expected 0x%08X\n",
                      reg, (val & mask), (write & mask));
                *data = reg;
-               return 1;
+               return true;
        }
-       return 0;
+       return false;
 }
 
 #define REG_PATTERN_TEST_ARRAY(reg, offset, mask, write)                       \
@@ -1717,6 +1719,7 @@ static int e1000_link_test(struct e1000_adapter *adapter, u64 *data)
        *data = 0;
        if (hw->phy.media_type == e1000_media_type_internal_serdes) {
                int i = 0;
+
                hw->mac.serdes_has_link = false;
 
                /* On some blade server designs, link establishment
@@ -2315,5 +2318,5 @@ static const struct ethtool_ops e1000_ethtool_ops = {
 
 void e1000e_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &e1000_ethtool_ops);
+       netdev->ethtool_ops = &e1000_ethtool_ops;
 }
index 6b3de5f39a97862e2f5742b14a24530ba6ecc33f..72f5475c4b9093d075e608070c4415a4e0ad5afd 100644 (file)
@@ -469,8 +469,9 @@ struct e1000_mac_operations {
        s32  (*setup_led)(struct e1000_hw *);
        void (*write_vfta)(struct e1000_hw *, u32, u32);
        void (*config_collision_dist)(struct e1000_hw *);
-       void (*rar_set)(struct e1000_hw *, u8 *, u32);
+       int  (*rar_set)(struct e1000_hw *, u8 *, u32);
        s32  (*read_mac_addr)(struct e1000_hw *);
+       u32  (*rar_get_count)(struct e1000_hw *);
 };
 
 /* When to use various PHY register access functions:
index f0bbd4246d71d857eba4c439cac33e0c8a930864..8894ab8ed6bd82de2c0f720939d31a906096a12c 100644 (file)
@@ -139,8 +139,9 @@ static s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link);
 static s32 e1000_set_mdio_slow_mode_hv(struct e1000_hw *hw);
 static bool e1000_check_mng_mode_ich8lan(struct e1000_hw *hw);
 static bool e1000_check_mng_mode_pchlan(struct e1000_hw *hw);
-static void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index);
-static void e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index);
+static int e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index);
+static int e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index);
+static u32 e1000_rar_get_count_pch_lpt(struct e1000_hw *hw);
 static s32 e1000_k1_workaround_lv(struct e1000_hw *hw);
 static void e1000_gate_hw_phy_config_ich8lan(struct e1000_hw *hw, bool gate);
 static s32 e1000_disable_ulp_lpt_lp(struct e1000_hw *hw, bool force);
@@ -704,6 +705,7 @@ static s32 e1000_init_mac_params_ich8lan(struct e1000_hw *hw)
                mac->ops.rar_set = e1000_rar_set_pch_lpt;
                mac->ops.setup_physical_interface =
                    e1000_setup_copper_link_pch_lpt;
+               mac->ops.rar_get_count = e1000_rar_get_count_pch_lpt;
        }
 
        /* Enable PCS Lock-loss workaround for ICH8 */
@@ -1334,6 +1336,7 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
        if (((hw->mac.type == e1000_pch2lan) ||
             (hw->mac.type == e1000_pch_lpt)) && link) {
                u32 reg;
+
                reg = er32(STATUS);
                if (!(reg & (E1000_STATUS_FD | E1000_STATUS_SPEED_MASK))) {
                        u16 emi_addr;
@@ -1634,9 +1637,9 @@ static bool e1000_check_mng_mode_ich8lan(struct e1000_hw *hw)
        u32 fwsm;
 
        fwsm = er32(FWSM);
-       return ((fwsm & E1000_ICH_FWSM_FW_VALID) &&
+       return (fwsm & E1000_ICH_FWSM_FW_VALID) &&
                ((fwsm & E1000_FWSM_MODE_MASK) ==
-                (E1000_ICH_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT)));
+                (E1000_ICH_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT));
 }
 
 /**
@@ -1667,7 +1670,7 @@ static bool e1000_check_mng_mode_pchlan(struct e1000_hw *hw)
  *  contain the MAC address but RAR[1-6] are reserved for manageability (ME).
  *  Use SHRA[0-3] in place of those reserved for ME.
  **/
-static void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index)
+static int e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index)
 {
        u32 rar_low, rar_high;
 
@@ -1689,7 +1692,7 @@ static void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index)
                e1e_flush();
                ew32(RAH(index), rar_high);
                e1e_flush();
-               return;
+               return 0;
        }
 
        /* RAR[1-6] are owned by manageability.  Skip those and program the
@@ -1712,7 +1715,7 @@ static void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index)
                /* verify the register updates */
                if ((er32(SHRAL(index - 1)) == rar_low) &&
                    (er32(SHRAH(index - 1)) == rar_high))
-                       return;
+                       return 0;
 
                e_dbg("SHRA[%d] might be locked by ME - FWSM=0x%8.8x\n",
                      (index - 1), er32(FWSM));
@@ -1720,6 +1723,43 @@ static void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index)
 
 out:
        e_dbg("Failed to write receive address at index %d\n", index);
+       return -E1000_ERR_CONFIG;
+}
+
+/**
+ *  e1000_rar_get_count_pch_lpt - Get the number of available SHRA
+ *  @hw: pointer to the HW structure
+ *
+ *  Get the number of available receive registers that the Host can
+ *  program. SHRA[0-10] are the shared receive address registers
+ *  that are shared between the Host and manageability engine (ME).
+ *  ME can reserve any number of addresses and the host needs to be
+ *  able to tell how many available registers it has access to.
+ **/
+static u32 e1000_rar_get_count_pch_lpt(struct e1000_hw *hw)
+{
+       u32 wlock_mac;
+       u32 num_entries;
+
+       wlock_mac = er32(FWSM) & E1000_FWSM_WLOCK_MAC_MASK;
+       wlock_mac >>= E1000_FWSM_WLOCK_MAC_SHIFT;
+
+       switch (wlock_mac) {
+       case 0:
+               /* All SHRA[0..10] and RAR[0] available */
+               num_entries = hw->mac.rar_entry_count;
+               break;
+       case 1:
+               /* Only RAR[0] available */
+               num_entries = 1;
+               break;
+       default:
+               /* SHRA[0..(wlock_mac - 1)] available + RAR[0] */
+               num_entries = wlock_mac + 1;
+               break;
+       }
+
+       return num_entries;
 }
 
 /**
@@ -1733,7 +1773,7 @@ static void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index)
  *  contain the MAC address. SHRA[0-10] are the shared receive address
  *  registers that are shared between the Host and manageability engine (ME).
  **/
-static void e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index)
+static int e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index)
 {
        u32 rar_low, rar_high;
        u32 wlock_mac;
@@ -1755,7 +1795,7 @@ static void e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index)
                e1e_flush();
                ew32(RAH(index), rar_high);
                e1e_flush();
-               return;
+               return 0;
        }
 
        /* The manageability engine (ME) can lock certain SHRAR registers that
@@ -1787,12 +1827,13 @@ static void e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index)
                        /* verify the register updates */
                        if ((er32(SHRAL_PCH_LPT(index - 1)) == rar_low) &&
                            (er32(SHRAH_PCH_LPT(index - 1)) == rar_high))
-                               return;
+                               return 0;
                }
        }
 
 out:
        e_dbg("Failed to write receive address at index %d\n", index);
+       return -E1000_ERR_CONFIG;
 }
 
 /**
@@ -4976,6 +5017,7 @@ static const struct e1000_mac_operations ich8_mac_ops = {
        /* id_led_init dependent on mac type */
        .config_collision_dist  = e1000e_config_collision_dist_generic,
        .rar_set                = e1000e_rar_set_generic,
+       .rar_get_count          = e1000e_rar_get_count_generic,
 };
 
 static const struct e1000_phy_operations ich8_phy_ops = {
index baa0a466d1d05ca533999b9ffcac3846a476ab5f..8c386f3a15ebb6649e347f5316555827b151819b 100644 (file)
@@ -211,6 +211,11 @@ s32 e1000_check_alt_mac_addr_generic(struct e1000_hw *hw)
        return 0;
 }
 
+u32 e1000e_rar_get_count_generic(struct e1000_hw *hw)
+{
+       return hw->mac.rar_entry_count;
+}
+
 /**
  *  e1000e_rar_set_generic - Set receive address register
  *  @hw: pointer to the HW structure
@@ -220,7 +225,7 @@ s32 e1000_check_alt_mac_addr_generic(struct e1000_hw *hw)
  *  Sets the receive address array register at index to the address passed
  *  in by addr.
  **/
-void e1000e_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index)
+int e1000e_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index)
 {
        u32 rar_low, rar_high;
 
@@ -244,6 +249,8 @@ void e1000e_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index)
        e1e_flush();
        ew32(RAH(index), rar_high);
        e1e_flush();
+
+       return 0;
 }
 
 /**
index 4e81c2825b7a1ca60b9a870ce61737e29d4e7972..0513d90cdeeaa980b3fb226eaac6feebe481c4f3 100644 (file)
@@ -61,7 +61,8 @@ void e1000e_update_adaptive(struct e1000_hw *hw);
 void e1000_write_vfta_generic(struct e1000_hw *hw, u32 offset, u32 value);
 
 void e1000_set_lan_id_multi_port_pcie(struct e1000_hw *hw);
-void e1000e_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index);
+u32 e1000e_rar_get_count_generic(struct e1000_hw *hw);
+int e1000e_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index);
 void e1000e_config_collision_dist_generic(struct e1000_hw *hw);
 
 #endif
index 3e69386add04ab43bc083d92411734320fc9845f..201cc93f36256d5776e882399d3afdf594b81192 100644 (file)
@@ -123,6 +123,36 @@ static const struct e1000_reg_info e1000_reg_info_tbl[] = {
        {0, NULL}
 };
 
+/**
+ * __ew32_prepare - prepare to write to MAC CSR register on certain parts
+ * @hw: pointer to the HW structure
+ *
+ * When updating the MAC CSR registers, the Manageability Engine (ME) could
+ * be accessing the registers at the same time.  Normally, this is handled in
+ * h/w by an arbiter but on some parts there is a bug that acknowledges Host
+ * accesses later than it should which could result in the register to have
+ * an incorrect value.  Workaround this by checking the FWSM register which
+ * has bit 24 set while ME is accessing MAC CSR registers, wait if it is set
+ * and try again a number of times.
+ **/
+s32 __ew32_prepare(struct e1000_hw *hw)
+{
+       s32 i = E1000_ICH_FWSM_PCIM2PCI_COUNT;
+
+       while ((er32(FWSM) & E1000_ICH_FWSM_PCIM2PCI) && --i)
+               udelay(50);
+
+       return i;
+}
+
+void __ew32(struct e1000_hw *hw, unsigned long reg, u32 val)
+{
+       if (hw->adapter->flags2 & FLAG2_PCIM2PCI_ARBITER_WA)
+               __ew32_prepare(hw);
+
+       writel(val, hw->hw_addr + reg);
+}
+
 /**
  * e1000_regdump - register printout routine
  * @hw: pointer to the HW structure
@@ -599,6 +629,7 @@ static void e1000e_update_rdt_wa(struct e1000_ring *rx_ring, unsigned int i)
 
        if (unlikely(!ret_val && (i != readl(rx_ring->tail)))) {
                u32 rctl = er32(RCTL);
+
                ew32(RCTL, rctl & ~E1000_RCTL_EN);
                e_err("ME firmware caused invalid RDT - resetting\n");
                schedule_work(&adapter->reset_task);
@@ -615,6 +646,7 @@ static void e1000e_update_tdt_wa(struct e1000_ring *tx_ring, unsigned int i)
 
        if (unlikely(!ret_val && (i != readl(tx_ring->tail)))) {
                u32 tctl = er32(TCTL);
+
                ew32(TCTL, tctl & ~E1000_TCTL_EN);
                e_err("ME firmware caused invalid TDT - resetting\n");
                schedule_work(&adapter->reset_task);
@@ -1198,6 +1230,7 @@ static bool e1000_clean_tx_irq(struct e1000_ring *tx_ring)
        while ((eop_desc->upper.data & cpu_to_le32(E1000_TXD_STAT_DD)) &&
               (count < tx_ring->count)) {
                bool cleaned = false;
+
                rmb();          /* read buffer_info after eop_desc */
                for (; !cleaned; count++) {
                        tx_desc = E1000_TX_DESC(*tx_ring, i);
@@ -1753,6 +1786,7 @@ static irqreturn_t e1000_intr_msi(int __always_unused irq, void *data)
                    adapter->flags & FLAG_RX_NEEDS_RESTART) {
                        /* disable receives */
                        u32 rctl = er32(RCTL);
+
                        ew32(RCTL, rctl & ~E1000_RCTL_EN);
                        adapter->flags |= FLAG_RESTART_NOW;
                }
@@ -1960,6 +1994,7 @@ static void e1000_configure_msix(struct e1000_adapter *adapter)
        /* Workaround issue with spurious interrupts on 82574 in MSI-X mode */
        if (hw->mac.type == e1000_82574) {
                u32 rfctl = er32(RFCTL);
+
                rfctl |= E1000_RFCTL_ACK_DIS;
                ew32(RFCTL, rfctl);
        }
@@ -2204,6 +2239,7 @@ static void e1000_irq_disable(struct e1000_adapter *adapter)
 
        if (adapter->msix_entries) {
                int i;
+
                for (i = 0; i < adapter->num_vectors; i++)
                        synchronize_irq(adapter->msix_entries[i].vector);
        } else {
@@ -2921,6 +2957,7 @@ static void e1000_configure_tx(struct e1000_adapter *adapter)
 
        if (adapter->flags2 & FLAG2_DMA_BURST) {
                u32 txdctl = er32(TXDCTL(0));
+
                txdctl &= ~(E1000_TXDCTL_PTHRESH | E1000_TXDCTL_HTHRESH |
                            E1000_TXDCTL_WTHRESH);
                /* set up some performance related parameters to encourage the
@@ -3239,6 +3276,7 @@ static void e1000_configure_rx(struct e1000_adapter *adapter)
 
                if (adapter->flags & FLAG_IS_ICH) {
                        u32 rxdctl = er32(RXDCTL(0));
+
                        ew32(RXDCTL(0), rxdctl | 0x3);
                }
 
@@ -3303,9 +3341,11 @@ static int e1000e_write_uc_addr_list(struct net_device *netdev)
 {
        struct e1000_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
-       unsigned int rar_entries = hw->mac.rar_entry_count;
+       unsigned int rar_entries;
        int count = 0;
 
+       rar_entries = hw->mac.ops.rar_get_count(hw);
+
        /* save a rar entry for our hardware address */
        rar_entries--;
 
@@ -3324,9 +3364,13 @@ static int e1000e_write_uc_addr_list(struct net_device *netdev)
                 * combining
                 */
                netdev_for_each_uc_addr(ha, netdev) {
+                       int rval;
+
                        if (!rar_entries)
                                break;
-                       hw->mac.ops.rar_set(hw, ha->addr, rar_entries--);
+                       rval = hw->mac.ops.rar_set(hw, ha->addr, rar_entries--);
+                       if (rval < 0)
+                               return -ENOMEM;
                        count++;
                }
        }
@@ -4085,12 +4129,37 @@ static cycle_t e1000e_cyclecounter_read(const struct cyclecounter *cc)
        struct e1000_adapter *adapter = container_of(cc, struct e1000_adapter,
                                                     cc);
        struct e1000_hw *hw = &adapter->hw;
-       cycle_t systim;
+       cycle_t systim, systim_next;
 
        /* latch SYSTIMH on read of SYSTIML */
        systim = (cycle_t)er32(SYSTIML);
        systim |= (cycle_t)er32(SYSTIMH) << 32;
 
+       if ((hw->mac.type == e1000_82574) || (hw->mac.type == e1000_82583)) {
+               u64 incvalue, time_delta, rem, temp;
+               int i;
+
+               /* errata for 82574/82583 possible bad bits read from SYSTIMH/L
+                * check to see that the time is incrementing at a reasonable
+                * rate and is a multiple of incvalue
+                */
+               incvalue = er32(TIMINCA) & E1000_TIMINCA_INCVALUE_MASK;
+               for (i = 0; i < E1000_MAX_82574_SYSTIM_REREADS; i++) {
+                       /* latch SYSTIMH on read of SYSTIML */
+                       systim_next = (cycle_t)er32(SYSTIML);
+                       systim_next |= (cycle_t)er32(SYSTIMH) << 32;
+
+                       time_delta = systim_next - systim;
+                       temp = time_delta;
+                       rem = do_div(temp, incvalue);
+
+                       systim = systim_next;
+
+                       if ((time_delta < E1000_82574_SYSTIM_EPSILON) &&
+                           (rem == 0))
+                               break;
+               }
+       }
        return systim;
 }
 
@@ -4491,7 +4560,7 @@ static void e1000e_update_phy_task(struct work_struct *work)
        e1000_get_phy_info(hw);
 
        /* Enable EEE on 82579 after link up */
-       if (hw->phy.type == e1000_phy_82579)
+       if (hw->phy.type >= e1000_phy_82579)
                e1000_set_eee_pchlan(hw);
 }
 
@@ -4695,6 +4764,7 @@ static void e1000e_update_stats(struct e1000_adapter *adapter)
        /* Correctable ECC Errors */
        if (hw->mac.type == e1000_pch_lpt) {
                u32 pbeccsts = er32(PBECCSTS);
+
                adapter->corr_errors +=
                    pbeccsts & E1000_PBECCSTS_CORR_ERR_CNT_MASK;
                adapter->uncorr_errors +=
@@ -4808,6 +4878,7 @@ static void e1000e_enable_receives(struct e1000_adapter *adapter)
            (adapter->flags & FLAG_RESTART_NOW)) {
                struct e1000_hw *hw = &adapter->hw;
                u32 rctl = er32(RCTL);
+
                ew32(RCTL, rctl | E1000_RCTL_EN);
                adapter->flags &= ~FLAG_RESTART_NOW;
        }
@@ -4930,6 +5001,7 @@ static void e1000_watchdog_task(struct work_struct *work)
                        if ((adapter->flags & FLAG_TARC_SPEED_MODE_BIT) &&
                            !txb2b) {
                                u32 tarc0;
+
                                tarc0 = er32(TARC(0));
                                tarc0 &= ~SPEED_MODE_BIT;
                                ew32(TARC(0), tarc0);
@@ -5170,7 +5242,7 @@ static bool e1000_tx_csum(struct e1000_ring *tx_ring, struct sk_buff *skb)
        __be16 protocol;
 
        if (skb->ip_summed != CHECKSUM_PARTIAL)
-               return 0;
+               return false;
 
        if (skb->protocol == cpu_to_be16(ETH_P_8021Q))
                protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;
@@ -5215,7 +5287,7 @@ static bool e1000_tx_csum(struct e1000_ring *tx_ring, struct sk_buff *skb)
                i = 0;
        tx_ring->next_to_use = i;
 
-       return 1;
+       return true;
 }
 
 static int e1000_tx_map(struct e1000_ring *tx_ring, struct sk_buff *skb,
@@ -6209,6 +6281,7 @@ static int __e1000_resume(struct pci_dev *pdev)
                e1e_wphy(&adapter->hw, BM_WUS, ~0);
        } else {
                u32 wus = er32(WUS);
+
                if (wus) {
                        e_info("MAC Wakeup cause - %s\n",
                               wus & E1000_WUS_EX ? "Unicast Packet" :
@@ -7027,7 +7100,7 @@ static const struct pci_error_handlers e1000_err_handler = {
        .resume = e1000_io_resume,
 };
 
-static DEFINE_PCI_DEVICE_TABLE(e1000_pci_tbl) = {
+static const struct pci_device_id e1000_pci_tbl[] = {
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_COPPER), board_82571 },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_FIBER), board_82571 },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_COPPER), board_82571 },
@@ -7144,6 +7217,7 @@ static struct pci_driver e1000_driver = {
 static int __init e1000_init_module(void)
 {
        int ret;
+
        pr_info("Intel(R) PRO/1000 Network Driver - %s\n",
                e1000e_driver_version);
        pr_info("Copyright(c) 1999 - 2014 Intel Corporation.\n");
index a9a976f04bffe957e22b0852bd8918c6d6f63bd0..b1f212b7baf7e71f0b2a3a160463898019778a00 100644 (file)
@@ -398,6 +398,7 @@ s32 e1000e_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
                /* Loop to allow for up to whole page write of eeprom */
                while (widx < words) {
                        u16 word_out = data[widx];
+
                        word_out = (word_out >> 8) | (word_out << 8);
                        e1000_shift_out_eec_bits(hw, word_out, 16);
                        widx++;
index d0ac0f3249c886415d308c4a0cd376feda3d44db..aa1923f7ebdd2e56dd0ebd436feb69164426ad7c 100644 (file)
@@ -436,6 +436,7 @@ void e1000e_check_options(struct e1000_adapter *adapter)
 
                if (num_IntMode > bd) {
                        unsigned int int_mode = IntMode[bd];
+
                        e1000_validate_option(&int_mode, &opt, adapter);
                        adapter->int_mode = int_mode;
                } else {
@@ -457,6 +458,7 @@ void e1000e_check_options(struct e1000_adapter *adapter)
 
                if (num_SmartPowerDownEnable > bd) {
                        unsigned int spd = SmartPowerDownEnable[bd];
+
                        e1000_validate_option(&spd, &opt, adapter);
                        if ((adapter->flags & FLAG_HAS_SMART_POWER_DOWN) && spd)
                                adapter->flags |= FLAG_SMART_POWER_DOWN;
@@ -473,6 +475,7 @@ void e1000e_check_options(struct e1000_adapter *adapter)
 
                if (num_CrcStripping > bd) {
                        unsigned int crc_stripping = CrcStripping[bd];
+
                        e1000_validate_option(&crc_stripping, &opt, adapter);
                        if (crc_stripping == OPTION_ENABLED) {
                                adapter->flags2 |= FLAG2_CRC_STRIPPING;
@@ -495,6 +498,7 @@ void e1000e_check_options(struct e1000_adapter *adapter)
 
                if (num_KumeranLockLoss > bd) {
                        unsigned int kmrn_lock_loss = KumeranLockLoss[bd];
+
                        e1000_validate_option(&kmrn_lock_loss, &opt, adapter);
                        enabled = kmrn_lock_loss;
                }
index 00b3fc98bf309bf3d371a984f0da7245caf07013..b2005e13fb01583a10f58aa37339feb2336191bd 100644 (file)
@@ -2896,6 +2896,7 @@ static s32 __e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data,
                    (hw->phy.addr == 2) &&
                    !(MAX_PHY_REG_ADDRESS & reg) && (data & (1 << 11))) {
                        u16 data2 = 0x7EFF;
+
                        ret_val = e1000_access_phy_debug_regs_hv(hw,
                                                                 (1 << 6) | 0x3,
                                                                 &data2, false);
index beb7b4393a6c26fc46c917a798a6fd7bfaee28ea..65985846345deea3cc16d6f3cfd26add64dbfa7f 100644 (file)
@@ -72,6 +72,7 @@
 #define I40E_MIN_NUM_DESCRIPTORS      64
 #define I40E_MIN_MSIX                 2
 #define I40E_DEFAULT_NUM_VMDQ_VSI     8 /* max 256 VSIs */
+#define I40E_MIN_VSI_ALLOC            51 /* LAN, ATR, FCOE, 32 VF, 16 VMDQ */
 #define I40E_DEFAULT_QUEUES_PER_VMDQ  2 /* max 16 qps */
 #define I40E_DEFAULT_QUEUES_PER_VF    4
 #define I40E_DEFAULT_QUEUES_PER_TC    1 /* should be a power of 2 */
 #define STRINGIFY(foo)  #foo
 #define XSTRINGIFY(bar) STRINGIFY(bar)
 
-#ifndef ARCH_HAS_PREFETCH
-#define prefetch(X)
-#endif
-
 #define I40E_RX_DESC(R, i)                     \
        ((ring_is_16byte_desc_enabled(R))       \
                ? (union i40e_32byte_rx_desc *) \
@@ -157,11 +154,23 @@ struct i40e_lump_tracking {
 #define I40E_FDIR_BUFFER_FULL_MARGIN   10
 #define I40E_FDIR_BUFFER_HEAD_ROOM     200
 
+enum i40e_fd_stat_idx {
+       I40E_FD_STAT_ATR,
+       I40E_FD_STAT_SB,
+       I40E_FD_STAT_PF_COUNT
+};
+#define I40E_FD_STAT_PF_IDX(pf_id) ((pf_id) * I40E_FD_STAT_PF_COUNT)
+#define I40E_FD_ATR_STAT_IDX(pf_id) \
+                       (I40E_FD_STAT_PF_IDX(pf_id) + I40E_FD_STAT_ATR)
+#define I40E_FD_SB_STAT_IDX(pf_id)  \
+                       (I40E_FD_STAT_PF_IDX(pf_id) + I40E_FD_STAT_SB)
+
 struct i40e_fdir_filter {
        struct hlist_node fdir_node;
        /* filter ipnut set */
        u8 flow_type;
        u8 ip4_proto;
+       /* TX packet view of src and dst */
        __be32 dst_ip[4];
        __be32 src_ip[4];
        __be16 src_port;
@@ -205,7 +214,6 @@ struct i40e_pf {
        unsigned long state;
        unsigned long link_check_timeout;
        struct msix_entry *msix_entries;
-       u16 num_msix_entries;
        bool fc_autoneg_status;
 
        u16 eeprom_version;
@@ -220,11 +228,14 @@ struct i40e_pf {
        u16 rss_size;              /* num queues in the RSS array */
        u16 rss_size_max;          /* HW defined max RSS queues */
        u16 fdir_pf_filter_count;  /* num of guaranteed filters for this PF */
+       u16 num_alloc_vsi;         /* num VSIs this driver supports */
        u8 atr_sample_rate;
        bool wol_en;
 
        struct hlist_head fdir_filter_list;
        u16 fdir_pf_active_filters;
+       u16 fd_sb_cnt_idx;
+       u16 fd_atr_cnt_idx;
 
 #ifdef CONFIG_I40E_VXLAN
        __be16  vxlan_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS];
@@ -266,6 +277,7 @@ struct i40e_pf {
 #ifdef CONFIG_I40E_VXLAN
 #define I40E_FLAG_VXLAN_FILTER_SYNC            (u64)(1 << 27)
 #endif
+#define I40E_FLAG_DCB_CAPABLE                  (u64)(1 << 29)
 
        /* tracks features that get auto disabled by errors */
        u64 auto_disable_flags;
@@ -300,7 +312,6 @@ struct i40e_pf {
        u16 pf_seid;
        u16 main_vsi_seid;
        u16 mac_seid;
-       struct i40e_aqc_get_switch_config_data *sw_config;
        struct kobject *switch_kobj;
 #ifdef CONFIG_DEBUG_FS
        struct dentry *i40e_dbg_pf;
@@ -329,9 +340,7 @@ struct i40e_pf {
        struct ptp_clock *ptp_clock;
        struct ptp_clock_info ptp_caps;
        struct sk_buff *ptp_tx_skb;
-       struct work_struct ptp_tx_work;
        struct hwtstamp_config tstamp_config;
-       unsigned long ptp_tx_start;
        unsigned long last_rx_ptp_check;
        spinlock_t tmreg_lock; /* Used to protect the device time registers. */
        u64 ptp_base_adj;
@@ -420,6 +429,7 @@ struct i40e_vsi {
        struct i40e_q_vector **q_vectors;
        int num_q_vectors;
        int base_vector;
+       bool irqs_ready;
 
        u16 seid;            /* HW index of this VSI (absolute index) */
        u16 id;              /* VSI number */
@@ -540,6 +550,15 @@ static inline bool i40e_rx_is_programming_status(u64 qw)
                (qw >> I40E_RX_PROG_STATUS_DESC_LENGTH_SHIFT);
 }
 
+/**
+ * i40e_get_fd_cnt_all - get the total FD filter space available
+ * @pf: pointer to the pf struct
+ **/
+static inline int i40e_get_fd_cnt_all(struct i40e_pf *pf)
+{
+       return pf->hw.fdir_shared_filter_count + pf->fdir_pf_filter_count;
+}
+
 /* needed by i40e_ethtool.c */
 int i40e_up(struct i40e_vsi *vsi);
 void i40e_down(struct i40e_vsi *vsi);
index ed3902bf249b3e4ceb047ed14762d6ea50b1a4c0..7a027499fc57fc968d9ca33df5c65bb9e6825123 100644 (file)
 
 static void i40e_resume_aq(struct i40e_hw *hw);
 
+/**
+ * i40e_is_nvm_update_op - return true if this is an NVM update operation
+ * @desc: API request descriptor
+ **/
+static inline bool i40e_is_nvm_update_op(struct i40e_aq_desc *desc)
+{
+       return (desc->opcode == i40e_aqc_opc_nvm_erase) ||
+              (desc->opcode == i40e_aqc_opc_nvm_update);
+}
+
 /**
  *  i40e_adminq_init_regs - Initialize AdminQ registers
  *  @hw: pointer to the hardware structure
@@ -281,8 +291,11 @@ static void i40e_free_asq_bufs(struct i40e_hw *hw)
  *
  *  Configure base address and length registers for the transmit queue
  **/
-static void i40e_config_asq_regs(struct i40e_hw *hw)
+static i40e_status i40e_config_asq_regs(struct i40e_hw *hw)
 {
+       i40e_status ret_code = 0;
+       u32 reg = 0;
+
        if (hw->mac.type == I40E_MAC_VF) {
                /* configure the transmit queue */
                wr32(hw, I40E_VF_ATQBAH1,
@@ -291,6 +304,7 @@ static void i40e_config_asq_regs(struct i40e_hw *hw)
                    lower_32_bits(hw->aq.asq.desc_buf.pa));
                wr32(hw, I40E_VF_ATQLEN1, (hw->aq.num_asq_entries |
                                          I40E_VF_ATQLEN1_ATQENABLE_MASK));
+               reg = rd32(hw, I40E_VF_ATQBAL1);
        } else {
                /* configure the transmit queue */
                wr32(hw, I40E_PF_ATQBAH,
@@ -299,7 +313,14 @@ static void i40e_config_asq_regs(struct i40e_hw *hw)
                    lower_32_bits(hw->aq.asq.desc_buf.pa));
                wr32(hw, I40E_PF_ATQLEN, (hw->aq.num_asq_entries |
                                          I40E_PF_ATQLEN_ATQENABLE_MASK));
+               reg = rd32(hw, I40E_PF_ATQBAL);
        }
+
+       /* Check one register to verify that config was applied */
+       if (reg != lower_32_bits(hw->aq.asq.desc_buf.pa))
+               ret_code = I40E_ERR_ADMIN_QUEUE_ERROR;
+
+       return ret_code;
 }
 
 /**
@@ -308,8 +329,11 @@ static void i40e_config_asq_regs(struct i40e_hw *hw)
  *
  * Configure base address and length registers for the receive (event queue)
  **/
-static void i40e_config_arq_regs(struct i40e_hw *hw)
+static i40e_status i40e_config_arq_regs(struct i40e_hw *hw)
 {
+       i40e_status ret_code = 0;
+       u32 reg = 0;
+
        if (hw->mac.type == I40E_MAC_VF) {
                /* configure the receive queue */
                wr32(hw, I40E_VF_ARQBAH1,
@@ -318,6 +342,7 @@ static void i40e_config_arq_regs(struct i40e_hw *hw)
                    lower_32_bits(hw->aq.arq.desc_buf.pa));
                wr32(hw, I40E_VF_ARQLEN1, (hw->aq.num_arq_entries |
                                          I40E_VF_ARQLEN1_ARQENABLE_MASK));
+               reg = rd32(hw, I40E_VF_ARQBAL1);
        } else {
                /* configure the receive queue */
                wr32(hw, I40E_PF_ARQBAH,
@@ -326,10 +351,17 @@ static void i40e_config_arq_regs(struct i40e_hw *hw)
                    lower_32_bits(hw->aq.arq.desc_buf.pa));
                wr32(hw, I40E_PF_ARQLEN, (hw->aq.num_arq_entries |
                                          I40E_PF_ARQLEN_ARQENABLE_MASK));
+               reg = rd32(hw, I40E_PF_ARQBAL);
        }
 
        /* Update tail in the HW to post pre-allocated buffers */
        wr32(hw, hw->aq.arq.tail, hw->aq.num_arq_entries - 1);
+
+       /* Check one register to verify that config was applied */
+       if (reg != lower_32_bits(hw->aq.arq.desc_buf.pa))
+               ret_code = I40E_ERR_ADMIN_QUEUE_ERROR;
+
+       return ret_code;
 }
 
 /**
@@ -377,7 +409,9 @@ static i40e_status i40e_init_asq(struct i40e_hw *hw)
                goto init_adminq_free_rings;
 
        /* initialize base registers */
-       i40e_config_asq_regs(hw);
+       ret_code = i40e_config_asq_regs(hw);
+       if (ret_code)
+               goto init_adminq_free_rings;
 
        /* success! */
        goto init_adminq_exit;
@@ -434,7 +468,9 @@ static i40e_status i40e_init_arq(struct i40e_hw *hw)
                goto init_adminq_free_rings;
 
        /* initialize base registers */
-       i40e_config_arq_regs(hw);
+       ret_code = i40e_config_arq_regs(hw);
+       if (ret_code)
+               goto init_adminq_free_rings;
 
        /* success! */
        goto init_adminq_exit;
@@ -577,14 +613,14 @@ i40e_status i40e_init_adminq(struct i40e_hw *hw)
        i40e_read_nvm_word(hw, I40E_SR_NVM_EETRACK_HI, &eetrack_hi);
        hw->nvm.eetrack = (eetrack_hi << 16) | eetrack_lo;
 
-       if (hw->aq.api_maj_ver != I40E_FW_API_VERSION_MAJOR ||
-           hw->aq.api_min_ver > I40E_FW_API_VERSION_MINOR) {
+       if (hw->aq.api_maj_ver > I40E_FW_API_VERSION_MAJOR) {
                ret_code = I40E_ERR_FIRMWARE_API_VERSION;
                goto init_adminq_free_arq;
        }
 
        /* pre-emptive resource lock release */
        i40e_aq_release_resource(hw, I40E_NVM_RESOURCE_ID, 0, NULL);
+       hw->aq.nvm_busy = false;
 
        ret_code = i40e_aq_set_hmc_resource_profile(hw,
                                                    I40E_HMC_PROFILE_DEFAULT,
@@ -708,6 +744,12 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw,
                goto asq_send_command_exit;
        }
 
+       if (i40e_is_nvm_update_op(desc) && hw->aq.nvm_busy) {
+               i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, "AQTX: NVM busy.\n");
+               status = I40E_ERR_NVM;
+               goto asq_send_command_exit;
+       }
+
        details = I40E_ADMINQ_DETAILS(hw->aq.asq, hw->aq.asq.next_to_use);
        if (cmd_details) {
                *details = *cmd_details;
@@ -835,6 +877,9 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw,
                hw->aq.asq_last_status = (enum i40e_admin_queue_err)retval;
        }
 
+       if (i40e_is_nvm_update_op(desc))
+               hw->aq.nvm_busy = true;
+
        /* update the error if time out occurred */
        if ((!cmd_completed) &&
            (!details->async && !details->postpone)) {
@@ -929,6 +974,9 @@ i40e_status i40e_clean_arq_element(struct i40e_hw *hw,
                               e->msg_size);
        }
 
+       if (i40e_is_nvm_update_op(&e->desc))
+               hw->aq.nvm_busy = false;
+
        /* Restore the original datalen and buffer address in the desc,
         * FW updates datalen to indicate the event message
         * size
index 993f7685a9111694726c1a9bee44f3653651df72..b1552fbc48a0e6876acf838ab7eadf1e72fe492e 100644 (file)
@@ -90,6 +90,7 @@ struct i40e_adminq_info {
        u16 fw_min_ver;                 /* firmware minor version */
        u16 api_maj_ver;                /* api major version */
        u16 api_min_ver;                /* api minor version */
+       bool nvm_busy;
 
        struct mutex asq_mutex; /* Send queue lock */
        struct mutex arq_mutex; /* Receive queue lock */
index 7b6374a8f8da5cbb5054c431dce910d75de205c5..15f289f2917f70bb313501164f9c5fd8ef4cae05 100644 (file)
@@ -34,7 +34,7 @@
  */
 
 #define I40E_FW_API_VERSION_MAJOR  0x0001
-#define I40E_FW_API_VERSION_MINOR  0x0001
+#define I40E_FW_API_VERSION_MINOR  0x0002
 
 struct i40e_aq_desc {
        __le16 flags;
@@ -123,6 +123,7 @@ enum i40e_admin_queue_opc {
        i40e_aqc_opc_get_version      = 0x0001,
        i40e_aqc_opc_driver_version   = 0x0002,
        i40e_aqc_opc_queue_shutdown   = 0x0003,
+       i40e_aqc_opc_set_pf_context   = 0x0004,
 
        /* resource ownership */
        i40e_aqc_opc_request_resource = 0x0008,
@@ -182,9 +183,6 @@ enum i40e_admin_queue_opc {
        i40e_aqc_opc_add_mirror_rule    = 0x0260,
        i40e_aqc_opc_delete_mirror_rule = 0x0261,
 
-       i40e_aqc_opc_set_storm_control_config = 0x0280,
-       i40e_aqc_opc_get_storm_control_config = 0x0281,
-
        /* DCB commands */
        i40e_aqc_opc_dcb_ignore_pfc = 0x0301,
        i40e_aqc_opc_dcb_updated    = 0x0302,
@@ -207,6 +205,7 @@ enum i40e_admin_queue_opc {
        i40e_aqc_opc_query_switching_comp_bw_config        = 0x041A,
        i40e_aqc_opc_suspend_port_tx                       = 0x041B,
        i40e_aqc_opc_resume_port_tx                        = 0x041C,
+       i40e_aqc_opc_configure_partition_bw                = 0x041D,
 
        /* hmc */
        i40e_aqc_opc_query_hmc_resource_profile = 0x0500,
@@ -224,13 +223,15 @@ enum i40e_admin_queue_opc {
        i40e_aqc_opc_get_partner_advt    = 0x0616,
        i40e_aqc_opc_set_lb_modes        = 0x0618,
        i40e_aqc_opc_get_phy_wol_caps    = 0x0621,
-       i40e_aqc_opc_set_phy_reset       = 0x0622,
+       i40e_aqc_opc_set_phy_debug       = 0x0622,
        i40e_aqc_opc_upload_ext_phy_fm   = 0x0625,
 
        /* NVM commands */
-       i40e_aqc_opc_nvm_read   = 0x0701,
-       i40e_aqc_opc_nvm_erase  = 0x0702,
-       i40e_aqc_opc_nvm_update = 0x0703,
+       i40e_aqc_opc_nvm_read         = 0x0701,
+       i40e_aqc_opc_nvm_erase        = 0x0702,
+       i40e_aqc_opc_nvm_update       = 0x0703,
+       i40e_aqc_opc_nvm_config_read  = 0x0704,
+       i40e_aqc_opc_nvm_config_write = 0x0705,
 
        /* virtualization commands */
        i40e_aqc_opc_send_msg_to_pf   = 0x0801,
@@ -272,8 +273,6 @@ enum i40e_admin_queue_opc {
        i40e_aqc_opc_debug_set_mode         = 0xFF01,
        i40e_aqc_opc_debug_read_reg         = 0xFF03,
        i40e_aqc_opc_debug_write_reg        = 0xFF04,
-       i40e_aqc_opc_debug_read_reg_sg      = 0xFF05,
-       i40e_aqc_opc_debug_write_reg_sg     = 0xFF06,
        i40e_aqc_opc_debug_modify_reg       = 0xFF07,
        i40e_aqc_opc_debug_dump_internals   = 0xFF08,
        i40e_aqc_opc_debug_modify_internals = 0xFF09,
@@ -341,6 +340,14 @@ struct i40e_aqc_queue_shutdown {
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_queue_shutdown);
 
+/* Set PF context (0x0004, direct) */
+struct i40e_aqc_set_pf_context {
+       u8      pf_id;
+       u8      reserved[15];
+};
+
+I40E_CHECK_CMD_LENGTH(i40e_aqc_set_pf_context);
+
 /* Request resource ownership (direct 0x0008)
  * Release resource ownership (direct 0x0009)
  */
@@ -1289,27 +1296,6 @@ struct i40e_aqc_add_delete_mirror_rule_completion {
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_add_delete_mirror_rule_completion);
 
-/* Set Storm Control Configuration (direct 0x0280)
- * Get Storm Control Configuration (direct 0x0281)
- *    the command and response use the same descriptor structure
- */
-struct i40e_aqc_set_get_storm_control_config {
-       __le32 broadcast_threshold;
-       __le32 multicast_threshold;
-       __le32 control_flags;
-#define I40E_AQC_STORM_CONTROL_MDIPW            0x01
-#define I40E_AQC_STORM_CONTROL_MDICW            0x02
-#define I40E_AQC_STORM_CONTROL_BDIPW            0x04
-#define I40E_AQC_STORM_CONTROL_BDICW            0x08
-#define I40E_AQC_STORM_CONTROL_BIDU             0x10
-#define I40E_AQC_STORM_CONTROL_INTERVAL_SHIFT   8
-#define I40E_AQC_STORM_CONTROL_INTERVAL_MASK    (0x3FF << \
-                                       I40E_AQC_STORM_CONTROL_INTERVAL_SHIFT)
-       u8     reserved[4];
-};
-
-I40E_CHECK_CMD_LENGTH(i40e_aqc_set_get_storm_control_config);
-
 /* DCB 0x03xx*/
 
 /* PFC Ignore (direct 0x0301)
@@ -1427,11 +1413,12 @@ I40E_CHECK_CMD_LENGTH(i40e_aqc_configure_switching_comp_bw_limit);
 struct i40e_aqc_configure_switching_comp_ets_data {
        u8     reserved[4];
        u8     tc_valid_bits;
-       u8     reserved1;
+       u8     seepage;
+#define I40E_AQ_ETS_SEEPAGE_EN_MASK     0x1
        u8     tc_strict_priority_flags;
-       u8     reserved2[17];
+       u8     reserved1[17];
        u8     tc_bw_share_credits[8];
-       u8     reserved3[96];
+       u8     reserved2[96];
 };
 
 /* Configure Switching Component Bandwidth Limits per Tc (indirect 0x0416) */
@@ -1499,6 +1486,15 @@ struct i40e_aqc_query_switching_comp_bw_config_resp {
  * (direct 0x041B and 0x041C) uses the generic SEID struct
  */
 
+/* Configure partition BW
+ * (indirect 0x041D)
+ */
+struct i40e_aqc_configure_partition_bw_data {
+       __le16 pf_valid_bits;
+       u8     min_bw[16];      /* guaranteed bandwidth */
+       u8     max_bw[16];      /* bandwidth limit */
+};
+
 /* Get and set the active HMC resource profile and status.
  * (direct 0x0500) and (direct 0x0501)
  */
@@ -1539,6 +1535,8 @@ enum i40e_aq_phy_type {
        I40E_PHY_TYPE_XLPPI                     = 0x9,
        I40E_PHY_TYPE_40GBASE_CR4_CU            = 0xA,
        I40E_PHY_TYPE_10GBASE_CR1_CU            = 0xB,
+       I40E_PHY_TYPE_10GBASE_AOC               = 0xC,
+       I40E_PHY_TYPE_40GBASE_AOC               = 0xD,
        I40E_PHY_TYPE_100BASE_TX                = 0x11,
        I40E_PHY_TYPE_1000BASE_T                = 0x12,
        I40E_PHY_TYPE_10GBASE_T                 = 0x13,
@@ -1549,7 +1547,10 @@ enum i40e_aq_phy_type {
        I40E_PHY_TYPE_40GBASE_CR4               = 0x18,
        I40E_PHY_TYPE_40GBASE_SR4               = 0x19,
        I40E_PHY_TYPE_40GBASE_LR4               = 0x1A,
-       I40E_PHY_TYPE_20GBASE_KR2               = 0x1B,
+       I40E_PHY_TYPE_1000BASE_SX               = 0x1B,
+       I40E_PHY_TYPE_1000BASE_LX               = 0x1C,
+       I40E_PHY_TYPE_1000BASE_T_OPTICAL        = 0x1D,
+       I40E_PHY_TYPE_20GBASE_KR2               = 0x1E,
        I40E_PHY_TYPE_MAX
 };
 
@@ -1583,11 +1584,8 @@ struct i40e_aq_get_phy_abilities_resp {
 #define I40E_AQ_PHY_FLAG_PAUSE_TX         0x01
 #define I40E_AQ_PHY_FLAG_PAUSE_RX         0x02
 #define I40E_AQ_PHY_FLAG_LOW_POWER        0x04
-#define I40E_AQ_PHY_FLAG_AN_SHIFT         3
-#define I40E_AQ_PHY_FLAG_AN_MASK          (0x3 << I40E_AQ_PHY_FLAG_AN_SHIFT)
-#define I40E_AQ_PHY_FLAG_AN_OFF           0x00 /* link forced on */
-#define I40E_AQ_PHY_FLAG_AN_OFF_LINK_DOWN 0x01
-#define I40E_AQ_PHY_FLAG_AN_ON            0x02
+#define I40E_AQ_PHY_LINK_ENABLED                 0x08
+#define I40E_AQ_PHY_AN_ENABLED                   0x10
 #define I40E_AQ_PHY_FLAG_MODULE_QUAL      0x20
        __le16 eee_capability;
 #define I40E_AQ_EEE_100BASE_TX       0x0002
@@ -1696,6 +1694,7 @@ struct i40e_aqc_get_link_status {
 #define I40E_AQ_LINK_TX_ACTIVE       0x00
 #define I40E_AQ_LINK_TX_DRAINED      0x01
 #define I40E_AQ_LINK_TX_FLUSHED      0x03
+#define I40E_AQ_LINK_FORCED_40G      0x10
        u8     loopback;         /* use defines from i40e_aqc_set_lb_mode */
        __le16 max_frame_size;
        u8     config;
@@ -1747,14 +1746,21 @@ struct i40e_aqc_set_lb_mode {
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_set_lb_mode);
 
-/* Set PHY Reset command (0x0622) */
-struct i40e_aqc_set_phy_reset {
-       u8     reset_flags;
-#define I40E_AQ_PHY_RESET_REQUEST  0x02
+/* Set PHY Debug command (0x0622) */
+struct i40e_aqc_set_phy_debug {
+       u8     command_flags;
+#define I40E_AQ_PHY_DEBUG_RESET_INTERNAL       0x02
+#define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_SHIFT 2
+#define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_MASK  (0x03 << \
+                                       I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_SHIFT)
+#define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_NONE  0x00
+#define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_HARD  0x01
+#define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_SOFT  0x02
+#define I40E_AQ_PHY_DEBUG_DISABLE_LINK_FW      0x10
        u8     reserved[15];
 };
 
-I40E_CHECK_CMD_LENGTH(i40e_aqc_set_phy_reset);
+I40E_CHECK_CMD_LENGTH(i40e_aqc_set_phy_debug);
 
 enum i40e_aq_phy_reg_type {
        I40E_AQC_PHY_REG_INTERNAL         = 0x1,
@@ -1779,6 +1785,47 @@ struct i40e_aqc_nvm_update {
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_nvm_update);
 
+/* NVM Config Read (indirect 0x0704) */
+struct i40e_aqc_nvm_config_read {
+       __le16 cmd_flags;
+#define ANVM_SINGLE_OR_MULTIPLE_FEATURES_MASK  1
+#define ANVM_READ_SINGLE_FEATURE               0
+#define ANVM_READ_MULTIPLE_FEATURES            1
+       __le16 element_count;
+       __le16 element_id;              /* Feature/field ID */
+       u8     reserved[2];
+       __le32 address_high;
+       __le32 address_low;
+};
+
+I40E_CHECK_CMD_LENGTH(i40e_aqc_nvm_config_read);
+
+/* NVM Config Write (indirect 0x0705) */
+struct i40e_aqc_nvm_config_write {
+       __le16 cmd_flags;
+       __le16 element_count;
+       u8     reserved[4];
+       __le32 address_high;
+       __le32 address_low;
+};
+
+I40E_CHECK_CMD_LENGTH(i40e_aqc_nvm_config_write);
+
+struct i40e_aqc_nvm_config_data_feature {
+       __le16 feature_id;
+       __le16 instance_id;
+       __le16 feature_options;
+       __le16 feature_selection;
+};
+
+struct i40e_aqc_nvm_config_data_immediate_field {
+#define ANVM_FEATURE_OR_IMMEDIATE_MASK 0x2
+       __le16 field_id;
+       __le16 instance_id;
+       __le16 field_options;
+       __le16 field_value;
+};
+
 /* Send to PF command (indirect 0x0801) id is only used by PF
  * Send to VF command (indirect 0x0802) id is only used by PF
  * Send to Peer PF command (indirect 0x0803)
index 922cdcc45c54b1d36de2a7c0b7faf0ed8cdf1e0d..6e65f19dd6e58aadf1fc7df3df41ce8a617be959 100644 (file)
@@ -43,12 +43,10 @@ static i40e_status i40e_set_mac_type(struct i40e_hw *hw)
        if (hw->vendor_id == PCI_VENDOR_ID_INTEL) {
                switch (hw->device_id) {
                case I40E_DEV_ID_SFP_XL710:
-               case I40E_DEV_ID_SFP_X710:
                case I40E_DEV_ID_QEMU:
                case I40E_DEV_ID_KX_A:
                case I40E_DEV_ID_KX_B:
                case I40E_DEV_ID_KX_C:
-               case I40E_DEV_ID_KX_D:
                case I40E_DEV_ID_QSFP_A:
                case I40E_DEV_ID_QSFP_B:
                case I40E_DEV_ID_QSFP_C:
@@ -133,7 +131,11 @@ void i40e_debug_aq(struct i40e_hw *hw, enum i40e_debug_mask mask, void *desc,
  **/
 bool i40e_check_asq_alive(struct i40e_hw *hw)
 {
-       return !!(rd32(hw, hw->aq.asq.len) & I40E_PF_ATQLEN_ATQENABLE_MASK);
+       if (hw->aq.asq.len)
+               return !!(rd32(hw, hw->aq.asq.len) &
+                         I40E_PF_ATQLEN_ATQENABLE_MASK);
+       else
+               return false;
 }
 
 /**
@@ -652,6 +654,36 @@ i40e_status i40e_get_mac_addr(struct i40e_hw *hw, u8 *mac_addr)
        return status;
 }
 
+/**
+ * i40e_pre_tx_queue_cfg - pre tx queue configure
+ * @hw: pointer to the HW structure
+ * @queue: target pf queue index
+ * @enable: state change request
+ *
+ * Handles hw requirement to indicate intention to enable
+ * or disable target queue.
+ **/
+void i40e_pre_tx_queue_cfg(struct i40e_hw *hw, u32 queue, bool enable)
+{
+       u32 abs_queue_idx = hw->func_caps.base_queue + queue;
+       u32 reg_block = 0;
+       u32 reg_val;
+
+       if (abs_queue_idx >= 128)
+               reg_block = abs_queue_idx / 128;
+
+       reg_val = rd32(hw, I40E_GLLAN_TXPRE_QDIS(reg_block));
+       reg_val &= ~I40E_GLLAN_TXPRE_QDIS_QINDX_MASK;
+       reg_val |= (abs_queue_idx << I40E_GLLAN_TXPRE_QDIS_QINDX_SHIFT);
+
+       if (enable)
+               reg_val |= I40E_GLLAN_TXPRE_QDIS_CLEAR_QDIS_MASK;
+       else
+               reg_val |= I40E_GLLAN_TXPRE_QDIS_SET_QDIS_MASK;
+
+       wr32(hw, I40E_GLLAN_TXPRE_QDIS(reg_block), reg_val);
+}
+
 /**
  * i40e_get_media_type - Gets media type
  * @hw: pointer to the hardware structure
@@ -699,7 +731,7 @@ static enum i40e_media_type i40e_get_media_type(struct i40e_hw *hw)
 }
 
 #define I40E_PF_RESET_WAIT_COUNT_A0    200
-#define I40E_PF_RESET_WAIT_COUNT       10
+#define I40E_PF_RESET_WAIT_COUNT       100
 /**
  * i40e_pf_reset - Reset the PF
  * @hw: pointer to the hardware structure
@@ -789,6 +821,9 @@ void i40e_clear_pxe_mode(struct i40e_hw *hw)
 {
        u32 reg;
 
+       if (i40e_check_asq_alive(hw))
+               i40e_aq_clear_pxe_mode(hw, NULL);
+
        /* Clear single descriptor fetch/write-back mode */
        reg = rd32(hw, I40E_GLLAN_RCTL_0);
 
@@ -906,6 +941,33 @@ void i40e_led_set(struct i40e_hw *hw, u32 mode, bool blink)
 
 /* Admin command wrappers */
 
+/**
+ * i40e_aq_clear_pxe_mode
+ * @hw: pointer to the hw struct
+ * @cmd_details: pointer to command details structure or NULL
+ *
+ * Tell the firmware that the driver is taking over from PXE
+ **/
+i40e_status i40e_aq_clear_pxe_mode(struct i40e_hw *hw,
+                               struct i40e_asq_cmd_details *cmd_details)
+{
+       i40e_status status;
+       struct i40e_aq_desc desc;
+       struct i40e_aqc_clear_pxe *cmd =
+               (struct i40e_aqc_clear_pxe *)&desc.params.raw;
+
+       i40e_fill_default_direct_cmd_desc(&desc,
+                                         i40e_aqc_opc_clear_pxe_mode);
+
+       cmd->rx_cnt = 0x2;
+
+       status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details);
+
+       wr32(hw, I40E_GLLAN_RCTL_0, 0x1);
+
+       return status;
+}
+
 /**
  * i40e_aq_set_link_restart_an
  * @hw: pointer to the hw struct
@@ -975,6 +1037,13 @@ i40e_status i40e_aq_get_link_info(struct i40e_hw *hw,
        hw_link_info->an_info = resp->an_info;
        hw_link_info->ext_info = resp->ext_info;
        hw_link_info->loopback = resp->loopback;
+       hw_link_info->max_frame_size = le16_to_cpu(resp->max_frame_size);
+       hw_link_info->pacing = resp->config & I40E_AQ_CONFIG_PACING_MASK;
+
+       if (resp->config & I40E_AQ_CONFIG_CRC_ENA)
+               hw_link_info->crc_enable = true;
+       else
+               hw_link_info->crc_enable = false;
 
        if (resp->command_flags & cpu_to_le16(I40E_AQ_LSE_ENABLE))
                hw_link_info->lse_enable = true;
@@ -1021,8 +1090,6 @@ i40e_status i40e_aq_add_vsi(struct i40e_hw *hw,
        cmd->vsi_flags = cpu_to_le16(vsi_ctx->flags);
 
        desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
-       if (sizeof(vsi_ctx->info) > I40E_AQ_LARGE_BUF)
-               desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB);
 
        status = i40e_asq_send_command(hw, &desc, &vsi_ctx->info,
                                    sizeof(vsi_ctx->info), cmd_details);
@@ -1163,8 +1230,6 @@ i40e_status i40e_aq_get_vsi_params(struct i40e_hw *hw,
        cmd->uplink_seid = cpu_to_le16(vsi_ctx->seid);
 
        desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_BUF);
-       if (sizeof(vsi_ctx->info) > I40E_AQ_LARGE_BUF)
-               desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB);
 
        status = i40e_asq_send_command(hw, &desc, &vsi_ctx->info,
                                    sizeof(vsi_ctx->info), NULL);
@@ -1203,8 +1268,6 @@ i40e_status i40e_aq_update_vsi_params(struct i40e_hw *hw,
        cmd->uplink_seid = cpu_to_le16(vsi_ctx->seid);
 
        desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
-       if (sizeof(vsi_ctx->info) > I40E_AQ_LARGE_BUF)
-               desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB);
 
        status = i40e_asq_send_command(hw, &desc, &vsi_ctx->info,
                                    sizeof(vsi_ctx->info), cmd_details);
@@ -1300,6 +1363,7 @@ i40e_status i40e_aq_send_driver_version(struct i40e_hw *hw,
        struct i40e_aqc_driver_version *cmd =
                (struct i40e_aqc_driver_version *)&desc.params.raw;
        i40e_status status;
+       u16 len;
 
        if (dv == NULL)
                return I40E_ERR_PARAM;
@@ -1311,7 +1375,14 @@ i40e_status i40e_aq_send_driver_version(struct i40e_hw *hw,
        cmd->driver_minor_ver = dv->minor_version;
        cmd->driver_build_ver = dv->build_version;
        cmd->driver_subbuild_ver = dv->subbuild_version;
-       status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details);
+
+       len = 0;
+       while (len < sizeof(dv->driver_string) &&
+              (dv->driver_string[len] < 0x80) &&
+              dv->driver_string[len])
+               len++;
+       status = i40e_asq_send_command(hw, &desc, dv->driver_string,
+                                      len, cmd_details);
 
        return status;
 }
@@ -1900,6 +1971,12 @@ static void i40e_parse_discover_capabilities(struct i40e_hw *hw, void *buff,
                }
        }
 
+       /* Software override ensuring FCoE is disabled if npar or mfp
+        * mode because it is not supported in these modes.
+        */
+       if (p->npar_enable || p->mfp_mode_1)
+               p->fcoe = false;
+
        /* additional HW specific goodies that might
         * someday be HW version specific
         */
@@ -2094,8 +2171,8 @@ i40e_status i40e_aq_start_lldp(struct i40e_hw *hw,
  * @cmd_details: pointer to command details structure or NULL
  **/
 i40e_status i40e_aq_add_udp_tunnel(struct i40e_hw *hw,
-                               u16 udp_port, u8 header_len,
-                               u8 protocol_index, u8 *filter_index,
+                               u16 udp_port, u8 protocol_index,
+                               u8 *filter_index,
                                struct i40e_asq_cmd_details *cmd_details)
 {
        struct i40e_aq_desc desc;
@@ -2252,6 +2329,35 @@ static i40e_status i40e_aq_tx_sched_cmd(struct i40e_hw *hw, u16 seid,
        return status;
 }
 
+/**
+ * i40e_aq_config_vsi_bw_limit - Configure VSI BW Limit
+ * @hw: pointer to the hw struct
+ * @seid: VSI seid
+ * @credit: BW limit credits (0 = disabled)
+ * @max_credit: Max BW limit credits
+ * @cmd_details: pointer to command details structure or NULL
+ **/
+i40e_status i40e_aq_config_vsi_bw_limit(struct i40e_hw *hw,
+                               u16 seid, u16 credit, u8 max_credit,
+                               struct i40e_asq_cmd_details *cmd_details)
+{
+       struct i40e_aq_desc desc;
+       struct i40e_aqc_configure_vsi_bw_limit *cmd =
+               (struct i40e_aqc_configure_vsi_bw_limit *)&desc.params.raw;
+       i40e_status status;
+
+       i40e_fill_default_direct_cmd_desc(&desc,
+                                         i40e_aqc_opc_configure_vsi_bw_limit);
+
+       cmd->vsi_seid = cpu_to_le16(seid);
+       cmd->credit = cpu_to_le16(credit);
+       cmd->max_credit = max_credit;
+
+       status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details);
+
+       return status;
+}
+
 /**
  * i40e_aq_config_vsi_tc_bw - Config VSI BW Allocation per TC
  * @hw: pointer to the hw struct
@@ -2405,7 +2511,7 @@ static i40e_status i40e_validate_filter_settings(struct i40e_hw *hw,
 {
        u32 fcoe_cntx_size, fcoe_filt_size;
        u32 pe_cntx_size, pe_filt_size;
-       u32 fcoe_fmax, pe_fmax;
+       u32 fcoe_fmax;
        u32 val;
 
        /* Validate FCoE settings passed */
@@ -2480,13 +2586,6 @@ static i40e_status i40e_validate_filter_settings(struct i40e_hw *hw,
        if (fcoe_filt_size + fcoe_cntx_size >  fcoe_fmax)
                return I40E_ERR_INVALID_SIZE;
 
-       /* PEHSIZE + PEDSIZE should not be greater than PMPEXFMAX */
-       val = rd32(hw, I40E_GLHMC_PEXFMAX);
-       pe_fmax = (val & I40E_GLHMC_PEXFMAX_PMPEXFMAX_MASK)
-                  >> I40E_GLHMC_PEXFMAX_PMPEXFMAX_SHIFT;
-       if (pe_filt_size + pe_cntx_size >  pe_fmax)
-               return I40E_ERR_INVALID_SIZE;
-
        return 0;
 }
 
index 6e8103abfd0d699401f29287ea63cfba799a2fe1..00bc0cdb3a0304b87ec9935b148a81c683927f51 100644 (file)
@@ -232,7 +232,7 @@ static void i40e_dcbnl_del_app(struct i40e_pf *pf,
                              struct i40e_ieee_app_priority_table *app)
 {
        int v, err;
-       for (v = 0; v < pf->hw.func_caps.num_vsis; v++) {
+       for (v = 0; v < pf->num_alloc_vsi; v++) {
                if (pf->vsi[v] && pf->vsi[v]->netdev) {
                        err = i40e_dcbnl_vsi_del_app(pf->vsi[v], app);
                        if (err)
@@ -302,8 +302,8 @@ void i40e_dcbnl_setup(struct i40e_vsi *vsi)
        struct net_device *dev = vsi->netdev;
        struct i40e_pf *pf = i40e_netdev_to_pf(dev);
 
-       /* DCB not enabled */
-       if (!(pf->flags & I40E_FLAG_DCB_ENABLED))
+       /* Not DCB capable */
+       if (!(pf->flags & I40E_FLAG_DCB_CAPABLE))
                return;
 
        /* Do not setup DCB NL ops for MFP mode */
index 3c37386fd138fdeb3941170e5789687d90385fd5..cffdfc21290fdced44a5b1265eb2fb04a23608cf 100644 (file)
@@ -45,7 +45,7 @@ static struct i40e_vsi *i40e_dbg_find_vsi(struct i40e_pf *pf, int seid)
        if (seid < 0)
                dev_info(&pf->pdev->dev, "%d: bad seid\n", seid);
        else
-               for (i = 0; i < pf->hw.func_caps.num_vsis; i++)
+               for (i = 0; i < pf->num_alloc_vsi; i++)
                        if (pf->vsi[i] && (pf->vsi[i]->seid == seid))
                                return pf->vsi[i];
 
@@ -843,7 +843,7 @@ static void i40e_dbg_dump_vsi_no_seid(struct i40e_pf *pf)
 {
        int i;
 
-       for (i = 0; i < pf->hw.func_caps.num_vsis; i++)
+       for (i = 0; i < pf->num_alloc_vsi; i++)
                if (pf->vsi[i])
                        dev_info(&pf->pdev->dev, "dump vsi[%d]: %d\n",
                                 i, pf->vsi[i]->seid);
@@ -862,12 +862,11 @@ static void i40e_dbg_dump_eth_stats(struct i40e_pf *pf,
                 "    rx_bytes = \t%lld \trx_unicast = \t\t%lld \trx_multicast = \t%lld\n",
                estats->rx_bytes, estats->rx_unicast, estats->rx_multicast);
        dev_info(&pf->pdev->dev,
-                "    rx_broadcast = \t%lld \trx_discards = \t\t%lld \trx_errors = \t%lld\n",
-                estats->rx_broadcast, estats->rx_discards, estats->rx_errors);
+                "    rx_broadcast = \t%lld \trx_discards = \t\t%lld\n",
+                estats->rx_broadcast, estats->rx_discards);
        dev_info(&pf->pdev->dev,
-                "    rx_missed = \t%lld \trx_unknown_protocol = \t%lld \ttx_bytes = \t%lld\n",
-                estats->rx_missed, estats->rx_unknown_protocol,
-                estats->tx_bytes);
+                "    rx_unknown_protocol = \t%lld \ttx_bytes = \t%lld\n",
+                estats->rx_unknown_protocol, estats->tx_bytes);
        dev_info(&pf->pdev->dev,
                 "    tx_unicast = \t%lld \ttx_multicast = \t\t%lld \ttx_broadcast = \t%lld\n",
                 estats->tx_unicast, estats->tx_multicast, estats->tx_broadcast);
@@ -1527,7 +1526,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                        cnt = sscanf(&cmd_buf[15], "%i", &vsi_seid);
                        if (cnt == 0) {
                                int i;
-                               for (i = 0; i < pf->hw.func_caps.num_vsis; i++)
+                               for (i = 0; i < pf->num_alloc_vsi; i++)
                                        i40e_vsi_reset_stats(pf->vsi[i]);
                                dev_info(&pf->pdev->dev, "vsi clear stats called for all vsi's\n");
                        } else if (cnt == 1) {
@@ -1744,10 +1743,6 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                i40e_dbg_cmd_fd_ctrl(pf, I40E_FLAG_FD_ATR_ENABLED, false);
        } else if (strncmp(cmd_buf, "fd-atr on", 9) == 0) {
                i40e_dbg_cmd_fd_ctrl(pf, I40E_FLAG_FD_ATR_ENABLED, true);
-       } else if (strncmp(cmd_buf, "fd-sb off", 9) == 0) {
-               i40e_dbg_cmd_fd_ctrl(pf, I40E_FLAG_FD_SB_ENABLED, false);
-       } else if (strncmp(cmd_buf, "fd-sb on", 8) == 0) {
-               i40e_dbg_cmd_fd_ctrl(pf, I40E_FLAG_FD_SB_ENABLED, true);
        } else if (strncmp(cmd_buf, "lldp", 4) == 0) {
                if (strncmp(&cmd_buf[5], "stop", 4) == 0) {
                        int ret;
@@ -1967,8 +1962,6 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                dev_info(&pf->pdev->dev, "  rem fd_filter <dest q_index> <flex_off> <pctype> <dest_vsi> <dest_ctl> <fd_status> <cnt_index> <fd_id> <packet_len> <packet>\n");
                dev_info(&pf->pdev->dev, "  fd-atr off\n");
                dev_info(&pf->pdev->dev, "  fd-atr on\n");
-               dev_info(&pf->pdev->dev, "  fd-sb off\n");
-               dev_info(&pf->pdev->dev, "  fd-sb on\n");
                dev_info(&pf->pdev->dev, "  lldp start\n");
                dev_info(&pf->pdev->dev, "  lldp stop\n");
                dev_info(&pf->pdev->dev, "  lldp get local\n");
index b2380daef8c142941f0bda2b7d11620f7de75ad5..56438bd579e61a24d2f2c2cefefc89eecf2a926a 100644 (file)
@@ -67,17 +67,25 @@ static i40e_status i40e_diag_reg_pattern_test(struct i40e_hw *hw,
 
 struct i40e_diag_reg_test_info i40e_reg_list[] = {
        /* offset               mask         elements   stride */
-       {I40E_QTX_CTL(0),       0x0000FFBF,   4, I40E_QTX_CTL(1) - I40E_QTX_CTL(0)},
-       {I40E_PFINT_ITR0(0),    0x00000FFF,   3, I40E_PFINT_ITR0(1) - I40E_PFINT_ITR0(0)},
-       {I40E_PFINT_ITRN(0, 0), 0x00000FFF,   8, I40E_PFINT_ITRN(0, 1) - I40E_PFINT_ITRN(0, 0)},
-       {I40E_PFINT_ITRN(1, 0), 0x00000FFF,   8, I40E_PFINT_ITRN(1, 1) - I40E_PFINT_ITRN(1, 0)},
-       {I40E_PFINT_ITRN(2, 0), 0x00000FFF,   8, I40E_PFINT_ITRN(2, 1) - I40E_PFINT_ITRN(2, 0)},
-       {I40E_PFINT_STAT_CTL0,  0x0000000C,   1, 0},
-       {I40E_PFINT_LNKLST0,    0x00001FFF,   1, 0},
-       {I40E_PFINT_LNKLSTN(0), 0x000007FF,  64, I40E_PFINT_LNKLSTN(1) - I40E_PFINT_LNKLSTN(0)},
-       {I40E_QINT_TQCTL(0),    0x000000FF,  64, I40E_QINT_TQCTL(1) - I40E_QINT_TQCTL(0)},
-       {I40E_QINT_RQCTL(0),    0x000000FF,  64, I40E_QINT_RQCTL(1) - I40E_QINT_RQCTL(0)},
-       {I40E_PFINT_ICR0_ENA,   0xF7F20000,   1, 0},
+       {I40E_QTX_CTL(0),       0x0000FFBF, 1,
+               I40E_QTX_CTL(1) - I40E_QTX_CTL(0)},
+       {I40E_PFINT_ITR0(0),    0x00000FFF, 3,
+               I40E_PFINT_ITR0(1) - I40E_PFINT_ITR0(0)},
+       {I40E_PFINT_ITRN(0, 0), 0x00000FFF, 1,
+               I40E_PFINT_ITRN(0, 1) - I40E_PFINT_ITRN(0, 0)},
+       {I40E_PFINT_ITRN(1, 0), 0x00000FFF, 1,
+               I40E_PFINT_ITRN(1, 1) - I40E_PFINT_ITRN(1, 0)},
+       {I40E_PFINT_ITRN(2, 0), 0x00000FFF, 1,
+               I40E_PFINT_ITRN(2, 1) - I40E_PFINT_ITRN(2, 0)},
+       {I40E_PFINT_STAT_CTL0,  0x0000000C, 1, 0},
+       {I40E_PFINT_LNKLST0,    0x00001FFF, 1, 0},
+       {I40E_PFINT_LNKLSTN(0), 0x000007FF, 1,
+               I40E_PFINT_LNKLSTN(1) - I40E_PFINT_LNKLSTN(0)},
+       {I40E_QINT_TQCTL(0),    0x000000FF, 1,
+               I40E_QINT_TQCTL(1) - I40E_QINT_TQCTL(0)},
+       {I40E_QINT_RQCTL(0),    0x000000FF, 1,
+               I40E_QINT_RQCTL(1) - I40E_QINT_RQCTL(0)},
+       {I40E_PFINT_ICR0_ENA,   0xF7F20000, 1, 0},
        { 0 }
 };
 
@@ -93,9 +101,25 @@ i40e_status i40e_diag_reg_test(struct i40e_hw *hw)
        u32 reg, mask;
        u32 i, j;
 
-       for (i = 0; (i40e_reg_list[i].offset != 0) && !ret_code; i++) {
+       for (i = 0; i40e_reg_list[i].offset != 0 &&
+                                            !ret_code; i++) {
+
+               /* set actual reg range for dynamically allocated resources */
+               if (i40e_reg_list[i].offset == I40E_QTX_CTL(0) &&
+                   hw->func_caps.num_tx_qp != 0)
+                       i40e_reg_list[i].elements = hw->func_caps.num_tx_qp;
+               if ((i40e_reg_list[i].offset == I40E_PFINT_ITRN(0, 0) ||
+                    i40e_reg_list[i].offset == I40E_PFINT_ITRN(1, 0) ||
+                    i40e_reg_list[i].offset == I40E_PFINT_ITRN(2, 0) ||
+                    i40e_reg_list[i].offset == I40E_QINT_TQCTL(0) ||
+                    i40e_reg_list[i].offset == I40E_QINT_RQCTL(0)) &&
+                   hw->func_caps.num_msix_vectors != 0)
+                       i40e_reg_list[i].elements =
+                               hw->func_caps.num_msix_vectors - 1;
+
+               /* test register access */
                mask = i40e_reg_list[i].mask;
-               for (j = 0; (j < i40e_reg_list[i].elements) && !ret_code; j++) {
+               for (j = 0; j < i40e_reg_list[i].elements && !ret_code; j++) {
                        reg = i40e_reg_list[i].offset +
                              (j * i40e_reg_list[i].stride);
                        ret_code = i40e_diag_reg_pattern_test(hw, reg, mask);
index 03d99cbc5c251bcbb0120667ff1d53e304da00a9..4a488ffcd6b0b1221e31755116acf7830f49d20c 100644 (file)
@@ -46,6 +46,8 @@ struct i40e_stats {
                I40E_STAT(struct i40e_pf, _name, _stat)
 #define I40E_VSI_STAT(_name, _stat) \
                I40E_STAT(struct i40e_vsi, _name, _stat)
+#define I40E_VEB_STAT(_name, _stat) \
+               I40E_STAT(struct i40e_veb, _name, _stat)
 
 static const struct i40e_stats i40e_gstrings_net_stats[] = {
        I40E_NETDEV_STAT(rx_packets),
@@ -56,12 +58,36 @@ static const struct i40e_stats i40e_gstrings_net_stats[] = {
        I40E_NETDEV_STAT(tx_errors),
        I40E_NETDEV_STAT(rx_dropped),
        I40E_NETDEV_STAT(tx_dropped),
-       I40E_NETDEV_STAT(multicast),
        I40E_NETDEV_STAT(collisions),
        I40E_NETDEV_STAT(rx_length_errors),
        I40E_NETDEV_STAT(rx_crc_errors),
 };
 
+static const struct i40e_stats i40e_gstrings_veb_stats[] = {
+       I40E_VEB_STAT("rx_bytes", stats.rx_bytes),
+       I40E_VEB_STAT("tx_bytes", stats.tx_bytes),
+       I40E_VEB_STAT("rx_unicast", stats.rx_unicast),
+       I40E_VEB_STAT("tx_unicast", stats.tx_unicast),
+       I40E_VEB_STAT("rx_multicast", stats.rx_multicast),
+       I40E_VEB_STAT("tx_multicast", stats.tx_multicast),
+       I40E_VEB_STAT("rx_broadcast", stats.rx_broadcast),
+       I40E_VEB_STAT("tx_broadcast", stats.tx_broadcast),
+       I40E_VEB_STAT("rx_discards", stats.rx_discards),
+       I40E_VEB_STAT("tx_discards", stats.tx_discards),
+       I40E_VEB_STAT("tx_errors", stats.tx_errors),
+       I40E_VEB_STAT("rx_unknown_protocol", stats.rx_unknown_protocol),
+};
+
+static const struct i40e_stats i40e_gstrings_misc_stats[] = {
+       I40E_VSI_STAT("rx_unicast", eth_stats.rx_unicast),
+       I40E_VSI_STAT("tx_unicast", eth_stats.tx_unicast),
+       I40E_VSI_STAT("rx_multicast", eth_stats.rx_multicast),
+       I40E_VSI_STAT("tx_multicast", eth_stats.tx_multicast),
+       I40E_VSI_STAT("rx_broadcast", eth_stats.rx_broadcast),
+       I40E_VSI_STAT("tx_broadcast", eth_stats.tx_broadcast),
+       I40E_VSI_STAT("rx_unknown_protocol", eth_stats.rx_unknown_protocol),
+};
+
 static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi,
                                 struct ethtool_rxnfc *cmd);
 
@@ -78,7 +104,12 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi,
 static struct i40e_stats i40e_gstrings_stats[] = {
        I40E_PF_STAT("rx_bytes", stats.eth.rx_bytes),
        I40E_PF_STAT("tx_bytes", stats.eth.tx_bytes),
-       I40E_PF_STAT("rx_errors", stats.eth.rx_errors),
+       I40E_PF_STAT("rx_unicast", stats.eth.rx_unicast),
+       I40E_PF_STAT("tx_unicast", stats.eth.tx_unicast),
+       I40E_PF_STAT("rx_multicast", stats.eth.rx_multicast),
+       I40E_PF_STAT("tx_multicast", stats.eth.tx_multicast),
+       I40E_PF_STAT("rx_broadcast", stats.eth.rx_broadcast),
+       I40E_PF_STAT("tx_broadcast", stats.eth.tx_broadcast),
        I40E_PF_STAT("tx_errors", stats.eth.tx_errors),
        I40E_PF_STAT("rx_dropped", stats.eth.rx_discards),
        I40E_PF_STAT("tx_dropped", stats.eth.tx_discards),
@@ -88,6 +119,7 @@ static struct i40e_stats i40e_gstrings_stats[] = {
        I40E_PF_STAT("mac_local_faults", stats.mac_local_faults),
        I40E_PF_STAT("mac_remote_faults", stats.mac_remote_faults),
        I40E_PF_STAT("tx_timeout", tx_timeout_count),
+       I40E_PF_STAT("rx_csum_bad", hw_csum_rx_error),
        I40E_PF_STAT("rx_length_errors", stats.rx_length_errors),
        I40E_PF_STAT("link_xon_rx", stats.link_xon_rx),
        I40E_PF_STAT("link_xoff_rx", stats.link_xoff_rx),
@@ -112,8 +144,10 @@ static struct i40e_stats i40e_gstrings_stats[] = {
        I40E_PF_STAT("rx_oversize", stats.rx_oversize),
        I40E_PF_STAT("rx_jabber", stats.rx_jabber),
        I40E_PF_STAT("VF_admin_queue_requests", vf_aq_requests),
-       I40E_PF_STAT("tx_hwtstamp_timeouts", tx_hwtstamp_timeouts),
        I40E_PF_STAT("rx_hwtstamp_cleared", rx_hwtstamp_cleared),
+       I40E_PF_STAT("fdir_atr_match", stats.fd_atr_match),
+       I40E_PF_STAT("fdir_sb_match", stats.fd_sb_match),
+
        /* LPI stats */
        I40E_PF_STAT("tx_lpi_status", stats.tx_lpi_status),
        I40E_PF_STAT("rx_lpi_status", stats.rx_lpi_status),
@@ -122,11 +156,14 @@ static struct i40e_stats i40e_gstrings_stats[] = {
 };
 
 #define I40E_QUEUE_STATS_LEN(n) \
-  ((((struct i40e_netdev_priv *)netdev_priv((n)))->vsi->num_queue_pairs + \
-    ((struct i40e_netdev_priv *)netdev_priv((n)))->vsi->num_queue_pairs) * 2)
+       (((struct i40e_netdev_priv *)netdev_priv((n)))->vsi->num_queue_pairs \
+           * 2 /* Tx and Rx together */                                     \
+           * (sizeof(struct i40e_queue_stats) / sizeof(u64)))
 #define I40E_GLOBAL_STATS_LEN  ARRAY_SIZE(i40e_gstrings_stats)
 #define I40E_NETDEV_STATS_LEN   ARRAY_SIZE(i40e_gstrings_net_stats)
+#define I40E_MISC_STATS_LEN    ARRAY_SIZE(i40e_gstrings_misc_stats)
 #define I40E_VSI_STATS_LEN(n)   (I40E_NETDEV_STATS_LEN + \
+                                I40E_MISC_STATS_LEN + \
                                 I40E_QUEUE_STATS_LEN((n)))
 #define I40E_PFC_STATS_LEN ( \
                (FIELD_SIZEOF(struct i40e_pf, stats.priority_xoff_rx) + \
@@ -135,6 +172,7 @@ static struct i40e_stats i40e_gstrings_stats[] = {
                 FIELD_SIZEOF(struct i40e_pf, stats.priority_xon_tx) + \
                 FIELD_SIZEOF(struct i40e_pf, stats.priority_xon_2_xoff)) \
                 / sizeof(u64))
+#define I40E_VEB_STATS_LEN     ARRAY_SIZE(i40e_gstrings_veb_stats)
 #define I40E_PF_STATS_LEN(n)   (I40E_GLOBAL_STATS_LEN + \
                                 I40E_PFC_STATS_LEN + \
                                 I40E_VSI_STATS_LEN((n)))
@@ -620,10 +658,15 @@ static int i40e_get_sset_count(struct net_device *netdev, int sset)
        case ETH_SS_TEST:
                return I40E_TEST_LEN;
        case ETH_SS_STATS:
-               if (vsi == pf->vsi[pf->lan_vsi])
-                       return I40E_PF_STATS_LEN(netdev);
-               else
+               if (vsi == pf->vsi[pf->lan_vsi]) {
+                       int len = I40E_PF_STATS_LEN(netdev);
+
+                       if (pf->lan_veb != I40E_NO_VEB)
+                               len += I40E_VEB_STATS_LEN;
+                       return len;
+               } else {
                        return I40E_VSI_STATS_LEN(netdev);
+               }
        default:
                return -EOPNOTSUPP;
        }
@@ -633,6 +676,7 @@ static void i40e_get_ethtool_stats(struct net_device *netdev,
                                   struct ethtool_stats *stats, u64 *data)
 {
        struct i40e_netdev_priv *np = netdev_priv(netdev);
+       struct i40e_ring *tx_ring, *rx_ring;
        struct i40e_vsi *vsi = np->vsi;
        struct i40e_pf *pf = vsi->back;
        int i = 0;
@@ -648,10 +692,14 @@ 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 < I40E_MISC_STATS_LEN; j++) {
+               p = (char *)vsi + i40e_gstrings_misc_stats[j].stat_offset;
+               data[i++] = (i40e_gstrings_misc_stats[j].sizeof_stat ==
+                           sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+       }
        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;
+       for (j = 0; j < vsi->num_queue_pairs; j++) {
+               tx_ring = ACCESS_ONCE(vsi->tx_rings[j]);
 
                if (!tx_ring)
                        continue;
@@ -662,33 +710,45 @@ static void i40e_get_ethtool_stats(struct net_device *netdev,
                        data[i] = tx_ring->stats.packets;
                        data[i + 1] = tx_ring->stats.bytes;
                } while (u64_stats_fetch_retry_irq(&tx_ring->syncp, start));
+               i += 2;
 
                /* Rx ring is the 2nd half of the queue pair */
                rx_ring = &tx_ring[1];
                do {
                        start = u64_stats_fetch_begin_irq(&rx_ring->syncp);
-                       data[i + 2] = rx_ring->stats.packets;
-                       data[i + 3] = rx_ring->stats.bytes;
+                       data[i] = rx_ring->stats.packets;
+                       data[i + 1] = rx_ring->stats.bytes;
                } while (u64_stats_fetch_retry_irq(&rx_ring->syncp, start));
+               i += 2;
        }
        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;
-                       data[i++] = (i40e_gstrings_stats[j].sizeof_stat ==
-                                  sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
-               }
-               for (j = 0; j < I40E_MAX_USER_PRIORITY; j++) {
-                       data[i++] = pf->stats.priority_xon_tx[j];
-                       data[i++] = pf->stats.priority_xoff_tx[j];
-               }
-               for (j = 0; j < I40E_MAX_USER_PRIORITY; j++) {
-                       data[i++] = pf->stats.priority_xon_rx[j];
-                       data[i++] = pf->stats.priority_xoff_rx[j];
+       if (vsi != pf->vsi[pf->lan_vsi])
+               return;
+
+       if (pf->lan_veb != I40E_NO_VEB) {
+               struct i40e_veb *veb = pf->veb[pf->lan_veb];
+               for (j = 0; j < I40E_VEB_STATS_LEN; j++) {
+                       p = (char *)veb;
+                       p += i40e_gstrings_veb_stats[j].stat_offset;
+                       data[i++] = (i40e_gstrings_veb_stats[j].sizeof_stat ==
+                                    sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
                }
-               for (j = 0; j < I40E_MAX_USER_PRIORITY; j++)
-                       data[i++] = pf->stats.priority_xon_2_xoff[j];
        }
+       for (j = 0; j < I40E_GLOBAL_STATS_LEN; j++) {
+               p = (char *)pf + i40e_gstrings_stats[j].stat_offset;
+               data[i++] = (i40e_gstrings_stats[j].sizeof_stat ==
+                            sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+       }
+       for (j = 0; j < I40E_MAX_USER_PRIORITY; j++) {
+               data[i++] = pf->stats.priority_xon_tx[j];
+               data[i++] = pf->stats.priority_xoff_tx[j];
+       }
+       for (j = 0; j < I40E_MAX_USER_PRIORITY; j++) {
+               data[i++] = pf->stats.priority_xon_rx[j];
+               data[i++] = pf->stats.priority_xoff_rx[j];
+       }
+       for (j = 0; j < I40E_MAX_USER_PRIORITY; j++)
+               data[i++] = pf->stats.priority_xon_2_xoff[j];
 }
 
 static void i40e_get_strings(struct net_device *netdev, u32 stringset,
@@ -713,6 +773,11 @@ static void i40e_get_strings(struct net_device *netdev, u32 stringset,
                                 i40e_gstrings_net_stats[i].stat_string);
                        p += ETH_GSTRING_LEN;
                }
+               for (i = 0; i < I40E_MISC_STATS_LEN; i++) {
+                       snprintf(p, ETH_GSTRING_LEN, "%s",
+                                i40e_gstrings_misc_stats[i].stat_string);
+                       p += ETH_GSTRING_LEN;
+               }
                for (i = 0; i < vsi->num_queue_pairs; i++) {
                        snprintf(p, ETH_GSTRING_LEN, "tx-%u.tx_packets", i);
                        p += ETH_GSTRING_LEN;
@@ -723,34 +788,42 @@ static void i40e_get_strings(struct net_device *netdev, u32 stringset,
                        snprintf(p, ETH_GSTRING_LEN, "rx-%u.rx_bytes", i);
                        p += ETH_GSTRING_LEN;
                }
-               if (vsi == pf->vsi[pf->lan_vsi]) {
-                       for (i = 0; i < I40E_GLOBAL_STATS_LEN; i++) {
-                               snprintf(p, ETH_GSTRING_LEN, "port.%s",
-                                        i40e_gstrings_stats[i].stat_string);
-                               p += ETH_GSTRING_LEN;
-                       }
-                       for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
-                               snprintf(p, ETH_GSTRING_LEN,
-                                        "port.tx_priority_%u_xon", i);
-                               p += ETH_GSTRING_LEN;
-                               snprintf(p, ETH_GSTRING_LEN,
-                                        "port.tx_priority_%u_xoff", i);
-                               p += ETH_GSTRING_LEN;
-                       }
-                       for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
-                               snprintf(p, ETH_GSTRING_LEN,
-                                        "port.rx_priority_%u_xon", i);
-                               p += ETH_GSTRING_LEN;
-                               snprintf(p, ETH_GSTRING_LEN,
-                                        "port.rx_priority_%u_xoff", i);
-                               p += ETH_GSTRING_LEN;
-                       }
-                       for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
-                               snprintf(p, ETH_GSTRING_LEN,
-                                        "port.rx_priority_%u_xon_2_xoff", i);
+               if (vsi != pf->vsi[pf->lan_vsi])
+                       return;
+
+               if (pf->lan_veb != I40E_NO_VEB) {
+                       for (i = 0; i < I40E_VEB_STATS_LEN; i++) {
+                               snprintf(p, ETH_GSTRING_LEN, "veb.%s",
+                                       i40e_gstrings_veb_stats[i].stat_string);
                                p += ETH_GSTRING_LEN;
                        }
                }
+               for (i = 0; i < I40E_GLOBAL_STATS_LEN; i++) {
+                       snprintf(p, ETH_GSTRING_LEN, "port.%s",
+                                i40e_gstrings_stats[i].stat_string);
+                       p += ETH_GSTRING_LEN;
+               }
+               for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
+                       snprintf(p, ETH_GSTRING_LEN,
+                                "port.tx_priority_%u_xon", i);
+                       p += ETH_GSTRING_LEN;
+                       snprintf(p, ETH_GSTRING_LEN,
+                                "port.tx_priority_%u_xoff", i);
+                       p += ETH_GSTRING_LEN;
+               }
+               for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
+                       snprintf(p, ETH_GSTRING_LEN,
+                                "port.rx_priority_%u_xon", i);
+                       p += ETH_GSTRING_LEN;
+                       snprintf(p, ETH_GSTRING_LEN,
+                                "port.rx_priority_%u_xoff", i);
+                       p += ETH_GSTRING_LEN;
+               }
+               for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
+                       snprintf(p, ETH_GSTRING_LEN,
+                                "port.rx_priority_%u_xon_2_xoff", i);
+                       p += ETH_GSTRING_LEN;
+               }
                /* BUG_ON(p - data != I40E_STATS_LEN * ETH_GSTRING_LEN); */
                break;
        }
@@ -1007,14 +1080,13 @@ static int i40e_get_coalesce(struct net_device *netdev,
        ec->rx_max_coalesced_frames_irq = vsi->work_limit;
 
        if (ITR_IS_DYNAMIC(vsi->rx_itr_setting))
-               ec->rx_coalesce_usecs = 1;
-       else
-               ec->rx_coalesce_usecs = vsi->rx_itr_setting;
+               ec->use_adaptive_rx_coalesce = 1;
 
        if (ITR_IS_DYNAMIC(vsi->tx_itr_setting))
-               ec->tx_coalesce_usecs = 1;
-       else
-               ec->tx_coalesce_usecs = vsi->tx_itr_setting;
+               ec->use_adaptive_tx_coalesce = 1;
+
+       ec->rx_coalesce_usecs = vsi->rx_itr_setting & ~I40E_ITR_DYNAMIC;
+       ec->tx_coalesce_usecs = vsi->tx_itr_setting & ~I40E_ITR_DYNAMIC;
 
        return 0;
 }
@@ -1033,37 +1105,27 @@ static int i40e_set_coalesce(struct net_device *netdev,
        if (ec->tx_max_coalesced_frames_irq || ec->rx_max_coalesced_frames_irq)
                vsi->work_limit = ec->tx_max_coalesced_frames_irq;
 
-       switch (ec->rx_coalesce_usecs) {
-       case 0:
-               vsi->rx_itr_setting = 0;
-               break;
-       case 1:
-               vsi->rx_itr_setting = (I40E_ITR_DYNAMIC |
-                                      ITR_REG_TO_USEC(I40E_ITR_RX_DEF));
-               break;
-       default:
-               if ((ec->rx_coalesce_usecs < (I40E_MIN_ITR << 1)) ||
-                   (ec->rx_coalesce_usecs > (I40E_MAX_ITR << 1)))
-                       return -EINVAL;
+       if ((ec->rx_coalesce_usecs >= (I40E_MIN_ITR << 1)) &&
+           (ec->rx_coalesce_usecs <= (I40E_MAX_ITR << 1)))
                vsi->rx_itr_setting = ec->rx_coalesce_usecs;
-               break;
-       }
+       else
+               return -EINVAL;
 
-       switch (ec->tx_coalesce_usecs) {
-       case 0:
-               vsi->tx_itr_setting = 0;
-               break;
-       case 1:
-               vsi->tx_itr_setting = (I40E_ITR_DYNAMIC |
-                                      ITR_REG_TO_USEC(I40E_ITR_TX_DEF));
-               break;
-       default:
-               if ((ec->tx_coalesce_usecs < (I40E_MIN_ITR << 1)) ||
-                   (ec->tx_coalesce_usecs > (I40E_MAX_ITR << 1)))
-                       return -EINVAL;
+       if ((ec->tx_coalesce_usecs >= (I40E_MIN_ITR << 1)) &&
+           (ec->tx_coalesce_usecs <= (I40E_MAX_ITR << 1)))
                vsi->tx_itr_setting = ec->tx_coalesce_usecs;
-               break;
-       }
+       else
+               return -EINVAL;
+
+       if (ec->use_adaptive_rx_coalesce)
+               vsi->rx_itr_setting |= I40E_ITR_DYNAMIC;
+       else
+               vsi->rx_itr_setting &= ~I40E_ITR_DYNAMIC;
+
+       if (ec->use_adaptive_tx_coalesce)
+               vsi->tx_itr_setting |= I40E_ITR_DYNAMIC;
+       else
+               vsi->tx_itr_setting &= ~I40E_ITR_DYNAMIC;
 
        vector = vsi->base_vector;
        for (i = 0; i < vsi->num_q_vectors; i++, vector++) {
@@ -1140,8 +1202,7 @@ static int i40e_get_ethtool_fdir_all(struct i40e_pf *pf,
        int cnt = 0;
 
        /* report total rule count */
-       cmd->data = pf->hw.fdir_shared_filter_count +
-                   pf->fdir_pf_filter_count;
+       cmd->data = i40e_get_fd_cnt_all(pf);
 
        hlist_for_each_entry_safe(rule, node2,
                                  &pf->fdir_filter_list, fdir_node) {
@@ -1175,10 +1236,6 @@ static int i40e_get_ethtool_fdir_entry(struct i40e_pf *pf,
        struct i40e_fdir_filter *rule = NULL;
        struct hlist_node *node2;
 
-       /* report total rule count */
-       cmd->data = pf->hw.fdir_shared_filter_count +
-                   pf->fdir_pf_filter_count;
-
        hlist_for_each_entry_safe(rule, node2,
                                  &pf->fdir_filter_list, fdir_node) {
                if (fsp->location <= rule->fd_id)
@@ -1189,11 +1246,24 @@ static int i40e_get_ethtool_fdir_entry(struct i40e_pf *pf,
                return -EINVAL;
 
        fsp->flow_type = rule->flow_type;
-       fsp->h_u.tcp_ip4_spec.psrc = rule->src_port;
-       fsp->h_u.tcp_ip4_spec.pdst = rule->dst_port;
-       fsp->h_u.tcp_ip4_spec.ip4src = rule->src_ip[0];
-       fsp->h_u.tcp_ip4_spec.ip4dst = rule->dst_ip[0];
-       fsp->ring_cookie = rule->q_index;
+       if (fsp->flow_type == IP_USER_FLOW) {
+               fsp->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;
+               fsp->h_u.usr_ip4_spec.proto = 0;
+               fsp->m_u.usr_ip4_spec.proto = 0;
+       }
+
+       /* Reverse the src and dest notion, since the HW views them from
+        * Tx perspective where as the user expects it from Rx filter view.
+        */
+       fsp->h_u.tcp_ip4_spec.psrc = rule->dst_port;
+       fsp->h_u.tcp_ip4_spec.pdst = rule->src_port;
+       fsp->h_u.tcp_ip4_spec.ip4src = rule->dst_ip[0];
+       fsp->h_u.tcp_ip4_spec.ip4dst = rule->src_ip[0];
+
+       if (rule->dest_ctl == I40E_FILTER_PROGRAM_DESC_DEST_DROP_PACKET)
+               fsp->ring_cookie = RX_CLS_FLOW_DISC;
+       else
+               fsp->ring_cookie = rule->q_index;
 
        return 0;
 }
@@ -1223,6 +1293,8 @@ static int i40e_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
                break;
        case ETHTOOL_GRXCLSRLCNT:
                cmd->rule_cnt = pf->fdir_pf_active_filters;
+               /* report total rule count */
+               cmd->data = i40e_get_fd_cnt_all(pf);
                ret = 0;
                break;
        case ETHTOOL_GRXCLSRULE:
@@ -1291,16 +1363,12 @@ static int i40e_set_rss_hash_opt(struct i40e_pf *pf, struct ethtool_rxnfc *nfc)
        case UDP_V4_FLOW:
                switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
                case 0:
-                       hena &=
-                       ~(((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) |
-                       ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP) |
-                       ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4));
+                       hena &= ~(((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) |
+                                 ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4));
                        break;
                case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
-                       hena |=
-                       (((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP)  |
-                       ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP) |
-                       ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4));
+                       hena |= (((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) |
+                                 ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4));
                        break;
                default:
                        return -EINVAL;
@@ -1309,16 +1377,12 @@ static int i40e_set_rss_hash_opt(struct i40e_pf *pf, struct ethtool_rxnfc *nfc)
        case UDP_V6_FLOW:
                switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
                case 0:
-                       hena &=
-                       ~(((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) |
-                       ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP) |
-                       ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6));
+                       hena &= ~(((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) |
+                                 ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6));
                        break;
                case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
-                       hena |=
-                       (((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP)  |
-                       ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP) |
-                       ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6));
+                       hena |= (((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) |
+                                ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6));
                        break;
                default:
                        return -EINVAL;
@@ -1503,7 +1567,8 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi,
                return -EINVAL;
        }
 
-       if (fsp->ring_cookie >= vsi->num_queue_pairs)
+       if ((fsp->ring_cookie != RX_CLS_FLOW_DISC) &&
+           (fsp->ring_cookie >= vsi->num_queue_pairs))
                return -EINVAL;
 
        input = kzalloc(sizeof(*input), GFP_KERNEL);
@@ -1524,13 +1589,17 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi,
        input->pctype = 0;
        input->dest_vsi = vsi->id;
        input->fd_status = I40E_FILTER_PROGRAM_DESC_FD_STATUS_FD_ID;
-       input->cnt_index = 0;
+       input->cnt_index  = pf->fd_sb_cnt_idx;
        input->flow_type = fsp->flow_type;
        input->ip4_proto = fsp->h_u.usr_ip4_spec.proto;
-       input->src_port = fsp->h_u.tcp_ip4_spec.psrc;
-       input->dst_port = fsp->h_u.tcp_ip4_spec.pdst;
-       input->src_ip[0] = fsp->h_u.tcp_ip4_spec.ip4src;
-       input->dst_ip[0] = fsp->h_u.tcp_ip4_spec.ip4dst;
+
+       /* Reverse the src and dest notion, since the HW expects them to be from
+        * Tx perspective where as the input from user is from Rx filter view.
+        */
+       input->dst_port = fsp->h_u.tcp_ip4_spec.psrc;
+       input->src_port = fsp->h_u.tcp_ip4_spec.pdst;
+       input->dst_ip[0] = fsp->h_u.tcp_ip4_spec.ip4src;
+       input->src_ip[0] = fsp->h_u.tcp_ip4_spec.ip4dst;
 
        ret = i40e_add_del_fdir(vsi, input, true);
        if (ret)
@@ -1692,5 +1761,5 @@ static const struct ethtool_ops i40e_ethtool_ops = {
 
 void i40e_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &i40e_ethtool_ops);
+       netdev->ethtool_ops = &i40e_ethtool_ops;
 }
index bf2d4cc5b56927e87a1be2cd5772198533d27afa..9b987ccc9e828738caef1c4811c5db1ca33b388d 100644 (file)
@@ -201,7 +201,7 @@ i40e_status i40e_add_pd_table_entry(struct i40e_hw *hw,
  **/
 i40e_status i40e_remove_pd_bp(struct i40e_hw *hw,
                                        struct i40e_hmc_info *hmc_info,
-                                       u32 idx, bool is_pf)
+                                       u32 idx)
 {
        i40e_status ret_code = 0;
        struct i40e_hmc_pd_entry *pd_entry;
@@ -237,10 +237,7 @@ i40e_status i40e_remove_pd_bp(struct i40e_hw *hw,
        pd_addr = (u64 *)pd_table->pd_page_addr.va;
        pd_addr += rel_pd_idx;
        memset(pd_addr, 0, sizeof(u64));
-       if (is_pf)
-               I40E_INVALIDATE_PF_HMC_PD(hw, sd_idx, idx);
-       else
-               I40E_INVALIDATE_VF_HMC_PD(hw, sd_idx, idx, hmc_info->hmc_fn_id);
+       I40E_INVALIDATE_PF_HMC_PD(hw, sd_idx, idx);
 
        /* free memory here */
        ret_code = i40e_free_dma_mem(hw, &(pd_entry->bp.addr));
index 0cd4701234f8c0fc41f64e926640fca1a5ad9757..b45d8fedc5e755e712e753c4f6b47a977545c45a 100644 (file)
@@ -163,11 +163,6 @@ struct i40e_hmc_info {
            (((sd_idx) << I40E_PFHMC_PDINV_PMSDIDX_SHIFT) |             \
             ((pd_idx) << I40E_PFHMC_PDINV_PMPDIDX_SHIFT)))
 
-#define I40E_INVALIDATE_VF_HMC_PD(hw, sd_idx, pd_idx, hmc_fn_id)          \
-       wr32((hw), I40E_GLHMC_VFPDINV((hmc_fn_id) - I40E_FIRST_VF_FPM_ID), \
-            (((sd_idx) << I40E_PFHMC_PDINV_PMSDIDX_SHIFT) |               \
-             ((pd_idx) << I40E_PFHMC_PDINV_PMPDIDX_SHIFT)))
-
 /**
  * I40E_FIND_SD_INDEX_LIMIT - finds segment descriptor index limit
  * @hmc_info: pointer to the HMC configuration information structure
@@ -226,7 +221,7 @@ i40e_status i40e_add_pd_table_entry(struct i40e_hw *hw,
                                              u32 pd_index);
 i40e_status i40e_remove_pd_bp(struct i40e_hw *hw,
                                        struct i40e_hmc_info *hmc_info,
-                                       u32 idx, bool is_pf);
+                                       u32 idx);
 i40e_status i40e_prep_remove_sd_bp(struct i40e_hmc_info *hmc_info,
                                             u32 idx);
 i40e_status i40e_remove_sd_bp_new(struct i40e_hw *hw,
index d5d98fe2691dd6048f7edba7f6b4847076e5316d..870ab1ee072cdf4759e848a8eca6cdbf6dc0a793 100644 (file)
@@ -397,7 +397,7 @@ static i40e_status i40e_create_lan_hmc_object(struct i40e_hw *hw,
                                /* remove the backing pages from pd_idx1 to i */
                                while (i && (i > pd_idx1)) {
                                        i40e_remove_pd_bp(hw, info->hmc_info,
-                                                         (i - 1), true);
+                                                         (i - 1));
                                        i--;
                                }
                        }
@@ -433,11 +433,7 @@ static i40e_status i40e_create_lan_hmc_object(struct i40e_hw *hw,
                                      ((j - 1) * I40E_HMC_MAX_BP_COUNT));
                        pd_lmt1 = min(pd_lmt, (j * I40E_HMC_MAX_BP_COUNT));
                        for (i = pd_idx1; i < pd_lmt1; i++) {
-                               i40e_remove_pd_bp(
-                                       hw,
-                                       info->hmc_info,
-                                       i,
-                                       true);
+                               i40e_remove_pd_bp(hw, info->hmc_info, i);
                        }
                        i40e_remove_pd_page(hw, info->hmc_info, (j - 1));
                        break;
@@ -616,8 +612,7 @@ static i40e_status i40e_delete_lan_hmc_object(struct i40e_hw *hw,
                pd_table =
                        &info->hmc_info->sd_table.sd_entry[sd_idx].u.pd_table;
                if (pd_table->pd_entry[rel_pd_idx].valid) {
-                       ret_code = i40e_remove_pd_bp(hw, info->hmc_info,
-                                                    j, true);
+                       ret_code = i40e_remove_pd_bp(hw, info->hmc_info, j);
                        if (ret_code)
                                goto exit;
                }
@@ -747,6 +742,7 @@ static struct i40e_context_ele i40e_hmc_rxq_ce_info[] = {
        { I40E_HMC_STORE(i40e_hmc_obj_rxq, tphdata_ena),  1,    195 },
        { I40E_HMC_STORE(i40e_hmc_obj_rxq, tphhead_ena),  1,    196 },
        { I40E_HMC_STORE(i40e_hmc_obj_rxq, lrxqthresh),   3,    198 },
+       { I40E_HMC_STORE(i40e_hmc_obj_rxq, prefena),      1,    201 },
        { 0 }
 };
 
index 341de925a2983181a1cd7d7f18d59996fd128158..eb65fe23c4a70077e31e2546208aaeaecf93224d 100644 (file)
@@ -56,6 +56,7 @@ struct i40e_hmc_obj_rxq {
        u8  tphdata_ena;
        u8  tphhead_ena;
        u8  lrxqthresh;
+       u8  prefena;    /* NOTE: normally must be set to 1 at init */
 };
 
 /* Tx queue context data */
index 2e72449f1265e9c82c1f3273f7a40a9cce3f4504..275ca9a1719ed812e7ee15ac8bfda447ee31e217 100644 (file)
@@ -38,8 +38,8 @@ static const char i40e_driver_string[] =
 #define DRV_KERN "-k"
 
 #define DRV_VERSION_MAJOR 0
-#define DRV_VERSION_MINOR 3
-#define DRV_VERSION_BUILD 36
+#define DRV_VERSION_MINOR 4
+#define DRV_VERSION_BUILD 10
 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \
             __stringify(DRV_VERSION_MINOR) "." \
             __stringify(DRV_VERSION_BUILD)    DRV_KERN
@@ -67,12 +67,10 @@ static int i40e_veb_get_bw_info(struct i40e_veb *veb);
  */
 static DEFINE_PCI_DEVICE_TABLE(i40e_pci_tbl) = {
        {PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_XL710), 0},
-       {PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_X710), 0},
        {PCI_VDEVICE(INTEL, I40E_DEV_ID_QEMU), 0},
        {PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_A), 0},
        {PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_B), 0},
        {PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_C), 0},
-       {PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_D), 0},
        {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_A), 0},
        {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_B), 0},
        {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_C), 0},
@@ -356,6 +354,7 @@ static struct rtnl_link_stats64 *i40e_get_netdev_stats_struct(
                                             struct rtnl_link_stats64 *stats)
 {
        struct i40e_netdev_priv *np = netdev_priv(netdev);
+       struct i40e_ring *tx_ring, *rx_ring;
        struct i40e_vsi *vsi = np->vsi;
        struct rtnl_link_stats64 *vsi_stats = i40e_get_vsi_stats_struct(vsi);
        int i;
@@ -368,7 +367,6 @@ static struct rtnl_link_stats64 *i40e_get_netdev_stats_struct(
 
        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;
 
@@ -397,7 +395,7 @@ static struct rtnl_link_stats64 *i40e_get_netdev_stats_struct(
        }
        rcu_read_unlock();
 
-       /* following stats updated by ixgbe_watchdog_task() */
+       /* following stats updated by i40e_watchdog_subtask() */
        stats->multicast        = vsi_stats->multicast;
        stats->tx_errors        = vsi_stats->tx_errors;
        stats->tx_dropped       = vsi_stats->tx_dropped;
@@ -530,6 +528,12 @@ void i40e_update_eth_stats(struct i40e_vsi *vsi)
        i40e_stat_update32(hw, I40E_GLV_RDPC(stat_idx),
                           vsi->stat_offsets_loaded,
                           &oes->rx_discards, &es->rx_discards);
+       i40e_stat_update32(hw, I40E_GLV_RUPP(stat_idx),
+                          vsi->stat_offsets_loaded,
+                          &oes->rx_unknown_protocol, &es->rx_unknown_protocol);
+       i40e_stat_update32(hw, I40E_GLV_TEPC(stat_idx),
+                          vsi->stat_offsets_loaded,
+                          &oes->tx_errors, &es->tx_errors);
 
        i40e_stat_update48(hw, I40E_GLV_GORCH(stat_idx),
                           I40E_GLV_GORCL(stat_idx),
@@ -648,10 +652,10 @@ static void i40e_update_link_xoff_rx(struct i40e_pf *pf)
                return;
 
        /* Clear the __I40E_HANG_CHECK_ARMED bit for all Tx rings */
-       for (v = 0; v < pf->hw.func_caps.num_vsis; v++) {
+       for (v = 0; v < pf->num_alloc_vsi; v++) {
                struct i40e_vsi *vsi = pf->vsi[v];
 
-               if (!vsi)
+               if (!vsi || !vsi->tx_rings[0])
                        continue;
 
                for (i = 0; i < vsi->num_queue_pairs; i++) {
@@ -702,10 +706,10 @@ static void i40e_update_prio_xoff_rx(struct i40e_pf *pf)
        }
 
        /* Clear the __I40E_HANG_CHECK_ARMED bit for Tx rings */
-       for (v = 0; v < pf->hw.func_caps.num_vsis; v++) {
+       for (v = 0; v < pf->num_alloc_vsi; v++) {
                struct i40e_vsi *vsi = pf->vsi[v];
 
-               if (!vsi)
+               if (!vsi || !vsi->tx_rings[0])
                        continue;
 
                for (i = 0; i < vsi->num_queue_pairs; i++) {
@@ -720,19 +724,18 @@ static void i40e_update_prio_xoff_rx(struct i40e_pf *pf)
 }
 
 /**
- * i40e_update_stats - Update the board statistics counters.
+ * i40e_update_vsi_stats - Update the vsi statistics counters.
  * @vsi: the VSI to be updated
  *
  * There are a few instances where we store the same stat in a
  * couple of different structs.  This is partly because we have
  * the netdev stats that need to be filled out, which is slightly
  * different from the "eth_stats" defined by the chip and used in
- * VF communications.  We sort it all out here in a central place.
+ * VF communications.  We sort it out here.
  **/
-void i40e_update_stats(struct i40e_vsi *vsi)
+static void i40e_update_vsi_stats(struct i40e_vsi *vsi)
 {
        struct i40e_pf *pf = vsi->back;
-       struct i40e_hw *hw = &pf->hw;
        struct rtnl_link_stats64 *ons;
        struct rtnl_link_stats64 *ns;   /* netdev stats */
        struct i40e_eth_stats *oes;
@@ -741,8 +744,6 @@ void i40e_update_stats(struct i40e_vsi *vsi)
        u32 rx_page, rx_buf;
        u64 rx_p, rx_b;
        u64 tx_p, tx_b;
-       u32 val;
-       int i;
        u16 q;
 
        if (test_bit(__I40E_DOWN, &vsi->state) ||
@@ -804,195 +805,255 @@ void i40e_update_stats(struct i40e_vsi *vsi)
        ns->tx_packets = tx_p;
        ns->tx_bytes = tx_b;
 
-       i40e_update_eth_stats(vsi);
        /* update netdev stats from eth stats */
-       ons->rx_errors = oes->rx_errors;
-       ns->rx_errors = es->rx_errors;
+       i40e_update_eth_stats(vsi);
        ons->tx_errors = oes->tx_errors;
        ns->tx_errors = es->tx_errors;
        ons->multicast = oes->rx_multicast;
        ns->multicast = es->rx_multicast;
+       ons->rx_dropped = oes->rx_discards;
+       ns->rx_dropped = es->rx_discards;
        ons->tx_dropped = oes->tx_discards;
        ns->tx_dropped = es->tx_discards;
 
-       /* Get the port data only if this is the main PF VSI */
+       /* pull in a couple PF stats if this is the main vsi */
        if (vsi == pf->vsi[pf->lan_vsi]) {
-               struct i40e_hw_port_stats *nsd = &pf->stats;
-               struct i40e_hw_port_stats *osd = &pf->stats_offsets;
+               ns->rx_crc_errors = pf->stats.crc_errors;
+               ns->rx_errors = pf->stats.crc_errors + pf->stats.illegal_bytes;
+               ns->rx_length_errors = pf->stats.rx_length_errors;
+       }
+}
 
-               i40e_stat_update48(hw, I40E_GLPRT_GORCH(hw->port),
-                                  I40E_GLPRT_GORCL(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->eth.rx_bytes, &nsd->eth.rx_bytes);
-               i40e_stat_update48(hw, I40E_GLPRT_GOTCH(hw->port),
-                                  I40E_GLPRT_GOTCL(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->eth.tx_bytes, &nsd->eth.tx_bytes);
-               i40e_stat_update32(hw, I40E_GLPRT_RDPC(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->eth.rx_discards,
-                                  &nsd->eth.rx_discards);
-               i40e_stat_update32(hw, I40E_GLPRT_TDPC(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->eth.tx_discards,
-                                  &nsd->eth.tx_discards);
-               i40e_stat_update48(hw, I40E_GLPRT_MPRCH(hw->port),
-                                  I40E_GLPRT_MPRCL(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->eth.rx_multicast,
-                                  &nsd->eth.rx_multicast);
+/**
+ * i40e_update_pf_stats - Update the pf statistics counters.
+ * @pf: the PF to be updated
+ **/
+static void i40e_update_pf_stats(struct i40e_pf *pf)
+{
+       struct i40e_hw_port_stats *osd = &pf->stats_offsets;
+       struct i40e_hw_port_stats *nsd = &pf->stats;
+       struct i40e_hw *hw = &pf->hw;
+       u32 val;
+       int i;
 
-               i40e_stat_update32(hw, I40E_GLPRT_TDOLD(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->tx_dropped_link_down,
-                                  &nsd->tx_dropped_link_down);
+       i40e_stat_update48(hw, I40E_GLPRT_GORCH(hw->port),
+                          I40E_GLPRT_GORCL(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->eth.rx_bytes, &nsd->eth.rx_bytes);
+       i40e_stat_update48(hw, I40E_GLPRT_GOTCH(hw->port),
+                          I40E_GLPRT_GOTCL(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->eth.tx_bytes, &nsd->eth.tx_bytes);
+       i40e_stat_update32(hw, I40E_GLPRT_RDPC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->eth.rx_discards,
+                          &nsd->eth.rx_discards);
+       i40e_stat_update32(hw, I40E_GLPRT_TDPC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->eth.tx_discards,
+                          &nsd->eth.tx_discards);
 
-               i40e_stat_update32(hw, I40E_GLPRT_CRCERRS(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->crc_errors, &nsd->crc_errors);
-               ns->rx_crc_errors = nsd->crc_errors;
+       i40e_stat_update48(hw, I40E_GLPRT_UPRCH(hw->port),
+                          I40E_GLPRT_UPRCL(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->eth.rx_unicast,
+                          &nsd->eth.rx_unicast);
+       i40e_stat_update48(hw, I40E_GLPRT_MPRCH(hw->port),
+                          I40E_GLPRT_MPRCL(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->eth.rx_multicast,
+                          &nsd->eth.rx_multicast);
+       i40e_stat_update48(hw, I40E_GLPRT_BPRCH(hw->port),
+                          I40E_GLPRT_BPRCL(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->eth.rx_broadcast,
+                          &nsd->eth.rx_broadcast);
+       i40e_stat_update48(hw, I40E_GLPRT_UPTCH(hw->port),
+                          I40E_GLPRT_UPTCL(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->eth.tx_unicast,
+                          &nsd->eth.tx_unicast);
+       i40e_stat_update48(hw, I40E_GLPRT_MPTCH(hw->port),
+                          I40E_GLPRT_MPTCL(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->eth.tx_multicast,
+                          &nsd->eth.tx_multicast);
+       i40e_stat_update48(hw, I40E_GLPRT_BPTCH(hw->port),
+                          I40E_GLPRT_BPTCL(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->eth.tx_broadcast,
+                          &nsd->eth.tx_broadcast);
 
-               i40e_stat_update32(hw, I40E_GLPRT_ILLERRC(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->illegal_bytes, &nsd->illegal_bytes);
-               ns->rx_errors = nsd->crc_errors
-                               + nsd->illegal_bytes;
+       i40e_stat_update32(hw, I40E_GLPRT_TDOLD(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->tx_dropped_link_down,
+                          &nsd->tx_dropped_link_down);
 
-               i40e_stat_update32(hw, I40E_GLPRT_MLFC(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->mac_local_faults,
-                                  &nsd->mac_local_faults);
-               i40e_stat_update32(hw, I40E_GLPRT_MRFC(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->mac_remote_faults,
-                                  &nsd->mac_remote_faults);
+       i40e_stat_update32(hw, I40E_GLPRT_CRCERRS(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->crc_errors, &nsd->crc_errors);
 
-               i40e_stat_update32(hw, I40E_GLPRT_RLEC(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->rx_length_errors,
-                                  &nsd->rx_length_errors);
-               ns->rx_length_errors = nsd->rx_length_errors;
+       i40e_stat_update32(hw, I40E_GLPRT_ILLERRC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->illegal_bytes, &nsd->illegal_bytes);
 
-               i40e_stat_update32(hw, I40E_GLPRT_LXONRXC(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->link_xon_rx, &nsd->link_xon_rx);
-               i40e_stat_update32(hw, I40E_GLPRT_LXONTXC(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->link_xon_tx, &nsd->link_xon_tx);
-               i40e_update_prio_xoff_rx(pf);  /* handles I40E_GLPRT_LXOFFRXC */
-               i40e_stat_update32(hw, I40E_GLPRT_LXOFFTXC(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->link_xoff_tx, &nsd->link_xoff_tx);
-
-               for (i = 0; i < 8; i++) {
-                       i40e_stat_update32(hw, I40E_GLPRT_PXONRXC(hw->port, i),
-                                          pf->stat_offsets_loaded,
-                                          &osd->priority_xon_rx[i],
-                                          &nsd->priority_xon_rx[i]);
-                       i40e_stat_update32(hw, I40E_GLPRT_PXONTXC(hw->port, i),
-                                          pf->stat_offsets_loaded,
-                                          &osd->priority_xon_tx[i],
-                                          &nsd->priority_xon_tx[i]);
-                       i40e_stat_update32(hw, I40E_GLPRT_PXOFFTXC(hw->port, i),
-                                          pf->stat_offsets_loaded,
-                                          &osd->priority_xoff_tx[i],
-                                          &nsd->priority_xoff_tx[i]);
-                       i40e_stat_update32(hw,
-                                          I40E_GLPRT_RXON2OFFCNT(hw->port, i),
-                                          pf->stat_offsets_loaded,
-                                          &osd->priority_xon_2_xoff[i],
-                                          &nsd->priority_xon_2_xoff[i]);
-               }
+       i40e_stat_update32(hw, I40E_GLPRT_MLFC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->mac_local_faults,
+                          &nsd->mac_local_faults);
+       i40e_stat_update32(hw, I40E_GLPRT_MRFC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->mac_remote_faults,
+                          &nsd->mac_remote_faults);
 
-               i40e_stat_update48(hw, I40E_GLPRT_PRC64H(hw->port),
-                                  I40E_GLPRT_PRC64L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->rx_size_64, &nsd->rx_size_64);
-               i40e_stat_update48(hw, I40E_GLPRT_PRC127H(hw->port),
-                                  I40E_GLPRT_PRC127L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->rx_size_127, &nsd->rx_size_127);
-               i40e_stat_update48(hw, I40E_GLPRT_PRC255H(hw->port),
-                                  I40E_GLPRT_PRC255L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->rx_size_255, &nsd->rx_size_255);
-               i40e_stat_update48(hw, I40E_GLPRT_PRC511H(hw->port),
-                                  I40E_GLPRT_PRC511L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->rx_size_511, &nsd->rx_size_511);
-               i40e_stat_update48(hw, I40E_GLPRT_PRC1023H(hw->port),
-                                  I40E_GLPRT_PRC1023L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->rx_size_1023, &nsd->rx_size_1023);
-               i40e_stat_update48(hw, I40E_GLPRT_PRC1522H(hw->port),
-                                  I40E_GLPRT_PRC1522L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->rx_size_1522, &nsd->rx_size_1522);
-               i40e_stat_update48(hw, I40E_GLPRT_PRC9522H(hw->port),
-                                  I40E_GLPRT_PRC9522L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->rx_size_big, &nsd->rx_size_big);
+       i40e_stat_update32(hw, I40E_GLPRT_RLEC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_length_errors,
+                          &nsd->rx_length_errors);
 
-               i40e_stat_update48(hw, I40E_GLPRT_PTC64H(hw->port),
-                                  I40E_GLPRT_PTC64L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->tx_size_64, &nsd->tx_size_64);
-               i40e_stat_update48(hw, I40E_GLPRT_PTC127H(hw->port),
-                                  I40E_GLPRT_PTC127L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->tx_size_127, &nsd->tx_size_127);
-               i40e_stat_update48(hw, I40E_GLPRT_PTC255H(hw->port),
-                                  I40E_GLPRT_PTC255L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->tx_size_255, &nsd->tx_size_255);
-               i40e_stat_update48(hw, I40E_GLPRT_PTC511H(hw->port),
-                                  I40E_GLPRT_PTC511L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->tx_size_511, &nsd->tx_size_511);
-               i40e_stat_update48(hw, I40E_GLPRT_PTC1023H(hw->port),
-                                  I40E_GLPRT_PTC1023L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->tx_size_1023, &nsd->tx_size_1023);
-               i40e_stat_update48(hw, I40E_GLPRT_PTC1522H(hw->port),
-                                  I40E_GLPRT_PTC1522L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->tx_size_1522, &nsd->tx_size_1522);
-               i40e_stat_update48(hw, I40E_GLPRT_PTC9522H(hw->port),
-                                  I40E_GLPRT_PTC9522L(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->tx_size_big, &nsd->tx_size_big);
+       i40e_stat_update32(hw, I40E_GLPRT_LXONRXC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->link_xon_rx, &nsd->link_xon_rx);
+       i40e_stat_update32(hw, I40E_GLPRT_LXONTXC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->link_xon_tx, &nsd->link_xon_tx);
+       i40e_update_prio_xoff_rx(pf);  /* handles I40E_GLPRT_LXOFFRXC */
+       i40e_stat_update32(hw, I40E_GLPRT_LXOFFTXC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->link_xoff_tx, &nsd->link_xoff_tx);
 
-               i40e_stat_update32(hw, I40E_GLPRT_RUC(hw->port),
-                                  pf->stat_offsets_loaded,
-                                  &osd->rx_undersize, &nsd->rx_undersize);
-               i40e_stat_update32(hw, I40E_GLPRT_RFC(hw->port),
+       for (i = 0; i < 8; i++) {
+               i40e_stat_update32(hw, I40E_GLPRT_PXONRXC(hw->port, i),
                                   pf->stat_offsets_loaded,
-                                  &osd->rx_fragments, &nsd->rx_fragments);
-               i40e_stat_update32(hw, I40E_GLPRT_ROC(hw->port),
+                                  &osd->priority_xon_rx[i],
+                                  &nsd->priority_xon_rx[i]);
+               i40e_stat_update32(hw, I40E_GLPRT_PXONTXC(hw->port, i),
                                   pf->stat_offsets_loaded,
-                                  &osd->rx_oversize, &nsd->rx_oversize);
-               i40e_stat_update32(hw, I40E_GLPRT_RJC(hw->port),
+                                  &osd->priority_xon_tx[i],
+                                  &nsd->priority_xon_tx[i]);
+               i40e_stat_update32(hw, I40E_GLPRT_PXOFFTXC(hw->port, i),
                                   pf->stat_offsets_loaded,
-                                  &osd->rx_jabber, &nsd->rx_jabber);
-
-               val = rd32(hw, I40E_PRTPM_EEE_STAT);
-               nsd->tx_lpi_status =
-                              (val & I40E_PRTPM_EEE_STAT_TX_LPI_STATUS_MASK) >>
-                               I40E_PRTPM_EEE_STAT_TX_LPI_STATUS_SHIFT;
-               nsd->rx_lpi_status =
-                              (val & I40E_PRTPM_EEE_STAT_RX_LPI_STATUS_MASK) >>
-                               I40E_PRTPM_EEE_STAT_RX_LPI_STATUS_SHIFT;
-               i40e_stat_update32(hw, I40E_PRTPM_TLPIC,
+                                  &osd->priority_xoff_tx[i],
+                                  &nsd->priority_xoff_tx[i]);
+               i40e_stat_update32(hw,
+                                  I40E_GLPRT_RXON2OFFCNT(hw->port, i),
                                   pf->stat_offsets_loaded,
-                                  &osd->tx_lpi_count, &nsd->tx_lpi_count);
-               i40e_stat_update32(hw, I40E_PRTPM_RLPIC,
-                                  pf->stat_offsets_loaded,
-                                  &osd->rx_lpi_count, &nsd->rx_lpi_count);
+                                  &osd->priority_xon_2_xoff[i],
+                                  &nsd->priority_xon_2_xoff[i]);
        }
 
+       i40e_stat_update48(hw, I40E_GLPRT_PRC64H(hw->port),
+                          I40E_GLPRT_PRC64L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_size_64, &nsd->rx_size_64);
+       i40e_stat_update48(hw, I40E_GLPRT_PRC127H(hw->port),
+                          I40E_GLPRT_PRC127L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_size_127, &nsd->rx_size_127);
+       i40e_stat_update48(hw, I40E_GLPRT_PRC255H(hw->port),
+                          I40E_GLPRT_PRC255L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_size_255, &nsd->rx_size_255);
+       i40e_stat_update48(hw, I40E_GLPRT_PRC511H(hw->port),
+                          I40E_GLPRT_PRC511L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_size_511, &nsd->rx_size_511);
+       i40e_stat_update48(hw, I40E_GLPRT_PRC1023H(hw->port),
+                          I40E_GLPRT_PRC1023L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_size_1023, &nsd->rx_size_1023);
+       i40e_stat_update48(hw, I40E_GLPRT_PRC1522H(hw->port),
+                          I40E_GLPRT_PRC1522L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_size_1522, &nsd->rx_size_1522);
+       i40e_stat_update48(hw, I40E_GLPRT_PRC9522H(hw->port),
+                          I40E_GLPRT_PRC9522L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_size_big, &nsd->rx_size_big);
+
+       i40e_stat_update48(hw, I40E_GLPRT_PTC64H(hw->port),
+                          I40E_GLPRT_PTC64L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->tx_size_64, &nsd->tx_size_64);
+       i40e_stat_update48(hw, I40E_GLPRT_PTC127H(hw->port),
+                          I40E_GLPRT_PTC127L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->tx_size_127, &nsd->tx_size_127);
+       i40e_stat_update48(hw, I40E_GLPRT_PTC255H(hw->port),
+                          I40E_GLPRT_PTC255L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->tx_size_255, &nsd->tx_size_255);
+       i40e_stat_update48(hw, I40E_GLPRT_PTC511H(hw->port),
+                          I40E_GLPRT_PTC511L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->tx_size_511, &nsd->tx_size_511);
+       i40e_stat_update48(hw, I40E_GLPRT_PTC1023H(hw->port),
+                          I40E_GLPRT_PTC1023L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->tx_size_1023, &nsd->tx_size_1023);
+       i40e_stat_update48(hw, I40E_GLPRT_PTC1522H(hw->port),
+                          I40E_GLPRT_PTC1522L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->tx_size_1522, &nsd->tx_size_1522);
+       i40e_stat_update48(hw, I40E_GLPRT_PTC9522H(hw->port),
+                          I40E_GLPRT_PTC9522L(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->tx_size_big, &nsd->tx_size_big);
+
+       i40e_stat_update32(hw, I40E_GLPRT_RUC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_undersize, &nsd->rx_undersize);
+       i40e_stat_update32(hw, I40E_GLPRT_RFC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_fragments, &nsd->rx_fragments);
+       i40e_stat_update32(hw, I40E_GLPRT_ROC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_oversize, &nsd->rx_oversize);
+       i40e_stat_update32(hw, I40E_GLPRT_RJC(hw->port),
+                          pf->stat_offsets_loaded,
+                          &osd->rx_jabber, &nsd->rx_jabber);
+
+       /* FDIR stats */
+       i40e_stat_update32(hw, I40E_GLQF_PCNT(pf->fd_atr_cnt_idx),
+                          pf->stat_offsets_loaded,
+                          &osd->fd_atr_match, &nsd->fd_atr_match);
+       i40e_stat_update32(hw, I40E_GLQF_PCNT(pf->fd_sb_cnt_idx),
+                          pf->stat_offsets_loaded,
+                          &osd->fd_sb_match, &nsd->fd_sb_match);
+
+       val = rd32(hw, I40E_PRTPM_EEE_STAT);
+       nsd->tx_lpi_status =
+                      (val & I40E_PRTPM_EEE_STAT_TX_LPI_STATUS_MASK) >>
+                       I40E_PRTPM_EEE_STAT_TX_LPI_STATUS_SHIFT;
+       nsd->rx_lpi_status =
+                      (val & I40E_PRTPM_EEE_STAT_RX_LPI_STATUS_MASK) >>
+                       I40E_PRTPM_EEE_STAT_RX_LPI_STATUS_SHIFT;
+       i40e_stat_update32(hw, I40E_PRTPM_TLPIC,
+                          pf->stat_offsets_loaded,
+                          &osd->tx_lpi_count, &nsd->tx_lpi_count);
+       i40e_stat_update32(hw, I40E_PRTPM_RLPIC,
+                          pf->stat_offsets_loaded,
+                          &osd->rx_lpi_count, &nsd->rx_lpi_count);
+
        pf->stat_offsets_loaded = true;
 }
 
+/**
+ * i40e_update_stats - Update the various statistics counters.
+ * @vsi: the VSI to be updated
+ *
+ * Update the various stats for this VSI and its related entities.
+ **/
+void i40e_update_stats(struct i40e_vsi *vsi)
+{
+       struct i40e_pf *pf = vsi->back;
+
+       if (vsi == pf->vsi[pf->lan_vsi])
+               i40e_update_pf_stats(pf);
+
+       i40e_update_vsi_stats(vsi);
+}
+
 /**
  * i40e_find_filter - Search VSI filter list for specific mac/vlan filter
  * @vsi: the VSI to be searched
@@ -1100,6 +1161,30 @@ struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, u8 *macaddr,
                                        struct i40e_mac_filter, list);
 }
 
+/**
+ * i40e_rm_default_mac_filter - Remove the default MAC filter set by NVM
+ * @vsi: the PF Main VSI - inappropriate for any other VSI
+ * @macaddr: the MAC address
+ **/
+static void i40e_rm_default_mac_filter(struct i40e_vsi *vsi, u8 *macaddr)
+{
+       struct i40e_aqc_remove_macvlan_element_data element;
+       struct i40e_pf *pf = vsi->back;
+       i40e_status aq_ret;
+
+       /* Only appropriate for the PF main VSI */
+       if (vsi->type != I40E_VSI_MAIN)
+               return;
+
+       ether_addr_copy(element.mac_addr, macaddr);
+       element.vlan_tag = 0;
+       element.flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH |
+                       I40E_AQC_MACVLAN_DEL_IGNORE_VLAN;
+       aq_ret = i40e_aq_remove_macvlan(&pf->hw, vsi->seid, &element, 1, NULL);
+       if (aq_ret)
+               dev_err(&pf->pdev->dev, "Could not remove default MAC-VLAN\n");
+}
+
 /**
  * i40e_add_filter - Add a mac/vlan filter to the VSI
  * @vsi: the VSI to be searched
@@ -1125,7 +1210,7 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi,
                if (!f)
                        goto add_filter_out;
 
-               memcpy(f->macaddr, macaddr, ETH_ALEN);
+               ether_addr_copy(f->macaddr, macaddr);
                f->vlan = vlan;
                f->changed = true;
 
@@ -1249,7 +1334,7 @@ static int i40e_set_mac(struct net_device *netdev, void *p)
                        return -EADDRNOTAVAIL;
                }
 
-               memcpy(vsi->back->hw.mac.addr, addr->sa_data, netdev->addr_len);
+               ether_addr_copy(vsi->back->hw.mac.addr, addr->sa_data);
        }
 
        /* In order to be sure to not drop any packets, add the new address
@@ -1263,7 +1348,7 @@ static int i40e_set_mac(struct net_device *netdev, void *p)
        i40e_del_filter(vsi, netdev->dev_addr, I40E_VLAN_ANY, false, false);
        i40e_sync_vsi_filters(vsi);
 
-       memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
+       ether_addr_copy(netdev->dev_addr, addr->sa_data);
 
        return 0;
 }
@@ -1313,7 +1398,7 @@ static void i40e_vsi_setup_queue_map(struct i40e_vsi *vsi,
        vsi->tc_config.numtc = numtc;
        vsi->tc_config.enabled_tc = enabled_tc ? enabled_tc : 1;
        /* Number of queues per enabled TC */
-       num_tc_qps = rounddown_pow_of_two(vsi->alloc_queue_pairs/numtc);
+       num_tc_qps = vsi->alloc_queue_pairs/numtc;
        num_tc_qps = min_t(int, num_tc_qps, I40E_MAX_QUEUES_PER_TC);
 
        /* Setup queue offset/count for all TCs for given VSI */
@@ -1520,8 +1605,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
                        cmd_flags = 0;
 
                        /* add to delete list */
-                       memcpy(del_list[num_del].mac_addr,
-                              f->macaddr, ETH_ALEN);
+                       ether_addr_copy(del_list[num_del].mac_addr, f->macaddr);
                        del_list[num_del].vlan_tag =
                                cpu_to_le16((u16)(f->vlan ==
                                            I40E_VLAN_ANY ? 0 : f->vlan));
@@ -1542,7 +1626,9 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
                                num_del = 0;
                                memset(del_list, 0, sizeof(*del_list));
 
-                               if (aq_ret)
+                               if (aq_ret &&
+                                   pf->hw.aq.asq_last_status !=
+                                                             I40E_AQ_RC_ENOENT)
                                        dev_info(&pf->pdev->dev,
                                                 "ignoring delete macvlan error, err %d, aq_err %d while flushing a full buffer\n",
                                                 aq_ret,
@@ -1554,7 +1640,8 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
                                                     del_list, num_del, NULL);
                        num_del = 0;
 
-                       if (aq_ret)
+                       if (aq_ret &&
+                           pf->hw.aq.asq_last_status != I40E_AQ_RC_ENOENT)
                                dev_info(&pf->pdev->dev,
                                         "ignoring delete macvlan error, err %d, aq_err %d\n",
                                         aq_ret, pf->hw.aq.asq_last_status);
@@ -1583,8 +1670,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
                        cmd_flags = 0;
 
                        /* add to add array */
-                       memcpy(add_list[num_add].mac_addr,
-                              f->macaddr, ETH_ALEN);
+                       ether_addr_copy(add_list[num_add].mac_addr, f->macaddr);
                        add_list[num_add].vlan_tag =
                                cpu_to_le16(
                                 (u16)(f->vlan == I40E_VLAN_ANY ? 0 : f->vlan));
@@ -1681,7 +1767,7 @@ static void i40e_sync_filters_subtask(struct i40e_pf *pf)
                return;
        pf->flags &= ~I40E_FLAG_FILTER_SYNC;
 
-       for (v = 0; v < pf->hw.func_caps.num_vsis; v++) {
+       for (v = 0; v < pf->num_alloc_vsi; v++) {
                if (pf->vsi[v] &&
                    (pf->vsi[v]->flags & I40E_VSI_FLAG_FILTER_CHANGED))
                        i40e_sync_vsi_filters(pf->vsi[v]);
@@ -1698,7 +1784,7 @@ static void i40e_sync_filters_subtask(struct i40e_pf *pf)
 static int i40e_change_mtu(struct net_device *netdev, int new_mtu)
 {
        struct i40e_netdev_priv *np = netdev_priv(netdev);
-       int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN;
+       int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
        struct i40e_vsi *vsi = np->vsi;
 
        /* MTU < 68 is an error and causes problems on some kernels */
@@ -2312,6 +2398,8 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring)
        rx_ctx.crcstrip = 1;
        rx_ctx.l2tsel = 1;
        rx_ctx.showiv = 1;
+       /* set the prefena field to 1 because the manual says to */
+       rx_ctx.prefena = 1;
 
        /* clear the context in the HMC */
        err = i40e_clear_lan_rx_queue_context(hw, pf_q);
@@ -2413,6 +2501,7 @@ static int i40e_vsi_configure_rx(struct i40e_vsi *vsi)
  **/
 static void i40e_vsi_config_dcb_rings(struct i40e_vsi *vsi)
 {
+       struct i40e_ring *tx_ring, *rx_ring;
        u16 qoffset, qcount;
        int i, n;
 
@@ -2426,8 +2515,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];
+                       rx_ring = vsi->rx_rings[i];
+                       tx_ring = vsi->tx_rings[i];
                        rx_ring->dcb_tc = n;
                        tx_ring->dcb_tc = n;
                }
@@ -2565,7 +2654,6 @@ static void i40e_enable_misc_int_causes(struct i40e_hw *hw)
              I40E_PFINT_ICR0_ENA_PCI_EXCEPTION_MASK |
              I40E_PFINT_ICR0_ENA_GPIO_MASK          |
              I40E_PFINT_ICR0_ENA_TIMESYNC_MASK      |
-             I40E_PFINT_ICR0_ENA_STORM_DETECT_MASK  |
              I40E_PFINT_ICR0_ENA_HMC_ERR_MASK       |
              I40E_PFINT_ICR0_ENA_VFLR_MASK          |
              I40E_PFINT_ICR0_ENA_ADMINQ_MASK;
@@ -2733,6 +2821,7 @@ static int i40e_vsi_request_irq_msix(struct i40e_vsi *vsi, char *basename)
                                      &q_vector->affinity_mask);
        }
 
+       vsi->irqs_ready = true;
        return 0;
 
 free_queue_irqs:
@@ -3152,6 +3241,12 @@ static int i40e_vsi_control_tx(struct i40e_vsi *vsi, bool enable)
 
        pf_q = vsi->base_queue;
        for (i = 0; i < vsi->num_queue_pairs; i++, pf_q++) {
+
+               /* warn the TX unit of coming changes */
+               i40e_pre_tx_queue_cfg(&pf->hw, pf_q, enable);
+               if (!enable)
+                       udelay(10);
+
                for (j = 0; j < 50; j++) {
                        tx_reg = rd32(hw, I40E_QTX_ENA(pf_q));
                        if (((tx_reg >> I40E_QTX_ENA_QENA_REQ_SHIFT) & 1) ==
@@ -3160,9 +3255,7 @@ static int i40e_vsi_control_tx(struct i40e_vsi *vsi, bool enable)
                        usleep_range(1000, 2000);
                }
                /* Skip if the queue is already in the requested state */
-               if (enable && (tx_reg & I40E_QTX_ENA_QENA_STAT_MASK))
-                       continue;
-               if (!enable && !(tx_reg & I40E_QTX_ENA_QENA_STAT_MASK))
+               if (enable == !!(tx_reg & I40E_QTX_ENA_QENA_STAT_MASK))
                        continue;
 
                /* turn on/off the queue */
@@ -3178,13 +3271,8 @@ static int i40e_vsi_control_tx(struct i40e_vsi *vsi, bool enable)
                /* wait for the change to finish */
                for (j = 0; j < 10; j++) {
                        tx_reg = rd32(hw, I40E_QTX_ENA(pf_q));
-                       if (enable) {
-                               if ((tx_reg & I40E_QTX_ENA_QENA_STAT_MASK))
-                                       break;
-                       } else {
-                               if (!(tx_reg & I40E_QTX_ENA_QENA_STAT_MASK))
-                                       break;
-                       }
+                       if (enable == !!(tx_reg & I40E_QTX_ENA_QENA_STAT_MASK))
+                               break;
 
                        udelay(10);
                }
@@ -3223,15 +3311,9 @@ static int i40e_vsi_control_rx(struct i40e_vsi *vsi, bool enable)
                        usleep_range(1000, 2000);
                }
 
-               if (enable) {
-                       /* is STAT set ? */
-                       if ((rx_reg & I40E_QRX_ENA_QENA_STAT_MASK))
-                               continue;
-               } else {
-                       /* is !STAT set ? */
-                       if (!(rx_reg & I40E_QRX_ENA_QENA_STAT_MASK))
-                               continue;
-               }
+               /* Skip if the queue is already in the requested state */
+               if (enable == !!(rx_reg & I40E_QRX_ENA_QENA_STAT_MASK))
+                       continue;
 
                /* turn on/off the queue */
                if (enable)
@@ -3244,13 +3326,8 @@ static int i40e_vsi_control_rx(struct i40e_vsi *vsi, bool enable)
                for (j = 0; j < 10; j++) {
                        rx_reg = rd32(hw, I40E_QRX_ENA(pf_q));
 
-                       if (enable) {
-                               if ((rx_reg & I40E_QRX_ENA_QENA_STAT_MASK))
-                                       break;
-                       } else {
-                               if (!(rx_reg & I40E_QRX_ENA_QENA_STAT_MASK))
-                                       break;
-                       }
+                       if (enable == !!(rx_reg & I40E_QRX_ENA_QENA_STAT_MASK))
+                               break;
 
                        udelay(10);
                }
@@ -3304,6 +3381,10 @@ static void i40e_vsi_free_irq(struct i40e_vsi *vsi)
                if (!vsi->q_vectors)
                        return;
 
+               if (!vsi->irqs_ready)
+                       return;
+
+               vsi->irqs_ready = false;
                for (i = 0; i < vsi->num_q_vectors; i++) {
                        u16 vector = i + base;
 
@@ -3476,7 +3557,7 @@ static void i40e_clear_interrupt_scheme(struct i40e_pf *pf)
        int i;
 
        i40e_put_lump(pf->irq_pile, 0, I40E_PILE_VALID_BIT-1);
-       for (i = 0; i < pf->hw.func_caps.num_vsis; i++)
+       for (i = 0; i < pf->num_alloc_vsi; i++)
                if (pf->vsi[i])
                        i40e_vsi_free_q_vectors(pf->vsi[i]);
        i40e_reset_interrupt_capability(pf);
@@ -3512,6 +3593,19 @@ static void i40e_napi_disable_all(struct i40e_vsi *vsi)
                napi_disable(&vsi->q_vectors[q_idx]->napi);
 }
 
+/**
+ * i40e_vsi_close - Shut down a VSI
+ * @vsi: the vsi to be quelled
+ **/
+static void i40e_vsi_close(struct i40e_vsi *vsi)
+{
+       if (!test_and_set_bit(__I40E_DOWN, &vsi->state))
+               i40e_down(vsi);
+       i40e_vsi_free_irq(vsi);
+       i40e_vsi_free_tx_resources(vsi);
+       i40e_vsi_free_rx_resources(vsi);
+}
+
 /**
  * i40e_quiesce_vsi - Pause a given VSI
  * @vsi: the VSI being paused
@@ -3525,8 +3619,7 @@ static void i40e_quiesce_vsi(struct i40e_vsi *vsi)
        if (vsi->netdev && netif_running(vsi->netdev)) {
                vsi->netdev->netdev_ops->ndo_stop(vsi->netdev);
        } else {
-               set_bit(__I40E_DOWN, &vsi->state);
-               i40e_down(vsi);
+               i40e_vsi_close(vsi);
        }
 }
 
@@ -3543,7 +3636,7 @@ static void i40e_unquiesce_vsi(struct i40e_vsi *vsi)
        if (vsi->netdev && netif_running(vsi->netdev))
                vsi->netdev->netdev_ops->ndo_open(vsi->netdev);
        else
-               i40e_up(vsi);   /* this clears the DOWN bit */
+               i40e_vsi_open(vsi);   /* this clears the DOWN bit */
 }
 
 /**
@@ -3554,7 +3647,7 @@ static void i40e_pf_quiesce_all_vsi(struct i40e_pf *pf)
 {
        int v;
 
-       for (v = 0; v < pf->hw.func_caps.num_vsis; v++) {
+       for (v = 0; v < pf->num_alloc_vsi; v++) {
                if (pf->vsi[v])
                        i40e_quiesce_vsi(pf->vsi[v]);
        }
@@ -3568,7 +3661,7 @@ static void i40e_pf_unquiesce_all_vsi(struct i40e_pf *pf)
 {
        int v;
 
-       for (v = 0; v < pf->hw.func_caps.num_vsis; v++) {
+       for (v = 0; v < pf->num_alloc_vsi; v++) {
                if (pf->vsi[v])
                        i40e_unquiesce_vsi(pf->vsi[v]);
        }
@@ -4009,7 +4102,7 @@ static void i40e_dcb_reconfigure(struct i40e_pf *pf)
        }
 
        /* Update each VSI */
-       for (v = 0; v < pf->hw.func_caps.num_vsis; v++) {
+       for (v = 0; v < pf->num_alloc_vsi; v++) {
                if (!pf->vsi[v])
                        continue;
 
@@ -4028,6 +4121,8 @@ static void i40e_dcb_reconfigure(struct i40e_pf *pf)
                                 pf->vsi[v]->seid);
                        /* Will try to configure as many components */
                } else {
+                       /* Re-configure VSI vectors based on updated TC map */
+                       i40e_vsi_map_rings_to_vectors(pf->vsi[v]);
                        if (pf->vsi[v]->netdev)
                                i40e_dcbnl_set_all(pf->vsi[v]);
                }
@@ -4065,14 +4160,69 @@ static int i40e_init_pf_dcb(struct i40e_pf *pf)
                        /* When status is not DISABLED then DCBX in FW */
                        pf->dcbx_cap = DCB_CAP_DCBX_LLD_MANAGED |
                                       DCB_CAP_DCBX_VER_IEEE;
-                       pf->flags |= I40E_FLAG_DCB_ENABLED;
+
+                       pf->flags |= I40E_FLAG_DCB_CAPABLE;
+                       /* Enable DCB tagging only when more than one TC */
+                       if (i40e_dcb_get_num_tc(&hw->local_dcbx_config) > 1)
+                               pf->flags |= I40E_FLAG_DCB_ENABLED;
                }
+       } else {
+               dev_info(&pf->pdev->dev, "AQ Querying DCB configuration failed: %d\n",
+                        pf->hw.aq.asq_last_status);
        }
 
 out:
        return err;
 }
 #endif /* CONFIG_I40E_DCB */
+#define SPEED_SIZE 14
+#define FC_SIZE 8
+/**
+ * i40e_print_link_message - print link up or down
+ * @vsi: the VSI for which link needs a message
+ */
+static void i40e_print_link_message(struct i40e_vsi *vsi, bool isup)
+{
+       char speed[SPEED_SIZE] = "Unknown";
+       char fc[FC_SIZE] = "RX/TX";
+
+       if (!isup) {
+               netdev_info(vsi->netdev, "NIC Link is Down\n");
+               return;
+       }
+
+       switch (vsi->back->hw.phy.link_info.link_speed) {
+       case I40E_LINK_SPEED_40GB:
+               strncpy(speed, "40 Gbps", SPEED_SIZE);
+               break;
+       case I40E_LINK_SPEED_10GB:
+               strncpy(speed, "10 Gbps", SPEED_SIZE);
+               break;
+       case I40E_LINK_SPEED_1GB:
+               strncpy(speed, "1000 Mbps", SPEED_SIZE);
+               break;
+       default:
+               break;
+       }
+
+       switch (vsi->back->hw.fc.current_mode) {
+       case I40E_FC_FULL:
+               strncpy(fc, "RX/TX", FC_SIZE);
+               break;
+       case I40E_FC_TX_PAUSE:
+               strncpy(fc, "TX", FC_SIZE);
+               break;
+       case I40E_FC_RX_PAUSE:
+               strncpy(fc, "RX", FC_SIZE);
+               break;
+       default:
+               strncpy(fc, "None", FC_SIZE);
+               break;
+       }
+
+       netdev_info(vsi->netdev, "NIC Link is Up %s Full Duplex, Flow Control: %s\n",
+                   speed, fc);
+}
 
 /**
  * i40e_up_complete - Finish the last steps of bringing up a connection
@@ -4099,11 +4249,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");
+               i40e_print_link_message(vsi, true);
                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_print_link_message(vsi, false);
        }
 
        /* replay FDIR SB filters */
@@ -4309,24 +4459,32 @@ int i40e_vsi_open(struct i40e_vsi *vsi)
        if (err)
                goto err_setup_rx;
 
-       if (!vsi->netdev) {
-               err = EINVAL;
-               goto err_setup_rx;
-       }
-       snprintf(int_name, sizeof(int_name) - 1, "%s-%s",
-                dev_driver_string(&pf->pdev->dev), vsi->netdev->name);
-       err = i40e_vsi_request_irq(vsi, int_name);
-       if (err)
-               goto err_setup_rx;
+       if (vsi->netdev) {
+               snprintf(int_name, sizeof(int_name) - 1, "%s-%s",
+                        dev_driver_string(&pf->pdev->dev), vsi->netdev->name);
+               err = i40e_vsi_request_irq(vsi, int_name);
+               if (err)
+                       goto err_setup_rx;
 
-       /* Notify the stack of the actual queue counts. */
-       err = netif_set_real_num_tx_queues(vsi->netdev, vsi->num_queue_pairs);
-       if (err)
-               goto err_set_queues;
+               /* Notify the stack of the actual queue counts. */
+               err = netif_set_real_num_tx_queues(vsi->netdev,
+                                                  vsi->num_queue_pairs);
+               if (err)
+                       goto err_set_queues;
 
-       err = netif_set_real_num_rx_queues(vsi->netdev, vsi->num_queue_pairs);
-       if (err)
-               goto err_set_queues;
+               err = netif_set_real_num_rx_queues(vsi->netdev,
+                                                  vsi->num_queue_pairs);
+               if (err)
+                       goto err_set_queues;
+
+       } else if (vsi->type == I40E_VSI_FDIR) {
+               snprintf(int_name, sizeof(int_name) - 1, "%s-fdir",
+                        dev_driver_string(&pf->pdev->dev));
+               err = i40e_vsi_request_irq(vsi, int_name);
+       } else {
+               err = -EINVAL;
+               goto err_setup_rx;
+       }
 
        err = i40e_up_complete(vsi);
        if (err)
@@ -4383,14 +4541,7 @@ static int i40e_close(struct net_device *netdev)
        struct i40e_netdev_priv *np = netdev_priv(netdev);
        struct i40e_vsi *vsi = np->vsi;
 
-       if (test_and_set_bit(__I40E_DOWN, &vsi->state))
-               return 0;
-
-       i40e_down(vsi);
-       i40e_vsi_free_irq(vsi);
-
-       i40e_vsi_free_tx_resources(vsi);
-       i40e_vsi_free_rx_resources(vsi);
+       i40e_vsi_close(vsi);
 
        return 0;
 }
@@ -4410,6 +4561,9 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags)
 
        WARN_ON(in_interrupt());
 
+       if (i40e_check_asq_alive(&pf->hw))
+               i40e_vc_notify_reset(pf);
+
        /* do the biggest reset indicated */
        if (reset_flags & (1 << __I40E_GLOBAL_RESET_REQUESTED)) {
 
@@ -4475,7 +4629,7 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags)
                /* Find the VSI(s) that requested a re-init */
                dev_info(&pf->pdev->dev,
                         "VSI reinit requested\n");
-               for (v = 0; v < pf->hw.func_caps.num_vsis; v++) {
+               for (v = 0; v < pf->num_alloc_vsi; v++) {
                        struct i40e_vsi *vsi = pf->vsi[v];
                        if (vsi != NULL &&
                            test_bit(__I40E_REINIT_REQUESTED, &vsi->state)) {
@@ -4565,6 +4719,10 @@ static int i40e_handle_lldp_event(struct i40e_pf *pf,
        int ret = 0;
        u8 type;
 
+       /* Not DCB capable or capability disabled */
+       if (!(pf->flags & I40E_FLAG_DCB_CAPABLE))
+               return ret;
+
        /* Ignore if event is not for Nearest Bridge */
        type = ((mib->type >> I40E_AQ_LLDP_BRIDGE_TYPE_SHIFT)
                & I40E_AQ_LLDP_BRIDGE_TYPE_MASK);
@@ -4606,6 +4764,12 @@ static int i40e_handle_lldp_event(struct i40e_pf *pf,
        if (!need_reconfig)
                goto exit;
 
+       /* Enable DCB tagging only when more than one TC */
+       if (i40e_dcb_get_num_tc(dcbx_cfg) > 1)
+               pf->flags |= I40E_FLAG_DCB_ENABLED;
+       else
+               pf->flags &= ~I40E_FLAG_DCB_ENABLED;
+
        /* Reconfiguration needed quiesce all VSIs */
        i40e_pf_quiesce_all_vsi(pf);
 
@@ -4709,8 +4873,7 @@ void i40e_fdir_check_and_reenable(struct i40e_pf *pf)
            (pf->flags & I40E_FLAG_FD_SB_ENABLED))
                return;
        fcnt_prog = i40e_get_current_fd_count(pf);
-       fcnt_avail = pf->hw.fdir_shared_filter_count +
-                                              pf->fdir_pf_filter_count;
+       fcnt_avail = i40e_get_fd_cnt_all(pf);
        if (fcnt_prog < (fcnt_avail - I40E_FDIR_BUFFER_HEAD_ROOM)) {
                if ((pf->flags & I40E_FLAG_FD_SB_ENABLED) &&
                    (pf->auto_disable_flags & I40E_FLAG_FD_SB_ENABLED)) {
@@ -4803,7 +4966,7 @@ static void i40e_veb_link_event(struct i40e_veb *veb, bool link_up)
                        i40e_veb_link_event(pf->veb[i], link_up);
 
        /* ... now the local VSIs */
-       for (i = 0; i < pf->hw.func_caps.num_vsis; i++)
+       for (i = 0; i < pf->num_alloc_vsi; i++)
                if (pf->vsi[i] && (pf->vsi[i]->uplink_seid == veb->seid))
                        i40e_vsi_link_event(pf->vsi[i], link_up);
 }
@@ -4821,10 +4984,8 @@ static void i40e_link_event(struct i40e_pf *pf)
 
        if (new_link == old_link)
                return;
-
        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"));
+               i40e_print_link_message(pf->vsi[pf->lan_vsi], new_link);
 
        /* Notify the base of the switch tree connected to
         * the link.  Floating VEBs are not notified.
@@ -4862,7 +5023,7 @@ static void i40e_check_hang_subtask(struct i40e_pf *pf)
         *     for each q_vector
         *         force an interrupt
         */
-       for (v = 0; v < pf->hw.func_caps.num_vsis; v++) {
+       for (v = 0; v < pf->num_alloc_vsi; v++) {
                struct i40e_vsi *vsi = pf->vsi[v];
                int armed = 0;
 
@@ -4912,7 +5073,7 @@ static void i40e_watchdog_subtask(struct i40e_pf *pf)
        /* Update the stats for active netdevs so the network stack
         * can look at updated numbers whenever it cares to
         */
-       for (i = 0; i < pf->hw.func_caps.num_vsis; i++)
+       for (i = 0; i < pf->num_alloc_vsi; i++)
                if (pf->vsi[i] && pf->vsi[i]->netdev)
                        i40e_update_stats(pf->vsi[i]);
 
@@ -5018,11 +5179,47 @@ static void i40e_clean_adminq_subtask(struct i40e_pf *pf)
        u16 pending, i = 0;
        i40e_status ret;
        u16 opcode;
+       u32 oldval;
        u32 val;
 
        if (!test_bit(__I40E_ADMINQ_EVENT_PENDING, &pf->state))
                return;
 
+       /* check for error indications */
+       val = rd32(&pf->hw, pf->hw.aq.arq.len);
+       oldval = val;
+       if (val & I40E_PF_ARQLEN_ARQVFE_MASK) {
+               dev_info(&pf->pdev->dev, "ARQ VF Error detected\n");
+               val &= ~I40E_PF_ARQLEN_ARQVFE_MASK;
+       }
+       if (val & I40E_PF_ARQLEN_ARQOVFL_MASK) {
+               dev_info(&pf->pdev->dev, "ARQ Overflow Error detected\n");
+               val &= ~I40E_PF_ARQLEN_ARQOVFL_MASK;
+       }
+       if (val & I40E_PF_ARQLEN_ARQCRIT_MASK) {
+               dev_info(&pf->pdev->dev, "ARQ Critical Error detected\n");
+               val &= ~I40E_PF_ARQLEN_ARQCRIT_MASK;
+       }
+       if (oldval != val)
+               wr32(&pf->hw, pf->hw.aq.arq.len, val);
+
+       val = rd32(&pf->hw, pf->hw.aq.asq.len);
+       oldval = val;
+       if (val & I40E_PF_ATQLEN_ATQVFE_MASK) {
+               dev_info(&pf->pdev->dev, "ASQ VF Error detected\n");
+               val &= ~I40E_PF_ATQLEN_ATQVFE_MASK;
+       }
+       if (val & I40E_PF_ATQLEN_ATQOVFL_MASK) {
+               dev_info(&pf->pdev->dev, "ASQ Overflow Error detected\n");
+               val &= ~I40E_PF_ATQLEN_ATQOVFL_MASK;
+       }
+       if (val & I40E_PF_ATQLEN_ATQCRIT_MASK) {
+               dev_info(&pf->pdev->dev, "ASQ Critical Error detected\n");
+               val &= ~I40E_PF_ATQLEN_ATQCRIT_MASK;
+       }
+       if (oldval != val)
+               wr32(&pf->hw, pf->hw.aq.asq.len, val);
+
        event.msg_size = I40E_MAX_AQ_BUF_SIZE;
        event.msg_buf = kzalloc(event.msg_size, GFP_KERNEL);
        if (!event.msg_buf)
@@ -5128,7 +5325,7 @@ static int i40e_reconstitute_veb(struct i40e_veb *veb)
        int ret;
 
        /* build VSI that owns this VEB, temporarily attached to base VEB */
-       for (v = 0; v < pf->hw.func_caps.num_vsis && !ctl_vsi; v++) {
+       for (v = 0; v < pf->num_alloc_vsi && !ctl_vsi; v++) {
                if (pf->vsi[v] &&
                    pf->vsi[v]->veb_idx == veb->idx &&
                    pf->vsi[v]->flags & I40E_VSI_FLAG_VEB_OWNER) {
@@ -5158,7 +5355,7 @@ static int i40e_reconstitute_veb(struct i40e_veb *veb)
                goto end_reconstitute;
 
        /* create the remaining VSIs attached to this VEB */
-       for (v = 0; v < pf->hw.func_caps.num_vsis; v++) {
+       for (v = 0; v < pf->num_alloc_vsi; v++) {
                if (!pf->vsi[v] || pf->vsi[v] == ctl_vsi)
                        continue;
 
@@ -5226,9 +5423,6 @@ static int i40e_get_capabilities(struct i40e_pf *pf)
                }
        } while (err);
 
-       /* increment MSI-X count because current FW skips one */
-       pf->hw.func_caps.num_msix_vectors++;
-
        if (((pf->hw.aq.fw_maj_ver == 2) && (pf->hw.aq.fw_min_ver < 22)) ||
            (pf->hw.aq.fw_maj_ver < 2)) {
                pf->hw.func_caps.num_msix_vectors++;
@@ -5267,15 +5461,14 @@ static int i40e_vsi_clear(struct i40e_vsi *vsi);
 static void i40e_fdir_sb_setup(struct i40e_pf *pf)
 {
        struct i40e_vsi *vsi;
-       bool new_vsi = false;
-       int err, i;
+       int i;
 
        if (!(pf->flags & I40E_FLAG_FD_SB_ENABLED))
                return;
 
        /* find existing VSI and see if it needs configuring */
        vsi = NULL;
-       for (i = 0; i < pf->hw.func_caps.num_vsis; i++) {
+       for (i = 0; i < pf->num_alloc_vsi; i++) {
                if (pf->vsi[i] && pf->vsi[i]->type == I40E_VSI_FDIR) {
                        vsi = pf->vsi[i];
                        break;
@@ -5288,47 +5481,12 @@ static void i40e_fdir_sb_setup(struct i40e_pf *pf)
                                     pf->vsi[pf->lan_vsi]->seid, 0);
                if (!vsi) {
                        dev_info(&pf->pdev->dev, "Couldn't create FDir VSI\n");
-                       goto err_vsi;
+                       pf->flags &= ~I40E_FLAG_FD_SB_ENABLED;
+                       return;
                }
-               new_vsi = true;
-       }
-       i40e_vsi_setup_irqhandler(vsi, i40e_fdir_clean_ring);
-
-       err = i40e_vsi_setup_tx_resources(vsi);
-       if (err)
-               goto err_setup_tx;
-       err = i40e_vsi_setup_rx_resources(vsi);
-       if (err)
-               goto err_setup_rx;
-
-       if (new_vsi) {
-               char int_name[IFNAMSIZ + 9];
-               err = i40e_vsi_configure(vsi);
-               if (err)
-                       goto err_setup_rx;
-               snprintf(int_name, sizeof(int_name) - 1, "%s-fdir",
-                        dev_driver_string(&pf->pdev->dev));
-               err = i40e_vsi_request_irq(vsi, int_name);
-               if (err)
-                       goto err_setup_rx;
-               err = i40e_up_complete(vsi);
-               if (err)
-                       goto err_up_complete;
-               clear_bit(__I40E_NEEDS_RESTART, &vsi->state);
        }
 
-       return;
-
-err_up_complete:
-       i40e_down(vsi);
-       i40e_vsi_free_irq(vsi);
-err_setup_rx:
-       i40e_vsi_free_rx_resources(vsi);
-err_setup_tx:
-       i40e_vsi_free_tx_resources(vsi);
-err_vsi:
-       pf->flags &= ~I40E_FLAG_FD_SB_ENABLED;
-       i40e_vsi_clear(vsi);
+       i40e_vsi_setup_irqhandler(vsi, i40e_fdir_clean_ring);
 }
 
 /**
@@ -5340,7 +5498,7 @@ static void i40e_fdir_teardown(struct i40e_pf *pf)
        int i;
 
        i40e_fdir_filter_exit(pf);
-       for (i = 0; i < pf->hw.func_caps.num_vsis; i++) {
+       for (i = 0; i < pf->num_alloc_vsi; i++) {
                if (pf->vsi[i] && pf->vsi[i]->type == I40E_VSI_FDIR) {
                        i40e_vsi_release(pf->vsi[i]);
                        break;
@@ -5357,7 +5515,7 @@ static void i40e_fdir_teardown(struct i40e_pf *pf)
 static int i40e_prep_for_reset(struct i40e_pf *pf)
 {
        struct i40e_hw *hw = &pf->hw;
-       i40e_status ret;
+       i40e_status ret = 0;
        u32 v;
 
        clear_bit(__I40E_RESET_INTR_RECEIVED, &pf->state);
@@ -5366,13 +5524,10 @@ static int i40e_prep_for_reset(struct i40e_pf *pf)
 
        dev_dbg(&pf->pdev->dev, "Tearing down internal switch for reset\n");
 
-       if (i40e_check_asq_alive(hw))
-               i40e_vc_notify_reset(pf);
-
        /* quiesce the VSIs and their queues that are not already DOWN */
        i40e_pf_quiesce_all_vsi(pf);
 
-       for (v = 0; v < pf->hw.func_caps.num_vsis; v++) {
+       for (v = 0; v < pf->num_alloc_vsi; v++) {
                if (pf->vsi[v])
                        pf->vsi[v]->seid = 0;
        }
@@ -5380,14 +5535,33 @@ static int i40e_prep_for_reset(struct i40e_pf *pf)
        i40e_shutdown_adminq(&pf->hw);
 
        /* call shutdown HMC */
-       ret = i40e_shutdown_lan_hmc(hw);
-       if (ret) {
-               dev_info(&pf->pdev->dev, "shutdown_lan_hmc failed: %d\n", ret);
-               clear_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state);
+       if (hw->hmc.hmc_obj) {
+               ret = i40e_shutdown_lan_hmc(hw);
+               if (ret) {
+                       dev_warn(&pf->pdev->dev,
+                                "shutdown_lan_hmc failed: %d\n", ret);
+                       clear_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state);
+               }
        }
        return ret;
 }
 
+/**
+ * i40e_send_version - update firmware with driver version
+ * @pf: PF struct
+ */
+static void i40e_send_version(struct i40e_pf *pf)
+{
+       struct i40e_driver_version dv;
+
+       dv.major_version = DRV_VERSION_MAJOR;
+       dv.minor_version = DRV_VERSION_MINOR;
+       dv.build_version = DRV_VERSION_BUILD;
+       dv.subbuild_version = 0;
+       strncpy(dv.driver_string, DRV_VERSION, sizeof(dv.driver_string));
+       i40e_aq_send_driver_version(&pf->hw, &dv, NULL);
+}
+
 /**
  * i40e_reset_and_rebuild - reset and rebuild using a saved config
  * @pf: board private structure
@@ -5395,7 +5569,6 @@ static int i40e_prep_for_reset(struct i40e_pf *pf)
  **/
 static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit)
 {
-       struct i40e_driver_version dv;
        struct i40e_hw *hw = &pf->hw;
        i40e_status ret;
        u32 v;
@@ -5405,8 +5578,10 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit)
         * because the reset will make them disappear.
         */
        ret = i40e_pf_reset(hw);
-       if (ret)
+       if (ret) {
                dev_info(&pf->pdev->dev, "PF reset failed, %d\n", ret);
+               goto end_core_reset;
+       }
        pf->pfr_count++;
 
        if (test_bit(__I40E_DOWN, &pf->state))
@@ -5426,6 +5601,7 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit)
                i40e_verify_eeprom(pf);
        }
 
+       i40e_clear_pxe_mode(hw);
        ret = i40e_get_capabilities(pf);
        if (ret) {
                dev_info(&pf->pdev->dev, "i40e_get_capabilities failed, %d\n",
@@ -5526,13 +5702,7 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit)
        }
 
        /* tell the firmware that we're starting */
-       dv.major_version = DRV_VERSION_MAJOR;
-       dv.minor_version = DRV_VERSION_MINOR;
-       dv.build_version = DRV_VERSION_BUILD;
-       dv.subbuild_version = 0;
-       i40e_aq_send_driver_version(&pf->hw, &dv, NULL);
-
-       dev_info(&pf->pdev->dev, "reset complete\n");
+       i40e_send_version(pf);
 
 end_core_reset:
        clear_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state);
@@ -5642,7 +5812,6 @@ static void i40e_handle_mdd_event(struct i40e_pf *pf)
  **/
 static void i40e_sync_vxlan_filters_subtask(struct i40e_pf *pf)
 {
-       const int vxlan_hdr_qwords = 4;
        struct i40e_hw *hw = &pf->hw;
        i40e_status ret;
        u8 filter_index;
@@ -5660,7 +5829,6 @@ static void i40e_sync_vxlan_filters_subtask(struct i40e_pf *pf)
                        port = pf->vxlan_ports[i];
                        ret = port ?
                              i40e_aq_add_udp_tunnel(hw, ntohs(port),
-                                                    vxlan_hdr_qwords,
                                                     I40E_AQC_TUNNEL_TYPE_VXLAN,
                                                     &filter_index, NULL)
                              : i40e_aq_del_udp_tunnel(hw, i, NULL);
@@ -5839,15 +6007,15 @@ static int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type)
         * find next empty vsi slot, looping back around if necessary
         */
        i = pf->next_vsi;
-       while (i < pf->hw.func_caps.num_vsis && pf->vsi[i])
+       while (i < pf->num_alloc_vsi && pf->vsi[i])
                i++;
-       if (i >= pf->hw.func_caps.num_vsis) {
+       if (i >= pf->num_alloc_vsi) {
                i = 0;
                while (i < pf->next_vsi && pf->vsi[i])
                        i++;
        }
 
-       if (i < pf->hw.func_caps.num_vsis && !pf->vsi[i]) {
+       if (i < pf->num_alloc_vsi && !pf->vsi[i]) {
                vsi_idx = i;             /* Found one! */
        } else {
                ret = -ENODEV;
@@ -5870,6 +6038,7 @@ static int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type)
        vsi->netdev_registered = false;
        vsi->work_limit = I40E_DEFAULT_IRQ_WORK;
        INIT_LIST_HEAD(&vsi->mac_filter_list);
+       vsi->irqs_ready = false;
 
        ret = i40e_set_num_rings_in_vsi(vsi);
        if (ret)
@@ -5987,14 +6156,12 @@ static void i40e_vsi_clear_rings(struct i40e_vsi *vsi)
  **/
 static int i40e_alloc_rings(struct i40e_vsi *vsi)
 {
+       struct i40e_ring *tx_ring, *rx_ring;
        struct i40e_pf *pf = vsi->back;
        int i;
 
        /* Set basic values in the rings to be used later during open() */
        for (i = 0; i < vsi->alloc_queue_pairs; i++) {
-               struct i40e_ring *tx_ring;
-               struct i40e_ring *rx_ring;
-
                /* allocate space for both Tx and Rx in one shot */
                tx_ring = kzalloc(sizeof(struct i40e_ring) * 2, GFP_KERNEL);
                if (!tx_ring)
@@ -6052,8 +6219,6 @@ static int i40e_reserve_msix_vectors(struct i40e_pf *pf, int vectors)
                vectors = 0;
        }
 
-       pf->num_msix_entries = vectors;
-
        return vectors;
 }
 
@@ -6107,6 +6272,16 @@ static int i40e_init_msix(struct i40e_pf *pf)
        for (i = 0; i < v_budget; i++)
                pf->msix_entries[i].entry = i;
        vec = i40e_reserve_msix_vectors(pf, v_budget);
+
+       if (vec != v_budget) {
+               /* If we have limited resources, we will start with no vectors
+                * for the special features and then allocate vectors to some
+                * of these features based on the policy and at the end disable
+                * the features that did not get any vectors.
+                */
+               pf->num_vmdq_msix = 0;
+       }
+
        if (vec < I40E_MIN_MSIX) {
                pf->flags &= ~I40E_FLAG_MSIX_ENABLED;
                kfree(pf->msix_entries);
@@ -6115,27 +6290,25 @@ static int i40e_init_msix(struct i40e_pf *pf)
 
        } else if (vec == I40E_MIN_MSIX) {
                /* Adjust for minimal MSIX use */
-               dev_info(&pf->pdev->dev, "Features disabled, not enough MSI-X vectors\n");
-               pf->flags &= ~I40E_FLAG_VMDQ_ENABLED;
                pf->num_vmdq_vsis = 0;
                pf->num_vmdq_qps = 0;
-               pf->num_vmdq_msix = 0;
                pf->num_lan_qps = 1;
                pf->num_lan_msix = 1;
 
        } else if (vec != v_budget) {
+               /* reserve the misc vector */
+               vec--;
+
                /* Scale vector usage down */
                pf->num_vmdq_msix = 1;    /* force VMDqs to only one vector */
-               vec--;                    /* reserve the misc vector */
+               pf->num_vmdq_vsis = 1;
 
                /* partition out the remaining vectors */
                switch (vec) {
                case 2:
-                       pf->num_vmdq_vsis = 1;
                        pf->num_lan_msix = 1;
                        break;
                case 3:
-                       pf->num_vmdq_vsis = 1;
                        pf->num_lan_msix = 2;
                        break;
                default:
@@ -6147,6 +6320,11 @@ static int i40e_init_msix(struct i40e_pf *pf)
                }
        }
 
+       if ((pf->flags & I40E_FLAG_VMDQ_ENABLED) &&
+           (pf->num_vmdq_msix == 0)) {
+               dev_info(&pf->pdev->dev, "VMDq disabled, not enough MSI-X vectors\n");
+               pf->flags &= ~I40E_FLAG_VMDQ_ENABLED;
+       }
        return err;
 }
 
@@ -6171,7 +6349,7 @@ static int i40e_vsi_alloc_q_vector(struct i40e_vsi *vsi, int 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);
+                              i40e_napi_poll, NAPI_POLL_WEIGHT);
 
        q_vector->rx.latency_range = I40E_LOW_LATENCY;
        q_vector->tx.latency_range = I40E_LOW_LATENCY;
@@ -6231,7 +6409,7 @@ static void i40e_init_interrupt_scheme(struct i40e_pf *pf)
                if (err) {
                        pf->flags &= ~(I40E_FLAG_MSIX_ENABLED   |
                                       I40E_FLAG_RSS_ENABLED    |
-                                      I40E_FLAG_DCB_ENABLED    |
+                                      I40E_FLAG_DCB_CAPABLE    |
                                       I40E_FLAG_SRIOV_ENABLED  |
                                       I40E_FLAG_FD_SB_ENABLED  |
                                       I40E_FLAG_FD_ATR_ENABLED |
@@ -6364,7 +6542,6 @@ int i40e_reconfig_rss_queues(struct i40e_pf *pf, int queue_count)
                return 0;
 
        queue_count = min_t(int, queue_count, pf->rss_size_max);
-       queue_count = rounddown_pow_of_two(queue_count);
 
        if (queue_count != pf->rss_size) {
                i40e_prep_for_reset(pf);
@@ -6407,6 +6584,10 @@ static int i40e_sw_init(struct i40e_pf *pf)
                    I40E_FLAG_MSIX_ENABLED    |
                    I40E_FLAG_RX_1BUF_ENABLED;
 
+       /* Set default ITR */
+       pf->rx_itr_default = I40E_ITR_DYNAMIC | I40E_ITR_RX_DEF;
+       pf->tx_itr_default = I40E_ITR_DYNAMIC | I40E_ITR_TX_DEF;
+
        /* Depending on PF configurations, it is possible that the RSS
         * maximum might end up larger than the available queues
         */
@@ -6416,7 +6597,6 @@ static int i40e_sw_init(struct i40e_pf *pf)
        if (pf->hw.func_caps.rss) {
                pf->flags |= I40E_FLAG_RSS_ENABLED;
                pf->rss_size = min_t(int, pf->rss_size_max, num_online_cpus());
-               pf->rss_size = rounddown_pow_of_two(pf->rss_size);
        } else {
                pf->rss_size = 1;
        }
@@ -6432,8 +6612,12 @@ static int i40e_sw_init(struct i40e_pf *pf)
            (pf->hw.func_caps.fd_filters_best_effort > 0)) {
                pf->flags |= I40E_FLAG_FD_ATR_ENABLED;
                pf->atr_sample_rate = I40E_DEFAULT_ATR_SAMPLE_RATE;
+               /* Setup a counter for fd_atr per pf */
+               pf->fd_atr_cnt_idx = I40E_FD_ATR_STAT_IDX(pf->hw.pf_id);
                if (!(pf->flags & I40E_FLAG_MFP_ENABLED)) {
                        pf->flags |= I40E_FLAG_FD_SB_ENABLED;
+                       /* Setup a counter for fd_sb per pf */
+                       pf->fd_sb_cnt_idx = I40E_FD_SB_STAT_IDX(pf->hw.pf_id);
                } else {
                        dev_info(&pf->pdev->dev,
                                 "Flow Director Sideband mode Disabled in MFP mode\n");
@@ -6649,6 +6833,96 @@ static void i40e_del_vxlan_port(struct net_device *netdev,
 }
 
 #endif
+#ifdef HAVE_FDB_OPS
+#ifdef USE_CONST_DEV_UC_CHAR
+static int i40e_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
+                           struct net_device *dev,
+                           const unsigned char *addr,
+                           u16 flags)
+#else
+static int i40e_ndo_fdb_add(struct ndmsg *ndm,
+                           struct net_device *dev,
+                           unsigned char *addr,
+                           u16 flags)
+#endif
+{
+       struct i40e_netdev_priv *np = netdev_priv(dev);
+       struct i40e_pf *pf = np->vsi->back;
+       int err = 0;
+
+       if (!(pf->flags & I40E_FLAG_SRIOV_ENABLED))
+               return -EOPNOTSUPP;
+
+       /* Hardware does not support aging addresses so if a
+        * ndm_state is given only allow permanent addresses
+        */
+       if (ndm->ndm_state && !(ndm->ndm_state & NUD_PERMANENT)) {
+               netdev_info(dev, "FDB only supports static addresses\n");
+               return -EINVAL;
+       }
+
+       if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr))
+               err = dev_uc_add_excl(dev, addr);
+       else if (is_multicast_ether_addr(addr))
+               err = dev_mc_add_excl(dev, addr);
+       else
+               err = -EINVAL;
+
+       /* Only return duplicate errors if NLM_F_EXCL is set */
+       if (err == -EEXIST && !(flags & NLM_F_EXCL))
+               err = 0;
+
+       return err;
+}
+
+#ifndef USE_DEFAULT_FDB_DEL_DUMP
+#ifdef USE_CONST_DEV_UC_CHAR
+static int i40e_ndo_fdb_del(struct ndmsg *ndm,
+                           struct net_device *dev,
+                           const unsigned char *addr)
+#else
+static int i40e_ndo_fdb_del(struct ndmsg *ndm,
+                           struct net_device *dev,
+                           unsigned char *addr)
+#endif
+{
+       struct i40e_netdev_priv *np = netdev_priv(dev);
+       struct i40e_pf *pf = np->vsi->back;
+       int err = -EOPNOTSUPP;
+
+       if (ndm->ndm_state & NUD_PERMANENT) {
+               netdev_info(dev, "FDB only supports static addresses\n");
+               return -EINVAL;
+       }
+
+       if (pf->flags & I40E_FLAG_SRIOV_ENABLED) {
+               if (is_unicast_ether_addr(addr))
+                       err = dev_uc_del(dev, addr);
+               else if (is_multicast_ether_addr(addr))
+                       err = dev_mc_del(dev, addr);
+               else
+                       err = -EINVAL;
+       }
+
+       return err;
+}
+
+static int i40e_ndo_fdb_dump(struct sk_buff *skb,
+                            struct netlink_callback *cb,
+                            struct net_device *dev,
+                            int idx)
+{
+       struct i40e_netdev_priv *np = netdev_priv(dev);
+       struct i40e_pf *pf = np->vsi->back;
+
+       if (pf->flags & I40E_FLAG_SRIOV_ENABLED)
+               idx = ndo_dflt_fdb_dump(skb, cb, dev, idx);
+
+       return idx;
+}
+
+#endif /* USE_DEFAULT_FDB_DEL_DUMP */
+#endif /* HAVE_FDB_OPS */
 static const struct net_device_ops i40e_netdev_ops = {
        .ndo_open               = i40e_open,
        .ndo_stop               = i40e_close,
@@ -6669,13 +6943,21 @@ static const struct net_device_ops i40e_netdev_ops = {
        .ndo_set_features       = i40e_set_features,
        .ndo_set_vf_mac         = i40e_ndo_set_vf_mac,
        .ndo_set_vf_vlan        = i40e_ndo_set_vf_port_vlan,
-       .ndo_set_vf_tx_rate     = i40e_ndo_set_vf_bw,
+       .ndo_set_vf_rate        = i40e_ndo_set_vf_bw,
        .ndo_get_vf_config      = i40e_ndo_get_vf_config,
        .ndo_set_vf_link_state  = i40e_ndo_set_vf_link_state,
+       .ndo_set_vf_spoofchk    = i40e_ndo_set_vf_spoofck,
 #ifdef CONFIG_I40E_VXLAN
        .ndo_add_vxlan_port     = i40e_add_vxlan_port,
        .ndo_del_vxlan_port     = i40e_del_vxlan_port,
 #endif
+#ifdef HAVE_FDB_OPS
+       .ndo_fdb_add            = i40e_ndo_fdb_add,
+#ifndef USE_DEFAULT_FDB_DEL_DUMP
+       .ndo_fdb_del            = i40e_ndo_fdb_del,
+       .ndo_fdb_dump           = i40e_ndo_fdb_dump,
+#endif
+#endif
 };
 
 /**
@@ -6720,16 +7002,26 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
                           NETIF_F_TSO_ECN             |
                           NETIF_F_TSO6                |
                           NETIF_F_RXCSUM              |
-                          NETIF_F_NTUPLE              |
                           NETIF_F_RXHASH              |
                           0;
 
+       if (!(pf->flags & I40E_FLAG_MFP_ENABLED))
+               netdev->features |= NETIF_F_NTUPLE;
+
        /* copy netdev features into list of user selectable features */
        netdev->hw_features |= netdev->features;
 
        if (vsi->type == I40E_VSI_MAIN) {
                SET_NETDEV_DEV(netdev, &pf->pdev->dev);
-               memcpy(mac_addr, hw->mac.perm_addr, ETH_ALEN);
+               ether_addr_copy(mac_addr, hw->mac.perm_addr);
+               /* The following two steps are necessary to prevent reception
+                * of tagged packets - by default the NVM loads a MAC-VLAN
+                * filter that will accept any tagged packet.  This is to
+                * prevent that during normal operations until a specific
+                * VLAN tag filter has been set.
+                */
+               i40e_rm_default_mac_filter(vsi, mac_addr);
+               i40e_add_filter(vsi, mac_addr, I40E_VLAN_ANY, false, true);
        } else {
                /* relate the VSI_VMDQ name to the VSI_MAIN name */
                snprintf(netdev->name, IFNAMSIZ, "%sv%%d",
@@ -6739,8 +7031,8 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
        }
        i40e_add_filter(vsi, brdcast, I40E_VLAN_ANY, false, false);
 
-       memcpy(netdev->dev_addr, mac_addr, ETH_ALEN);
-       memcpy(netdev->perm_addr, mac_addr, ETH_ALEN);
+       ether_addr_copy(netdev->dev_addr, mac_addr);
+       ether_addr_copy(netdev->perm_addr, mac_addr);
        /* vlan gets same features (except vlan offload)
         * after any tweaks for specific VSI types
         */
@@ -6772,7 +7064,6 @@ static void i40e_vsi_delete(struct i40e_vsi *vsi)
                return;
 
        i40e_aq_delete_element(&vsi->back->hw, vsi->seid, NULL);
-       return;
 }
 
 /**
@@ -6898,6 +7189,13 @@ static int i40e_add_vsi(struct i40e_vsi *vsi)
 
                ctxt.info.valid_sections |= cpu_to_le16(I40E_AQ_VSI_PROP_VLAN_VALID);
                ctxt.info.port_vlan_flags |= I40E_AQ_VSI_PVLAN_MODE_ALL;
+               if (pf->vf[vsi->vf_id].spoofchk) {
+                       ctxt.info.valid_sections |=
+                               cpu_to_le16(I40E_AQ_VSI_PROP_SECURITY_VALID);
+                       ctxt.info.sec_flags |=
+                               (I40E_AQ_VSI_SEC_FLAG_ENABLE_VLAN_CHK |
+                                I40E_AQ_VSI_SEC_FLAG_ENABLE_MAC_CHK);
+               }
                /* Setup the VSI tx/rx queue map for TC0 only for now */
                i40e_vsi_setup_queue_map(vsi, &ctxt, enabled_tc, true);
                break;
@@ -6982,11 +7280,7 @@ int i40e_vsi_release(struct i40e_vsi *vsi)
                                unregister_netdev(vsi->netdev);
                        }
                } else {
-                       if (!test_and_set_bit(__I40E_DOWN, &vsi->state))
-                               i40e_down(vsi);
-                       i40e_vsi_free_irq(vsi);
-                       i40e_vsi_free_tx_resources(vsi);
-                       i40e_vsi_free_rx_resources(vsi);
+                       i40e_vsi_close(vsi);
                }
                i40e_vsi_disable_irq(vsi);
        }
@@ -7013,7 +7307,7 @@ int i40e_vsi_release(struct i40e_vsi *vsi)
         * the orphan VEBs yet.  We'll wait for an explicit remove request
         * from up the network stack.
         */
-       for (n = 0, i = 0; i < pf->hw.func_caps.num_vsis; i++) {
+       for (n = 0, i = 0; i < pf->num_alloc_vsi; i++) {
                if (pf->vsi[i] &&
                    pf->vsi[i]->uplink_seid == uplink_seid &&
                    (pf->vsi[i]->flags & I40E_VSI_FLAG_VEB_OWNER) == 0) {
@@ -7192,7 +7486,7 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type,
 
        if (!veb && uplink_seid != pf->mac_seid) {
 
-               for (i = 0; i < pf->hw.func_caps.num_vsis; i++) {
+               for (i = 0; i < pf->num_alloc_vsi; i++) {
                        if (pf->vsi[i] && pf->vsi[i]->seid == uplink_seid) {
                                vsi = pf->vsi[i];
                                break;
@@ -7435,7 +7729,7 @@ static void i40e_switch_branch_release(struct i40e_veb *branch)
         * NOTE: Removing the last VSI on a VEB has the SIDE EFFECT of removing
         *       the VEB itself, so don't use (*branch) after this loop.
         */
-       for (i = 0; i < pf->hw.func_caps.num_vsis; i++) {
+       for (i = 0; i < pf->num_alloc_vsi; i++) {
                if (!pf->vsi[i])
                        continue;
                if (pf->vsi[i]->uplink_seid == branch_seid &&
@@ -7487,7 +7781,7 @@ void i40e_veb_release(struct i40e_veb *veb)
        pf = veb->pf;
 
        /* find the remaining VSI and check for extras */
-       for (i = 0; i < pf->hw.func_caps.num_vsis; i++) {
+       for (i = 0; i < pf->num_alloc_vsi; i++) {
                if (pf->vsi[i] && pf->vsi[i]->uplink_seid == veb->seid) {
                        n++;
                        vsi = pf->vsi[i];
@@ -7516,8 +7810,6 @@ void i40e_veb_release(struct i40e_veb *veb)
 
        i40e_aq_delete_element(&pf->hw, veb->seid, NULL);
        i40e_veb_clear(veb);
-
-       return;
 }
 
 /**
@@ -7601,10 +7893,10 @@ struct i40e_veb *i40e_veb_setup(struct i40e_pf *pf, u16 flags,
        }
 
        /* make sure there is such a vsi and uplink */
-       for (vsi_idx = 0; vsi_idx < pf->hw.func_caps.num_vsis; vsi_idx++)
+       for (vsi_idx = 0; vsi_idx < pf->num_alloc_vsi; vsi_idx++)
                if (pf->vsi[vsi_idx] && pf->vsi[vsi_idx]->seid == vsi_seid)
                        break;
-       if (vsi_idx >= pf->hw.func_caps.num_vsis && vsi_seid != 0) {
+       if (vsi_idx >= pf->num_alloc_vsi && vsi_seid != 0) {
                dev_info(&pf->pdev->dev, "vsi seid %d not found\n",
                         vsi_seid);
                return NULL;
@@ -7639,6 +7931,8 @@ struct i40e_veb *i40e_veb_setup(struct i40e_pf *pf, u16 flags,
        ret = i40e_add_veb(veb, pf->vsi[vsi_idx]);
        if (ret)
                goto err_veb;
+       if (vsi_idx == pf->lan_vsi)
+               pf->lan_veb = veb->idx;
 
        return veb;
 
@@ -7774,15 +8068,6 @@ int i40e_fetch_switch_configuration(struct i40e_pf *pf, bool printconfig)
                                 "header: %d reported %d total\n",
                                 num_reported, num_total);
 
-               if (num_reported) {
-                       int sz = sizeof(*sw_config) * num_reported;
-
-                       kfree(pf->sw_config);
-                       pf->sw_config = kzalloc(sz, GFP_KERNEL);
-                       if (pf->sw_config)
-                               memcpy(pf->sw_config, sw_config, sz);
-               }
-
                for (i = 0; i < num_reported; i++) {
                        struct i40e_aqc_switch_config_element_resp *ele =
                                &sw_config->element[i];
@@ -7949,9 +8234,7 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf)
        queues_left = pf->hw.func_caps.num_tx_qp;
 
        if ((queues_left == 1) ||
-           !(pf->flags & I40E_FLAG_MSIX_ENABLED) ||
-           !(pf->flags & (I40E_FLAG_RSS_ENABLED | I40E_FLAG_FD_SB_ENABLED |
-                          I40E_FLAG_DCB_ENABLED))) {
+           !(pf->flags & I40E_FLAG_MSIX_ENABLED)) {
                /* one qp for PF, no queues for anything else */
                queues_left = 0;
                pf->rss_size = pf->num_lan_qps = 1;
@@ -7960,14 +8243,27 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf)
                pf->flags &= ~(I40E_FLAG_RSS_ENABLED    |
                               I40E_FLAG_FD_SB_ENABLED  |
                               I40E_FLAG_FD_ATR_ENABLED |
-                              I40E_FLAG_DCB_ENABLED    |
+                              I40E_FLAG_DCB_CAPABLE    |
                               I40E_FLAG_SRIOV_ENABLED  |
                               I40E_FLAG_VMDQ_ENABLED);
+       } else if (!(pf->flags & (I40E_FLAG_RSS_ENABLED |
+                                 I40E_FLAG_FD_SB_ENABLED |
+                                 I40E_FLAG_FD_ATR_ENABLED |
+                                 I40E_FLAG_DCB_CAPABLE))) {
+               /* one qp for PF */
+               pf->rss_size = pf->num_lan_qps = 1;
+               queues_left -= pf->num_lan_qps;
+
+               pf->flags &= ~(I40E_FLAG_RSS_ENABLED    |
+                              I40E_FLAG_FD_SB_ENABLED  |
+                              I40E_FLAG_FD_ATR_ENABLED |
+                              I40E_FLAG_DCB_ENABLED    |
+                              I40E_FLAG_VMDQ_ENABLED);
        } else {
                /* Not enough queues for all TCs */
-               if ((pf->flags & I40E_FLAG_DCB_ENABLED) &&
+               if ((pf->flags & I40E_FLAG_DCB_CAPABLE) &&
                    (queues_left < I40E_MAX_TRAFFIC_CLASS)) {
-                       pf->flags &= ~I40E_FLAG_DCB_ENABLED;
+                       pf->flags &= ~I40E_FLAG_DCB_CAPABLE;
                        dev_info(&pf->pdev->dev, "not enough queues for DCB. DCB is disabled.\n");
                }
                pf->num_lan_qps = pf->rss_size_max;
@@ -7998,7 +8294,6 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf)
        }
 
        pf->queues_left = queues_left;
-       return;
 }
 
 /**
@@ -8055,12 +8350,13 @@ static void i40e_print_features(struct i40e_pf *pf)
 
        if (pf->flags & I40E_FLAG_RSS_ENABLED)
                buf += sprintf(buf, "RSS ");
-       buf += sprintf(buf, "FDir ");
        if (pf->flags & I40E_FLAG_FD_ATR_ENABLED)
-               buf += sprintf(buf, "ATR ");
-       if (pf->flags & I40E_FLAG_FD_SB_ENABLED)
+               buf += sprintf(buf, "FD_ATR ");
+       if (pf->flags & I40E_FLAG_FD_SB_ENABLED) {
+               buf += sprintf(buf, "FD_SB ");
                buf += sprintf(buf, "NTUPLE ");
-       if (pf->flags & I40E_FLAG_DCB_ENABLED)
+       }
+       if (pf->flags & I40E_FLAG_DCB_CAPABLE)
                buf += sprintf(buf, "DCB ");
        if (pf->flags & I40E_FLAG_PTP)
                buf += sprintf(buf, "PTP ");
@@ -8083,13 +8379,13 @@ static void i40e_print_features(struct i40e_pf *pf)
  **/
 static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
-       struct i40e_driver_version dv;
        struct i40e_pf *pf;
        struct i40e_hw *hw;
        static u16 pfs_found;
        u16 link_status;
        int err = 0;
        u32 len;
+       u32 i;
 
        err = pci_enable_device_mem(pdev);
        if (err)
@@ -8201,6 +8497,10 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        i40e_verify_eeprom(pf);
 
+       /* Rev 0 hardware was never productized */
+       if (hw->revision_id < 1)
+               dev_warn(&pdev->dev, "This device is a pre-production adapter/LOM. Please be aware there may be issues with your hardware. If you are experiencing problems please contact your Intel or hardware representative who provided you with this hardware.\n");
+
        i40e_clear_pxe_mode(hw);
        err = i40e_get_capabilities(pf);
        if (err)
@@ -8234,7 +8534,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                goto err_mac_addr;
        }
        dev_info(&pdev->dev, "MAC address: %pM\n", hw->mac.addr);
-       memcpy(hw->mac.perm_addr, hw->mac.addr, ETH_ALEN);
+       ether_addr_copy(hw->mac.perm_addr, hw->mac.addr);
 
        pci_set_drvdata(pdev, pf);
        pci_save_state(pdev);
@@ -8242,8 +8542,8 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        err = i40e_init_pf_dcb(pf);
        if (err) {
                dev_info(&pdev->dev, "init_pf_dcb failed: %d\n", err);
-               pf->flags &= ~I40E_FLAG_DCB_ENABLED;
-               goto err_init_dcb;
+               pf->flags &= ~I40E_FLAG_DCB_CAPABLE;
+               /* Continue without DCB enabled */
        }
 #endif /* CONFIG_I40E_DCB */
 
@@ -8264,10 +8564,18 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        i40e_determine_queue_usage(pf);
        i40e_init_interrupt_scheme(pf);
 
-       /* Set up the *vsi struct based on the number of VSIs in the HW,
-        * and set up our local tracking of the MAIN PF vsi.
+       /* The number of VSIs reported by the FW is the minimum guaranteed
+        * to us; HW supports far more and we share the remaining pool with
+        * the other PFs. We allocate space for more than the guarantee with
+        * the understanding that we might not get them all later.
         */
-       len = sizeof(struct i40e_vsi *) * pf->hw.func_caps.num_vsis;
+       if (pf->hw.func_caps.num_vsis < I40E_MIN_VSI_ALLOC)
+               pf->num_alloc_vsi = I40E_MIN_VSI_ALLOC;
+       else
+               pf->num_alloc_vsi = pf->hw.func_caps.num_vsis;
+
+       /* Set up the *vsi struct and our local tracking of the MAIN PF vsi. */
+       len = sizeof(struct i40e_vsi *) * pf->num_alloc_vsi;
        pf->vsi = kzalloc(len, GFP_KERNEL);
        if (!pf->vsi) {
                err = -ENOMEM;
@@ -8279,6 +8587,13 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                dev_info(&pdev->dev, "setup_pf_switch failed: %d\n", err);
                goto err_vsis;
        }
+       /* if FDIR VSI was set up, start it now */
+       for (i = 0; i < pf->num_alloc_vsi; i++) {
+               if (pf->vsi[i] && pf->vsi[i]->type == I40E_VSI_FDIR) {
+                       i40e_vsi_open(pf->vsi[i]);
+                       break;
+               }
+       }
 
        /* The main driver is (mostly) up and happy. We need to set this state
         * before setting up the misc vector or we get a race and the vector
@@ -8300,6 +8615,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                }
        }
 
+#ifdef CONFIG_PCI_IOV
        /* prep for VF support */
        if ((pf->flags & I40E_FLAG_SRIOV_ENABLED) &&
            (pf->flags & I40E_FLAG_MSIX_ENABLED) &&
@@ -8322,17 +8638,14 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                                         err);
                }
        }
+#endif /* CONFIG_PCI_IOV */
 
        pfs_found++;
 
        i40e_dbg_pf_init(pf);
 
        /* tell the firmware that we're starting */
-       dv.major_version = DRV_VERSION_MAJOR;
-       dv.minor_version = DRV_VERSION_MINOR;
-       dv.build_version = DRV_VERSION_BUILD;
-       dv.subbuild_version = 0;
-       i40e_aq_send_driver_version(&pf->hw, &dv, NULL);
+       i40e_send_version(pf);
 
        /* since everything's happy, start the service_task timer */
        mod_timer(&pf->service_timer,
@@ -8373,9 +8686,6 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 err_switch_setup:
        i40e_reset_interrupt_capability(pf);
        del_timer_sync(&pf->service_timer);
-#ifdef CONFIG_I40E_DCB
-err_init_dcb:
-#endif /* CONFIG_I40E_DCB */
 err_mac_addr:
 err_configure_lan_hmc:
        (void)i40e_shutdown_lan_hmc(hw);
@@ -8456,10 +8766,13 @@ static void i40e_remove(struct pci_dev *pdev)
        }
 
        /* shutdown and destroy the HMC */
-       ret_code = i40e_shutdown_lan_hmc(&pf->hw);
-       if (ret_code)
-               dev_warn(&pdev->dev,
-                        "Failed to destroy the HMC resources: %d\n", ret_code);
+       if (pf->hw.hmc.hmc_obj) {
+               ret_code = i40e_shutdown_lan_hmc(&pf->hw);
+               if (ret_code)
+                       dev_warn(&pdev->dev,
+                                "Failed to destroy the HMC resources: %d\n",
+                                ret_code);
+       }
 
        /* shutdown the adminq */
        ret_code = i40e_shutdown_adminq(&pf->hw);
@@ -8470,7 +8783,7 @@ static void i40e_remove(struct pci_dev *pdev)
 
        /* Clear all dynamic memory lists of rings, q_vectors, and VSIs */
        i40e_clear_interrupt_scheme(pf);
-       for (i = 0; i < pf->hw.func_caps.num_vsis; i++) {
+       for (i = 0; i < pf->num_alloc_vsi; i++) {
                if (pf->vsi[i]) {
                        i40e_vsi_clear_rings(pf->vsi[i]);
                        i40e_vsi_clear(pf->vsi[i]);
@@ -8485,7 +8798,6 @@ static void i40e_remove(struct pci_dev *pdev)
 
        kfree(pf->qp_pile);
        kfree(pf->irq_pile);
-       kfree(pf->sw_config);
        kfree(pf->vsi);
 
        /* force a PF reset to clean anything leftover */
index 9cd57e617959b1622ec57f8c8f6e2146c9c800a6..a430699c41d5e2be548db369bca02efdcaad40bf 100644 (file)
@@ -70,10 +70,12 @@ i40e_status i40e_aq_get_firmware_version(struct i40e_hw *hw,
                                u16 *fw_major_version, u16 *fw_minor_version,
                                u16 *api_major_version, u16 *api_minor_version,
                                struct i40e_asq_cmd_details *cmd_details);
-i40e_status i40e_aq_set_phy_reset(struct i40e_hw *hw,
+i40e_status i40e_aq_set_phy_debug(struct i40e_hw *hw, u8 cmd_flags,
                                struct i40e_asq_cmd_details *cmd_details);
 i40e_status i40e_aq_set_default_vsi(struct i40e_hw *hw, u16 vsi_id,
                                struct i40e_asq_cmd_details *cmd_details);
+i40e_status i40e_aq_clear_pxe_mode(struct i40e_hw *hw,
+                               struct i40e_asq_cmd_details *cmd_details);
 i40e_status i40e_aq_set_link_restart_an(struct i40e_hw *hw,
                                struct i40e_asq_cmd_details *cmd_details);
 i40e_status i40e_aq_get_link_info(struct i40e_hw *hw,
@@ -157,8 +159,8 @@ i40e_status i40e_aq_stop_lldp(struct i40e_hw *hw, bool shutdown_agent,
 i40e_status i40e_aq_start_lldp(struct i40e_hw *hw,
                                struct i40e_asq_cmd_details *cmd_details);
 i40e_status i40e_aq_add_udp_tunnel(struct i40e_hw *hw,
-                               u16 udp_port, u8 header_len,
-                               u8 protocol_index, u8 *filter_index,
+                               u16 udp_port, u8 protocol_index,
+                               u8 *filter_index,
                                struct i40e_asq_cmd_details *cmd_details);
 i40e_status i40e_aq_del_udp_tunnel(struct i40e_hw *hw, u8 index,
                                struct i40e_asq_cmd_details *cmd_details);
@@ -167,6 +169,9 @@ i40e_status i40e_aq_delete_element(struct i40e_hw *hw, u16 seid,
 i40e_status i40e_aq_mac_address_write(struct i40e_hw *hw,
                                    u16 flags, u8 *mac_addr,
                                    struct i40e_asq_cmd_details *cmd_details);
+i40e_status i40e_aq_config_vsi_bw_limit(struct i40e_hw *hw,
+                               u16 seid, u16 credit, u8 max_credit,
+                               struct i40e_asq_cmd_details *cmd_details);
 i40e_status i40e_aq_dcb_updated(struct i40e_hw *hw,
                                struct i40e_asq_cmd_details *cmd_details);
 i40e_status i40e_aq_set_hmc_resource_profile(struct i40e_hw *hw,
@@ -216,6 +221,7 @@ bool i40e_get_link_status(struct i40e_hw *hw);
 i40e_status i40e_get_mac_addr(struct i40e_hw *hw,
                                                u8 *mac_addr);
 i40e_status i40e_validate_mac_addr(u8 *mac_addr);
+void i40e_pre_tx_queue_cfg(struct i40e_hw *hw, u32 queue, bool enable);
 /* prototype for functions used for NVM access */
 i40e_status i40e_init_nvm(struct i40e_hw *hw);
 i40e_status i40e_acquire_nvm(struct i40e_hw *hw,
index e61e63720800fcbcc9c13b269f531b65e3902d7e..101f439acda6adfd7e57084865f3bd1b16d06362 100644 (file)
@@ -48,7 +48,6 @@
                                        I40E_PRTTSYN_CTL1_TSYNTYPE_SHIFT)
 #define I40E_PRTTSYN_CTL1_TSYNTYPE_V2  (0x2 << \
                                        I40E_PRTTSYN_CTL1_TSYNTYPE_SHIFT)
-#define I40E_PTP_TX_TIMEOUT  (HZ * 15)
 
 /**
  * i40e_ptp_read - Read the PHC time from the device
@@ -216,40 +215,6 @@ static int i40e_ptp_settime(struct ptp_clock_info *ptp,
        return 0;
 }
 
-/**
- * i40e_ptp_tx_work
- * @work: pointer to work struct
- *
- * This work function polls the PRTTSYN_STAT_0.TXTIME bit to determine when a
- * Tx timestamp event has occurred, in order to pass the Tx timestamp value up
- * the stack in the skb.
- */
-static void i40e_ptp_tx_work(struct work_struct *work)
-{
-       struct i40e_pf *pf = container_of(work, struct i40e_pf,
-                                         ptp_tx_work);
-       struct i40e_hw *hw = &pf->hw;
-       u32 prttsyn_stat_0;
-
-       if (!pf->ptp_tx_skb)
-               return;
-
-       if (time_is_before_jiffies(pf->ptp_tx_start +
-                                  I40E_PTP_TX_TIMEOUT)) {
-               dev_kfree_skb_any(pf->ptp_tx_skb);
-               pf->ptp_tx_skb = NULL;
-               pf->tx_hwtstamp_timeouts++;
-               dev_warn(&pf->pdev->dev, "clearing Tx timestamp hang\n");
-               return;
-       }
-
-       prttsyn_stat_0 = rd32(hw, I40E_PRTTSYN_STAT_0);
-       if (prttsyn_stat_0 & I40E_PRTTSYN_STAT_0_TXTIME_MASK)
-               i40e_ptp_tx_hwtstamp(pf);
-       else
-               schedule_work(&pf->ptp_tx_work);
-}
-
 /**
  * i40e_ptp_enable - Enable/disable ancillary features of the PHC subsystem
  * @ptp: The PTP clock structure
@@ -608,7 +573,6 @@ void i40e_ptp_init(struct i40e_pf *pf)
                u32 regval;
 
                spin_lock_init(&pf->tmreg_lock);
-               INIT_WORK(&pf->ptp_tx_work, i40e_ptp_tx_work);
 
                dev_info(&pf->pdev->dev, "%s: added PHC on %s\n", __func__,
                         netdev->name);
@@ -647,7 +611,6 @@ void i40e_ptp_stop(struct i40e_pf *pf)
        pf->ptp_tx = false;
        pf->ptp_rx = false;
 
-       cancel_work_sync(&pf->ptp_tx_work);
        if (pf->ptp_tx_skb) {
                dev_kfree_skb_any(pf->ptp_tx_skb);
                pf->ptp_tx_skb = NULL;
index 1d40f425acf1833657902dce6dd7206b5493e7eb..947de98500f33a4162c441967f6a6bacdd0ab148 100644 (file)
 #define I40E_PFINT_ICR0_GPIO_MASK (0x1 << I40E_PFINT_ICR0_GPIO_SHIFT)
 #define I40E_PFINT_ICR0_TIMESYNC_SHIFT 23
 #define I40E_PFINT_ICR0_TIMESYNC_MASK (0x1 << I40E_PFINT_ICR0_TIMESYNC_SHIFT)
-#define I40E_PFINT_ICR0_STORM_DETECT_SHIFT 24
-#define I40E_PFINT_ICR0_STORM_DETECT_MASK (0x1 << I40E_PFINT_ICR0_STORM_DETECT_SHIFT)
 #define I40E_PFINT_ICR0_LINK_STAT_CHANGE_SHIFT 25
 #define I40E_PFINT_ICR0_LINK_STAT_CHANGE_MASK (0x1 << I40E_PFINT_ICR0_LINK_STAT_CHANGE_SHIFT)
 #define I40E_PFINT_ICR0_HMC_ERR_SHIFT 26
 #define I40E_PFINT_ICR0_ENA_GPIO_MASK (0x1 << I40E_PFINT_ICR0_ENA_GPIO_SHIFT)
 #define I40E_PFINT_ICR0_ENA_TIMESYNC_SHIFT 23
 #define I40E_PFINT_ICR0_ENA_TIMESYNC_MASK (0x1 << I40E_PFINT_ICR0_ENA_TIMESYNC_SHIFT)
-#define I40E_PFINT_ICR0_ENA_STORM_DETECT_SHIFT 24
-#define I40E_PFINT_ICR0_ENA_STORM_DETECT_MASK (0x1 << I40E_PFINT_ICR0_ENA_STORM_DETECT_SHIFT)
 #define I40E_PFINT_ICR0_ENA_LINK_STAT_CHANGE_SHIFT 25
 #define I40E_PFINT_ICR0_ENA_LINK_STAT_CHANGE_MASK (0x1 << I40E_PFINT_ICR0_ENA_LINK_STAT_CHANGE_SHIFT)
 #define I40E_PFINT_ICR0_ENA_HMC_ERR_SHIFT 26
 #define I40E_GLLAN_TSOMSK_M 0x000442DC
 #define I40E_GLLAN_TSOMSK_M_TCPMSKM_SHIFT 0
 #define I40E_GLLAN_TSOMSK_M_TCPMSKM_MASK (0xFFF << I40E_GLLAN_TSOMSK_M_TCPMSKM_SHIFT)
+#define I40E_GLLAN_TXPRE_QDIS(_i) (0x000E6500 + ((_i) * 4)) /* i=0..11 */
+#define I40E_GLLAN_TXPRE_QDIS_QINDX_SHIFT 0
+#define I40E_GLLAN_TXPRE_QDIS_QINDX_MASK (0x7FF << I40E_GLLAN_TXPRE_QDIS_QINDX_SHIFT)
+#define I40E_GLLAN_TXPRE_QDIS_SET_QDIS_SHIFT 30
+#define I40E_GLLAN_TXPRE_QDIS_SET_QDIS_MASK (0x1 << I40E_GLLAN_TXPRE_QDIS_SET_QDIS_SHIFT)
+#define I40E_GLLAN_TXPRE_QDIS_CLEAR_QDIS_SHIFT 31
+#define I40E_GLLAN_TXPRE_QDIS_CLEAR_QDIS_MASK (0x1 << I40E_GLLAN_TXPRE_QDIS_CLEAR_QDIS_SHIFT)
+
 #define I40E_PFLAN_QALLOC 0x001C0400
 #define I40E_PFLAN_QALLOC_FIRSTQ_SHIFT 0
 #define I40E_PFLAN_QALLOC_FIRSTQ_MASK (0x7FF << I40E_PFLAN_QALLOC_FIRSTQ_SHIFT)
index 9478ddc66caf8d71cfecd6c322e635d247e3fd95..e49f31dbd5d87cfdd4268c8837053746ef27c3dc 100644 (file)
@@ -24,6 +24,7 @@
  *
  ******************************************************************************/
 
+#include <linux/prefetch.h>
 #include "i40e.h"
 #include "i40e_prototype.h"
 
@@ -61,7 +62,7 @@ int i40e_program_fdir_filter(struct i40e_fdir_filter *fdir_data, u8 *raw_packet,
 
        /* find existing FDIR VSI */
        vsi = NULL;
-       for (i = 0; i < pf->hw.func_caps.num_vsis; i++)
+       for (i = 0; i < pf->num_alloc_vsi; i++)
                if (pf->vsi[i] && pf->vsi[i]->type == I40E_VSI_FDIR)
                        vsi = pf->vsi[i];
        if (!vsi)
@@ -120,7 +121,7 @@ int i40e_program_fdir_filter(struct i40e_fdir_filter *fdir_data, u8 *raw_packet,
                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;
+                       I40E_TXD_FLTR_QW1_CNTINDEX_MASK;
        }
 
        fdir_desc->dtype_cmd_cntindex = cpu_to_le32(dcc);
@@ -183,7 +184,6 @@ static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi,
        struct iphdr *ip;
        bool err = false;
        int ret;
-       int i;
        static char packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0,
                0x45, 0, 0, 0x1c, 0, 0, 0x40, 0, 0x40, 0x11, 0, 0, 0, 0, 0, 0,
                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
@@ -199,21 +199,17 @@ static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi,
        ip->saddr = fd_data->src_ip[0];
        udp->source = fd_data->src_port;
 
-       for (i = I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP;
-            i <= I40E_FILTER_PCTYPE_NONF_IPV4_UDP; i++) {
-               fd_data->pctype = i;
-               ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add);
-
-               if (ret) {
-                       dev_info(&pf->pdev->dev,
-                                "Filter command send failed for PCTYPE %d (ret = %d)\n",
-                                fd_data->pctype, ret);
-                       err = true;
-               } else {
-                       dev_info(&pf->pdev->dev,
-                                "Filter OK for PCTYPE %d (ret = %d)\n",
-                                fd_data->pctype, ret);
-               }
+       fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
+       ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add);
+       if (ret) {
+               dev_info(&pf->pdev->dev,
+                        "Filter command send failed for PCTYPE %d (ret = %d)\n",
+                        fd_data->pctype, ret);
+               err = true;
+       } else {
+               dev_info(&pf->pdev->dev,
+                        "Filter OK for PCTYPE %d (ret = %d)\n",
+                        fd_data->pctype, ret);
        }
 
        return err ? -EOPNOTSUPP : 0;
@@ -262,7 +258,7 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi,
                }
        }
 
-       fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN;
+       fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP;
        ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add);
 
        if (ret) {
@@ -455,22 +451,20 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring,
 
                /* filter programming failed most likely due to table full */
                fcnt_prog = i40e_get_current_fd_count(pf);
-               fcnt_avail = pf->hw.fdir_shared_filter_count +
-                                                      pf->fdir_pf_filter_count;
-
+               fcnt_avail = i40e_get_fd_cnt_all(pf);
                /* If ATR is running fcnt_prog can quickly change,
                 * if we are very close to full, it makes sense to disable
                 * FD ATR/SB and then re-enable it when there is room.
                 */
                if (fcnt_prog >= (fcnt_avail - I40E_FDIR_BUFFER_FULL_MARGIN)) {
                        /* Turn off ATR first */
-                       if (pf->flags | I40E_FLAG_FD_ATR_ENABLED) {
+                       if (pf->flags & I40E_FLAG_FD_ATR_ENABLED) {
                                pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED;
                                dev_warn(&pdev->dev, "FD filter space full, ATR for further flows will be turned off\n");
                                pf->auto_disable_flags |=
                                                       I40E_FLAG_FD_ATR_ENABLED;
                                pf->flags |= I40E_FLAG_FDIR_REQUIRES_REINIT;
-                       } else if (pf->flags | I40E_FLAG_FD_SB_ENABLED) {
+                       } else if (pf->flags & I40E_FLAG_FD_SB_ENABLED) {
                                pf->flags &= ~I40E_FLAG_FD_SB_ENABLED;
                                dev_warn(&pdev->dev, "FD filter space full, new ntuple rules will not be added\n");
                                pf->auto_disable_flags |=
@@ -1199,10 +1193,12 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
                                    u32 rx_error,
                                    u16 rx_ptype)
 {
+       struct i40e_rx_ptype_decoded decoded = decode_rx_desc_ptype(rx_ptype);
+       bool ipv4 = false, ipv6 = false;
        bool ipv4_tunnel, ipv6_tunnel;
        __wsum rx_udp_csum;
-       __sum16 csum;
        struct iphdr *iph;
+       __sum16 csum;
 
        ipv4_tunnel = (rx_ptype > I40E_RX_PTYPE_GRENAT4_MAC_PAY3) &&
                      (rx_ptype < I40E_RX_PTYPE_GRENAT4_MACVLAN_IPV6_ICMP_PAY4);
@@ -1213,29 +1209,57 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
        skb->ip_summed = CHECKSUM_NONE;
 
        /* Rx csum enabled and ip headers found? */
-       if (!(vsi->netdev->features & NETIF_F_RXCSUM &&
-             rx_status & (1 << I40E_RX_DESC_STATUS_L3L4P_SHIFT)))
+       if (!(vsi->netdev->features & NETIF_F_RXCSUM))
+               return;
+
+       /* did the hardware decode the packet and checksum? */
+       if (!(rx_status & (1 << I40E_RX_DESC_STATUS_L3L4P_SHIFT)))
+               return;
+
+       /* both known and outer_ip must be set for the below code to work */
+       if (!(decoded.known && decoded.outer_ip))
                return;
 
+       if (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP &&
+           decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV4)
+               ipv4 = true;
+       else if (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP &&
+                decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV6)
+               ipv6 = true;
+
+       if (ipv4 &&
+           (rx_error & ((1 << I40E_RX_DESC_ERROR_IPE_SHIFT) |
+                        (1 << I40E_RX_DESC_ERROR_EIPE_SHIFT))))
+               goto checksum_fail;
+
        /* likely incorrect csum if alternate IP extension headers found */
-       if (rx_status & (1 << I40E_RX_DESC_STATUS_IPV6EXADD_SHIFT))
+       if (ipv6 &&
+           decoded.inner_prot == I40E_RX_PTYPE_INNER_PROT_TCP &&
+           rx_error & (1 << I40E_RX_DESC_ERROR_L4E_SHIFT) &&
+           rx_status & (1 << I40E_RX_DESC_STATUS_IPV6EXADD_SHIFT))
+               /* don't increment checksum err here, non-fatal err */
                return;
 
-       /* IP or L4 or outmost IP checksum error */
-       if (rx_error & ((1 << I40E_RX_DESC_ERROR_IPE_SHIFT) |
-                       (1 << I40E_RX_DESC_ERROR_L4E_SHIFT) |
-                       (1 << I40E_RX_DESC_ERROR_EIPE_SHIFT))) {
-               vsi->back->hw_csum_rx_error++;
+       /* there was some L4 error, count error and punt packet to the stack */
+       if (rx_error & (1 << I40E_RX_DESC_ERROR_L4E_SHIFT))
+               goto checksum_fail;
+
+       /* handle packets that were not able to be checksummed due
+        * to arrival speed, in this case the stack can compute
+        * the csum.
+        */
+       if (rx_error & (1 << I40E_RX_DESC_ERROR_PPRS_SHIFT))
                return;
-       }
 
+       /* If VXLAN traffic has an outer UDPv4 checksum we need to check
+        * it in the driver, hardware does not do it for us.
+        * Since L3L4P bit was set we assume a valid IHL value (>=5)
+        * so the total length of IPv4 header is IHL*4 bytes
+        * The UDP_0 bit *may* bet set if the *inner* header is UDP
+        */
        if (ipv4_tunnel &&
+           (decoded.inner_prot != I40E_RX_PTYPE_INNER_PROT_UDP) &&
            !(rx_status & (1 << I40E_RX_DESC_STATUS_UDP_0_SHIFT))) {
-               /* If VXLAN traffic has an outer UDPv4 checksum we need to check
-                * it in the driver, hardware does not do it for us.
-                * Since L3L4P bit was set we assume a valid IHL value (>=5)
-                * so the total length of IPv4 header is IHL*4 bytes
-                */
                skb->transport_header = skb->mac_header +
                                        sizeof(struct ethhdr) +
                                        (ip_hdr(skb)->ihl * 4);
@@ -1252,13 +1276,16 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
                                (skb->len - skb_transport_offset(skb)),
                                IPPROTO_UDP, rx_udp_csum);
 
-               if (udp_hdr(skb)->check != csum) {
-                       vsi->back->hw_csum_rx_error++;
-                       return;
-               }
+               if (udp_hdr(skb)->check != csum)
+                       goto checksum_fail;
        }
 
        skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+       return;
+
+checksum_fail:
+       vsi->back->hw_csum_rx_error++;
 }
 
 /**
@@ -1435,6 +1462,9 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget)
                /* ERR_MASK will only have valid bits if EOP set */
                if (unlikely(rx_error & (1 << I40E_RX_DESC_ERROR_RXE_SHIFT))) {
                        dev_kfree_skb_any(skb);
+                       /* TODO: shouldn't we increment a counter indicating the
+                        * drop?
+                        */
                        goto next_desc;
                }
 
@@ -1665,6 +1695,11 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
        dtype_cmd |= I40E_FILTER_PROGRAM_DESC_FD_STATUS_FD_ID <<
                     I40E_TXD_FLTR_QW1_FD_STATUS_SHIFT;
 
+       dtype_cmd |= I40E_TXD_FLTR_QW1_CNT_ENA_MASK;
+       dtype_cmd |=
+               ((u32)pf->fd_atr_cnt_idx << I40E_TXD_FLTR_QW1_CNTINDEX_SHIFT) &
+               I40E_TXD_FLTR_QW1_CNTINDEX_MASK;
+
        fdir_desc->qindex_flex_ptype_vsi = cpu_to_le32(flex_ptype);
        fdir_desc->dtype_cmd_cntindex = cpu_to_le32(dtype_cmd);
 }
@@ -1825,9 +1860,6 @@ static int i40e_tsyn(struct i40e_ring *tx_ring, struct sk_buff *skb,
        *cd_type_cmd_tso_mss |= (u64)I40E_TX_CTX_DESC_TSYN <<
                                I40E_TXD_CTX_QW1_CMD_SHIFT;
 
-       pf->ptp_tx_start = jiffies;
-       schedule_work(&pf->ptp_tx_work);
-
        return 1;
 }
 
@@ -2179,9 +2211,7 @@ static int i40e_maybe_stop_tx(struct i40e_ring *tx_ring, int size)
 static int i40e_xmit_descriptor_count(struct sk_buff *skb,
                                      struct i40e_ring *tx_ring)
 {
-#if PAGE_SIZE > I40E_MAX_DATA_PER_TXD
        unsigned int f;
-#endif
        int count = 0;
 
        /* need: 1 descriptor per page * PAGE_SIZE/I40E_MAX_DATA_PER_TXD,
@@ -2190,12 +2220,9 @@ static int i40e_xmit_descriptor_count(struct sk_buff *skb,
         *       + 1 desc for context descriptor,
         * otherwise try next time
         */
-#if PAGE_SIZE > I40E_MAX_DATA_PER_TXD
        for (f = 0; f < skb_shinfo(skb)->nr_frags; f++)
                count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size);
-#else
-       count += skb_shinfo(skb)->nr_frags;
-#endif
+
        count += TXD_USE_COUNT(skb_headlen(skb));
        if (i40e_maybe_stop_tx(tx_ring, count + 4 + 1)) {
                tx_ring->tx_stats.tx_busy++;
index d5349698e513b511a8ac9e5e6c798d888cdce409..0277894fe1c46db030045d819484ccc6f2cb6f72 100644 (file)
@@ -27,7 +27,7 @@
 #ifndef _I40E_TXRX_H_
 #define _I40E_TXRX_H_
 
-/* Interrupt Throttling and Rate Limiting (storm control) Goodies */
+/* Interrupt Throttling and Rate Limiting Goodies */
 
 #define I40E_MAX_ITR               0x0FF0  /* reg uses 2 usec resolution */
 #define I40E_MIN_ITR               0x0004  /* reg uses 2 usec resolution */
@@ -69,16 +69,11 @@ enum i40e_dyn_idx_t {
 
 /* Supported RSS offloads */
 #define I40E_DEFAULT_RSS_HENA ( \
-       ((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) | \
-       ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_SCTP) | \
-       ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_OTHER) | \
        ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4) | \
-       ((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) | \
-       ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP) | \
@@ -122,11 +117,11 @@ enum i40e_dyn_idx_t {
 #define i40e_rx_desc i40e_32byte_rx_desc
 
 #define I40E_MIN_TX_LEN                17
-#define I40E_MAX_DATA_PER_TXD  16383   /* aka 16kB - 1 */
+#define I40E_MAX_DATA_PER_TXD  8192
 
 /* Tx Descriptors needed, worst case */
 #define TXD_USE_COUNT(S) DIV_ROUND_UP((S), I40E_MAX_DATA_PER_TXD)
-#define DESC_NEEDED ((MAX_SKB_FRAGS * TXD_USE_COUNT(PAGE_SIZE)) + 4)
+#define DESC_NEEDED (MAX_SKB_FRAGS + 4)
 
 #define I40E_TX_FLAGS_CSUM             (u32)(1)
 #define I40E_TX_FLAGS_HW_VLAN          (u32)(1 << 1)
@@ -184,7 +179,6 @@ enum i40e_ring_state_t {
        __I40E_TX_DETECT_HANG,
        __I40E_HANG_CHECK_ARMED,
        __I40E_RX_PS_ENABLED,
-       __I40E_RX_LRO_ENABLED,
        __I40E_RX_16BYTE_DESC_ENABLED,
 };
 
@@ -200,12 +194,6 @@ enum i40e_ring_state_t {
        set_bit(__I40E_TX_DETECT_HANG, &(ring)->state)
 #define clear_check_for_tx_hang(ring) \
        clear_bit(__I40E_TX_DETECT_HANG, &(ring)->state)
-#define ring_is_lro_enabled(ring) \
-       test_bit(__I40E_RX_LRO_ENABLED, &(ring)->state)
-#define set_ring_lro_enabled(ring) \
-       set_bit(__I40E_RX_LRO_ENABLED, &(ring)->state)
-#define clear_ring_lro_enabled(ring) \
-       clear_bit(__I40E_RX_LRO_ENABLED, &(ring)->state)
 #define ring_is_16byte_desc_enabled(ring) \
        test_bit(__I40E_RX_16BYTE_DESC_ENABLED, &(ring)->state)
 #define set_ring_16byte_desc_enabled(ring) \
index 71a968fe557f33e12f6feb81d416f4f5a55946fb..9d39ff23c5fbfbbed52ec0ba5437e585fc3a0c68 100644 (file)
 
 /* Device IDs */
 #define I40E_DEV_ID_SFP_XL710          0x1572
-#define I40E_DEV_ID_SFP_X710           0x1573
 #define I40E_DEV_ID_QEMU               0x1574
 #define I40E_DEV_ID_KX_A               0x157F
 #define I40E_DEV_ID_KX_B               0x1580
 #define I40E_DEV_ID_KX_C               0x1581
-#define I40E_DEV_ID_KX_D               0x1582
 #define I40E_DEV_ID_QSFP_A             0x1583
 #define I40E_DEV_ID_QSFP_B             0x1584
 #define I40E_DEV_ID_QSFP_C             0x1585
@@ -60,8 +58,8 @@
 /* Max default timeout in ms, */
 #define I40E_MAX_NVM_TIMEOUT           18000
 
-/* Switch from mc to the 2usec global time (this is the GTIME resolution) */
-#define I40E_MS_TO_GTIME(time)         (((time) * 1000) / 2)
+/* Switch from ms to the 1usec global time (this is the GTIME resolution) */
+#define I40E_MS_TO_GTIME(time)         ((time) * 1000)
 
 /* forward declaration */
 struct i40e_hw;
@@ -167,6 +165,9 @@ struct i40e_link_status {
        u8 loopback;
        /* is Link Status Event notification to SW enabled */
        bool lse_enable;
+       u16 max_frame_size;
+       bool crc_enable;
+       u8 pacing;
 };
 
 struct i40e_phy_info {
@@ -409,6 +410,7 @@ struct i40e_driver_version {
        u8 minor_version;
        u8 build_version;
        u8 subbuild_version;
+       u8 driver_string[32];
 };
 
 /* RX Descriptors */
@@ -488,9 +490,6 @@ union i40e_32byte_rx_desc {
        } wb;  /* writeback */
 };
 
-#define I40E_RXD_QW1_STATUS_SHIFT      0
-#define I40E_RXD_QW1_STATUS_MASK       (0x7FFFUL << I40E_RXD_QW1_STATUS_SHIFT)
-
 enum i40e_rx_desc_status_bits {
        /* Note: These are predefined bit offsets */
        I40E_RX_DESC_STATUS_DD_SHIFT            = 0,
@@ -507,9 +506,14 @@ enum i40e_rx_desc_status_bits {
        I40E_RX_DESC_STATUS_LPBK_SHIFT          = 14,
        I40E_RX_DESC_STATUS_IPV6EXADD_SHIFT     = 15,
        I40E_RX_DESC_STATUS_RESERVED_SHIFT      = 16, /* 2 BITS */
-       I40E_RX_DESC_STATUS_UDP_0_SHIFT         = 18
+       I40E_RX_DESC_STATUS_UDP_0_SHIFT         = 18,
+       I40E_RX_DESC_STATUS_LAST /* this entry must be last!!! */
 };
 
+#define I40E_RXD_QW1_STATUS_SHIFT      0
+#define I40E_RXD_QW1_STATUS_MASK       (((1 << I40E_RX_DESC_STATUS_LAST) - 1) \
+                                        << I40E_RXD_QW1_STATUS_SHIFT)
+
 #define I40E_RXD_QW1_STATUS_TSYNINDX_SHIFT   I40E_RX_DESC_STATUS_TSYNINDX_SHIFT
 #define I40E_RXD_QW1_STATUS_TSYNINDX_MASK      (0x3UL << \
                                             I40E_RXD_QW1_STATUS_TSYNINDX_SHIFT)
@@ -537,7 +541,8 @@ enum i40e_rx_desc_error_bits {
        I40E_RX_DESC_ERROR_IPE_SHIFT            = 3,
        I40E_RX_DESC_ERROR_L4E_SHIFT            = 4,
        I40E_RX_DESC_ERROR_EIPE_SHIFT           = 5,
-       I40E_RX_DESC_ERROR_OVERSIZE_SHIFT       = 6
+       I40E_RX_DESC_ERROR_OVERSIZE_SHIFT       = 6,
+       I40E_RX_DESC_ERROR_PPRS_SHIFT           = 7
 };
 
 enum i40e_rx_desc_error_l3l4e_fcoe_masks {
@@ -658,7 +663,6 @@ enum i40e_rx_desc_ext_status_bits {
        I40E_RX_DESC_EXT_STATUS_L2TAG3P_SHIFT   = 1,
        I40E_RX_DESC_EXT_STATUS_FLEXBL_SHIFT    = 2, /* 2 BITS */
        I40E_RX_DESC_EXT_STATUS_FLEXBH_SHIFT    = 4, /* 2 BITS */
-       I40E_RX_DESC_EXT_STATUS_FTYPE_SHIFT     = 6, /* 3 BITS */
        I40E_RX_DESC_EXT_STATUS_FDLONGB_SHIFT   = 9,
        I40E_RX_DESC_EXT_STATUS_FCOELONGB_SHIFT = 10,
        I40E_RX_DESC_EXT_STATUS_PELONGB_SHIFT   = 11,
@@ -862,18 +866,14 @@ struct i40e_filter_program_desc {
 
 /* Packet Classifier Types for filters */
 enum i40e_filter_pctype {
-       /* Note: Values 0-28 are reserved for future use */
-       I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP        = 29,
-       I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP      = 30,
+       /* Note: Values 0-30 are reserved for future use */
        I40E_FILTER_PCTYPE_NONF_IPV4_UDP                = 31,
-       I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN            = 32,
+       /* Note: Value 32 is reserved for future use */
        I40E_FILTER_PCTYPE_NONF_IPV4_TCP                = 33,
        I40E_FILTER_PCTYPE_NONF_IPV4_SCTP               = 34,
        I40E_FILTER_PCTYPE_NONF_IPV4_OTHER              = 35,
        I40E_FILTER_PCTYPE_FRAG_IPV4                    = 36,
-       /* Note: Values 37-38 are reserved for future use */
-       I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP        = 39,
-       I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP      = 40,
+       /* Note: Values 37-40 are reserved for future use */
        I40E_FILTER_PCTYPE_NONF_IPV6_UDP                = 41,
        I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN            = 42,
        I40E_FILTER_PCTYPE_NONF_IPV6_TCP                = 43,
@@ -955,6 +955,16 @@ struct i40e_vsi_context {
        struct i40e_aqc_vsi_properties_data info;
 };
 
+struct i40e_veb_context {
+       u16 seid;
+       u16 uplink_seid;
+       u16 veb_number;
+       u16 vebs_allocated;
+       u16 vebs_unallocated;
+       u16 flags;
+       struct i40e_aqc_get_veb_parameters_completion info;
+};
+
 /* Statistics collected by each port, VSI, VEB, and S-channel */
 struct i40e_eth_stats {
        u64 rx_bytes;                   /* gorc */
@@ -962,8 +972,6 @@ struct i40e_eth_stats {
        u64 rx_multicast;               /* mprc */
        u64 rx_broadcast;               /* bprc */
        u64 rx_discards;                /* rdpc */
-       u64 rx_errors;                  /* repc */
-       u64 rx_missed;                  /* rmpc */
        u64 rx_unknown_protocol;        /* rupp */
        u64 tx_bytes;                   /* gotc */
        u64 tx_unicast;                 /* uptc */
@@ -1015,9 +1023,12 @@ struct i40e_hw_port_stats {
        u64 tx_size_big;                /* ptc9522 */
        u64 mac_short_packet_dropped;   /* mspdc */
        u64 checksum_error;             /* xec */
+       /* flow director stats */
+       u64 fd_atr_match;
+       u64 fd_sb_match;
        /* EEE LPI */
-       bool tx_lpi_status;
-       bool rx_lpi_status;
+       u32 tx_lpi_status;
+       u32 rx_lpi_status;
        u64 tx_lpi_count;               /* etlpic */
        u64 rx_lpi_count;               /* erlpic */
 };
index 22a1b69cd6464ff072248b59e6acf421da0215f0..70951d2edcad9f04a9cc4bac25701dc61a4c4484 100644 (file)
@@ -341,10 +341,6 @@ struct i40e_virtchnl_pf_event {
        int severity;
 };
 
-/* The following are TBD, not necessary for LAN functionality.
- * I40E_VIRTCHNL_OP_FCOE
- */
-
 /* VF reset states - these are written into the RSTAT register:
  * I40E_VFGEN_RSTAT1 on the PF
  * I40E_VFGEN_RSTAT on the VF
index 02c11a7f7d29e80c533b48894e5ee0b5619a9c91..f5b9d20625736b5dc451ac32dc25cc38d7a0d99c 100644 (file)
 
 /***********************misc routines*****************************/
 
+/**
+ * i40e_vc_disable_vf
+ * @pf: pointer to the pf info
+ * @vf: pointer to the vf info
+ *
+ * Disable the VF through a SW reset
+ **/
+static inline void i40e_vc_disable_vf(struct i40e_pf *pf, struct i40e_vf *vf)
+{
+       struct i40e_hw *hw = &pf->hw;
+       u32 reg;
+
+       reg = rd32(hw, I40E_VPGEN_VFRTRIG(vf->vf_id));
+       reg |= I40E_VPGEN_VFRTRIG_VFSWR_MASK;
+       wr32(hw, I40E_VPGEN_VFRTRIG(vf->vf_id), reg);
+       i40e_flush(hw);
+}
+
 /**
  * i40e_vc_isvalid_vsi_id
  * @vf: pointer to the vf info
@@ -230,9 +248,8 @@ static int i40e_config_vsi_tx_queue(struct i40e_vf *vf, u16 vsi_idx,
        tx_ctx.qlen = info->ring_len;
        tx_ctx.rdylist = le16_to_cpu(pf->vsi[vsi_idx]->info.qs_handle[0]);
        tx_ctx.rdylist_act = 0;
-       tx_ctx.head_wb_ena = 1;
-       tx_ctx.head_wb_addr = info->dma_ring_addr +
-                             (info->ring_len * sizeof(struct i40e_tx_desc));
+       tx_ctx.head_wb_ena = info->headwb_enabled;
+       tx_ctx.head_wb_addr = info->dma_headwb_addr;
 
        /* clear the context in the HMC */
        ret = i40e_clear_lan_tx_queue_context(hw, pf_queue_id);
@@ -336,6 +353,7 @@ static int i40e_config_vsi_rx_queue(struct i40e_vf *vf, u16 vsi_idx,
        rx_ctx.tphhead_ena = 1;
        rx_ctx.lrxqthresh = 2;
        rx_ctx.crcstrip = 1;
+       rx_ctx.prefena = 1;
 
        /* clear the context in the HMC */
        ret = i40e_clear_lan_rx_queue_context(hw, pf_queue_id);
@@ -416,6 +434,15 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type)
        if (ret)
                dev_err(&pf->pdev->dev, "Unable to program ucast filters\n");
 
+       /* Set VF bandwidth if specified */
+       if (vf->tx_rate) {
+               ret = i40e_aq_config_vsi_bw_limit(&pf->hw, vsi->seid,
+                                                 vf->tx_rate / 50, 0, NULL);
+               if (ret)
+                       dev_err(&pf->pdev->dev, "Unable to set tx rate, VF %d, error code %d.\n",
+                               vf->vf_id, ret);
+       }
+
 error_alloc_vsi_res:
        return ret;
 }
@@ -815,6 +842,10 @@ void i40e_free_vfs(struct i40e_pf *pf)
        kfree(pf->vf);
        pf->vf = NULL;
 
+       /* This check is for when the driver is unloaded while VFs are
+        * assigned. Setting the number of VFs to 0 through sysfs is caught
+        * before this function ever gets called.
+        */
        if (!i40e_vfs_are_assigned(pf)) {
                pci_disable_sriov(pf->pdev);
                /* Acknowledge VFLR for all VFS. Without this, VFs will fail to
@@ -867,6 +898,7 @@ int i40e_alloc_vfs(struct i40e_pf *pf, u16 num_alloc_vfs)
                ret = -ENOMEM;
                goto err_alloc;
        }
+       pf->vf = vfs;
 
        /* apply default profile */
        for (i = 0; i < num_alloc_vfs; i++) {
@@ -876,13 +908,13 @@ int i40e_alloc_vfs(struct i40e_pf *pf, u16 num_alloc_vfs)
 
                /* assign default capabilities */
                set_bit(I40E_VIRTCHNL_VF_CAP_L2, &vfs[i].vf_caps);
+               vfs[i].spoofchk = true;
                /* vf resources get allocated during reset */
                i40e_reset_vf(&vfs[i], false);
 
                /* enable vf vplan_qtable mappings */
                i40e_enable_vf_mappings(&vfs[i]);
        }
-       pf->vf = vfs;
        pf->num_alloc_vfs = num_alloc_vfs;
 
        i40e_enable_pf_switch_lb(pf);
@@ -951,7 +983,12 @@ int i40e_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
        if (num_vfs)
                return i40e_pci_sriov_enable(pdev, num_vfs);
 
-       i40e_free_vfs(pf);
+       if (!i40e_vfs_are_assigned(pf)) {
+               i40e_free_vfs(pf);
+       } else {
+               dev_warn(&pdev->dev, "Unable to free VFs because some are assigned to VMs.\n");
+               return -EINVAL;
+       }
        return 0;
 }
 
@@ -2022,16 +2059,14 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
        }
 
        /* delete the temporary mac address */
-       i40e_del_filter(vsi, vf->default_lan_addr.addr, 0, true, false);
+       i40e_del_filter(vsi, vf->default_lan_addr.addr, vf->port_vlan_id,
+                       true, false);
 
-       /* add the new mac address */
-       f = i40e_add_filter(vsi, mac, 0, true, false);
-       if (!f) {
-               dev_err(&pf->pdev->dev,
-                       "Unable to add VF ucast filter\n");
-               ret = -ENOMEM;
-               goto error_param;
-       }
+       /* Delete all the filters for this VSI - we're going to kill it
+        * anyway.
+        */
+       list_for_each_entry(f, &vsi->mac_filter_list, list)
+               i40e_del_filter(vsi, f->macaddr, f->vlan, true, false);
 
        dev_info(&pf->pdev->dev, "Setting MAC %pM on VF %d\n", mac, vf_id);
        /* program mac filter */
@@ -2040,7 +2075,7 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
                ret = -EIO;
                goto error_param;
        }
-       memcpy(vf->default_lan_addr.addr, mac, ETH_ALEN);
+       ether_addr_copy(vf->default_lan_addr.addr, mac);
        vf->pf_set_mac = true;
        dev_info(&pf->pdev->dev, "Reload the VF driver to make this change effective.\n");
        ret = 0;
@@ -2088,18 +2123,28 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev,
                goto error_pvid;
        }
 
-       if (vsi->info.pvid == 0 && i40e_is_vsi_in_vlan(vsi))
+       if (vsi->info.pvid == 0 && i40e_is_vsi_in_vlan(vsi)) {
                dev_err(&pf->pdev->dev,
                        "VF %d has already configured VLAN filters and the administrator is requesting a port VLAN override.\nPlease unload and reload the VF driver for this change to take effect.\n",
                        vf_id);
+               /* Administrator Error - knock the VF offline until he does
+                * the right thing by reconfiguring his network correctly
+                * and then reloading the VF driver.
+                */
+               i40e_vc_disable_vf(pf, vf);
+       }
 
        /* Check for condition where there was already a port VLAN ID
         * filter set and now it is being deleted by setting it to zero.
+        * Additionally check for the condition where there was a port
+        * VLAN but now there is a new and different port VLAN being set.
         * Before deleting all the old VLAN filters we must add new ones
         * with -1 (I40E_VLAN_ANY) or otherwise we're left with all our
         * MAC addresses deleted.
         */
-       if (!(vlan_id || qos) && vsi->info.pvid)
+       if ((!(vlan_id || qos) ||
+           (vlan_id | qos) != le16_to_cpu(vsi->info.pvid)) &&
+           vsi->info.pvid)
                ret = i40e_vsi_add_vlan(vsi, I40E_VLAN_ANY);
 
        if (vsi->info.pvid) {
@@ -2150,6 +2195,8 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev,
        return ret;
 }
 
+#define I40E_BW_CREDIT_DIVISOR 50     /* 50Mbps per BW credit */
+#define I40E_MAX_BW_INACTIVE_ACCUM 4  /* device can accumulate 4 credits max */
 /**
  * i40e_ndo_set_vf_bw
  * @netdev: network interface device structure
@@ -2158,9 +2205,76 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev,
  *
  * configure vf tx rate
  **/
-int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int tx_rate)
+int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate,
+                      int max_tx_rate)
 {
-       return -EOPNOTSUPP;
+       struct i40e_netdev_priv *np = netdev_priv(netdev);
+       struct i40e_pf *pf = np->vsi->back;
+       struct i40e_vsi *vsi;
+       struct i40e_vf *vf;
+       int speed = 0;
+       int ret = 0;
+
+       /* validate the request */
+       if (vf_id >= pf->num_alloc_vfs) {
+               dev_err(&pf->pdev->dev, "Invalid VF Identifier %d.\n", vf_id);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       if (min_tx_rate) {
+               dev_err(&pf->pdev->dev, "Invalid min tx rate (%d) (greater than 0) specified for vf %d.\n",
+                       min_tx_rate, vf_id);
+               return -EINVAL;
+       }
+
+       vf = &(pf->vf[vf_id]);
+       vsi = pf->vsi[vf->lan_vsi_index];
+       if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) {
+               dev_err(&pf->pdev->dev, "Uninitialized VF %d.\n", vf_id);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       switch (pf->hw.phy.link_info.link_speed) {
+       case I40E_LINK_SPEED_40GB:
+               speed = 40000;
+               break;
+       case I40E_LINK_SPEED_10GB:
+               speed = 10000;
+               break;
+       case I40E_LINK_SPEED_1GB:
+               speed = 1000;
+               break;
+       default:
+               break;
+       }
+
+       if (max_tx_rate > speed) {
+               dev_err(&pf->pdev->dev, "Invalid max tx rate %d specified for vf %d.",
+                       max_tx_rate, vf->vf_id);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       if ((max_tx_rate < 50) && (max_tx_rate > 0)) {
+               dev_warn(&pf->pdev->dev, "Setting max Tx rate to minimum usable value of 50Mbps.\n");
+               max_tx_rate = 50;
+       }
+
+       /* Tx rate credits are in values of 50Mbps, 0 is disabled*/
+       ret = i40e_aq_config_vsi_bw_limit(&pf->hw, vsi->seid,
+                                         max_tx_rate / I40E_BW_CREDIT_DIVISOR,
+                                         I40E_MAX_BW_INACTIVE_ACCUM, NULL);
+       if (ret) {
+               dev_err(&pf->pdev->dev, "Unable to set max tx rate, error code %d.\n",
+                       ret);
+               ret = -EIO;
+               goto error;
+       }
+       vf->tx_rate = max_tx_rate;
+error:
+       return ret;
 }
 
 /**
@@ -2200,10 +2314,18 @@ int i40e_ndo_get_vf_config(struct net_device *netdev,
 
        memcpy(&ivi->mac, vf->default_lan_addr.addr, ETH_ALEN);
 
-       ivi->tx_rate = 0;
+       ivi->max_tx_rate = vf->tx_rate;
+       ivi->min_tx_rate = 0;
        ivi->vlan = le16_to_cpu(vsi->info.pvid) & I40E_VLAN_MASK;
        ivi->qos = (le16_to_cpu(vsi->info.pvid) & I40E_PRIORITY_MASK) >>
                   I40E_VLAN_PRIORITY_SHIFT;
+       if (vf->link_forced == false)
+               ivi->linkstate = IFLA_VF_LINK_STATE_AUTO;
+       else if (vf->link_up == true)
+               ivi->linkstate = IFLA_VF_LINK_STATE_ENABLE;
+       else
+               ivi->linkstate = IFLA_VF_LINK_STATE_DISABLE;
+       ivi->spoofchk = vf->spoofchk;
        ret = 0;
 
 error_param:
@@ -2270,3 +2392,50 @@ int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link)
 error_out:
        return ret;
 }
+
+/**
+ * i40e_ndo_set_vf_spoofchk
+ * @netdev: network interface device structure
+ * @vf_id: vf identifier
+ * @enable: flag to enable or disable feature
+ *
+ * Enable or disable VF spoof checking
+ **/
+int i40e_ndo_set_vf_spoofck(struct net_device *netdev, int vf_id, bool enable)
+{
+       struct i40e_netdev_priv *np = netdev_priv(netdev);
+       struct i40e_vsi *vsi = np->vsi;
+       struct i40e_pf *pf = vsi->back;
+       struct i40e_vsi_context ctxt;
+       struct i40e_hw *hw = &pf->hw;
+       struct i40e_vf *vf;
+       int ret = 0;
+
+       /* validate the request */
+       if (vf_id >= pf->num_alloc_vfs) {
+               dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       vf = &(pf->vf[vf_id]);
+
+       if (enable == vf->spoofchk)
+               goto out;
+
+       vf->spoofchk = enable;
+       memset(&ctxt, 0, sizeof(ctxt));
+       ctxt.seid = pf->vsi[vf->lan_vsi_index]->seid;
+       ctxt.pf_num = pf->hw.pf_id;
+       ctxt.info.valid_sections = cpu_to_le16(I40E_AQ_VSI_PROP_SECURITY_VALID);
+       if (enable)
+               ctxt.info.sec_flags |= I40E_AQ_VSI_SEC_FLAG_ENABLE_MAC_CHK;
+       ret = i40e_aq_update_vsi_params(hw, &ctxt, NULL);
+       if (ret) {
+               dev_err(&pf->pdev->dev, "Error %d updating VSI parameters\n",
+                       ret);
+               ret = -EIO;
+       }
+out:
+       return ret;
+}
index 389c47f396d5261d708228f48d8e1bf8ed90e4c5..63e7e0d81ad22754622695dabc1a0475885477ee 100644 (file)
@@ -98,8 +98,10 @@ struct i40e_vf {
 
        unsigned long vf_caps;  /* vf's adv. capabilities */
        unsigned long vf_states;        /* vf's runtime states */
+       unsigned int tx_rate;   /* Tx bandwidth limit in Mbps */
        bool link_forced;
        bool link_up;           /* only valid if vf link is forced */
+       bool spoofchk;
 };
 
 void i40e_free_vfs(struct i40e_pf *pf);
@@ -115,10 +117,12 @@ void i40e_vc_notify_vf_reset(struct i40e_vf *vf);
 int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac);
 int i40e_ndo_set_vf_port_vlan(struct net_device *netdev,
                              int vf_id, u16 vlan_id, u8 qos);
-int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int tx_rate);
+int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate,
+                      int max_tx_rate);
 int i40e_ndo_get_vf_config(struct net_device *netdev,
                           int vf_id, struct ifla_vf_info *ivi);
 int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link);
+int i40e_ndo_set_vf_spoofck(struct net_device *netdev, int vf_id, bool enable);
 
 void i40e_vc_notify_link_state(struct i40e_pf *pf);
 void i40e_vc_notify_reset(struct i40e_pf *pf);
index e09be37a07a8384f41731cfd9e7a744c646e7216..3a423836a565294aa82dadbeb93e4d45619ccd86 100644 (file)
@@ -1,7 +1,7 @@
 ################################################################################
 #
 # Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
-# Copyright(c) 2013 Intel Corporation.
+# Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
 # 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, see <http://www.gnu.org/licenses/>.
+#
 # The full GNU General Public License is included in this distribution in
 # the file called "COPYING".
 #
index 5470ce95936ed483abc547f1ef12b1ef30a34721..eb67cce3e8f9a1bed6a0eabfaaeb7067acd703ef 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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, see <http://www.gnu.org/licenses/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
 #include "i40e_adminq.h"
 #include "i40e_prototype.h"
 
+/**
+ * i40e_is_nvm_update_op - return true if this is an NVM update operation
+ * @desc: API request descriptor
+ **/
+static inline bool i40e_is_nvm_update_op(struct i40e_aq_desc *desc)
+{
+       return (desc->opcode == i40e_aqc_opc_nvm_erase) ||
+              (desc->opcode == i40e_aqc_opc_nvm_update);
+}
+
 /**
  *  i40e_adminq_init_regs - Initialize AdminQ registers
  *  @hw: pointer to the hardware structure
@@ -276,8 +289,11 @@ static void i40e_free_asq_bufs(struct i40e_hw *hw)
  *
  *  Configure base address and length registers for the transmit queue
  **/
-static void i40e_config_asq_regs(struct i40e_hw *hw)
+static i40e_status i40e_config_asq_regs(struct i40e_hw *hw)
 {
+       i40e_status ret_code = 0;
+       u32 reg = 0;
+
        if (hw->mac.type == I40E_MAC_VF) {
                /* configure the transmit queue */
                wr32(hw, I40E_VF_ATQBAH1,
@@ -286,6 +302,7 @@ static void i40e_config_asq_regs(struct i40e_hw *hw)
                    lower_32_bits(hw->aq.asq.desc_buf.pa));
                wr32(hw, I40E_VF_ATQLEN1, (hw->aq.num_asq_entries |
                                          I40E_VF_ATQLEN1_ATQENABLE_MASK));
+               reg = rd32(hw, I40E_VF_ATQBAL1);
        } else {
                /* configure the transmit queue */
                wr32(hw, I40E_PF_ATQBAH,
@@ -294,7 +311,14 @@ static void i40e_config_asq_regs(struct i40e_hw *hw)
                    lower_32_bits(hw->aq.asq.desc_buf.pa));
                wr32(hw, I40E_PF_ATQLEN, (hw->aq.num_asq_entries |
                                          I40E_PF_ATQLEN_ATQENABLE_MASK));
+               reg = rd32(hw, I40E_PF_ATQBAL);
        }
+
+       /* Check one register to verify that config was applied */
+       if (reg != lower_32_bits(hw->aq.asq.desc_buf.pa))
+               ret_code = I40E_ERR_ADMIN_QUEUE_ERROR;
+
+       return ret_code;
 }
 
 /**
@@ -303,8 +327,11 @@ static void i40e_config_asq_regs(struct i40e_hw *hw)
  *
  * Configure base address and length registers for the receive (event queue)
  **/
-static void i40e_config_arq_regs(struct i40e_hw *hw)
+static i40e_status i40e_config_arq_regs(struct i40e_hw *hw)
 {
+       i40e_status ret_code = 0;
+       u32 reg = 0;
+
        if (hw->mac.type == I40E_MAC_VF) {
                /* configure the receive queue */
                wr32(hw, I40E_VF_ARQBAH1,
@@ -313,6 +340,7 @@ static void i40e_config_arq_regs(struct i40e_hw *hw)
                    lower_32_bits(hw->aq.arq.desc_buf.pa));
                wr32(hw, I40E_VF_ARQLEN1, (hw->aq.num_arq_entries |
                                          I40E_VF_ARQLEN1_ARQENABLE_MASK));
+               reg = rd32(hw, I40E_VF_ARQBAL1);
        } else {
                /* configure the receive queue */
                wr32(hw, I40E_PF_ARQBAH,
@@ -321,10 +349,17 @@ static void i40e_config_arq_regs(struct i40e_hw *hw)
                    lower_32_bits(hw->aq.arq.desc_buf.pa));
                wr32(hw, I40E_PF_ARQLEN, (hw->aq.num_arq_entries |
                                          I40E_PF_ARQLEN_ARQENABLE_MASK));
+               reg = rd32(hw, I40E_PF_ARQBAL);
        }
 
        /* Update tail in the HW to post pre-allocated buffers */
        wr32(hw, hw->aq.arq.tail, hw->aq.num_arq_entries - 1);
+
+       /* Check one register to verify that config was applied */
+       if (reg != lower_32_bits(hw->aq.arq.desc_buf.pa))
+               ret_code = I40E_ERR_ADMIN_QUEUE_ERROR;
+
+       return ret_code;
 }
 
 /**
@@ -372,7 +407,9 @@ static i40e_status i40e_init_asq(struct i40e_hw *hw)
                goto init_adminq_free_rings;
 
        /* initialize base registers */
-       i40e_config_asq_regs(hw);
+       ret_code = i40e_config_asq_regs(hw);
+       if (ret_code)
+               goto init_adminq_free_rings;
 
        /* success! */
        goto init_adminq_exit;
@@ -429,7 +466,9 @@ static i40e_status i40e_init_arq(struct i40e_hw *hw)
                goto init_adminq_free_rings;
 
        /* initialize base registers */
-       i40e_config_arq_regs(hw);
+       ret_code = i40e_config_arq_regs(hw);
+       if (ret_code)
+               goto init_adminq_free_rings;
 
        /* success! */
        goto init_adminq_exit;
@@ -659,6 +698,12 @@ i40e_status i40evf_asq_send_command(struct i40e_hw *hw,
                goto asq_send_command_exit;
        }
 
+       if (i40e_is_nvm_update_op(desc) && hw->aq.nvm_busy) {
+               i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, "AQTX: NVM busy.\n");
+               status = I40E_ERR_NVM;
+               goto asq_send_command_exit;
+       }
+
        details = I40E_ADMINQ_DETAILS(hw->aq.asq, hw->aq.asq.next_to_use);
        if (cmd_details) {
                *details = *cmd_details;
@@ -786,6 +831,9 @@ i40e_status i40evf_asq_send_command(struct i40e_hw *hw,
                hw->aq.asq_last_status = (enum i40e_admin_queue_err)retval;
        }
 
+       if (i40e_is_nvm_update_op(desc))
+               hw->aq.nvm_busy = true;
+
        /* update the error if time out occurred */
        if ((!cmd_completed) &&
            (!details->async && !details->postpone)) {
@@ -880,6 +928,9 @@ i40e_status i40evf_clean_arq_element(struct i40e_hw *hw,
                               e->msg_size);
        }
 
+       if (i40e_is_nvm_update_op(&e->desc))
+               hw->aq.nvm_busy = false;
+
        /* Restore the original datalen and buffer address in the desc,
         * FW updates datalen to indicate the event message
         * size
index 8f72c31d95cc85adafac60190967de3f607b4bd6..e3472c62e1554194740a9233aadedaa5f13077e3 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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, see <http://www.gnu.org/licenses/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
@@ -87,6 +90,7 @@ struct i40e_adminq_info {
        u16 fw_min_ver;                 /* firmware minor version */
        u16 api_maj_ver;                /* api major version */
        u16 api_min_ver;                /* api minor version */
+       bool nvm_busy;
 
        struct mutex asq_mutex; /* Send queue lock */
        struct mutex arq_mutex; /* Receive queue lock */
index 97662b6bd98a3e5badd0660bfcc9c932500ae124..e656ea7a79207296eea4da9975194239bae4a4bc 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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, see <http://www.gnu.org/licenses/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
@@ -31,7 +34,7 @@
  */
 
 #define I40E_FW_API_VERSION_MAJOR  0x0001
-#define I40E_FW_API_VERSION_MINOR  0x0001
+#define I40E_FW_API_VERSION_MINOR  0x0002
 #define I40E_FW_API_VERSION_A0_MINOR  0x0000
 
 struct i40e_aq_desc {
@@ -121,6 +124,7 @@ enum i40e_admin_queue_opc {
        i40e_aqc_opc_get_version      = 0x0001,
        i40e_aqc_opc_driver_version   = 0x0002,
        i40e_aqc_opc_queue_shutdown   = 0x0003,
+       i40e_aqc_opc_set_pf_context   = 0x0004,
 
        /* resource ownership */
        i40e_aqc_opc_request_resource = 0x0008,
@@ -180,9 +184,6 @@ enum i40e_admin_queue_opc {
        i40e_aqc_opc_add_mirror_rule    = 0x0260,
        i40e_aqc_opc_delete_mirror_rule = 0x0261,
 
-       i40e_aqc_opc_set_storm_control_config = 0x0280,
-       i40e_aqc_opc_get_storm_control_config = 0x0281,
-
        /* DCB commands */
        i40e_aqc_opc_dcb_ignore_pfc = 0x0301,
        i40e_aqc_opc_dcb_updated    = 0x0302,
@@ -205,6 +206,7 @@ enum i40e_admin_queue_opc {
        i40e_aqc_opc_query_switching_comp_bw_config        = 0x041A,
        i40e_aqc_opc_suspend_port_tx                       = 0x041B,
        i40e_aqc_opc_resume_port_tx                        = 0x041C,
+       i40e_aqc_opc_configure_partition_bw                = 0x041D,
 
        /* hmc */
        i40e_aqc_opc_query_hmc_resource_profile = 0x0500,
@@ -222,13 +224,15 @@ enum i40e_admin_queue_opc {
        i40e_aqc_opc_get_partner_advt    = 0x0616,
        i40e_aqc_opc_set_lb_modes        = 0x0618,
        i40e_aqc_opc_get_phy_wol_caps    = 0x0621,
-       i40e_aqc_opc_set_phy_reset       = 0x0622,
+       i40e_aqc_opc_set_phy_debug       = 0x0622,
        i40e_aqc_opc_upload_ext_phy_fm   = 0x0625,
 
        /* NVM commands */
-       i40e_aqc_opc_nvm_read   = 0x0701,
-       i40e_aqc_opc_nvm_erase  = 0x0702,
-       i40e_aqc_opc_nvm_update = 0x0703,
+       i40e_aqc_opc_nvm_read         = 0x0701,
+       i40e_aqc_opc_nvm_erase        = 0x0702,
+       i40e_aqc_opc_nvm_update       = 0x0703,
+       i40e_aqc_opc_nvm_config_read  = 0x0704,
+       i40e_aqc_opc_nvm_config_write = 0x0705,
 
        /* virtualization commands */
        i40e_aqc_opc_send_msg_to_pf   = 0x0801,
@@ -270,8 +274,6 @@ enum i40e_admin_queue_opc {
        i40e_aqc_opc_debug_set_mode         = 0xFF01,
        i40e_aqc_opc_debug_read_reg         = 0xFF03,
        i40e_aqc_opc_debug_write_reg        = 0xFF04,
-       i40e_aqc_opc_debug_read_reg_sg      = 0xFF05,
-       i40e_aqc_opc_debug_write_reg_sg     = 0xFF06,
        i40e_aqc_opc_debug_modify_reg       = 0xFF07,
        i40e_aqc_opc_debug_dump_internals   = 0xFF08,
        i40e_aqc_opc_debug_modify_internals = 0xFF09,
@@ -339,6 +341,14 @@ struct i40e_aqc_queue_shutdown {
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_queue_shutdown);
 
+/* Set PF context (0x0004, direct) */
+struct i40e_aqc_set_pf_context {
+       u8      pf_id;
+       u8      reserved[15];
+};
+
+I40E_CHECK_CMD_LENGTH(i40e_aqc_set_pf_context);
+
 /* Request resource ownership (direct 0x0008)
  * Release resource ownership (direct 0x0009)
  */
@@ -678,7 +688,6 @@ struct i40e_aqc_add_get_update_vsi {
 #define I40E_AQ_VSI_TYPE_PF             0x2
 #define I40E_AQ_VSI_TYPE_EMP_MNG        0x3
 #define I40E_AQ_VSI_FLAG_CASCADED_PV    0x4
-#define I40E_AQ_VSI_FLAG_CLOUD_VSI      0x8
        __le32 addr_high;
        __le32 addr_low;
 };
@@ -1040,7 +1049,9 @@ struct i40e_aqc_set_vsi_promiscuous_modes {
 #define I40E_AQC_SET_VSI_PROMISC_VLAN        0x10
        __le16 seid;
 #define I40E_AQC_VSI_PROM_CMD_SEID_MASK      0x3FF
-       u8     reserved[10];
+       __le16 vlan_tag;
+#define I40E_AQC_SET_VSI_VLAN_VALID          0x8000
+       u8     reserved[8];
 };
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_set_vsi_promiscuous_modes);
@@ -1289,27 +1300,6 @@ struct i40e_aqc_add_delete_mirror_rule_completion {
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_add_delete_mirror_rule_completion);
 
-/* Set Storm Control Configuration (direct 0x0280)
- * Get Storm Control Configuration (direct 0x0281)
- *    the command and response use the same descriptor structure
- */
-struct i40e_aqc_set_get_storm_control_config {
-       __le32 broadcast_threshold;
-       __le32 multicast_threshold;
-       __le32 control_flags;
-#define I40E_AQC_STORM_CONTROL_MDIPW            0x01
-#define I40E_AQC_STORM_CONTROL_MDICW            0x02
-#define I40E_AQC_STORM_CONTROL_BDIPW            0x04
-#define I40E_AQC_STORM_CONTROL_BDICW            0x08
-#define I40E_AQC_STORM_CONTROL_BIDU             0x10
-#define I40E_AQC_STORM_CONTROL_INTERVAL_SHIFT   8
-#define I40E_AQC_STORM_CONTROL_INTERVAL_MASK    (0x3FF << \
-                                       I40E_AQC_STORM_CONTROL_INTERVAL_SHIFT)
-       u8     reserved[4];
-};
-
-I40E_CHECK_CMD_LENGTH(i40e_aqc_set_get_storm_control_config);
-
 /* DCB 0x03xx*/
 
 /* PFC Ignore (direct 0x0301)
@@ -1427,11 +1417,12 @@ I40E_CHECK_CMD_LENGTH(i40e_aqc_configure_switching_comp_bw_limit);
 struct i40e_aqc_configure_switching_comp_ets_data {
        u8     reserved[4];
        u8     tc_valid_bits;
-       u8     reserved1;
+       u8     seepage;
+#define I40E_AQ_ETS_SEEPAGE_EN_MASK     0x1
        u8     tc_strict_priority_flags;
-       u8     reserved2[17];
+       u8     reserved1[17];
        u8     tc_bw_share_credits[8];
-       u8     reserved3[96];
+       u8     reserved2[96];
 };
 
 /* Configure Switching Component Bandwidth Limits per Tc (indirect 0x0416) */
@@ -1499,6 +1490,15 @@ struct i40e_aqc_query_switching_comp_bw_config_resp {
  * (direct 0x041B and 0x041C) uses the generic SEID struct
  */
 
+/* Configure partition BW
+ * (indirect 0x041D)
+ */
+struct i40e_aqc_configure_partition_bw_data {
+       __le16 pf_valid_bits;
+       u8     min_bw[16];      /* guaranteed bandwidth */
+       u8     max_bw[16];      /* bandwidth limit */
+};
+
 /* Get and set the active HMC resource profile and status.
  * (direct 0x0500) and (direct 0x0501)
  */
@@ -1539,6 +1539,8 @@ enum i40e_aq_phy_type {
        I40E_PHY_TYPE_XLPPI                     = 0x9,
        I40E_PHY_TYPE_40GBASE_CR4_CU            = 0xA,
        I40E_PHY_TYPE_10GBASE_CR1_CU            = 0xB,
+       I40E_PHY_TYPE_10GBASE_AOC               = 0xC,
+       I40E_PHY_TYPE_40GBASE_AOC               = 0xD,
        I40E_PHY_TYPE_100BASE_TX                = 0x11,
        I40E_PHY_TYPE_1000BASE_T                = 0x12,
        I40E_PHY_TYPE_10GBASE_T                 = 0x13,
@@ -1549,7 +1551,10 @@ enum i40e_aq_phy_type {
        I40E_PHY_TYPE_40GBASE_CR4               = 0x18,
        I40E_PHY_TYPE_40GBASE_SR4               = 0x19,
        I40E_PHY_TYPE_40GBASE_LR4               = 0x1A,
-       I40E_PHY_TYPE_20GBASE_KR2               = 0x1B,
+       I40E_PHY_TYPE_1000BASE_SX               = 0x1B,
+       I40E_PHY_TYPE_1000BASE_LX               = 0x1C,
+       I40E_PHY_TYPE_1000BASE_T_OPTICAL        = 0x1D,
+       I40E_PHY_TYPE_20GBASE_KR2               = 0x1E,
        I40E_PHY_TYPE_MAX
 };
 
@@ -1583,11 +1588,8 @@ struct i40e_aq_get_phy_abilities_resp {
 #define I40E_AQ_PHY_FLAG_PAUSE_TX         0x01
 #define I40E_AQ_PHY_FLAG_PAUSE_RX         0x02
 #define I40E_AQ_PHY_FLAG_LOW_POWER        0x04
-#define I40E_AQ_PHY_FLAG_AN_SHIFT         3
-#define I40E_AQ_PHY_FLAG_AN_MASK          (0x3 << I40E_AQ_PHY_FLAG_AN_SHIFT)
-#define I40E_AQ_PHY_FLAG_AN_OFF           0x00 /* link forced on */
-#define I40E_AQ_PHY_FLAG_AN_OFF_LINK_DOWN 0x01
-#define I40E_AQ_PHY_FLAG_AN_ON            0x02
+#define I40E_AQ_PHY_LINK_ENABLED                 0x08
+#define I40E_AQ_PHY_AN_ENABLED                   0x10
 #define I40E_AQ_PHY_FLAG_MODULE_QUAL      0x20
        __le16 eee_capability;
 #define I40E_AQ_EEE_100BASE_TX       0x0002
@@ -1696,6 +1698,7 @@ struct i40e_aqc_get_link_status {
 #define I40E_AQ_LINK_TX_ACTIVE       0x00
 #define I40E_AQ_LINK_TX_DRAINED      0x01
 #define I40E_AQ_LINK_TX_FLUSHED      0x03
+#define I40E_AQ_LINK_FORCED_40G      0x10
        u8     loopback;         /* use defines from i40e_aqc_set_lb_mode */
        __le16 max_frame_size;
        u8     config;
@@ -1747,14 +1750,21 @@ struct i40e_aqc_set_lb_mode {
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_set_lb_mode);
 
-/* Set PHY Reset command (0x0622) */
-struct i40e_aqc_set_phy_reset {
-       u8     reset_flags;
-#define I40E_AQ_PHY_RESET_REQUEST  0x02
+/* Set PHY Debug command (0x0622) */
+struct i40e_aqc_set_phy_debug {
+       u8     command_flags;
+#define I40E_AQ_PHY_DEBUG_RESET_INTERNAL       0x02
+#define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_SHIFT 2
+#define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_MASK  (0x03 << \
+                                       I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_SHIFT)
+#define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_NONE  0x00
+#define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_HARD  0x01
+#define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_SOFT  0x02
+#define I40E_AQ_PHY_DEBUG_DISABLE_LINK_FW      0x10
        u8     reserved[15];
 };
 
-I40E_CHECK_CMD_LENGTH(i40e_aqc_set_phy_reset);
+I40E_CHECK_CMD_LENGTH(i40e_aqc_set_phy_debug);
 
 enum i40e_aq_phy_reg_type {
        I40E_AQC_PHY_REG_INTERNAL         = 0x1,
@@ -1779,6 +1789,47 @@ struct i40e_aqc_nvm_update {
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_nvm_update);
 
+/* NVM Config Read (indirect 0x0704) */
+struct i40e_aqc_nvm_config_read {
+       __le16 cmd_flags;
+#define ANVM_SINGLE_OR_MULTIPLE_FEATURES_MASK  1
+#define ANVM_READ_SINGLE_FEATURE               0
+#define ANVM_READ_MULTIPLE_FEATURES            1
+       __le16 element_count;
+       __le16 element_id;              /* Feature/field ID */
+       u8     reserved[2];
+       __le32 address_high;
+       __le32 address_low;
+};
+
+I40E_CHECK_CMD_LENGTH(i40e_aqc_nvm_config_read);
+
+/* NVM Config Write (indirect 0x0705) */
+struct i40e_aqc_nvm_config_write {
+       __le16 cmd_flags;
+       __le16 element_count;
+       u8     reserved[4];
+       __le32 address_high;
+       __le32 address_low;
+};
+
+I40E_CHECK_CMD_LENGTH(i40e_aqc_nvm_config_write);
+
+struct i40e_aqc_nvm_config_data_feature {
+       __le16 feature_id;
+       __le16 instance_id;
+       __le16 feature_options;
+       __le16 feature_selection;
+};
+
+struct i40e_aqc_nvm_config_data_immediate_field {
+#define ANVM_FEATURE_OR_IMMEDIATE_MASK 0x2
+       __le16 field_id;
+       __le16 instance_id;
+       __le16 field_options;
+       __le16 field_value;
+};
+
 /* Send to PF command (indirect 0x0801) id is only used by PF
  * Send to VF command (indirect 0x0802) id is only used by PF
  * Send to Peer PF command (indirect 0x0803)
@@ -1948,19 +1999,12 @@ I40E_CHECK_CMD_LENGTH(i40e_aqc_lldp_start);
 /* Add Udp Tunnel command and completion (direct 0x0B00) */
 struct i40e_aqc_add_udp_tunnel {
        __le16 udp_port;
-       u8     header_len; /* in DWords, 1 to 15 */
+       u8     reserved0[3];
        u8     protocol_type;
-#define I40E_AQC_TUNNEL_TYPE_TEREDO    0x0
-#define I40E_AQC_TUNNEL_TYPE_VXLAN     0x2
-#define I40E_AQC_TUNNEL_TYPE_NGE       0x3
-       u8     variable_udp_length;
-#define I40E_AQC_TUNNEL_FIXED_UDP_LENGTH       0x0
-#define I40E_AQC_TUNNEL_VARIABLE_UDP_LENGTH    0x1
-       u8              udp_key_index;
-#define I40E_AQC_TUNNEL_KEY_INDEX_VXLAN                        0x0
-#define I40E_AQC_TUNNEL_KEY_INDEX_NGE                  0x1
-#define I40E_AQC_TUNNEL_KEY_INDEX_PROPRIETARY_UDP      0x2
-       u8              reserved[10];
+#define I40E_AQC_TUNNEL_TYPE_VXLAN     0x00
+#define I40E_AQC_TUNNEL_TYPE_NGE       0x01
+#define I40E_AQC_TUNNEL_TYPE_TEREDO    0x10
+       u8     reserved1[10];
 };
 
 I40E_CHECK_CMD_LENGTH(i40e_aqc_add_udp_tunnel);
index d8654fb9e525a82881c88cf882d91c716577ec0f..8e6a6dd9212bb0812f42378ad208ccd451e43ee4 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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, see <http://www.gnu.org/licenses/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
index ae084378faabff7669794cd0f47a9072f1698243..a43155afdbe24784d750f5fa8878e57a100913c8 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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, see <http://www.gnu.org/licenses/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
@@ -40,12 +43,10 @@ i40e_status i40e_set_mac_type(struct i40e_hw *hw)
        if (hw->vendor_id == PCI_VENDOR_ID_INTEL) {
                switch (hw->device_id) {
                case I40E_DEV_ID_SFP_XL710:
-               case I40E_DEV_ID_SFP_X710:
                case I40E_DEV_ID_QEMU:
                case I40E_DEV_ID_KX_A:
                case I40E_DEV_ID_KX_B:
                case I40E_DEV_ID_KX_C:
-               case I40E_DEV_ID_KX_D:
                case I40E_DEV_ID_QSFP_A:
                case I40E_DEV_ID_QSFP_B:
                case I40E_DEV_ID_QSFP_C:
@@ -130,7 +131,11 @@ void i40evf_debug_aq(struct i40e_hw *hw, enum i40e_debug_mask mask, void *desc,
  **/
 bool i40evf_check_asq_alive(struct i40e_hw *hw)
 {
-       return !!(rd32(hw, hw->aq.asq.len) & I40E_PF_ATQLEN_ATQENABLE_MASK);
+       if (hw->aq.asq.len)
+               return !!(rd32(hw, hw->aq.asq.len) &
+                         I40E_PF_ATQLEN_ATQENABLE_MASK);
+       else
+               return false;
 }
 
 /**
index cb97b3eed440ff764c1a9dbf705e45bc9e308b2f..a2ad9a4e399da70707a9ed984588a8c2cde04686 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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, see <http://www.gnu.org/licenses/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
@@ -160,11 +163,6 @@ struct i40e_hmc_info {
            (((sd_idx) << I40E_PFHMC_PDINV_PMSDIDX_SHIFT) |             \
             ((pd_idx) << I40E_PFHMC_PDINV_PMPDIDX_SHIFT)))
 
-#define I40E_INVALIDATE_VF_HMC_PD(hw, sd_idx, pd_idx, hmc_fn_id)          \
-       wr32((hw), I40E_GLHMC_VFPDINV((hmc_fn_id) - I40E_FIRST_VF_FPM_ID), \
-            (((sd_idx) << I40E_PFHMC_PDINV_PMSDIDX_SHIFT) |               \
-             ((pd_idx) << I40E_PFHMC_PDINV_PMPDIDX_SHIFT)))
-
 /**
  * I40E_FIND_SD_INDEX_LIMIT - finds segment descriptor index limit
  * @hmc_info: pointer to the HMC configuration information structure
@@ -223,7 +221,7 @@ i40e_status i40e_add_pd_table_entry(struct i40e_hw *hw,
                                              u32 pd_index);
 i40e_status i40e_remove_pd_bp(struct i40e_hw *hw,
                                        struct i40e_hmc_info *hmc_info,
-                                       u32 idx, bool is_pf);
+                                       u32 idx);
 i40e_status i40e_prep_remove_sd_bp(struct i40e_hmc_info *hmc_info,
                                             u32 idx);
 i40e_status i40e_remove_sd_bp_new(struct i40e_hw *hw,
index 17e42ca26d0ba045fc51a0efd1ec0b1af0fffd71..d6f762241537804be777b97ad5f29616f990058f 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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, see <http://www.gnu.org/licenses/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
@@ -53,6 +56,7 @@ struct i40e_hmc_obj_rxq {
        u8  tphdata_ena;
        u8  tphhead_ena;
        u8  lrxqthresh;
+       u8  prefena;    /* NOTE: normally must be set to 1 at init */
 };
 
 /* Tx queue context data */
index 622f373b745d59092355131aa20f5f5c57d486c8..21a91b14bf819365bfea82276324b14da6ef403c 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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, see <http://www.gnu.org/licenses/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
index 97ab8c2b76f8f0e6cf1c6c485eaeafee0e42a5a1..849edcc2e398f7bfdaaea5eaf078ba714977ca5f 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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, see <http://www.gnu.org/licenses/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
index 30af953cf106a4afc6aa9e0939d24adce8d68418..3698396558186f67736fae33294b9993ccf8abad 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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, see <http://www.gnu.org/licenses/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
 #define I40E_PFINT_ICR0_GPIO_MASK (0x1 << I40E_PFINT_ICR0_GPIO_SHIFT)
 #define I40E_PFINT_ICR0_TIMESYNC_SHIFT 23
 #define I40E_PFINT_ICR0_TIMESYNC_MASK (0x1 << I40E_PFINT_ICR0_TIMESYNC_SHIFT)
-#define I40E_PFINT_ICR0_STORM_DETECT_SHIFT 24
-#define I40E_PFINT_ICR0_STORM_DETECT_MASK (0x1 << I40E_PFINT_ICR0_STORM_DETECT_SHIFT)
 #define I40E_PFINT_ICR0_LINK_STAT_CHANGE_SHIFT 25
 #define I40E_PFINT_ICR0_LINK_STAT_CHANGE_MASK (0x1 << I40E_PFINT_ICR0_LINK_STAT_CHANGE_SHIFT)
 #define I40E_PFINT_ICR0_HMC_ERR_SHIFT 26
 #define I40E_PFINT_ICR0_ENA_GPIO_MASK (0x1 << I40E_PFINT_ICR0_ENA_GPIO_SHIFT)
 #define I40E_PFINT_ICR0_ENA_TIMESYNC_SHIFT 23
 #define I40E_PFINT_ICR0_ENA_TIMESYNC_MASK (0x1 << I40E_PFINT_ICR0_ENA_TIMESYNC_SHIFT)
-#define I40E_PFINT_ICR0_ENA_STORM_DETECT_SHIFT 24
-#define I40E_PFINT_ICR0_ENA_STORM_DETECT_MASK (0x1 << I40E_PFINT_ICR0_ENA_STORM_DETECT_SHIFT)
 #define I40E_PFINT_ICR0_ENA_LINK_STAT_CHANGE_SHIFT 25
 #define I40E_PFINT_ICR0_ENA_LINK_STAT_CHANGE_MASK (0x1 << I40E_PFINT_ICR0_ENA_LINK_STAT_CHANGE_SHIFT)
 #define I40E_PFINT_ICR0_ENA_HMC_ERR_SHIFT 26
 #define I40E_GLLAN_TSOMSK_M 0x000442DC
 #define I40E_GLLAN_TSOMSK_M_TCPMSKM_SHIFT 0
 #define I40E_GLLAN_TSOMSK_M_TCPMSKM_MASK (0xFFF << I40E_GLLAN_TSOMSK_M_TCPMSKM_SHIFT)
+#define I40E_GLLAN_TXPRE_QDIS(_i) (0x000E6500 + ((_i) * 4)) /* i=0..11 */
+#define I40E_GLLAN_TXPRE_QDIS_QINDX_SHIFT 0
+#define I40E_GLLAN_TXPRE_QDIS_QINDX_MASK (0x7FF << I40E_GLLAN_TXPRE_QDIS_QINDX_SHIFT)
+#define I40E_GLLAN_TXPRE_QDIS_SET_QDIS_SHIFT 30
+#define I40E_GLLAN_TXPRE_QDIS_SET_QDIS_MASK (0x1 << I40E_GLLAN_TXPRE_QDIS_SET_QDIS_SHIFT)
+#define I40E_GLLAN_TXPRE_QDIS_CLEAR_QDIS_SHIFT 31
+#define I40E_GLLAN_TXPRE_QDIS_CLEAR_QDIS_MASK (0x1 << I40E_GLLAN_TXPRE_QDIS_CLEAR_QDIS_SHIFT)
+
 #define I40E_PFLAN_QALLOC 0x001C0400
 #define I40E_PFLAN_QALLOC_FIRSTQ_SHIFT 0
 #define I40E_PFLAN_QALLOC_FIRSTQ_MASK (0x7FF << I40E_PFLAN_QALLOC_FIRSTQ_SHIFT)
index 7c08cc2e339b89f05d79d37dc5e46ba9921cb17b..7fa7a41915c1acce92ce1b8d46cc22d7043f96c2 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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, see <http://www.gnu.org/licenses/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
index b9f50f40abe18b78cf0c3ff580ded29666491d92..48ebb6cd69f28608b73fb0e22ff5b8d1a35f0389 100644 (file)
@@ -12,6 +12,9 @@
  * 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, see <http://www.gnu.org/licenses/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
@@ -725,10 +728,12 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
                                    u32 rx_error,
                                    u16 rx_ptype)
 {
+       struct i40e_rx_ptype_decoded decoded = decode_rx_desc_ptype(rx_ptype);
+       bool ipv4 = false, ipv6 = false;
        bool ipv4_tunnel, ipv6_tunnel;
        __wsum rx_udp_csum;
-       __sum16 csum;
        struct iphdr *iph;
+       __sum16 csum;
 
        ipv4_tunnel = (rx_ptype > I40E_RX_PTYPE_GRENAT4_MAC_PAY3) &&
                      (rx_ptype < I40E_RX_PTYPE_GRENAT4_MACVLAN_IPV6_ICMP_PAY4);
@@ -739,29 +744,57 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
        skb->ip_summed = CHECKSUM_NONE;
 
        /* Rx csum enabled and ip headers found? */
-       if (!(vsi->netdev->features & NETIF_F_RXCSUM &&
-             rx_status & (1 << I40E_RX_DESC_STATUS_L3L4P_SHIFT)))
+       if (!(vsi->netdev->features & NETIF_F_RXCSUM))
                return;
 
+       /* did the hardware decode the packet and checksum? */
+       if (!(rx_status & (1 << I40E_RX_DESC_STATUS_L3L4P_SHIFT)))
+               return;
+
+       /* both known and outer_ip must be set for the below code to work */
+       if (!(decoded.known && decoded.outer_ip))
+               return;
+
+       if (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP &&
+           decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV4)
+               ipv4 = true;
+       else if (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP &&
+                decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV6)
+               ipv6 = true;
+
+       if (ipv4 &&
+           (rx_error & ((1 << I40E_RX_DESC_ERROR_IPE_SHIFT) |
+                        (1 << I40E_RX_DESC_ERROR_EIPE_SHIFT))))
+               goto checksum_fail;
+
        /* likely incorrect csum if alternate IP extension headers found */
-       if (rx_status & (1 << I40E_RX_DESC_STATUS_IPV6EXADD_SHIFT))
+       if (ipv6 &&
+           decoded.inner_prot == I40E_RX_PTYPE_INNER_PROT_TCP &&
+           rx_error & (1 << I40E_RX_DESC_ERROR_L4E_SHIFT) &&
+           rx_status & (1 << I40E_RX_DESC_STATUS_IPV6EXADD_SHIFT))
+               /* don't increment checksum err here, non-fatal err */
                return;
 
-       /* IP or L4 or outmost IP checksum error */
-       if (rx_error & ((1 << I40E_RX_DESC_ERROR_IPE_SHIFT) |
-                       (1 << I40E_RX_DESC_ERROR_L4E_SHIFT) |
-                       (1 << I40E_RX_DESC_ERROR_EIPE_SHIFT))) {
-               vsi->back->hw_csum_rx_error++;
+       /* there was some L4 error, count error and punt packet to the stack */
+       if (rx_error & (1 << I40E_RX_DESC_ERROR_L4E_SHIFT))
+               goto checksum_fail;
+
+       /* handle packets that were not able to be checksummed due
+        * to arrival speed, in this case the stack can compute
+        * the csum.
+        */
+       if (rx_error & (1 << I40E_RX_DESC_ERROR_PPRS_SHIFT))
                return;
-       }
 
+       /* If VXLAN traffic has an outer UDPv4 checksum we need to check
+        * it in the driver, hardware does not do it for us.
+        * Since L3L4P bit was set we assume a valid IHL value (>=5)
+        * so the total length of IPv4 header is IHL*4 bytes
+        * The UDP_0 bit *may* bet set if the *inner* header is UDP
+        */
        if (ipv4_tunnel &&
+           (decoded.inner_prot != I40E_RX_PTYPE_INNER_PROT_UDP) &&
            !(rx_status & (1 << I40E_RX_DESC_STATUS_UDP_0_SHIFT))) {
-               /* If VXLAN traffic has an outer UDPv4 checksum we need to check
-                * it in the driver, hardware does not do it for us.
-                * Since L3L4P bit was set we assume a valid IHL value (>=5)
-                * so the total length of IPv4 header is IHL*4 bytes
-                */
                skb->transport_header = skb->mac_header +
                                        sizeof(struct ethhdr) +
                                        (ip_hdr(skb)->ihl * 4);
@@ -778,13 +811,16 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
                                (skb->len - skb_transport_offset(skb)),
                                IPPROTO_UDP, rx_udp_csum);
 
-               if (udp_hdr(skb)->check != csum) {
-                       vsi->back->hw_csum_rx_error++;
-                       return;
-               }
+               if (udp_hdr(skb)->check != csum)
+                       goto checksum_fail;
        }
 
        skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+       return;
+
+checksum_fail:
+       vsi->back->hw_csum_rx_error++;
 }
 
 /**
@@ -953,6 +989,9 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget)
                /* ERR_MASK will only have valid bits if EOP set */
                if (unlikely(rx_error & (1 << I40E_RX_DESC_ERROR_RXE_SHIFT))) {
                        dev_kfree_skb_any(skb);
+                       /* TODO: shouldn't we increment a counter indicating the
+                        * drop?
+                        */
                        goto next_desc;
                }
 
@@ -1508,9 +1547,7 @@ static int i40e_maybe_stop_tx(struct i40e_ring *tx_ring, int size)
 static int i40e_xmit_descriptor_count(struct sk_buff *skb,
                                      struct i40e_ring *tx_ring)
 {
-#if PAGE_SIZE > I40E_MAX_DATA_PER_TXD
        unsigned int f;
-#endif
        int count = 0;
 
        /* need: 1 descriptor per page * PAGE_SIZE/I40E_MAX_DATA_PER_TXD,
@@ -1519,12 +1556,9 @@ static int i40e_xmit_descriptor_count(struct sk_buff *skb,
         *       + 1 desc for context descriptor,
         * otherwise try next time
         */
-#if PAGE_SIZE > I40E_MAX_DATA_PER_TXD
        for (f = 0; f < skb_shinfo(skb)->nr_frags; f++)
                count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size);
-#else
-       count += skb_shinfo(skb)->nr_frags;
-#endif
+
        count += TXD_USE_COUNT(skb_headlen(skb));
        if (i40e_maybe_stop_tx(tx_ring, count + 4 + 1)) {
                tx_ring->tx_stats.tx_busy++;
index 10bf49e18d7f6c0ed8d1af47d5d30544c5a8951e..30d248bc5d199d50ba14cc79356d689c2265aebc 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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, see <http://www.gnu.org/licenses/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
@@ -24,7 +27,7 @@
 #ifndef _I40E_TXRX_H_
 #define _I40E_TXRX_H_
 
-/* Interrupt Throttling and Rate Limiting (storm control) Goodies */
+/* Interrupt Throttling and Rate Limiting Goodies */
 
 #define I40E_MAX_ITR               0x0FF0  /* reg uses 2 usec resolution */
 #define I40E_MIN_ITR               0x0004  /* reg uses 2 usec resolution */
@@ -66,16 +69,11 @@ enum i40e_dyn_idx_t {
 
 /* Supported RSS offloads */
 #define I40E_DEFAULT_RSS_HENA ( \
-       ((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) | \
-       ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_SCTP) | \
-       ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_OTHER) | \
        ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4) | \
-       ((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) | \
-       ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN) | \
        ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP) | \
@@ -119,11 +117,11 @@ enum i40e_dyn_idx_t {
 #define i40e_rx_desc i40e_32byte_rx_desc
 
 #define I40E_MIN_TX_LEN                17
-#define I40E_MAX_DATA_PER_TXD  16383   /* aka 16kB - 1 */
+#define I40E_MAX_DATA_PER_TXD  8192
 
 /* Tx Descriptors needed, worst case */
 #define TXD_USE_COUNT(S) DIV_ROUND_UP((S), I40E_MAX_DATA_PER_TXD)
-#define DESC_NEEDED ((MAX_SKB_FRAGS * TXD_USE_COUNT(PAGE_SIZE)) + 4)
+#define DESC_NEEDED (MAX_SKB_FRAGS + 4)
 
 #define I40E_TX_FLAGS_CSUM             (u32)(1)
 #define I40E_TX_FLAGS_HW_VLAN          (u32)(1 << 1)
@@ -180,7 +178,6 @@ enum i40e_ring_state_t {
        __I40E_TX_DETECT_HANG,
        __I40E_HANG_CHECK_ARMED,
        __I40E_RX_PS_ENABLED,
-       __I40E_RX_LRO_ENABLED,
        __I40E_RX_16BYTE_DESC_ENABLED,
 };
 
@@ -196,12 +193,6 @@ enum i40e_ring_state_t {
        set_bit(__I40E_TX_DETECT_HANG, &(ring)->state)
 #define clear_check_for_tx_hang(ring) \
        clear_bit(__I40E_TX_DETECT_HANG, &(ring)->state)
-#define ring_is_lro_enabled(ring) \
-       test_bit(__I40E_RX_LRO_ENABLED, &(ring)->state)
-#define set_ring_lro_enabled(ring) \
-       set_bit(__I40E_RX_LRO_ENABLED, &(ring)->state)
-#define clear_ring_lro_enabled(ring) \
-       clear_bit(__I40E_RX_LRO_ENABLED, &(ring)->state)
 #define ring_is_16byte_desc_enabled(ring) \
        test_bit(__I40E_RX_16BYTE_DESC_ENABLED, &(ring)->state)
 #define set_ring_16byte_desc_enabled(ring) \
index 4673b3381eddaa1672edca1f60b85e89d33ab247..d3cf5a69de54d6e7e7bc648dd478873b88510b5c 100644 (file)
@@ -12,6 +12,9 @@
  * 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, see <http://www.gnu.org/licenses/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
 #include "i40e_lan_hmc.h"
 
 /* Device IDs */
-#define I40E_DEV_ID_SFP_XL710  0x1572
-#define I40E_DEV_ID_SFP_X710           0x1573
+#define I40E_DEV_ID_SFP_XL710          0x1572
 #define I40E_DEV_ID_QEMU               0x1574
 #define I40E_DEV_ID_KX_A               0x157F
 #define I40E_DEV_ID_KX_B               0x1580
 #define I40E_DEV_ID_KX_C               0x1581
-#define I40E_DEV_ID_KX_D               0x1582
 #define I40E_DEV_ID_QSFP_A             0x1583
 #define I40E_DEV_ID_QSFP_B             0x1584
 #define I40E_DEV_ID_QSFP_C             0x1585
@@ -57,8 +58,8 @@
 /* Max default timeout in ms, */
 #define I40E_MAX_NVM_TIMEOUT           18000
 
-/* Switch from mc to the 2usec global time (this is the GTIME resolution) */
-#define I40E_MS_TO_GTIME(time)         (((time) * 1000) / 2)
+/* Switch from ms to the 1usec global time (this is the GTIME resolution) */
+#define I40E_MS_TO_GTIME(time)         ((time) * 1000)
 
 /* forward declaration */
 struct i40e_hw;
@@ -101,15 +102,6 @@ enum i40e_debug_mask {
        I40E_DEBUG_ALL                  = 0xFFFFFFFF
 };
 
-/* PCI Bus Info */
-#define I40E_PCI_LINK_WIDTH_1          0x10
-#define I40E_PCI_LINK_WIDTH_2          0x20
-#define I40E_PCI_LINK_WIDTH_4          0x40
-#define I40E_PCI_LINK_WIDTH_8          0x80
-#define I40E_PCI_LINK_SPEED_2500       0x1
-#define I40E_PCI_LINK_SPEED_5000       0x2
-#define I40E_PCI_LINK_SPEED_8000       0x3
-
 /* These are structs for managing the hardware information and the operations.
  * The structures of function pointers are filled out at init time when we
  * know for sure exactly which hardware we're working with.  This gives us the
@@ -173,6 +165,9 @@ struct i40e_link_status {
        u8 loopback;
        /* is Link Status Event notification to SW enabled */
        bool lse_enable;
+       u16 max_frame_size;
+       bool crc_enable;
+       u8 pacing;
 };
 
 struct i40e_phy_info {
@@ -415,6 +410,7 @@ struct i40e_driver_version {
        u8 minor_version;
        u8 build_version;
        u8 subbuild_version;
+       u8 driver_string[32];
 };
 
 /* RX Descriptors */
@@ -494,9 +490,6 @@ union i40e_32byte_rx_desc {
        } wb;  /* writeback */
 };
 
-#define I40E_RXD_QW1_STATUS_SHIFT      0
-#define I40E_RXD_QW1_STATUS_MASK       (0x7FFFUL << I40E_RXD_QW1_STATUS_SHIFT)
-
 enum i40e_rx_desc_status_bits {
        /* Note: These are predefined bit offsets */
        I40E_RX_DESC_STATUS_DD_SHIFT            = 0,
@@ -513,9 +506,14 @@ enum i40e_rx_desc_status_bits {
        I40E_RX_DESC_STATUS_LPBK_SHIFT          = 14,
        I40E_RX_DESC_STATUS_IPV6EXADD_SHIFT     = 15,
        I40E_RX_DESC_STATUS_RESERVED_SHIFT      = 16, /* 2 BITS */
-       I40E_RX_DESC_STATUS_UDP_0_SHIFT         = 18
+       I40E_RX_DESC_STATUS_UDP_0_SHIFT         = 18,
+       I40E_RX_DESC_STATUS_LAST /* this entry must be last!!! */
 };
 
+#define I40E_RXD_QW1_STATUS_SHIFT      0
+#define I40E_RXD_QW1_STATUS_MASK       (((1 << I40E_RX_DESC_STATUS_LAST) - 1) \
+                                        << I40E_RXD_QW1_STATUS_SHIFT)
+
 #define I40E_RXD_QW1_STATUS_TSYNINDX_SHIFT   I40E_RX_DESC_STATUS_TSYNINDX_SHIFT
 #define I40E_RXD_QW1_STATUS_TSYNINDX_MASK      (0x3UL << \
                                             I40E_RXD_QW1_STATUS_TSYNINDX_SHIFT)
@@ -543,7 +541,8 @@ enum i40e_rx_desc_error_bits {
        I40E_RX_DESC_ERROR_IPE_SHIFT            = 3,
        I40E_RX_DESC_ERROR_L4E_SHIFT            = 4,
        I40E_RX_DESC_ERROR_EIPE_SHIFT           = 5,
-       I40E_RX_DESC_ERROR_OVERSIZE_SHIFT       = 6
+       I40E_RX_DESC_ERROR_OVERSIZE_SHIFT       = 6,
+       I40E_RX_DESC_ERROR_PPRS_SHIFT           = 7
 };
 
 enum i40e_rx_desc_error_l3l4e_fcoe_masks {
@@ -664,7 +663,6 @@ enum i40e_rx_desc_ext_status_bits {
        I40E_RX_DESC_EXT_STATUS_L2TAG3P_SHIFT   = 1,
        I40E_RX_DESC_EXT_STATUS_FLEXBL_SHIFT    = 2, /* 2 BITS */
        I40E_RX_DESC_EXT_STATUS_FLEXBH_SHIFT    = 4, /* 2 BITS */
-       I40E_RX_DESC_EXT_STATUS_FTYPE_SHIFT     = 6, /* 3 BITS */
        I40E_RX_DESC_EXT_STATUS_FDLONGB_SHIFT   = 9,
        I40E_RX_DESC_EXT_STATUS_FCOELONGB_SHIFT = 10,
        I40E_RX_DESC_EXT_STATUS_PELONGB_SHIFT   = 11,
@@ -868,18 +866,14 @@ struct i40e_filter_program_desc {
 
 /* Packet Classifier Types for filters */
 enum i40e_filter_pctype {
-       /* Note: Values 0-28 are reserved for future use */
-       I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP        = 29,
-       I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP      = 30,
+       /* Note: Values 0-30 are reserved for future use */
        I40E_FILTER_PCTYPE_NONF_IPV4_UDP                = 31,
-       I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN            = 32,
+       /* Note: Value 32 is reserved for future use */
        I40E_FILTER_PCTYPE_NONF_IPV4_TCP                = 33,
        I40E_FILTER_PCTYPE_NONF_IPV4_SCTP               = 34,
        I40E_FILTER_PCTYPE_NONF_IPV4_OTHER              = 35,
        I40E_FILTER_PCTYPE_FRAG_IPV4                    = 36,
-       /* Note: Values 37-38 are reserved for future use */
-       I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP        = 39,
-       I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP      = 40,
+       /* Note: Values 37-40 are reserved for future use */
        I40E_FILTER_PCTYPE_NONF_IPV6_UDP                = 41,
        I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN            = 42,
        I40E_FILTER_PCTYPE_NONF_IPV6_TCP                = 43,
@@ -961,6 +955,16 @@ struct i40e_vsi_context {
        struct i40e_aqc_vsi_properties_data info;
 };
 
+struct i40e_veb_context {
+       u16 seid;
+       u16 uplink_seid;
+       u16 veb_number;
+       u16 vebs_allocated;
+       u16 vebs_unallocated;
+       u16 flags;
+       struct i40e_aqc_get_veb_parameters_completion info;
+};
+
 /* Statistics collected by each port, VSI, VEB, and S-channel */
 struct i40e_eth_stats {
        u64 rx_bytes;                   /* gorc */
@@ -968,8 +972,6 @@ struct i40e_eth_stats {
        u64 rx_multicast;               /* mprc */
        u64 rx_broadcast;               /* bprc */
        u64 rx_discards;                /* rdpc */
-       u64 rx_errors;                  /* repc */
-       u64 rx_missed;                  /* rmpc */
        u64 rx_unknown_protocol;        /* rupp */
        u64 tx_bytes;                   /* gotc */
        u64 tx_unicast;                 /* uptc */
@@ -1021,9 +1023,12 @@ struct i40e_hw_port_stats {
        u64 tx_size_big;                /* ptc9522 */
        u64 mac_short_packet_dropped;   /* mspdc */
        u64 checksum_error;             /* xec */
+       /* flow director stats */
+       u64 fd_atr_match;
+       u64 fd_sb_match;
        /* EEE LPI */
-       bool tx_lpi_status;
-       bool rx_lpi_status;
+       u32 tx_lpi_status;
+       u32 rx_lpi_status;
        u64 tx_lpi_count;               /* etlpic */
        u64 rx_lpi_count;               /* erlpic */
 };
index ccf45d04b7ef88e74e99d5ad844ad10df8d9b4f8..cd18d5689006f19eaf2cf7fa1abade5b7adc1a0b 100644 (file)
@@ -1,7 +1,7 @@
 /*******************************************************************************
  *
  * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver
- * Copyright(c) 2013 Intel Corporation.
+ * Copyright(c) 2013 - 2014 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,
@@ -12,6 +12,9 @@
  * 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, see <http://www.gnu.org/licenses/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
@@ -338,10 +341,6 @@ struct i40e_virtchnl_pf_event {
        int severity;
 };
 
-/* The following are TBD, not necessary for LAN functionality.
- * I40E_VIRTCHNL_OP_FCOE
- */
-
 /* VF reset states - these are written into the RSTAT register:
  * I40E_VFGEN_RSTAT1 on the PF
  * I40E_VFGEN_RSTAT on the VF
index 807807d6238738c0e111739e9dc96d1f2200d77a..30ef519d4b911a5c59b4cbf06316b7be0547acd2 100644 (file)
@@ -12,6 +12,9 @@
  * 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, see <http://www.gnu.org/licenses/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
@@ -77,7 +80,7 @@ struct i40e_vsi {
 #define I40EVF_MIN_TXD       64
 #define I40EVF_MAX_RXD       4096
 #define I40EVF_MIN_RXD       64
-#define I40EVF_REQ_DESCRIPTOR_MULTIPLE  8
+#define I40EVF_REQ_DESCRIPTOR_MULTIPLE  32
 
 /* Supported Rx Buffer Sizes */
 #define I40EVF_RXBUFFER_64    64     /* Used for packet split */
@@ -193,10 +196,12 @@ struct i40evf_adapter {
        struct i40e_ring *tx_rings[I40E_MAX_VSI_QP];
        u32 tx_timeout_count;
        struct list_head mac_filter_list;
+       u32 tx_desc_count;
 
        /* RX */
        struct i40e_ring *rx_rings[I40E_MAX_VSI_QP];
        u64 hw_csum_rx_error;
+       u32 rx_desc_count;
        int num_msix_vectors;
        struct msix_entry *msix_entries;
 
index 8b0db1ce179c5447ce83240098e6c076d6782ed6..60407a9df0c1ef5103b4e0f198f556b229a9417a 100644 (file)
@@ -12,6 +12,9 @@
  * 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, see <http://www.gnu.org/licenses/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
@@ -44,8 +47,6 @@ static const struct i40evf_stats i40evf_gstrings_stats[] = {
        I40EVF_STAT("rx_multicast", current_stats.rx_multicast),
        I40EVF_STAT("rx_broadcast", current_stats.rx_broadcast),
        I40EVF_STAT("rx_discards", current_stats.rx_discards),
-       I40EVF_STAT("rx_errors", current_stats.rx_errors),
-       I40EVF_STAT("rx_missed", current_stats.rx_missed),
        I40EVF_STAT("rx_unknown_protocol", current_stats.rx_unknown_protocol),
        I40EVF_STAT("tx_bytes", current_stats.tx_bytes),
        I40EVF_STAT("tx_unicast", current_stats.tx_unicast),
@@ -56,10 +57,12 @@ static const struct i40evf_stats i40evf_gstrings_stats[] = {
 };
 
 #define I40EVF_GLOBAL_STATS_LEN ARRAY_SIZE(i40evf_gstrings_stats)
-#define I40EVF_QUEUE_STATS_LEN \
+#define I40EVF_QUEUE_STATS_LEN(_dev) \
        (((struct i40evf_adapter *) \
-               netdev_priv(netdev))->vsi_res->num_queue_pairs * 4)
-#define I40EVF_STATS_LEN (I40EVF_GLOBAL_STATS_LEN + I40EVF_QUEUE_STATS_LEN)
+               netdev_priv(_dev))->vsi_res->num_queue_pairs \
+                 * 2 * (sizeof(struct i40e_queue_stats) / sizeof(u64)))
+#define I40EVF_STATS_LEN(_dev) \
+       (I40EVF_GLOBAL_STATS_LEN + I40EVF_QUEUE_STATS_LEN(_dev))
 
 /**
  * i40evf_get_settings - Get Link Speed and Duplex settings
@@ -75,7 +78,7 @@ static int i40evf_get_settings(struct net_device *netdev,
        /* In the future the VF will be able to query the PF for
         * some information - for now use a dummy value
         */
-       ecmd->supported = SUPPORTED_10000baseT_Full;
+       ecmd->supported = 0;
        ecmd->autoneg = AUTONEG_DISABLE;
        ecmd->transceiver = XCVR_DUMMY1;
        ecmd->port = PORT_NONE;
@@ -94,9 +97,9 @@ static int i40evf_get_settings(struct net_device *netdev,
 static int i40evf_get_sset_count(struct net_device *netdev, int sset)
 {
        if (sset == ETH_SS_STATS)
-               return I40EVF_STATS_LEN;
+               return I40EVF_STATS_LEN(netdev);
        else
-               return -ENOTSUPP;
+               return -EINVAL;
 }
 
 /**
@@ -219,13 +222,11 @@ static void i40evf_get_ringparam(struct net_device *netdev,
                                  struct ethtool_ringparam *ring)
 {
        struct i40evf_adapter *adapter = netdev_priv(netdev);
-       struct i40e_ring *tx_ring = adapter->tx_rings[0];
-       struct i40e_ring *rx_ring = adapter->rx_rings[0];
 
        ring->rx_max_pending = I40EVF_MAX_RXD;
        ring->tx_max_pending = I40EVF_MAX_TXD;
-       ring->rx_pending = rx_ring->count;
-       ring->tx_pending = tx_ring->count;
+       ring->rx_pending = adapter->rx_desc_count;
+       ring->tx_pending = adapter->tx_desc_count;
 }
 
 /**
@@ -241,7 +242,6 @@ static int i40evf_set_ringparam(struct net_device *netdev,
 {
        struct i40evf_adapter *adapter = netdev_priv(netdev);
        u32 new_rx_count, new_tx_count;
-       int i;
 
        if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending))
                return -EINVAL;
@@ -257,17 +257,16 @@ static int i40evf_set_ringparam(struct net_device *netdev,
        new_rx_count = ALIGN(new_rx_count, I40EVF_REQ_DESCRIPTOR_MULTIPLE);
 
        /* if nothing to do return success */
-       if ((new_tx_count == adapter->tx_rings[0]->count) &&
-           (new_rx_count == adapter->rx_rings[0]->count))
+       if ((new_tx_count == adapter->tx_desc_count) &&
+           (new_rx_count == adapter->rx_desc_count))
                return 0;
 
-       for (i = 0; i < adapter->vsi_res->num_queue_pairs; i++) {
-               adapter->tx_rings[0]->count = new_tx_count;
-               adapter->rx_rings[0]->count = new_rx_count;
-       }
+       adapter->tx_desc_count = new_tx_count;
+       adapter->rx_desc_count = new_rx_count;
 
        if (netif_running(netdev))
                i40evf_reinit_locked(adapter);
+
        return 0;
 }
 
@@ -290,14 +289,13 @@ static int i40evf_get_coalesce(struct net_device *netdev,
        ec->rx_max_coalesced_frames = vsi->work_limit;
 
        if (ITR_IS_DYNAMIC(vsi->rx_itr_setting))
-               ec->rx_coalesce_usecs = 1;
-       else
-               ec->rx_coalesce_usecs = vsi->rx_itr_setting;
+               ec->use_adaptive_rx_coalesce = 1;
 
        if (ITR_IS_DYNAMIC(vsi->tx_itr_setting))
-               ec->tx_coalesce_usecs = 1;
-       else
-               ec->tx_coalesce_usecs = vsi->tx_itr_setting;
+               ec->use_adaptive_tx_coalesce = 1;
+
+       ec->rx_coalesce_usecs = vsi->rx_itr_setting & ~I40E_ITR_DYNAMIC;
+       ec->tx_coalesce_usecs = vsi->tx_itr_setting & ~I40E_ITR_DYNAMIC;
 
        return 0;
 }
@@ -318,54 +316,361 @@ static int i40evf_set_coalesce(struct net_device *netdev,
        struct i40e_q_vector *q_vector;
        int i;
 
-       if (ec->tx_max_coalesced_frames || ec->rx_max_coalesced_frames)
-               vsi->work_limit = ec->tx_max_coalesced_frames;
+       if (ec->tx_max_coalesced_frames_irq || ec->rx_max_coalesced_frames_irq)
+               vsi->work_limit = ec->tx_max_coalesced_frames_irq;
+
+       if ((ec->rx_coalesce_usecs >= (I40E_MIN_ITR << 1)) &&
+           (ec->rx_coalesce_usecs <= (I40E_MAX_ITR << 1)))
+               vsi->rx_itr_setting = ec->rx_coalesce_usecs;
+
+       else
+               return -EINVAL;
+
+       if ((ec->tx_coalesce_usecs >= (I40E_MIN_ITR << 1)) &&
+           (ec->tx_coalesce_usecs <= (I40E_MAX_ITR << 1)))
+               vsi->tx_itr_setting = ec->tx_coalesce_usecs;
+       else if (ec->use_adaptive_tx_coalesce)
+               vsi->tx_itr_setting = (I40E_ITR_DYNAMIC |
+                                      ITR_REG_TO_USEC(I40E_ITR_RX_DEF));
+       else
+               return -EINVAL;
+
+       if (ec->use_adaptive_rx_coalesce)
+               vsi->rx_itr_setting |= I40E_ITR_DYNAMIC;
+       else
+               vsi->rx_itr_setting &= ~I40E_ITR_DYNAMIC;
+
+       if (ec->use_adaptive_tx_coalesce)
+               vsi->tx_itr_setting |= I40E_ITR_DYNAMIC;
+       else
+               vsi->tx_itr_setting &= ~I40E_ITR_DYNAMIC;
 
-       switch (ec->rx_coalesce_usecs) {
-       case 0:
-               vsi->rx_itr_setting = 0;
+       for (i = 0; i < adapter->num_msix_vectors - NONQ_VECS; i++) {
+               q_vector = adapter->q_vector[i];
+               q_vector->rx.itr = ITR_TO_REG(vsi->rx_itr_setting);
+               wr32(hw, I40E_VFINT_ITRN1(0, i), q_vector->rx.itr);
+               q_vector->tx.itr = ITR_TO_REG(vsi->tx_itr_setting);
+               wr32(hw, I40E_VFINT_ITRN1(1, i), q_vector->tx.itr);
+               i40e_flush(hw);
+       }
+
+       return 0;
+}
+
+/**
+ * i40e_get_rss_hash_opts - Get RSS hash Input Set for each flow type
+ * @adapter: board private structure
+ * @cmd: ethtool rxnfc command
+ *
+ * Returns Success if the flow is supported, else Invalid Input.
+ **/
+static int i40evf_get_rss_hash_opts(struct i40evf_adapter *adapter,
+                                   struct ethtool_rxnfc *cmd)
+{
+       struct i40e_hw *hw = &adapter->hw;
+       u64 hena = (u64)rd32(hw, I40E_VFQF_HENA(0)) |
+                  ((u64)rd32(hw, I40E_VFQF_HENA(1)) << 32);
+
+       /* We always hash on IP src and dest addresses */
+       cmd->data = RXH_IP_SRC | RXH_IP_DST;
+
+       switch (cmd->flow_type) {
+       case TCP_V4_FLOW:
+               if (hena & ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP))
+                       cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
                break;
-       case 1:
-               vsi->rx_itr_setting = (I40E_ITR_DYNAMIC
-                                      | ITR_REG_TO_USEC(I40E_ITR_RX_DEF));
+       case UDP_V4_FLOW:
+               if (hena & ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP))
+                       cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
                break;
-       default:
-               if ((ec->rx_coalesce_usecs < (I40E_MIN_ITR << 1)) ||
-                   (ec->rx_coalesce_usecs > (I40E_MAX_ITR << 1)))
-                       return -EINVAL;
-               vsi->rx_itr_setting = ec->rx_coalesce_usecs;
+
+       case SCTP_V4_FLOW:
+       case AH_ESP_V4_FLOW:
+       case AH_V4_FLOW:
+       case ESP_V4_FLOW:
+       case IPV4_FLOW:
+               break;
+
+       case TCP_V6_FLOW:
+               if (hena & ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP))
+                       cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
                break;
+       case UDP_V6_FLOW:
+               if (hena & ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP))
+                       cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+               break;
+
+       case SCTP_V6_FLOW:
+       case AH_ESP_V6_FLOW:
+       case AH_V6_FLOW:
+       case ESP_V6_FLOW:
+       case IPV6_FLOW:
+               break;
+       default:
+               cmd->data = 0;
+               return -EINVAL;
        }
 
-       switch (ec->tx_coalesce_usecs) {
-       case 0:
-               vsi->tx_itr_setting = 0;
+       return 0;
+}
+
+/**
+ * i40evf_get_rxnfc - command to get RX flow classification rules
+ * @netdev: network interface device structure
+ * @cmd: ethtool rxnfc command
+ *
+ * Returns Success if the command is supported.
+ **/
+static int i40evf_get_rxnfc(struct net_device *netdev,
+                           struct ethtool_rxnfc *cmd,
+                           u32 *rule_locs)
+{
+       struct i40evf_adapter *adapter = netdev_priv(netdev);
+       int ret = -EOPNOTSUPP;
+
+       switch (cmd->cmd) {
+       case ETHTOOL_GRXRINGS:
+               cmd->data = adapter->vsi_res->num_queue_pairs;
+               ret = 0;
                break;
-       case 1:
-               vsi->tx_itr_setting = (I40E_ITR_DYNAMIC
-                                      | ITR_REG_TO_USEC(I40E_ITR_TX_DEF));
+       case ETHTOOL_GRXFH:
+               ret = i40evf_get_rss_hash_opts(adapter, cmd);
                break;
        default:
-               if ((ec->tx_coalesce_usecs < (I40E_MIN_ITR << 1)) ||
-                   (ec->tx_coalesce_usecs > (I40E_MAX_ITR << 1)))
+               break;
+       }
+
+       return ret;
+}
+
+/**
+ * i40evf_set_rss_hash_opt - Enable/Disable flow types for RSS hash
+ * @adapter: board private structure
+ * @cmd: ethtool rxnfc command
+ *
+ * Returns Success if the flow input set is supported.
+ **/
+static int i40evf_set_rss_hash_opt(struct i40evf_adapter *adapter,
+                                  struct ethtool_rxnfc *nfc)
+{
+       struct i40e_hw *hw = &adapter->hw;
+
+       u64 hena = (u64)rd32(hw, I40E_VFQF_HENA(0)) |
+                  ((u64)rd32(hw, I40E_VFQF_HENA(1)) << 32);
+
+       /* RSS does not support anything other than hashing
+        * to queues on src and dst IPs and ports
+        */
+       if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST |
+                         RXH_L4_B_0_1 | RXH_L4_B_2_3))
+               return -EINVAL;
+
+       /* We need at least the IP SRC and DEST fields for hashing */
+       if (!(nfc->data & RXH_IP_SRC) ||
+           !(nfc->data & RXH_IP_DST))
+               return -EINVAL;
+
+       switch (nfc->flow_type) {
+       case TCP_V4_FLOW:
+               switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
+               case 0:
+                       hena &= ~((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP);
+                       break;
+               case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
+                       hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP);
+                       break;
+               default:
                        return -EINVAL;
-               vsi->tx_itr_setting = ec->tx_coalesce_usecs;
+               }
+               break;
+       case TCP_V6_FLOW:
+               switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
+               case 0:
+                       hena &= ~((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP);
+                       break;
+               case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
+                       hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP);
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+       case UDP_V4_FLOW:
+               switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
+               case 0:
+                       hena &= ~(((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) |
+                                 ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4));
+                       break;
+               case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
+                       hena |= (((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) |
+                                ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4));
+                       break;
+               default:
+                       return -EINVAL;
+               }
                break;
+       case UDP_V6_FLOW:
+               switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
+               case 0:
+                       hena &= ~(((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) |
+                                 ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6));
+                       break;
+               case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
+                       hena |= (((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) |
+                                ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6));
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+       case AH_ESP_V4_FLOW:
+       case AH_V4_FLOW:
+       case ESP_V4_FLOW:
+       case SCTP_V4_FLOW:
+               if ((nfc->data & RXH_L4_B_0_1) ||
+                   (nfc->data & RXH_L4_B_2_3))
+                       return -EINVAL;
+               hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_OTHER);
+               break;
+       case AH_ESP_V6_FLOW:
+       case AH_V6_FLOW:
+       case ESP_V6_FLOW:
+       case SCTP_V6_FLOW:
+               if ((nfc->data & RXH_L4_B_0_1) ||
+                   (nfc->data & RXH_L4_B_2_3))
+                       return -EINVAL;
+               hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_OTHER);
+               break;
+       case IPV4_FLOW:
+               hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_OTHER) |
+                       ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4);
+               break;
+       case IPV6_FLOW:
+               hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_OTHER) |
+                       ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6);
+               break;
+       default:
+               return -EINVAL;
        }
 
-       for (i = 0; i < adapter->num_msix_vectors - NONQ_VECS; i++) {
-               q_vector = adapter->q_vector[i];
-               q_vector->rx.itr = ITR_TO_REG(vsi->rx_itr_setting);
-               wr32(hw, I40E_VFINT_ITRN1(0, i), q_vector->rx.itr);
-               q_vector->tx.itr = ITR_TO_REG(vsi->tx_itr_setting);
-               wr32(hw, I40E_VFINT_ITRN1(1, i), q_vector->tx.itr);
-               i40e_flush(hw);
+       wr32(hw, I40E_VFQF_HENA(0), (u32)hena);
+       wr32(hw, I40E_VFQF_HENA(1), (u32)(hena >> 32));
+       i40e_flush(hw);
+
+       return 0;
+}
+
+/**
+ * i40evf_set_rxnfc - command to set RX flow classification rules
+ * @netdev: network interface device structure
+ * @cmd: ethtool rxnfc command
+ *
+ * Returns Success if the command is supported.
+ **/
+static int i40evf_set_rxnfc(struct net_device *netdev,
+                           struct ethtool_rxnfc *cmd)
+{
+       struct i40evf_adapter *adapter = netdev_priv(netdev);
+       int ret = -EOPNOTSUPP;
+
+       switch (cmd->cmd) {
+       case ETHTOOL_SRXFH:
+               ret = i40evf_set_rss_hash_opt(adapter, cmd);
+               break;
+       default:
+               break;
+       }
+
+       return ret;
+}
+
+/**
+ * i40evf_get_channels: get the number of channels supported by the device
+ * @netdev: network interface device structure
+ * @ch: channel information structure
+ *
+ * For the purposes of our device, we only use combined channels, i.e. a tx/rx
+ * queue pair. Report one extra channel to match our "other" MSI-X vector.
+ **/
+static void i40evf_get_channels(struct net_device *netdev,
+                               struct ethtool_channels *ch)
+{
+       struct i40evf_adapter *adapter = netdev_priv(netdev);
+
+       /* Report maximum channels */
+       ch->max_combined = adapter->vsi_res->num_queue_pairs;
+
+       ch->max_other = NONQ_VECS;
+       ch->other_count = NONQ_VECS;
+
+       ch->combined_count = adapter->vsi_res->num_queue_pairs;
+}
+
+/**
+ * i40evf_get_rxfh_indir_size - get the rx flow hash indirection table size
+ * @netdev: network interface device structure
+ *
+ * Returns the table size.
+ **/
+static u32 i40evf_get_rxfh_indir_size(struct net_device *netdev)
+{
+       return (I40E_VFQF_HLUT_MAX_INDEX + 1) * 4;
+}
+
+/**
+ * i40evf_get_rxfh - get the rx flow hash indirection table
+ * @netdev: network interface device structure
+ * @indir: indirection table
+ * @key: hash key (will be %NULL until get_rxfh_key_size is implemented)
+ *
+ * Reads the indirection table directly from the hardware. Always returns 0.
+ **/
+static int i40evf_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key)
+{
+       struct i40evf_adapter *adapter = netdev_priv(netdev);
+       struct i40e_hw *hw = &adapter->hw;
+       u32 hlut_val;
+       int i, j;
+
+       for (i = 0, j = 0; i < I40E_VFQF_HLUT_MAX_INDEX; i++) {
+               hlut_val = rd32(hw, I40E_VFQF_HLUT(i));
+               indir[j++] = hlut_val & 0xff;
+               indir[j++] = (hlut_val >> 8) & 0xff;
+               indir[j++] = (hlut_val >> 16) & 0xff;
+               indir[j++] = (hlut_val >> 24) & 0xff;
+       }
+       return 0;
+}
+
+/**
+ * i40evf_set_rxfh - set the rx flow hash indirection table
+ * @netdev: network interface device structure
+ * @indir: indirection table
+ * @key: hash key (will be %NULL until get_rxfh_key_size is implemented)
+ *
+ * Returns -EINVAL if the table specifies an inavlid queue id, otherwise
+ * returns 0 after programming the table.
+ **/
+static int i40evf_set_rxfh(struct net_device *netdev, const u32 *indir,
+                          const u8 *key)
+{
+       struct i40evf_adapter *adapter = netdev_priv(netdev);
+       struct i40e_hw *hw = &adapter->hw;
+       u32 hlut_val;
+       int i, j;
+
+       for (i = 0, j = 0; i < I40E_VFQF_HLUT_MAX_INDEX + 1; i++) {
+               hlut_val = indir[j++];
+               hlut_val |= indir[j++] << 8;
+               hlut_val |= indir[j++] << 16;
+               hlut_val |= indir[j++] << 24;
+               wr32(hw, I40E_VFQF_HLUT(i), hlut_val);
        }
 
        return 0;
 }
 
-static struct ethtool_ops i40evf_ethtool_ops = {
+static const struct ethtool_ops i40evf_ethtool_ops = {
        .get_settings           = i40evf_get_settings,
        .get_drvinfo            = i40evf_get_drvinfo,
        .get_link               = ethtool_op_get_link,
@@ -378,6 +683,12 @@ static struct ethtool_ops i40evf_ethtool_ops = {
        .set_msglevel           = i40evf_set_msglevel,
        .get_coalesce           = i40evf_get_coalesce,
        .set_coalesce           = i40evf_set_coalesce,
+       .get_rxnfc              = i40evf_get_rxnfc,
+       .set_rxnfc              = i40evf_set_rxnfc,
+       .get_rxfh_indir_size    = i40evf_get_rxfh_indir_size,
+       .get_rxfh               = i40evf_get_rxfh,
+       .set_rxfh               = i40evf_set_rxfh,
+       .get_channels           = i40evf_get_channels,
 };
 
 /**
@@ -389,5 +700,5 @@ static struct ethtool_ops i40evf_ethtool_ops = {
  **/
 void i40evf_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &i40evf_ethtool_ops);
+       netdev->ethtool_ops = &i40evf_ethtool_ops;
 }
index 2797548fde0dd918051a8f033472749e86d124c9..7fc5f3b5d6bf610b936ae229dd31cf93435c2ee4 100644 (file)
@@ -12,6 +12,9 @@
  * 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, see <http://www.gnu.org/licenses/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
 #include "i40e_prototype.h"
 static int i40evf_setup_all_tx_resources(struct i40evf_adapter *adapter);
 static int i40evf_setup_all_rx_resources(struct i40evf_adapter *adapter);
+static void i40evf_free_all_tx_resources(struct i40evf_adapter *adapter);
+static void i40evf_free_all_rx_resources(struct i40evf_adapter *adapter);
 static int i40evf_close(struct net_device *netdev);
 
 char i40evf_driver_name[] = "i40evf";
 static const char i40evf_driver_string[] =
        "Intel(R) XL710 X710 Virtual Function Network Driver";
 
-#define DRV_VERSION "0.9.16"
+#define DRV_VERSION "0.9.34"
 const char i40evf_driver_version[] = DRV_VERSION;
 static const char i40evf_copyright[] =
        "Copyright (c) 2013 - 2014 Intel Corporation.";
@@ -167,7 +172,6 @@ static void i40evf_tx_timeout(struct net_device *netdev)
        struct i40evf_adapter *adapter = netdev_priv(netdev);
 
        adapter->tx_timeout_count++;
-       dev_info(&adapter->pdev->dev, "TX timeout detected.\n");
        if (!(adapter->flags & I40EVF_FLAG_RESET_PENDING)) {
                adapter->flags |= I40EVF_FLAG_RESET_NEEDED;
                schedule_work(&adapter->reset_task);
@@ -657,12 +661,9 @@ i40evf_vlan_filter *i40evf_add_vlan(struct i40evf_adapter *adapter, u16 vlan)
        f = i40evf_find_vlan(adapter, vlan);
        if (NULL == f) {
                f = kzalloc(sizeof(*f), GFP_ATOMIC);
-               if (NULL == f) {
-                       dev_info(&adapter->pdev->dev,
-                                "%s: no memory for new VLAN filter\n",
-                                __func__);
+               if (NULL == f)
                        return NULL;
-               }
+
                f->vlan = vlan;
 
                INIT_LIST_HEAD(&f->list);
@@ -688,7 +689,6 @@ static void i40evf_del_vlan(struct i40evf_adapter *adapter, u16 vlan)
                f->remove = true;
                adapter->aq_required |= I40EVF_FLAG_AQ_DEL_VLAN_FILTER;
        }
-       return;
 }
 
 /**
@@ -767,14 +767,12 @@ i40evf_mac_filter *i40evf_add_filter(struct i40evf_adapter *adapter,
        if (NULL == f) {
                f = kzalloc(sizeof(*f), GFP_ATOMIC);
                if (NULL == f) {
-                       dev_info(&adapter->pdev->dev,
-                                "%s: no memory for new filter\n", __func__);
                        clear_bit(__I40EVF_IN_CRITICAL_TASK,
                                  &adapter->crit_section);
                        return NULL;
                }
 
-               memcpy(f->macaddr, macaddr, ETH_ALEN);
+               ether_addr_copy(f->macaddr, macaddr);
 
                list_add(&f->list, &adapter->mac_filter_list);
                f->add = true;
@@ -807,9 +805,8 @@ static int i40evf_set_mac(struct net_device *netdev, void *p)
 
        f = i40evf_add_filter(adapter, addr->sa_data);
        if (f) {
-               memcpy(hw->mac.addr, addr->sa_data, netdev->addr_len);
-               memcpy(netdev->dev_addr, adapter->hw.mac.addr,
-                      netdev->addr_len);
+               ether_addr_copy(hw->mac.addr, addr->sa_data);
+               ether_addr_copy(netdev->dev_addr, adapter->hw.mac.addr);
        }
 
        return (f == NULL) ? -ENOMEM : 0;
@@ -841,7 +838,7 @@ static void i40evf_set_rx_mode(struct net_device *netdev)
        list_for_each_entry_safe(f, ftmp, &adapter->mac_filter_list, list) {
                bool found = false;
 
-               if (f->macaddr[0] & 0x01) {
+               if (is_multicast_ether_addr(f->macaddr)) {
                        netdev_for_each_mc_addr(mca, netdev) {
                                if (ether_addr_equal(mca->addr, f->macaddr)) {
                                        found = true;
@@ -970,6 +967,9 @@ void i40evf_down(struct i40evf_adapter *adapter)
        struct net_device *netdev = adapter->netdev;
        struct i40evf_mac_filter *f;
 
+       if (adapter->state == __I40EVF_DOWN)
+               return;
+
        /* remove all MAC filters */
        list_for_each_entry(f, &adapter->mac_filter_list, list) {
                f->remove = true;
@@ -1027,30 +1027,21 @@ i40evf_acquire_msix_vectors(struct i40evf_adapter *adapter, int vectors)
         * Right now, we simply care about how many we'll get; we'll
         * set them up later while requesting irq's.
         */
-       while (vectors >= vector_threshold) {
-               err = pci_enable_msix(adapter->pdev, adapter->msix_entries,
-                                     vectors);
-               if (!err) /* Success in acquiring all requested vectors. */
-                       break;
-               else if (err < 0)
-                       vectors = 0; /* Nasty failure, quit now */
-               else /* err == number of vectors we should try again with */
-                       vectors = err;
-       }
-
-       if (vectors < vector_threshold) {
-               dev_err(&adapter->pdev->dev, "Unable to allocate MSI-X interrupts.\n");
+       err = pci_enable_msix_range(adapter->pdev, adapter->msix_entries,
+                                   vector_threshold, vectors);
+       if (err < 0) {
+               dev_err(&adapter->pdev->dev, "Unable to allocate MSI-X interrupts\n");
                kfree(adapter->msix_entries);
                adapter->msix_entries = NULL;
-               err = -EIO;
-       } else {
-               /* Adjust for only the vectors we'll use, which is minimum
-                * of max_msix_q_vectors + NONQ_VECS, or the number of
-                * vectors we were allocated.
-                */
-               adapter->num_msix_vectors = vectors;
+               return err;
        }
-       return err;
+
+       /* Adjust for only the vectors we'll use, which is minimum
+        * of max_msix_q_vectors + NONQ_VECS, or the number of
+        * vectors we were allocated.
+        */
+       adapter->num_msix_vectors = err;
+       return 0;
 }
 
 /**
@@ -1096,14 +1087,14 @@ static int i40evf_alloc_queues(struct i40evf_adapter *adapter)
                tx_ring->queue_index = i;
                tx_ring->netdev = adapter->netdev;
                tx_ring->dev = &adapter->pdev->dev;
-               tx_ring->count = I40EVF_DEFAULT_TXD;
+               tx_ring->count = adapter->tx_desc_count;
                adapter->tx_rings[i] = tx_ring;
 
                rx_ring = &tx_ring[1];
                rx_ring->queue_index = i;
                rx_ring->netdev = adapter->netdev;
                rx_ring->dev = &adapter->pdev->dev;
-               rx_ring->count = I40EVF_DEFAULT_RXD;
+               rx_ring->count = adapter->rx_desc_count;
                adapter->rx_rings[i] = rx_ring;
        }
 
@@ -1141,9 +1132,6 @@ static int i40evf_set_interrupt_capability(struct i40evf_adapter *adapter)
        v_budget = min_t(int, pairs, (int)(num_online_cpus() * 2)) + NONQ_VECS;
        v_budget = min_t(int, v_budget, (int)adapter->vf_res->max_vectors);
 
-       /* A failure in MSI-X entry allocation isn't fatal, but it does
-        * mean we disable MSI-X capabilities of the adapter.
-        */
        adapter->msix_entries = kcalloc(v_budget,
                                        sizeof(struct msix_entry), GFP_KERNEL);
        if (!adapter->msix_entries) {
@@ -1183,7 +1171,7 @@ static int i40evf_alloc_q_vectors(struct i40evf_adapter *adapter)
                q_vector->vsi = &adapter->vsi;
                q_vector->v_idx = q_idx;
                netif_napi_add(adapter->netdev, &q_vector->napi,
-                                      i40evf_napi_poll, 64);
+                                      i40evf_napi_poll, NAPI_POLL_WEIGHT);
                adapter->q_vector[q_idx] = q_vector;
        }
 
@@ -1236,8 +1224,6 @@ void i40evf_reset_interrupt_capability(struct i40evf_adapter *adapter)
        pci_disable_msix(adapter->pdev);
        kfree(adapter->msix_entries);
        adapter->msix_entries = NULL;
-
-       return;
 }
 
 /**
@@ -1309,7 +1295,6 @@ static void i40evf_watchdog_task(struct work_struct *work)
                goto restart_watchdog;
 
        if (adapter->flags & I40EVF_FLAG_PF_COMMS_FAILED) {
-               dev_info(&adapter->pdev->dev, "Checking for redemption\n");
                if ((rd32(hw, I40E_VFGEN_RSTAT) & 0x3) == I40E_VFR_VFACTIVE) {
                        /* A chance for redemption! */
                        dev_err(&adapter->pdev->dev, "Hardware came out of reset. Attempting reinit.\n");
@@ -1340,8 +1325,7 @@ static void i40evf_watchdog_task(struct work_struct *work)
            (rd32(hw, I40E_VFGEN_RSTAT) & 0x3) != I40E_VFR_VFACTIVE) {
                adapter->state = __I40EVF_RESETTING;
                adapter->flags |= I40EVF_FLAG_RESET_PENDING;
-               dev_err(&adapter->pdev->dev, "Hardware reset detected.\n");
-               dev_info(&adapter->pdev->dev, "Scheduling reset task\n");
+               dev_err(&adapter->pdev->dev, "Hardware reset detected\n");
                schedule_work(&adapter->reset_task);
                adapter->aq_pending = 0;
                adapter->aq_required = 0;
@@ -1413,7 +1397,7 @@ static void i40evf_watchdog_task(struct work_struct *work)
 }
 
 /**
- * i40evf_configure_rss - increment to next available tx queue
+ * next_queue - increment to next available tx queue
  * @adapter: board private structure
  * @j: queue counter
  *
@@ -1504,15 +1488,12 @@ static void i40evf_reset_task(struct work_struct *work)
        for (i = 0; i < I40EVF_RESET_WAIT_COUNT; i++) {
                rstat_val = rd32(hw, I40E_VFGEN_RSTAT) &
                            I40E_VFGEN_RSTAT_VFR_STATE_MASK;
-               if (rstat_val != I40E_VFR_VFACTIVE) {
-                       dev_info(&adapter->pdev->dev, "Reset now occurring\n");
+               if (rstat_val != I40E_VFR_VFACTIVE)
                        break;
-               } else {
+               else
                        msleep(I40EVF_RESET_WAIT_MS);
-               }
        }
        if (i == I40EVF_RESET_WAIT_COUNT) {
-               dev_err(&adapter->pdev->dev, "Reset was not detected\n");
                adapter->flags &= ~I40EVF_FLAG_RESET_PENDING;
                goto continue_reset; /* act like the reset happened */
        }
@@ -1521,22 +1502,24 @@ static void i40evf_reset_task(struct work_struct *work)
        for (i = 0; i < I40EVF_RESET_WAIT_COUNT; i++) {
                rstat_val = rd32(hw, I40E_VFGEN_RSTAT) &
                            I40E_VFGEN_RSTAT_VFR_STATE_MASK;
-               if (rstat_val == I40E_VFR_VFACTIVE) {
-                       dev_info(&adapter->pdev->dev, "Reset is complete. Reinitializing.\n");
+               if (rstat_val == I40E_VFR_VFACTIVE)
                        break;
-               } else {
+               else
                        msleep(I40EVF_RESET_WAIT_MS);
-               }
        }
        if (i == I40EVF_RESET_WAIT_COUNT) {
                /* reset never finished */
-               dev_err(&adapter->pdev->dev, "Reset never finished (%x). PF driver is dead, and so am I.\n",
+               dev_err(&adapter->pdev->dev, "Reset never finished (%x)\n",
                        rstat_val);
                adapter->flags |= I40EVF_FLAG_PF_COMMS_FAILED;
 
-               if (netif_running(adapter->netdev))
-                       i40evf_close(adapter->netdev);
-
+               if (netif_running(adapter->netdev)) {
+                       set_bit(__I40E_DOWN, &adapter->vsi.state);
+                       i40evf_down(adapter);
+                       i40evf_free_traffic_irqs(adapter);
+                       i40evf_free_all_tx_resources(adapter);
+                       i40evf_free_all_rx_resources(adapter);
+               }
                i40evf_free_misc_irq(adapter);
                i40evf_reset_interrupt_capability(adapter);
                i40evf_free_queues(adapter);
@@ -1591,7 +1574,7 @@ static void i40evf_reset_task(struct work_struct *work)
        }
        return;
 reset_err:
-       dev_err(&adapter->pdev->dev, "failed to allocate resources during reinit.\n");
+       dev_err(&adapter->pdev->dev, "failed to allocate resources during reinit\n");
        i40evf_close(adapter->netdev);
 }
 
@@ -1607,6 +1590,7 @@ static void i40evf_adminq_task(struct work_struct *work)
        struct i40e_arq_event_info event;
        struct i40e_virtchnl_msg *v_msg;
        i40e_status ret;
+       u32 val, oldval;
        u16 pending;
 
        if (adapter->flags & I40EVF_FLAG_PF_COMMS_FAILED)
@@ -1614,11 +1598,9 @@ static void i40evf_adminq_task(struct work_struct *work)
 
        event.msg_size = I40EVF_MAX_AQ_BUF_SIZE;
        event.msg_buf = kzalloc(event.msg_size, GFP_KERNEL);
-       if (!event.msg_buf) {
-               dev_info(&adapter->pdev->dev, "%s: no memory for ARQ clean\n",
-                                __func__);
+       if (!event.msg_buf)
                return;
-       }
+
        v_msg = (struct i40e_virtchnl_msg *)&event.desc;
        do {
                ret = i40evf_clean_arq_element(hw, &event, &pending);
@@ -1636,6 +1618,41 @@ static void i40evf_adminq_task(struct work_struct *work)
                }
        } while (pending);
 
+       /* check for error indications */
+       val = rd32(hw, hw->aq.arq.len);
+       oldval = val;
+       if (val & I40E_VF_ARQLEN_ARQVFE_MASK) {
+               dev_info(&adapter->pdev->dev, "ARQ VF Error detected\n");
+               val &= ~I40E_VF_ARQLEN_ARQVFE_MASK;
+       }
+       if (val & I40E_VF_ARQLEN_ARQOVFL_MASK) {
+               dev_info(&adapter->pdev->dev, "ARQ Overflow Error detected\n");
+               val &= ~I40E_VF_ARQLEN_ARQOVFL_MASK;
+       }
+       if (val & I40E_VF_ARQLEN_ARQCRIT_MASK) {
+               dev_info(&adapter->pdev->dev, "ARQ Critical Error detected\n");
+               val &= ~I40E_VF_ARQLEN_ARQCRIT_MASK;
+       }
+       if (oldval != val)
+               wr32(hw, hw->aq.arq.len, val);
+
+       val = rd32(hw, hw->aq.asq.len);
+       oldval = val;
+       if (val & I40E_VF_ATQLEN_ATQVFE_MASK) {
+               dev_info(&adapter->pdev->dev, "ASQ VF Error detected\n");
+               val &= ~I40E_VF_ATQLEN_ATQVFE_MASK;
+       }
+       if (val & I40E_VF_ATQLEN_ATQOVFL_MASK) {
+               dev_info(&adapter->pdev->dev, "ASQ Overflow Error detected\n");
+               val &= ~I40E_VF_ATQLEN_ATQOVFL_MASK;
+       }
+       if (val & I40E_VF_ATQLEN_ATQCRIT_MASK) {
+               dev_info(&adapter->pdev->dev, "ASQ Critical Error detected\n");
+               val &= ~I40E_VF_ATQLEN_ATQCRIT_MASK;
+       }
+       if (oldval != val)
+               wr32(hw, hw->aq.asq.len, val);
+
        /* re-enable Admin queue interrupt cause */
        i40evf_misc_irq_enable(adapter);
 
@@ -1673,6 +1690,7 @@ static int i40evf_setup_all_tx_resources(struct i40evf_adapter *adapter)
        int i, err = 0;
 
        for (i = 0; i < adapter->vsi_res->num_queue_pairs; i++) {
+               adapter->tx_rings[i]->count = adapter->tx_desc_count;
                err = i40evf_setup_tx_descriptors(adapter->tx_rings[i]);
                if (!err)
                        continue;
@@ -1700,6 +1718,7 @@ static int i40evf_setup_all_rx_resources(struct i40evf_adapter *adapter)
        int i, err = 0;
 
        for (i = 0; i < adapter->vsi_res->num_queue_pairs; i++) {
+               adapter->rx_rings[i]->count = adapter->rx_desc_count;
                err = i40evf_setup_rx_descriptors(adapter->rx_rings[i]);
                if (!err)
                        continue;
@@ -1804,12 +1823,11 @@ static int i40evf_close(struct net_device *netdev)
        if (adapter->state <= __I40EVF_DOWN)
                return 0;
 
-       /* signal that we are down to the interrupt handler */
-       adapter->state = __I40EVF_DOWN;
 
        set_bit(__I40E_DOWN, &adapter->vsi.state);
 
        i40evf_down(adapter);
+       adapter->state = __I40EVF_DOWN;
        i40evf_free_traffic_irqs(adapter);
 
        i40evf_free_all_tx_resources(adapter);
@@ -1848,8 +1866,6 @@ void i40evf_reinit_locked(struct i40evf_adapter *adapter)
 
        WARN_ON(in_interrupt());
 
-       adapter->state = __I40EVF_RESETTING;
-
        i40evf_down(adapter);
 
        /* allocate transmit descriptors */
@@ -1872,7 +1888,7 @@ void i40evf_reinit_locked(struct i40evf_adapter *adapter)
        return;
 
 err_reinit:
-       dev_err(&adapter->pdev->dev, "failed to allocate resources during reinit.\n");
+       dev_err(&adapter->pdev->dev, "failed to allocate resources during reinit\n");
        i40evf_close(netdev);
 }
 
@@ -1967,7 +1983,7 @@ static void i40evf_init_task(struct work_struct *work)
                }
                err = i40evf_check_reset_complete(hw);
                if (err) {
-                       dev_err(&pdev->dev, "Device is still in reset (%d)\n",
+                       dev_info(&pdev->dev, "Device is still in reset (%d), retrying\n",
                                err);
                        goto err;
                }
@@ -1993,14 +2009,14 @@ static void i40evf_init_task(struct work_struct *work)
                break;
        case __I40EVF_INIT_VERSION_CHECK:
                if (!i40evf_asq_done(hw)) {
-                       dev_err(&pdev->dev, "Admin queue command never completed.\n");
+                       dev_err(&pdev->dev, "Admin queue command never completed\n");
                        goto err;
                }
 
                /* aq msg sent, awaiting reply */
                err = i40evf_verify_api_ver(adapter);
                if (err) {
-                       dev_err(&pdev->dev, "Unable to verify API version (%d)\n",
+                       dev_info(&pdev->dev, "Unable to verify API version (%d), retrying\n",
                                err);
                        goto err;
                }
@@ -2074,12 +2090,12 @@ static void i40evf_init_task(struct work_struct *work)
        netdev->hw_features &= ~NETIF_F_RXCSUM;
 
        if (!is_valid_ether_addr(adapter->hw.mac.addr)) {
-               dev_info(&pdev->dev, "Invalid MAC address %pMAC, using random\n",
+               dev_info(&pdev->dev, "Invalid MAC address %pM, using random\n",
                         adapter->hw.mac.addr);
                random_ether_addr(adapter->hw.mac.addr);
        }
-       memcpy(netdev->dev_addr, adapter->hw.mac.addr, netdev->addr_len);
-       memcpy(netdev->perm_addr, adapter->hw.mac.addr, netdev->addr_len);
+       ether_addr_copy(netdev->dev_addr, adapter->hw.mac.addr);
+       ether_addr_copy(netdev->perm_addr, adapter->hw.mac.addr);
 
        INIT_LIST_HEAD(&adapter->mac_filter_list);
        INIT_LIST_HEAD(&adapter->vlan_filter_list);
@@ -2087,7 +2103,7 @@ static void i40evf_init_task(struct work_struct *work)
        if (NULL == f)
                goto err_sw_init;
 
-       memcpy(f->macaddr, adapter->hw.mac.addr, ETH_ALEN);
+       ether_addr_copy(f->macaddr, adapter->hw.mac.addr);
        f->add = true;
        adapter->aq_required |= I40EVF_FLAG_AQ_ADD_MAC_FILTER;
 
@@ -2098,6 +2114,8 @@ static void i40evf_init_task(struct work_struct *work)
        adapter->watchdog_timer.data = (unsigned long)adapter;
        mod_timer(&adapter->watchdog_timer, jiffies + 1);
 
+       adapter->tx_desc_count = I40EVF_DEFAULT_TXD;
+       adapter->rx_desc_count = I40EVF_DEFAULT_RXD;
        err = i40evf_init_interrupt_scheme(adapter);
        if (err)
                goto err_sw_init;
@@ -2114,8 +2132,10 @@ static void i40evf_init_task(struct work_struct *work)
        adapter->vsi.back = adapter;
        adapter->vsi.base_vector = 1;
        adapter->vsi.work_limit = I40E_DEFAULT_IRQ_WORK;
-       adapter->vsi.rx_itr_setting = I40E_ITR_DYNAMIC;
-       adapter->vsi.tx_itr_setting = I40E_ITR_DYNAMIC;
+       adapter->vsi.rx_itr_setting = (I40E_ITR_DYNAMIC |
+                                      ITR_REG_TO_USEC(I40E_ITR_RX_DEF));
+       adapter->vsi.tx_itr_setting = (I40E_ITR_DYNAMIC |
+                                      ITR_REG_TO_USEC(I40E_ITR_TX_DEF));
        adapter->vsi.netdev = adapter->netdev;
 
        if (!adapter->netdev_registered) {
@@ -2128,7 +2148,7 @@ static void i40evf_init_task(struct work_struct *work)
 
        netif_tx_stop_all_queues(netdev);
 
-       dev_info(&pdev->dev, "MAC address: %pMAC\n", adapter->hw.mac.addr);
+       dev_info(&pdev->dev, "MAC address: %pM\n", adapter->hw.mac.addr);
        if (netdev->features & NETIF_F_GRO)
                dev_info(&pdev->dev, "GRO is enabled\n");
 
@@ -2152,12 +2172,11 @@ static void i40evf_init_task(struct work_struct *work)
 err:
        /* Things went into the weeds, so try again later */
        if (++adapter->aq_wait_count > I40EVF_AQ_MAX_ERR) {
-               dev_err(&pdev->dev, "Failed to communicate with PF; giving up.\n");
+               dev_err(&pdev->dev, "Failed to communicate with PF; giving up\n");
                adapter->flags |= I40EVF_FLAG_PF_COMMS_FAILED;
                return; /* do not reschedule */
        }
        schedule_delayed_work(&adapter->init_task, HZ * 3);
-       return;
 }
 
 /**
index e294f012647d801417af4ca1f68d0629cbaf08cc..2dc0bac7671789fa32cec15d143c217c9b99fef8 100644 (file)
@@ -12,6 +12,9 @@
  * 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, see <http://www.gnu.org/licenses/>.
+ *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
@@ -216,11 +219,9 @@ void i40evf_configure_queues(struct i40evf_adapter *adapter)
        len = sizeof(struct i40e_virtchnl_vsi_queue_config_info) +
                       (sizeof(struct i40e_virtchnl_queue_pair_info) * pairs);
        vqci = kzalloc(len, GFP_ATOMIC);
-       if (!vqci) {
-               dev_err(&adapter->pdev->dev, "%s: unable to allocate memory\n",
-                       __func__);
+       if (!vqci)
                return;
-       }
+
        vqci->vsi_id = adapter->vsi_res->vsi_id;
        vqci->num_queue_pairs = pairs;
        vqpi = vqci->qpair;
@@ -232,6 +233,9 @@ void i40evf_configure_queues(struct i40evf_adapter *adapter)
                vqpi->txq.queue_id = i;
                vqpi->txq.ring_len = adapter->tx_rings[i]->count;
                vqpi->txq.dma_ring_addr = adapter->tx_rings[i]->dma;
+               vqpi->txq.headwb_enabled = 1;
+               vqpi->txq.dma_headwb_addr = vqpi->txq.dma_ring_addr +
+                   (vqpi->txq.ring_len * sizeof(struct i40e_tx_desc));
 
                vqpi->rxq.vsi_id = vqci->vsi_id;
                vqpi->rxq.queue_id = i;
@@ -329,11 +333,8 @@ void i40evf_map_queues(struct i40evf_adapter *adapter)
              (adapter->num_msix_vectors *
                sizeof(struct i40e_virtchnl_vector_map));
        vimi = kzalloc(len, GFP_ATOMIC);
-       if (!vimi) {
-               dev_err(&adapter->pdev->dev, "%s: unable to allocate memory\n",
-                       __func__);
+       if (!vimi)
                return;
-       }
 
        vimi->num_vectors = adapter->num_msix_vectors;
        /* Queue vectors first */
@@ -390,7 +391,7 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter)
        len = sizeof(struct i40e_virtchnl_ether_addr_list) +
              (count * sizeof(struct i40e_virtchnl_ether_addr));
        if (len > I40EVF_MAX_AQ_BUF_SIZE) {
-               dev_warn(&adapter->pdev->dev, "%s: Too many MAC address changes in one request.\n",
+               dev_warn(&adapter->pdev->dev, "%s: Too many MAC address changes in one request\n",
                        __func__);
                count = (I40EVF_MAX_AQ_BUF_SIZE -
                         sizeof(struct i40e_virtchnl_ether_addr_list)) /
@@ -399,16 +400,14 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter)
        }
 
        veal = kzalloc(len, GFP_ATOMIC);
-       if (!veal) {
-               dev_err(&adapter->pdev->dev, "%s: unable to allocate memory\n",
-                       __func__);
+       if (!veal)
                return;
-       }
+
        veal->vsi_id = adapter->vsi_res->vsi_id;
        veal->num_elements = count;
        list_for_each_entry(f, &adapter->mac_filter_list, list) {
                if (f->add) {
-                       memcpy(veal->list[i].addr, f->macaddr, ETH_ALEN);
+                       ether_addr_copy(veal->list[i].addr, f->macaddr);
                        i++;
                        f->add = false;
                }
@@ -454,7 +453,7 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter)
        len = sizeof(struct i40e_virtchnl_ether_addr_list) +
              (count * sizeof(struct i40e_virtchnl_ether_addr));
        if (len > I40EVF_MAX_AQ_BUF_SIZE) {
-               dev_warn(&adapter->pdev->dev, "%s: Too many MAC address changes in one request.\n",
+               dev_warn(&adapter->pdev->dev, "%s: Too many MAC address changes in one request\n",
                        __func__);
                count = (I40EVF_MAX_AQ_BUF_SIZE -
                         sizeof(struct i40e_virtchnl_ether_addr_list)) /
@@ -462,16 +461,14 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter)
                len = I40EVF_MAX_AQ_BUF_SIZE;
        }
        veal = kzalloc(len, GFP_ATOMIC);
-       if (!veal) {
-               dev_err(&adapter->pdev->dev, "%s: unable to allocate memory\n",
-                       __func__);
+       if (!veal)
                return;
-       }
+
        veal->vsi_id = adapter->vsi_res->vsi_id;
        veal->num_elements = count;
        list_for_each_entry_safe(f, ftmp, &adapter->mac_filter_list, list) {
                if (f->remove) {
-                       memcpy(veal->list[i].addr, f->macaddr, ETH_ALEN);
+                       ether_addr_copy(veal->list[i].addr, f->macaddr);
                        i++;
                        list_del(&f->list);
                        kfree(f);
@@ -518,7 +515,7 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter)
        len = sizeof(struct i40e_virtchnl_vlan_filter_list) +
              (count * sizeof(u16));
        if (len > I40EVF_MAX_AQ_BUF_SIZE) {
-               dev_warn(&adapter->pdev->dev, "%s: Too many VLAN changes in one request.\n",
+               dev_warn(&adapter->pdev->dev, "%s: Too many VLAN changes in one request\n",
                        __func__);
                count = (I40EVF_MAX_AQ_BUF_SIZE -
                         sizeof(struct i40e_virtchnl_vlan_filter_list)) /
@@ -526,11 +523,9 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter)
                len = I40EVF_MAX_AQ_BUF_SIZE;
        }
        vvfl = kzalloc(len, GFP_ATOMIC);
-       if (!vvfl) {
-               dev_err(&adapter->pdev->dev, "%s: unable to allocate memory\n",
-                       __func__);
+       if (!vvfl)
                return;
-       }
+
        vvfl->vsi_id = adapter->vsi_res->vsi_id;
        vvfl->num_elements = count;
        list_for_each_entry(f, &adapter->vlan_filter_list, list) {
@@ -580,7 +575,7 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter)
        len = sizeof(struct i40e_virtchnl_vlan_filter_list) +
              (count * sizeof(u16));
        if (len > I40EVF_MAX_AQ_BUF_SIZE) {
-               dev_warn(&adapter->pdev->dev, "%s: Too many VLAN changes in one request.\n",
+               dev_warn(&adapter->pdev->dev, "%s: Too many VLAN changes in one request\n",
                        __func__);
                count = (I40EVF_MAX_AQ_BUF_SIZE -
                         sizeof(struct i40e_virtchnl_vlan_filter_list)) /
@@ -588,11 +583,9 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter)
                len = I40EVF_MAX_AQ_BUF_SIZE;
        }
        vvfl = kzalloc(len, GFP_ATOMIC);
-       if (!vvfl) {
-               dev_err(&adapter->pdev->dev, "%s: unable to allocate memory\n",
-                       __func__);
+       if (!vvfl)
                return;
-       }
+
        vvfl->vsi_id = adapter->vsi_res->vsi_id;
        vvfl->num_elements = count;
        list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list, list) {
@@ -721,7 +714,7 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
                return;
        }
        if (v_opcode != adapter->current_op) {
-               dev_err(&adapter->pdev->dev, "%s: Pending op is %d, received %d.\n",
+               dev_err(&adapter->pdev->dev, "%s: Pending op is %d, received %d\n",
                        __func__, adapter->current_op, v_opcode);
                /* We're probably completely screwed at this point, but clear
                 * the current op and try to carry on....
@@ -730,7 +723,7 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
                return;
        }
        if (v_retval) {
-               dev_err(&adapter->pdev->dev, "%s: PF returned error %d to our request %d!\n",
+               dev_err(&adapter->pdev->dev, "%s: PF returned error %d to our request %d\n",
                        __func__, v_retval, v_opcode);
        }
        switch (v_opcode) {
@@ -745,9 +738,8 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
                                                 stats->tx_broadcast;
                adapter->net_stats.rx_bytes = stats->rx_bytes;
                adapter->net_stats.tx_bytes = stats->tx_bytes;
-               adapter->net_stats.rx_errors = stats->rx_errors;
                adapter->net_stats.tx_errors = stats->tx_errors;
-               adapter->net_stats.rx_dropped = stats->rx_missed;
+               adapter->net_stats.rx_dropped = stats->rx_discards;
                adapter->net_stats.tx_dropped = stats->tx_discards;
                adapter->current_stats = *stats;
                }
@@ -781,7 +773,7 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
                adapter->aq_pending &= ~(I40EVF_FLAG_AQ_MAP_VECTORS);
                break;
        default:
-               dev_warn(&adapter->pdev->dev, "%s: Received unexpected message %d from PF.\n",
+               dev_warn(&adapter->pdev->dev, "%s: Received unexpected message %d from PF\n",
                        __func__, v_opcode);
                break;
        } /* switch v_opcode */
index fa36fe12e77502658cfe864780849d6f00e93c2e..a2db388cc31e6018c892ddb2932e95ba869a09ec 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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.
-
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 /* e1000_82575
  * e1000_82576
@@ -73,9 +70,8 @@ static s32  igb_validate_nvm_checksum_82580(struct e1000_hw *hw);
 static s32  igb_update_nvm_checksum_82580(struct e1000_hw *hw);
 static s32 igb_validate_nvm_checksum_i350(struct e1000_hw *hw);
 static s32 igb_update_nvm_checksum_i350(struct e1000_hw *hw);
-static const u16 e1000_82580_rxpbs_table[] =
-       { 36, 72, 144, 1, 2, 4, 8, 16,
-         35, 70, 140 };
+static const u16 e1000_82580_rxpbs_table[] = {
+       36, 72, 144, 1, 2, 4, 8, 16, 35, 70, 140 };
 
 /**
  *  igb_sgmii_uses_mdio_82575 - Determine if I2C pins are for external MDIO
@@ -159,7 +155,7 @@ static s32 igb_check_for_link_media_swap(struct e1000_hw *hw)
                ret_val = igb_check_for_link_82575(hw);
        }
 
-       return E1000_SUCCESS;
+       return 0;
 }
 
 /**
@@ -526,7 +522,7 @@ static s32 igb_set_sfp_media_type_82575(struct e1000_hw *hw)
 static s32 igb_get_invariants_82575(struct e1000_hw *hw)
 {
        struct e1000_mac_info *mac = &hw->mac;
-       struct e1000_dev_spec_82575 * dev_spec = &hw->dev_spec._82575;
+       struct e1000_dev_spec_82575 *dev_spec = &hw->dev_spec._82575;
        s32 ret_val;
        u32 ctrl_ext = 0;
        u32 link_mode = 0;
@@ -1008,7 +1004,6 @@ static s32 igb_set_d0_lplu_state_82575(struct e1000_hw *hw, bool active)
 static s32 igb_set_d0_lplu_state_82580(struct e1000_hw *hw, bool active)
 {
        struct e1000_phy_info *phy = &hw->phy;
-       s32 ret_val = 0;
        u16 data;
 
        data = rd32(E1000_82580_PHY_POWER_MGMT);
@@ -1032,7 +1027,7 @@ static s32 igb_set_d0_lplu_state_82580(struct e1000_hw *hw, bool active)
                        data &= ~E1000_82580_PM_SPD; }
 
        wr32(E1000_82580_PHY_POWER_MGMT, data);
-       return ret_val;
+       return 0;
 }
 
 /**
@@ -1052,7 +1047,6 @@ static s32 igb_set_d0_lplu_state_82580(struct e1000_hw *hw, bool active)
 static s32 igb_set_d3_lplu_state_82580(struct e1000_hw *hw, bool active)
 {
        struct e1000_phy_info *phy = &hw->phy;
-       s32 ret_val = 0;
        u16 data;
 
        data = rd32(E1000_82580_PHY_POWER_MGMT);
@@ -1077,7 +1071,7 @@ static s32 igb_set_d3_lplu_state_82580(struct e1000_hw *hw, bool active)
        }
 
        wr32(E1000_82580_PHY_POWER_MGMT, data);
-       return ret_val;
+       return 0;
 }
 
 /**
@@ -1180,8 +1174,8 @@ static void igb_release_swfw_sync_82575(struct e1000_hw *hw, u16 mask)
 {
        u32 swfw_sync;
 
-       while (igb_get_hw_semaphore(hw) != 0);
-       /* Empty */
+       while (igb_get_hw_semaphore(hw) != 0)
+               ; /* Empty */
 
        swfw_sync = rd32(E1000_SW_FW_SYNC);
        swfw_sync &= ~mask;
@@ -1203,7 +1197,6 @@ static void igb_release_swfw_sync_82575(struct e1000_hw *hw, u16 mask)
 static s32 igb_get_cfg_done_82575(struct e1000_hw *hw)
 {
        s32 timeout = PHY_CFG_TIMEOUT;
-       s32 ret_val = 0;
        u32 mask = E1000_NVM_CFG_DONE_PORT_0;
 
        if (hw->bus.func == 1)
@@ -1216,7 +1209,7 @@ static s32 igb_get_cfg_done_82575(struct e1000_hw *hw)
        while (timeout) {
                if (rd32(E1000_EEMNGCTL) & mask)
                        break;
-               msleep(1);
+               usleep_range(1000, 2000);
                timeout--;
        }
        if (!timeout)
@@ -1227,7 +1220,7 @@ static s32 igb_get_cfg_done_82575(struct e1000_hw *hw)
            (hw->phy.type == e1000_phy_igp_3))
                igb_phy_init_script_igp3(hw);
 
-       return ret_val;
+       return 0;
 }
 
 /**
@@ -1269,7 +1262,7 @@ static s32 igb_check_for_link_82575(struct e1000_hw *hw)
 
        if (hw->phy.media_type != e1000_media_type_copper) {
                ret_val = igb_get_pcs_speed_and_duplex_82575(hw, &speed,
-                                                            &duplex);
+                                                            &duplex);
                /* Use this flag to determine if link needs to be checked or
                 * not.  If  we have link clear the flag so that we do not
                 * continue to check for link.
@@ -1316,7 +1309,7 @@ void igb_power_up_serdes_link_82575(struct e1000_hw *hw)
 
        /* flush the write to verify completion */
        wrfl();
-       msleep(1);
+       usleep_range(1000, 2000);
 }
 
 /**
@@ -1411,7 +1404,7 @@ void igb_shutdown_serdes_link_82575(struct e1000_hw *hw)
 
                /* flush the write to verify completion */
                wrfl();
-               msleep(1);
+               usleep_range(1000, 2000);
        }
 }
 
@@ -1436,9 +1429,8 @@ static s32 igb_reset_hw_82575(struct e1000_hw *hw)
 
        /* set the completion timeout for interface */
        ret_val = igb_set_pcie_completion_timeout(hw);
-       if (ret_val) {
+       if (ret_val)
                hw_dbg("PCI-E Set completion timeout has failed.\n");
-       }
 
        hw_dbg("Masking off all interrupts\n");
        wr32(E1000_IMC, 0xffffffff);
@@ -1447,7 +1439,7 @@ static s32 igb_reset_hw_82575(struct e1000_hw *hw)
        wr32(E1000_TCTL, E1000_TCTL_PSP);
        wrfl();
 
-       msleep(10);
+       usleep_range(10000, 20000);
 
        ctrl = rd32(E1000_CTRL);
 
@@ -1622,7 +1614,7 @@ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw)
 {
        u32 ctrl_ext, ctrl_reg, reg, anadv_reg;
        bool pcs_autoneg;
-       s32 ret_val = E1000_SUCCESS;
+       s32 ret_val = 0;
        u16 data;
 
        if ((hw->phy.media_type != e1000_media_type_internal_serdes) &&
@@ -1676,7 +1668,7 @@ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw)
                    hw->mac.type == e1000_82576) {
                        ret_val = hw->nvm.ops.read(hw, NVM_COMPAT, 1, &data);
                        if (ret_val) {
-                               printk(KERN_DEBUG "NVM Read Error\n\n");
+                               hw_dbg(KERN_DEBUG "NVM Read Error\n\n");
                                return ret_val;
                        }
 
@@ -1689,7 +1681,7 @@ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw)
                 * link either autoneg or be forced to 1000/Full
                 */
                ctrl_reg |= E1000_CTRL_SPD_1000 | E1000_CTRL_FRCSPD |
-                           E1000_CTRL_FD | E1000_CTRL_FRCDPX;
+                               E1000_CTRL_FD | E1000_CTRL_FRCDPX;
 
                /* set speed of 1000/Full if speed/duplex is forced */
                reg |= E1000_PCS_LCTL_FSV_1000 | E1000_PCS_LCTL_FDV_FULL;
@@ -1925,7 +1917,7 @@ void igb_rx_fifo_flush_82575(struct e1000_hw *hw)
        }
        /* Poll all queues to verify they have shut down */
        for (ms_wait = 0; ms_wait < 10; ms_wait++) {
-               msleep(1);
+               usleep_range(1000, 2000);
                rx_enabled = 0;
                for (i = 0; i < 4; i++)
                        rx_enabled |= rd32(E1000_RXDCTL(i));
@@ -1953,7 +1945,7 @@ void igb_rx_fifo_flush_82575(struct e1000_hw *hw)
        wr32(E1000_RCTL, temp_rctl);
        wr32(E1000_RCTL, temp_rctl | E1000_RCTL_EN);
        wrfl();
-       msleep(2);
+       usleep_range(2000, 3000);
 
        /* Enable RX queues that were previously enabled and restore our
         * previous state
@@ -2005,14 +1997,14 @@ static s32 igb_set_pcie_completion_timeout(struct e1000_hw *hw)
         * 16ms to 55ms
         */
        ret_val = igb_read_pcie_cap_reg(hw, PCIE_DEVICE_CONTROL2,
-                                       &pcie_devctl2);
+                                       &pcie_devctl2);
        if (ret_val)
                goto out;
 
        pcie_devctl2 |= PCIE_DEVICE_CONTROL2_16ms;
 
        ret_val = igb_write_pcie_cap_reg(hw, PCIE_DEVICE_CONTROL2,
-                                        &pcie_devctl2);
+                                        &pcie_devctl2);
 out:
        /* disable completion timeout resend */
        gcr &= ~E1000_GCR_CMPL_TMOUT_RESEND;
@@ -2241,7 +2233,7 @@ static s32 igb_reset_hw_82580(struct e1000_hw *hw)
        wr32(E1000_TCTL, E1000_TCTL_PSP);
        wrfl();
 
-       msleep(10);
+       usleep_range(10000, 11000);
 
        /* Determine whether or not a global dev reset is requested */
        if (global_device_reset &&
@@ -2259,7 +2251,7 @@ static s32 igb_reset_hw_82580(struct e1000_hw *hw)
 
        /* Add delay to insure DEV_RST has time to complete */
        if (global_device_reset)
-               msleep(5);
+               usleep_range(5000, 6000);
 
        ret_val = igb_get_auto_rd_done(hw);
        if (ret_val) {
@@ -2436,8 +2428,7 @@ static s32 igb_update_nvm_checksum_82580(struct e1000_hw *hw)
 
        ret_val = hw->nvm.ops.read(hw, NVM_COMPATIBILITY_REG_3, 1, &nvm_data);
        if (ret_val) {
-               hw_dbg("NVM Read Error while updating checksum"
-                       " compatibility bit.\n");
+               hw_dbg("NVM Read Error while updating checksum compatibility bit.\n");
                goto out;
        }
 
@@ -2447,8 +2438,7 @@ static s32 igb_update_nvm_checksum_82580(struct e1000_hw *hw)
                ret_val = hw->nvm.ops.write(hw, NVM_COMPATIBILITY_REG_3, 1,
                                        &nvm_data);
                if (ret_val) {
-                       hw_dbg("NVM Write Error while updating checksum"
-                               " compatibility bit.\n");
+                       hw_dbg("NVM Write Error while updating checksum compatibility bit.\n");
                        goto out;
                }
        }
@@ -2525,7 +2515,7 @@ static s32 igb_update_nvm_checksum_i350(struct e1000_hw *hw)
 static s32 __igb_access_emi_reg(struct e1000_hw *hw, u16 address,
                                  u16 *data, bool read)
 {
-       s32 ret_val = E1000_SUCCESS;
+       s32 ret_val = 0;
 
        ret_val = hw->phy.ops.write_reg(hw, E1000_EMIADD, address);
        if (ret_val)
@@ -2559,7 +2549,6 @@ s32 igb_read_emi_reg(struct e1000_hw *hw, u16 addr, u16 *data)
  **/
 s32 igb_set_eee_i350(struct e1000_hw *hw)
 {
-       s32 ret_val = 0;
        u32 ipcnfg, eeer;
 
        if ((hw->mac.type < e1000_i350) ||
@@ -2593,7 +2582,7 @@ s32 igb_set_eee_i350(struct e1000_hw *hw)
        rd32(E1000_EEER);
 out:
 
-       return ret_val;
+       return 0;
 }
 
 /**
@@ -2720,7 +2709,6 @@ static const u8 e1000_emc_therm_limit[4] = {
  **/
 static s32 igb_get_thermal_sensor_data_generic(struct e1000_hw *hw)
 {
-       s32 status = E1000_SUCCESS;
        u16 ets_offset;
        u16 ets_cfg;
        u16 ets_sensor;
@@ -2738,7 +2726,7 @@ static s32 igb_get_thermal_sensor_data_generic(struct e1000_hw *hw)
        /* Return the internal sensor only if ETS is unsupported */
        hw->nvm.ops.read(hw, NVM_ETS_CFG, 1, &ets_offset);
        if ((ets_offset == 0x0000) || (ets_offset == 0xFFFF))
-               return status;
+               return 0;
 
        hw->nvm.ops.read(hw, ets_offset, 1, &ets_cfg);
        if (((ets_cfg & NVM_ETS_TYPE_MASK) >> NVM_ETS_TYPE_SHIFT)
@@ -2762,7 +2750,7 @@ static s32 igb_get_thermal_sensor_data_generic(struct e1000_hw *hw)
                                        E1000_I2C_THERMAL_SENSOR_ADDR,
                                        &data->sensor[i].temp);
        }
-       return status;
+       return 0;
 }
 
 /**
@@ -2774,7 +2762,6 @@ static s32 igb_get_thermal_sensor_data_generic(struct e1000_hw *hw)
  **/
 static s32 igb_init_thermal_sensor_thresh_generic(struct e1000_hw *hw)
 {
-       s32 status = E1000_SUCCESS;
        u16 ets_offset;
        u16 ets_cfg;
        u16 ets_sensor;
@@ -2800,7 +2787,7 @@ static s32 igb_init_thermal_sensor_thresh_generic(struct e1000_hw *hw)
        /* Return the internal sensor only if ETS is unsupported */
        hw->nvm.ops.read(hw, NVM_ETS_CFG, 1, &ets_offset);
        if ((ets_offset == 0x0000) || (ets_offset == 0xFFFF))
-               return status;
+               return 0;
 
        hw->nvm.ops.read(hw, ets_offset, 1, &ets_cfg);
        if (((ets_cfg & NVM_ETS_TYPE_MASK) >> NVM_ETS_TYPE_SHIFT)
@@ -2831,7 +2818,7 @@ static s32 igb_init_thermal_sensor_thresh_generic(struct e1000_hw *hw)
                                                        low_thresh_delta;
                }
        }
-       return status;
+       return 0;
 }
 
 #endif
index 09d78be72416563beeda5e3cb72a7338b2aa7060..b407c55738fadf0a333ee7947fedd2c0c31638a1 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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.
-
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #ifndef _E1000_82575_H_
 #define _E1000_82575_H_
@@ -37,9 +34,9 @@ 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) | \
-                                     (ID_LED_DEF1_DEF2 <<  4) | \
-                                     (ID_LED_OFF1_ON2))
+                                    (ID_LED_DEF1_DEF2 <<  8) | \
+                                    (ID_LED_DEF1_DEF2 <<  4) | \
+                                    (ID_LED_OFF1_ON2))
 
 #define E1000_RAR_ENTRIES_82575        16
 #define E1000_RAR_ENTRIES_82576        24
@@ -67,16 +64,16 @@ s32 igb_write_i2c_byte(struct e1000_hw *hw, u8 byte_offset, u8 dev_addr,
 #define E1000_MRQC_RSS_FIELD_IPV6_UDP_EX    0x01000000
 
 #define E1000_EICR_TX_QUEUE ( \
-    E1000_EICR_TX_QUEUE0 |    \
-    E1000_EICR_TX_QUEUE1 |    \
-    E1000_EICR_TX_QUEUE2 |    \
-    E1000_EICR_TX_QUEUE3)
+       E1000_EICR_TX_QUEUE0 |    \
+       E1000_EICR_TX_QUEUE1 |    \
+       E1000_EICR_TX_QUEUE2 |    \
+       E1000_EICR_TX_QUEUE3)
 
 #define E1000_EICR_RX_QUEUE ( \
-    E1000_EICR_RX_QUEUE0 |    \
-    E1000_EICR_RX_QUEUE1 |    \
-    E1000_EICR_RX_QUEUE2 |    \
-    E1000_EICR_RX_QUEUE3)
+       E1000_EICR_RX_QUEUE0 |    \
+       E1000_EICR_RX_QUEUE1 |    \
+       E1000_EICR_RX_QUEUE2 |    \
+       E1000_EICR_RX_QUEUE3)
 
 /* Immediate Interrupt Rx (A.K.A. Low Latency Interrupt) */
 #define E1000_IMIREXT_SIZE_BP     0x00001000  /* Packet size bypass */
@@ -92,8 +89,7 @@ union e1000_adv_rx_desc {
                struct {
                        struct {
                                __le16 pkt_info;   /* RSS type, Packet type */
-                               __le16 hdr_info;   /* Split Header,
-                                                   * header buffer length */
+                               __le16 hdr_info;   /* Split Head, buf len */
                        } lo_dword;
                        union {
                                __le32 rss;          /* RSS Hash */
index b05bf925ac721982d8ded6d3aa647d64236890f4..2a8bb35c2df2bb2824015768af176abe5dfeebae 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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.
-
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #ifndef _E1000_DEFINES_H_
 #define _E1000_DEFINES_H_
 
 /* Same mask, but for extended and packet split descriptors */
 #define E1000_RXDEXT_ERR_FRAME_ERR_MASK ( \
-    E1000_RXDEXT_STATERR_CE  |            \
-    E1000_RXDEXT_STATERR_SE  |            \
-    E1000_RXDEXT_STATERR_SEQ |            \
-    E1000_RXDEXT_STATERR_CXE |            \
-    E1000_RXDEXT_STATERR_RXE)
+       E1000_RXDEXT_STATERR_CE  |            \
+       E1000_RXDEXT_STATERR_SE  |            \
+       E1000_RXDEXT_STATERR_SEQ |            \
+       E1000_RXDEXT_STATERR_CXE |            \
+       E1000_RXDEXT_STATERR_RXE)
 
 #define E1000_MRQC_RSS_FIELD_IPV4_TCP          0x00010000
 #define E1000_MRQC_RSS_FIELD_IPV4              0x00020000
 #define E1000_TCTL_RTLC   0x01000000    /* Re-transmit on late collision */
 
 /* DMA Coalescing register fields */
-#define E1000_DMACR_DMACWT_MASK         0x00003FFF /* DMA Coalescing
-                                                       * Watchdog Timer */
-#define E1000_DMACR_DMACTHR_MASK        0x00FF0000 /* DMA Coalescing Receive
-                                                       * Threshold */
+#define E1000_DMACR_DMACWT_MASK         0x00003FFF /* DMA Coal Watchdog Timer */
+#define E1000_DMACR_DMACTHR_MASK        0x00FF0000 /* DMA Coal Rx Threshold */
 #define E1000_DMACR_DMACTHR_SHIFT       16
-#define E1000_DMACR_DMAC_LX_MASK        0x30000000 /* Lx when no PCIe
-                                                       * transactions */
+#define E1000_DMACR_DMAC_LX_MASK        0x30000000 /* Lx when no PCIe trans */
 #define E1000_DMACR_DMAC_LX_SHIFT       28
 #define E1000_DMACR_DMAC_EN             0x80000000 /* Enable DMA Coalescing */
 /* DMA Coalescing BMC-to-OS Watchdog Enable */
 #define E1000_DMACR_DC_BMC2OSW_EN      0x00008000
 
-#define E1000_DMCTXTH_DMCTTHR_MASK      0x00000FFF /* DMA Coalescing Transmit
-                                                       * Threshold */
+#define E1000_DMCTXTH_DMCTTHR_MASK      0x00000FFF /* DMA Coal Tx Threshold */
 
 #define E1000_DMCTLX_TTLX_MASK          0x00000FFF /* Time to LX request */
 
-#define E1000_DMCRTRH_UTRESH_MASK       0x0007FFFF /* Receive Traffic Rate
-                                                       * Threshold */
-#define E1000_DMCRTRH_LRPRCW            0x80000000 /* Rcv packet rate in
-                                                       * current window */
+#define E1000_DMCRTRH_UTRESH_MASK       0x0007FFFF /* Rx Traffic Rate Thresh */
+#define E1000_DMCRTRH_LRPRCW            0x80000000 /* Rx pkt rate curr window */
 
-#define E1000_DMCCNT_CCOUNT_MASK        0x01FFFFFF /* DMA Coal Rcv Traffic
-                                                       * Current Cnt */
+#define E1000_DMCCNT_CCOUNT_MASK        0x01FFFFFF /* DMA Coal Rx Current Cnt */
 
-#define E1000_FCRTC_RTH_COAL_MASK       0x0003FFF0 /* Flow ctrl Rcv Threshold
-                                                       * High val */
+#define E1000_FCRTC_RTH_COAL_MASK       0x0003FFF0 /* FC Rx Thresh High val */
 #define E1000_FCRTC_RTH_COAL_SHIFT      4
 #define E1000_PCIEMISC_LX_DECISION      0x00000080 /* Lx power decision */
 
 /* Timestamp in Rx buffer */
 #define E1000_RXPBS_CFG_TS_EN           0x80000000
 
+#define I210_RXPBSIZE_DEFAULT          0x000000A2 /* RXPBSIZE default */
+#define I210_TXPBSIZE_DEFAULT          0x04000014 /* TXPBSIZE default */
+
 /* SerDes Control */
 #define E1000_SCTL_DISABLE_SERDES_LOOPBACK 0x0400
 
  *   o LSC    = Link Status Change
  */
 #define IMS_ENABLE_MASK ( \
-    E1000_IMS_RXT0   |    \
-    E1000_IMS_TXDW   |    \
-    E1000_IMS_RXDMT0 |    \
-    E1000_IMS_RXSEQ  |    \
-    E1000_IMS_LSC    |    \
-    E1000_IMS_DOUTSYNC)
+       E1000_IMS_RXT0   |    \
+       E1000_IMS_TXDW   |    \
+       E1000_IMS_RXDMT0 |    \
+       E1000_IMS_RXSEQ  |    \
+       E1000_IMS_LSC    |    \
+       E1000_IMS_DOUTSYNC)
 
 /* Interrupt Mask Set */
 #define E1000_IMS_TXDW      E1000_ICR_TXDW      /* Transmit desc written back */
 #define E1000_RAH_POOL_1 0x00040000
 
 /* Error Codes */
-#define E1000_SUCCESS      0
 #define E1000_ERR_NVM      1
 #define E1000_ERR_PHY      2
 #define E1000_ERR_CONFIG   3
 #define E1000_VFTA_ENTRY_BIT_SHIFT_MASK      0x1F
 
 /* DMA Coalescing register fields */
-#define E1000_PCIEMISC_LX_DECISION      0x00000080 /* Lx power decision based
-                                                      on DMA coal */
+#define E1000_PCIEMISC_LX_DECISION      0x00000080 /* Lx power on DMA coal */
 
 /* Tx Rate-Scheduler Config fields */
 #define E1000_RTTBCNRC_RS_ENA          0x80000000
index 10741d170f2ddad46b2fad14d362998bdc19d639..89925e4058498ea1c1ffda3195576d8abcda611e 100644 (file)
@@ -1,28 +1,24 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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.
-
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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,
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #ifndef _E1000_HW_H_
 #define _E1000_HW_H_
@@ -320,15 +316,15 @@ struct e1000_host_mng_command_info {
 #include "e1000_mbx.h"
 
 struct e1000_mac_operations {
-       s32  (*check_for_link)(struct e1000_hw *);
-       s32  (*reset_hw)(struct e1000_hw *);
-       s32  (*init_hw)(struct e1000_hw *);
+       s32 (*check_for_link)(struct e1000_hw *);
+       s32 (*reset_hw)(struct e1000_hw *);
+       s32 (*init_hw)(struct e1000_hw *);
        bool (*check_mng_mode)(struct e1000_hw *);
-       s32  (*setup_physical_interface)(struct e1000_hw *);
+       s32 (*setup_physical_interface)(struct e1000_hw *);
        void (*rar_set)(struct e1000_hw *, u8 *, u32);
-       s32  (*read_mac_addr)(struct e1000_hw *);
-       s32  (*get_speed_and_duplex)(struct e1000_hw *, u16 *, u16 *);
-       s32  (*acquire_swfw_sync)(struct e1000_hw *, u16);
+       s32 (*read_mac_addr)(struct e1000_hw *);
+       s32 (*get_speed_and_duplex)(struct e1000_hw *, u16 *, u16 *);
+       s32 (*acquire_swfw_sync)(struct e1000_hw *, u16);
        void (*release_swfw_sync)(struct e1000_hw *, u16);
 #ifdef CONFIG_IGB_HWMON
        s32 (*get_thermal_sensor_data)(struct e1000_hw *);
@@ -338,31 +334,31 @@ struct e1000_mac_operations {
 };
 
 struct e1000_phy_operations {
-       s32  (*acquire)(struct e1000_hw *);
-       s32  (*check_polarity)(struct e1000_hw *);
-       s32  (*check_reset_block)(struct e1000_hw *);
-       s32  (*force_speed_duplex)(struct e1000_hw *);
-       s32  (*get_cfg_done)(struct e1000_hw *hw);
-       s32  (*get_cable_length)(struct e1000_hw *);
-       s32  (*get_phy_info)(struct e1000_hw *);
-       s32  (*read_reg)(struct e1000_hw *, u32, u16 *);
+       s32 (*acquire)(struct e1000_hw *);
+       s32 (*check_polarity)(struct e1000_hw *);
+       s32 (*check_reset_block)(struct e1000_hw *);
+       s32 (*force_speed_duplex)(struct e1000_hw *);
+       s32 (*get_cfg_done)(struct e1000_hw *hw);
+       s32 (*get_cable_length)(struct e1000_hw *);
+       s32 (*get_phy_info)(struct e1000_hw *);
+       s32 (*read_reg)(struct e1000_hw *, u32, u16 *);
        void (*release)(struct e1000_hw *);
-       s32  (*reset)(struct e1000_hw *);
-       s32  (*set_d0_lplu_state)(struct e1000_hw *, bool);
-       s32  (*set_d3_lplu_state)(struct e1000_hw *, bool);
-       s32  (*write_reg)(struct e1000_hw *, u32, u16);
+       s32 (*reset)(struct e1000_hw *);
+       s32 (*set_d0_lplu_state)(struct e1000_hw *, bool);
+       s32 (*set_d3_lplu_state)(struct e1000_hw *, bool);
+       s32 (*write_reg)(struct e1000_hw *, u32, u16);
        s32 (*read_i2c_byte)(struct e1000_hw *, u8, u8, u8 *);
        s32 (*write_i2c_byte)(struct e1000_hw *, u8, u8, u8);
 };
 
 struct e1000_nvm_operations {
-       s32  (*acquire)(struct e1000_hw *);
-       s32  (*read)(struct e1000_hw *, u16, u16, u16 *);
+       s32 (*acquire)(struct e1000_hw *);
+       s32 (*read)(struct e1000_hw *, u16, u16, u16 *);
        void (*release)(struct e1000_hw *);
-       s32  (*write)(struct e1000_hw *, u16, u16, u16 *);
-       s32  (*update)(struct e1000_hw *);
-       s32  (*validate)(struct e1000_hw *);
-       s32  (*valid_led_default)(struct e1000_hw *, u16 *);
+       s32 (*write)(struct e1000_hw *, u16, u16, u16 *);
+       s32 (*update)(struct e1000_hw *);
+       s32 (*validate)(struct e1000_hw *);
+       s32 (*valid_led_default)(struct e1000_hw *, u16 *);
 };
 
 #define E1000_MAX_SENSORS              3
index f67f8a170b90c81bcf4738154f1f7cead7171992..337161f440dd67aa473f616621933332955a842c 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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.
-
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 /* e1000_i210
  * e1000_i211
@@ -100,7 +97,7 @@ static s32 igb_get_hw_semaphore_i210(struct e1000_hw *hw)
                return -E1000_ERR_NVM;
        }
 
-       return E1000_SUCCESS;
+       return 0;
 }
 
 /**
@@ -142,7 +139,7 @@ s32 igb_acquire_swfw_sync_i210(struct e1000_hw *hw, u16 mask)
        u32 swfw_sync;
        u32 swmask = mask;
        u32 fwmask = mask << 16;
-       s32 ret_val = E1000_SUCCESS;
+       s32 ret_val = 0;
        s32 i = 0, timeout = 200; /* FIXME: find real value to use here */
 
        while (i < timeout) {
@@ -187,7 +184,7 @@ void igb_release_swfw_sync_i210(struct e1000_hw *hw, u16 mask)
 {
        u32 swfw_sync;
 
-       while (igb_get_hw_semaphore_i210(hw) != E1000_SUCCESS)
+       while (igb_get_hw_semaphore_i210(hw))
                ; /* Empty */
 
        swfw_sync = rd32(E1000_SW_FW_SYNC);
@@ -210,7 +207,7 @@ void igb_release_swfw_sync_i210(struct e1000_hw *hw, u16 mask)
 static s32 igb_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, u16 words,
                                  u16 *data)
 {
-       s32 status = E1000_SUCCESS;
+       s32 status = 0;
        u16 i, count;
 
        /* We cannot hold synchronization semaphores for too long,
@@ -220,7 +217,7 @@ static s32 igb_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, u16 words,
        for (i = 0; i < words; i += E1000_EERD_EEWR_MAX_COUNT) {
                count = (words - i) / E1000_EERD_EEWR_MAX_COUNT > 0 ?
                        E1000_EERD_EEWR_MAX_COUNT : (words - i);
-               if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) {
+               if (!(hw->nvm.ops.acquire(hw))) {
                        status = igb_read_nvm_eerd(hw, offset, count,
                                                     data + i);
                        hw->nvm.ops.release(hw);
@@ -228,7 +225,7 @@ static s32 igb_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, u16 words,
                        status = E1000_ERR_SWFW_SYNC;
                }
 
-               if (status != E1000_SUCCESS)
+               if (status)
                        break;
        }
 
@@ -253,7 +250,7 @@ static s32 igb_write_nvm_srwr(struct e1000_hw *hw, u16 offset, u16 words,
        struct e1000_nvm_info *nvm = &hw->nvm;
        u32 i, k, eewr = 0;
        u32 attempts = 100000;
-       s32 ret_val = E1000_SUCCESS;
+       s32 ret_val = 0;
 
        /* A check for invalid values:  offset too large, too many words,
         * too many words for the offset, and not enough words.
@@ -275,13 +272,13 @@ static s32 igb_write_nvm_srwr(struct e1000_hw *hw, u16 offset, u16 words,
                for (k = 0; k < attempts; k++) {
                        if (E1000_NVM_RW_REG_DONE &
                            rd32(E1000_SRWR)) {
-                               ret_val = E1000_SUCCESS;
+                               ret_val = 0;
                                break;
                        }
                        udelay(5);
        }
 
-               if (ret_val != E1000_SUCCESS) {
+               if (ret_val) {
                        hw_dbg("Shadow RAM write EEWR timed out\n");
                        break;
                }
@@ -310,7 +307,7 @@ static s32 igb_write_nvm_srwr(struct e1000_hw *hw, u16 offset, u16 words,
 static s32 igb_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, u16 words,
                                   u16 *data)
 {
-       s32 status = E1000_SUCCESS;
+       s32 status = 0;
        u16 i, count;
 
        /* We cannot hold synchronization semaphores for too long,
@@ -320,7 +317,7 @@ static s32 igb_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, u16 words,
        for (i = 0; i < words; i += E1000_EERD_EEWR_MAX_COUNT) {
                count = (words - i) / E1000_EERD_EEWR_MAX_COUNT > 0 ?
                        E1000_EERD_EEWR_MAX_COUNT : (words - i);
-               if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) {
+               if (!(hw->nvm.ops.acquire(hw))) {
                        status = igb_write_nvm_srwr(hw, offset, count,
                                                      data + i);
                        hw->nvm.ops.release(hw);
@@ -328,7 +325,7 @@ static s32 igb_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, u16 words,
                        status = E1000_ERR_SWFW_SYNC;
                }
 
-               if (status != E1000_SUCCESS)
+               if (status)
                        break;
        }
 
@@ -367,12 +364,12 @@ static s32 igb_read_invm_word_i210(struct e1000_hw *hw, u8 address, u16 *data)
                                *data = INVM_DWORD_TO_WORD_DATA(invm_dword);
                                hw_dbg("Read INVM Word 0x%02x = %x\n",
                                          address, *data);
-                               status = E1000_SUCCESS;
+                               status = 0;
                                break;
                        }
                }
        }
-       if (status != E1000_SUCCESS)
+       if (status)
                hw_dbg("Requested word 0x%02x not found in OTP\n", address);
        return status;
 }
@@ -388,7 +385,7 @@ static s32 igb_read_invm_word_i210(struct e1000_hw *hw, u8 address, u16 *data)
 static s32 igb_read_invm_i210(struct e1000_hw *hw, u16 offset,
                                u16 words __always_unused, u16 *data)
 {
-       s32 ret_val = E1000_SUCCESS;
+       s32 ret_val = 0;
 
        /* Only the MAC addr is required to be present in the iNVM */
        switch (offset) {
@@ -398,43 +395,44 @@ static s32 igb_read_invm_i210(struct e1000_hw *hw, u16 offset,
                                                     &data[1]);
                ret_val |= igb_read_invm_word_i210(hw, (u8)offset+2,
                                                     &data[2]);
-               if (ret_val != E1000_SUCCESS)
+               if (ret_val)
                        hw_dbg("MAC Addr not found in iNVM\n");
                break;
        case NVM_INIT_CTRL_2:
                ret_val = igb_read_invm_word_i210(hw, (u8)offset, data);
-               if (ret_val != E1000_SUCCESS) {
+               if (ret_val) {
                        *data = NVM_INIT_CTRL_2_DEFAULT_I211;
-                       ret_val = E1000_SUCCESS;
+                       ret_val = 0;
                }
                break;
        case NVM_INIT_CTRL_4:
                ret_val = igb_read_invm_word_i210(hw, (u8)offset, data);
-               if (ret_val != E1000_SUCCESS) {
+               if (ret_val) {
                        *data = NVM_INIT_CTRL_4_DEFAULT_I211;
-                       ret_val = E1000_SUCCESS;
+                       ret_val = 0;
                }
                break;
        case NVM_LED_1_CFG:
                ret_val = igb_read_invm_word_i210(hw, (u8)offset, data);
-               if (ret_val != E1000_SUCCESS) {
+               if (ret_val) {
                        *data = NVM_LED_1_CFG_DEFAULT_I211;
-                       ret_val = E1000_SUCCESS;
+                       ret_val = 0;
                }
                break;
        case NVM_LED_0_2_CFG:
                ret_val = igb_read_invm_word_i210(hw, (u8)offset, data);
-               if (ret_val != E1000_SUCCESS) {
+               if (ret_val) {
                        *data = NVM_LED_0_2_CFG_DEFAULT_I211;
-                       ret_val = E1000_SUCCESS;
+                       ret_val = 0;
                }
                break;
        case NVM_ID_LED_SETTINGS:
                ret_val = igb_read_invm_word_i210(hw, (u8)offset, data);
-               if (ret_val != E1000_SUCCESS) {
+               if (ret_val) {
                        *data = ID_LED_RESERVED_FFFF;
-                       ret_val = E1000_SUCCESS;
+                       ret_val = 0;
                }
+               break;
        case NVM_SUB_DEV_ID:
                *data = hw->subsystem_device_id;
                break;
@@ -488,14 +486,14 @@ s32 igb_read_invm_version(struct e1000_hw *hw,
                /* Check if we have first version location used */
                if ((i == 1) && ((*record & E1000_INVM_VER_FIELD_ONE) == 0)) {
                        version = 0;
-                       status = E1000_SUCCESS;
+                       status = 0;
                        break;
                }
                /* Check if we have second version location used */
                else if ((i == 1) &&
                         ((*record & E1000_INVM_VER_FIELD_TWO) == 0)) {
                        version = (*record & E1000_INVM_VER_FIELD_ONE) >> 3;
-                       status = E1000_SUCCESS;
+                       status = 0;
                        break;
                }
                /* Check if we have odd version location
@@ -506,7 +504,7 @@ s32 igb_read_invm_version(struct e1000_hw *hw,
                         (i != 1))) {
                        version = (*next_record & E1000_INVM_VER_FIELD_TWO)
                                  >> 13;
-                       status = E1000_SUCCESS;
+                       status = 0;
                        break;
                }
                /* Check if we have even version location
@@ -515,12 +513,12 @@ s32 igb_read_invm_version(struct e1000_hw *hw,
                else if (((*record & E1000_INVM_VER_FIELD_TWO) == 0) &&
                         ((*record & 0x3) == 0)) {
                        version = (*record & E1000_INVM_VER_FIELD_ONE) >> 3;
-                       status = E1000_SUCCESS;
+                       status = 0;
                        break;
                }
        }
 
-       if (status == E1000_SUCCESS) {
+       if (!status) {
                invm_ver->invm_major = (version & E1000_INVM_MAJOR_MASK)
                                        >> E1000_INVM_MAJOR_SHIFT;
                invm_ver->invm_minor = version & E1000_INVM_MINOR_MASK;
@@ -533,7 +531,7 @@ s32 igb_read_invm_version(struct e1000_hw *hw,
                /* Check if we have image type in first location used */
                if ((i == 1) && ((*record & E1000_INVM_IMGTYPE_FIELD) == 0)) {
                        invm_ver->invm_img_type = 0;
-                       status = E1000_SUCCESS;
+                       status = 0;
                        break;
                }
                /* Check if we have image type in first location used */
@@ -542,7 +540,7 @@ s32 igb_read_invm_version(struct e1000_hw *hw,
                         ((((*record & 0x3) != 0) && (i != 1)))) {
                        invm_ver->invm_img_type =
                                (*next_record & E1000_INVM_IMGTYPE_FIELD) >> 23;
-                       status = E1000_SUCCESS;
+                       status = 0;
                        break;
                }
        }
@@ -558,10 +556,10 @@ s32 igb_read_invm_version(struct e1000_hw *hw,
  **/
 static s32 igb_validate_nvm_checksum_i210(struct e1000_hw *hw)
 {
-       s32 status = E1000_SUCCESS;
+       s32 status = 0;
        s32 (*read_op_ptr)(struct e1000_hw *, u16, u16, u16 *);
 
-       if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) {
+       if (!(hw->nvm.ops.acquire(hw))) {
 
                /* Replace the read function with semaphore grabbing with
                 * the one that skips this for a while.
@@ -593,7 +591,7 @@ static s32 igb_validate_nvm_checksum_i210(struct e1000_hw *hw)
  **/
 static s32 igb_update_nvm_checksum_i210(struct e1000_hw *hw)
 {
-       s32 ret_val = E1000_SUCCESS;
+       s32 ret_val = 0;
        u16 checksum = 0;
        u16 i, nvm_data;
 
@@ -602,12 +600,12 @@ static s32 igb_update_nvm_checksum_i210(struct e1000_hw *hw)
         * EEPROM read fails
         */
        ret_val = igb_read_nvm_eerd(hw, 0, 1, &nvm_data);
-       if (ret_val != E1000_SUCCESS) {
+       if (ret_val) {
                hw_dbg("EEPROM read failed\n");
                goto out;
        }
 
-       if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) {
+       if (!(hw->nvm.ops.acquire(hw))) {
                /* Do not use hw->nvm.ops.write, hw->nvm.ops.read
                 * because we do not want to take the synchronization
                 * semaphores twice here.
@@ -625,7 +623,7 @@ static s32 igb_update_nvm_checksum_i210(struct e1000_hw *hw)
                checksum = (u16) NVM_SUM - checksum;
                ret_val = igb_write_nvm_srwr(hw, NVM_CHECKSUM_REG, 1,
                                                &checksum);
-               if (ret_val != E1000_SUCCESS) {
+               if (ret_val) {
                        hw->nvm.ops.release(hw);
                        hw_dbg("NVM Write Error while updating checksum.\n");
                        goto out;
@@ -654,7 +652,7 @@ static s32 igb_pool_flash_update_done_i210(struct e1000_hw *hw)
        for (i = 0; i < E1000_FLUDONE_ATTEMPTS; i++) {
                reg = rd32(E1000_EECD);
                if (reg & E1000_EECD_FLUDONE_I210) {
-                       ret_val = E1000_SUCCESS;
+                       ret_val = 0;
                        break;
                }
                udelay(5);
@@ -687,7 +685,7 @@ bool igb_get_flash_presence_i210(struct e1000_hw *hw)
  **/
 static s32 igb_update_flash_i210(struct e1000_hw *hw)
 {
-       s32 ret_val = E1000_SUCCESS;
+       s32 ret_val = 0;
        u32 flup;
 
        ret_val = igb_pool_flash_update_done_i210(hw);
@@ -700,7 +698,7 @@ static s32 igb_update_flash_i210(struct e1000_hw *hw)
        wr32(E1000_EECD, flup);
 
        ret_val = igb_pool_flash_update_done_i210(hw);
-       if (ret_val == E1000_SUCCESS)
+       if (ret_val)
                hw_dbg("Flash update complete\n");
        else
                hw_dbg("Flash update time out\n");
@@ -753,7 +751,7 @@ s32 igb_valid_led_default_i210(struct e1000_hw *hw, u16 *data)
 static s32 __igb_access_xmdio_reg(struct e1000_hw *hw, u16 address,
                                  u8 dev_addr, u16 *data, bool read)
 {
-       s32 ret_val = E1000_SUCCESS;
+       s32 ret_val = 0;
 
        ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAC, dev_addr);
        if (ret_val)
index 907fe99a9813130e45a3dddf0d5d48c6dfdc492d..9f34976687baedc7eb4d4844678cb2592c10e9d1 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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.
-
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #ifndef _E1000_I210_H_
 #define _E1000_I210_H_
index 1e0c404db81a263861c2b0e683ec5b702b305df8..2a88595f956cf4e3089d20a986f0adb9db48681e 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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.
-
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #include <linux/if_ether.h>
 #include <linux/delay.h>
@@ -442,7 +439,7 @@ static u32 igb_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr)
  *  The caller must have a packed mc_addr_list of multicast addresses.
  **/
 void igb_update_mc_addr_list(struct e1000_hw *hw,
-                             u8 *mc_addr_list, u32 mc_addr_count)
+                            u8 *mc_addr_list, u32 mc_addr_count)
 {
        u32 hash_value, hash_bit, hash_reg;
        int i;
@@ -866,8 +863,7 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw)
                        goto out;
 
                if (!(mii_status_reg & MII_SR_AUTONEG_COMPLETE)) {
-                       hw_dbg("Copper PHY and Auto Neg "
-                                "has not completed.\n");
+                       hw_dbg("Copper PHY and Auto Neg has not completed.\n");
                        goto out;
                }
 
@@ -1265,7 +1261,7 @@ s32 igb_get_auto_rd_done(struct e1000_hw *hw)
        while (i < AUTO_READ_DONE_TIMEOUT) {
                if (rd32(E1000_EECD) & E1000_EECD_AUTO_RD)
                        break;
-               msleep(1);
+               usleep_range(1000, 2000);
                i++;
        }
 
@@ -1298,7 +1294,7 @@ static s32 igb_valid_led_default(struct e1000_hw *hw, u16 *data)
        }
 
        if (*data == ID_LED_RESERVED_0000 || *data == ID_LED_RESERVED_FFFF) {
-               switch(hw->phy.media_type) {
+               switch (hw->phy.media_type) {
                case e1000_media_type_internal_serdes:
                        *data = ID_LED_DEFAULT_82575_SERDES;
                        break;
index 99299ba8ee3a2def53ab2e6fcc8c5c039aca0265..ea24961b0d705e557a6b9bd57772d984ab0927ae 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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.
-
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #ifndef _E1000_MAC_H_
 #define _E1000_MAC_H_
index d5b121771c313716543b16f5a8464866afd4f8e2..162cc49345d09babbd7fab30ec0917215e1a9b64 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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.
-
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #include "e1000_mbx.h"
 
index f52f5515e5a8a8aedcc1568f90fdf1b986b2947e..d20af6b2f581698098a972d557a0a93fe19d1d48 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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.
-
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #ifndef _E1000_MBX_H_
 #define _E1000_MBX_H_
index 9abf82919c65535d7b3fd7f7d7200f80fffd1f0a..e8280d0d7f022942c81d399945259cf4b0a5d1f9 100644 (file)
@@ -1,28 +1,24 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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.
-
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #include <linux/if_ether.h>
 #include <linux/delay.h>
@@ -480,6 +476,7 @@ s32 igb_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
                /* Loop to allow for up to whole page write of eeprom */
                while (widx < words) {
                        u16 word_out = data[widx];
+
                        word_out = (word_out >> 8) | (word_out << 8);
                        igb_shift_out_eec_bits(hw, word_out, 16);
                        widx++;
@@ -801,5 +798,4 @@ void igb_get_fw_version(struct e1000_hw *hw, struct e1000_fw_version *fw_vers)
                fw_vers->etrack_id = (eeprom_verh << NVM_ETRACK_SHIFT)
                        | eeprom_verl;
        }
-       return;
 }
index 5b101170b17e4bbc9af310c8aacd5e0b891344a0..febc9cdb739125174e143b0159feb0b529cb5ac6 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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.
-
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #ifndef _E1000_NVM_H_
 #define _E1000_NVM_H_
@@ -32,7 +29,7 @@ void igb_release_nvm(struct e1000_hw *hw);
 s32  igb_read_mac_addr(struct e1000_hw *hw);
 s32  igb_read_part_num(struct e1000_hw *hw, u32 *part_num);
 s32  igb_read_part_string(struct e1000_hw *hw, u8 *part_num,
-                          u32 part_num_size);
+                         u32 part_num_size);
 s32  igb_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, u16 *data);
 s32  igb_read_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data);
 s32  igb_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data);
index 4009bbab7407d21945e7c1af20df5deedba564a3..c1bb64d8366fa5e7741905ea9fc22d4b241054ce 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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.
-
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #include <linux/if_ether.h>
 #include <linux/delay.h>
@@ -924,8 +921,7 @@ static s32 igb_copper_link_autoneg(struct e1000_hw *hw)
        if (phy->autoneg_wait_to_complete) {
                ret_val = igb_wait_autoneg(hw);
                if (ret_val) {
-                       hw_dbg("Error while waiting for "
-                              "autoneg to complete\n");
+                       hw_dbg("Error while waiting for autoneg to complete\n");
                        goto out;
                }
        }
@@ -2208,16 +2204,10 @@ s32 igb_phy_init_script_igp3(struct e1000_hw *hw)
 void igb_power_up_phy_copper(struct e1000_hw *hw)
 {
        u16 mii_reg = 0;
-       u16 power_reg = 0;
 
        /* The PHY will retain its settings across a power down/up cycle */
        hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg);
        mii_reg &= ~MII_CR_POWER_DOWN;
-       if (hw->phy.type == e1000_phy_i210) {
-               hw->phy.ops.read_reg(hw, GS40G_COPPER_SPEC, &power_reg);
-               power_reg &= ~GS40G_CS_POWER_DOWN;
-               hw->phy.ops.write_reg(hw, GS40G_COPPER_SPEC, power_reg);
-       }
        hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg);
 }
 
@@ -2231,20 +2221,12 @@ void igb_power_up_phy_copper(struct e1000_hw *hw)
 void igb_power_down_phy_copper(struct e1000_hw *hw)
 {
        u16 mii_reg = 0;
-       u16 power_reg = 0;
 
        /* The PHY will retain its settings across a power down/up cycle */
        hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg);
        mii_reg |= MII_CR_POWER_DOWN;
-
-       /* i210 Phy requires an additional bit for power up/down */
-       if (hw->phy.type == e1000_phy_i210) {
-               hw->phy.ops.read_reg(hw, GS40G_COPPER_SPEC, &power_reg);
-               power_reg |= GS40G_CS_POWER_DOWN;
-               hw->phy.ops.write_reg(hw, GS40G_COPPER_SPEC, power_reg);
-       }
        hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg);
-       msleep(1);
+       usleep_range(1000, 2000);
 }
 
 /**
index 4c2c36c46a7398d217c1418b3966b4cde5813812..7af4ffab0285653c4c400992edcd163782f919ac 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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.
-
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #ifndef _E1000_PHY_H_
 #define _E1000_PHY_H_
@@ -154,7 +151,6 @@ s32  igb_check_polarity_m88(struct e1000_hw *hw);
 #define GS40G_MAC_LB                   0x4140
 #define GS40G_MAC_SPEED_1G             0X0006
 #define GS40G_COPPER_SPEC              0x0010
-#define GS40G_CS_POWER_DOWN            0x0002
 #define GS40G_LINE_LB                  0x4000
 
 /* SFP modules ID memory locations */
index bdb246e848e13bb5e569f279336dbb5a2c5bfe86..1cc4b1a7e597d32823ff2cc221aa54ce370a5a15 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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.
-
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #ifndef _E1000_REGS_H_
 #define _E1000_REGS_H_
                                    : (0x0E038 + ((_n) * 0x40)))
 #define E1000_TDWBAH(_n)  ((_n) < 4 ? (0x0383C + ((_n) * 0x100)) \
                                    : (0x0E03C + ((_n) * 0x40)))
+
+#define E1000_RXPBS    0x02404  /* Rx Packet Buffer Size - RW */
+#define E1000_TXPBS    0x03404  /* Tx Packet Buffer Size - RW */
+
 #define E1000_TDFH     0x03410  /* TX Data FIFO Head - RW */
 #define E1000_TDFT     0x03418  /* TX Data FIFO Tail - RW */
 #define E1000_TDFHS    0x03420  /* TX Data FIFO Head Saved - RW */
 #define E1000_RA2      0x054E0  /* 2nd half of Rx address array - RW Array */
 #define E1000_PSRTYPE(_i)       (0x05480 + ((_i) * 4))
 #define E1000_RAL(_i)  (((_i) <= 15) ? (0x05400 + ((_i) * 8)) : \
-                                       (0x054E0 + ((_i - 16) * 8)))
+                                       (0x054E0 + ((_i - 16) * 8)))
 #define E1000_RAH(_i)  (((_i) <= 15) ? (0x05404 + ((_i) * 8)) : \
-                                       (0x054E4 + ((_i - 16) * 8)))
+                                       (0x054E4 + ((_i - 16) * 8)))
 #define E1000_IP4AT_REG(_i)     (0x05840 + ((_i) * 8))
 #define E1000_IP6AT_REG(_i)     (0x05880 + ((_i) * 4))
 #define E1000_WUPM_REG(_i)      (0x05A00 + ((_i) * 4))
 #define E1000_VMBMEM(_n)       (0x00800 + (64 * (_n)))
 #define E1000_VMOLR(_n)        (0x05AD0 + (4 * (_n)))
 #define E1000_DVMOLR(_n)       (0x0C038 + (64 * (_n)))
-#define E1000_VLVF(_n)         (0x05D00 + (4 * (_n))) /* VLAN Virtual Machine
-                                                       * Filter - RW */
+#define E1000_VLVF(_n)         (0x05D00 + (4 * (_n))) /* VLAN VM Filter */
 #define E1000_VMVIR(_n)        (0x03700 + (4 * (_n)))
 
 struct e1000_hw;
index 27130065d92a70679292ef316aaadf848772944c..06102d1f7c0362208118ec99dcaba0e6db89eeb3 100644 (file)
@@ -1,29 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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.
-
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
-
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 /* Linux PRO/1000 Ethernet Driver main header file */
 
@@ -198,6 +194,7 @@ struct igb_tx_buffer {
        unsigned int bytecount;
        u16 gso_segs;
        __be16 protocol;
+
        DEFINE_DMA_UNMAP_ADDR(dma);
        DEFINE_DMA_UNMAP_LEN(len);
        u32 tx_flags;
index e5570acbeea84509855a98383ab128876e7447c9..c737d1f4083829ae0eed6e7ecff1d29b5c75239e 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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.
-
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 /* ethtool support for igb */
 
@@ -144,6 +141,7 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
        struct e1000_dev_spec_82575 *dev_spec = &hw->dev_spec._82575;
        struct e1000_sfp_flags *eth_flags = &dev_spec->eth_flags;
        u32 status;
+       u32 speed;
 
        status = rd32(E1000_STATUS);
        if (hw->phy.media_type == e1000_media_type_copper) {
@@ -218,13 +216,13 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
        if (status & E1000_STATUS_LU) {
                if ((status & E1000_STATUS_2P5_SKU) &&
                    !(status & E1000_STATUS_2P5_SKU_OVER)) {
-                       ecmd->speed = SPEED_2500;
+                       speed = SPEED_2500;
                } else if (status & E1000_STATUS_SPEED_1000) {
-                       ecmd->speed = SPEED_1000;
+                       speed = SPEED_1000;
                } else if (status & E1000_STATUS_SPEED_100) {
-                       ecmd->speed = SPEED_100;
+                       speed = SPEED_100;
                } else {
-                       ecmd->speed = SPEED_10;
+                       speed = SPEED_10;
                }
                if ((status & E1000_STATUS_FD) ||
                    hw->phy.media_type != e1000_media_type_copper)
@@ -232,9 +230,10 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
                else
                        ecmd->duplex = DUPLEX_HALF;
        } else {
-               ecmd->speed = -1;
-               ecmd->duplex = -1;
+               speed = SPEED_UNKNOWN;
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
+       ethtool_cmd_speed_set(ecmd, speed);
        if ((hw->phy.media_type == e1000_media_type_fiber) ||
            hw->mac.autoneg)
                ecmd->autoneg = AUTONEG_ENABLE;
@@ -286,7 +285,7 @@ static int igb_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
        }
 
        while (test_and_set_bit(__IGB_RESETTING, &adapter->state))
-               msleep(1);
+               usleep_range(1000, 2000);
 
        if (ecmd->autoneg == AUTONEG_ENABLE) {
                hw->mac.autoneg = 1;
@@ -399,7 +398,7 @@ static int igb_set_pauseparam(struct net_device *netdev,
        adapter->fc_autoneg = pause->autoneg;
 
        while (test_and_set_bit(__IGB_RESETTING, &adapter->state))
-               msleep(1);
+               usleep_range(1000, 2000);
 
        if (adapter->fc_autoneg == AUTONEG_ENABLE) {
                hw->fc.requested_mode = e1000_fc_default;
@@ -886,7 +885,7 @@ static int igb_set_ringparam(struct net_device *netdev,
        }
 
        while (test_and_set_bit(__IGB_RESETTING, &adapter->state))
-               msleep(1);
+               usleep_range(1000, 2000);
 
        if (!netif_running(adapter->netdev)) {
                for (i = 0; i < adapter->num_tx_queues; i++)
@@ -1060,8 +1059,8 @@ static struct igb_reg_test reg_test_i350[] = {
        { E1000_TDT(0),    0x100, 4,  PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
        { E1000_TDT(4),    0x40,  4,  PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
        { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0xFFFFFFFF, 0x00000000 },
-       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0x003FFFFB },
-       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0xFFFFFFFF },
+       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0x003FFFFB },
+       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0xFFFFFFFF },
        { E1000_TCTL,      0x100, 1,  SET_READ_TEST, 0xFFFFFFFF, 0x00000000 },
        { E1000_RA,        0, 16, TABLE64_TEST_LO,
                                                0xFFFFFFFF, 0xFFFFFFFF },
@@ -1103,8 +1102,8 @@ static struct igb_reg_test reg_test_82580[] = {
        { E1000_TDT(0),    0x100, 4,  PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
        { E1000_TDT(4),    0x40,  4,  PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
        { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0xFFFFFFFF, 0x00000000 },
-       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0x003FFFFB },
-       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0xFFFFFFFF },
+       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0x003FFFFB },
+       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0xFFFFFFFF },
        { E1000_TCTL,      0x100, 1,  SET_READ_TEST, 0xFFFFFFFF, 0x00000000 },
        { E1000_RA,        0, 16, TABLE64_TEST_LO,
                                                0xFFFFFFFF, 0xFFFFFFFF },
@@ -1132,8 +1131,10 @@ static struct igb_reg_test reg_test_82576[] = {
        { E1000_RDBAH(4),  0x40, 12, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF },
        { E1000_RDLEN(4),  0x40, 12, PATTERN_TEST, 0x000FFFF0, 0x000FFFFF },
        /* Enable all RX queues before testing. */
-       { E1000_RXDCTL(0), 0x100, 4,  WRITE_NO_TEST, 0, E1000_RXDCTL_QUEUE_ENABLE },
-       { E1000_RXDCTL(4), 0x40, 12,  WRITE_NO_TEST, 0, E1000_RXDCTL_QUEUE_ENABLE },
+       { E1000_RXDCTL(0), 0x100, 4, WRITE_NO_TEST, 0,
+         E1000_RXDCTL_QUEUE_ENABLE },
+       { E1000_RXDCTL(4), 0x40, 12, WRITE_NO_TEST, 0,
+         E1000_RXDCTL_QUEUE_ENABLE },
        /* RDH is read-only for 82576, only test RDT. */
        { E1000_RDT(0),    0x100, 4,  PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
        { E1000_RDT(4),    0x40, 12,  PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
@@ -1149,14 +1150,14 @@ static struct igb_reg_test reg_test_82576[] = {
        { E1000_TDBAH(4),  0x40, 12,  PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF },
        { E1000_TDLEN(4),  0x40, 12,  PATTERN_TEST, 0x000FFFF0, 0x000FFFFF },
        { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0xFFFFFFFF, 0x00000000 },
-       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0x003FFFFB },
-       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0xFFFFFFFF },
+       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0x003FFFFB },
+       { E1000_RCTL,      0x100, 1,  SET_READ_TEST, 0x04CFB0FE, 0xFFFFFFFF },
        { E1000_TCTL,      0x100, 1,  SET_READ_TEST, 0xFFFFFFFF, 0x00000000 },
        { E1000_RA,        0, 16, TABLE64_TEST_LO, 0xFFFFFFFF, 0xFFFFFFFF },
        { E1000_RA,        0, 16, TABLE64_TEST_HI, 0x83FFFFFF, 0xFFFFFFFF },
        { E1000_RA2,       0, 8, TABLE64_TEST_LO, 0xFFFFFFFF, 0xFFFFFFFF },
        { E1000_RA2,       0, 8, TABLE64_TEST_HI, 0x83FFFFFF, 0xFFFFFFFF },
-       { E1000_MTA,       0, 128,TABLE32_TEST, 0xFFFFFFFF, 0xFFFFFFFF },
+       { E1000_MTA,       0, 128, TABLE32_TEST, 0xFFFFFFFF, 0xFFFFFFFF },
        { 0, 0, 0, 0 }
 };
 
@@ -1170,7 +1171,8 @@ static struct igb_reg_test reg_test_82575[] = {
        { E1000_RDBAH(0),  0x100, 4, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF },
        { E1000_RDLEN(0),  0x100, 4, PATTERN_TEST, 0x000FFF80, 0x000FFFFF },
        /* Enable all four RX queues before testing. */
-       { E1000_RXDCTL(0), 0x100, 4, WRITE_NO_TEST, 0, E1000_RXDCTL_QUEUE_ENABLE },
+       { E1000_RXDCTL(0), 0x100, 4, WRITE_NO_TEST, 0,
+         E1000_RXDCTL_QUEUE_ENABLE },
        /* RDH is read-only for 82575, only test RDT. */
        { E1000_RDT(0),    0x100, 4, PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
        { E1000_RXDCTL(0), 0x100, 4, WRITE_NO_TEST, 0, 0 },
@@ -1196,8 +1198,8 @@ static bool reg_pattern_test(struct igb_adapter *adapter, u64 *data,
 {
        struct e1000_hw *hw = &adapter->hw;
        u32 pat, val;
-       static const u32 _test[] =
-               {0x5A5A5A5A, 0xA5A5A5A5, 0x00000000, 0xFFFFFFFF};
+       static const u32 _test[] = {
+               0x5A5A5A5A, 0xA5A5A5A5, 0x00000000, 0xFFFFFFFF};
        for (pat = 0; pat < ARRAY_SIZE(_test); pat++) {
                wr32(reg, (_test[pat] & write));
                val = rd32(reg) & mask;
@@ -1206,11 +1208,11 @@ static bool reg_pattern_test(struct igb_adapter *adapter, u64 *data,
                                "pattern test reg %04X failed: got 0x%08X expected 0x%08X\n",
                                reg, val, (_test[pat] & write & mask));
                        *data = reg;
-                       return 1;
+                       return true;
                }
        }
 
-       return 0;
+       return false;
 }
 
 static bool reg_set_and_check(struct igb_adapter *adapter, u64 *data,
@@ -1218,17 +1220,18 @@ static bool reg_set_and_check(struct igb_adapter *adapter, u64 *data,
 {
        struct e1000_hw *hw = &adapter->hw;
        u32 val;
+
        wr32(reg, write & mask);
        val = rd32(reg);
        if ((write & mask) != (val & mask)) {
                dev_err(&adapter->pdev->dev,
-                       "set/check reg %04X test failed: got 0x%08X expected 0x%08X\n", reg,
-                       (val & mask), (write & mask));
+                       "set/check reg %04X test failed: got 0x%08X expected 0x%08X\n",
+                       reg, (val & mask), (write & mask));
                *data = reg;
-               return 1;
+               return true;
        }
 
-       return 0;
+       return false;
 }
 
 #define REG_PATTERN_TEST(reg, mask, write) \
@@ -1387,14 +1390,14 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
        /* Hook up test interrupt handler just for this test */
        if (adapter->flags & IGB_FLAG_HAS_MSIX) {
                if (request_irq(adapter->msix_entries[0].vector,
-                               igb_test_intr, 0, netdev->name, adapter)) {
+                               igb_test_intr, 0, netdev->name, adapter)) {
                        *data = 1;
                        return -1;
                }
        } else if (adapter->flags & IGB_FLAG_HAS_MSI) {
                shared_int = false;
                if (request_irq(irq,
-                               igb_test_intr, 0, netdev->name, adapter)) {
+                               igb_test_intr, 0, netdev->name, adapter)) {
                        *data = 1;
                        return -1;
                }
@@ -1412,7 +1415,7 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
        /* Disable all the interrupts */
        wr32(E1000_IMC, ~0);
        wrfl();
-       msleep(10);
+       usleep_range(10000, 11000);
 
        /* Define all writable bits for ICS */
        switch (hw->mac.type) {
@@ -1459,7 +1462,7 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
                        wr32(E1000_IMC, mask);
                        wr32(E1000_ICS, mask);
                        wrfl();
-                       msleep(10);
+                       usleep_range(10000, 11000);
 
                        if (adapter->test_icr & mask) {
                                *data = 3;
@@ -1481,7 +1484,7 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
                wr32(E1000_IMS, mask);
                wr32(E1000_ICS, mask);
                wrfl();
-               msleep(10);
+               usleep_range(10000, 11000);
 
                if (!(adapter->test_icr & mask)) {
                        *data = 4;
@@ -1503,7 +1506,7 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
                        wr32(E1000_IMC, ~mask);
                        wr32(E1000_ICS, ~mask);
                        wrfl();
-                       msleep(10);
+                       usleep_range(10000, 11000);
 
                        if (adapter->test_icr & mask) {
                                *data = 5;
@@ -1515,7 +1518,7 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
        /* Disable all the interrupts */
        wr32(E1000_IMC, ~0);
        wrfl();
-       msleep(10);
+       usleep_range(10000, 11000);
 
        /* Unhook test interrupt handler */
        if (adapter->flags & IGB_FLAG_HAS_MSIX)
@@ -1664,8 +1667,8 @@ static int igb_setup_loopback_test(struct igb_adapter *adapter)
                (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_I354_SGMII)) {
-
+               (hw->device_id == E1000_DEV_ID_I354_SGMII) ||
+               (hw->device_id == E1000_DEV_ID_I354_BACKPLANE_2_5GBPS)) {
                        /* Enable DH89xxCC MPHY for near end loopback */
                        reg = rd32(E1000_MPHY_ADDR_CTL);
                        reg = (reg & E1000_MPHY_ADDR_CTL_OFFSET_MASK) |
@@ -1949,6 +1952,7 @@ static int igb_link_test(struct igb_adapter *adapter, u64 *data)
        *data = 0;
        if (hw->phy.media_type == e1000_media_type_internal_serdes) {
                int i = 0;
+
                hw->mac.serdes_has_link = false;
 
                /* On some blade server designs, link establishment
@@ -2413,9 +2417,11 @@ static int igb_get_rss_hash_opts(struct igb_adapter *adapter,
        switch (cmd->flow_type) {
        case TCP_V4_FLOW:
                cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+               /* Fall through */
        case UDP_V4_FLOW:
                if (adapter->flags & IGB_FLAG_RSS_FIELD_IPV4_UDP)
                        cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+               /* Fall through */
        case SCTP_V4_FLOW:
        case AH_ESP_V4_FLOW:
        case AH_V4_FLOW:
@@ -2425,9 +2431,11 @@ static int igb_get_rss_hash_opts(struct igb_adapter *adapter,
                break;
        case TCP_V6_FLOW:
                cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+               /* Fall through */
        case UDP_V6_FLOW:
                if (adapter->flags & IGB_FLAG_RSS_FIELD_IPV6_UDP)
                        cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+               /* Fall through */
        case SCTP_V6_FLOW:
        case AH_ESP_V6_FLOW:
        case AH_V6_FLOW:
@@ -2730,7 +2738,7 @@ static int igb_get_module_info(struct net_device *netdev,
 {
        struct igb_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
-       u32 status = E1000_SUCCESS;
+       u32 status = 0;
        u16 sff8472_rev, addr_mode;
        bool page_swap = false;
 
@@ -2740,12 +2748,12 @@ static int igb_get_module_info(struct net_device *netdev,
 
        /* Check whether we support SFF-8472 or not */
        status = igb_read_phy_reg_i2c(hw, IGB_SFF_8472_COMP, &sff8472_rev);
-       if (status != E1000_SUCCESS)
+       if (status)
                return -EIO;
 
        /* addressing mode is not supported */
        status = igb_read_phy_reg_i2c(hw, IGB_SFF_8472_SWAP, &addr_mode);
-       if (status != E1000_SUCCESS)
+       if (status)
                return -EIO;
 
        /* addressing mode is not supported */
@@ -2772,7 +2780,7 @@ static int igb_get_module_eeprom(struct net_device *netdev,
 {
        struct igb_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
-       u32 status = E1000_SUCCESS;
+       u32 status = 0;
        u16 *dataword;
        u16 first_word, last_word;
        int i = 0;
@@ -2791,7 +2799,7 @@ static int igb_get_module_eeprom(struct net_device *netdev,
        /* Read EEPROM block, SFF-8079/SFF-8472, word at a time */
        for (i = 0; i < last_word - first_word + 1; i++) {
                status = igb_read_phy_reg_i2c(hw, first_word + i, &dataword[i]);
-               if (status != E1000_SUCCESS) {
+               if (status) {
                        /* Error occurred while reading module */
                        kfree(dataword);
                        return -EIO;
@@ -2824,7 +2832,7 @@ static u32 igb_get_rxfh_indir_size(struct net_device *netdev)
        return IGB_RETA_SIZE;
 }
 
-static int igb_get_rxfh_indir(struct net_device *netdev, u32 *indir)
+static int igb_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key)
 {
        struct igb_adapter *adapter = netdev_priv(netdev);
        int i;
@@ -2870,7 +2878,8 @@ void igb_write_rss_indir_tbl(struct igb_adapter *adapter)
        }
 }
 
-static int igb_set_rxfh_indir(struct net_device *netdev, const u32 *indir)
+static int igb_set_rxfh(struct net_device *netdev, const u32 *indir,
+                       const u8 *key)
 {
        struct igb_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
@@ -3019,8 +3028,8 @@ static const struct ethtool_ops igb_ethtool_ops = {
        .get_module_info        = igb_get_module_info,
        .get_module_eeprom      = igb_get_module_eeprom,
        .get_rxfh_indir_size    = igb_get_rxfh_indir_size,
-       .get_rxfh_indir         = igb_get_rxfh_indir,
-       .set_rxfh_indir         = igb_set_rxfh_indir,
+       .get_rxfh               = igb_get_rxfh,
+       .set_rxfh               = igb_set_rxfh,
        .get_channels           = igb_get_channels,
        .set_channels           = igb_set_channels,
        .begin                  = igb_ethtool_begin,
@@ -3029,5 +3038,5 @@ static const struct ethtool_ops igb_ethtool_ops = {
 
 void igb_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &igb_ethtool_ops);
+       netdev->ethtool_ops = &igb_ethtool_ops;
 }
index 8333f67acf96b3a6e83746c1cedd1f0eff7636ec..44b6a68f1af727136271132014b1efa412ee7e32 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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.
-
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #include "igb.h"
 #include "e1000_82575.h"
index 16430a8440fa3c5bcc299427f3052f6f2a7bdfcc..f145adbb55ac011905636ac17492c55c519e2789 100644 (file)
@@ -1,28 +1,25 @@
-/*******************************************************************************
-
-  Intel(R) Gigabit Ethernet Linux driver
-  Copyright(c) 2007-2014 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.
-
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, see <http://www.gnu.org/licenses/>.
-
-  The full GNU General Public License is included in this distribution in
-  the file called "COPYING".
-
-  Contact Information:
-  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
+/* Intel(R) Gigabit Ethernet Linux driver
+ * Copyright(c) 2007-2014 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
@@ -75,7 +72,7 @@ static const struct e1000_info *igb_info_tbl[] = {
        [board_82575] = &e1000_82575_info,
 };
 
-static DEFINE_PCI_DEVICE_TABLE(igb_pci_tbl) = {
+static const struct pci_device_id igb_pci_tbl[] = {
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_BACKPLANE_1GBPS) },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_SGMII) },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_BACKPLANE_2_5GBPS) },
@@ -117,7 +114,6 @@ static DEFINE_PCI_DEVICE_TABLE(igb_pci_tbl) = {
 
 MODULE_DEVICE_TABLE(pci, igb_pci_tbl);
 
-void igb_reset(struct igb_adapter *);
 static int igb_setup_all_tx_resources(struct igb_adapter *);
 static int igb_setup_all_rx_resources(struct igb_adapter *);
 static void igb_free_all_tx_resources(struct igb_adapter *);
@@ -141,7 +137,7 @@ static void igb_watchdog(unsigned long);
 static void igb_watchdog_task(struct work_struct *);
 static netdev_tx_t igb_xmit_frame(struct sk_buff *skb, struct net_device *);
 static struct rtnl_link_stats64 *igb_get_stats64(struct net_device *dev,
-                                                struct rtnl_link_stats64 *stats);
+                                         struct rtnl_link_stats64 *stats);
 static int igb_change_mtu(struct net_device *, int);
 static int igb_set_mac(struct net_device *, void *);
 static void igb_set_uta(struct igb_adapter *adapter);
@@ -159,7 +155,8 @@ static bool igb_clean_rx_irq(struct igb_q_vector *, int);
 static int igb_ioctl(struct net_device *, struct ifreq *, int cmd);
 static void igb_tx_timeout(struct net_device *);
 static void igb_reset_task(struct work_struct *);
-static void igb_vlan_mode(struct net_device *netdev, netdev_features_t features);
+static void igb_vlan_mode(struct net_device *netdev,
+                         netdev_features_t features);
 static int igb_vlan_rx_add_vid(struct net_device *, __be16, u16);
 static int igb_vlan_rx_kill_vid(struct net_device *, __be16, u16);
 static void igb_restore_vlan(struct igb_adapter *);
@@ -172,7 +169,7 @@ static void igb_restore_vf_multicasts(struct igb_adapter *adapter);
 static int igb_ndo_set_vf_mac(struct net_device *netdev, int vf, u8 *mac);
 static int igb_ndo_set_vf_vlan(struct net_device *netdev,
                               int vf, u16 vlan, u8 qos);
-static int igb_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate);
+static int igb_ndo_set_vf_bw(struct net_device *, int, int, int);
 static int igb_ndo_set_vf_spoofchk(struct net_device *netdev, int vf,
                                   bool setting);
 static int igb_ndo_get_vf_config(struct net_device *netdev, int vf,
@@ -215,10 +212,9 @@ static struct notifier_block dca_notifier = {
 static void igb_netpoll(struct net_device *);
 #endif
 #ifdef CONFIG_PCI_IOV
-static unsigned int max_vfs = 0;
+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");
+MODULE_PARM_DESC(max_vfs, "Maximum number of virtual functions to allocate per physical function");
 #endif /* CONFIG_PCI_IOV */
 
 static pci_ers_result_t igb_io_error_detected(struct pci_dev *,
@@ -384,8 +380,7 @@ static void igb_dump(struct igb_adapter *adapter)
        /* Print netdevice Info */
        if (netdev) {
                dev_info(&adapter->pdev->dev, "Net device Info\n");
-               pr_info("Device Name     state            trans_start      "
-                       "last_rx\n");
+               pr_info("Device Name     state            trans_start      last_rx\n");
                pr_info("%-15s %016lX %016lX %016lX\n", netdev->name,
                        netdev->state, netdev->trans_start, netdev->last_rx);
        }
@@ -438,9 +433,7 @@ static void igb_dump(struct igb_adapter *adapter)
                pr_info("------------------------------------\n");
                pr_info("TX QUEUE INDEX = %d\n", tx_ring->queue_index);
                pr_info("------------------------------------\n");
-               pr_info("T [desc]     [address 63:0  ] [PlPOCIStDDM Ln] "
-                       "[bi->dma       ] leng  ntw timestamp        "
-                       "bi->skb\n");
+               pr_info("T [desc]     [address 63:0  ] [PlPOCIStDDM Ln] [bi->dma       ] leng  ntw timestamp        bi->skb\n");
 
                for (i = 0; tx_ring->desc && (i < tx_ring->count); i++) {
                        const char *next_desc;
@@ -458,9 +451,8 @@ static void igb_dump(struct igb_adapter *adapter)
                        else
                                next_desc = "";
 
-                       pr_info("T [0x%03X]    %016llX %016llX %016llX"
-                               " %04X  %p %016llX %p%s\n", i,
-                               le64_to_cpu(u0->a),
+                       pr_info("T [0x%03X]    %016llX %016llX %016llX %04X  %p %016llX %p%s\n",
+                               i, le64_to_cpu(u0->a),
                                le64_to_cpu(u0->b),
                                (u64)dma_unmap_addr(buffer_info, dma),
                                dma_unmap_len(buffer_info, len),
@@ -519,10 +511,8 @@ static void igb_dump(struct igb_adapter *adapter)
                pr_info("------------------------------------\n");
                pr_info("RX QUEUE INDEX = %d\n", rx_ring->queue_index);
                pr_info("------------------------------------\n");
-               pr_info("R  [desc]      [ PktBuf     A0] [  HeadBuf   DD] "
-                       "[bi->dma       ] [bi->skb] <-- Adv Rx Read format\n");
-               pr_info("RWB[desc]      [PcsmIpSHl PtRs] [vl er S cks ln] -----"
-                       "----------- [bi->skb] <-- Adv Rx Write-Back format\n");
+               pr_info("R  [desc]      [ PktBuf     A0] [  HeadBuf   DD] [bi->dma       ] [bi->skb] <-- Adv Rx Read format\n");
+               pr_info("RWB[desc]      [PcsmIpSHl PtRs] [vl er S cks ln] ---------------- [bi->skb] <-- Adv Rx Write-Back format\n");
 
                for (i = 0; i < rx_ring->count; i++) {
                        const char *next_desc;
@@ -584,7 +574,7 @@ static int igb_get_i2c_data(void *data)
        struct e1000_hw *hw = &adapter->hw;
        s32 i2cctl = rd32(E1000_I2CPARAMS);
 
-       return ((i2cctl & E1000_I2C_DATA_IN) != 0);
+       return !!(i2cctl & E1000_I2C_DATA_IN);
 }
 
 /**
@@ -648,7 +638,7 @@ static int igb_get_i2c_clk(void *data)
        struct e1000_hw *hw = &adapter->hw;
        s32 i2cctl = rd32(E1000_I2CPARAMS);
 
-       return ((i2cctl & E1000_I2C_CLK_IN) != 0);
+       return !!(i2cctl & E1000_I2C_CLK_IN);
 }
 
 static const struct i2c_algo_bit_data igb_i2c_algo = {
@@ -681,9 +671,9 @@ struct net_device *igb_get_hw_dev(struct e1000_hw *hw)
 static int __init igb_init_module(void)
 {
        int ret;
+
        pr_info("%s - version %s\n",
               igb_driver_string, igb_driver_version);
-
        pr_info("%s\n", igb_copyright);
 
 #ifdef CONFIG_IGB_DCA
@@ -736,12 +726,14 @@ static void igb_cache_ring_register(struct igb_adapter *adapter)
                                adapter->rx_ring[i]->reg_idx = rbase_offset +
                                                               Q_IDX_82576(i);
                }
+               /* Fall through */
        case e1000_82575:
        case e1000_82580:
        case e1000_i350:
        case e1000_i354:
        case e1000_i210:
        case e1000_i211:
+               /* Fall through */
        default:
                for (; i < adapter->num_rx_queues; i++)
                        adapter->rx_ring[i]->reg_idx = rbase_offset + i;
@@ -1292,8 +1284,7 @@ static int igb_alloc_q_vector(struct igb_adapter *adapter,
                if (adapter->hw.mac.type >= e1000_82576)
                        set_bit(IGB_RING_FLAG_RX_SCTP_CSUM, &ring->flags);
 
-               /*
-                * On i350, i354, i210, and i211, loopback VLAN packets
+               /* On i350, i354, i210, and i211, loopback VLAN packets
                 * have the tag byte-swapped.
                 */
                if (adapter->hw.mac.type >= e1000_i350)
@@ -1345,6 +1336,7 @@ static int igb_alloc_q_vectors(struct igb_adapter *adapter)
        for (; v_idx < q_vectors; v_idx++) {
                int rqpv = DIV_ROUND_UP(rxr_remaining, q_vectors - v_idx);
                int tqpv = DIV_ROUND_UP(txr_remaining, q_vectors - v_idx);
+
                err = igb_alloc_q_vector(adapter, q_vectors, v_idx,
                                         tqpv, txr_idx, rqpv, rxr_idx);
 
@@ -1484,6 +1476,7 @@ static void igb_irq_disable(struct igb_adapter *adapter)
         */
        if (adapter->flags & IGB_FLAG_HAS_MSIX) {
                u32 regval = rd32(E1000_EIAM);
+
                wr32(E1000_EIAM, regval & ~adapter->eims_enable_mask);
                wr32(E1000_EIMC, adapter->eims_enable_mask);
                regval = rd32(E1000_EIAC);
@@ -1495,6 +1488,7 @@ static void igb_irq_disable(struct igb_adapter *adapter)
        wrfl();
        if (adapter->flags & IGB_FLAG_HAS_MSIX) {
                int i;
+
                for (i = 0; i < adapter->num_q_vectors; i++)
                        synchronize_irq(adapter->msix_entries[i].vector);
        } else {
@@ -1513,6 +1507,7 @@ static void igb_irq_enable(struct igb_adapter *adapter)
        if (adapter->flags & IGB_FLAG_HAS_MSIX) {
                u32 ims = E1000_IMS_LSC | E1000_IMS_DOUTSYNC | E1000_IMS_DRSTA;
                u32 regval = rd32(E1000_EIAC);
+
                wr32(E1000_EIAC, regval | adapter->eims_enable_mask);
                regval = rd32(E1000_EIAM);
                wr32(E1000_EIAM, regval | adapter->eims_enable_mask);
@@ -1745,6 +1740,7 @@ int igb_up(struct igb_adapter *adapter)
        /* notify VFs that reset has been completed */
        if (adapter->vfs_allocated_count) {
                u32 reg_data = rd32(E1000_CTRL_EXT);
+
                reg_data |= E1000_CTRL_EXT_PFRSTD;
                wr32(E1000_CTRL_EXT, reg_data);
        }
@@ -1787,7 +1783,7 @@ void igb_down(struct igb_adapter *adapter)
        wr32(E1000_TCTL, tctl);
        /* flush both disables and wait for them to finish */
        wrfl();
-       msleep(10);
+       usleep_range(10000, 11000);
 
        igb_irq_disable(adapter);
 
@@ -1827,7 +1823,7 @@ void igb_reinit_locked(struct igb_adapter *adapter)
 {
        WARN_ON(in_interrupt());
        while (test_and_set_bit(__IGB_RESETTING, &adapter->state))
-               msleep(1);
+               usleep_range(1000, 2000);
        igb_down(adapter);
        igb_up(adapter);
        clear_bit(__IGB_RESETTING, &adapter->state);
@@ -1960,6 +1956,7 @@ void igb_reset(struct igb_adapter *adapter)
        /* disable receive for all VFs and wait one second */
        if (adapter->vfs_allocated_count) {
                int i;
+
                for (i = 0 ; i < adapter->vfs_allocated_count; i++)
                        adapter->vf_data[i].flags &= IGB_VF_FLAG_PF_SET_MAC;
 
@@ -2087,7 +2084,7 @@ static const struct net_device_ops igb_netdev_ops = {
        .ndo_vlan_rx_kill_vid   = igb_vlan_rx_kill_vid,
        .ndo_set_vf_mac         = igb_ndo_set_vf_mac,
        .ndo_set_vf_vlan        = igb_ndo_set_vf_vlan,
-       .ndo_set_vf_tx_rate     = igb_ndo_set_vf_bw,
+       .ndo_set_vf_rate        = igb_ndo_set_vf_bw,
        .ndo_set_vf_spoofchk    = igb_ndo_set_vf_spoofchk,
        .ndo_get_vf_config      = igb_ndo_get_vf_config,
 #ifdef CONFIG_NET_POLL_CONTROLLER
@@ -2142,7 +2139,6 @@ void igb_set_fw_version(struct igb_adapter *adapter)
                }
                break;
        }
-       return;
 }
 
 /**
@@ -2203,11 +2199,11 @@ static void igb_init_mas(struct igb_adapter *adapter)
  **/
 static s32 igb_init_i2c(struct igb_adapter *adapter)
 {
-       s32 status = E1000_SUCCESS;
+       s32 status = 0;
 
        /* I2C interface supported on i350 devices */
        if (adapter->hw.mac.type != e1000_i350)
-               return E1000_SUCCESS;
+               return 0;
 
        /* Initialize the i2c bus which is controlled by the registers.
         * This bus will use the i2c_algo_bit structue that implements
@@ -2437,6 +2433,12 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        /* get firmware version for ethtool -i */
        igb_set_fw_version(adapter);
 
+       /* configure RXPBSIZE and TXPBSIZE */
+       if (hw->mac.type == e1000_i210) {
+               wr32(E1000_RXPBS, I210_RXPBSIZE_DEFAULT);
+               wr32(E1000_TXPBS, I210_TXPBSIZE_DEFAULT);
+       }
+
        setup_timer(&adapter->watchdog_timer, igb_watchdog,
                    (unsigned long) adapter);
        setup_timer(&adapter->phy_info_timer, igb_update_phy_info,
@@ -2529,7 +2531,8 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        }
 
        /* let the f/w know that the h/w is now under the control of the
-        * driver. */
+        * driver.
+        */
        igb_get_hw_control(adapter);
 
        strcpy(netdev->name, "eth%d");
@@ -3077,6 +3080,7 @@ static int __igb_open(struct net_device *netdev, bool resuming)
        /* notify VFs that reset has been completed */
        if (adapter->vfs_allocated_count) {
                u32 reg_data = rd32(E1000_CTRL_EXT);
+
                reg_data |= E1000_CTRL_EXT_PFRSTD;
                wr32(E1000_CTRL_EXT, reg_data);
        }
@@ -3248,7 +3252,7 @@ void igb_setup_tctl(struct igb_adapter *adapter)
  *  Configure a transmit ring after a reset.
  **/
 void igb_configure_tx_ring(struct igb_adapter *adapter,
-                           struct igb_ring *ring)
+                          struct igb_ring *ring)
 {
        struct e1000_hw *hw = &adapter->hw;
        u32 txdctl = 0;
@@ -3389,7 +3393,8 @@ static void igb_setup_mrqc(struct igb_adapter *adapter)
 
        if (adapter->rss_indir_tbl_init != num_rx_queues) {
                for (j = 0; j < IGB_RETA_SIZE; j++)
-                       adapter->rss_indir_tbl[j] = (j * num_rx_queues) / IGB_RETA_SIZE;
+                       adapter->rss_indir_tbl[j] =
+                       (j * num_rx_queues) / IGB_RETA_SIZE;
                adapter->rss_indir_tbl_init = num_rx_queues;
        }
        igb_write_rss_indir_tbl(adapter);
@@ -3430,6 +3435,7 @@ static void igb_setup_mrqc(struct igb_adapter *adapter)
                if (hw->mac.type > e1000_82575) {
                        /* Set the default pool for the PF's first queue */
                        u32 vtctl = rd32(E1000_VT_CTL);
+
                        vtctl &= ~(E1000_VT_CTL_DEFAULT_POOL_MASK |
                                   E1000_VT_CTL_DISABLE_DEF_POOL);
                        vtctl |= adapter->vfs_allocated_count <<
@@ -3511,7 +3517,7 @@ void igb_setup_rctl(struct igb_adapter *adapter)
 }
 
 static inline int igb_set_vf_rlpml(struct igb_adapter *adapter, int size,
-                                   int vfn)
+                                  int vfn)
 {
        struct e1000_hw *hw = &adapter->hw;
        u32 vmolr;
@@ -4058,7 +4064,8 @@ static void igb_check_wvbr(struct igb_adapter *adapter)
        switch (hw->mac.type) {
        case e1000_82576:
        case e1000_i350:
-               if (!(wvbr = rd32(E1000_WVBR)))
+               wvbr = rd32(E1000_WVBR);
+               if (!wvbr)
                        return;
                break;
        default:
@@ -4077,7 +4084,7 @@ static void igb_spoof_check(struct igb_adapter *adapter)
        if (!adapter->wvbr)
                return;
 
-       for(j = 0; j < adapter->vfs_allocated_count; j++) {
+       for (j = 0; j < adapter->vfs_allocated_count; j++) {
                if (adapter->wvbr & (1 << j) ||
                    adapter->wvbr & (1 << (j + IGB_STAGGERED_QUEUE_OFFSET))) {
                        dev_warn(&adapter->pdev->dev,
@@ -4209,14 +4216,15 @@ static void igb_watchdog_task(struct work_struct *work)
 
                if (!netif_carrier_ok(netdev)) {
                        u32 ctrl;
+
                        hw->mac.ops.get_speed_and_duplex(hw,
                                                         &adapter->link_speed,
                                                         &adapter->link_duplex);
 
                        ctrl = rd32(E1000_CTRL);
                        /* Links status message must follow this format */
-                       printk(KERN_INFO "igb: %s NIC Link is Up %d Mbps %s "
-                              "Duplex, Flow Control: %s\n",
+                       netdev_info(netdev,
+                              "igb: %s NIC Link is Up %d Mbps %s Duplex, Flow Control: %s\n",
                               netdev->name,
                               adapter->link_speed,
                               adapter->link_duplex == FULL_DUPLEX ?
@@ -4242,11 +4250,8 @@ static void igb_watchdog_task(struct work_struct *work)
 
                        /* check for thermal sensor event */
                        if (igb_thermal_sensor_event(hw,
-                           E1000_THSTAT_LINK_THROTTLE)) {
-                               netdev_info(netdev, "The network adapter link "
-                                           "speed was downshifted because it "
-                                           "overheated\n");
-                       }
+                           E1000_THSTAT_LINK_THROTTLE))
+                               netdev_info(netdev, "The network adapter link speed was downshifted because it overheated\n");
 
                        /* adjust timeout factor according to speed/duplex */
                        adapter->tx_timeout_factor = 1;
@@ -4277,12 +4282,11 @@ static void igb_watchdog_task(struct work_struct *work)
                        /* check for thermal sensor event */
                        if (igb_thermal_sensor_event(hw,
                            E1000_THSTAT_PWR_DOWN)) {
-                               netdev_err(netdev, "The network adapter was "
-                                          "stopped because it overheated\n");
+                               netdev_err(netdev, "The network adapter was stopped because it overheated\n");
                        }
 
                        /* Links status message must follow this format */
-                       printk(KERN_INFO "igb: %s NIC Link is Down\n",
+                       netdev_info(netdev, "igb: %s NIC Link is Down\n",
                               netdev->name);
                        netif_carrier_off(netdev);
 
@@ -4344,6 +4348,7 @@ static void igb_watchdog_task(struct work_struct *work)
        /* Cause software interrupt to ensure Rx ring is cleaned */
        if (adapter->flags & IGB_FLAG_HAS_MSIX) {
                u32 eics = 0;
+
                for (i = 0; i < adapter->num_q_vectors; i++)
                        eics |= adapter->q_vector[i]->eims_value;
                wr32(E1000_EICS, eics);
@@ -4483,13 +4488,12 @@ static void igb_update_itr(struct igb_q_vector *q_vector,
        case low_latency:  /* 50 usec aka 20000 ints/s */
                if (bytes > 10000) {
                        /* this if handles the TSO accounting */
-                       if (bytes/packets > 8000) {
+                       if (bytes/packets > 8000)
                                itrval = bulk_latency;
-                       } else if ((packets < 10) || ((bytes/packets) > 1200)) {
+                       else if ((packets < 10) || ((bytes/packets) > 1200))
                                itrval = bulk_latency;
-                       } else if ((packets > 35)) {
+                       else if ((packets > 35))
                                itrval = lowest_latency;
-                       }
                } else if (bytes/packets > 2000) {
                        itrval = bulk_latency;
                } else if (packets <= 2 && bytes < 512) {
@@ -4675,6 +4679,7 @@ static void igb_tx_csum(struct igb_ring *tx_ring, struct igb_tx_buffer *first)
                        return;
        } else {
                u8 l4_hdr = 0;
+
                switch (first->protocol) {
                case htons(ETH_P_IP):
                        vlan_macip_lens |= skb_network_header_len(skb);
@@ -4962,6 +4967,7 @@ netdev_tx_t igb_xmit_frame_ring(struct sk_buff *skb,
         */
        if (NETDEV_FRAG_PAGE_MAX_SIZE > IGB_MAX_DATA_PER_TXD) {
                unsigned short f;
+
                for (f = 0; f < skb_shinfo(skb)->nr_frags; f++)
                        count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size);
        } else {
@@ -5140,7 +5146,7 @@ static int igb_change_mtu(struct net_device *netdev, int new_mtu)
                max_frame = ETH_FRAME_LEN + ETH_FCS_LEN;
 
        while (test_and_set_bit(__IGB_RESETTING, &adapter->state))
-               msleep(1);
+               usleep_range(1000, 2000);
 
        /* igb_down has a dependency on max_frame_size */
        adapter->max_frame_size = max_frame;
@@ -5621,6 +5627,7 @@ static int igb_set_vf_promisc(struct igb_adapter *adapter, u32 *msgbuf, u32 vf)
                        vmolr |= E1000_VMOLR_MPME;
                } else if (vf_data->num_vf_mc_hashes) {
                        int j;
+
                        vmolr |= E1000_VMOLR_ROMPE;
                        for (j = 0; j < vf_data->num_vf_mc_hashes; j++)
                                igb_mta_set(hw, vf_data->vf_mc_hashes[j]);
@@ -5672,6 +5679,7 @@ static void igb_restore_vf_multicasts(struct igb_adapter *adapter)
 
        for (i = 0; i < adapter->vfs_allocated_count; i++) {
                u32 vmolr = rd32(E1000_VMOLR(i));
+
                vmolr &= ~(E1000_VMOLR_ROMPE | E1000_VMOLR_MPME);
 
                vf_data = &adapter->vf_data[i];
@@ -5770,6 +5778,7 @@ static s32 igb_vlvf_set(struct igb_adapter *adapter, u32 vid, bool add, u32 vf)
 
                        if (!adapter->vf_data[vf].vlans_enabled) {
                                u32 size;
+
                                reg = rd32(E1000_VMOLR(vf));
                                size = reg & E1000_VMOLR_RLPML_MASK;
                                size += 4;
@@ -5798,6 +5807,7 @@ static s32 igb_vlvf_set(struct igb_adapter *adapter, u32 vid, bool add, u32 vf)
                        adapter->vf_data[vf].vlans_enabled--;
                        if (!adapter->vf_data[vf].vlans_enabled) {
                                u32 size;
+
                                reg = rd32(E1000_VMOLR(vf));
                                size = reg & E1000_VMOLR_RLPML_MASK;
                                size -= 4;
@@ -5902,8 +5912,8 @@ static int igb_set_vf_vlan(struct igb_adapter *adapter, u32 *msgbuf, u32 vf)
         */
        if (!add && (adapter->netdev->flags & IFF_PROMISC)) {
                u32 vlvf, bits;
-
                int regndx = igb_find_vlvf_entry(adapter, vid);
+
                if (regndx < 0)
                        goto out;
                /* See if any other pools are set for this VLAN filter
@@ -6494,7 +6504,7 @@ static void igb_reuse_rx_page(struct igb_ring *rx_ring,
        rx_ring->next_to_alloc = (nta < rx_ring->count) ? nta : 0;
 
        /* transfer page from old buffer to new buffer */
-       memcpy(new_buff, old_buff, sizeof(struct igb_rx_buffer));
+       *new_buff = *old_buff;
 
        /* sync the buffer for use by the device */
        dma_sync_single_range_for_device(rx_ring->dev, old_buff->dma,
@@ -6963,6 +6973,7 @@ static void igb_process_skb_fields(struct igb_ring *rx_ring,
        if ((dev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
            igb_test_staterr(rx_desc, E1000_RXD_STAT_VP)) {
                u16 vid;
+
                if (igb_test_staterr(rx_desc, E1000_RXDEXT_STATERR_LB) &&
                    test_bit(IGB_RING_FLAG_RX_LB_VLAN_BSWAP, &rx_ring->flags))
                        vid = be16_to_cpu(rx_desc->wb.upper.vlan);
@@ -7051,7 +7062,7 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget)
        if (cleaned_count)
                igb_alloc_rx_buffers(rx_ring, cleaned_count);
 
-       return (total_packets < budget);
+       return total_packets < budget;
 }
 
 static bool igb_alloc_mapped_page(struct igb_ring *rx_ring,
@@ -7172,7 +7183,7 @@ static int igb_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
                break;
        case SIOCGMIIREG:
                if (igb_read_phy_reg(&adapter->hw, data->reg_num & 0x1F,
-                                    &data->val_out))
+                                    &data->val_out))
                        return -EIO;
                break;
        case SIOCSMIIREG:
@@ -7873,7 +7884,8 @@ static void igb_check_vf_rate_limit(struct igb_adapter *adapter)
        }
 }
 
-static int igb_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate)
+static int igb_ndo_set_vf_bw(struct net_device *netdev, int vf,
+                            int min_tx_rate, int max_tx_rate)
 {
        struct igb_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
@@ -7882,15 +7894,19 @@ static int igb_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate)
        if (hw->mac.type != e1000_82576)
                return -EOPNOTSUPP;
 
+       if (min_tx_rate)
+               return -EINVAL;
+
        actual_link_speed = igb_link_mbps(adapter->link_speed);
        if ((vf >= adapter->vfs_allocated_count) ||
            (!(rd32(E1000_STATUS) & E1000_STATUS_LU)) ||
-           (tx_rate < 0) || (tx_rate > actual_link_speed))
+           (max_tx_rate < 0) ||
+           (max_tx_rate > actual_link_speed))
                return -EINVAL;
 
        adapter->vf_rate_link_speed = actual_link_speed;
-       adapter->vf_data[vf].tx_rate = (u16)tx_rate;
-       igb_set_vf_rate_limit(hw, vf, tx_rate, actual_link_speed);
+       adapter->vf_data[vf].tx_rate = (u16)max_tx_rate;
+       igb_set_vf_rate_limit(hw, vf, max_tx_rate, actual_link_speed);
 
        return 0;
 }
@@ -7919,7 +7935,7 @@ static int igb_ndo_set_vf_spoofchk(struct net_device *netdev, int vf,
        wr32(reg_offset, reg_val);
 
        adapter->vf_data[vf].spoofchk_enabled = setting;
-       return E1000_SUCCESS;
+       return 0;
 }
 
 static int igb_ndo_get_vf_config(struct net_device *netdev,
@@ -7930,7 +7946,8 @@ static int igb_ndo_get_vf_config(struct net_device *netdev,
                return -EINVAL;
        ivi->vf = vf;
        memcpy(&ivi->mac, adapter->vf_data[vf].vf_mac_addresses, ETH_ALEN);
-       ivi->tx_rate = adapter->vf_data[vf].tx_rate;
+       ivi->max_tx_rate = adapter->vf_data[vf].tx_rate;
+       ivi->min_tx_rate = 0;
        ivi->vlan = adapter->vf_data[vf].pf_vlan;
        ivi->qos = adapter->vf_data[vf].pf_qos;
        ivi->spoofchk = adapter->vf_data[vf].spoofchk_enabled;
@@ -7955,11 +7972,13 @@ static void igb_vmm_control(struct igb_adapter *adapter)
                reg = rd32(E1000_DTXCTL);
                reg |= E1000_DTXCTL_VLAN_ADDED;
                wr32(E1000_DTXCTL, reg);
+               /* Fall through */
        case e1000_82580:
                /* enable replication vlan tag stripping */
                reg = rd32(E1000_RPLOLR);
                reg |= E1000_RPLOLR_STRVLAN;
                wr32(E1000_RPLOLR, reg);
+               /* Fall through */
        case e1000_i350:
                /* none of the above registers are supported by i350 */
                break;
@@ -8049,6 +8068,7 @@ static void igb_init_dmac(struct igb_adapter *adapter, u32 pba)
                } /* endif adapter->dmac is not disabled */
        } else if (hw->mac.type == e1000_82580) {
                u32 reg = rd32(E1000_PCIEMISC);
+
                wr32(E1000_PCIEMISC, reg & ~E1000_PCIEMISC_LX_DECISION);
                wr32(E1000_DMACR, 0);
        }
@@ -8077,8 +8097,7 @@ s32 igb_read_i2c_byte(struct e1000_hw *hw, u8 byte_offset,
 
        swfw_mask = E1000_SWFW_PHY0_SM;
 
-       if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask)
-           != E1000_SUCCESS)
+       if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask))
                return E1000_ERR_SWFW_SYNC;
 
        status = i2c_smbus_read_byte_data(this_client, byte_offset);
@@ -8088,7 +8107,7 @@ s32 igb_read_i2c_byte(struct e1000_hw *hw, u8 byte_offset,
                return E1000_ERR_I2C;
        else {
                *data = status;
-               return E1000_SUCCESS;
+               return 0;
        }
 }
 
@@ -8113,7 +8132,7 @@ s32 igb_write_i2c_byte(struct e1000_hw *hw, u8 byte_offset,
        if (!this_client)
                return E1000_ERR_I2C;
 
-       if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask) != E1000_SUCCESS)
+       if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask))
                return E1000_ERR_SWFW_SYNC;
        status = i2c_smbus_write_byte_data(this_client, byte_offset, data);
        hw->mac.ops.release_swfw_sync(hw, swfw_mask);
@@ -8121,7 +8140,7 @@ s32 igb_write_i2c_byte(struct e1000_hw *hw, u8 byte_offset,
        if (status)
                return E1000_ERR_I2C;
        else
-               return E1000_SUCCESS;
+               return 0;
 
 }
 
index ab25e49365f79e26cfcad7a7ad12b8c5192824c9..794c139f0cc04f86bf8319ea7e42840677db2c6c 100644 (file)
@@ -360,8 +360,8 @@ static int igb_ptp_settime_i210(struct ptp_clock_info *ptp,
        return 0;
 }
 
-static int igb_ptp_enable(struct ptp_clock_info *ptp,
-                         struct ptp_clock_request *rq, int on)
+static int igb_ptp_feature_enable(struct ptp_clock_info *ptp,
+                                 struct ptp_clock_request *rq, int on)
 {
        return -EOPNOTSUPP;
 }
@@ -559,10 +559,11 @@ int igb_ptp_get_ts_config(struct net_device *netdev, struct ifreq *ifr)
        return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ?
                -EFAULT : 0;
 }
+
 /**
- * igb_ptp_set_ts_config - control hardware time stamping
- * @netdev:
- * @ifreq:
+ * igb_ptp_set_timestamp_mode - setup hardware for timestamping
+ * @adapter: networking device structure
+ * @config: hwtstamp configuration
  *
  * Outgoing time stamping can be enabled and disabled. Play nice and
  * disable it when requested, although it shouldn't case any overhead
@@ -575,12 +576,11 @@ int igb_ptp_get_ts_config(struct net_device *netdev, struct ifreq *ifr)
  * type has to be specified. Matching the kind of event packet is
  * not supported, with the exception of "all V2 events regardless of
  * level 2 or 4".
- **/
-int igb_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr)
+ */
+static int igb_ptp_set_timestamp_mode(struct igb_adapter *adapter,
+                                     struct hwtstamp_config *config)
 {
-       struct igb_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
-       struct hwtstamp_config *config = &adapter->tstamp_config;
        u32 tsync_tx_ctl = E1000_TSYNCTXCTL_ENABLED;
        u32 tsync_rx_ctl = E1000_TSYNCRXCTL_ENABLED;
        u32 tsync_rx_cfg = 0;
@@ -588,9 +588,6 @@ int igb_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr)
        bool is_l2 = false;
        u32 regval;
 
-       if (copy_from_user(config, ifr->ifr_data, sizeof(*config)))
-               return -EFAULT;
-
        /* reserved for future extensions */
        if (config->flags)
                return -EINVAL;
@@ -725,7 +722,33 @@ int igb_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr)
        regval = rd32(E1000_RXSTMPL);
        regval = rd32(E1000_RXSTMPH);
 
-       return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ?
+       return 0;
+}
+
+/**
+ * igb_ptp_set_ts_config - set hardware time stamping config
+ * @netdev:
+ * @ifreq:
+ *
+ **/
+int igb_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr)
+{
+       struct igb_adapter *adapter = netdev_priv(netdev);
+       struct hwtstamp_config config;
+       int err;
+
+       if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
+               return -EFAULT;
+
+       err = igb_ptp_set_timestamp_mode(adapter, &config);
+       if (err)
+               return err;
+
+       /* save these settings for future reference */
+       memcpy(&adapter->tstamp_config, &config,
+              sizeof(adapter->tstamp_config));
+
+       return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
                -EFAULT : 0;
 }
 
@@ -745,7 +768,7 @@ void igb_ptp_init(struct igb_adapter *adapter)
                adapter->ptp_caps.adjtime = igb_ptp_adjtime_82576;
                adapter->ptp_caps.gettime = igb_ptp_gettime_82576;
                adapter->ptp_caps.settime = igb_ptp_settime_82576;
-               adapter->ptp_caps.enable = igb_ptp_enable;
+               adapter->ptp_caps.enable = igb_ptp_feature_enable;
                adapter->cc.read = igb_ptp_read_82576;
                adapter->cc.mask = CLOCKSOURCE_MASK(64);
                adapter->cc.mult = 1;
@@ -765,7 +788,7 @@ void igb_ptp_init(struct igb_adapter *adapter)
                adapter->ptp_caps.adjtime = igb_ptp_adjtime_82576;
                adapter->ptp_caps.gettime = igb_ptp_gettime_82576;
                adapter->ptp_caps.settime = igb_ptp_settime_82576;
-               adapter->ptp_caps.enable = igb_ptp_enable;
+               adapter->ptp_caps.enable = igb_ptp_feature_enable;
                adapter->cc.read = igb_ptp_read_82580;
                adapter->cc.mask = CLOCKSOURCE_MASK(IGB_NBITS_82580);
                adapter->cc.mult = 1;
@@ -784,7 +807,7 @@ void igb_ptp_init(struct igb_adapter *adapter)
                adapter->ptp_caps.adjtime = igb_ptp_adjtime_i210;
                adapter->ptp_caps.gettime = igb_ptp_gettime_i210;
                adapter->ptp_caps.settime = igb_ptp_settime_i210;
-               adapter->ptp_caps.enable = igb_ptp_enable;
+               adapter->ptp_caps.enable = igb_ptp_feature_enable;
                /* Enable the timer functions by clearing bit 31. */
                wr32(E1000_TSAUXC, 0x0);
                break;
@@ -820,6 +843,9 @@ void igb_ptp_init(struct igb_adapter *adapter)
                wr32(E1000_IMS, E1000_IMS_TS);
        }
 
+       adapter->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;
+       adapter->tstamp_config.tx_type = HWTSTAMP_TX_OFF;
+
        adapter->ptp_clock = ptp_clock_register(&adapter->ptp_caps,
                                                &adapter->pdev->dev);
        if (IS_ERR(adapter->ptp_clock)) {
@@ -884,7 +910,7 @@ void igb_ptp_reset(struct igb_adapter *adapter)
                return;
 
        /* reset the tstamp_config */
-       memset(&adapter->tstamp_config, 0, sizeof(adapter->tstamp_config));
+       igb_ptp_set_timestamp_mode(adapter, &adapter->tstamp_config);
 
        switch (adapter->hw.mac.type) {
        case e1000_82576:
index 90eef07943f4d50bbd027646e170abca5abfb1a3..2178f87e9f610f3a95222275978d2b68a9d7832d 100644 (file)
@@ -101,8 +101,8 @@ static int igbvf_get_settings(struct net_device *netdev,
                else
                        ecmd->duplex = DUPLEX_HALF;
        } else {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
 
        ecmd->autoneg = AUTONEG_DISABLE;
@@ -119,7 +119,6 @@ static int igbvf_set_settings(struct net_device *netdev,
 static void igbvf_get_pauseparam(struct net_device *netdev,
                                  struct ethtool_pauseparam *pause)
 {
-       return;
 }
 
 static int igbvf_set_pauseparam(struct net_device *netdev,
@@ -476,5 +475,5 @@ static const struct ethtool_ops igbvf_ethtool_ops = {
 
 void igbvf_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &igbvf_ethtool_ops);
+       netdev->ethtool_ops = &igbvf_ethtool_ops;
 }
index dbb7dd2f8e360e4d6c1013182e9d667a19d7fa56..b311e9e710d2636ddc3a17ad4d1cc10ba917cfb1 100644 (file)
@@ -107,8 +107,8 @@ ixgb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
                ethtool_cmd_speed_set(ecmd, SPEED_10000);
                ecmd->duplex = DUPLEX_FULL;
        } else {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
 
        ecmd->autoneg = AUTONEG_DISABLE;
@@ -656,5 +656,5 @@ static const struct ethtool_ops ixgb_ethtool_ops = {
 
 void ixgb_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &ixgb_ethtool_ops);
+       netdev->ethtool_ops = &ixgb_ethtool_ops;
 }
index c6c4ca7d68e61a7676cd214e85f7837ee1309d39..ac9f2148cdc5ca6d20e81a43170a9550ff6ffce6 100644 (file)
@@ -155,7 +155,6 @@ struct vf_data_storage {
 struct vf_macvlans {
        struct list_head l;
        int vf;
-       int rar_entry;
        bool free;
        bool is_macvlan;
        u8 vf_macvlan[ETH_ALEN];
@@ -363,7 +362,7 @@ struct ixgbe_ring_container {
        for (pos = (head).ring; pos != NULL; pos = pos->next)
 
 #define MAX_RX_PACKET_BUFFERS ((adapter->flags & IXGBE_FLAG_DCB_ENABLED) \
-                              ? 8 : 1)
+                             ? 8 : 1)
 #define MAX_TX_PACKET_BUFFERS MAX_RX_PACKET_BUFFERS
 
 /* MAX_Q_VECTORS of these are allocated,
@@ -613,6 +612,15 @@ static inline void ixgbe_write_tail(struct ixgbe_ring *ring, u32 value)
 #define MAX_MSIX_VECTORS_82598 18
 #define MAX_Q_VECTORS_82598 16
 
+struct ixgbe_mac_addr {
+       u8 addr[ETH_ALEN];
+       u16 queue;
+       u16 state; /* bitmask */
+};
+#define IXGBE_MAC_STATE_DEFAULT                0x1
+#define IXGBE_MAC_STATE_MODIFIED       0x2
+#define IXGBE_MAC_STATE_IN_USE         0x4
+
 #define MAX_Q_VECTORS MAX_Q_VECTORS_82599
 #define MAX_MSIX_COUNT MAX_MSIX_VECTORS_82599
 
@@ -785,6 +793,7 @@ struct ixgbe_adapter {
 
        u32 timer_event_accumulator;
        u32 vferr_refcount;
+       struct ixgbe_mac_addr *mac_table;
        struct kobject *info_kobj;
 #ifdef CONFIG_IXGBE_HWMON
        struct hwmon_buff *ixgbe_hwmon_buff;
@@ -863,6 +872,13 @@ 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);
+#ifdef CONFIG_PCI_IOV
+void ixgbe_full_sync_mac_table(struct ixgbe_adapter *adapter);
+#endif
+int ixgbe_add_mac_filter(struct ixgbe_adapter *adapter,
+                        u8 *addr, u16 queue);
+int ixgbe_del_mac_filter(struct ixgbe_adapter *adapter,
+                        u8 *addr, u16 queue);
 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 *);
@@ -941,6 +957,7 @@ static inline struct netdev_queue *txring_txq(const struct ixgbe_ring *ring)
 }
 
 void ixgbe_ptp_init(struct ixgbe_adapter *adapter);
+void ixgbe_ptp_suspend(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);
index 4c78ea8946c1b48838db6d7e9ce890089ff6ab59..15609331ec170ddc6bb9e62c4227107fc6b50cad 100644 (file)
 #define IXGBE_82598_RX_PB_SIZE  512
 
 static s32 ixgbe_setup_copper_link_82598(struct ixgbe_hw *hw,
-                                         ixgbe_link_speed speed,
-                                         bool autoneg_wait_to_complete);
+                                        ixgbe_link_speed speed,
+                                        bool autoneg_wait_to_complete);
 static s32 ixgbe_read_i2c_eeprom_82598(struct ixgbe_hw *hw, u8 byte_offset,
-                                       u8 *eeprom_data);
+                                      u8 *eeprom_data);
 
 /**
  *  ixgbe_set_pcie_completion_timeout - set pci-e completion timeout
@@ -140,7 +140,7 @@ static s32 ixgbe_init_phy_ops_82598(struct ixgbe_hw *hw)
                phy->ops.setup_link = &ixgbe_setup_phy_link_tnx;
                phy->ops.check_link = &ixgbe_check_phy_link_tnx;
                phy->ops.get_firmware_version =
-                            &ixgbe_get_phy_firmware_version_tnx;
+                            &ixgbe_get_phy_firmware_version_tnx;
                break;
        case ixgbe_phy_nl:
                phy->ops.reset = &ixgbe_reset_phy_nl;
@@ -156,8 +156,8 @@ static s32 ixgbe_init_phy_ops_82598(struct ixgbe_hw *hw)
 
                /* Check to see if SFP+ module is supported */
                ret_val = ixgbe_get_sfp_init_sequence_offsets(hw,
-                                                           &list_offset,
-                                                           &data_offset);
+                                                           &list_offset,
+                                                           &data_offset);
                if (ret_val != 0) {
                        ret_val = IXGBE_ERR_SFP_NOT_SUPPORTED;
                        goto out;
@@ -219,8 +219,8 @@ static s32 ixgbe_start_hw_82598(struct ixgbe_hw *hw)
  *  Determines the link capabilities by reading the AUTOC register.
  **/
 static s32 ixgbe_get_link_capabilities_82598(struct ixgbe_hw *hw,
-                                             ixgbe_link_speed *speed,
-                                             bool *autoneg)
+                                            ixgbe_link_speed *speed,
+                                            bool *autoneg)
 {
        s32 status = 0;
        u32 autoc = 0;
@@ -337,19 +337,25 @@ static s32 ixgbe_fc_enable_82598(struct ixgbe_hw *hw)
        int i;
        bool link_up;
 
-       /*
-        * Validate the water mark configuration for packet buffer 0.  Zero
-        * water marks indicate that the packet buffer was not configured
-        * and the watermarks for packet buffer 0 should always be configured.
-        */
-       if (!hw->fc.low_water ||
-           !hw->fc.high_water[0] ||
-           !hw->fc.pause_time) {
-               hw_dbg(hw, "Invalid water mark configuration\n");
+       /* Validate the water mark configuration */
+       if (!hw->fc.pause_time) {
                ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS;
                goto out;
        }
 
+       /* Low water mark of zero causes XOFF floods */
+       for (i = 0; i < MAX_TRAFFIC_CLASS; i++) {
+               if ((hw->fc.current_mode & ixgbe_fc_tx_pause) &&
+                   hw->fc.high_water[i]) {
+                       if (!hw->fc.low_water[i] ||
+                           hw->fc.low_water[i] >= hw->fc.high_water[i]) {
+                               hw_dbg(hw, "Invalid water mark configuration\n");
+                               ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS;
+                               goto out;
+                       }
+               }
+       }
+
        /*
         * On 82598 having Rx FC on causes resets while doing 1G
         * so if it's on turn it off once we know link_speed. For
@@ -432,12 +438,11 @@ static s32 ixgbe_fc_enable_82598(struct ixgbe_hw *hw)
        IXGBE_WRITE_REG(hw, IXGBE_FCTRL, fctrl_reg);
        IXGBE_WRITE_REG(hw, IXGBE_RMCS, rmcs_reg);
 
-       fcrtl = (hw->fc.low_water << 10) | IXGBE_FCRTL_XONE;
-
        /* Set up and enable Rx high/low water mark thresholds, enable XON. */
        for (i = 0; i < MAX_TRAFFIC_CLASS; i++) {
                if ((hw->fc.current_mode & ixgbe_fc_tx_pause) &&
                    hw->fc.high_water[i]) {
+                       fcrtl = (hw->fc.low_water[i] << 10) | IXGBE_FCRTL_XONE;
                        fcrth = (hw->fc.high_water[i] << 10) | IXGBE_FCRTH_FCEN;
                        IXGBE_WRITE_REG(hw, IXGBE_FCRTL(i), fcrtl);
                        IXGBE_WRITE_REG(hw, IXGBE_FCRTH(i), fcrth);
@@ -468,7 +473,7 @@ static s32 ixgbe_fc_enable_82598(struct ixgbe_hw *hw)
  *  Restarts the link.  Performs autonegotiation if needed.
  **/
 static s32 ixgbe_start_mac_link_82598(struct ixgbe_hw *hw,
-                                      bool autoneg_wait_to_complete)
+                                     bool autoneg_wait_to_complete)
 {
        u32 autoc_reg;
        u32 links_reg;
@@ -550,8 +555,8 @@ static s32 ixgbe_validate_link_ready(struct ixgbe_hw *hw)
  *  Reads the links register to determine if link is up and the current speed
  **/
 static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw,
-                                      ixgbe_link_speed *speed, bool *link_up,
-                                      bool link_up_wait_to_complete)
+                                     ixgbe_link_speed *speed, bool *link_up,
+                                     bool link_up_wait_to_complete)
 {
        u32 links_reg;
        u32 i;
@@ -567,7 +572,7 @@ static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw,
                hw->phy.ops.read_reg(hw, 0xC79F, MDIO_MMD_PMAPMD, &link_reg);
                hw->phy.ops.read_reg(hw, 0xC79F, MDIO_MMD_PMAPMD, &link_reg);
                hw->phy.ops.read_reg(hw, 0xC00C, MDIO_MMD_PMAPMD,
-                                    &adapt_comp_reg);
+                                    &adapt_comp_reg);
                if (link_up_wait_to_complete) {
                        for (i = 0; i < IXGBE_LINK_UP_TIME; i++) {
                                if ((link_reg & 1) &&
@@ -579,11 +584,11 @@ static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw,
                                }
                                msleep(100);
                                hw->phy.ops.read_reg(hw, 0xC79F,
-                                                    MDIO_MMD_PMAPMD,
-                                                    &link_reg);
+                                                    MDIO_MMD_PMAPMD,
+                                                    &link_reg);
                                hw->phy.ops.read_reg(hw, 0xC00C,
-                                                    MDIO_MMD_PMAPMD,
-                                                    &adapt_comp_reg);
+                                                    MDIO_MMD_PMAPMD,
+                                                    &adapt_comp_reg);
                        }
                } else {
                        if ((link_reg & 1) && ((adapt_comp_reg & 1) == 0))
@@ -656,7 +661,7 @@ static s32 ixgbe_setup_mac_link_82598(struct ixgbe_hw *hw,
 
        /* Set KX4/KX support according to speed requested */
        else if (link_mode == IXGBE_AUTOC_LMS_KX4_AN ||
-                link_mode == IXGBE_AUTOC_LMS_KX4_AN_1G_AN) {
+                link_mode == IXGBE_AUTOC_LMS_KX4_AN_1G_AN) {
                autoc &= ~IXGBE_AUTOC_KX4_KX_SUPP_MASK;
                if (speed & IXGBE_LINK_SPEED_10GB_FULL)
                        autoc |= IXGBE_AUTOC_KX4_SUPP;
@@ -689,14 +694,14 @@ static s32 ixgbe_setup_mac_link_82598(struct ixgbe_hw *hw,
  *  Sets the link speed in the AUTOC register in the MAC and restarts link.
  **/
 static s32 ixgbe_setup_copper_link_82598(struct ixgbe_hw *hw,
-                                               ixgbe_link_speed speed,
-                                               bool autoneg_wait_to_complete)
+                                              ixgbe_link_speed speed,
+                                              bool autoneg_wait_to_complete)
 {
        s32 status;
 
        /* Setup the PHY according to input speed */
        status = hw->phy.ops.setup_link_speed(hw, speed,
-                                             autoneg_wait_to_complete);
+                                             autoneg_wait_to_complete);
        /* Set up MAC */
        ixgbe_start_mac_link_82598(hw, autoneg_wait_to_complete);
 
@@ -735,28 +740,28 @@ static s32 ixgbe_reset_hw_82598(struct ixgbe_hw *hw)
        if (analog_val & IXGBE_ATLAS_PDN_TX_REG_EN) {
                /* Enable Tx Atlas so packets can be transmitted again */
                hw->mac.ops.read_analog_reg8(hw, IXGBE_ATLAS_PDN_LPBK,
-                                            &analog_val);
+                                            &analog_val);
                analog_val &= ~IXGBE_ATLAS_PDN_TX_REG_EN;
                hw->mac.ops.write_analog_reg8(hw, IXGBE_ATLAS_PDN_LPBK,
-                                             analog_val);
+                                             analog_val);
 
                hw->mac.ops.read_analog_reg8(hw, IXGBE_ATLAS_PDN_10G,
-                                            &analog_val);
+                                            &analog_val);
                analog_val &= ~IXGBE_ATLAS_PDN_TX_10G_QL_ALL;
                hw->mac.ops.write_analog_reg8(hw, IXGBE_ATLAS_PDN_10G,
-                                             analog_val);
+                                             analog_val);
 
                hw->mac.ops.read_analog_reg8(hw, IXGBE_ATLAS_PDN_1G,
-                                            &analog_val);
+                                            &analog_val);
                analog_val &= ~IXGBE_ATLAS_PDN_TX_1G_QL_ALL;
                hw->mac.ops.write_analog_reg8(hw, IXGBE_ATLAS_PDN_1G,
-                                             analog_val);
+                                             analog_val);
 
                hw->mac.ops.read_analog_reg8(hw, IXGBE_ATLAS_PDN_AN,
-                                            &analog_val);
+                                            &analog_val);
                analog_val &= ~IXGBE_ATLAS_PDN_TX_AN_QL_ALL;
                hw->mac.ops.write_analog_reg8(hw, IXGBE_ATLAS_PDN_AN,
-                                             analog_val);
+                                             analog_val);
        }
 
        /* Reset PHY */
@@ -955,7 +960,7 @@ static s32 ixgbe_clear_vfta_82598(struct ixgbe_hw *hw)
        for (vlanbyte = 0; vlanbyte < 4; vlanbyte++)
                for (offset = 0; offset < hw->mac.vft_size; offset++)
                        IXGBE_WRITE_REG(hw, IXGBE_VFTAVIND(vlanbyte, offset),
-                                       0);
+                                       0);
 
        return 0;
 }
@@ -973,7 +978,7 @@ static s32 ixgbe_read_analog_reg8_82598(struct ixgbe_hw *hw, u32 reg, u8 *val)
        u32  atlas_ctl;
 
        IXGBE_WRITE_REG(hw, IXGBE_ATLASCTL,
-                       IXGBE_ATLASCTL_WRITE_CMD | (reg << 8));
+                       IXGBE_ATLASCTL_WRITE_CMD | (reg << 8));
        IXGBE_WRITE_FLUSH(hw);
        udelay(10);
        atlas_ctl = IXGBE_READ_REG(hw, IXGBE_ATLASCTL);
@@ -1273,8 +1278,6 @@ static void ixgbe_set_rxpba_82598(struct ixgbe_hw *hw, int num_pb,
        /* Setup Tx packet buffer sizes */
        for (i = 0; i < IXGBE_MAX_PACKET_BUFFERS; i++)
                IXGBE_WRITE_REG(hw, IXGBE_TXPBSIZE(i), IXGBE_TXPBSIZE_40KB);
-
-       return;
 }
 
 static struct ixgbe_mac_operations mac_ops_82598 = {
index f32b3dd1ba8e18911fd58876ed20e494d92ae389..bc7c924240a52490d50531060783e0f13bc97f20 100644 (file)
@@ -48,17 +48,17 @@ static s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
                                                 ixgbe_link_speed speed,
                                                 bool autoneg_wait_to_complete);
 static s32 ixgbe_setup_mac_link_smartspeed(struct ixgbe_hw *hw,
-                                           ixgbe_link_speed speed,
-                                           bool autoneg_wait_to_complete);
+                                          ixgbe_link_speed speed,
+                                          bool autoneg_wait_to_complete);
 static void ixgbe_stop_mac_link_on_d3_82599(struct ixgbe_hw *hw);
 static s32 ixgbe_start_mac_link_82599(struct ixgbe_hw *hw,
                                      bool autoneg_wait_to_complete);
 static s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw,
-                               ixgbe_link_speed speed,
-                               bool autoneg_wait_to_complete);
+                              ixgbe_link_speed speed,
+                              bool autoneg_wait_to_complete);
 static s32 ixgbe_setup_copper_link_82599(struct ixgbe_hw *hw,
-                                         ixgbe_link_speed speed,
-                                         bool autoneg_wait_to_complete);
+                                        ixgbe_link_speed speed,
+                                        bool autoneg_wait_to_complete);
 static s32 ixgbe_verify_fw_version_82599(struct ixgbe_hw *hw);
 static s32 ixgbe_read_i2c_byte_82599(struct ixgbe_hw *hw, u8 byte_offset,
                                     u8 dev_addr, u8 *data);
@@ -96,9 +96,9 @@ static void ixgbe_init_mac_link_ops_82599(struct ixgbe_hw *hw)
        if ((mac->ops.get_media_type(hw) == ixgbe_media_type_fiber) &&
            !ixgbe_mng_enabled(hw)) {
                mac->ops.disable_tx_laser =
-                                      &ixgbe_disable_tx_laser_multispeed_fiber;
+                                      &ixgbe_disable_tx_laser_multispeed_fiber;
                mac->ops.enable_tx_laser =
-                                       &ixgbe_enable_tx_laser_multispeed_fiber;
+                                       &ixgbe_enable_tx_laser_multispeed_fiber;
                mac->ops.flap_tx_laser = &ixgbe_flap_tx_laser_multispeed_fiber;
        } else {
                mac->ops.disable_tx_laser = NULL;
@@ -132,13 +132,13 @@ static s32 ixgbe_setup_sfp_modules_82599(struct ixgbe_hw *hw)
                hw->phy.ops.reset = NULL;
 
                ret_val = ixgbe_get_sfp_init_sequence_offsets(hw, &list_offset,
-                                                             &data_offset);
+                                                             &data_offset);
                if (ret_val != 0)
                        goto setup_sfp_out;
 
                /* PHY config will finish before releasing the semaphore */
                ret_val = hw->mac.ops.acquire_swfw_sync(hw,
-                                                       IXGBE_GSSR_MAC_CSR_SM);
+                                                       IXGBE_GSSR_MAC_CSR_SM);
                if (ret_val != 0) {
                        ret_val = IXGBE_ERR_SWFW_SYNC;
                        goto setup_sfp_out;
@@ -334,7 +334,7 @@ static s32 ixgbe_init_phy_ops_82599(struct ixgbe_hw *hw)
                phy->ops.check_link = &ixgbe_check_phy_link_tnx;
                phy->ops.setup_link = &ixgbe_setup_phy_link_tnx;
                phy->ops.get_firmware_version =
-                            &ixgbe_get_phy_firmware_version_tnx;
+                            &ixgbe_get_phy_firmware_version_tnx;
                break;
        default:
                break;
@@ -352,7 +352,7 @@ static s32 ixgbe_init_phy_ops_82599(struct ixgbe_hw *hw)
  *  Determines the link capabilities by reading the AUTOC register.
  **/
 static s32 ixgbe_get_link_capabilities_82599(struct ixgbe_hw *hw,
-                                             ixgbe_link_speed *speed,
+                                            ixgbe_link_speed *speed,
                                             bool *autoneg)
 {
        s32 status = 0;
@@ -543,7 +543,7 @@ static void ixgbe_stop_mac_link_on_d3_82599(struct ixgbe_hw *hw)
  *  Restarts the link.  Performs autonegotiation if needed.
  **/
 static s32 ixgbe_start_mac_link_82599(struct ixgbe_hw *hw,
-                               bool autoneg_wait_to_complete)
+                              bool autoneg_wait_to_complete)
 {
        u32 autoc_reg;
        u32 links_reg;
@@ -672,8 +672,8 @@ static void ixgbe_flap_tx_laser_multispeed_fiber(struct ixgbe_hw *hw)
  *  Set the link speed in the AUTOC register and restarts link.
  **/
 static s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
-                                          ixgbe_link_speed speed,
-                                          bool autoneg_wait_to_complete)
+                                         ixgbe_link_speed speed,
+                                         bool autoneg_wait_to_complete)
 {
        s32 status = 0;
        ixgbe_link_speed link_speed = IXGBE_LINK_SPEED_UNKNOWN;
@@ -820,8 +820,8 @@ static s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
         */
        if (speedcnt > 1)
                status = ixgbe_setup_mac_link_multispeed_fiber(hw,
-                                                              highest_link_speed,
-                                                              autoneg_wait_to_complete);
+                                                              highest_link_speed,
+                                                              autoneg_wait_to_complete);
 
 out:
        /* Set autoneg_advertised value based on input link speed */
@@ -1009,8 +1009,8 @@ static s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw,
                if (speed & IXGBE_LINK_SPEED_1GB_FULL)
                        autoc |= IXGBE_AUTOC_KX_SUPP;
        } else if ((pma_pmd_1g == IXGBE_AUTOC_1G_SFI) &&
-                  (link_mode == IXGBE_AUTOC_LMS_1G_LINK_NO_AN ||
-                   link_mode == IXGBE_AUTOC_LMS_1G_AN)) {
+                  (link_mode == IXGBE_AUTOC_LMS_1G_LINK_NO_AN ||
+                   link_mode == IXGBE_AUTOC_LMS_1G_AN)) {
                /* Switch from 1G SFI to 10G SFI if requested */
                if ((speed == IXGBE_LINK_SPEED_10GB_FULL) &&
                    (pma_pmd_10g_serial == IXGBE_AUTOC2_10G_SFI)) {
@@ -1018,7 +1018,7 @@ static s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw,
                        autoc |= IXGBE_AUTOC_LMS_10G_SERIAL;
                }
        } else if ((pma_pmd_10g_serial == IXGBE_AUTOC2_10G_SFI) &&
-                  (link_mode == IXGBE_AUTOC_LMS_10G_SERIAL)) {
+                  (link_mode == IXGBE_AUTOC_LMS_10G_SERIAL)) {
                /* Switch from 10G SFI to 1G SFI if requested */
                if ((speed == IXGBE_LINK_SPEED_1GB_FULL) &&
                    (pma_pmd_1g == IXGBE_AUTOC_1G_SFI)) {
@@ -1051,7 +1051,7 @@ static s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw,
                                }
                                if (!(links_reg & IXGBE_LINKS_KX_AN_COMP)) {
                                        status =
-                                               IXGBE_ERR_AUTONEG_NOT_COMPLETE;
+                                               IXGBE_ERR_AUTONEG_NOT_COMPLETE;
                                        hw_dbg(hw, "Autoneg did not complete.\n");
                                }
                        }
@@ -1074,14 +1074,14 @@ static s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw,
  *  Restarts link on PHY and MAC based on settings passed in.
  **/
 static s32 ixgbe_setup_copper_link_82599(struct ixgbe_hw *hw,
-                                         ixgbe_link_speed speed,
-                                         bool autoneg_wait_to_complete)
+                                        ixgbe_link_speed speed,
+                                        bool autoneg_wait_to_complete)
 {
        s32 status;
 
        /* Setup the PHY according to input speed */
        status = hw->phy.ops.setup_link_speed(hw, speed,
-                                             autoneg_wait_to_complete);
+                                             autoneg_wait_to_complete);
        /* Set up MAC */
        ixgbe_start_mac_link_82599(hw, autoneg_wait_to_complete);
 
@@ -1224,7 +1224,7 @@ static s32 ixgbe_reset_hw_82599(struct ixgbe_hw *hw)
                    (hw->mac.orig_autoc2 & IXGBE_AUTOC2_UPPER_MASK)) {
                        autoc2 &= ~IXGBE_AUTOC2_UPPER_MASK;
                        autoc2 |= (hw->mac.orig_autoc2 &
-                                  IXGBE_AUTOC2_UPPER_MASK);
+                                  IXGBE_AUTOC2_UPPER_MASK);
                        IXGBE_WRITE_REG(hw, IXGBE_AUTOC2, autoc2);
                }
        }
@@ -1246,7 +1246,7 @@ static s32 ixgbe_reset_hw_82599(struct ixgbe_hw *hw)
        /* Add the SAN MAC address to the RAR only if it's a valid address */
        if (is_valid_ether_addr(hw->mac.san_addr)) {
                hw->mac.ops.set_rar(hw, hw->mac.num_rar_entries - 1,
-                                   hw->mac.san_addr, 0, IXGBE_RAH_AV);
+                                   hw->mac.san_addr, 0, IXGBE_RAH_AV);
 
                /* Save the SAN MAC RAR index */
                hw->mac.san_mac_rar_index = hw->mac.num_rar_entries - 1;
@@ -1257,7 +1257,7 @@ static s32 ixgbe_reset_hw_82599(struct ixgbe_hw *hw)
 
        /* Store the alternative WWNN/WWPN prefix */
        hw->mac.ops.get_wwn_prefix(hw, &hw->mac.wwnn_prefix,
-                                      &hw->mac.wwpn_prefix);
+                                      &hw->mac.wwpn_prefix);
 
 reset_hw_out:
        return status;
@@ -1271,6 +1271,7 @@ s32 ixgbe_reinit_fdir_tables_82599(struct ixgbe_hw *hw)
 {
        int i;
        u32 fdirctrl = IXGBE_READ_REG(hw, IXGBE_FDIRCTRL);
+
        fdirctrl &= ~IXGBE_FDIRCTRL_INIT_DONE;
 
        /*
@@ -1284,8 +1285,7 @@ s32 ixgbe_reinit_fdir_tables_82599(struct ixgbe_hw *hw)
                udelay(10);
        }
        if (i >= IXGBE_FDIRCMD_CMD_POLL) {
-               hw_dbg(hw, "Flow Director previous command isn't complete, "
-                      "aborting table re-initialization.\n");
+               hw_dbg(hw, "Flow Director previous command isn't complete, aborting table re-initialization.\n");
                return IXGBE_ERR_FDIR_REINIT_FAILED;
        }
 
@@ -1299,12 +1299,12 @@ s32 ixgbe_reinit_fdir_tables_82599(struct ixgbe_hw *hw)
         * - write 0 to bit 8 of FDIRCMD register
         */
        IXGBE_WRITE_REG(hw, IXGBE_FDIRCMD,
-                       (IXGBE_READ_REG(hw, IXGBE_FDIRCMD) |
-                        IXGBE_FDIRCMD_CLEARHT));
+                       (IXGBE_READ_REG(hw, IXGBE_FDIRCMD) |
+                        IXGBE_FDIRCMD_CLEARHT));
        IXGBE_WRITE_FLUSH(hw);
        IXGBE_WRITE_REG(hw, IXGBE_FDIRCMD,
-                       (IXGBE_READ_REG(hw, IXGBE_FDIRCMD) &
-                        ~IXGBE_FDIRCMD_CLEARHT));
+                       (IXGBE_READ_REG(hw, IXGBE_FDIRCMD) &
+                        ~IXGBE_FDIRCMD_CLEARHT));
        IXGBE_WRITE_FLUSH(hw);
        /*
         * Clear FDIR Hash register to clear any leftover hashes
@@ -1319,7 +1319,7 @@ s32 ixgbe_reinit_fdir_tables_82599(struct ixgbe_hw *hw)
        /* Poll init-done after we write FDIRCTRL register */
        for (i = 0; i < IXGBE_FDIR_INIT_DONE_POLL; i++) {
                if (IXGBE_READ_REG(hw, IXGBE_FDIRCTRL) &
-                                  IXGBE_FDIRCTRL_INIT_DONE)
+                                  IXGBE_FDIRCTRL_INIT_DONE)
                        break;
                usleep_range(1000, 2000);
        }
@@ -1368,7 +1368,7 @@ static void ixgbe_fdir_enable_82599(struct ixgbe_hw *hw, u32 fdirctrl)
        IXGBE_WRITE_FLUSH(hw);
        for (i = 0; i < IXGBE_FDIR_INIT_DONE_POLL; i++) {
                if (IXGBE_READ_REG(hw, IXGBE_FDIRCTRL) &
-                                  IXGBE_FDIRCTRL_INIT_DONE)
+                                  IXGBE_FDIRCTRL_INIT_DONE)
                        break;
                usleep_range(1000, 2000);
        }
@@ -1453,7 +1453,7 @@ do { \
                bucket_hash ^= hi_hash_dword >> n; \
        else if (IXGBE_ATR_SIGNATURE_HASH_KEY & (0x01 << (n + 16))) \
                sig_hash ^= hi_hash_dword << (16 - n); \
-} while (0);
+} while (0)
 
 /**
  *  ixgbe_atr_compute_sig_hash_82599 - Compute the signature hash
@@ -1529,9 +1529,9 @@ static u32 ixgbe_atr_compute_sig_hash_82599(union ixgbe_atr_hash_dword input,
  *  @queue: queue index to direct traffic to
  **/
 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)
+                                         union ixgbe_atr_hash_dword input,
+                                         union ixgbe_atr_hash_dword common,
+                                         u8 queue)
 {
        u64  fdirhashcmd;
        u32  fdircmd;
@@ -1555,7 +1555,7 @@ s32 ixgbe_fdir_add_signature_filter_82599(struct ixgbe_hw *hw,
 
        /* configure FDIRCMD register */
        fdircmd = IXGBE_FDIRCMD_CMD_ADD_FLOW | IXGBE_FDIRCMD_FILTER_UPDATE |
-                 IXGBE_FDIRCMD_LAST | IXGBE_FDIRCMD_QUEUE_EN;
+                 IXGBE_FDIRCMD_LAST | IXGBE_FDIRCMD_QUEUE_EN;
        fdircmd |= input.formatted.flow_type << IXGBE_FDIRCMD_FLOW_TYPE_SHIFT;
        fdircmd |= (u32)queue << IXGBE_FDIRCMD_RX_QUEUE_SHIFT;
 
@@ -1579,7 +1579,7 @@ do { \
                bucket_hash ^= lo_hash_dword >> n; \
        if (IXGBE_ATR_BUCKET_HASH_KEY & (0x01 << (n + 16))) \
                bucket_hash ^= hi_hash_dword >> n; \
-} while (0);
+} while (0)
 
 /**
  *  ixgbe_atr_compute_perfect_hash_82599 - Compute the perfect filter hash
@@ -1651,6 +1651,7 @@ void ixgbe_atr_compute_perfect_hash_82599(union ixgbe_atr_input *input,
 static u32 ixgbe_get_fdirtcpm_82599(union ixgbe_atr_input *input_mask)
 {
        u32 mask = ntohs(input_mask->formatted.dst_port);
+
        mask <<= IXGBE_FDIRTCPM_DPORTM_SHIFT;
        mask |= ntohs(input_mask->formatted.src_port);
        mask = ((mask & 0x55555555) << 1) | ((mask & 0xAAAAAAAA) >> 1);
@@ -1885,7 +1886,7 @@ static s32 ixgbe_read_analog_reg8_82599(struct ixgbe_hw *hw, u32 reg, u8 *val)
        u32  core_ctl;
 
        IXGBE_WRITE_REG(hw, IXGBE_CORECTL, IXGBE_CORECTL_WRITE_CMD |
-                       (reg << 8));
+                       (reg << 8));
        IXGBE_WRITE_FLUSH(hw);
        udelay(10);
        core_ctl = IXGBE_READ_REG(hw, IXGBE_CORECTL);
index 981b8a7b100df5360954d056ff2c5c4d25246d73..4e5385a2a4658c8c5f0dc9ea4ee279d08693351d 100644 (file)
@@ -41,7 +41,7 @@ static void ixgbe_release_eeprom_semaphore(struct ixgbe_hw *hw);
 static s32 ixgbe_ready_eeprom(struct ixgbe_hw *hw);
 static void ixgbe_standby_eeprom(struct ixgbe_hw *hw);
 static void ixgbe_shift_out_eeprom_bits(struct ixgbe_hw *hw, u16 data,
-                                        u16 count);
+                                       u16 count);
 static u16 ixgbe_shift_in_eeprom_bits(struct ixgbe_hw *hw, u16 count);
 static void ixgbe_raise_eeprom_clk(struct ixgbe_hw *hw, u32 *eec);
 static void ixgbe_lower_eeprom_clk(struct ixgbe_hw *hw, u32 *eec);
@@ -271,6 +271,7 @@ static s32 ixgbe_setup_fc(struct ixgbe_hw *hw)
  **/
 s32 ixgbe_start_hw_generic(struct ixgbe_hw *hw)
 {
+       s32 ret_val;
        u32 ctrl_ext;
 
        /* Set the media type */
@@ -292,12 +293,15 @@ s32 ixgbe_start_hw_generic(struct ixgbe_hw *hw)
        IXGBE_WRITE_FLUSH(hw);
 
        /* Setup flow control */
-       ixgbe_setup_fc(hw);
+       ret_val = ixgbe_setup_fc(hw);
+       if (!ret_val)
+               goto out;
 
        /* Clear adapter stopped flag */
        hw->adapter_stopped = false;
 
-       return 0;
+out:
+       return ret_val;
 }
 
 /**
@@ -481,7 +485,7 @@ s32 ixgbe_clear_hw_cntrs_generic(struct ixgbe_hw *hw)
  *  Reads the part number string from the EEPROM.
  **/
 s32 ixgbe_read_pba_string_generic(struct ixgbe_hw *hw, u8 *pba_num,
-                                  u32 pba_num_size)
+                                 u32 pba_num_size)
 {
        s32 ret_val;
        u16 data;
@@ -814,9 +818,8 @@ s32 ixgbe_init_eeprom_params_generic(struct ixgbe_hw *hw)
                        eeprom->address_bits = 16;
                else
                        eeprom->address_bits = 8;
-               hw_dbg(hw, "Eeprom params: type = %d, size = %d, address bits: "
-                         "%d\n", eeprom->type, eeprom->word_size,
-                         eeprom->address_bits);
+               hw_dbg(hw, "Eeprom params: type = %d, size = %d, address bits: %d\n",
+                      eeprom->type, eeprom->word_size, eeprom->address_bits);
        }
 
        return 0;
@@ -1388,8 +1391,7 @@ static s32 ixgbe_get_eeprom_semaphore(struct ixgbe_hw *hw)
        }
 
        if (i == timeout) {
-               hw_dbg(hw, "Driver can't access the Eeprom - SMBI Semaphore "
-                      "not granted.\n");
+               hw_dbg(hw, "Driver can't access the Eeprom - SMBI Semaphore not granted.\n");
                /*
                 * this release is particularly important because our attempts
                 * above to get the semaphore may have succeeded, and if there
@@ -1434,14 +1436,12 @@ static s32 ixgbe_get_eeprom_semaphore(struct ixgbe_hw *hw)
                 * was not granted because we don't have access to the EEPROM
                 */
                if (i >= timeout) {
-                       hw_dbg(hw, "SWESMBI Software EEPROM semaphore "
-                              "not granted.\n");
+                       hw_dbg(hw, "SWESMBI Software EEPROM semaphore not granted.\n");
                        ixgbe_release_eeprom_semaphore(hw);
                        status = IXGBE_ERR_EEPROM;
                }
        } else {
-               hw_dbg(hw, "Software semaphore SMBI between device drivers "
-                      "not granted.\n");
+               hw_dbg(hw, "Software semaphore SMBI between device drivers not granted.\n");
        }
 
        return status;
@@ -1483,7 +1483,7 @@ static s32 ixgbe_ready_eeprom(struct ixgbe_hw *hw)
         */
        for (i = 0; i < IXGBE_EEPROM_MAX_RETRY_SPI; i += 5) {
                ixgbe_shift_out_eeprom_bits(hw, IXGBE_EEPROM_RDSR_OPCODE_SPI,
-                                           IXGBE_EEPROM_OPCODE_BITS);
+                                           IXGBE_EEPROM_OPCODE_BITS);
                spi_stat_reg = (u8)ixgbe_shift_in_eeprom_bits(hw, 8);
                if (!(spi_stat_reg & IXGBE_EEPROM_STATUS_RDY_SPI))
                        break;
@@ -1532,7 +1532,7 @@ static void ixgbe_standby_eeprom(struct ixgbe_hw *hw)
  *  @count: number of bits to shift out
  **/
 static void ixgbe_shift_out_eeprom_bits(struct ixgbe_hw *hw, u16 data,
-                                        u16 count)
+                                       u16 count)
 {
        u32 eec;
        u32 mask;
@@ -1736,7 +1736,7 @@ u16 ixgbe_calc_eeprom_checksum_generic(struct ixgbe_hw *hw)
  *  caller does not need checksum_val, the value can be NULL.
  **/
 s32 ixgbe_validate_eeprom_checksum_generic(struct ixgbe_hw *hw,
-                                           u16 *checksum_val)
+                                          u16 *checksum_val)
 {
        s32 status;
        u16 checksum;
@@ -1809,7 +1809,7 @@ s32 ixgbe_update_eeprom_checksum_generic(struct ixgbe_hw *hw)
  *  Puts an ethernet address into a receive address register.
  **/
 s32 ixgbe_set_rar_generic(struct ixgbe_hw *hw, u32 index, u8 *addr, u32 vmdq,
-                          u32 enable_addr)
+                         u32 enable_addr)
 {
        u32 rar_low, rar_high;
        u32 rar_entries = hw->mac.num_rar_entries;
@@ -2053,7 +2053,7 @@ s32 ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw,
 
        if (hw->addr_ctrl.mta_in_use > 0)
                IXGBE_WRITE_REG(hw, IXGBE_MCSTCTRL,
-                               IXGBE_MCSTCTRL_MFE | hw->mac.mc_filter_type);
+                               IXGBE_MCSTCTRL_MFE | hw->mac.mc_filter_type);
 
        hw_dbg(hw, "ixgbe_update_mc_addr_list_generic Complete\n");
        return 0;
@@ -2071,7 +2071,7 @@ s32 ixgbe_enable_mc_generic(struct ixgbe_hw *hw)
 
        if (a->mta_in_use > 0)
                IXGBE_WRITE_REG(hw, IXGBE_MCSTCTRL, IXGBE_MCSTCTRL_MFE |
-                               hw->mac.mc_filter_type);
+                               hw->mac.mc_filter_type);
 
        return 0;
 }
@@ -2106,19 +2106,25 @@ s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw)
        u32 fcrtl, fcrth;
        int i;
 
-       /*
-        * Validate the water mark configuration for packet buffer 0.  Zero
-        * water marks indicate that the packet buffer was not configured
-        * and the watermarks for packet buffer 0 should always be configured.
-        */
-       if (!hw->fc.low_water ||
-           !hw->fc.high_water[0] ||
-           !hw->fc.pause_time) {
-               hw_dbg(hw, "Invalid water mark configuration\n");
+       /* Validate the water mark configuration. */
+       if (!hw->fc.pause_time) {
                ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS;
                goto out;
        }
 
+       /* Low water mark of zero causes XOFF floods */
+       for (i = 0; i < MAX_TRAFFIC_CLASS; i++) {
+               if ((hw->fc.current_mode & ixgbe_fc_tx_pause) &&
+                   hw->fc.high_water[i]) {
+                       if (!hw->fc.low_water[i] ||
+                           hw->fc.low_water[i] >= hw->fc.high_water[i]) {
+                               hw_dbg(hw, "Invalid water mark configuration\n");
+                               ret_val = IXGBE_ERR_INVALID_LINK_SETTINGS;
+                               goto out;
+                       }
+               }
+       }
+
        /* Negotiate the fc mode to use */
        ixgbe_fc_autoneg(hw);
 
@@ -2181,12 +2187,11 @@ s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw)
        IXGBE_WRITE_REG(hw, IXGBE_MFLCN, mflcn_reg);
        IXGBE_WRITE_REG(hw, IXGBE_FCCFG, fccfg_reg);
 
-       fcrtl = (hw->fc.low_water << 10) | IXGBE_FCRTL_XONE;
-
        /* Set up and enable Rx high/low water mark thresholds, enable XON. */
        for (i = 0; i < MAX_TRAFFIC_CLASS; i++) {
                if ((hw->fc.current_mode & ixgbe_fc_tx_pause) &&
                    hw->fc.high_water[i]) {
+                       fcrtl = (hw->fc.low_water[i] << 10) | IXGBE_FCRTL_XONE;
                        IXGBE_WRITE_REG(hw, IXGBE_FCRTL_82599(i), fcrtl);
                        fcrth = (hw->fc.high_water[i] << 10) | IXGBE_FCRTH_FCEN;
                } else {
@@ -2654,8 +2659,7 @@ s32 ixgbe_disable_rx_buff_generic(struct ixgbe_hw *hw)
 
        /* For informational purposes only */
        if (i >= IXGBE_MAX_SECRX_POLL)
-               hw_dbg(hw, "Rx unit being enabled before security "
-                      "path fully disabled.  Continuing with init.\n");
+               hw_dbg(hw, "Rx unit being enabled before security path fully disabled. Continuing with init.\n");
 
        return 0;
 
@@ -2782,7 +2786,7 @@ s32 ixgbe_blink_led_stop_generic(struct ixgbe_hw *hw, u32 index)
  *  get and set mac_addr routines.
  **/
 static s32 ixgbe_get_san_mac_addr_offset(struct ixgbe_hw *hw,
-                                        u16 *san_mac_offset)
+                                       u16 *san_mac_offset)
 {
        s32 ret_val;
 
@@ -2828,7 +2832,7 @@ s32 ixgbe_get_san_mac_addr_generic(struct ixgbe_hw *hw, u8 *san_mac_addr)
        hw->mac.ops.set_lan_id(hw);
        /* apply the port offset to the address offset */
        (hw->bus.func) ? (san_mac_offset += IXGBE_SAN_MAC_ADDR_PORT1_OFFSET) :
-                        (san_mac_offset += IXGBE_SAN_MAC_ADDR_PORT0_OFFSET);
+                        (san_mac_offset += IXGBE_SAN_MAC_ADDR_PORT0_OFFSET);
        for (i = 0; i < 3; i++) {
                ret_val = hw->eeprom.ops.read(hw, san_mac_offset,
                                              &san_mac_data);
@@ -3068,7 +3072,7 @@ static s32 ixgbe_find_vlvf_slot(struct ixgbe_hw *hw, u32 vlan)
  *  Turn on/off specified VLAN in the VLAN filter table.
  **/
 s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind,
-                           bool vlan_on)
+                          bool vlan_on)
 {
        s32 regindex;
        u32 bitindex;
@@ -3190,9 +3194,9 @@ s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind,
                                 * Ignore it. */
                                vfta_changed = false;
                        }
-               }
-               else
+               } else {
                        IXGBE_WRITE_REG(hw, IXGBE_VLVF(vlvf_index), 0);
+               }
        }
 
        if (vfta_changed)
@@ -3292,7 +3296,7 @@ s32 ixgbe_check_mac_link_generic(struct ixgbe_hw *hw, ixgbe_link_speed *speed,
  *  block to check the support for the alternative WWNN/WWPN prefix support.
  **/
 s32 ixgbe_get_wwn_prefix_generic(struct ixgbe_hw *hw, u16 *wwnn_prefix,
-                                        u16 *wwpn_prefix)
+                                       u16 *wwpn_prefix)
 {
        u16 offset, caps;
        u16 alt_san_mac_blk_offset;
index f12c40fb5537a18604ff030f4adc4287946dbff4..2ae5d4b8fc93e318bba749c6042affb8eb1616a7 100644 (file)
@@ -39,7 +39,7 @@ s32 ixgbe_start_hw_generic(struct ixgbe_hw *hw);
 s32 ixgbe_start_hw_gen2(struct ixgbe_hw *hw);
 s32 ixgbe_clear_hw_cntrs_generic(struct ixgbe_hw *hw);
 s32 ixgbe_read_pba_string_generic(struct ixgbe_hw *hw, u8 *pba_num,
-                                  u32 pba_num_size);
+                                 u32 pba_num_size);
 s32 ixgbe_get_mac_addr_generic(struct ixgbe_hw *hw, u8 *mac_addr);
 enum ixgbe_bus_width ixgbe_convert_bus_width(u16 link_status);
 enum ixgbe_bus_speed ixgbe_convert_bus_speed(u16 link_status);
@@ -61,16 +61,16 @@ s32 ixgbe_write_eewr_generic(struct ixgbe_hw *hw, u16 offset, u16 data);
 s32 ixgbe_write_eewr_buffer_generic(struct ixgbe_hw *hw, u16 offset,
                                    u16 words, u16 *data);
 s32 ixgbe_read_eeprom_bit_bang_generic(struct ixgbe_hw *hw, u16 offset,
-                                       u16 *data);
+                                      u16 *data);
 s32 ixgbe_read_eeprom_buffer_bit_bang_generic(struct ixgbe_hw *hw, u16 offset,
                                              u16 words, u16 *data);
 u16 ixgbe_calc_eeprom_checksum_generic(struct ixgbe_hw *hw);
 s32 ixgbe_validate_eeprom_checksum_generic(struct ixgbe_hw *hw,
-                                           u16 *checksum_val);
+                                          u16 *checksum_val);
 s32 ixgbe_update_eeprom_checksum_generic(struct ixgbe_hw *hw);
 
 s32 ixgbe_set_rar_generic(struct ixgbe_hw *hw, u32 index, u8 *addr, u32 vmdq,
-                          u32 enable_addr);
+                         u32 enable_addr);
 s32 ixgbe_clear_rar_generic(struct ixgbe_hw *hw, u32 index);
 s32 ixgbe_init_rx_addrs_generic(struct ixgbe_hw *hw);
 s32 ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw,
@@ -92,13 +92,13 @@ s32 ixgbe_set_vmdq_san_mac_generic(struct ixgbe_hw *hw, u32 vmdq);
 s32 ixgbe_clear_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq);
 s32 ixgbe_init_uta_tables_generic(struct ixgbe_hw *hw);
 s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan,
-                           u32 vind, bool vlan_on);
+                          u32 vind, bool vlan_on);
 s32 ixgbe_clear_vfta_generic(struct ixgbe_hw *hw);
 s32 ixgbe_check_mac_link_generic(struct ixgbe_hw *hw,
-                                 ixgbe_link_speed *speed,
-                                 bool *link_up, bool link_up_wait_to_complete);
+                                ixgbe_link_speed *speed,
+                                bool *link_up, bool link_up_wait_to_complete);
 s32 ixgbe_get_wwn_prefix_generic(struct ixgbe_hw *hw, u16 *wwnn_prefix,
-                                 u16 *wwpn_prefix);
+                                u16 *wwpn_prefix);
 
 s32 prot_autoc_read_generic(struct ixgbe_hw *hw, bool *, u32 *reg_val);
 s32 prot_autoc_write_generic(struct ixgbe_hw *hw, u32 reg_val, bool locked);
@@ -141,8 +141,6 @@ static inline bool ixgbe_removed(void __iomem *addr)
        return unlikely(!addr);
 }
 
-void ixgbe_check_remove(struct ixgbe_hw *hw, u32 reg);
-
 static inline void ixgbe_write_reg(struct ixgbe_hw *hw, u32 reg, u32 value)
 {
        u8 __iomem *reg_addr = ACCESS_ONCE(hw->hw_addr);
@@ -172,18 +170,7 @@ static inline void ixgbe_write_reg64(struct ixgbe_hw *hw, u32 reg, u64 value)
 }
 #define IXGBE_WRITE_REG64(a, reg, value) ixgbe_write_reg64((a), (reg), (value))
 
-static inline u32 ixgbe_read_reg(struct ixgbe_hw *hw, u32 reg)
-{
-       u8 __iomem *reg_addr = ACCESS_ONCE(hw->hw_addr);
-       u32 value;
-
-       if (ixgbe_removed(reg_addr))
-               return IXGBE_FAILED_READ_REG;
-       value = readl(reg_addr + reg);
-       if (unlikely(value == IXGBE_FAILED_READ_REG))
-               ixgbe_check_remove(hw, reg);
-       return value;
-}
+u32 ixgbe_read_reg(struct ixgbe_hw *hw, u32 reg);
 #define IXGBE_READ_REG(a, reg) ixgbe_read_reg((a), (reg))
 
 #define IXGBE_WRITE_REG_ARRAY(a, reg, offset, value) \
index e055e000131bdcf0c32280270361c957d0a4d8a0..a689ee0d4bedc2d837d0765b62ab2dd0ffe1d120 100644 (file)
@@ -267,7 +267,7 @@ void ixgbe_dcb_unpack_map(struct ixgbe_dcb_config *cfg, int direction, u8 *map)
  * Configure dcb settings and enable dcb mode.
  */
 s32 ixgbe_dcb_hw_config(struct ixgbe_hw *hw,
-                        struct ixgbe_dcb_config *dcb_config)
+                       struct ixgbe_dcb_config *dcb_config)
 {
        s32 ret = 0;
        u8 pfc_en;
@@ -389,7 +389,6 @@ static void ixgbe_dcb_read_rtrup2tc_82599(struct ixgbe_hw *hw, u8 *map)
        for (i = 0; i < MAX_USER_PRIORITY; i++)
                map[i] = IXGBE_RTRUP2TC_UP_MASK &
                        (reg >> (i * IXGBE_RTRUP2TC_UP_SHIFT));
-       return;
 }
 
 void ixgbe_dcb_read_rtrup2tc(struct ixgbe_hw *hw, u8 *map)
index 7a77f37a7cbcbd6b7b5b87dd78e9e50b677e778c..d3ba63f9ad3712fcf82d1bafc3408070112d1aa0 100644 (file)
@@ -208,7 +208,6 @@ s32 ixgbe_dcb_config_pfc_82598(struct ixgbe_hw *hw, u8 pfc_en)
 
        IXGBE_WRITE_REG(hw, IXGBE_FCTRL, reg);
 
-       fcrtl = (hw->fc.low_water << 10) | IXGBE_FCRTL_XONE;
        /* Configure PFC Tx thresholds per TC */
        for (i = 0; i < MAX_TRAFFIC_CLASS; i++) {
                if (!(pfc_en & (1 << i))) {
@@ -217,6 +216,7 @@ s32 ixgbe_dcb_config_pfc_82598(struct ixgbe_hw *hw, u8 pfc_en)
                        continue;
                }
 
+               fcrtl = (hw->fc.low_water[i] << 10) | IXGBE_FCRTL_XONE;
                reg = (hw->fc.high_water[i] << 10) | IXGBE_FCRTH_FCEN;
                IXGBE_WRITE_REG(hw, IXGBE_FCRTL(i), fcrtl);
                IXGBE_WRITE_REG(hw, IXGBE_FCRTH(i), reg);
index bdb99b3b0f30f4ee2253e81bc72e84fb6a0d1e30..3b932fe64ab66c916f86f4184f45d626cc687cb1 100644 (file)
@@ -242,7 +242,6 @@ s32 ixgbe_dcb_config_pfc_82599(struct ixgbe_hw *hw, u8 pfc_en, u8 *prio_tc)
                        max_tc = prio_tc[i];
        }
 
-       fcrtl = (hw->fc.low_water << 10) | IXGBE_FCRTL_XONE;
 
        /* Configure PFC Tx thresholds per TC */
        for (i = 0; i <= max_tc; i++) {
@@ -257,6 +256,7 @@ s32 ixgbe_dcb_config_pfc_82599(struct ixgbe_hw *hw, u8 pfc_en, u8 *prio_tc)
 
                if (enabled) {
                        reg = (hw->fc.high_water[i] << 10) | IXGBE_FCRTH_FCEN;
+                       fcrtl = (hw->fc.low_water[i] << 10) | IXGBE_FCRTL_XONE;
                        IXGBE_WRITE_REG(hw, IXGBE_FCRTL_82599(i), fcrtl);
                } else {
                        reg = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(i)) - 32;
index d5a1e3db0774a5580b8191126e682c33191f7fa6..90c370230e2001250752725ee7d10c0ed71de341 100644 (file)
 
 /* DCB register definitions */
 #define IXGBE_RTTDCS_TDPAC      0x00000001 /* 0 Round Robin,
-                                            * 1 WSP - Weighted Strict Priority
-                                            */
+                                           * 1 WSP - Weighted Strict Priority
+                                           */
 #define IXGBE_RTTDCS_VMPAC      0x00000002 /* 0 Round Robin,
-                                            * 1 WRR - Weighted Round Robin
-                                            */
+                                           * 1 WRR - Weighted Round Robin
+                                           */
 #define IXGBE_RTTDCS_TDRM       0x00000010 /* Transmit Recycle Mode */
 #define IXGBE_RTTDCS_ARBDIS     0x00000040 /* DCB arbiter disable */
 #define IXGBE_RTTDCS_BDPM       0x00400000 /* Bypass Data Pipe - must clear! */
 #define IXGBE_RTTDCS_BPBFSM     0x00800000 /* Bypass PB Free Space - must
-                                             * clear!
-                                             */
+                                            * clear!
+                                            */
 #define IXGBE_RTTDCS_SPEED_CHG  0x80000000 /* Link speed change */
 
 /* Receive UP2TC mapping */
 #define IXGBE_RTRPT4C_LSP       0x80000000 /* LSP enable bit */
 
 #define IXGBE_RDRXCTL_MPBEN     0x00000010 /* DMA config for multiple packet
-                                            * buffers enable
-                                            */
+                                           * buffers enable
+                                           */
 #define IXGBE_RDRXCTL_MCEN      0x00000040 /* DMA config for multiple cores
-                                            * (RSS) enable
-                                            */
+                                           * (RSS) enable
+                                           */
 
 /* RTRPCS Bit Masks */
 #define IXGBE_RTRPCS_RRM        0x00000002 /* Receive Recycle Mode enable */
@@ -81,8 +81,8 @@
 
 /* RTTPCS Bit Masks */
 #define IXGBE_RTTPCS_TPPAC      0x00000020 /* 0 Round Robin,
-                                            * 1 SP - Strict Priority
-                                            */
+                                           * 1 SP - Strict Priority
+                                           */
 #define IXGBE_RTTPCS_ARBDIS     0x00000040 /* Arbiter disable */
 #define IXGBE_RTTPCS_TPRM       0x00000100 /* Transmit Recycle Mode enable */
 #define IXGBE_RTTPCS_ARBD_SHIFT 22
index edd89a1ef27f67f0f40d0cefba9ebed3e39f3185..5172b6b12c097679b9f9b532869b500ea6cb4124 100644 (file)
@@ -192,8 +192,8 @@ static void ixgbe_dcbnl_get_perm_hw_addr(struct net_device *netdev,
 }
 
 static void ixgbe_dcbnl_set_pg_tc_cfg_tx(struct net_device *netdev, int tc,
-                                         u8 prio, u8 bwg_id, u8 bw_pct,
-                                         u8 up_map)
+                                        u8 prio, u8 bwg_id, u8 bw_pct,
+                                        u8 up_map)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
@@ -210,7 +210,7 @@ static void ixgbe_dcbnl_set_pg_tc_cfg_tx(struct net_device *netdev, int tc,
 }
 
 static void ixgbe_dcbnl_set_pg_bwg_cfg_tx(struct net_device *netdev, int bwg_id,
-                                          u8 bw_pct)
+                                         u8 bw_pct)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
@@ -218,8 +218,8 @@ static void ixgbe_dcbnl_set_pg_bwg_cfg_tx(struct net_device *netdev, int bwg_id,
 }
 
 static void ixgbe_dcbnl_set_pg_tc_cfg_rx(struct net_device *netdev, int tc,
-                                         u8 prio, u8 bwg_id, u8 bw_pct,
-                                         u8 up_map)
+                                        u8 prio, u8 bwg_id, u8 bw_pct,
+                                        u8 up_map)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
@@ -236,7 +236,7 @@ static void ixgbe_dcbnl_set_pg_tc_cfg_rx(struct net_device *netdev, int tc,
 }
 
 static void ixgbe_dcbnl_set_pg_bwg_cfg_rx(struct net_device *netdev, int bwg_id,
-                                          u8 bw_pct)
+                                         u8 bw_pct)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
@@ -244,8 +244,8 @@ static void ixgbe_dcbnl_set_pg_bwg_cfg_rx(struct net_device *netdev, int bwg_id,
 }
 
 static void ixgbe_dcbnl_get_pg_tc_cfg_tx(struct net_device *netdev, int tc,
-                                         u8 *prio, u8 *bwg_id, u8 *bw_pct,
-                                         u8 *up_map)
+                                        u8 *prio, u8 *bwg_id, u8 *bw_pct,
+                                        u8 *up_map)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
@@ -256,7 +256,7 @@ static void ixgbe_dcbnl_get_pg_tc_cfg_tx(struct net_device *netdev, int tc,
 }
 
 static void ixgbe_dcbnl_get_pg_bwg_cfg_tx(struct net_device *netdev, int bwg_id,
-                                          u8 *bw_pct)
+                                         u8 *bw_pct)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
@@ -264,8 +264,8 @@ static void ixgbe_dcbnl_get_pg_bwg_cfg_tx(struct net_device *netdev, int bwg_id,
 }
 
 static void ixgbe_dcbnl_get_pg_tc_cfg_rx(struct net_device *netdev, int tc,
-                                         u8 *prio, u8 *bwg_id, u8 *bw_pct,
-                                         u8 *up_map)
+                                        u8 *prio, u8 *bwg_id, u8 *bw_pct,
+                                        u8 *up_map)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
@@ -276,7 +276,7 @@ static void ixgbe_dcbnl_get_pg_tc_cfg_rx(struct net_device *netdev, int tc,
 }
 
 static void ixgbe_dcbnl_get_pg_bwg_cfg_rx(struct net_device *netdev, int bwg_id,
-                                          u8 *bw_pct)
+                                         u8 *bw_pct)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
@@ -284,7 +284,7 @@ static void ixgbe_dcbnl_get_pg_bwg_cfg_rx(struct net_device *netdev, int bwg_id,
 }
 
 static void ixgbe_dcbnl_set_pfc_cfg(struct net_device *netdev, int priority,
-                                    u8 setting)
+                                   u8 setting)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
@@ -295,7 +295,7 @@ static void ixgbe_dcbnl_set_pfc_cfg(struct net_device *netdev, int priority,
 }
 
 static void ixgbe_dcbnl_get_pfc_cfg(struct net_device *netdev, int priority,
-                                    u8 *setting)
+                                   u8 *setting)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
index 472b0f450bf90d4dc23fe5995d28d0d80c2d940e..5e2c1e35e517f915d2e6f43a5e7c1368d2f81bfe 100644 (file)
@@ -253,8 +253,7 @@ void ixgbe_dbg_adapter_init(struct ixgbe_adapter *adapter)
  **/
 void ixgbe_dbg_adapter_exit(struct ixgbe_adapter *adapter)
 {
-       if (adapter->ixgbe_dbg_adapter)
-               debugfs_remove_recursive(adapter->ixgbe_dbg_adapter);
+       debugfs_remove_recursive(adapter->ixgbe_dbg_adapter);
        adapter->ixgbe_dbg_adapter = NULL;
 }
 
index 6c55c14d082aa6285f43941e5d96ae86acdbb21b..a452730a327864111d72d13679fc47c2be870b7c 100644 (file)
@@ -141,8 +141,8 @@ static const struct ixgbe_stats ixgbe_gstrings_stats[] = {
                         sizeof(((struct ixgbe_adapter *)0)->stats.pxofftxc)) \
                        / sizeof(u64))
 #define IXGBE_STATS_LEN (IXGBE_GLOBAL_STATS_LEN + \
-                         IXGBE_PB_STATS_LEN + \
-                         IXGBE_QUEUE_STATS_LEN)
+                        IXGBE_PB_STATS_LEN + \
+                        IXGBE_QUEUE_STATS_LEN)
 
 static const char ixgbe_gstrings_test[][ETH_GSTRING_LEN] = {
        "Register test  (offline)", "Eeprom test    (offline)",
@@ -152,7 +152,7 @@ static const char ixgbe_gstrings_test[][ETH_GSTRING_LEN] = {
 #define IXGBE_TEST_LEN sizeof(ixgbe_gstrings_test) / ETH_GSTRING_LEN
 
 static int ixgbe_get_settings(struct net_device *netdev,
-                              struct ethtool_cmd *ecmd)
+                             struct ethtool_cmd *ecmd)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_hw *hw = &adapter->hw;
@@ -161,13 +161,6 @@ static int ixgbe_get_settings(struct net_device *netdev,
        bool autoneg = false;
        bool link_up;
 
-       /* SFP type is needed for get_link_capabilities */
-       if (hw->phy.media_type & (ixgbe_media_type_fiber |
-                                 ixgbe_media_type_fiber_qsfp)) {
-               if (hw->phy.sfp_type == ixgbe_sfp_type_not_present)
-                               hw->phy.ops.identify_sfp(hw);
-       }
-
        hw->mac.ops.get_link_capabilities(hw, &supported_link, &autoneg);
 
        /* set the supported link speeds */
@@ -303,15 +296,15 @@ static int ixgbe_get_settings(struct net_device *netdev,
                }
                ecmd->duplex = DUPLEX_FULL;
        } else {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
 
        return 0;
 }
 
 static int ixgbe_set_settings(struct net_device *netdev,
-                              struct ethtool_cmd *ecmd)
+                             struct ethtool_cmd *ecmd)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_hw *hw = &adapter->hw;
@@ -368,7 +361,7 @@ static int ixgbe_set_settings(struct net_device *netdev,
 }
 
 static void ixgbe_get_pauseparam(struct net_device *netdev,
-                                 struct ethtool_pauseparam *pause)
+                                struct ethtool_pauseparam *pause)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_hw *hw = &adapter->hw;
@@ -390,7 +383,7 @@ static void ixgbe_get_pauseparam(struct net_device *netdev,
 }
 
 static int ixgbe_set_pauseparam(struct net_device *netdev,
-                                struct ethtool_pauseparam *pause)
+                               struct ethtool_pauseparam *pause)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_hw *hw = &adapter->hw;
@@ -450,7 +443,7 @@ static int ixgbe_get_regs_len(struct net_device *netdev)
 #define IXGBE_GET_STAT(_A_, _R_) _A_->stats._R_
 
 static void ixgbe_get_regs(struct net_device *netdev,
-                           struct ethtool_regs *regs, void *p)
+                          struct ethtool_regs *regs, void *p)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_hw *hw = &adapter->hw;
@@ -812,7 +805,7 @@ static int ixgbe_get_eeprom_len(struct net_device *netdev)
 }
 
 static int ixgbe_get_eeprom(struct net_device *netdev,
-                            struct ethtool_eeprom *eeprom, u8 *bytes)
+                           struct ethtool_eeprom *eeprom, u8 *bytes)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_hw *hw = &adapter->hw;
@@ -918,7 +911,7 @@ static int ixgbe_set_eeprom(struct net_device *netdev,
 }
 
 static void ixgbe_get_drvinfo(struct net_device *netdev,
-                              struct ethtool_drvinfo *drvinfo)
+                             struct ethtool_drvinfo *drvinfo)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        u32 nvm_track_id;
@@ -940,7 +933,7 @@ static void ixgbe_get_drvinfo(struct net_device *netdev,
 }
 
 static void ixgbe_get_ringparam(struct net_device *netdev,
-                                struct ethtool_ringparam *ring)
+                               struct ethtool_ringparam *ring)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_ring *tx_ring = adapter->tx_ring[0];
@@ -953,7 +946,7 @@ static void ixgbe_get_ringparam(struct net_device *netdev,
 }
 
 static int ixgbe_set_ringparam(struct net_device *netdev,
-                               struct ethtool_ringparam *ring)
+                              struct ethtool_ringparam *ring)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_ring *temp_ring;
@@ -1082,7 +1075,7 @@ static int ixgbe_get_sset_count(struct net_device *netdev, int sset)
 }
 
 static void ixgbe_get_ethtool_stats(struct net_device *netdev,
-                                    struct ethtool_stats *stats, u64 *data)
+                                   struct ethtool_stats *stats, u64 *data)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct rtnl_link_stats64 temp;
@@ -1110,7 +1103,7 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev,
                }
 
                data[i] = (ixgbe_gstrings_stats[i].sizeof_stat ==
-                          sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+                          sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
        }
        for (j = 0; j < netdev->num_tx_queues; j++) {
                ring = adapter->tx_ring[j];
@@ -1180,7 +1173,7 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev,
 }
 
 static void ixgbe_get_strings(struct net_device *netdev, u32 stringset,
-                              u8 *data)
+                             u8 *data)
 {
        char *p = (char *)data;
        int i;
@@ -1357,8 +1350,7 @@ static bool reg_pattern_test(struct ixgbe_adapter *adapter, u64 *data, int reg,
                ixgbe_write_reg(&adapter->hw, reg, test_pattern[pat] & write);
                val = ixgbe_read_reg(&adapter->hw, reg);
                if (val != (test_pattern[pat] & write & mask)) {
-                       e_err(drv, "pattern test reg %04X failed: got "
-                             "0x%08X expected 0x%08X\n",
+                       e_err(drv, "pattern test reg %04X failed: got 0x%08X expected 0x%08X\n",
                              reg, val, (test_pattern[pat] & write & mask));
                        *data = reg;
                        ixgbe_write_reg(&adapter->hw, reg, before);
@@ -1382,8 +1374,8 @@ static bool reg_set_and_check(struct ixgbe_adapter *adapter, u64 *data, int reg,
        ixgbe_write_reg(&adapter->hw, reg, write & mask);
        val = ixgbe_read_reg(&adapter->hw, reg);
        if ((write & mask) != (val & mask)) {
-               e_err(drv, "set/check reg %04X test failed: got 0x%08X "
-                     "expected 0x%08X\n", reg, (val & mask), (write & mask));
+               e_err(drv, "set/check reg %04X test failed: got 0x%08X expected 0x%08X\n",
+                     reg, (val & mask), (write & mask));
                *data = reg;
                ixgbe_write_reg(&adapter->hw, reg, before);
                return true;
@@ -1430,8 +1422,8 @@ static int ixgbe_reg_test(struct ixgbe_adapter *adapter, u64 *data)
        ixgbe_write_reg(&adapter->hw, IXGBE_STATUS, toggle);
        after = ixgbe_read_reg(&adapter->hw, IXGBE_STATUS) & toggle;
        if (value != after) {
-               e_err(drv, "failed STATUS register test got: 0x%08X "
-                     "expected: 0x%08X\n", after, value);
+               e_err(drv, "failed STATUS register test got: 0x%08X expected: 0x%08X\n",
+                     after, value);
                *data = 1;
                return 1;
        }
@@ -1533,10 +1525,10 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data)
                        return -1;
                }
        } else if (!request_irq(irq, ixgbe_test_intr, IRQF_PROBE_SHARED,
-                               netdev->name, netdev)) {
+                               netdev->name, netdev)) {
                shared_int = false;
        } else if (request_irq(irq, ixgbe_test_intr, IRQF_SHARED,
-                              netdev->name, netdev)) {
+                              netdev->name, netdev)) {
                *data = 1;
                return -1;
        }
@@ -1563,9 +1555,9 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data)
                         */
                        adapter->test_icr = 0;
                        IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC,
-                                       ~mask & 0x00007FFF);
+                                       ~mask & 0x00007FFF);
                        IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS,
-                                       ~mask & 0x00007FFF);
+                                       ~mask & 0x00007FFF);
                        IXGBE_WRITE_FLUSH(&adapter->hw);
                        usleep_range(10000, 20000);
 
@@ -1587,7 +1579,7 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data)
                IXGBE_WRITE_FLUSH(&adapter->hw);
                usleep_range(10000, 20000);
 
-               if (!(adapter->test_icr &mask)) {
+               if (!(adapter->test_icr & mask)) {
                        *data = 4;
                        break;
                }
@@ -1602,9 +1594,9 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data)
                         */
                        adapter->test_icr = 0;
                        IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC,
-                                       ~mask & 0x00007FFF);
+                                       ~mask & 0x00007FFF);
                        IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS,
-                                       ~mask & 0x00007FFF);
+                                       ~mask & 0x00007FFF);
                        IXGBE_WRITE_FLUSH(&adapter->hw);
                        usleep_range(10000, 20000);
 
@@ -1964,7 +1956,7 @@ static int ixgbe_loopback_test(struct ixgbe_adapter *adapter, u64 *data)
 }
 
 static void ixgbe_diag_test(struct net_device *netdev,
-                            struct ethtool_test *eth_test, u64 *data)
+                           struct ethtool_test *eth_test, u64 *data)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        bool if_running = netif_running(netdev);
@@ -1987,10 +1979,7 @@ static void ixgbe_diag_test(struct net_device *netdev,
                        int i;
                        for (i = 0; i < adapter->num_vfs; i++) {
                                if (adapter->vfinfo[i].clear_to_send) {
-                                       netdev_warn(netdev, "%s",
-                                                   "offline diagnostic is not "
-                                                   "supported when VFs are "
-                                                   "present\n");
+                                       netdev_warn(netdev, "offline diagnostic is not supported when VFs are present\n");
                                        data[0] = 1;
                                        data[1] = 1;
                                        data[2] = 1;
@@ -2037,8 +2026,7 @@ static void ixgbe_diag_test(struct net_device *netdev,
                 * loopback diagnostic. */
                if (adapter->flags & (IXGBE_FLAG_SRIOV_ENABLED |
                                      IXGBE_FLAG_VMDQ_ENABLED)) {
-                       e_info(hw, "Skip MAC loopback diagnostic in VT "
-                              "mode\n");
+                       e_info(hw, "Skip MAC loopback diagnostic in VT mode\n");
                        data[3] = 0;
                        goto skip_loopback;
                }
@@ -2078,7 +2066,7 @@ static void ixgbe_diag_test(struct net_device *netdev,
 }
 
 static int ixgbe_wol_exclusion(struct ixgbe_adapter *adapter,
-                               struct ethtool_wolinfo *wol)
+                              struct ethtool_wolinfo *wol)
 {
        struct ixgbe_hw *hw = &adapter->hw;
        int retval = 0;
@@ -2094,12 +2082,12 @@ static int ixgbe_wol_exclusion(struct ixgbe_adapter *adapter,
 }
 
 static void ixgbe_get_wol(struct net_device *netdev,
-                          struct ethtool_wolinfo *wol)
+                         struct ethtool_wolinfo *wol)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
        wol->supported = WAKE_UCAST | WAKE_MCAST |
-                        WAKE_BCAST | WAKE_MAGIC;
+                        WAKE_BCAST | WAKE_MAGIC;
        wol->wolopts = 0;
 
        if (ixgbe_wol_exclusion(adapter, wol) ||
@@ -2181,7 +2169,7 @@ static int ixgbe_set_phys_id(struct net_device *netdev,
 }
 
 static int ixgbe_get_coalesce(struct net_device *netdev,
-                              struct ethtool_coalesce *ec)
+                             struct ethtool_coalesce *ec)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
 
@@ -2222,8 +2210,7 @@ static bool ixgbe_update_rsc(struct ixgbe_adapter *adapter)
            adapter->rx_itr_setting > IXGBE_MIN_RSC_ITR) {
                if (!(adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED)) {
                        adapter->flags2 |= IXGBE_FLAG2_RSC_ENABLED;
-                       e_info(probe, "rx-usecs value high enough "
-                                     "to re-enable RSC\n");
+                       e_info(probe, "rx-usecs value high enough to re-enable RSC\n");
                        return true;
                }
        /* if interrupt rate is too high then disable RSC */
@@ -2236,7 +2223,7 @@ static bool ixgbe_update_rsc(struct ixgbe_adapter *adapter)
 }
 
 static int ixgbe_set_coalesce(struct net_device *netdev,
-                              struct ethtool_coalesce *ec)
+                             struct ethtool_coalesce *ec)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_q_vector *q_vector;
@@ -2421,9 +2408,11 @@ static int ixgbe_get_rss_hash_opts(struct ixgbe_adapter *adapter,
        switch (cmd->flow_type) {
        case TCP_V4_FLOW:
                cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+               /* fallthrough */
        case UDP_V4_FLOW:
                if (adapter->flags2 & IXGBE_FLAG2_RSS_FIELD_IPV4_UDP)
                        cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+               /* fallthrough */
        case SCTP_V4_FLOW:
        case AH_ESP_V4_FLOW:
        case AH_V4_FLOW:
@@ -2433,9 +2422,11 @@ static int ixgbe_get_rss_hash_opts(struct ixgbe_adapter *adapter,
                break;
        case TCP_V6_FLOW:
                cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+               /* fallthrough */
        case UDP_V6_FLOW:
                if (adapter->flags2 & IXGBE_FLAG2_RSS_FIELD_IPV6_UDP)
                        cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+               /* fallthrough */
        case SCTP_V6_FLOW:
        case AH_ESP_V6_FLOW:
        case AH_V6_FLOW:
@@ -2787,8 +2778,7 @@ static int ixgbe_set_rss_hash_opt(struct ixgbe_adapter *adapter,
 
                if ((flags2 & UDP_RSS_FLAGS) &&
                    !(adapter->flags2 & UDP_RSS_FLAGS))
-                       e_warn(drv, "enabling UDP RSS: fragmented packets"
-                              " may arrive out of order to the stack above\n");
+                       e_warn(drv, "enabling UDP RSS: fragmented packets may arrive out of order to the stack above\n");
 
                adapter->flags2 = flags2;
 
@@ -3099,5 +3089,5 @@ static const struct ethtool_ops ixgbe_ethtool_ops = {
 
 void ixgbe_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &ixgbe_ethtool_ops);
+       netdev->ethtool_ops = &ixgbe_ethtool_ops;
 }
index b16cc786750dec8bb7bf29749db331a31f028ec9..0772b7730fce92de4e2ff54d44f6528397c2b3a9 100644 (file)
@@ -81,9 +81,7 @@ struct ixgbe_fcoe {
        void *extra_ddp_buffer;
        dma_addr_t extra_ddp_buffer_dma;
        unsigned long mode;
-#ifdef CONFIG_IXGBE_DCB
        u8 up;
-#endif
 };
 
 #endif /* _IXGBE_FCOE_H */
index 2067d392cc3d33850254e9a2d719a2db6491b6f5..2d9451e3968624db0b527dd00e9a106adfae16be 100644 (file)
@@ -1113,8 +1113,8 @@ static void ixgbe_set_interrupt_capability(struct ixgbe_adapter *adapter)
        err = pci_enable_msi(adapter->pdev);
        if (err) {
                netif_printk(adapter, hw, KERN_DEBUG, adapter->netdev,
-                            "Unable to allocate MSI interrupt, "
-                            "falling back to legacy.  Error: %d\n", err);
+                            "Unable to allocate MSI interrupt, falling back to legacy.  Error: %d\n",
+                            err);
                return;
        }
        adapter->flags |= IXGBE_FLAG_MSI_ENABLED;
index c047c3ef8d71e8d03c7860f6beb03023a9bf5d3b..f5aa3311ea286bbd45d56933ad015e33f48d69fb 100644 (file)
@@ -301,7 +301,7 @@ static void ixgbe_remove_adapter(struct ixgbe_hw *hw)
                ixgbe_service_event_schedule(adapter);
 }
 
-void ixgbe_check_remove(struct ixgbe_hw *hw, u32 reg)
+static void ixgbe_check_remove(struct ixgbe_hw *hw, u32 reg)
 {
        u32 value;
 
@@ -320,6 +320,32 @@ void ixgbe_check_remove(struct ixgbe_hw *hw, u32 reg)
                ixgbe_remove_adapter(hw);
 }
 
+/**
+ * ixgbe_read_reg - Read from device register
+ * @hw: hw specific details
+ * @reg: offset of register to read
+ *
+ * Returns : value read or IXGBE_FAILED_READ_REG if removed
+ *
+ * This function is used to read device registers. It checks for device
+ * removal by confirming any read that returns all ones by checking the
+ * status register value for all ones. This function avoids reading from
+ * the hardware if a removal was previously detected in which case it
+ * returns IXGBE_FAILED_READ_REG (all ones).
+ */
+u32 ixgbe_read_reg(struct ixgbe_hw *hw, u32 reg)
+{
+       u8 __iomem *reg_addr = ACCESS_ONCE(hw->hw_addr);
+       u32 value;
+
+       if (ixgbe_removed(reg_addr))
+               return IXGBE_FAILED_READ_REG;
+       value = readl(reg_addr + reg);
+       if (unlikely(value == IXGBE_FAILED_READ_REG))
+               ixgbe_check_remove(hw, reg);
+       return value;
+}
+
 static bool ixgbe_check_cfg_remove(struct ixgbe_hw *hw, struct pci_dev *pdev)
 {
        u16 value;
@@ -3742,35 +3768,6 @@ static int ixgbe_vlan_rx_kill_vid(struct net_device *netdev,
        return 0;
 }
 
-/**
- * ixgbe_vlan_filter_disable - helper to disable hw vlan filtering
- * @adapter: driver data
- */
-static void ixgbe_vlan_filter_disable(struct ixgbe_adapter *adapter)
-{
-       struct ixgbe_hw *hw = &adapter->hw;
-       u32 vlnctrl;
-
-       vlnctrl = IXGBE_READ_REG(hw, IXGBE_VLNCTRL);
-       vlnctrl &= ~(IXGBE_VLNCTRL_VFE | IXGBE_VLNCTRL_CFIEN);
-       IXGBE_WRITE_REG(hw, IXGBE_VLNCTRL, vlnctrl);
-}
-
-/**
- * ixgbe_vlan_filter_enable - helper to enable hw vlan filtering
- * @adapter: driver data
- */
-static void ixgbe_vlan_filter_enable(struct ixgbe_adapter *adapter)
-{
-       struct ixgbe_hw *hw = &adapter->hw;
-       u32 vlnctrl;
-
-       vlnctrl = IXGBE_READ_REG(hw, IXGBE_VLNCTRL);
-       vlnctrl |= IXGBE_VLNCTRL_VFE;
-       vlnctrl &= ~IXGBE_VLNCTRL_CFIEN;
-       IXGBE_WRITE_REG(hw, IXGBE_VLNCTRL, vlnctrl);
-}
-
 /**
  * ixgbe_vlan_strip_disable - helper to disable hw vlan stripping
  * @adapter: driver data
@@ -3849,6 +3846,158 @@ static void ixgbe_restore_vlan(struct ixgbe_adapter *adapter)
                ixgbe_vlan_rx_add_vid(adapter->netdev, htons(ETH_P_8021Q), vid);
 }
 
+/**
+ * ixgbe_write_mc_addr_list - write multicast addresses to MTA
+ * @netdev: network interface device structure
+ *
+ * Writes multicast address list to the MTA hash table.
+ * Returns: -ENOMEM on failure
+ *                0 on no addresses written
+ *                X on writing X addresses to MTA
+ **/
+static int ixgbe_write_mc_addr_list(struct net_device *netdev)
+{
+       struct ixgbe_adapter *adapter = netdev_priv(netdev);
+       struct ixgbe_hw *hw = &adapter->hw;
+
+       if (!netif_running(netdev))
+               return 0;
+
+       if (hw->mac.ops.update_mc_addr_list)
+               hw->mac.ops.update_mc_addr_list(hw, netdev);
+       else
+               return -ENOMEM;
+
+#ifdef CONFIG_PCI_IOV
+       ixgbe_restore_vf_multicasts(adapter);
+#endif
+
+       return netdev_mc_count(netdev);
+}
+
+#ifdef CONFIG_PCI_IOV
+void ixgbe_full_sync_mac_table(struct ixgbe_adapter *adapter)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+       int i;
+       for (i = 0; i < hw->mac.num_rar_entries; i++) {
+               if (adapter->mac_table[i].state & IXGBE_MAC_STATE_IN_USE)
+                       hw->mac.ops.set_rar(hw, i, adapter->mac_table[i].addr,
+                                           adapter->mac_table[i].queue,
+                                           IXGBE_RAH_AV);
+               else
+                       hw->mac.ops.clear_rar(hw, i);
+
+               adapter->mac_table[i].state &= ~(IXGBE_MAC_STATE_MODIFIED);
+       }
+}
+#endif
+
+static void ixgbe_sync_mac_table(struct ixgbe_adapter *adapter)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+       int i;
+       for (i = 0; i < hw->mac.num_rar_entries; i++) {
+               if (adapter->mac_table[i].state & IXGBE_MAC_STATE_MODIFIED) {
+                       if (adapter->mac_table[i].state &
+                           IXGBE_MAC_STATE_IN_USE)
+                               hw->mac.ops.set_rar(hw, i,
+                                               adapter->mac_table[i].addr,
+                                               adapter->mac_table[i].queue,
+                                               IXGBE_RAH_AV);
+                       else
+                               hw->mac.ops.clear_rar(hw, i);
+
+                       adapter->mac_table[i].state &=
+                                               ~(IXGBE_MAC_STATE_MODIFIED);
+               }
+       }
+}
+
+static void ixgbe_flush_sw_mac_table(struct ixgbe_adapter *adapter)
+{
+       int i;
+       struct ixgbe_hw *hw = &adapter->hw;
+
+       for (i = 0; i < hw->mac.num_rar_entries; i++) {
+               adapter->mac_table[i].state |= IXGBE_MAC_STATE_MODIFIED;
+               adapter->mac_table[i].state &= ~IXGBE_MAC_STATE_IN_USE;
+               memset(adapter->mac_table[i].addr, 0, ETH_ALEN);
+               adapter->mac_table[i].queue = 0;
+       }
+       ixgbe_sync_mac_table(adapter);
+}
+
+static int ixgbe_available_rars(struct ixgbe_adapter *adapter)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+       int i, count = 0;
+
+       for (i = 0; i < hw->mac.num_rar_entries; i++) {
+               if (adapter->mac_table[i].state == 0)
+                       count++;
+       }
+       return count;
+}
+
+/* this function destroys the first RAR entry */
+static void ixgbe_mac_set_default_filter(struct ixgbe_adapter *adapter,
+                                        u8 *addr)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+
+       memcpy(&adapter->mac_table[0].addr, addr, ETH_ALEN);
+       adapter->mac_table[0].queue = VMDQ_P(0);
+       adapter->mac_table[0].state = (IXGBE_MAC_STATE_DEFAULT |
+                                      IXGBE_MAC_STATE_IN_USE);
+       hw->mac.ops.set_rar(hw, 0, adapter->mac_table[0].addr,
+                           adapter->mac_table[0].queue,
+                           IXGBE_RAH_AV);
+}
+
+int ixgbe_add_mac_filter(struct ixgbe_adapter *adapter, u8 *addr, u16 queue)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+       int i;
+
+       if (is_zero_ether_addr(addr))
+               return -EINVAL;
+
+       for (i = 0; i < hw->mac.num_rar_entries; i++) {
+               if (adapter->mac_table[i].state & IXGBE_MAC_STATE_IN_USE)
+                       continue;
+               adapter->mac_table[i].state |= (IXGBE_MAC_STATE_MODIFIED |
+                                               IXGBE_MAC_STATE_IN_USE);
+               ether_addr_copy(adapter->mac_table[i].addr, addr);
+               adapter->mac_table[i].queue = queue;
+               ixgbe_sync_mac_table(adapter);
+               return i;
+       }
+       return -ENOMEM;
+}
+
+int ixgbe_del_mac_filter(struct ixgbe_adapter *adapter, u8 *addr, u16 queue)
+{
+       /* search table for addr, if found, set to 0 and sync */
+       int i;
+       struct ixgbe_hw *hw = &adapter->hw;
+
+       if (is_zero_ether_addr(addr))
+               return -EINVAL;
+
+       for (i = 0; i < hw->mac.num_rar_entries; i++) {
+               if (ether_addr_equal(addr, adapter->mac_table[i].addr) &&
+                   adapter->mac_table[i].queue == queue) {
+                       adapter->mac_table[i].state |= IXGBE_MAC_STATE_MODIFIED;
+                       adapter->mac_table[i].state &= ~IXGBE_MAC_STATE_IN_USE;
+                       memset(adapter->mac_table[i].addr, 0, ETH_ALEN);
+                       adapter->mac_table[i].queue = 0;
+                       ixgbe_sync_mac_table(adapter);
+                       return 0;
+               }
+       }
+       return -ENOMEM;
+}
 /**
  * ixgbe_write_uc_addr_list - write unicast addresses to RAR table
  * @netdev: network interface device structure
@@ -3858,39 +4007,23 @@ static void ixgbe_restore_vlan(struct ixgbe_adapter *adapter)
  *                0 on no addresses written
  *                X on writing X addresses to the RAR table
  **/
-static int ixgbe_write_uc_addr_list(struct net_device *netdev)
+static int ixgbe_write_uc_addr_list(struct net_device *netdev, int vfn)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
-       struct ixgbe_hw *hw = &adapter->hw;
-       unsigned int rar_entries = hw->mac.num_rar_entries - 1;
        int count = 0;
 
-       /* 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;
-
        /* return ENOMEM indicating insufficient memory for addresses */
-       if (netdev_uc_count(netdev) > rar_entries)
+       if (netdev_uc_count(netdev) > ixgbe_available_rars(adapter))
                return -ENOMEM;
 
        if (!netdev_uc_empty(netdev)) {
                struct netdev_hw_addr *ha;
-               /* return error if we do not support writing to RAR table */
-               if (!hw->mac.ops.set_rar)
-                       return -ENOMEM;
-
                netdev_for_each_uc_addr(ha, netdev) {
-                       if (!rar_entries)
-                               break;
-                       hw->mac.ops.set_rar(hw, rar_entries--, ha->addr,
-                                           VMDQ_P(0), IXGBE_RAH_AV);
+                       ixgbe_del_mac_filter(adapter, ha->addr, vfn);
+                       ixgbe_add_mac_filter(adapter, ha->addr, vfn);
                        count++;
                }
        }
-       /* write the addresses in reverse order to avoid write combining */
-       for (; rar_entries > 0 ; rar_entries--)
-               hw->mac.ops.clear_rar(hw, rar_entries);
-
        return count;
 }
 
@@ -3908,11 +4041,12 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_hw *hw = &adapter->hw;
        u32 fctrl, vmolr = IXGBE_VMOLR_BAM | IXGBE_VMOLR_AUPE;
+       u32 vlnctrl;
        int count;
 
        /* Check for Promiscuous and All Multicast modes */
-
        fctrl = IXGBE_READ_REG(hw, IXGBE_FCTRL);
+       vlnctrl = IXGBE_READ_REG(hw, IXGBE_VLNCTRL);
 
        /* set all bits that we expect to always be set */
        fctrl &= ~IXGBE_FCTRL_SBP; /* disable store-bad-packets */
@@ -3922,26 +4056,24 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
 
        /* clear the bits we are changing the status of */
        fctrl &= ~(IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE);
-
+       vlnctrl &= ~(IXGBE_VLNCTRL_VFE | IXGBE_VLNCTRL_CFIEN);
        if (netdev->flags & IFF_PROMISC) {
                hw->addr_ctrl.user_set_promisc = true;
                fctrl |= (IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE);
-               vmolr |= (IXGBE_VMOLR_ROPE | IXGBE_VMOLR_MPE);
+               vmolr |= IXGBE_VMOLR_MPE;
                /* Only disable hardware filter vlans in promiscuous mode
                 * if SR-IOV and VMDQ are disabled - otherwise ensure
                 * that hardware VLAN filters remain enabled.
                 */
                if (!(adapter->flags & (IXGBE_FLAG_VMDQ_ENABLED |
                                        IXGBE_FLAG_SRIOV_ENABLED)))
-                       ixgbe_vlan_filter_disable(adapter);
-               else
-                       ixgbe_vlan_filter_enable(adapter);
+                       vlnctrl |= (IXGBE_VLNCTRL_VFE | IXGBE_VLNCTRL_CFIEN);
        } else {
                if (netdev->flags & IFF_ALLMULTI) {
                        fctrl |= IXGBE_FCTRL_MPE;
                        vmolr |= IXGBE_VMOLR_MPE;
                }
-               ixgbe_vlan_filter_enable(adapter);
+               vlnctrl |= IXGBE_VLNCTRL_VFE;
                hw->addr_ctrl.user_set_promisc = false;
        }
 
@@ -3950,7 +4082,7 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
         * sufficient space to store all the addresses then enable
         * unicast promiscuous mode
         */
-       count = ixgbe_write_uc_addr_list(netdev);
+       count = ixgbe_write_uc_addr_list(netdev, VMDQ_P(0));
        if (count < 0) {
                fctrl |= IXGBE_FCTRL_UPE;
                vmolr |= IXGBE_VMOLR_ROPE;
@@ -3960,11 +4092,13 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
         * 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);
+       count = ixgbe_write_mc_addr_list(netdev);
+       if (count < 0) {
+               fctrl |= IXGBE_FCTRL_MPE;
+               vmolr |= IXGBE_VMOLR_MPE;
+       } else if (count) {
+               vmolr |= IXGBE_VMOLR_ROMPE;
+       }
 
        if (hw->mac.type != ixgbe_mac_82598EB) {
                vmolr |= IXGBE_READ_REG(hw, IXGBE_VMOLR(VMDQ_P(0))) &
@@ -3985,6 +4119,7 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
                /* NOTE:  VLAN filtering is disabled by setting PROMISC */
        }
 
+       IXGBE_WRITE_REG(hw, IXGBE_VLNCTRL, vlnctrl);
        IXGBE_WRITE_REG(hw, IXGBE_FCTRL, fctrl);
 
        if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX)
@@ -4101,8 +4236,8 @@ static int ixgbe_hpbthresh(struct ixgbe_adapter *adapter, int pb)
            (tc < IXGBE_FCOE_JUMBO_FRAME_SIZE) &&
            (pb == ixgbe_fcoe_get_tc(adapter)))
                tc = IXGBE_FCOE_JUMBO_FRAME_SIZE;
-
 #endif
+
        /* Calculate delay value for device */
        switch (hw->mac.type) {
        case ixgbe_mac_X540:
@@ -4143,7 +4278,7 @@ static int ixgbe_hpbthresh(struct ixgbe_adapter *adapter, int pb)
  * @adapter: board private structure to calculate for
  * @pb: packet buffer to calculate
  */
-static int ixgbe_lpbthresh(struct ixgbe_adapter *adapter)
+static int ixgbe_lpbthresh(struct ixgbe_adapter *adapter, int pb)
 {
        struct ixgbe_hw *hw = &adapter->hw;
        struct net_device *dev = adapter->netdev;
@@ -4153,6 +4288,14 @@ static int ixgbe_lpbthresh(struct ixgbe_adapter *adapter)
        /* Calculate max LAN frame size */
        tc = dev->mtu + ETH_HLEN + ETH_FCS_LEN;
 
+#ifdef IXGBE_FCOE
+       /* FCoE traffic class uses FCOE jumbo frames */
+       if ((dev->features & NETIF_F_FCOE_MTU) &&
+           (tc < IXGBE_FCOE_JUMBO_FRAME_SIZE) &&
+           (pb == netdev_get_prio_tc_map(dev, adapter->fcoe.up)))
+               tc = IXGBE_FCOE_JUMBO_FRAME_SIZE;
+#endif
+
        /* Calculate delay value for device */
        switch (hw->mac.type) {
        case ixgbe_mac_X540:
@@ -4179,15 +4322,17 @@ static void ixgbe_pbthresh_setup(struct ixgbe_adapter *adapter)
        if (!num_tc)
                num_tc = 1;
 
-       hw->fc.low_water = ixgbe_lpbthresh(adapter);
-
        for (i = 0; i < num_tc; i++) {
                hw->fc.high_water[i] = ixgbe_hpbthresh(adapter, i);
+               hw->fc.low_water[i] = ixgbe_lpbthresh(adapter, i);
 
                /* Low water marks must not be larger than high water marks */
-               if (hw->fc.low_water > hw->fc.high_water[i])
-                       hw->fc.low_water = 0;
+               if (hw->fc.low_water[i] > hw->fc.high_water[i])
+                       hw->fc.low_water[i] = 0;
        }
+
+       for (; i < MAX_TRAFFIC_CLASS; i++)
+               hw->fc.high_water[i] = 0;
 }
 
 static void ixgbe_configure_pb(struct ixgbe_adapter *adapter)
@@ -4249,20 +4394,10 @@ static void ixgbe_macvlan_set_rx_mode(struct net_device *dev, unsigned int pool,
                vmolr |= IXGBE_VMOLR_ROMPE;
                hw->mac.ops.update_mc_addr_list(hw, dev);
        }
-       ixgbe_write_uc_addr_list(adapter->netdev);
+       ixgbe_write_uc_addr_list(adapter->netdev, pool);
        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;
@@ -4521,6 +4656,8 @@ static inline bool ixgbe_is_sfp(struct ixgbe_hw *hw)
        case ixgbe_phy_qsfp_active_unknown:
        case ixgbe_phy_qsfp_intel:
        case ixgbe_phy_qsfp_unknown:
+       /* ixgbe_phy_none is set when no SFP module is present */
+       case ixgbe_phy_none:
                return true;
        case ixgbe_phy_nl:
                if (hw->mac.type == ixgbe_mac_82598EB)
@@ -4742,7 +4879,9 @@ void ixgbe_up(struct ixgbe_adapter *adapter)
 void ixgbe_reset(struct ixgbe_adapter *adapter)
 {
        struct ixgbe_hw *hw = &adapter->hw;
+       struct net_device *netdev = adapter->netdev;
        int err;
+       u8 old_addr[ETH_ALEN];
 
        if (ixgbe_removed(hw->hw_addr))
                return;
@@ -4778,9 +4917,10 @@ void ixgbe_reset(struct ixgbe_adapter *adapter)
        }
 
        clear_bit(__IXGBE_IN_SFP_INIT, &adapter->state);
-
-       /* reprogram the RAR[0] in case user changed it. */
-       hw->mac.ops.set_rar(hw, 0, hw->mac.addr, VMDQ_P(0), IXGBE_RAH_AV);
+       /* do not flush user set addresses */
+       memcpy(old_addr, &adapter->mac_table[0].addr, netdev->addr_len);
+       ixgbe_flush_sw_mac_table(adapter);
+       ixgbe_mac_set_default_filter(adapter, old_addr);
 
        /* update SAN MAC vmdq pool selection */
        if (hw->mac.san_mac_rar_index)
@@ -5026,6 +5166,10 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter)
 #endif /* CONFIG_IXGBE_DCB */
 #endif /* IXGBE_FCOE */
 
+       adapter->mac_table = kzalloc(sizeof(struct ixgbe_mac_addr) *
+                                    hw->mac.num_rar_entries,
+                                    GFP_ATOMIC);
+
        /* Set MAC specific capability flags and exceptions */
        switch (hw->mac.type) {
        case ixgbe_mac_82598EB:
@@ -5517,6 +5661,17 @@ static int ixgbe_open(struct net_device *netdev)
        return err;
 }
 
+static void ixgbe_close_suspend(struct ixgbe_adapter *adapter)
+{
+       ixgbe_ptp_suspend(adapter);
+
+       ixgbe_down(adapter);
+       ixgbe_free_irq(adapter);
+
+       ixgbe_free_all_tx_resources(adapter);
+       ixgbe_free_all_rx_resources(adapter);
+}
+
 /**
  * ixgbe_close - Disables a network interface
  * @netdev: network interface device structure
@@ -5534,14 +5689,10 @@ static int ixgbe_close(struct net_device *netdev)
 
        ixgbe_ptp_stop(adapter);
 
-       ixgbe_down(adapter);
-       ixgbe_free_irq(adapter);
+       ixgbe_close_suspend(adapter);
 
        ixgbe_fdir_filter_exit(adapter);
 
-       ixgbe_free_all_tx_resources(adapter);
-       ixgbe_free_all_rx_resources(adapter);
-
        ixgbe_release_hw_control(adapter);
 
        return 0;
@@ -5608,12 +5759,8 @@ static int __ixgbe_shutdown(struct pci_dev *pdev, bool *enable_wake)
        netif_device_detach(netdev);
 
        rtnl_lock();
-       if (netif_running(netdev)) {
-               ixgbe_down(adapter);
-               ixgbe_free_irq(adapter);
-               ixgbe_free_all_tx_resources(adapter);
-               ixgbe_free_all_rx_resources(adapter);
-       }
+       if (netif_running(netdev))
+               ixgbe_close_suspend(adapter);
        rtnl_unlock();
 
        ixgbe_clear_interrupt_scheme(adapter);
@@ -5945,7 +6092,7 @@ static void ixgbe_fdir_reinit_subtask(struct ixgbe_adapter *adapter)
        if (ixgbe_reinit_fdir_tables_82599(hw) == 0) {
                for (i = 0; i < adapter->num_tx_queues; i++)
                        set_bit(__IXGBE_TX_FDIR_INIT_DONE,
-                               &(adapter->tx_ring[i]->state));
+                               &(adapter->tx_ring[i]->state));
                /* re-enable flow director interrupts */
                IXGBE_WRITE_REG(hw, IXGBE_EIMS, IXGBE_EIMS_FLOW_DIR);
        } else {
@@ -7172,16 +7319,17 @@ static int ixgbe_set_mac(struct net_device *netdev, void *p)
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        struct ixgbe_hw *hw = &adapter->hw;
        struct sockaddr *addr = p;
+       int ret;
 
        if (!is_valid_ether_addr(addr->sa_data))
                return -EADDRNOTAVAIL;
 
+       ixgbe_del_mac_filter(adapter, hw->mac.addr, VMDQ_P(0));
        memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
        memcpy(hw->mac.addr, addr->sa_data, netdev->addr_len);
 
-       hw->mac.ops.set_rar(hw, 0, hw->mac.addr, VMDQ_P(0), IXGBE_RAH_AV);
-
-       return 0;
+       ret = ixgbe_add_mac_filter(adapter, hw->mac.addr, VMDQ_P(0));
+       return ret > 0 ? 0 : ret;
 }
 
 static int
@@ -7783,7 +7931,7 @@ static const struct net_device_ops ixgbe_netdev_ops = {
        .ndo_do_ioctl           = ixgbe_ioctl,
        .ndo_set_vf_mac         = ixgbe_ndo_set_vf_mac,
        .ndo_set_vf_vlan        = ixgbe_ndo_set_vf_vlan,
-       .ndo_set_vf_tx_rate     = ixgbe_ndo_set_vf_bw,
+       .ndo_set_vf_rate        = ixgbe_ndo_set_vf_bw,
        .ndo_set_vf_spoofchk    = ixgbe_ndo_set_vf_spoofchk,
        .ndo_get_vf_config      = ixgbe_ndo_get_vf_config,
        .ndo_get_stats64        = ixgbe_get_stats64,
@@ -8187,6 +8335,8 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                goto err_sw_init;
        }
 
+       ixgbe_mac_set_default_filter(adapter, hw->mac.perm_addr);
+
        setup_timer(&adapter->service_timer, &ixgbe_service_timer,
                    (unsigned long) adapter);
 
@@ -8242,7 +8392,7 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        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);
+                          part_str);
        else
                e_dev_info("MAC: %d, PHY: %d, PBA No: %s\n",
                           hw->mac.type, hw->phy.type, part_str);
@@ -8304,8 +8454,8 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        ixgbe_dbg_adapter_init(adapter);
 
-       /* Need link setup for MNG FW, else wait for IXGBE_UP */
-       if (ixgbe_mng_enabled(hw) && hw->mac.ops.setup_link)
+       /* setup link for SFP devices with MNG FW, else wait for IXGBE_UP */
+       if (ixgbe_mng_enabled(hw) && ixgbe_is_sfp(hw) && hw->mac.ops.setup_link)
                hw->mac.ops.setup_link(hw,
                        IXGBE_LINK_SPEED_10GB_FULL | IXGBE_LINK_SPEED_1GB_FULL,
                        true);
@@ -8319,6 +8469,7 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        ixgbe_disable_sriov(adapter);
        adapter->flags2 &= ~IXGBE_FLAG2_SEARCH_FOR_SFP;
        iounmap(adapter->io_addr);
+       kfree(adapter->mac_table);
 err_ioremap:
        free_netdev(netdev);
 err_alloc_etherdev:
@@ -8392,6 +8543,7 @@ static void ixgbe_remove(struct pci_dev *pdev)
 
        e_dev_info("complete\n");
 
+       kfree(adapter->mac_table);
        free_netdev(netdev);
 
        pci_disable_pcie_error_reporting(pdev);
index f5c6af2b891bd3dc797e7c4f5640b006eac78810..1918e0abf734a0d0e85f9f9df5567cc24e8cfc21 100644 (file)
@@ -223,7 +223,7 @@ static s32 ixgbe_read_posted_mbx(struct ixgbe_hw *hw, u32 *msg, u16 size,
  *  received an ack to that message within delay * timeout period
  **/
 static s32 ixgbe_write_posted_mbx(struct ixgbe_hw *hw, u32 *msg, u16 size,
-                           u16 mbx_id)
+                          u16 mbx_id)
 {
        struct ixgbe_mbx_info *mbx = &hw->mbx;
        s32 ret_val = IXGBE_ERR_MBX;
@@ -269,7 +269,7 @@ static s32 ixgbe_check_for_msg_pf(struct ixgbe_hw *hw, u16 vf_number)
        u32 vf_bit = vf_number % 16;
 
        if (!ixgbe_check_for_bit_pf(hw, IXGBE_MBVFICR_VFREQ_VF1 << vf_bit,
-                                   index)) {
+                                   index)) {
                ret_val = 0;
                hw->mbx.stats.reqs++;
        }
@@ -291,7 +291,7 @@ static s32 ixgbe_check_for_ack_pf(struct ixgbe_hw *hw, u16 vf_number)
        u32 vf_bit = vf_number % 16;
 
        if (!ixgbe_check_for_bit_pf(hw, IXGBE_MBVFICR_VFACK_VF1 << vf_bit,
-                                   index)) {
+                                   index)) {
                ret_val = 0;
                hw->mbx.stats.acks++;
        }
@@ -366,7 +366,7 @@ static s32 ixgbe_obtain_mbx_lock_pf(struct ixgbe_hw *hw, u16 vf_number)
  *  returns SUCCESS if it successfully copied message into the buffer
  **/
 static s32 ixgbe_write_mbx_pf(struct ixgbe_hw *hw, u32 *msg, u16 size,
-                              u16 vf_number)
+                             u16 vf_number)
 {
        s32 ret_val;
        u16 i;
@@ -407,7 +407,7 @@ static s32 ixgbe_write_mbx_pf(struct ixgbe_hw *hw, u32 *msg, u16 size,
  *  a message due to a VF request so no polling for message is needed.
  **/
 static s32 ixgbe_read_mbx_pf(struct ixgbe_hw *hw, u32 *msg, u16 size,
-                             u16 vf_number)
+                            u16 vf_number)
 {
        s32 ret_val;
        u16 i;
index a9b9ad69ed0ec315d22d38c92eb1885c2900c9c3..a5cb755de3a994a34f68e0c9142414a8da52cc68 100644 (file)
  * Message ACK's are the value or'd with 0xF0000000
  */
 #define IXGBE_VT_MSGTYPE_ACK      0x80000000  /* Messages below or'd with
-                                               * this are the ACK */
+                                              * this are the ACK */
 #define IXGBE_VT_MSGTYPE_NACK     0x40000000  /* Messages below or'd with
-                                               * this are the NACK */
+                                              * this are the NACK */
 #define IXGBE_VT_MSGTYPE_CTS      0x20000000  /* Indicates that VF is still
-                                                 clear to send requests */
+                                                clear to send requests */
 #define IXGBE_VT_MSGINFO_SHIFT    16
 /* bits 23:16 are used for exra info for certain messages */
 #define IXGBE_VT_MSGINFO_MASK     (0xFF << IXGBE_VT_MSGINFO_SHIFT)
index a76af8e28a04be16386c444cc947581638f3befa..ff68b7a9deff15af054ec66238557fa47d3c1480 100644 (file)
@@ -67,7 +67,7 @@ s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw)
                        if (mdio45_probe(&hw->phy.mdio, phy_addr) == 0) {
                                ixgbe_get_phy_id(hw);
                                hw->phy.type =
-                                       ixgbe_get_phy_type_from_id(hw->phy.id);
+                                       ixgbe_get_phy_type_from_id(hw->phy.id);
 
                                if (hw->phy.type == ixgbe_phy_unknown) {
                                        hw->phy.ops.read_reg(hw,
@@ -136,12 +136,12 @@ static s32 ixgbe_get_phy_id(struct ixgbe_hw *hw)
        u16 phy_id_low = 0;
 
        status = hw->phy.ops.read_reg(hw, MDIO_DEVID1, MDIO_MMD_PMAPMD,
-                                     &phy_id_high);
+                                     &phy_id_high);
 
        if (status == 0) {
                hw->phy.id = (u32)(phy_id_high << 16);
                status = hw->phy.ops.read_reg(hw, MDIO_DEVID2, MDIO_MMD_PMAPMD,
-                                             &phy_id_low);
+                                             &phy_id_low);
                hw->phy.id |= (u32)(phy_id_low & IXGBE_PHY_REVISION_MASK);
                hw->phy.revision = (u32)(phy_id_low & ~IXGBE_PHY_REVISION_MASK);
        }
@@ -318,7 +318,7 @@ s32 ixgbe_read_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type,
  *  @phy_data: Pointer to read data from PHY register
  **/
 s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
-                               u32 device_type, u16 *phy_data)
+                              u32 device_type, u16 *phy_data)
 {
        s32 status;
        u16 gssr;
@@ -421,7 +421,7 @@ s32 ixgbe_write_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr,
  *  @phy_data: Data to write to the PHY register
  **/
 s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
-                                u32 device_type, u16 phy_data)
+                               u32 device_type, u16 phy_data)
 {
        s32 status;
        u16 gssr;
@@ -548,8 +548,8 @@ s32 ixgbe_setup_phy_link_generic(struct ixgbe_hw *hw)
  *  @speed: new link speed
  **/
 s32 ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw,
-                                       ixgbe_link_speed speed,
-                                       bool autoneg_wait_to_complete)
+                                      ixgbe_link_speed speed,
+                                      bool autoneg_wait_to_complete)
 {
 
        /*
@@ -582,8 +582,8 @@ s32 ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw,
  * Determines the link capabilities by reading the AUTOC register.
  */
 s32 ixgbe_get_copper_link_capabilities_generic(struct ixgbe_hw *hw,
-                                               ixgbe_link_speed *speed,
-                                               bool *autoneg)
+                                              ixgbe_link_speed *speed,
+                                              bool *autoneg)
 {
        s32 status = IXGBE_ERR_LINK_SETUP;
        u16 speed_ability;
@@ -592,7 +592,7 @@ s32 ixgbe_get_copper_link_capabilities_generic(struct ixgbe_hw *hw,
        *autoneg = true;
 
        status = hw->phy.ops.read_reg(hw, MDIO_SPEED, MDIO_MMD_PMAPMD,
-                                     &speed_ability);
+                                     &speed_ability);
 
        if (status == 0) {
                if (speed_ability & MDIO_SPEED_10G)
@@ -806,11 +806,11 @@ s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw)
 
        /* reset the PHY and poll for completion */
        hw->phy.ops.write_reg(hw, MDIO_CTRL1, MDIO_MMD_PHYXS,
-                             (phy_data | MDIO_CTRL1_RESET));
+                             (phy_data | MDIO_CTRL1_RESET));
 
        for (i = 0; i < 100; i++) {
                hw->phy.ops.read_reg(hw, MDIO_CTRL1, MDIO_MMD_PHYXS,
-                                    &phy_data);
+                                    &phy_data);
                if ((phy_data & MDIO_CTRL1_RESET) == 0)
                        break;
                usleep_range(10000, 20000);
@@ -824,7 +824,7 @@ s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw)
 
        /* Get init offsets */
        ret_val = ixgbe_get_sfp_init_sequence_offsets(hw, &list_offset,
-                                                     &data_offset);
+                                                     &data_offset);
        if (ret_val != 0)
                goto out;
 
@@ -838,7 +838,7 @@ s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw)
                if (ret_val)
                        goto err_eeprom;
                control = (eword & IXGBE_CONTROL_MASK_NL) >>
-                          IXGBE_CONTROL_SHIFT_NL;
+                          IXGBE_CONTROL_SHIFT_NL;
                edata = eword & IXGBE_DATA_MASK_NL;
                switch (control) {
                case IXGBE_DELAY_NL:
@@ -859,7 +859,7 @@ s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw)
                                if (ret_val)
                                        goto err_eeprom;
                                hw->phy.ops.write_reg(hw, phy_offset,
-                                                     MDIO_MMD_PMAPMD, eword);
+                                                     MDIO_MMD_PMAPMD, eword);
                                hw_dbg(hw, "Wrote %4.4x to %4.4x\n", eword,
                                       phy_offset);
                                data_offset++;
@@ -1010,10 +1010,10 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw)
                        if (cable_tech & IXGBE_SFF_DA_PASSIVE_CABLE) {
                                if (hw->bus.lan_id == 0)
                                        hw->phy.sfp_type =
-                                                    ixgbe_sfp_type_da_cu_core0;
+                                                    ixgbe_sfp_type_da_cu_core0;
                                else
                                        hw->phy.sfp_type =
-                                                    ixgbe_sfp_type_da_cu_core1;
+                                                    ixgbe_sfp_type_da_cu_core1;
                        } else if (cable_tech & IXGBE_SFF_DA_ACTIVE_CABLE) {
                                hw->phy.ops.read_i2c_eeprom(
                                                hw, IXGBE_SFF_CABLE_SPEC_COMP,
@@ -1035,10 +1035,10 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw)
                                    IXGBE_SFF_10GBASELR_CAPABLE)) {
                                if (hw->bus.lan_id == 0)
                                        hw->phy.sfp_type =
-                                                     ixgbe_sfp_type_srlr_core0;
+                                                     ixgbe_sfp_type_srlr_core0;
                                else
                                        hw->phy.sfp_type =
-                                                     ixgbe_sfp_type_srlr_core1;
+                                                     ixgbe_sfp_type_srlr_core1;
                        } else if (comp_codes_1g & IXGBE_SFF_1GBASET_CAPABLE) {
                                if (hw->bus.lan_id == 0)
                                        hw->phy.sfp_type =
@@ -1087,15 +1087,15 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw)
                                goto err_read_i2c_eeprom;
 
                        status = hw->phy.ops.read_i2c_eeprom(hw,
-                                                   IXGBE_SFF_VENDOR_OUI_BYTE1,
-                                                   &oui_bytes[1]);
+                                                   IXGBE_SFF_VENDOR_OUI_BYTE1,
+                                                   &oui_bytes[1]);
 
                        if (status != 0)
                                goto err_read_i2c_eeprom;
 
                        status = hw->phy.ops.read_i2c_eeprom(hw,
-                                                   IXGBE_SFF_VENDOR_OUI_BYTE2,
-                                                   &oui_bytes[2]);
+                                                   IXGBE_SFF_VENDOR_OUI_BYTE2,
+                                                   &oui_bytes[2]);
 
                        if (status != 0)
                                goto err_read_i2c_eeprom;
@@ -1403,8 +1403,8 @@ static s32 ixgbe_identify_qsfp_module_generic(struct ixgbe_hw *hw)
  *  so it returns the offsets to the phy init sequence block.
  **/
 s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw,
-                                        u16 *list_offset,
-                                        u16 *data_offset)
+                                       u16 *list_offset,
+                                       u16 *data_offset)
 {
        u16 sfp_id;
        u16 sfp_type = hw->phy.sfp_type;
@@ -1493,11 +1493,11 @@ s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw,
  *  Performs byte read operation to SFP module's EEPROM over I2C interface.
  **/
 s32 ixgbe_read_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset,
-                                  u8 *eeprom_data)
+                                 u8 *eeprom_data)
 {
        return hw->phy.ops.read_i2c_byte(hw, byte_offset,
-                                        IXGBE_I2C_EEPROM_DEV_ADDR,
-                                        eeprom_data);
+                                        IXGBE_I2C_EEPROM_DEV_ADDR,
+                                        eeprom_data);
 }
 
 /**
@@ -1525,11 +1525,11 @@ s32 ixgbe_read_i2c_sff8472_generic(struct ixgbe_hw *hw, u8 byte_offset,
  *  Performs byte write operation to SFP module's EEPROM over I2C interface.
  **/
 s32 ixgbe_write_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset,
-                                   u8 eeprom_data)
+                                  u8 eeprom_data)
 {
        return hw->phy.ops.write_i2c_byte(hw, byte_offset,
-                                         IXGBE_I2C_EEPROM_DEV_ADDR,
-                                         eeprom_data);
+                                         IXGBE_I2C_EEPROM_DEV_ADDR,
+                                         eeprom_data);
 }
 
 /**
@@ -1542,7 +1542,7 @@ s32 ixgbe_write_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset,
  *  a specified device address.
  **/
 s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset,
-                                u8 dev_addr, u8 *data)
+                               u8 dev_addr, u8 *data)
 {
        s32 status = 0;
        u32 max_retry = 10;
@@ -1631,7 +1631,7 @@ s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset,
  *  a specified device address.
  **/
 s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset,
-                                 u8 dev_addr, u8 data)
+                                u8 dev_addr, u8 data)
 {
        s32 status = 0;
        u32 max_retry = 1;
@@ -2046,7 +2046,7 @@ s32 ixgbe_tn_check_overtemp(struct ixgbe_hw *hw)
 
        /* Check that the LASI temp alarm status was triggered */
        hw->phy.ops.read_reg(hw, IXGBE_TN_LASI_STATUS_REG,
-                            MDIO_MMD_PMAPMD, &phy_data);
+                            MDIO_MMD_PMAPMD, &phy_data);
 
        if (!(phy_data & IXGBE_TN_LASI_STATUS_TEMP_ALARM))
                goto out;
index 0bb047f751c2a4ac6049f4500f65f035651ba830..54071ed17e3b3d589d8250f4536f622e796049dc 100644 (file)
@@ -114,47 +114,47 @@ s32 ixgbe_init_phy_ops_generic(struct ixgbe_hw *hw);
 s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw);
 s32 ixgbe_reset_phy_generic(struct ixgbe_hw *hw);
 s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
-                               u32 device_type, u16 *phy_data);
+                              u32 device_type, u16 *phy_data);
 s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
-                                u32 device_type, u16 phy_data);
+                               u32 device_type, u16 phy_data);
 s32 ixgbe_read_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr,
                           u32 device_type, u16 *phy_data);
 s32 ixgbe_write_phy_reg_mdi(struct ixgbe_hw *hw, u32 reg_addr,
                            u32 device_type, u16 phy_data);
 s32 ixgbe_setup_phy_link_generic(struct ixgbe_hw *hw);
 s32 ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw,
-                                       ixgbe_link_speed speed,
-                                       bool autoneg_wait_to_complete);
+                                      ixgbe_link_speed speed,
+                                      bool autoneg_wait_to_complete);
 s32 ixgbe_get_copper_link_capabilities_generic(struct ixgbe_hw *hw,
-                                               ixgbe_link_speed *speed,
-                                               bool *autoneg);
+                                              ixgbe_link_speed *speed,
+                                              bool *autoneg);
 bool ixgbe_check_reset_blocked(struct ixgbe_hw *hw);
 
 /* PHY specific */
 s32 ixgbe_check_phy_link_tnx(struct ixgbe_hw *hw,
-                             ixgbe_link_speed *speed,
-                             bool *link_up);
+                            ixgbe_link_speed *speed,
+                            bool *link_up);
 s32 ixgbe_setup_phy_link_tnx(struct ixgbe_hw *hw);
 s32 ixgbe_get_phy_firmware_version_tnx(struct ixgbe_hw *hw,
-                                       u16 *firmware_version);
+                                      u16 *firmware_version);
 s32 ixgbe_get_phy_firmware_version_generic(struct ixgbe_hw *hw,
-                                           u16 *firmware_version);
+                                          u16 *firmware_version);
 
 s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw);
 s32 ixgbe_identify_module_generic(struct ixgbe_hw *hw);
 s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw);
 s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw,
-                                        u16 *list_offset,
-                                        u16 *data_offset);
+                                       u16 *list_offset,
+                                       u16 *data_offset);
 s32 ixgbe_tn_check_overtemp(struct ixgbe_hw *hw);
 s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset,
-                                u8 dev_addr, u8 *data);
+                               u8 dev_addr, u8 *data);
 s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset,
-                                 u8 dev_addr, u8 data);
+                                u8 dev_addr, u8 data);
 s32 ixgbe_read_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset,
-                                  u8 *eeprom_data);
+                                 u8 *eeprom_data);
 s32 ixgbe_read_i2c_sff8472_generic(struct ixgbe_hw *hw, u8 byte_offset,
                                   u8 *sff8472_data);
 s32 ixgbe_write_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset,
-                                   u8 eeprom_data);
+                                  u8 eeprom_data);
 #endif /* _IXGBE_PHY_H_ */
index 8902ae68345770ce5f28ba2ab91ee73bab3c7ff3..68f87ecb8a762da7e950a61ae2c9058bb61f5e8e 100644 (file)
@@ -26,7 +26,6 @@
 
 *******************************************************************************/
 #include "ixgbe.h"
-#include <linux/export.h>
 #include <linux/ptp_classify.h>
 
 /*
@@ -334,7 +333,7 @@ static int ixgbe_ptp_settime(struct ptp_clock_info *ptp,
 }
 
 /**
- * ixgbe_ptp_enable
+ * ixgbe_ptp_feature_enable
  * @ptp: the ptp clock structure
  * @rq: the requested feature to change
  * @on: whether to enable or disable the feature
@@ -342,8 +341,8 @@ static int ixgbe_ptp_settime(struct ptp_clock_info *ptp,
  * enable (or disable) ancillary features of the phc subsystem.
  * our driver only supports the PPS feature on the X540
  */
-static int ixgbe_ptp_enable(struct ptp_clock_info *ptp,
-                           struct ptp_clock_request *rq, int on)
+static int ixgbe_ptp_feature_enable(struct ptp_clock_info *ptp,
+                                   struct ptp_clock_request *rq, int on)
 {
        struct ixgbe_adapter *adapter =
                container_of(ptp, struct ixgbe_adapter, ptp_caps);
@@ -570,9 +569,9 @@ int ixgbe_ptp_get_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
 }
 
 /**
- * ixgbe_ptp_set_ts_config - control hardware time stamping
- * @adapter: pointer to adapter struct
- * @ifreq: ioctl data
+ * ixgbe_ptp_set_timestamp_mode - setup the hardware for the requested mode
+ * @adapter: the private ixgbe adapter structure
+ * @config: the hwtstamp configuration requested
  *
  * Outgoing time stamping can be enabled and disabled. Play nice and
  * disable it when requested, although it shouldn't cause any overhead
@@ -590,25 +589,25 @@ int ixgbe_ptp_get_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
  * packets, regardless of the type specified in the register, only use V2
  * Event mode. This more accurately tells the user what the hardware is going
  * to do anyways.
+ *
+ * Note: this may modify the hwtstamp configuration towards a more general
+ * mode, if required to support the specifically requested mode.
  */
-int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
+static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter,
+                                struct hwtstamp_config *config)
 {
        struct ixgbe_hw *hw = &adapter->hw;
-       struct hwtstamp_config config;
        u32 tsync_tx_ctl = IXGBE_TSYNCTXCTL_ENABLED;
        u32 tsync_rx_ctl = IXGBE_TSYNCRXCTL_ENABLED;
        u32 tsync_rx_mtrl = PTP_EV_PORT << 16;
        bool is_l2 = false;
        u32 regval;
 
-       if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
-               return -EFAULT;
-
        /* reserved for future extensions */
-       if (config.flags)
+       if (config->flags)
                return -EINVAL;
 
-       switch (config.tx_type) {
+       switch (config->tx_type) {
        case HWTSTAMP_TX_OFF:
                tsync_tx_ctl = 0;
        case HWTSTAMP_TX_ON:
@@ -617,7 +616,7 @@ int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
                return -ERANGE;
        }
 
-       switch (config.rx_filter) {
+       switch (config->rx_filter) {
        case HWTSTAMP_FILTER_NONE:
                tsync_rx_ctl = 0;
                tsync_rx_mtrl = 0;
@@ -641,7 +640,7 @@ int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
        case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
                tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_EVENT_V2;
                is_l2 = true;
-               config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
+               config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
                break;
        case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
        case HWTSTAMP_FILTER_ALL:
@@ -652,7 +651,7 @@ int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
                 * Delay_Req messages and hardware does not support
                 * timestamping all packets => return error
                 */
-               config.rx_filter = HWTSTAMP_FILTER_NONE;
+               config->rx_filter = HWTSTAMP_FILTER_NONE;
                return -ERANGE;
        }
 
@@ -671,7 +670,6 @@ int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
        else
                IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_1588), 0);
 
-
        /* enable/disable TX */
        regval = IXGBE_READ_REG(hw, IXGBE_TSYNCTXCTL);
        regval &= ~IXGBE_TSYNCTXCTL_ENABLED;
@@ -693,6 +691,29 @@ int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
        regval = IXGBE_READ_REG(hw, IXGBE_TXSTMPH);
        regval = IXGBE_READ_REG(hw, IXGBE_RXSTMPH);
 
+       return 0;
+}
+
+/**
+ * ixgbe_ptp_set_ts_config - user entry point for timestamp mode
+ * @adapter: pointer to adapter struct
+ * @ifreq: ioctl data
+ *
+ * Set hardware to requested mode. If unsupported, return an error with no
+ * changes. Otherwise, store the mode for future reference.
+ */
+int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
+{
+       struct hwtstamp_config config;
+       int err;
+
+       if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
+               return -EFAULT;
+
+       err = ixgbe_ptp_set_timestamp_mode(adapter, &config);
+       if (err)
+               return err;
+
        /* save these settings for future reference */
        memcpy(&adapter->tstamp_config, &config,
               sizeof(adapter->tstamp_config));
@@ -790,9 +811,13 @@ void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter)
  * ixgbe_ptp_reset
  * @adapter: the ixgbe private board structure
  *
- * When the MAC resets, all timesync features are reset. This function should be
- * called to re-enable the PTP clock structure. It will re-init the timecounter
- * structure based on the kernel time as well as setup the cycle counter data.
+ * When the MAC resets, all the hardware bits for timesync are reset. This
+ * function is used to re-enable the device for PTP based on current settings.
+ * We do lose the current clock time, so just reset the cyclecounter to the
+ * system real clock time.
+ *
+ * This function will maintain hwtstamp_config settings, and resets the SDP
+ * output if it was enabled.
  */
 void ixgbe_ptp_reset(struct ixgbe_adapter *adapter)
 {
@@ -804,8 +829,8 @@ void ixgbe_ptp_reset(struct ixgbe_adapter *adapter)
        IXGBE_WRITE_REG(hw, IXGBE_SYSTIMH, 0x00000000);
        IXGBE_WRITE_FLUSH(hw);
 
-       /* Reset the saved tstamp_config */
-       memset(&adapter->tstamp_config, 0, sizeof(adapter->tstamp_config));
+       /* reset the hardware timestamping mode */
+       ixgbe_ptp_set_timestamp_mode(adapter, &adapter->tstamp_config);
 
        ixgbe_ptp_start_cyclecounter(adapter);
 
@@ -825,16 +850,23 @@ void ixgbe_ptp_reset(struct ixgbe_adapter *adapter)
 }
 
 /**
- * ixgbe_ptp_init
+ * ixgbe_ptp_create_clock
  * @adapter: the ixgbe private adapter structure
  *
- * This function performs the required steps for enabling ptp
- * support. If ptp support has already been loaded it simply calls the
- * cyclecounter init routine and exits.
+ * This function performs setup of the user entry point function table and
+ * initializes the PTP clock device, which is used to access the clock-like
+ * features of the PTP core. It will be called by ixgbe_ptp_init, only if
+ * there isn't already a clock device (such as after a suspend/resume cycle,
+ * where the clock device wasn't destroyed).
  */
-void ixgbe_ptp_init(struct ixgbe_adapter *adapter)
+static int ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter)
 {
        struct net_device *netdev = adapter->netdev;
+       long err;
+
+       /* do nothing if we already have a clock device */
+       if (!IS_ERR_OR_NULL(adapter->ptp_clock))
+               return 0;
 
        switch (adapter->hw.mac.type) {
        case ixgbe_mac_X540:
@@ -851,7 +883,7 @@ void ixgbe_ptp_init(struct ixgbe_adapter *adapter)
                adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime;
                adapter->ptp_caps.gettime = ixgbe_ptp_gettime;
                adapter->ptp_caps.settime = ixgbe_ptp_settime;
-               adapter->ptp_caps.enable = ixgbe_ptp_enable;
+               adapter->ptp_caps.enable = ixgbe_ptp_feature_enable;
                break;
        case ixgbe_mac_82599EB:
                snprintf(adapter->ptp_caps.name,
@@ -867,24 +899,57 @@ void ixgbe_ptp_init(struct ixgbe_adapter *adapter)
                adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime;
                adapter->ptp_caps.gettime = ixgbe_ptp_gettime;
                adapter->ptp_caps.settime = ixgbe_ptp_settime;
-               adapter->ptp_caps.enable = ixgbe_ptp_enable;
+               adapter->ptp_caps.enable = ixgbe_ptp_feature_enable;
                break;
        default:
                adapter->ptp_clock = NULL;
-               return;
+               return -EOPNOTSUPP;
        }
 
-       spin_lock_init(&adapter->tmreg_lock);
-       INIT_WORK(&adapter->ptp_tx_work, ixgbe_ptp_tx_hwtstamp_work);
-
        adapter->ptp_clock = ptp_clock_register(&adapter->ptp_caps,
                                                &adapter->pdev->dev);
        if (IS_ERR(adapter->ptp_clock)) {
+               err = PTR_ERR(adapter->ptp_clock);
                adapter->ptp_clock = NULL;
                e_dev_err("ptp_clock_register failed\n");
+               return err;
        } else
                e_dev_info("registered PHC device on %s\n", netdev->name);
 
+       /* set default timestamp mode to disabled here. We do this in
+        * create_clock instead of init, because we don't want to override the
+        * previous settings during a resume cycle.
+        */
+       adapter->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;
+       adapter->tstamp_config.tx_type = HWTSTAMP_TX_OFF;
+
+       return 0;
+}
+
+/**
+ * ixgbe_ptp_init
+ * @adapter: the ixgbe private adapter structure
+ *
+ * This function performs the required steps for enabling PTP
+ * support. If PTP support has already been loaded it simply calls the
+ * cyclecounter init routine and exits.
+ */
+void ixgbe_ptp_init(struct ixgbe_adapter *adapter)
+{
+       /* initialize the spin lock first since we can't control when a user
+        * will call the entry functions once we have initialized the clock
+        * device
+        */
+       spin_lock_init(&adapter->tmreg_lock);
+
+       /* obtain a PTP device, or re-use an existing device */
+       if (ixgbe_ptp_create_clock(adapter))
+               return;
+
+       /* we have a clock so we can initialize work now */
+       INIT_WORK(&adapter->ptp_tx_work, ixgbe_ptp_tx_hwtstamp_work);
+
+       /* reset the PTP related hardware bits */
        ixgbe_ptp_reset(adapter);
 
        /* enter the IXGBE_PTP_RUNNING state */
@@ -894,28 +959,45 @@ void ixgbe_ptp_init(struct ixgbe_adapter *adapter)
 }
 
 /**
- * ixgbe_ptp_stop - disable ptp device and stop the overflow check
- * @adapter: pointer to adapter struct
+ * ixgbe_ptp_suspend - stop PTP work items
+ * @ adapter: pointer to adapter struct
  *
- * this function stops the ptp support, and cancels the delayed work.
+ * this function suspends PTP activity, and prevents more PTP work from being
+ * generated, but does not destroy the PTP clock device.
  */
-void ixgbe_ptp_stop(struct ixgbe_adapter *adapter)
+void ixgbe_ptp_suspend(struct ixgbe_adapter *adapter)
 {
        /* Leave the IXGBE_PTP_RUNNING state. */
        if (!test_and_clear_bit(__IXGBE_PTP_RUNNING, &adapter->state))
                return;
 
-       /* stop the PPS signal */
-       adapter->flags2 &= ~IXGBE_FLAG2_PTP_PPS_ENABLED;
-       ixgbe_ptp_setup_sdp(adapter);
+       /* since this might be called in suspend, we don't clear the state,
+        * but simply reset the auxiliary PPS signal control register
+        */
+       IXGBE_WRITE_REG(&adapter->hw, IXGBE_TSAUXC, 0x0);
 
+       /* ensure that we cancel any pending PTP Tx work item in progress */
        cancel_work_sync(&adapter->ptp_tx_work);
        if (adapter->ptp_tx_skb) {
                dev_kfree_skb_any(adapter->ptp_tx_skb);
                adapter->ptp_tx_skb = NULL;
                clear_bit_unlock(__IXGBE_PTP_TX_IN_PROGRESS, &adapter->state);
        }
+}
+
+/**
+ * ixgbe_ptp_stop - close the PTP device
+ * @adapter: pointer to adapter struct
+ *
+ * completely destroy the PTP device, should only be called when the device is
+ * being fully closed.
+ */
+void ixgbe_ptp_stop(struct ixgbe_adapter *adapter)
+{
+       /* first, suspend PTP activity */
+       ixgbe_ptp_suspend(adapter);
 
+       /* disable the PTP clock device */
        if (adapter->ptp_clock) {
                ptp_clock_unregister(adapter->ptp_clock);
                adapter->ptp_clock = NULL;
index e6c68d396c992fffb329a1cca4daadb47169faab..16b3a1cd9db6c0c32bef5223b479cf9f4b4a4d41 100644 (file)
@@ -72,8 +72,6 @@ static int __ixgbe_enable_sriov(struct ixgbe_adapter *adapter)
                for (i = 0; i < num_vf_macvlans; i++) {
                        mv_list->vf = -1;
                        mv_list->free = true;
-                       mv_list->rar_entry = hw->mac.num_rar_entries -
-                               (i + adapter->num_vfs + 1);
                        list_add(&mv_list->l, &adapter->vf_mvs.l);
                        mv_list++;
                }
@@ -327,6 +325,7 @@ static int ixgbe_set_vf_multicasts(struct ixgbe_adapter *adapter,
        u32 vector_bit;
        u32 vector_reg;
        u32 mta_reg;
+       u32 vmolr = IXGBE_READ_REG(hw, IXGBE_VMOLR(vf));
 
        /* only so many hash values supported */
        entries = min(entries, IXGBE_MAX_VF_MC_ENTRIES);
@@ -353,25 +352,13 @@ static int ixgbe_set_vf_multicasts(struct ixgbe_adapter *adapter,
                mta_reg |= (1 << vector_bit);
                IXGBE_WRITE_REG(hw, IXGBE_MTA(vector_reg), mta_reg);
        }
+       vmolr |= IXGBE_VMOLR_ROMPE;
+       IXGBE_WRITE_REG(hw, IXGBE_VMOLR(vf), vmolr);
 
        return 0;
 }
 
-static void ixgbe_restore_vf_macvlans(struct ixgbe_adapter *adapter)
-{
-       struct ixgbe_hw *hw = &adapter->hw;
-       struct list_head *pos;
-       struct vf_macvlans *entry;
-
-       list_for_each(pos, &adapter->vf_mvs.l) {
-               entry = list_entry(pos, struct vf_macvlans, l);
-               if (!entry->free)
-                       hw->mac.ops.set_rar(hw, entry->rar_entry,
-                                           entry->vf_macvlan,
-                                           entry->vf, IXGBE_RAH_AV);
-       }
-}
-
+#ifdef CONFIG_PCI_IOV
 void ixgbe_restore_vf_multicasts(struct ixgbe_adapter *adapter)
 {
        struct ixgbe_hw *hw = &adapter->hw;
@@ -382,6 +369,7 @@ void ixgbe_restore_vf_multicasts(struct ixgbe_adapter *adapter)
        u32 mta_reg;
 
        for (i = 0; i < adapter->num_vfs; i++) {
+               u32 vmolr = IXGBE_READ_REG(hw, IXGBE_VMOLR(i));
                vfinfo = &adapter->vfinfo[i];
                for (j = 0; j < vfinfo->num_vf_mc_hashes; j++) {
                        hw->addr_ctrl.mta_in_use++;
@@ -391,11 +379,18 @@ void ixgbe_restore_vf_multicasts(struct ixgbe_adapter *adapter)
                        mta_reg |= (1 << vector_bit);
                        IXGBE_WRITE_REG(hw, IXGBE_MTA(vector_reg), mta_reg);
                }
+
+               if (vfinfo->num_vf_mc_hashes)
+                       vmolr |= IXGBE_VMOLR_ROMPE;
+               else
+                       vmolr &= ~IXGBE_VMOLR_ROMPE;
+               IXGBE_WRITE_REG(hw, IXGBE_VMOLR(i), vmolr);
        }
 
        /* Restore any VF macvlans */
-       ixgbe_restore_vf_macvlans(adapter);
+       ixgbe_full_sync_mac_table(adapter);
 }
+#endif
 
 static int ixgbe_set_vf_vlan(struct ixgbe_adapter *adapter, int add, int vid,
                             u32 vf)
@@ -495,8 +490,7 @@ static s32 ixgbe_set_vf_lpe(struct ixgbe_adapter *adapter, u32 *msgbuf, u32 vf)
 static void ixgbe_set_vmolr(struct ixgbe_hw *hw, u32 vf, bool aupe)
 {
        u32 vmolr = IXGBE_READ_REG(hw, IXGBE_VMOLR(vf));
-       vmolr |= (IXGBE_VMOLR_ROMPE |
-                 IXGBE_VMOLR_BAM);
+       vmolr |= IXGBE_VMOLR_BAM;
        if (aupe)
                vmolr |= IXGBE_VMOLR_AUPE;
        else
@@ -514,7 +508,6 @@ static inline void ixgbe_vf_reset_event(struct ixgbe_adapter *adapter, u32 vf)
 {
        struct ixgbe_hw *hw = &adapter->hw;
        struct vf_data_storage *vfinfo = &adapter->vfinfo[vf];
-       int rar_entry = hw->mac.num_rar_entries - (vf + 1);
        u8 num_tcs = netdev_get_num_tc(adapter->netdev);
 
        /* add PF assigned VLAN or VLAN 0 */
@@ -544,7 +537,7 @@ static inline void ixgbe_vf_reset_event(struct ixgbe_adapter *adapter, u32 vf)
        /* Flush and reset the mta with the new values */
        ixgbe_set_rx_mode(adapter->netdev);
 
-       hw->mac.ops.clear_rar(hw, rar_entry);
+       ixgbe_del_mac_filter(adapter, adapter->vfinfo[vf].vf_mac_addresses, vf);
 
        /* reset VF api back to unknown */
        adapter->vfinfo[vf].vf_api = ixgbe_mbox_api_10;
@@ -553,11 +546,9 @@ static inline void ixgbe_vf_reset_event(struct ixgbe_adapter *adapter, u32 vf)
 static int ixgbe_set_vf_mac(struct ixgbe_adapter *adapter,
                            int vf, unsigned char *mac_addr)
 {
-       struct ixgbe_hw *hw = &adapter->hw;
-       int rar_entry = hw->mac.num_rar_entries - (vf + 1);
-
+       ixgbe_del_mac_filter(adapter, adapter->vfinfo[vf].vf_mac_addresses, vf);
        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);
+       ixgbe_add_mac_filter(adapter, adapter->vfinfo[vf].vf_mac_addresses, vf);
 
        return 0;
 }
@@ -565,7 +556,6 @@ static int ixgbe_set_vf_mac(struct ixgbe_adapter *adapter,
 static int ixgbe_set_vf_macvlan(struct ixgbe_adapter *adapter,
                                int vf, int index, unsigned char *mac_addr)
 {
-       struct ixgbe_hw *hw = &adapter->hw;
        struct list_head *pos;
        struct vf_macvlans *entry;
 
@@ -576,7 +566,8 @@ static int ixgbe_set_vf_macvlan(struct ixgbe_adapter *adapter,
                                entry->vf = -1;
                                entry->free = true;
                                entry->is_macvlan = false;
-                               hw->mac.ops.clear_rar(hw, entry->rar_entry);
+                               ixgbe_del_mac_filter(adapter,
+                                                    entry->vf_macvlan, vf);
                        }
                }
        }
@@ -612,7 +603,7 @@ static int ixgbe_set_vf_macvlan(struct ixgbe_adapter *adapter,
        entry->vf = vf;
        memcpy(entry->vf_macvlan, mac_addr, ETH_ALEN);
 
-       hw->mac.ops.set_rar(hw, entry->rar_entry, mac_addr, vf, IXGBE_RAH_AV);
+       ixgbe_add_mac_filter(adapter, mac_addr, vf);
 
        return 0;
 }
@@ -1138,9 +1129,9 @@ int ixgbe_ndo_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos)
                        adapter->vfinfo[vf].vlan_count--;
                adapter->vfinfo[vf].pf_vlan = 0;
                adapter->vfinfo[vf].pf_qos = 0;
-       }
+       }
 out:
-       return err;
+       return err;
 }
 
 static int ixgbe_link_mbps(struct ixgbe_adapter *adapter)
@@ -1231,7 +1222,8 @@ void ixgbe_check_vf_rate_limit(struct ixgbe_adapter *adapter)
        }
 }
 
-int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate)
+int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int min_tx_rate,
+                       int max_tx_rate)
 {
        struct ixgbe_adapter *adapter = netdev_priv(netdev);
        int link_speed;
@@ -1249,13 +1241,16 @@ int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate)
        if (link_speed != 10000)
                return -EINVAL;
 
+       if (min_tx_rate)
+               return -EINVAL;
+
        /* rate limit cannot be less than 10Mbs or greater than link speed */
-       if (tx_rate && ((tx_rate <= 10) || (tx_rate > link_speed)))
+       if (max_tx_rate && ((max_tx_rate <= 10) || (max_tx_rate > link_speed)))
                return -EINVAL;
 
        /* store values */
        adapter->vf_rate_link_speed = link_speed;
-       adapter->vfinfo[vf].tx_rate = tx_rate;
+       adapter->vfinfo[vf].tx_rate = max_tx_rate;
 
        /* update hardware configuration */
        ixgbe_set_vf_rate_limit(adapter, vf);
@@ -1297,7 +1292,8 @@ int ixgbe_ndo_get_vf_config(struct net_device *netdev,
                return -EINVAL;
        ivi->vf = vf;
        memcpy(&ivi->mac, adapter->vfinfo[vf].vf_mac_addresses, ETH_ALEN);
-       ivi->tx_rate = adapter->vfinfo[vf].tx_rate;
+       ivi->max_tx_rate = adapter->vfinfo[vf].tx_rate;
+       ivi->min_tx_rate = 0;
        ivi->vlan = adapter->vfinfo[vf].pf_vlan;
        ivi->qos = adapter->vfinfo[vf].pf_qos;
        ivi->spoofchk = adapter->vfinfo[vf].spoofchk_enabled;
index 139eaddfb2ed5c8c14a3891137a313b4fcaa3a6a..32c26d586c01e1c5d561922774df4b250d3fdd73 100644 (file)
@@ -34,7 +34,9 @@
  */
 #define IXGBE_MAX_VFS_DRV_LIMIT  (IXGBE_MAX_VF_FUNCTIONS - 1)
 
+#ifdef CONFIG_PCI_IOV
 void ixgbe_restore_vf_multicasts(struct ixgbe_adapter *adapter);
+#endif
 void ixgbe_msg_task(struct ixgbe_adapter *adapter);
 int ixgbe_vf_configuration(struct pci_dev *pdev, unsigned int event_mask);
 void ixgbe_disable_tx_rx(struct ixgbe_adapter *adapter);
@@ -42,7 +44,8 @@ void ixgbe_ping_all_vfs(struct ixgbe_adapter *adapter);
 int ixgbe_ndo_set_vf_mac(struct net_device *netdev, int queue, u8 *mac);
 int ixgbe_ndo_set_vf_vlan(struct net_device *netdev, int queue, u16 vlan,
                           u8 qos);
-int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int tx_rate);
+int ixgbe_ndo_set_vf_bw(struct net_device *netdev, int vf, int min_tx_rate,
+                       int max_tx_rate);
 int ixgbe_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting);
 int ixgbe_ndo_get_vf_config(struct net_device *netdev,
                            int vf, struct ifla_vf_info *ivi);
index 8a6ff2423f076974d1c3c408b97c497d00bdc277..9a89f98b35f0290391c1a4da654906ae2364002c 100644 (file)
@@ -160,7 +160,7 @@ struct ixgbe_thermal_sensor_data {
 #define IXGBE_MAX_EITR     0x00000FF8
 #define IXGBE_MIN_EITR     8
 #define IXGBE_EITR(_i)  (((_i) <= 23) ? (0x00820 + ((_i) * 4)) : \
-                         (0x012300 + (((_i) - 24) * 4)))
+                        (0x012300 + (((_i) - 24) * 4)))
 #define IXGBE_EITR_ITR_INT_MASK 0x00000FF8
 #define IXGBE_EITR_LLI_MOD      0x00008000
 #define IXGBE_EITR_CNT_WDIS     0x80000000
@@ -213,7 +213,7 @@ struct ixgbe_thermal_sensor_data {
  * 64-127: 0x0D014 + (n-64)*0x40
  */
 #define IXGBE_SRRCTL(_i) (((_i) <= 15) ? (0x02100 + ((_i) * 4)) : \
-                          (((_i) < 64) ? (0x01014 + ((_i) * 0x40)) : \
+                         (((_i) < 64) ? (0x01014 + ((_i) * 0x40)) : \
                          (0x0D014 + (((_i) - 64) * 0x40))))
 /*
  * Rx DCA Control Register:
@@ -222,11 +222,11 @@ struct ixgbe_thermal_sensor_data {
  * 64-127: 0x0D00C + (n-64)*0x40
  */
 #define IXGBE_DCA_RXCTRL(_i)    (((_i) <= 15) ? (0x02200 + ((_i) * 4)) : \
-                                 (((_i) < 64) ? (0x0100C + ((_i) * 0x40)) : \
+                                (((_i) < 64) ? (0x0100C + ((_i) * 0x40)) : \
                                 (0x0D00C + (((_i) - 64) * 0x40))))
 #define IXGBE_RDRXCTL           0x02F00
 #define IXGBE_RXPBSIZE(_i)      (0x03C00 + ((_i) * 4))
-                                             /* 8 of these 0x03C00 - 0x03C1C */
+                                            /* 8 of these 0x03C00 - 0x03C1C */
 #define IXGBE_RXCTRL    0x03000
 #define IXGBE_DROPEN    0x03D04
 #define IXGBE_RXPBSIZE_SHIFT 10
@@ -239,14 +239,14 @@ struct ixgbe_thermal_sensor_data {
 /* Multicast Table Array - 128 entries */
 #define IXGBE_MTA(_i)   (0x05200 + ((_i) * 4))
 #define IXGBE_RAL(_i)   (((_i) <= 15) ? (0x05400 + ((_i) * 8)) : \
-                         (0x0A200 + ((_i) * 8)))
+                        (0x0A200 + ((_i) * 8)))
 #define IXGBE_RAH(_i)   (((_i) <= 15) ? (0x05404 + ((_i) * 8)) : \
-                         (0x0A204 + ((_i) * 8)))
+                        (0x0A204 + ((_i) * 8)))
 #define IXGBE_MPSAR_LO(_i) (0x0A600 + ((_i) * 8))
 #define IXGBE_MPSAR_HI(_i) (0x0A604 + ((_i) * 8))
 /* Packet split receive type */
 #define IXGBE_PSRTYPE(_i)    (((_i) <= 15) ? (0x05480 + ((_i) * 4)) : \
-                              (0x0EA00 + ((_i) * 4)))
+                             (0x0EA00 + ((_i) * 4)))
 /* array of 4096 1-bit vlan filters */
 #define IXGBE_VFTA(_i)  (0x0A000 + ((_i) * 4))
 /*array of 4096 4-bit vlan vmdq indices */
@@ -696,7 +696,7 @@ struct ixgbe_thermal_sensor_data {
 
 #define IXGBE_RQSMR(_i) (0x02300 + ((_i) * 4))
 #define IXGBE_TQSMR(_i) (((_i) <= 7) ? (0x07300 + ((_i) * 4)) : \
-                         (0x08600 + ((_i) * 4)))
+                        (0x08600 + ((_i) * 4)))
 #define IXGBE_TQSM(_i)  (0x08600 + ((_i) * 4))
 
 #define IXGBE_QPRC(_i) (0x01030 + ((_i) * 0x40)) /* 16 of these */
@@ -820,7 +820,7 @@ struct ixgbe_thermal_sensor_data {
 #define IXGBE_GCR_EXT_VT_MODE_32        0x00000002
 #define IXGBE_GCR_EXT_VT_MODE_64        0x00000003
 #define IXGBE_GCR_EXT_SRIOV             (IXGBE_GCR_EXT_MSIX_EN | \
-                                         IXGBE_GCR_EXT_VT_MODE_64)
+                                        IXGBE_GCR_EXT_VT_MODE_64)
 
 /* Time Sync Registers */
 #define IXGBE_TSYNCRXCTL 0x05188 /* Rx Time Sync Control register - RW */
@@ -1396,10 +1396,10 @@ enum {
 #define IXGBE_EIMC_OTHER        IXGBE_EICR_OTHER     /* INT Cause Active */
 
 #define IXGBE_EIMS_ENABLE_MASK ( \
-                                IXGBE_EIMS_RTX_QUEUE       | \
-                                IXGBE_EIMS_LSC             | \
-                                IXGBE_EIMS_TCP_TIMER       | \
-                                IXGBE_EIMS_OTHER)
+                               IXGBE_EIMS_RTX_QUEUE       | \
+                               IXGBE_EIMS_LSC             | \
+                               IXGBE_EIMS_TCP_TIMER       | \
+                               IXGBE_EIMS_OTHER)
 
 /* Immediate Interrupt Rx (A.K.A. Low Latency Interrupt) */
 #define IXGBE_IMIR_PORT_IM_EN     0x00010000  /* TCP port enable */
@@ -2161,18 +2161,18 @@ enum {
 
 /* Masks to determine if packets should be dropped due to frame errors */
 #define IXGBE_RXD_ERR_FRAME_ERR_MASK ( \
-                                      IXGBE_RXD_ERR_CE | \
-                                      IXGBE_RXD_ERR_LE | \
-                                      IXGBE_RXD_ERR_PE | \
-                                      IXGBE_RXD_ERR_OSE | \
-                                      IXGBE_RXD_ERR_USE)
+                                     IXGBE_RXD_ERR_CE | \
+                                     IXGBE_RXD_ERR_LE | \
+                                     IXGBE_RXD_ERR_PE | \
+                                     IXGBE_RXD_ERR_OSE | \
+                                     IXGBE_RXD_ERR_USE)
 
 #define IXGBE_RXDADV_ERR_FRAME_ERR_MASK ( \
-                                      IXGBE_RXDADV_ERR_CE | \
-                                      IXGBE_RXDADV_ERR_LE | \
-                                      IXGBE_RXDADV_ERR_PE | \
-                                      IXGBE_RXDADV_ERR_OSE | \
-                                      IXGBE_RXDADV_ERR_USE)
+                                     IXGBE_RXDADV_ERR_CE | \
+                                     IXGBE_RXDADV_ERR_LE | \
+                                     IXGBE_RXDADV_ERR_PE | \
+                                     IXGBE_RXDADV_ERR_OSE | \
+                                     IXGBE_RXDADV_ERR_USE)
 
 /* Multicast bit mask */
 #define IXGBE_MCSTCTRL_MFE      0x4
@@ -2393,9 +2393,9 @@ struct ixgbe_adv_tx_context_desc {
 #define IXGBE_ADVTXD_CC         0x00000080 /* Check Context */
 #define IXGBE_ADVTXD_POPTS_SHIFT      8  /* Adv desc POPTS shift */
 #define IXGBE_ADVTXD_POPTS_IXSM (IXGBE_TXD_POPTS_IXSM << \
-                                 IXGBE_ADVTXD_POPTS_SHIFT)
+                                IXGBE_ADVTXD_POPTS_SHIFT)
 #define IXGBE_ADVTXD_POPTS_TXSM (IXGBE_TXD_POPTS_TXSM << \
-                                 IXGBE_ADVTXD_POPTS_SHIFT)
+                                IXGBE_ADVTXD_POPTS_SHIFT)
 #define IXGBE_ADVTXD_POPTS_ISCO_1ST  0x00000000 /* 1st TSO of iSCSI PDU */
 #define IXGBE_ADVTXD_POPTS_ISCO_MDL  0x00000800 /* Middle TSO of iSCSI PDU */
 #define IXGBE_ADVTXD_POPTS_ISCO_LAST 0x00001000 /* Last TSO of iSCSI PDU */
@@ -2435,10 +2435,10 @@ typedef u32 ixgbe_link_speed;
 #define IXGBE_LINK_SPEED_1GB_FULL  0x0020
 #define IXGBE_LINK_SPEED_10GB_FULL 0x0080
 #define IXGBE_LINK_SPEED_82598_AUTONEG (IXGBE_LINK_SPEED_1GB_FULL | \
-                                        IXGBE_LINK_SPEED_10GB_FULL)
+                                       IXGBE_LINK_SPEED_10GB_FULL)
 #define IXGBE_LINK_SPEED_82599_AUTONEG (IXGBE_LINK_SPEED_100_FULL | \
-                                        IXGBE_LINK_SPEED_1GB_FULL | \
-                                        IXGBE_LINK_SPEED_10GB_FULL)
+                                       IXGBE_LINK_SPEED_1GB_FULL | \
+                                       IXGBE_LINK_SPEED_10GB_FULL)
 
 
 /* Physical layer type */
@@ -2746,7 +2746,7 @@ struct ixgbe_bus_info {
 /* Flow control parameters */
 struct ixgbe_fc_info {
        u32 high_water[MAX_TRAFFIC_CLASS]; /* Flow Control High-water */
-       u32 low_water; /* Flow Control Low-water */
+       u32 low_water[MAX_TRAFFIC_CLASS]; /* Flow Control Low-water */
        u16 pause_time; /* Flow Control Pause timer */
        bool send_xon; /* Flow control send XON */
        bool strict_ieee; /* Strict IEEE mode */
@@ -2840,7 +2840,7 @@ struct ixgbe_hw;
 
 /* iterator type for walking multicast address lists */
 typedef u8* (*ixgbe_mc_addr_itr) (struct ixgbe_hw *hw, u8 **mc_addr_ptr,
-                                  u32 *vmdq);
+                                 u32 *vmdq);
 
 /* Function pointer table */
 struct ixgbe_eeprom_operations {
@@ -2887,7 +2887,7 @@ struct ixgbe_mac_operations {
        s32 (*setup_link)(struct ixgbe_hw *, ixgbe_link_speed, bool);
        s32 (*check_link)(struct ixgbe_hw *, ixgbe_link_speed *, bool *, bool);
        s32 (*get_link_capabilities)(struct ixgbe_hw *, ixgbe_link_speed *,
-                                    bool *);
+                                    bool *);
 
        /* Packet Buffer Manipulation */
        void (*set_rxpba)(struct ixgbe_hw *, int, u32, int);
index 188a5974b85c41f7b8279b6128f72f749e845f76..40dd798e1290efb6b3d67928d86320b12cb767fe 100644 (file)
@@ -81,7 +81,7 @@ static s32 ixgbe_setup_mac_link_X540(struct ixgbe_hw *hw,
                                     bool autoneg_wait_to_complete)
 {
        return hw->phy.ops.setup_link_speed(hw, speed,
-                                           autoneg_wait_to_complete);
+                                           autoneg_wait_to_complete);
 }
 
 /**
@@ -155,7 +155,7 @@ static s32 ixgbe_reset_hw_X540(struct ixgbe_hw *hw)
        /* Add the SAN MAC address to the RAR only if it's a valid address */
        if (is_valid_ether_addr(hw->mac.san_addr)) {
                hw->mac.ops.set_rar(hw, hw->mac.num_rar_entries - 1,
-                                   hw->mac.san_addr, 0, IXGBE_RAH_AV);
+                                   hw->mac.san_addr, 0, IXGBE_RAH_AV);
 
                /* Save the SAN MAC RAR index */
                hw->mac.san_mac_rar_index = hw->mac.num_rar_entries - 1;
@@ -166,7 +166,7 @@ static s32 ixgbe_reset_hw_X540(struct ixgbe_hw *hw)
 
        /* Store the alternative WWNN/WWPN prefix */
        hw->mac.ops.get_wwn_prefix(hw, &hw->mac.wwnn_prefix,
-                                  &hw->mac.wwpn_prefix);
+                                  &hw->mac.wwpn_prefix);
 
 reset_hw_out:
        return status;
@@ -237,9 +237,9 @@ static s32 ixgbe_init_eeprom_params_X540(struct ixgbe_hw *hw)
 
                eec = IXGBE_READ_REG(hw, IXGBE_EEC);
                eeprom_size = (u16)((eec & IXGBE_EEC_SIZE) >>
-                                   IXGBE_EEC_SIZE_SHIFT);
+                                   IXGBE_EEC_SIZE_SHIFT);
                eeprom->word_size = 1 << (eeprom_size +
-                                         IXGBE_EEPROM_WORD_SIZE_SHIFT);
+                                         IXGBE_EEPROM_WORD_SIZE_SHIFT);
 
                hw_dbg(hw, "Eeprom params: type = %d, size = %d\n",
                       eeprom->type, eeprom->word_size);
@@ -712,8 +712,7 @@ static s32 ixgbe_get_swfw_sync_semaphore(struct ixgbe_hw *hw)
                        udelay(50);
                }
        } else {
-               hw_dbg(hw, "Software semaphore SMBI between device drivers "
-                          "not granted.\n");
+               hw_dbg(hw, "Software semaphore SMBI between device drivers not granted.\n");
        }
 
        return status;
@@ -813,7 +812,7 @@ static struct ixgbe_mac_operations mac_ops_X540 = {
        .clear_hw_cntrs         = &ixgbe_clear_hw_cntrs_generic,
        .get_media_type         = &ixgbe_get_media_type_X540,
        .get_supported_physical_layer =
-                                  &ixgbe_get_supported_physical_layer_X540,
+                                 &ixgbe_get_supported_physical_layer_X540,
        .enable_rx_dma          = &ixgbe_enable_rx_dma_generic,
        .get_mac_addr           = &ixgbe_get_mac_addr_generic,
        .get_san_mac_addr       = &ixgbe_get_san_mac_addr_generic,
index 1baecb60f0657e20a4500380ece36a72f908c3d9..d420f124633f046d5b61f6c357656c2a3b13b9f0 100644 (file)
@@ -135,8 +135,8 @@ static int ixgbevf_get_settings(struct net_device *netdev,
                ethtool_cmd_speed_set(ecmd, speed);
                ecmd->duplex = DUPLEX_FULL;
        } else {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
 
        return 0;
@@ -813,5 +813,5 @@ static const struct ethtool_ops ixgbevf_ethtool_ops = {
 
 void ixgbevf_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &ixgbevf_ethtool_ops);
+       netdev->ethtool_ops = &ixgbevf_ethtool_ops;
 }
index de2793b06305528ee1f62da85be27a069df10306..75467f83772ca698795c495623cbae9ce1c2c7ef 100644 (file)
@@ -85,7 +85,7 @@ static DEFINE_PCI_DEVICE_TABLE(ixgbevf_pci_tbl) = {
 MODULE_DEVICE_TABLE(pci, ixgbevf_pci_tbl);
 
 MODULE_AUTHOR("Intel Corporation, <linux.nics@intel.com>");
-MODULE_DESCRIPTION("Intel(R) 82599 Virtual Function Driver");
+MODULE_DESCRIPTION("Intel(R) 10 Gigabit Virtual Function Network Driver");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(DRV_VERSION);
 
index b7b8d74c22d9c6f7e7f9aaa7c9211722ec5929fd..b151a949f352a20ec8e74b4f3a7b6bb194ce841c 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/in.h>
 #include <linux/ip.h>
+#include <net/tso.h>
 #include <linux/tcp.h>
 #include <linux/udp.h>
 #include <linux/etherdevice.h>
@@ -179,10 +180,18 @@ static char mv643xx_eth_driver_version[] = "1.4";
  * Misc definitions.
  */
 #define DEFAULT_RX_QUEUE_SIZE  128
-#define DEFAULT_TX_QUEUE_SIZE  256
+#define DEFAULT_TX_QUEUE_SIZE  512
 #define SKB_DMA_REALIGN                ((PAGE_SIZE - NET_SKB_PAD) % SMP_CACHE_BYTES)
 
+#define TSO_HEADER_SIZE                128
 
+/* Max number of allowed TCP segments for software TSO */
+#define MV643XX_MAX_TSO_SEGS 100
+#define MV643XX_MAX_SKB_DESCS (MV643XX_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS)
+
+#define IS_TSO_HEADER(txq, addr) \
+       ((addr >= txq->tso_hdrs_dma) && \
+        (addr < txq->tso_hdrs_dma + txq->tx_ring_size * TSO_HEADER_SIZE))
 /*
  * RX/TX descriptors.
  */
@@ -250,6 +259,7 @@ struct tx_desc {
 #define GEN_TCP_UDP_CHECKSUM           0x00020000
 #define UDP_FRAME                      0x00010000
 #define MAC_HDR_EXTRA_4_BYTES          0x00008000
+#define GEN_TCP_UDP_CHK_FULL           0x00000400
 #define MAC_HDR_EXTRA_8_BYTES          0x00000200
 
 #define TX_IHL_SHIFT                   11
@@ -345,6 +355,12 @@ struct tx_queue {
        int tx_curr_desc;
        int tx_used_desc;
 
+       int tx_stop_threshold;
+       int tx_wake_threshold;
+
+       char *tso_hdrs;
+       dma_addr_t tso_hdrs_dma;
+
        struct tx_desc *tx_desc_area;
        dma_addr_t tx_desc_dma;
        int tx_desc_area_size;
@@ -491,7 +507,7 @@ static void txq_maybe_wake(struct tx_queue *txq)
 
        if (netif_tx_queue_stopped(nq)) {
                __netif_tx_lock(nq, smp_processor_id());
-               if (txq->tx_ring_size - txq->tx_desc_count >= MAX_SKB_FRAGS + 1)
+               if (txq->tx_desc_count <= txq->tx_wake_threshold)
                        netif_tx_wake_queue(nq);
                __netif_tx_unlock(nq);
        }
@@ -661,6 +677,198 @@ static inline unsigned int has_tiny_unaligned_frags(struct sk_buff *skb)
        return 0;
 }
 
+static inline __be16 sum16_as_be(__sum16 sum)
+{
+       return (__force __be16)sum;
+}
+
+static int skb_tx_csum(struct mv643xx_eth_private *mp, struct sk_buff *skb,
+                      u16 *l4i_chk, u32 *command, int length)
+{
+       int ret;
+       u32 cmd = 0;
+
+       if (skb->ip_summed == CHECKSUM_PARTIAL) {
+               int hdr_len;
+               int tag_bytes;
+
+               BUG_ON(skb->protocol != htons(ETH_P_IP) &&
+                      skb->protocol != htons(ETH_P_8021Q));
+
+               hdr_len = (void *)ip_hdr(skb) - (void *)skb->data;
+               tag_bytes = hdr_len - ETH_HLEN;
+
+               if (length - hdr_len > mp->shared->tx_csum_limit ||
+                   unlikely(tag_bytes & ~12)) {
+                       ret = skb_checksum_help(skb);
+                       if (!ret)
+                               goto no_csum;
+                       return ret;
+               }
+
+               if (tag_bytes & 4)
+                       cmd |= MAC_HDR_EXTRA_4_BYTES;
+               if (tag_bytes & 8)
+                       cmd |= MAC_HDR_EXTRA_8_BYTES;
+
+               cmd |= GEN_TCP_UDP_CHECKSUM | GEN_TCP_UDP_CHK_FULL |
+                          GEN_IP_V4_CHECKSUM   |
+                          ip_hdr(skb)->ihl << TX_IHL_SHIFT;
+
+               /* TODO: Revisit this. With the usage of GEN_TCP_UDP_CHK_FULL
+                * it seems we don't need to pass the initial checksum. */
+               switch (ip_hdr(skb)->protocol) {
+               case IPPROTO_UDP:
+                       cmd |= UDP_FRAME;
+                       *l4i_chk = 0;
+                       break;
+               case IPPROTO_TCP:
+                       *l4i_chk = 0;
+                       break;
+               default:
+                       WARN(1, "protocol not supported");
+               }
+       } else {
+no_csum:
+               /* Errata BTS #50, IHL must be 5 if no HW checksum */
+               cmd |= 5 << TX_IHL_SHIFT;
+       }
+       *command = cmd;
+       return 0;
+}
+
+static inline int
+txq_put_data_tso(struct net_device *dev, struct tx_queue *txq,
+                struct sk_buff *skb, char *data, int length,
+                bool last_tcp, bool is_last)
+{
+       int tx_index;
+       u32 cmd_sts;
+       struct tx_desc *desc;
+
+       tx_index = txq->tx_curr_desc++;
+       if (txq->tx_curr_desc == txq->tx_ring_size)
+               txq->tx_curr_desc = 0;
+       desc = &txq->tx_desc_area[tx_index];
+
+       desc->l4i_chk = 0;
+       desc->byte_cnt = length;
+       desc->buf_ptr = dma_map_single(dev->dev.parent, data,
+                                      length, DMA_TO_DEVICE);
+       if (unlikely(dma_mapping_error(dev->dev.parent, desc->buf_ptr))) {
+               WARN(1, "dma_map_single failed!\n");
+               return -ENOMEM;
+       }
+
+       cmd_sts = BUFFER_OWNED_BY_DMA;
+       if (last_tcp) {
+               /* last descriptor in the TCP packet */
+               cmd_sts |= ZERO_PADDING | TX_LAST_DESC;
+               /* last descriptor in SKB */
+               if (is_last)
+                       cmd_sts |= TX_ENABLE_INTERRUPT;
+       }
+       desc->cmd_sts = cmd_sts;
+       return 0;
+}
+
+static inline void
+txq_put_hdr_tso(struct sk_buff *skb, struct tx_queue *txq, int length)
+{
+       struct mv643xx_eth_private *mp = txq_to_mp(txq);
+       int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+       int tx_index;
+       struct tx_desc *desc;
+       int ret;
+       u32 cmd_csum = 0;
+       u16 l4i_chk = 0;
+
+       tx_index = txq->tx_curr_desc;
+       desc = &txq->tx_desc_area[tx_index];
+
+       ret = skb_tx_csum(mp, skb, &l4i_chk, &cmd_csum, length);
+       if (ret)
+               WARN(1, "failed to prepare checksum!");
+
+       /* Should we set this? Can't use the value from skb_tx_csum()
+        * as it's not the correct initial L4 checksum to use. */
+       desc->l4i_chk = 0;
+
+       desc->byte_cnt = hdr_len;
+       desc->buf_ptr = txq->tso_hdrs_dma +
+                       txq->tx_curr_desc * TSO_HEADER_SIZE;
+       desc->cmd_sts = cmd_csum | BUFFER_OWNED_BY_DMA  | TX_FIRST_DESC |
+                                  GEN_CRC;
+
+       txq->tx_curr_desc++;
+       if (txq->tx_curr_desc == txq->tx_ring_size)
+               txq->tx_curr_desc = 0;
+}
+
+static int txq_submit_tso(struct tx_queue *txq, struct sk_buff *skb,
+                         struct net_device *dev)
+{
+       struct mv643xx_eth_private *mp = txq_to_mp(txq);
+       int total_len, data_left, ret;
+       int desc_count = 0;
+       struct tso_t tso;
+       int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+
+       /* Count needed descriptors */
+       if ((txq->tx_desc_count + tso_count_descs(skb)) >= txq->tx_ring_size) {
+               netdev_dbg(dev, "not enough descriptors for TSO!\n");
+               return -EBUSY;
+       }
+
+       /* Initialize the TSO handler, and prepare the first payload */
+       tso_start(skb, &tso);
+
+       total_len = skb->len - hdr_len;
+       while (total_len > 0) {
+               char *hdr;
+
+               data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len);
+               total_len -= data_left;
+               desc_count++;
+
+               /* prepare packet headers: MAC + IP + TCP */
+               hdr = txq->tso_hdrs + txq->tx_curr_desc * TSO_HEADER_SIZE;
+               tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0);
+               txq_put_hdr_tso(skb, txq, data_left);
+
+               while (data_left > 0) {
+                       int size;
+                       desc_count++;
+
+                       size = min_t(int, tso.size, data_left);
+                       ret = txq_put_data_tso(dev, txq, skb, tso.data, size,
+                                              size == data_left,
+                                              total_len == 0);
+                       if (ret)
+                               goto err_release;
+                       data_left -= size;
+                       tso_build_data(skb, &tso, size);
+               }
+       }
+
+       __skb_queue_tail(&txq->tx_skb, skb);
+       skb_tx_timestamp(skb);
+
+       /* clear TX_END status */
+       mp->work_tx_end &= ~(1 << txq->index);
+
+       /* ensure all descriptors are written before poking hardware */
+       wmb();
+       txq_enable(txq);
+       txq->tx_desc_count += desc_count;
+       return 0;
+err_release:
+       /* TODO: Release all used data descriptors; header descriptors must not
+        * be DMA-unmapped.
+        */
+       return ret;
+}
+
 static void txq_submit_frag_skb(struct tx_queue *txq, struct sk_buff *skb)
 {
        struct mv643xx_eth_private *mp = txq_to_mp(txq);
@@ -671,8 +879,10 @@ static void txq_submit_frag_skb(struct tx_queue *txq, struct sk_buff *skb)
                skb_frag_t *this_frag;
                int tx_index;
                struct tx_desc *desc;
+               void *addr;
 
                this_frag = &skb_shinfo(skb)->frags[frag];
+               addr = page_address(this_frag->page.p) + this_frag->page_offset;
                tx_index = txq->tx_curr_desc++;
                if (txq->tx_curr_desc == txq->tx_ring_size)
                        txq->tx_curr_desc = 0;
@@ -692,19 +902,13 @@ static void txq_submit_frag_skb(struct tx_queue *txq, struct sk_buff *skb)
 
                desc->l4i_chk = 0;
                desc->byte_cnt = skb_frag_size(this_frag);
-               desc->buf_ptr = skb_frag_dma_map(mp->dev->dev.parent,
-                                                this_frag, 0,
-                                                skb_frag_size(this_frag),
-                                                DMA_TO_DEVICE);
+               desc->buf_ptr = dma_map_single(mp->dev->dev.parent, addr,
+                                              desc->byte_cnt, DMA_TO_DEVICE);
        }
 }
 
-static inline __be16 sum16_as_be(__sum16 sum)
-{
-       return (__force __be16)sum;
-}
-
-static int txq_submit_skb(struct tx_queue *txq, struct sk_buff *skb)
+static int txq_submit_skb(struct tx_queue *txq, struct sk_buff *skb,
+                         struct net_device *dev)
 {
        struct mv643xx_eth_private *mp = txq_to_mp(txq);
        int nr_frags = skb_shinfo(skb)->nr_frags;
@@ -712,54 +916,22 @@ static int txq_submit_skb(struct tx_queue *txq, struct sk_buff *skb)
        struct tx_desc *desc;
        u32 cmd_sts;
        u16 l4i_chk;
-       int length;
+       int length, ret;
 
-       cmd_sts = TX_FIRST_DESC | GEN_CRC | BUFFER_OWNED_BY_DMA;
+       cmd_sts = 0;
        l4i_chk = 0;
 
-       if (skb->ip_summed == CHECKSUM_PARTIAL) {
-               int hdr_len;
-               int tag_bytes;
-
-               BUG_ON(skb->protocol != htons(ETH_P_IP) &&
-                      skb->protocol != htons(ETH_P_8021Q));
-
-               hdr_len = (void *)ip_hdr(skb) - (void *)skb->data;
-               tag_bytes = hdr_len - ETH_HLEN;
-               if (skb->len - hdr_len > mp->shared->tx_csum_limit ||
-                   unlikely(tag_bytes & ~12)) {
-                       if (skb_checksum_help(skb) == 0)
-                               goto no_csum;
-                       dev_kfree_skb_any(skb);
-                       return 1;
-               }
-
-               if (tag_bytes & 4)
-                       cmd_sts |= MAC_HDR_EXTRA_4_BYTES;
-               if (tag_bytes & 8)
-                       cmd_sts |= MAC_HDR_EXTRA_8_BYTES;
-
-               cmd_sts |= GEN_TCP_UDP_CHECKSUM |
-                          GEN_IP_V4_CHECKSUM   |
-                          ip_hdr(skb)->ihl << TX_IHL_SHIFT;
-
-               switch (ip_hdr(skb)->protocol) {
-               case IPPROTO_UDP:
-                       cmd_sts |= UDP_FRAME;
-                       l4i_chk = ntohs(sum16_as_be(udp_hdr(skb)->check));
-                       break;
-               case IPPROTO_TCP:
-                       l4i_chk = ntohs(sum16_as_be(tcp_hdr(skb)->check));
-                       break;
-               default:
-                       BUG();
-               }
-       } else {
-no_csum:
-               /* Errata BTS #50, IHL must be 5 if no HW checksum */
-               cmd_sts |= 5 << TX_IHL_SHIFT;
+       if (txq->tx_ring_size - txq->tx_desc_count < MAX_SKB_FRAGS + 1) {
+               if (net_ratelimit())
+                       netdev_err(dev, "tx queue full?!\n");
+               return -EBUSY;
        }
 
+       ret = skb_tx_csum(mp, skb, &l4i_chk, &cmd_sts, skb->len);
+       if (ret)
+               return ret;
+       cmd_sts |= TX_FIRST_DESC | GEN_CRC | BUFFER_OWNED_BY_DMA;
+
        tx_index = txq->tx_curr_desc++;
        if (txq->tx_curr_desc == txq->tx_ring_size)
                txq->tx_curr_desc = 0;
@@ -801,7 +973,7 @@ static int txq_submit_skb(struct tx_queue *txq, struct sk_buff *skb)
 static netdev_tx_t mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct mv643xx_eth_private *mp = netdev_priv(dev);
-       int length, queue;
+       int length, queue, ret;
        struct tx_queue *txq;
        struct netdev_queue *nq;
 
@@ -810,30 +982,26 @@ static netdev_tx_t mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev)
        nq = netdev_get_tx_queue(dev, queue);
 
        if (has_tiny_unaligned_frags(skb) && __skb_linearize(skb)) {
-               txq->tx_dropped++;
                netdev_printk(KERN_DEBUG, dev,
                              "failed to linearize skb with tiny unaligned fragment\n");
                return NETDEV_TX_BUSY;
        }
 
-       if (txq->tx_ring_size - txq->tx_desc_count < MAX_SKB_FRAGS + 1) {
-               if (net_ratelimit())
-                       netdev_err(dev, "tx queue full?!\n");
-               dev_kfree_skb_any(skb);
-               return NETDEV_TX_OK;
-       }
-
        length = skb->len;
 
-       if (!txq_submit_skb(txq, skb)) {
-               int entries_left;
-
+       if (skb_is_gso(skb))
+               ret = txq_submit_tso(txq, skb, dev);
+       else
+               ret = txq_submit_skb(txq, skb, dev);
+       if (!ret) {
                txq->tx_bytes += length;
                txq->tx_packets++;
 
-               entries_left = txq->tx_ring_size - txq->tx_desc_count;
-               if (entries_left < MAX_SKB_FRAGS + 1)
+               if (txq->tx_desc_count >= txq->tx_stop_threshold)
                        netif_tx_stop_queue(nq);
+       } else {
+               txq->tx_dropped++;
+               dev_kfree_skb_any(skb);
        }
 
        return NETDEV_TX_OK;
@@ -907,14 +1075,9 @@ static int txq_reclaim(struct tx_queue *txq, int budget, int force)
                        mp->dev->stats.tx_errors++;
                }
 
-               if (cmd_sts & TX_FIRST_DESC) {
+               if (!IS_TSO_HEADER(txq, desc->buf_ptr))
                        dma_unmap_single(mp->dev->dev.parent, desc->buf_ptr,
                                         desc->byte_cnt, DMA_TO_DEVICE);
-               } else {
-                       dma_unmap_page(mp->dev->dev.parent, desc->buf_ptr,
-                                      desc->byte_cnt, DMA_TO_DEVICE);
-               }
-
                dev_kfree_skb(skb);
        }
 
@@ -1010,8 +1173,9 @@ static void txq_set_fixed_prio_mode(struct tx_queue *txq)
 
 
 /* mii management interface *************************************************/
-static void mv643xx_adjust_pscr(struct mv643xx_eth_private *mp)
+static void mv643xx_eth_adjust_link(struct net_device *dev)
 {
+       struct mv643xx_eth_private *mp = netdev_priv(dev);
        u32 pscr = rdlp(mp, PORT_SERIAL_CONTROL);
        u32 autoneg_disable = FORCE_LINK_PASS |
                     DISABLE_AUTO_NEG_SPEED_GMII |
@@ -1387,7 +1551,7 @@ mv643xx_eth_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 
        ret = phy_ethtool_sset(mp->phy, cmd);
        if (!ret)
-               mv643xx_adjust_pscr(mp);
+               mv643xx_eth_adjust_link(dev);
        return ret;
 }
 
@@ -1456,7 +1620,11 @@ mv643xx_eth_set_ringparam(struct net_device *dev, struct ethtool_ringparam *er)
                return -EINVAL;
 
        mp->rx_ring_size = er->rx_pending < 4096 ? er->rx_pending : 4096;
-       mp->tx_ring_size = er->tx_pending < 4096 ? er->tx_pending : 4096;
+       mp->tx_ring_size = clamp_t(unsigned int, er->tx_pending,
+                                  MV643XX_MAX_SKB_DESCS * 2, 4096);
+       if (mp->tx_ring_size != er->tx_pending)
+               netdev_warn(dev, "TX queue size set to %u (requested %u)\n",
+                           mp->tx_ring_size, er->tx_pending);
 
        if (netif_running(dev)) {
                mv643xx_eth_stop(dev);
@@ -1832,6 +2000,13 @@ static int txq_init(struct mv643xx_eth_private *mp, int index)
 
        txq->tx_ring_size = mp->tx_ring_size;
 
+       /* A queue must always have room for at least one skb.
+        * Therefore, stop the queue when the free entries reaches
+        * the maximum number of descriptors per skb.
+        */
+       txq->tx_stop_threshold = txq->tx_ring_size - MV643XX_MAX_SKB_DESCS;
+       txq->tx_wake_threshold = txq->tx_stop_threshold / 2;
+
        txq->tx_desc_count = 0;
        txq->tx_curr_desc = 0;
        txq->tx_used_desc = 0;
@@ -1871,6 +2046,15 @@ static int txq_init(struct mv643xx_eth_private *mp, int index)
                                        nexti * sizeof(struct tx_desc);
        }
 
+       /* Allocate DMA buffers for TSO MAC/IP/TCP headers */
+       txq->tso_hdrs = dma_alloc_coherent(mp->dev->dev.parent,
+                                          txq->tx_ring_size * TSO_HEADER_SIZE,
+                                          &txq->tso_hdrs_dma, GFP_KERNEL);
+       if (txq->tso_hdrs == NULL) {
+               dma_free_coherent(mp->dev->dev.parent, txq->tx_desc_area_size,
+                                 txq->tx_desc_area, txq->tx_desc_dma);
+               return -ENOMEM;
+       }
        skb_queue_head_init(&txq->tx_skb);
 
        return 0;
@@ -1891,6 +2075,10 @@ static void txq_deinit(struct tx_queue *txq)
        else
                dma_free_coherent(mp->dev->dev.parent, txq->tx_desc_area_size,
                                  txq->tx_desc_area, txq->tx_desc_dma);
+       if (txq->tso_hdrs)
+               dma_free_coherent(mp->dev->dev.parent,
+                                 txq->tx_ring_size * TSO_HEADER_SIZE,
+                                 txq->tso_hdrs, txq->tso_hdrs_dma);
 }
 
 
@@ -2303,7 +2491,7 @@ static int mv643xx_eth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 
        ret = phy_mii_ioctl(mp->phy, ifr, cmd);
        if (!ret)
-               mv643xx_adjust_pscr(mp);
+               mv643xx_eth_adjust_link(dev);
        return ret;
 }
 
@@ -2678,6 +2866,7 @@ static void set_params(struct mv643xx_eth_private *mp,
                       struct mv643xx_eth_platform_data *pd)
 {
        struct net_device *dev = mp->dev;
+       unsigned int tx_ring_size;
 
        if (is_valid_ether_addr(pd->mac_addr))
                memcpy(dev->dev_addr, pd->mac_addr, ETH_ALEN);
@@ -2692,22 +2881,22 @@ static void set_params(struct mv643xx_eth_private *mp,
 
        mp->rxq_count = pd->rx_queue_count ? : 1;
 
-       mp->tx_ring_size = DEFAULT_TX_QUEUE_SIZE;
+       tx_ring_size = DEFAULT_TX_QUEUE_SIZE;
        if (pd->tx_queue_size)
-               mp->tx_ring_size = pd->tx_queue_size;
+               tx_ring_size = pd->tx_queue_size;
+
+       mp->tx_ring_size = clamp_t(unsigned int, tx_ring_size,
+                                  MV643XX_MAX_SKB_DESCS * 2, 4096);
+       if (mp->tx_ring_size != tx_ring_size)
+               netdev_warn(dev, "TX queue size set to %u (requested %u)\n",
+                           mp->tx_ring_size, tx_ring_size);
+
        mp->tx_desc_sram_addr = pd->tx_sram_addr;
        mp->tx_desc_sram_size = pd->tx_sram_size;
 
        mp->txq_count = pd->tx_queue_count ? : 1;
 }
 
-static void mv643xx_eth_adjust_link(struct net_device *dev)
-{
-       struct mv643xx_eth_private *mp = netdev_priv(dev);
-
-       mv643xx_adjust_pscr(mp);
-}
-
 static struct phy_device *phy_scan(struct mv643xx_eth_private *mp,
                                   int phy_addr)
 {
@@ -2889,7 +3078,7 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
        if (err)
                goto out;
 
-       SET_ETHTOOL_OPS(dev, &mv643xx_eth_ethtool_ops);
+       dev->ethtool_ops = &mv643xx_eth_ethtool_ops;
 
        init_pscr(mp, pd->speed, pd->duplex);
 
@@ -2921,11 +3110,14 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
        dev->watchdog_timeo = 2 * HZ;
        dev->base_addr = 0;
 
-       dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
-       dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
-       dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM;
+       dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
+       dev->vlan_features = dev->features;
+
+       dev->features |= NETIF_F_RXCSUM;
+       dev->hw_features = dev->features;
 
        dev->priv_flags |= IFF_UNICAST_FLT;
+       dev->gso_max_segs = MV643XX_MAX_TSO_SEGS;
 
        SET_NETDEV_DEV(dev, &pdev->dev);
 
index 9d5ced263a5eb3d1397e95b675a2f83e71432547..fc2fb25343f417964070c058baea2e8162dab595 100644 (file)
@@ -195,11 +195,10 @@ static int orion_mdio_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       bus = mdiobus_alloc_size(sizeof(struct orion_mdio_dev));
-       if (!bus) {
-               dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
+       bus = devm_mdiobus_alloc_size(&pdev->dev,
+                                     sizeof(struct orion_mdio_dev));
+       if (!bus)
                return -ENOMEM;
-       }
 
        bus->name = "orion_mdio_bus";
        bus->read = orion_mdio_read;
@@ -208,11 +207,10 @@ static int orion_mdio_probe(struct platform_device *pdev)
                 dev_name(&pdev->dev));
        bus->parent = &pdev->dev;
 
-       bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
-       if (!bus->irq) {
-               mdiobus_free(bus);
+       bus->irq = devm_kmalloc_array(&pdev->dev, PHY_MAX_ADDR, sizeof(int),
+                                     GFP_KERNEL);
+       if (!bus->irq)
                return -ENOMEM;
-       }
 
        for (i = 0; i < PHY_MAX_ADDR; i++)
                bus->irq[i] = PHY_POLL;
@@ -264,8 +262,6 @@ static int orion_mdio_probe(struct platform_device *pdev)
 out_mdio:
        if (!IS_ERR(dev->clk))
                clk_disable_unprepare(dev->clk);
-       kfree(bus->irq);
-       mdiobus_free(bus);
        return ret;
 }
 
@@ -276,8 +272,6 @@ static int orion_mdio_remove(struct platform_device *pdev)
 
        writel(0, dev->regs + MVMDIO_ERR_INT_MASK);
        mdiobus_unregister(bus);
-       kfree(bus->irq);
-       mdiobus_free(bus);
        if (!IS_ERR(dev->clk))
                clk_disable_unprepare(dev->clk);
 
index 14786c8bf99efcddbbbdff7bc0f9ee9e20933864..45beca17fa50a3d1e4f920ad44097a3df3d73d07 100644 (file)
@@ -23,6 +23,7 @@
 #include <net/ip.h>
 #include <net/ipv6.h>
 #include <linux/io.h>
+#include <net/tso.h>
 #include <linux/of.h>
 #include <linux/of_irq.h>
 #include <linux/of_mdio.h>
 #define MVNETA_RX_COAL_PKTS            32
 #define MVNETA_RX_COAL_USEC            100
 
-/* Napi polling weight */
-#define MVNETA_RX_POLL_WEIGHT          64
-
 /* The two bytes Marvell header. Either contains a special value used
  * by Marvell switches when a specific hardware mode is enabled (not
  * supported by this driver) or is filled automatically by zeroes on
 
 #define MVNETA_TX_MTU_MAX              0x3ffff
 
+/* TSO header size */
+#define TSO_HEADER_SIZE 128
+
 /* Max number of Rx descriptors */
 #define MVNETA_MAX_RXD 128
 
 /* Max number of Tx descriptors */
 #define MVNETA_MAX_TXD 532
 
+/* Max number of allowed TCP segments for software TSO */
+#define MVNETA_MAX_TSO_SEGS 100
+
+#define MVNETA_MAX_SKB_DESCS (MVNETA_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS)
+
 /* descriptor aligned size */
 #define MVNETA_DESC_ALIGNED_SIZE       32
 
              ETH_HLEN + ETH_FCS_LEN,                        \
              MVNETA_CPU_D_CACHE_LINE_SIZE)
 
+#define IS_TSO_HEADER(txq, addr) \
+       ((addr >= txq->tso_hdrs_phys) && \
+        (addr < txq->tso_hdrs_phys + txq->size * TSO_HEADER_SIZE))
+
 #define MVNETA_RX_BUF_SIZE(pkt_size)   ((pkt_size) + NET_SKB_PAD)
 
 struct mvneta_pcpu_stats {
@@ -279,9 +289,6 @@ struct mvneta_port {
        u32 cause_rx_tx;
        struct napi_struct napi;
 
-       /* Napi weight */
-       int weight;
-
        /* Core clock */
        struct clk *clk;
        u8 mcast_count[256];
@@ -390,6 +397,8 @@ struct mvneta_tx_queue {
         * descriptor ring
         */
        int count;
+       int tx_stop_threshold;
+       int tx_wake_threshold;
 
        /* Array of transmitted skb */
        struct sk_buff **tx_skb;
@@ -413,6 +422,12 @@ struct mvneta_tx_queue {
 
        /* Index of the next TX DMA descriptor to process */
        int next_desc_to_proc;
+
+       /* DMA buffers for TSO headers */
+       char *tso_hdrs;
+
+       /* DMA address of TSO headers */
+       dma_addr_t tso_hdrs_phys;
 };
 
 struct mvneta_rx_queue {
@@ -441,7 +456,10 @@ struct mvneta_rx_queue {
        int next_desc_to_proc;
 };
 
-static int rxq_number = 8;
+/* The hardware supports eight (8) rx queues, but we are only allowing
+ * the first one to be used. Therefore, let's just allocate one queue.
+ */
+static int rxq_number = 1;
 static int txq_number = 8;
 
 static int rxq_def;
@@ -1277,11 +1295,12 @@ static void mvneta_txq_bufs_free(struct mvneta_port *pp,
 
                mvneta_txq_inc_get(txq);
 
+               if (!IS_TSO_HEADER(txq, tx_desc->buf_phys_addr))
+                       dma_unmap_single(pp->dev->dev.parent,
+                                        tx_desc->buf_phys_addr,
+                                        tx_desc->data_size, DMA_TO_DEVICE);
                if (!skb)
                        continue;
-
-               dma_unmap_single(pp->dev->dev.parent, tx_desc->buf_phys_addr,
-                                tx_desc->data_size, DMA_TO_DEVICE);
                dev_kfree_skb_any(skb);
        }
 }
@@ -1302,7 +1321,7 @@ static void mvneta_txq_done(struct mvneta_port *pp,
        txq->count -= tx_done;
 
        if (netif_tx_queue_stopped(nq)) {
-               if (txq->size - txq->count >= MAX_SKB_FRAGS + 1)
+               if (txq->count <= txq->tx_wake_threshold)
                        netif_tx_wake_queue(nq);
        }
 }
@@ -1519,14 +1538,134 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo,
        return rx_done;
 }
 
+static inline void
+mvneta_tso_put_hdr(struct sk_buff *skb,
+                  struct mvneta_port *pp, struct mvneta_tx_queue *txq)
+{
+       struct mvneta_tx_desc *tx_desc;
+       int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+
+       txq->tx_skb[txq->txq_put_index] = NULL;
+       tx_desc = mvneta_txq_next_desc_get(txq);
+       tx_desc->data_size = hdr_len;
+       tx_desc->command = mvneta_skb_tx_csum(pp, skb);
+       tx_desc->command |= MVNETA_TXD_F_DESC;
+       tx_desc->buf_phys_addr = txq->tso_hdrs_phys +
+                                txq->txq_put_index * TSO_HEADER_SIZE;
+       mvneta_txq_inc_put(txq);
+}
+
+static inline int
+mvneta_tso_put_data(struct net_device *dev, struct mvneta_tx_queue *txq,
+                   struct sk_buff *skb, char *data, int size,
+                   bool last_tcp, bool is_last)
+{
+       struct mvneta_tx_desc *tx_desc;
+
+       tx_desc = mvneta_txq_next_desc_get(txq);
+       tx_desc->data_size = size;
+       tx_desc->buf_phys_addr = dma_map_single(dev->dev.parent, data,
+                                               size, DMA_TO_DEVICE);
+       if (unlikely(dma_mapping_error(dev->dev.parent,
+                    tx_desc->buf_phys_addr))) {
+               mvneta_txq_desc_put(txq);
+               return -ENOMEM;
+       }
+
+       tx_desc->command = 0;
+       txq->tx_skb[txq->txq_put_index] = NULL;
+
+       if (last_tcp) {
+               /* last descriptor in the TCP packet */
+               tx_desc->command = MVNETA_TXD_L_DESC;
+
+               /* last descriptor in SKB */
+               if (is_last)
+                       txq->tx_skb[txq->txq_put_index] = skb;
+       }
+       mvneta_txq_inc_put(txq);
+       return 0;
+}
+
+static int mvneta_tx_tso(struct sk_buff *skb, struct net_device *dev,
+                        struct mvneta_tx_queue *txq)
+{
+       int total_len, data_left;
+       int desc_count = 0;
+       struct mvneta_port *pp = netdev_priv(dev);
+       struct tso_t tso;
+       int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+       int i;
+
+       /* Count needed descriptors */
+       if ((txq->count + tso_count_descs(skb)) >= txq->size)
+               return 0;
+
+       if (skb_headlen(skb) < (skb_transport_offset(skb) + tcp_hdrlen(skb))) {
+               pr_info("*** Is this even  possible???!?!?\n");
+               return 0;
+       }
+
+       /* Initialize the TSO handler, and prepare the first payload */
+       tso_start(skb, &tso);
+
+       total_len = skb->len - hdr_len;
+       while (total_len > 0) {
+               char *hdr;
+
+               data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len);
+               total_len -= data_left;
+               desc_count++;
+
+               /* prepare packet headers: MAC + IP + TCP */
+               hdr = txq->tso_hdrs + txq->txq_put_index * TSO_HEADER_SIZE;
+               tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0);
+
+               mvneta_tso_put_hdr(skb, pp, txq);
+
+               while (data_left > 0) {
+                       int size;
+                       desc_count++;
+
+                       size = min_t(int, tso.size, data_left);
+
+                       if (mvneta_tso_put_data(dev, txq, skb,
+                                                tso.data, size,
+                                                size == data_left,
+                                                total_len == 0))
+                               goto err_release;
+                       data_left -= size;
+
+                       tso_build_data(skb, &tso, size);
+               }
+       }
+
+       return desc_count;
+
+err_release:
+       /* Release all used data descriptors; header descriptors must not
+        * be DMA-unmapped.
+        */
+       for (i = desc_count - 1; i >= 0; i--) {
+               struct mvneta_tx_desc *tx_desc = txq->descs + i;
+               if (!IS_TSO_HEADER(txq, tx_desc->buf_phys_addr))
+                       dma_unmap_single(pp->dev->dev.parent,
+                                        tx_desc->buf_phys_addr,
+                                        tx_desc->data_size,
+                                        DMA_TO_DEVICE);
+               mvneta_txq_desc_put(txq);
+       }
+       return 0;
+}
+
 /* Handle tx fragmentation processing */
 static int mvneta_tx_frag_process(struct mvneta_port *pp, struct sk_buff *skb,
                                  struct mvneta_tx_queue *txq)
 {
        struct mvneta_tx_desc *tx_desc;
-       int i;
+       int i, nr_frags = skb_shinfo(skb)->nr_frags;
 
-       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+       for (i = 0; i < nr_frags; i++) {
                skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
                void *addr = page_address(frag->page.p) + frag->page_offset;
 
@@ -1543,20 +1682,16 @@ static int mvneta_tx_frag_process(struct mvneta_port *pp, struct sk_buff *skb,
                        goto error;
                }
 
-               if (i == (skb_shinfo(skb)->nr_frags - 1)) {
+               if (i == nr_frags - 1) {
                        /* Last descriptor */
                        tx_desc->command = MVNETA_TXD_L_DESC | MVNETA_TXD_Z_PAD;
-
                        txq->tx_skb[txq->txq_put_index] = skb;
-
-                       mvneta_txq_inc_put(txq);
                } else {
                        /* Descriptor in the middle: Not First, Not Last */
                        tx_desc->command = 0;
-
                        txq->tx_skb[txq->txq_put_index] = NULL;
-                       mvneta_txq_inc_put(txq);
                }
+               mvneta_txq_inc_put(txq);
        }
 
        return 0;
@@ -1584,15 +1719,18 @@ static int mvneta_tx(struct sk_buff *skb, struct net_device *dev)
        u16 txq_id = skb_get_queue_mapping(skb);
        struct mvneta_tx_queue *txq = &pp->txqs[txq_id];
        struct mvneta_tx_desc *tx_desc;
-       struct netdev_queue *nq;
        int frags = 0;
        u32 tx_cmd;
 
        if (!netif_running(dev))
                goto out;
 
+       if (skb_is_gso(skb)) {
+               frags = mvneta_tx_tso(skb, dev, txq);
+               goto out;
+       }
+
        frags = skb_shinfo(skb)->nr_frags + 1;
-       nq    = netdev_get_tx_queue(dev, txq_id);
 
        /* Get a descriptor for the first part of the packet */
        tx_desc = mvneta_txq_next_desc_get(txq);
@@ -1635,15 +1773,16 @@ static int mvneta_tx(struct sk_buff *skb, struct net_device *dev)
                }
        }
 
-       txq->count += frags;
-       mvneta_txq_pend_desc_add(pp, txq, frags);
-
-       if (txq->size - txq->count < MAX_SKB_FRAGS + 1)
-               netif_tx_stop_queue(nq);
-
 out:
        if (frags > 0) {
                struct mvneta_pcpu_stats *stats = this_cpu_ptr(pp->stats);
+               struct netdev_queue *nq = netdev_get_tx_queue(dev, txq_id);
+
+               txq->count += frags;
+               mvneta_txq_pend_desc_add(pp, txq, frags);
+
+               if (txq->count >= txq->tx_stop_threshold)
+                       netif_tx_stop_queue(nq);
 
                u64_stats_update_begin(&stats->syncp);
                stats->tx_packets++;
@@ -2003,7 +2142,7 @@ static void mvneta_tx_reset(struct mvneta_port *pp)
 {
        int queue;
 
-       /* free the skb's in the hal tx ring */
+       /* free the skb's in the tx ring */
        for (queue = 0; queue < txq_number; queue++)
                mvneta_txq_done_force(pp, &pp->txqs[queue]);
 
@@ -2081,6 +2220,14 @@ static int mvneta_txq_init(struct mvneta_port *pp,
 {
        txq->size = pp->tx_ring_size;
 
+       /* A queue must always have room for at least one skb.
+        * Therefore, stop the queue when the free entries reaches
+        * the maximum number of descriptors per skb.
+        */
+       txq->tx_stop_threshold = txq->size - MVNETA_MAX_SKB_DESCS;
+       txq->tx_wake_threshold = txq->tx_stop_threshold / 2;
+
+
        /* Allocate memory for TX descriptors */
        txq->descs = dma_alloc_coherent(pp->dev->dev.parent,
                                        txq->size * MVNETA_DESC_ALIGNED_SIZE,
@@ -2109,6 +2256,18 @@ static int mvneta_txq_init(struct mvneta_port *pp,
                                  txq->descs, txq->descs_phys);
                return -ENOMEM;
        }
+
+       /* Allocate DMA buffers for TSO MAC/IP/TCP headers */
+       txq->tso_hdrs = dma_alloc_coherent(pp->dev->dev.parent,
+                                          txq->size * TSO_HEADER_SIZE,
+                                          &txq->tso_hdrs_phys, GFP_KERNEL);
+       if (txq->tso_hdrs == NULL) {
+               kfree(txq->tx_skb);
+               dma_free_coherent(pp->dev->dev.parent,
+                                 txq->size * MVNETA_DESC_ALIGNED_SIZE,
+                                 txq->descs, txq->descs_phys);
+               return -ENOMEM;
+       }
        mvneta_tx_done_pkts_coal_set(pp, txq, txq->done_pkts_coal);
 
        return 0;
@@ -2120,6 +2279,10 @@ static void mvneta_txq_deinit(struct mvneta_port *pp,
 {
        kfree(txq->tx_skb);
 
+       if (txq->tso_hdrs)
+               dma_free_coherent(pp->dev->dev.parent,
+                                 txq->size * TSO_HEADER_SIZE,
+                                 txq->tso_hdrs, txq->tso_hdrs_phys);
        if (txq->descs)
                dma_free_coherent(pp->dev->dev.parent,
                                  txq->size * MVNETA_DESC_ALIGNED_SIZE,
@@ -2279,24 +2442,28 @@ static int mvneta_change_mtu(struct net_device *dev, int mtu)
                return 0;
 
        /* The interface is running, so we have to force a
-        * reallocation of the RXQs
+        * reallocation of the queues
         */
        mvneta_stop_dev(pp);
 
        mvneta_cleanup_txqs(pp);
        mvneta_cleanup_rxqs(pp);
 
-       pp->pkt_size = MVNETA_RX_PKT_SIZE(pp->dev->mtu);
+       pp->pkt_size = MVNETA_RX_PKT_SIZE(dev->mtu);
        pp->frag_size = SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(pp->pkt_size)) +
                        SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
 
        ret = mvneta_setup_rxqs(pp);
        if (ret) {
-               netdev_err(pp->dev, "unable to setup rxqs after MTU change\n");
+               netdev_err(dev, "unable to setup rxqs after MTU change\n");
                return ret;
        }
 
-       mvneta_setup_txqs(pp);
+       ret = mvneta_setup_txqs(pp);
+       if (ret) {
+               netdev_err(dev, "unable to setup txqs after MTU change\n");
+               return ret;
+       }
 
        mvneta_start_dev(pp);
        mvneta_port_up(pp);
@@ -2323,22 +2490,19 @@ static void mvneta_get_mac_addr(struct mvneta_port *pp, unsigned char *addr)
 static int mvneta_set_mac_addr(struct net_device *dev, void *addr)
 {
        struct mvneta_port *pp = netdev_priv(dev);
-       u8 *mac = addr + 2;
-       int i;
-
-       if (netif_running(dev))
-               return -EBUSY;
+       struct sockaddr *sockaddr = addr;
+       int ret;
 
+       ret = eth_prepare_mac_addr_change(dev, addr);
+       if (ret < 0)
+               return ret;
        /* Remove previous address table entry */
        mvneta_mac_addr_set(pp, dev->dev_addr, -1);
 
        /* Set new addr in hw */
-       mvneta_mac_addr_set(pp, mac, rxq_def);
-
-       /* Set addr in the device */
-       for (i = 0; i < ETH_ALEN; i++)
-               dev->dev_addr[i] = mac[i];
+       mvneta_mac_addr_set(pp, sockaddr->sa_data, rxq_def);
 
+       eth_commit_mac_addr_change(dev, addr);
        return 0;
 }
 
@@ -2433,8 +2597,6 @@ static int mvneta_open(struct net_device *dev)
        struct mvneta_port *pp = netdev_priv(dev);
        int ret;
 
-       mvneta_mac_addr_set(pp, dev->dev_addr, rxq_def);
-
        pp->pkt_size = MVNETA_RX_PKT_SIZE(pp->dev->mtu);
        pp->frag_size = SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(pp->pkt_size)) +
                        SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
@@ -2600,8 +2762,12 @@ static int mvneta_ethtool_set_ringparam(struct net_device *dev,
                return -EINVAL;
        pp->rx_ring_size = ring->rx_pending < MVNETA_MAX_RXD ?
                ring->rx_pending : MVNETA_MAX_RXD;
-       pp->tx_ring_size = ring->tx_pending < MVNETA_MAX_TXD ?
-               ring->tx_pending : MVNETA_MAX_TXD;
+
+       pp->tx_ring_size = clamp_t(u16, ring->tx_pending,
+                                  MVNETA_MAX_SKB_DESCS * 2, MVNETA_MAX_TXD);
+       if (pp->tx_ring_size != ring->tx_pending)
+               netdev_warn(dev, "TX queue size set to %u (requested %u)\n",
+                           pp->tx_ring_size, ring->tx_pending);
 
        if (netif_running(dev)) {
                mvneta_stop(dev);
@@ -2638,7 +2804,7 @@ const struct ethtool_ops mvneta_eth_tool_ops = {
 };
 
 /* Initialize hw */
-static int mvneta_init(struct mvneta_port *pp, int phy_addr)
+static int mvneta_init(struct device *dev, struct mvneta_port *pp)
 {
        int queue;
 
@@ -2648,8 +2814,8 @@ static int mvneta_init(struct mvneta_port *pp, int phy_addr)
        /* Set port default values */
        mvneta_defaults_set(pp);
 
-       pp->txqs = kzalloc(txq_number * sizeof(struct mvneta_tx_queue),
-                          GFP_KERNEL);
+       pp->txqs = devm_kcalloc(dev, txq_number, sizeof(struct mvneta_tx_queue),
+                               GFP_KERNEL);
        if (!pp->txqs)
                return -ENOMEM;
 
@@ -2661,12 +2827,10 @@ static int mvneta_init(struct mvneta_port *pp, int phy_addr)
                txq->done_pkts_coal = MVNETA_TXDONE_COAL_PKTS;
        }
 
-       pp->rxqs = kzalloc(rxq_number * sizeof(struct mvneta_rx_queue),
-                          GFP_KERNEL);
-       if (!pp->rxqs) {
-               kfree(pp->txqs);
+       pp->rxqs = devm_kcalloc(dev, rxq_number, sizeof(struct mvneta_rx_queue),
+                               GFP_KERNEL);
+       if (!pp->rxqs)
                return -ENOMEM;
-       }
 
        /* Create Rx descriptor rings */
        for (queue = 0; queue < rxq_number; queue++) {
@@ -2680,12 +2844,6 @@ static int mvneta_init(struct mvneta_port *pp, int phy_addr)
        return 0;
 }
 
-static void mvneta_deinit(struct mvneta_port *pp)
-{
-       kfree(pp->txqs);
-       kfree(pp->rxqs);
-}
-
 /* platform glue : initialize decoding windows */
 static void mvneta_conf_mbus_windows(struct mvneta_port *pp,
                                     const struct mbus_dram_target_info *dram)
@@ -2768,7 +2926,6 @@ static int mvneta_probe(struct platform_device *pdev)
        struct resource *res;
        struct device_node *dn = pdev->dev.of_node;
        struct device_node *phy_node;
-       u32 phy_addr;
        struct mvneta_port *pp;
        struct net_device *dev;
        const char *dt_mac_addr;
@@ -2797,9 +2954,22 @@ static int mvneta_probe(struct platform_device *pdev)
 
        phy_node = of_parse_phandle(dn, "phy", 0);
        if (!phy_node) {
-               dev_err(&pdev->dev, "no associated PHY\n");
-               err = -ENODEV;
-               goto err_free_irq;
+               if (!of_phy_is_fixed_link(dn)) {
+                       dev_err(&pdev->dev, "no PHY specified\n");
+                       err = -ENODEV;
+                       goto err_free_irq;
+               }
+
+               err = of_phy_register_fixed_link(dn);
+               if (err < 0) {
+                       dev_err(&pdev->dev, "cannot register fixed PHY\n");
+                       goto err_free_irq;
+               }
+
+               /* In the case of a fixed PHY, the DT node associated
+                * to the PHY is the Ethernet MAC DT node.
+                */
+               phy_node = dn;
        }
 
        phy_mode = of_get_phy_mode(dn);
@@ -2813,11 +2983,9 @@ static int mvneta_probe(struct platform_device *pdev)
        dev->watchdog_timeo = 5 * HZ;
        dev->netdev_ops = &mvneta_netdev_ops;
 
-       SET_ETHTOOL_OPS(dev, &mvneta_eth_tool_ops);
+       dev->ethtool_ops = &mvneta_eth_tool_ops;
 
        pp = netdev_priv(dev);
-
-       pp->weight = MVNETA_RX_POLL_WEIGHT;
        pp->phy_node = phy_node;
        pp->phy_interface = phy_mode;
 
@@ -2864,33 +3032,32 @@ static int mvneta_probe(struct platform_device *pdev)
        pp->dev = dev;
        SET_NETDEV_DEV(dev, &pdev->dev);
 
-       err = mvneta_init(pp, phy_addr);
-       if (err < 0) {
-               dev_err(&pdev->dev, "can't init eth hal\n");
+       err = mvneta_init(&pdev->dev, pp);
+       if (err < 0)
                goto err_free_stats;
-       }
 
        err = mvneta_port_power_up(pp, phy_mode);
        if (err < 0) {
                dev_err(&pdev->dev, "can't power up port\n");
-               goto err_deinit;
+               goto err_free_stats;
        }
 
        dram_target_info = mv_mbus_dram_info();
        if (dram_target_info)
                mvneta_conf_mbus_windows(pp, dram_target_info);
 
-       netif_napi_add(dev, &pp->napi, mvneta_poll, pp->weight);
+       netif_napi_add(dev, &pp->napi, mvneta_poll, NAPI_POLL_WEIGHT);
 
-       dev->features = NETIF_F_SG | NETIF_F_IP_CSUM;
-       dev->hw_features |= NETIF_F_SG | NETIF_F_IP_CSUM;
-       dev->vlan_features |= NETIF_F_SG | NETIF_F_IP_CSUM;
+       dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
+       dev->hw_features |= dev->features;
+       dev->vlan_features |= dev->features;
        dev->priv_flags |= IFF_UNICAST_FLT;
+       dev->gso_max_segs = MVNETA_MAX_TSO_SEGS;
 
        err = register_netdev(dev);
        if (err < 0) {
                dev_err(&pdev->dev, "failed to register\n");
-               goto err_deinit;
+               goto err_free_stats;
        }
 
        netdev_info(dev, "Using %s mac address %pM\n", mac_from,
@@ -2900,8 +3067,6 @@ static int mvneta_probe(struct platform_device *pdev)
 
        return 0;
 
-err_deinit:
-       mvneta_deinit(pp);
 err_free_stats:
        free_percpu(pp->stats);
 err_clk:
@@ -2920,7 +3085,6 @@ static int mvneta_remove(struct platform_device *pdev)
        struct mvneta_port *pp = netdev_priv(dev);
 
        unregister_netdev(dev);
-       mvneta_deinit(pp);
        clk_disable_unprepare(pp->clk);
        free_percpu(pp->stats);
        irq_dispose_mapping(dev->irq);
index b358c2f6f4bdc3817f98ab53c36bf8f8c7753ffd..8f5aa7c62b18f41f8eaa1b31e209687059921a47 100644 (file)
@@ -1488,7 +1488,7 @@ static int pxa168_eth_probe(struct platform_device *pdev)
        dev->netdev_ops = &pxa168_eth_netdev_ops;
        dev->watchdog_timeo = 2 * HZ;
        dev->base_addr = 0;
-       SET_ETHTOOL_OPS(dev, &pxa168_ethtool_ops);
+       dev->ethtool_ops = &pxa168_ethtool_ops;
 
        INIT_WORK(&pep->tx_timeout_task, pxa168_eth_tx_timeout_task);
 
index b81106451a0a4d2d46d831d9e629d3a24212aada..69693384b58ccfefd2bd9918112b8b08431f0368 100644 (file)
@@ -4760,7 +4760,7 @@ static struct net_device *sky2_init_netdev(struct sky2_hw *hw, unsigned port,
 
        SET_NETDEV_DEV(dev, &hw->pdev->dev);
        dev->irq = hw->pdev->irq;
-       SET_ETHTOOL_OPS(dev, &sky2_ethtool_ops);
+       dev->ethtool_ops = &sky2_ethtool_ops;
        dev->watchdog_timeo = TX_WATCHDOG;
        dev->netdev_ops = &sky2_netdev_ops[port];
 
index 29b616990e528b234c94d59cf88d7c74c5fd41de..5d940a26055c64ab71250f1d998b9b38418fb2ec 100644 (file)
@@ -212,8 +212,7 @@ static int mlx4_comm_cmd_poll(struct mlx4_dev *dev, u8 cmd, u16 param,
 
        /* First, verify that the master reports correct status */
        if (comm_pending(dev)) {
-               mlx4_warn(dev, "Communication channel is not idle."
-                         "my toggle is %d (cmd:0x%x)\n",
+               mlx4_warn(dev, "Communication channel is not idle - my toggle is %d (cmd:0x%x)\n",
                          priv->cmd.comm_toggle, cmd);
                return -EAGAIN;
        }
@@ -422,9 +421,8 @@ static int mlx4_slave_cmd(struct mlx4_dev *dev, u64 in_param, u64 *out_param,
                                        *out_param =
                                                be64_to_cpu(vhcr->out_param);
                                else {
-                                       mlx4_err(dev, "response expected while"
-                                                "output mailbox is NULL for "
-                                                "command 0x%x\n", op);
+                                       mlx4_err(dev, "response expected while output mailbox is NULL for command 0x%x\n",
+                                                op);
                                        vhcr->status = CMD_STAT_BAD_PARAM;
                                }
                        }
@@ -439,16 +437,15 @@ static int mlx4_slave_cmd(struct mlx4_dev *dev, u64 in_param, u64 *out_param,
                                        *out_param =
                                                be64_to_cpu(vhcr->out_param);
                                else {
-                                       mlx4_err(dev, "response expected while"
-                                                "output mailbox is NULL for "
-                                                "command 0x%x\n", op);
+                                       mlx4_err(dev, "response expected while output mailbox is NULL for command 0x%x\n",
+                                                op);
                                        vhcr->status = CMD_STAT_BAD_PARAM;
                                }
                        }
                        ret = mlx4_status_to_errno(vhcr->status);
                } else
-                       mlx4_err(dev, "failed execution of VHCR_POST command"
-                                "opcode 0x%x\n", op);
+                       mlx4_err(dev, "failed execution of VHCR_POST command opcode 0x%x\n",
+                                op);
        }
 
        mutex_unlock(&priv->cmd.slave_cmd_mutex);
@@ -476,6 +473,13 @@ static int mlx4_cmd_poll(struct mlx4_dev *dev, u64 in_param, u64 *out_param,
                goto out;
        }
 
+       if (out_is_imm && !out_param) {
+               mlx4_err(dev, "response expected while output mailbox is NULL for command 0x%x\n",
+                        op);
+               err = -EINVAL;
+               goto out;
+       }
+
        err = mlx4_cmd_post(dev, in_param, out_param ? *out_param : 0,
                            in_modifier, op_modifier, op, CMD_POLL_TOKEN, 0);
        if (err)
@@ -554,6 +558,13 @@ static int mlx4_cmd_wait(struct mlx4_dev *dev, u64 in_param, u64 *out_param,
        cmd->free_head = context->next;
        spin_unlock(&cmd->context_lock);
 
+       if (out_is_imm && !out_param) {
+               mlx4_err(dev, "response expected while output mailbox is NULL for command 0x%x\n",
+                        op);
+               err = -EINVAL;
+               goto out;
+       }
+
        init_completion(&context->done);
 
        mlx4_cmd_post(dev, in_param, out_param ? *out_param : 0,
@@ -625,9 +636,8 @@ static int mlx4_ACCESS_MEM(struct mlx4_dev *dev, u64 master_addr,
 
        if ((slave_addr & 0xfff) | (master_addr & 0xfff) |
            (slave & ~0x7f) | (size & 0xff)) {
-               mlx4_err(dev, "Bad access mem params - slave_addr:0x%llx "
-                             "master_addr:0x%llx slave_id:%d size:%d\n",
-                             slave_addr, master_addr, slave, size);
+               mlx4_err(dev, "Bad access mem params - slave_addr:0x%llx master_addr:0x%llx slave_id:%d size:%d\n",
+                        slave_addr, master_addr, slave, size);
                return -EINVAL;
        }
 
@@ -1422,8 +1432,8 @@ static int mlx4_master_process_vhcr(struct mlx4_dev *dev, int slave,
                                      ALIGN(sizeof(struct mlx4_vhcr_cmd),
                                            MLX4_ACCESS_MEM_ALIGN), 1);
                if (ret) {
-                       mlx4_err(dev, "%s:Failed reading vhcr"
-                                "ret: 0x%x\n", __func__, ret);
+                       mlx4_err(dev, "%s: Failed reading vhcr ret: 0x%x\n",
+                                __func__, ret);
                        kfree(vhcr);
                        return ret;
                }
@@ -1474,9 +1484,8 @@ static int mlx4_master_process_vhcr(struct mlx4_dev *dev, int slave,
 
        /* Apply permission and bound checks if applicable */
        if (cmd->verify && cmd->verify(dev, slave, vhcr, inbox)) {
-               mlx4_warn(dev, "Command:0x%x from slave: %d failed protection "
-                         "checks for resource_id:%d\n", vhcr->op, slave,
-                         vhcr->in_modifier);
+               mlx4_warn(dev, "Command:0x%x from slave: %d failed protection checks for resource_id:%d\n",
+                         vhcr->op, slave, vhcr->in_modifier);
                vhcr_cmd->status = CMD_STAT_BAD_OP;
                goto out_status;
        }
@@ -1515,8 +1524,7 @@ static int mlx4_master_process_vhcr(struct mlx4_dev *dev, int slave,
        }
 
        if (err) {
-               mlx4_warn(dev, "vhcr command:0x%x slave:%d failed with"
-                         " error:%d, status %d\n",
+               mlx4_warn(dev, "vhcr command:0x%x slave:%d failed with error:%d, status %d\n",
                          vhcr->op, slave, vhcr->errno, err);
                vhcr_cmd->status = mlx4_errno_to_status(err);
                goto out_status;
@@ -1550,8 +1558,8 @@ static int mlx4_master_process_vhcr(struct mlx4_dev *dev, int slave,
                                 __func__);
                else if (vhcr->e_bit &&
                         mlx4_GEN_EQE(dev, slave, &priv->mfunc.master.cmd_eqe))
-                               mlx4_warn(dev, "Failed to generate command completion "
-                                         "eqe for slave %d\n", slave);
+                               mlx4_warn(dev, "Failed to generate command completion eqe for slave %d\n",
+                                         slave);
        }
 
 out:
@@ -1590,8 +1598,9 @@ static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv,
 
        mlx4_dbg(dev, "updating immediately admin params slave %d port %d\n",
                 slave, port);
-       mlx4_dbg(dev, "vlan %d QoS %d link down %d\n", vp_admin->default_vlan,
-                vp_admin->default_qos, vp_admin->link_state);
+       mlx4_dbg(dev, "vlan %d QoS %d link down %d\n",
+                vp_admin->default_vlan, vp_admin->default_qos,
+                vp_admin->link_state);
 
        work = kzalloc(sizeof(*work), GFP_KERNEL);
        if (!work)
@@ -1604,7 +1613,7 @@ static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv,
                                                   &admin_vlan_ix);
                        if (err) {
                                kfree(work);
-                               mlx4_warn((&priv->dev),
+                               mlx4_warn(&priv->dev,
                                          "No vlan resources slave %d, port %d\n",
                                          slave, port);
                                return err;
@@ -1613,7 +1622,7 @@ static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv,
                        admin_vlan_ix = NO_INDX;
                }
                work->flags |= MLX4_VF_IMMED_VLAN_FLAG_VLAN;
-               mlx4_dbg((&(priv->dev)),
+               mlx4_dbg(&priv->dev,
                         "alloc vlan %d idx  %d slave %d port %d\n",
                         (int)(vp_admin->default_vlan),
                         admin_vlan_ix, slave, port);
@@ -1676,12 +1685,12 @@ static int mlx4_master_activate_admin_state(struct mlx4_priv *priv, int slave)
                                                   vp_admin->default_vlan, &(vp_oper->vlan_idx));
                        if (err) {
                                vp_oper->vlan_idx = NO_INDX;
-                               mlx4_warn((&priv->dev),
+                               mlx4_warn(&priv->dev,
                                          "No vlan resorces slave %d, port %d\n",
                                          slave, port);
                                return err;
                        }
-                       mlx4_dbg((&(priv->dev)), "alloc vlan %d idx  %d slave %d port %d\n",
+                       mlx4_dbg(&priv->dev, "alloc vlan %d idx  %d slave %d port %d\n",
                                 (int)(vp_oper->state.default_vlan),
                                 vp_oper->vlan_idx, slave, port);
                }
@@ -1692,12 +1701,12 @@ static int mlx4_master_activate_admin_state(struct mlx4_priv *priv, int slave)
                        if (0 > vp_oper->mac_idx) {
                                err = vp_oper->mac_idx;
                                vp_oper->mac_idx = NO_INDX;
-                               mlx4_warn((&priv->dev),
+                               mlx4_warn(&priv->dev,
                                          "No mac resorces slave %d, port %d\n",
                                          slave, port);
                                return err;
                        }
-                       mlx4_dbg((&(priv->dev)), "alloc mac %llx idx  %d slave %d port %d\n",
+                       mlx4_dbg(&priv->dev, "alloc mac %llx idx  %d slave %d port %d\n",
                                 vp_oper->state.mac, vp_oper->mac_idx, slave, port);
                }
        }
@@ -1748,8 +1757,8 @@ static void mlx4_master_do_cmd(struct mlx4_dev *dev, int slave, u8 cmd,
        slave_state[slave].comm_toggle ^= 1;
        reply = (u32) slave_state[slave].comm_toggle << 31;
        if (toggle != slave_state[slave].comm_toggle) {
-               mlx4_warn(dev, "Incorrect toggle %d from slave %d. *** MASTER"
-                         "STATE COMPROMISIED ***\n", toggle, slave);
+               mlx4_warn(dev, "Incorrect toggle %d from slave %d. *** MASTER STATE COMPROMISED ***\n",
+                         toggle, slave);
                goto reset_slave;
        }
        if (cmd == MLX4_COMM_CMD_RESET) {
@@ -1776,8 +1785,8 @@ static void mlx4_master_do_cmd(struct mlx4_dev *dev, int slave, u8 cmd,
        /*command from slave in the middle of FLR*/
        if (cmd != MLX4_COMM_CMD_RESET &&
            MLX4_COMM_CMD_FLR == slave_state[slave].last_cmd) {
-               mlx4_warn(dev, "slave:%d is Trying to run cmd(0x%x) "
-                         "in the middle of FLR\n", slave, cmd);
+               mlx4_warn(dev, "slave:%d is Trying to run cmd(0x%x) in the middle of FLR\n",
+                         slave, cmd);
                return;
        }
 
@@ -1815,8 +1824,8 @@ static void mlx4_master_do_cmd(struct mlx4_dev *dev, int slave, u8 cmd,
 
                mutex_lock(&priv->cmd.slave_cmd_mutex);
                if (mlx4_master_process_vhcr(dev, slave, NULL)) {
-                       mlx4_err(dev, "Failed processing vhcr for slave:%d,"
-                                " resetting slave.\n", slave);
+                       mlx4_err(dev, "Failed processing vhcr for slave:%d, resetting slave\n",
+                                slave);
                        mutex_unlock(&priv->cmd.slave_cmd_mutex);
                        goto reset_slave;
                }
@@ -1833,8 +1842,7 @@ static void mlx4_master_do_cmd(struct mlx4_dev *dev, int slave, u8 cmd,
                is_going_down = 1;
        spin_unlock_irqrestore(&priv->mfunc.master.slave_state_lock, flags);
        if (is_going_down) {
-               mlx4_warn(dev, "Slave is going down aborting command(%d)"
-                         " executing from slave:%d\n",
+               mlx4_warn(dev, "Slave is going down aborting command(%d) executing from slave:%d\n",
                          cmd, slave);
                return;
        }
@@ -1897,10 +1905,9 @@ void mlx4_master_comm_channel(struct work_struct *work)
                        if (toggle != slt) {
                                if (master->slave_state[slave].comm_toggle
                                    != slt) {
-                                       printk(KERN_INFO "slave %d out of sync."
-                                              " read toggle %d, state toggle %d. "
-                                              "Resynching.\n", slave, slt,
-                                              master->slave_state[slave].comm_toggle);
+                                       pr_info("slave %d out of sync. read toggle %d, state toggle %d. Resynching.\n",
+                                               slave, slt,
+                                               master->slave_state[slave].comm_toggle);
                                        master->slave_state[slave].comm_toggle =
                                                slt;
                                }
@@ -1913,8 +1920,7 @@ void mlx4_master_comm_channel(struct work_struct *work)
        }
 
        if (reported && reported != served)
-               mlx4_warn(dev, "Got command event with bitmask from %d slaves"
-                         " but %d were served\n",
+               mlx4_warn(dev, "Got command event with bitmask from %d slaves but %d were served\n",
                          reported, served);
 
        if (mlx4_ARM_COMM_CHANNEL(dev))
@@ -1970,7 +1976,7 @@ int mlx4_multi_func_init(struct mlx4_dev *dev)
                ioremap(pci_resource_start(dev->pdev, 2) +
                        MLX4_SLAVE_COMM_BASE, MLX4_COMM_PAGESIZE);
        if (!priv->mfunc.comm) {
-               mlx4_err(dev, "Couldn't map communication vector.\n");
+               mlx4_err(dev, "Couldn't map communication vector\n");
                goto err_vhcr;
        }
 
@@ -2097,7 +2103,7 @@ int mlx4_cmd_init(struct mlx4_dev *dev)
                priv->cmd.hcr = ioremap(pci_resource_start(dev->pdev, 0) +
                                        MLX4_HCR_BASE, MLX4_HCR_SIZE);
                if (!priv->cmd.hcr) {
-                       mlx4_err(dev, "Couldn't map command register.\n");
+                       mlx4_err(dev, "Couldn't map command register\n");
                        return -ENOMEM;
                }
        }
@@ -2498,11 +2504,12 @@ int mlx4_get_vf_config(struct mlx4_dev *dev, int port, int vf, struct ifla_vf_in
        ivf->mac[4] = ((s_info->mac >> (1*8)) & 0xff);
        ivf->mac[5] = ((s_info->mac)  & 0xff);
 
-       ivf->vlan       = s_info->default_vlan;
-       ivf->qos        = s_info->default_qos;
-       ivf->tx_rate    = s_info->tx_rate;
-       ivf->spoofchk   = s_info->spoofchk;
-       ivf->linkstate  = s_info->link_state;
+       ivf->vlan               = s_info->default_vlan;
+       ivf->qos                = s_info->default_qos;
+       ivf->max_tx_rate        = s_info->tx_rate;
+       ivf->min_tx_rate        = 0;
+       ivf->spoofchk           = s_info->spoofchk;
+       ivf->linkstate          = s_info->link_state;
 
        return 0;
 }
index c90cde5b4aeee5cd91239a1cdea479490c388170..80f725228f5b7c8ab836f8bccd747fdc006782c6 100644 (file)
@@ -293,6 +293,9 @@ int mlx4_cq_alloc(struct mlx4_dev *dev, int nent,
        atomic_set(&cq->refcount, 1);
        init_completion(&cq->free);
 
+       cq->irq = priv->eq_table.eq[cq->vector].irq;
+       cq->irq_affinity_change = false;
+
        return 0;
 
 err_radix:
index c2cd8d31bcad5612395783e4d29e5141ac37ab6c..4b2130760eede3ad5f655cf08f2614d033c8ecb8 100644 (file)
@@ -125,8 +125,7 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq,
                                                   &cq->vector)) {
                                        cq->vector = (cq->ring + 1 + priv->port)
                                            % mdev->dev->caps.num_comp_vectors;
-                                       mlx4_warn(mdev, "Failed Assigning an EQ to "
-                                                 "%s ,Falling back to legacy EQ's\n",
+                                       mlx4_warn(mdev, "Failed assigning an EQ to %s, falling back to legacy EQ's\n",
                                                  name);
                                }
                        }
@@ -164,6 +163,13 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq,
                netif_napi_add(cq->dev, &cq->napi, mlx4_en_poll_tx_cq,
                               NAPI_POLL_WEIGHT);
        } else {
+               struct mlx4_en_rx_ring *ring = priv->rx_ring[cq->ring];
+
+               err = irq_set_affinity_hint(cq->mcq.irq,
+                                           ring->affinity_mask);
+               if (err)
+                       mlx4_warn(mdev, "Failed setting affinity hint\n");
+
                netif_napi_add(cq->dev, &cq->napi, mlx4_en_poll_rx_cq, 64);
                napi_hash_add(&cq->napi);
        }
@@ -180,8 +186,11 @@ void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq **pcq)
 
        mlx4_en_unmap_buffer(&cq->wqres.buf);
        mlx4_free_hwq_res(mdev->dev, &cq->wqres, cq->buf_size);
-       if (priv->mdev->dev->caps.comp_pool && cq->vector)
+       if (priv->mdev->dev->caps.comp_pool && cq->vector) {
+               if (!cq->is_tx)
+                       irq_set_affinity_hint(cq->mcq.irq, NULL);
                mlx4_release_eq(priv->mdev->dev, cq->vector);
+       }
        cq->vector = 0;
        cq->buf_size = 0;
        cq->buf = NULL;
index 3e8d33605fe7b17d7cde81092c92f132163abdc2..fa1a069e14e6f3ef21485172d612548034e00262 100644 (file)
@@ -378,8 +378,8 @@ static int mlx4_en_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
                ethtool_cmd_speed_set(cmd, priv->port_state.link_speed);
                cmd->duplex = DUPLEX_FULL;
        } else {
-               ethtool_cmd_speed_set(cmd, -1);
-               cmd->duplex = -1;
+               ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
+               cmd->duplex = DUPLEX_UNKNOWN;
        }
 
        if (trans_type > 0 && trans_type <= 0xC) {
@@ -564,7 +564,7 @@ static u32 mlx4_en_get_rxfh_indir_size(struct net_device *dev)
        return priv->rx_ring_num;
 }
 
-static int mlx4_en_get_rxfh_indir(struct net_device *dev, u32 *ring_index)
+static int mlx4_en_get_rxfh(struct net_device *dev, u32 *ring_index, u8 *key)
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_en_rss_map *rss_map = &priv->rss_map;
@@ -582,8 +582,8 @@ static int mlx4_en_get_rxfh_indir(struct net_device *dev, u32 *ring_index)
        return err;
 }
 
-static int mlx4_en_set_rxfh_indir(struct net_device *dev,
-               const u32 *ring_index)
+static int mlx4_en_set_rxfh(struct net_device *dev, const u32 *ring_index,
+                           const u8 *key)
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_en_dev *mdev = priv->mdev;
@@ -925,13 +925,13 @@ static int mlx4_en_flow_replace(struct net_device *dev,
                qpn = cmd->fs.ring_cookie & (EN_ETHTOOL_QP_ATTACH - 1);
        } else {
                if (cmd->fs.ring_cookie >= priv->rx_ring_num) {
-                       en_warn(priv, "rxnfc: RX ring (%llu) doesn't exist.\n",
+                       en_warn(priv, "rxnfc: RX ring (%llu) doesn't exist\n",
                                cmd->fs.ring_cookie);
                        return -EINVAL;
                }
                qpn = priv->rss_map.qps[cmd->fs.ring_cookie].qpn;
                if (!qpn) {
-                       en_warn(priv, "rxnfc: RX ring (%llu) is inactive.\n",
+                       en_warn(priv, "rxnfc: RX ring (%llu) is inactive\n",
                                cmd->fs.ring_cookie);
                        return -EINVAL;
                }
@@ -956,7 +956,7 @@ static int mlx4_en_flow_replace(struct net_device *dev,
        }
        err = mlx4_flow_attach(priv->mdev->dev, &rule, &reg_id);
        if (err) {
-               en_err(priv, "Fail to attach network rule at location %d.\n",
+               en_err(priv, "Fail to attach network rule at location %d\n",
                       cmd->fs.location);
                goto out_free_list;
        }
@@ -1121,7 +1121,7 @@ static int mlx4_en_set_channels(struct net_device *dev,
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_en_dev *mdev = priv->mdev;
-       int port_up;
+       int port_up = 0;
        int err = 0;
 
        if (channel->other_count || channel->combined_count ||
@@ -1151,7 +1151,8 @@ static int mlx4_en_set_channels(struct net_device *dev,
        netif_set_real_num_tx_queues(dev, priv->tx_ring_num);
        netif_set_real_num_rx_queues(dev, priv->rx_ring_num);
 
-       mlx4_en_setup_tc(dev, MLX4_EN_NUM_UP);
+       if (dev->num_tc)
+               mlx4_en_setup_tc(dev, MLX4_EN_NUM_UP);
 
        en_warn(priv, "Using %d TX rings\n", priv->tx_ring_num);
        en_warn(priv, "Using %d RX rings\n", priv->rx_ring_num);
@@ -1223,8 +1224,8 @@ const struct ethtool_ops mlx4_en_ethtool_ops = {
        .get_rxnfc = mlx4_en_get_rxnfc,
        .set_rxnfc = mlx4_en_set_rxnfc,
        .get_rxfh_indir_size = mlx4_en_get_rxfh_indir_size,
-       .get_rxfh_indir = mlx4_en_get_rxfh_indir,
-       .set_rxfh_indir = mlx4_en_set_rxfh_indir,
+       .get_rxfh = mlx4_en_get_rxfh,
+       .set_rxfh = mlx4_en_set_rxfh,
        .get_channels = mlx4_en_get_channels,
        .set_channels = mlx4_en_set_channels,
        .get_ts_info = mlx4_en_get_ts_info,
index 0c59d4fe7e3aae56afee09b7e279601192c9e26e..f953c1d7eae6a700a4fb7aacfacbaaa822c141d1 100644 (file)
@@ -133,7 +133,7 @@ static int mlx4_en_get_profile(struct mlx4_en_dev *mdev)
                        MLX4_EN_MAX_TX_RING_P_UP);
        if (params->udp_rss && !(mdev->dev->caps.flags
                                        & MLX4_DEV_CAP_FLAG_UDP_RSS)) {
-               mlx4_warn(mdev, "UDP RSS is not supported on this device.\n");
+               mlx4_warn(mdev, "UDP RSS is not supported on this device\n");
                params->udp_rss = 0;
        }
        for (i = 1; i <= MLX4_MAX_PORTS; i++) {
@@ -251,8 +251,7 @@ static void *mlx4_en_add(struct mlx4_dev *dev)
 
        mdev->LSO_support = !!(dev->caps.flags & (1 << 15));
        if (!mdev->LSO_support)
-               mlx4_warn(mdev, "LSO not supported, please upgrade to later "
-                               "FW version to enable LSO\n");
+               mlx4_warn(mdev, "LSO not supported, please upgrade to later FW version to enable LSO\n");
 
        if (mlx4_mr_alloc(mdev->dev, mdev->priv_pdn, 0, ~0ull,
                         MLX4_PERM_LOCAL_WRITE |  MLX4_PERM_LOCAL_READ,
@@ -268,7 +267,7 @@ static void *mlx4_en_add(struct mlx4_dev *dev)
        /* Build device profile according to supplied module parameters */
        err = mlx4_en_get_profile(mdev);
        if (err) {
-               mlx4_err(mdev, "Bad module parameters, aborting.\n");
+               mlx4_err(mdev, "Bad module parameters, aborting\n");
                goto err_mr;
        }
 
index 7e4b1720c3d1bec183957beeba7d395ce38c6e34..7d4fb7bf25933ddcddecebf0a551bbe4cfa6328a 100644 (file)
@@ -130,7 +130,7 @@ static enum mlx4_net_trans_rule_id mlx4_ip_proto_to_trans_rule_id(u8 ip_proto)
        case IPPROTO_TCP:
                return MLX4_NET_TRANS_RULE_ID_TCP;
        default:
-               return -EPROTONOSUPPORT;
+               return MLX4_NET_TRANS_RULE_NUM;
        }
 };
 
@@ -177,7 +177,7 @@ 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) {
+       if (spec_tcp_udp.id >= MLX4_NET_TRANS_RULE_NUM) {
                en_warn(priv, "RFS: ignoring unsupported ip protocol (%d)\n",
                        filter->ip_proto);
                goto ignore;
@@ -770,11 +770,12 @@ static int mlx4_en_do_set_mac(struct mlx4_en_priv *priv)
                                          priv->dev->dev_addr, priv->prev_mac);
                if (err)
                        en_err(priv, "Failed changing HW MAC address\n");
-               memcpy(priv->prev_mac, priv->dev->dev_addr,
-                      sizeof(priv->prev_mac));
        } else
                en_dbg(HW, priv, "Port is down while registering mac, exiting...\n");
 
+       memcpy(priv->prev_mac, priv->dev->dev_addr,
+              sizeof(priv->prev_mac));
+
        return err;
 }
 
@@ -788,9 +789,8 @@ static int mlx4_en_set_mac(struct net_device *dev, void *addr)
        if (!is_valid_ether_addr(saddr->sa_data))
                return -EADDRNOTAVAIL;
 
-       memcpy(dev->dev_addr, saddr->sa_data, ETH_ALEN);
-
        mutex_lock(&mdev->state_lock);
+       memcpy(dev->dev_addr, saddr->sa_data, ETH_ALEN);
        err = mlx4_en_do_set_mac(priv);
        mutex_unlock(&mdev->state_lock);
 
@@ -1526,6 +1526,27 @@ static void mlx4_en_linkstate(struct work_struct *work)
        mutex_unlock(&mdev->state_lock);
 }
 
+static int mlx4_en_init_affinity_hint(struct mlx4_en_priv *priv, int ring_idx)
+{
+       struct mlx4_en_rx_ring *ring = priv->rx_ring[ring_idx];
+       int numa_node = priv->mdev->dev->numa_node;
+       int ret = 0;
+
+       if (!zalloc_cpumask_var(&ring->affinity_mask, GFP_KERNEL))
+               return -ENOMEM;
+
+       ret = cpumask_set_cpu_local_first(ring_idx, numa_node,
+                                         ring->affinity_mask);
+       if (ret)
+               free_cpumask_var(ring->affinity_mask);
+
+       return ret;
+}
+
+static void mlx4_en_free_affinity_hint(struct mlx4_en_priv *priv, int ring_idx)
+{
+       free_cpumask_var(priv->rx_ring[ring_idx]->affinity_mask);
+}
 
 int mlx4_en_start_port(struct net_device *dev)
 {
@@ -1567,17 +1588,25 @@ int mlx4_en_start_port(struct net_device *dev)
 
                mlx4_en_cq_init_lock(cq);
 
+               err = mlx4_en_init_affinity_hint(priv, i);
+               if (err) {
+                       en_err(priv, "Failed preparing IRQ affinity hint\n");
+                       goto cq_err;
+               }
+
                err = mlx4_en_activate_cq(priv, cq, i);
                if (err) {
                        en_err(priv, "Failed activating Rx CQ\n");
+                       mlx4_en_free_affinity_hint(priv, i);
                        goto cq_err;
                }
                for (j = 0; j < cq->size; j++)
                        cq->buf[j].owner_sr_opcode = MLX4_CQE_OWNER_MASK;
                err = mlx4_en_set_cq_moder(priv, cq);
                if (err) {
-                       en_err(priv, "Failed setting cq moderation parameters");
+                       en_err(priv, "Failed setting cq moderation parameters\n");
                        mlx4_en_deactivate_cq(priv, cq);
+                       mlx4_en_free_affinity_hint(priv, i);
                        goto cq_err;
                }
                mlx4_en_arm_cq(priv, cq);
@@ -1615,7 +1644,7 @@ int mlx4_en_start_port(struct net_device *dev)
                }
                err = mlx4_en_set_cq_moder(priv, cq);
                if (err) {
-                       en_err(priv, "Failed setting cq moderation parameters");
+                       en_err(priv, "Failed setting cq moderation parameters\n");
                        mlx4_en_deactivate_cq(priv, cq);
                        goto tx_err;
                }
@@ -1715,8 +1744,10 @@ int mlx4_en_start_port(struct net_device *dev)
 mac_err:
        mlx4_en_put_qp(priv);
 cq_err:
-       while (rx_index--)
+       while (rx_index--) {
                mlx4_en_deactivate_cq(priv, priv->rx_cq[rx_index]);
+               mlx4_en_free_affinity_hint(priv, i);
+       }
        for (i = 0; i < priv->rx_ring_num; i++)
                mlx4_en_deactivate_rx_ring(priv, priv->rx_ring[i]);
 
@@ -1847,6 +1878,8 @@ void mlx4_en_stop_port(struct net_device *dev, int detach)
                        msleep(1);
                mlx4_en_deactivate_rx_ring(priv, priv->rx_ring[i]);
                mlx4_en_deactivate_cq(priv, cq);
+
+               mlx4_en_free_affinity_hint(priv, i);
        }
 }
 
@@ -2539,7 +2572,7 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
        netif_set_real_num_tx_queues(dev, priv->tx_ring_num);
        netif_set_real_num_rx_queues(dev, priv->rx_ring_num);
 
-       SET_ETHTOOL_OPS(dev, &mlx4_en_ethtool_ops);
+       dev->ethtool_ops = &mlx4_en_ethtool_ops;
 
        /*
         * Set driver features
@@ -2594,8 +2627,8 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
                                    prof->tx_pause, prof->tx_ppp,
                                    prof->rx_pause, prof->rx_ppp);
        if (err) {
-               en_err(priv, "Failed setting port general configurations "
-                      "for port %d, with error %d\n", priv->port, err);
+               en_err(priv, "Failed setting port general configurations for port %d, with error %d\n",
+                      priv->port, err);
                goto out;
        }
 
index 87857a6463ebb286f0110d7fc7a831d25743d27b..d2d415732d994178117eafb39cc6d8207ce5322e 100644 (file)
@@ -270,13 +270,11 @@ static int mlx4_en_fill_rx_buffers(struct mlx4_en_priv *priv)
                                                    ring->actual_size,
                                                    GFP_KERNEL)) {
                                if (ring->actual_size < MLX4_EN_MIN_RX_SIZE) {
-                                       en_err(priv, "Failed to allocate "
-                                                    "enough rx buffers\n");
+                                       en_err(priv, "Failed to allocate enough rx buffers\n");
                                        return -ENOMEM;
                                } else {
                                        new_size = rounddown_pow_of_two(ring->actual_size);
-                                       en_warn(priv, "Only %d buffers allocated "
-                                                     "reducing ring size to %d",
+                                       en_warn(priv, "Only %d buffers allocated reducing ring size to %d\n",
                                                ring->actual_size, new_size);
                                        goto reduce_rings;
                                }
@@ -685,10 +683,9 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
                /* Drop packet on bad receive or bad checksum */
                if (unlikely((cqe->owner_sr_opcode & MLX4_CQE_OPCODE_MASK) ==
                                                MLX4_CQE_OPCODE_ERROR)) {
-                       en_err(priv, "CQE completed in error - vendor "
-                                 "syndrom:%d syndrom:%d\n",
-                                 ((struct mlx4_err_cqe *) cqe)->vendor_err_syndrome,
-                                 ((struct mlx4_err_cqe *) cqe)->syndrome);
+                       en_err(priv, "CQE completed in error - vendor syndrom:%d syndrom:%d\n",
+                              ((struct mlx4_err_cqe *)cqe)->vendor_err_syndrome,
+                              ((struct mlx4_err_cqe *)cqe)->syndrome);
                        goto next;
                }
                if (unlikely(cqe->badfcs_enc & MLX4_CQE_BAD_FCS)) {
@@ -898,10 +895,17 @@ int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget)
        mlx4_en_cq_unlock_napi(cq);
 
        /* If we used up all the quota - we're probably not done yet... */
-       if (done == budget)
+       if (done == budget) {
                INC_PERF_COUNTER(priv->pstats.napi_quota);
-       else {
+               if (unlikely(cq->mcq.irq_affinity_change)) {
+                       cq->mcq.irq_affinity_change = false;
+                       napi_complete(napi);
+                       mlx4_en_arm_cq(priv, cq);
+                       return 0;
+               }
+       } else {
                /* Done for now */
+               cq->mcq.irq_affinity_change = false;
                napi_complete(napi);
                mlx4_en_arm_cq(priv, cq);
        }
@@ -944,8 +948,8 @@ void mlx4_en_calc_rx_buf(struct net_device *dev)
        priv->rx_skb_size = eff_mtu;
        priv->log_rx_info = ROUNDUP_LOG2(i * sizeof(struct mlx4_en_rx_alloc));
 
-       en_dbg(DRV, priv, "Rx buffer scatter-list (effective-mtu:%d "
-                 "num_frags:%d):\n", eff_mtu, priv->num_frags);
+       en_dbg(DRV, priv, "Rx buffer scatter-list (effective-mtu:%d num_frags:%d):\n",
+              eff_mtu, priv->num_frags);
        for (i = 0; i < priv->num_frags; i++) {
                en_err(priv,
                       "  frag:%d - size:%d prefix:%d align:%d stride:%d\n",
index bc0cc1eb214daf0029bae922f513751be071b0bb..8be7483f82368c7733e4251019672d0cd227be08 100644 (file)
@@ -108,9 +108,9 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv,
 
        ring->buf = ring->wqres.buf.direct.buf;
 
-       en_dbg(DRV, priv, "Allocated TX ring (addr:%p) - buf:%p size:%d "
-              "buf_size:%d dma:%llx\n", ring, ring->buf, ring->size,
-              ring->buf_size, (unsigned long long) ring->wqres.buf.direct.map);
+       en_dbg(DRV, priv, "Allocated TX ring (addr:%p) - buf:%p size:%d buf_size:%d dma:%llx\n",
+              ring, ring->buf, ring->size, ring->buf_size,
+              (unsigned long long) ring->wqres.buf.direct.map);
 
        ring->qpn = qpn;
        err = mlx4_qp_alloc(mdev->dev, ring->qpn, &ring->qp, GFP_KERNEL);
@@ -122,7 +122,7 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv,
 
        err = mlx4_bf_alloc(mdev->dev, &ring->bf, node);
        if (err) {
-               en_dbg(DRV, priv, "working without blueflame (%d)", err);
+               en_dbg(DRV, priv, "working without blueflame (%d)\n", err);
                ring->bf.uar = &mdev->priv_uar;
                ring->bf.uar->map = mdev->uar_map;
                ring->bf_enabled = false;
@@ -474,9 +474,15 @@ int mlx4_en_poll_tx_cq(struct napi_struct *napi, int budget)
        /* If we used up all the quota - we're probably not done yet... */
        if (done < budget) {
                /* Done for now */
+               cq->mcq.irq_affinity_change = false;
                napi_complete(napi);
                mlx4_en_arm_cq(priv, cq);
                return done;
+       } else if (unlikely(cq->mcq.irq_affinity_change)) {
+               cq->mcq.irq_affinity_change = false;
+               napi_complete(napi);
+               mlx4_en_arm_cq(priv, cq);
+               return 0;
        }
        return budget;
 }
index d501a2b0fb79f18e560fd0cd067aa4c19b83b447..d954ec1eac173752e23e57653ccd4d2cae2de944 100644 (file)
@@ -53,6 +53,11 @@ enum {
        MLX4_EQ_ENTRY_SIZE      = 0x20
 };
 
+struct mlx4_irq_notify {
+       void *arg;
+       struct irq_affinity_notify notify;
+};
+
 #define MLX4_EQ_STATUS_OK         ( 0 << 28)
 #define MLX4_EQ_STATUS_WRITE_FAIL  (10 << 28)
 #define MLX4_EQ_OWNER_SW          ( 0 << 24)
@@ -152,14 +157,13 @@ void mlx4_gen_slave_eqe(struct work_struct *work)
                                if (i != dev->caps.function &&
                                    master->slave_state[i].active)
                                        if (mlx4_GEN_EQE(dev, i, eqe))
-                                               mlx4_warn(dev, "Failed to "
-                                                         " generate event "
-                                                         "for slave %d\n", i);
+                                               mlx4_warn(dev, "Failed to generate event for slave %d\n",
+                                                         i);
                        }
                } else {
                        if (mlx4_GEN_EQE(dev, slave, eqe))
-                               mlx4_warn(dev, "Failed to generate event "
-                                              "for slave %d\n", slave);
+                               mlx4_warn(dev, "Failed to generate event for slave %d\n",
+                                         slave);
                }
                ++slave_eq->cons;
        }
@@ -177,8 +181,8 @@ static void slave_event(struct mlx4_dev *dev, u8 slave, struct mlx4_eqe *eqe)
        s_eqe = &slave_eq->event_eqe[slave_eq->prod & (SLAVE_EVENT_EQ_SIZE - 1)];
        if ((!!(s_eqe->owner & 0x80)) ^
            (!!(slave_eq->prod & SLAVE_EVENT_EQ_SIZE))) {
-               mlx4_warn(dev, "Master failed to generate an EQE for slave: %d. "
-                         "No free EQE on slave events queue\n", slave);
+               mlx4_warn(dev, "Master failed to generate an EQE for slave: %d. No free EQE on slave events queue\n",
+                         slave);
                spin_unlock_irqrestore(&slave_eq->event_lock, flags);
                return;
        }
@@ -375,9 +379,9 @@ int set_and_calc_slave_port_state(struct mlx4_dev *dev, int slave,
                }
                break;
        default:
-               pr_err("%s: BUG!!! UNKNOWN state: "
-                      "slave:%d, port:%d\n", __func__, slave, port);
-                       goto out;
+               pr_err("%s: BUG!!! UNKNOWN state: slave:%d, port:%d\n",
+                      __func__, slave, port);
+               goto out;
        }
        ret = mlx4_get_slave_port_state(dev, slave, port);
 
@@ -425,8 +429,8 @@ void mlx4_master_handle_slave_flr(struct work_struct *work)
        for (i = 0 ; i < dev->num_slaves; i++) {
 
                if (MLX4_COMM_CMD_FLR == slave_state[i].last_cmd) {
-                       mlx4_dbg(dev, "mlx4_handle_slave_flr: "
-                                "clean slave: %d\n", i);
+                       mlx4_dbg(dev, "mlx4_handle_slave_flr: clean slave: %d\n",
+                                i);
 
                        mlx4_delete_all_resources_for_slave(dev, i);
                        /*return the slave to running mode*/
@@ -438,8 +442,8 @@ void mlx4_master_handle_slave_flr(struct work_struct *work)
                        err = mlx4_cmd(dev, 0, i, 0, MLX4_CMD_INFORM_FLR_DONE,
                                       MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED);
                        if (err)
-                               mlx4_warn(dev, "Failed to notify FW on "
-                                         "FLR done (slave:%d)\n", i);
+                               mlx4_warn(dev, "Failed to notify FW on FLR done (slave:%d)\n",
+                                         i);
                }
        }
 }
@@ -490,9 +494,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
                                                be32_to_cpu(eqe->event.qp.qpn)
                                                & 0xffffff, &slave);
                                if (ret && ret != -ENOENT) {
-                                       mlx4_dbg(dev, "QP event %02x(%02x) on "
-                                                "EQ %d at index %u: could "
-                                                "not get slave id (%d)\n",
+                                       mlx4_dbg(dev, "QP event %02x(%02x) on EQ %d at index %u: could not get slave id (%d)\n",
                                                 eqe->type, eqe->subtype,
                                                 eq->eqn, eq->cons_index, ret);
                                        break;
@@ -520,23 +522,19 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
                                                & 0xffffff,
                                                &slave);
                                if (ret && ret != -ENOENT) {
-                                       mlx4_warn(dev, "SRQ event %02x(%02x) "
-                                                 "on EQ %d at index %u: could"
-                                                 " not get slave id (%d)\n",
+                                       mlx4_warn(dev, "SRQ event %02x(%02x) on EQ %d at index %u: could not get slave id (%d)\n",
                                                  eqe->type, eqe->subtype,
                                                  eq->eqn, eq->cons_index, ret);
                                        break;
                                }
-                               mlx4_warn(dev, "%s: slave:%d, srq_no:0x%x,"
-                                         " event: %02x(%02x)\n", __func__,
-                                         slave,
+                               mlx4_warn(dev, "%s: slave:%d, srq_no:0x%x, event: %02x(%02x)\n",
+                                         __func__, slave,
                                          be32_to_cpu(eqe->event.srq.srqn),
                                          eqe->type, eqe->subtype);
 
                                if (!ret && slave != dev->caps.function) {
-                                       mlx4_warn(dev, "%s: sending event "
-                                                 "%02x(%02x) to slave:%d\n",
-                                                  __func__, eqe->type,
+                                       mlx4_warn(dev, "%s: sending event %02x(%02x) to slave:%d\n",
+                                                 __func__, eqe->type,
                                                  eqe->subtype, slave);
                                        mlx4_slave_event(dev, slave, eqe);
                                        break;
@@ -569,8 +567,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
                                        if (dev->caps.port_type[port] == MLX4_PORT_TYPE_ETH) {
                                                if (i == mlx4_master_func_num(dev))
                                                        continue;
-                                               mlx4_dbg(dev, "%s: Sending MLX4_PORT_CHANGE_SUBTYPE_DOWN"
-                                                        " to slave: %d, port:%d\n",
+                                               mlx4_dbg(dev, "%s: Sending MLX4_PORT_CHANGE_SUBTYPE_DOWN to slave: %d, port:%d\n",
                                                         __func__, i, port);
                                                s_info = &priv->mfunc.master.vf_oper[slave].vport[port].state;
                                                if (IFLA_VF_LINK_STATE_AUTO == s_info->link_state) {
@@ -634,11 +631,9 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
                                        be32_to_cpu(eqe->event.cq_err.cqn)
                                        & 0xffffff, &slave);
                                if (ret && ret != -ENOENT) {
-                                       mlx4_dbg(dev, "CQ event %02x(%02x) on "
-                                                "EQ %d at index %u: could "
-                                                 "not get slave id (%d)\n",
-                                                 eqe->type, eqe->subtype,
-                                                 eq->eqn, eq->cons_index, ret);
+                                       mlx4_dbg(dev, "CQ event %02x(%02x) on EQ %d at index %u: could not get slave id (%d)\n",
+                                                eqe->type, eqe->subtype,
+                                                eq->eqn, eq->cons_index, ret);
                                        break;
                                }
 
@@ -667,8 +662,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
 
                case MLX4_EVENT_TYPE_COMM_CHANNEL:
                        if (!mlx4_is_master(dev)) {
-                               mlx4_warn(dev, "Received comm channel event "
-                                              "for non master device\n");
+                               mlx4_warn(dev, "Received comm channel event for non master device\n");
                                break;
                        }
                        memcpy(&priv->mfunc.master.comm_arm_bit_vector,
@@ -681,8 +675,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
                case MLX4_EVENT_TYPE_FLR_EVENT:
                        flr_slave = be32_to_cpu(eqe->event.flr_event.slave_id);
                        if (!mlx4_is_master(dev)) {
-                               mlx4_warn(dev, "Non-master function received"
-                                              "FLR event\n");
+                               mlx4_warn(dev, "Non-master function received FLR event\n");
                                break;
                        }
 
@@ -711,22 +704,17 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
                        if (eqe->subtype == MLX4_FATAL_WARNING_SUBTYPE_WARMING) {
                                if (mlx4_is_master(dev))
                                        for (i = 0; i < dev->num_slaves; i++) {
-                                               mlx4_dbg(dev, "%s: Sending "
-                                                       "MLX4_FATAL_WARNING_SUBTYPE_WARMING"
-                                                       " to slave: %d\n", __func__, i);
+                                               mlx4_dbg(dev, "%s: Sending MLX4_FATAL_WARNING_SUBTYPE_WARMING to slave: %d\n",
+                                                        __func__, i);
                                                if (i == dev->caps.function)
                                                        continue;
                                                mlx4_slave_event(dev, i, eqe);
                                        }
-                               mlx4_err(dev, "Temperature Threshold was reached! "
-                                       "Threshold: %d celsius degrees; "
-                                       "Current Temperature: %d\n",
-                                       be16_to_cpu(eqe->event.warming.warning_threshold),
-                                       be16_to_cpu(eqe->event.warming.current_temperature));
+                               mlx4_err(dev, "Temperature Threshold was reached! Threshold: %d celsius degrees; Current Temperature: %d\n",
+                                        be16_to_cpu(eqe->event.warming.warning_threshold),
+                                        be16_to_cpu(eqe->event.warming.current_temperature));
                        } else
-                               mlx4_warn(dev, "Unhandled event FATAL WARNING (%02x), "
-                                         "subtype %02x on EQ %d at index %u. owner=%x, "
-                                         "nent=0x%x, slave=%x, ownership=%s\n",
+                               mlx4_warn(dev, "Unhandled event FATAL WARNING (%02x), subtype %02x on EQ %d at index %u. owner=%x, nent=0x%x, slave=%x, ownership=%s\n",
                                          eqe->type, eqe->subtype, eq->eqn,
                                          eq->cons_index, eqe->owner, eq->nent,
                                          eqe->slave_id,
@@ -743,9 +731,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
                case MLX4_EVENT_TYPE_EEC_CATAS_ERROR:
                case MLX4_EVENT_TYPE_ECC_DETECT:
                default:
-                       mlx4_warn(dev, "Unhandled event %02x(%02x) on EQ %d at "
-                                 "index %u. owner=%x, nent=0x%x, slave=%x, "
-                                 "ownership=%s\n",
+                       mlx4_warn(dev, "Unhandled event %02x(%02x) on EQ %d at index %u. owner=%x, nent=0x%x, slave=%x, ownership=%s\n",
                                  eqe->type, eqe->subtype, eq->eqn,
                                  eq->cons_index, eqe->owner, eq->nent,
                                  eqe->slave_id,
@@ -1088,7 +1074,7 @@ static int mlx4_map_clr_int(struct mlx4_dev *dev)
        priv->clr_base = ioremap(pci_resource_start(dev->pdev, priv->fw.clr_int_bar) +
                                 priv->fw.clr_int_base, MLX4_CLR_INT_SIZE);
        if (!priv->clr_base) {
-               mlx4_err(dev, "Couldn't map interrupt clear register, aborting.\n");
+               mlx4_err(dev, "Couldn't map interrupt clear register, aborting\n");
                return -ENOMEM;
        }
 
@@ -1102,6 +1088,57 @@ static void mlx4_unmap_clr_int(struct mlx4_dev *dev)
        iounmap(priv->clr_base);
 }
 
+static void mlx4_irq_notifier_notify(struct irq_affinity_notify *notify,
+                                    const cpumask_t *mask)
+{
+       struct mlx4_irq_notify *n = container_of(notify,
+                                                struct mlx4_irq_notify,
+                                                notify);
+       struct mlx4_priv *priv = (struct mlx4_priv *)n->arg;
+       struct radix_tree_iter iter;
+       void **slot;
+
+       radix_tree_for_each_slot(slot, &priv->cq_table.tree, &iter, 0) {
+               struct mlx4_cq *cq = (struct mlx4_cq *)(*slot);
+
+               if (cq->irq == notify->irq)
+                       cq->irq_affinity_change = true;
+       }
+}
+
+static void mlx4_release_irq_notifier(struct kref *ref)
+{
+       struct mlx4_irq_notify *n = container_of(ref, struct mlx4_irq_notify,
+                                                notify.kref);
+       kfree(n);
+}
+
+static void mlx4_assign_irq_notifier(struct mlx4_priv *priv,
+                                    struct mlx4_dev *dev, int irq)
+{
+       struct mlx4_irq_notify *irq_notifier = NULL;
+       int err = 0;
+
+       irq_notifier = kzalloc(sizeof(*irq_notifier), GFP_KERNEL);
+       if (!irq_notifier) {
+               mlx4_warn(dev, "Failed to allocate irq notifier. irq %d\n",
+                         irq);
+               return;
+       }
+
+       irq_notifier->notify.irq = irq;
+       irq_notifier->notify.notify = mlx4_irq_notifier_notify;
+       irq_notifier->notify.release = mlx4_release_irq_notifier;
+       irq_notifier->arg = priv;
+       err = irq_set_affinity_notifier(irq, &irq_notifier->notify);
+       if (err) {
+               kfree(irq_notifier);
+               irq_notifier = NULL;
+               mlx4_warn(dev, "Failed to set irq notifier. irq %d\n", irq);
+       }
+}
+
+
 int mlx4_alloc_eq_table(struct mlx4_dev *dev)
 {
        struct mlx4_priv *priv = mlx4_priv(dev);
@@ -1372,6 +1409,9 @@ int mlx4_assign_eq(struct mlx4_dev *dev, char *name, struct cpu_rmap *rmap,
                                continue;
                                /*we dont want to break here*/
                        }
+                       mlx4_assign_irq_notifier(priv, dev,
+                                                priv->eq_table.eq[vec].irq);
+
                        eq_set_ci(&priv->eq_table.eq[vec], 1);
                }
        }
@@ -1398,6 +1438,9 @@ void mlx4_release_eq(struct mlx4_dev *dev, int vec)
                  Belonging to a legacy EQ*/
                mutex_lock(&priv->msix_ctl.pool_lock);
                if (priv->msix_ctl.pool_bm & 1ULL << i) {
+                       irq_set_affinity_notifier(
+                               priv->eq_table.eq[vec].irq,
+                               NULL);
                        free_irq(priv->eq_table.eq[vec].irq,
                                 &priv->eq_table.eq[vec]);
                        priv->msix_ctl.pool_bm &= ~(1ULL << i);
index 01e6dd61ee3c0ea6e3356c18a29227d98ca83582..688e1eabab29d697137477ea4a05b1dbb46a6d1f 100644 (file)
@@ -437,8 +437,7 @@ int mlx4_QUERY_FUNC_CAP(struct mlx4_dev *dev, u32 gen_or_port,
        } else if (dev->caps.port_type[gen_or_port] == MLX4_PORT_TYPE_IB) {
                MLX4_GET(field, outbox, QUERY_FUNC_CAP_FLAGS0_OFFSET);
                if (field & QUERY_FUNC_CAP_FLAGS0_FORCE_PHY_WQE_GID) {
-                       mlx4_err(dev, "phy_wqe_gid is "
-                                "enforced on this ib port\n");
+                       mlx4_err(dev, "phy_wqe_gid is enforced on this ib port\n");
                        err = -EPROTONOSUPPORT;
                        goto out;
                }
@@ -1070,10 +1069,10 @@ int mlx4_map_cmd(struct mlx4_dev *dev, u16 op, struct mlx4_icm *icm, u64 virt)
                 */
                lg = ffs(mlx4_icm_addr(&iter) | mlx4_icm_size(&iter)) - 1;
                if (lg < MLX4_ICM_PAGE_SHIFT) {
-                       mlx4_warn(dev, "Got FW area not aligned to %d (%llx/%lx).\n",
-                                  MLX4_ICM_PAGE_SIZE,
-                                  (unsigned long long) mlx4_icm_addr(&iter),
-                                  mlx4_icm_size(&iter));
+                       mlx4_warn(dev, "Got FW area not aligned to %d (%llx/%lx)\n",
+                                 MLX4_ICM_PAGE_SIZE,
+                                 (unsigned long long) mlx4_icm_addr(&iter),
+                                 mlx4_icm_size(&iter));
                        err = -EINVAL;
                        goto out;
                }
@@ -1109,14 +1108,14 @@ int mlx4_map_cmd(struct mlx4_dev *dev, u16 op, struct mlx4_icm *icm, u64 virt)
 
        switch (op) {
        case MLX4_CMD_MAP_FA:
-               mlx4_dbg(dev, "Mapped %d chunks/%d KB for FW.\n", tc, ts);
+               mlx4_dbg(dev, "Mapped %d chunks/%d KB for FW\n", tc, ts);
                break;
        case MLX4_CMD_MAP_ICM_AUX:
-               mlx4_dbg(dev, "Mapped %d chunks/%d KB for ICM aux.\n", tc, ts);
+               mlx4_dbg(dev, "Mapped %d chunks/%d KB for ICM aux\n", tc, ts);
                break;
        case MLX4_CMD_MAP_ICM:
-               mlx4_dbg(dev, "Mapped %d chunks/%d KB at %llx for ICM.\n",
-                         tc, ts, (unsigned long long) virt - (ts << 10));
+               mlx4_dbg(dev, "Mapped %d chunks/%d KB at %llx for ICM\n",
+                        tc, ts, (unsigned long long) virt - (ts << 10));
                break;
        }
 
@@ -1202,14 +1201,13 @@ int mlx4_QUERY_FW(struct mlx4_dev *dev)
        MLX4_GET(cmd_if_rev, outbox, QUERY_FW_CMD_IF_REV_OFFSET);
        if (cmd_if_rev < MLX4_COMMAND_INTERFACE_MIN_REV ||
            cmd_if_rev > MLX4_COMMAND_INTERFACE_MAX_REV) {
-               mlx4_err(dev, "Installed FW has unsupported "
-                        "command interface revision %d.\n",
+               mlx4_err(dev, "Installed FW has unsupported command interface revision %d\n",
                         cmd_if_rev);
                mlx4_err(dev, "(Installed FW version is %d.%d.%03d)\n",
                         (int) (dev->caps.fw_ver >> 32),
                         (int) (dev->caps.fw_ver >> 16) & 0xffff,
                         (int) dev->caps.fw_ver & 0xffff);
-               mlx4_err(dev, "This driver version supports only revisions %d to %d.\n",
+               mlx4_err(dev, "This driver version supports only revisions %d to %d\n",
                         MLX4_COMMAND_INTERFACE_MIN_REV, MLX4_COMMAND_INTERFACE_MAX_REV);
                err = -ENODEV;
                goto out;
index 26169b3eaed8b2f521f8aa02afd90e7717bc48b8..5f42f6d6e4c6fad9be8437950b44a7cc6658de7f 100644 (file)
@@ -104,8 +104,6 @@ module_param(enable_64b_cqe_eqe, bool, 0444);
 MODULE_PARM_DESC(enable_64b_cqe_eqe,
                 "Enable 64 byte CQEs/EQEs when the FW supports this (default: True)");
 
-#define HCA_GLOBAL_CAP_MASK            0
-
 #define PF_CONTEXT_BEHAVIOUR_MASK      MLX4_FUNC_CAP_64B_EQE_CQE
 
 static char mlx4_version[] =
@@ -134,8 +132,7 @@ MODULE_PARM_DESC(log_num_vlan, "Log2 max number of VLANs per ETH port (0-7)");
 
 static bool use_prio;
 module_param_named(use_prio, use_prio, bool, 0444);
-MODULE_PARM_DESC(use_prio, "Enable steering by VLAN priority on ETH ports "
-                 "(0/1, default 0)");
+MODULE_PARM_DESC(use_prio, "Enable steering by VLAN priority on ETH ports (deprecated)");
 
 int log_mtts_per_seg = ilog2(MLX4_MTT_ENTRY_PER_SEG);
 module_param_named(log_mtts_per_seg, log_mtts_per_seg, int, 0444);
@@ -163,8 +160,7 @@ int mlx4_check_port_params(struct mlx4_dev *dev,
        for (i = 0; i < dev->caps.num_ports - 1; i++) {
                if (port_type[i] != port_type[i + 1]) {
                        if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_DPDP)) {
-                               mlx4_err(dev, "Only same port types supported "
-                                        "on this HCA, aborting.\n");
+                               mlx4_err(dev, "Only same port types supported on this HCA, aborting\n");
                                return -EINVAL;
                        }
                }
@@ -172,8 +168,8 @@ int mlx4_check_port_params(struct mlx4_dev *dev,
 
        for (i = 0; i < dev->caps.num_ports; i++) {
                if (!(port_type[i] & dev->caps.supported_type[i+1])) {
-                       mlx4_err(dev, "Requested port type for port %d is not "
-                                     "supported on this HCA\n", i + 1);
+                       mlx4_err(dev, "Requested port type for port %d is not supported on this HCA\n",
+                                i + 1);
                        return -EINVAL;
                }
        }
@@ -195,26 +191,23 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
 
        err = mlx4_QUERY_DEV_CAP(dev, dev_cap);
        if (err) {
-               mlx4_err(dev, "QUERY_DEV_CAP command failed, aborting.\n");
+               mlx4_err(dev, "QUERY_DEV_CAP command failed, aborting\n");
                return err;
        }
 
        if (dev_cap->min_page_sz > PAGE_SIZE) {
-               mlx4_err(dev, "HCA minimum page size of %d bigger than "
-                        "kernel PAGE_SIZE of %ld, aborting.\n",
+               mlx4_err(dev, "HCA minimum page size of %d bigger than kernel PAGE_SIZE of %ld, aborting\n",
                         dev_cap->min_page_sz, PAGE_SIZE);
                return -ENODEV;
        }
        if (dev_cap->num_ports > MLX4_MAX_PORTS) {
-               mlx4_err(dev, "HCA has %d ports, but we only support %d, "
-                        "aborting.\n",
+               mlx4_err(dev, "HCA has %d ports, but we only support %d, aborting\n",
                         dev_cap->num_ports, MLX4_MAX_PORTS);
                return -ENODEV;
        }
 
        if (dev_cap->uar_size > pci_resource_len(dev->pdev, 2)) {
-               mlx4_err(dev, "HCA reported UAR size of 0x%x bigger than "
-                        "PCI resource 2 size of 0x%llx, aborting.\n",
+               mlx4_err(dev, "HCA reported UAR size of 0x%x bigger than PCI resource 2 size of 0x%llx, aborting\n",
                         dev_cap->uar_size,
                         (unsigned long long) pci_resource_len(dev->pdev, 2));
                return -ENODEV;
@@ -296,7 +289,6 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
 
        dev->caps.log_num_macs  = log_num_mac;
        dev->caps.log_num_vlans = MLX4_LOG_NUM_VLANS;
-       dev->caps.log_num_prios = use_prio ? 3 : 0;
 
        for (i = 1; i <= dev->caps.num_ports; ++i) {
                dev->caps.port_type[i] = MLX4_PORT_TYPE_NONE;
@@ -347,14 +339,12 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
 
                if (dev->caps.log_num_macs > dev_cap->log_max_macs[i]) {
                        dev->caps.log_num_macs = dev_cap->log_max_macs[i];
-                       mlx4_warn(dev, "Requested number of MACs is too much "
-                                 "for port %d, reducing to %d.\n",
+                       mlx4_warn(dev, "Requested number of MACs is too much for port %d, reducing to %d\n",
                                  i, 1 << dev->caps.log_num_macs);
                }
                if (dev->caps.log_num_vlans > dev_cap->log_max_vlans[i]) {
                        dev->caps.log_num_vlans = dev_cap->log_max_vlans[i];
-                       mlx4_warn(dev, "Requested number of VLANs is too much "
-                                 "for port %d, reducing to %d.\n",
+                       mlx4_warn(dev, "Requested number of VLANs is too much for port %d, reducing to %d\n",
                                  i, 1 << dev->caps.log_num_vlans);
                }
        }
@@ -366,7 +356,6 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
                dev->caps.reserved_qps_cnt[MLX4_QP_REGION_FC_ADDR] =
                (1 << dev->caps.log_num_macs) *
                (1 << dev->caps.log_num_vlans) *
-               (1 << dev->caps.log_num_prios) *
                dev->caps.num_ports;
        dev->caps.reserved_qps_cnt[MLX4_QP_REGION_FC_EXCH] = MLX4_NUM_FEXCH;
 
@@ -584,13 +573,14 @@ static int mlx4_slave_cap(struct mlx4_dev *dev)
        memset(&hca_param, 0, sizeof(hca_param));
        err = mlx4_QUERY_HCA(dev, &hca_param);
        if (err) {
-               mlx4_err(dev, "QUERY_HCA command failed, aborting.\n");
+               mlx4_err(dev, "QUERY_HCA command failed, aborting\n");
                return err;
        }
 
-       /*fail if the hca has an unknown capability */
-       if ((hca_param.global_caps | HCA_GLOBAL_CAP_MASK) !=
-           HCA_GLOBAL_CAP_MASK) {
+       /* fail if the hca has an unknown global capability
+        * at this time global_caps should be always zeroed
+        */
+       if (hca_param.global_caps) {
                mlx4_err(dev, "Unknown hca global capabilities\n");
                return -ENOSYS;
        }
@@ -603,19 +593,18 @@ static int mlx4_slave_cap(struct mlx4_dev *dev)
        dev->caps.max_qp_dest_rdma = 1 << hca_param.log_rd_per_qp;
        err = mlx4_dev_cap(dev, &dev_cap);
        if (err) {
-               mlx4_err(dev, "QUERY_DEV_CAP command failed, aborting.\n");
+               mlx4_err(dev, "QUERY_DEV_CAP command failed, aborting\n");
                return err;
        }
 
        err = mlx4_QUERY_FW(dev);
        if (err)
-               mlx4_err(dev, "QUERY_FW command failed: could not get FW version.\n");
+               mlx4_err(dev, "QUERY_FW command failed: could not get FW version\n");
 
        page_size = ~dev->caps.page_size_cap + 1;
        mlx4_warn(dev, "HCA minimum page size:%d\n", page_size);
        if (page_size > PAGE_SIZE) {
-               mlx4_err(dev, "HCA minimum page size of %d bigger than "
-                        "kernel PAGE_SIZE of %ld, aborting.\n",
+               mlx4_err(dev, "HCA minimum page size of %d bigger than kernel PAGE_SIZE of %ld, aborting\n",
                         page_size, PAGE_SIZE);
                return -ENODEV;
        }
@@ -633,8 +622,8 @@ static int mlx4_slave_cap(struct mlx4_dev *dev)
        memset(&func_cap, 0, sizeof(func_cap));
        err = mlx4_QUERY_FUNC_CAP(dev, 0, &func_cap);
        if (err) {
-               mlx4_err(dev, "QUERY_FUNC_CAP general command failed, aborting (%d).\n",
-                         err);
+               mlx4_err(dev, "QUERY_FUNC_CAP general command failed, aborting (%d)\n",
+                        err);
                return err;
        }
 
@@ -661,8 +650,8 @@ static int mlx4_slave_cap(struct mlx4_dev *dev)
        dev->caps.num_amgms             = 0;
 
        if (dev->caps.num_ports > MLX4_MAX_PORTS) {
-               mlx4_err(dev, "HCA has %d ports, but we only support %d, "
-                        "aborting.\n", dev->caps.num_ports, MLX4_MAX_PORTS);
+               mlx4_err(dev, "HCA has %d ports, but we only support %d, aborting\n",
+                        dev->caps.num_ports, MLX4_MAX_PORTS);
                return -ENODEV;
        }
 
@@ -682,8 +671,8 @@ static int mlx4_slave_cap(struct mlx4_dev *dev)
        for (i = 1; i <= dev->caps.num_ports; ++i) {
                err = mlx4_QUERY_FUNC_CAP(dev, (u32) i, &func_cap);
                if (err) {
-                       mlx4_err(dev, "QUERY_FUNC_CAP port command failed for"
-                                " port %d, aborting (%d).\n", i, err);
+                       mlx4_err(dev, "QUERY_FUNC_CAP port command failed for port %d, aborting (%d)\n",
+                                i, err);
                        goto err_mem;
                }
                dev->caps.qp0_qkey[i - 1] = func_cap.qp0_qkey;
@@ -702,8 +691,7 @@ static int mlx4_slave_cap(struct mlx4_dev *dev)
        if (dev->caps.uar_page_size * (dev->caps.num_uars -
                                       dev->caps.reserved_uars) >
                                       pci_resource_len(dev->pdev, 2)) {
-               mlx4_err(dev, "HCA reported UAR region size of 0x%x bigger than "
-                        "PCI resource 2 size of 0x%llx, aborting.\n",
+               mlx4_err(dev, "HCA reported UAR region size of 0x%x bigger than PCI resource 2 size of 0x%llx, aborting\n",
                         dev->caps.uar_page_size * dev->caps.num_uars,
                         (unsigned long long) pci_resource_len(dev->pdev, 2));
                goto err_mem;
@@ -725,7 +713,7 @@ static int mlx4_slave_cap(struct mlx4_dev *dev)
        }
 
        dev->caps.flags2 &= ~MLX4_DEV_CAP_FLAG2_TS;
-       mlx4_warn(dev, "Timestamping is not supported in slave mode.\n");
+       mlx4_warn(dev, "Timestamping is not supported in slave mode\n");
 
        slave_adjust_steering_mode(dev, &dev_cap, &hca_param);
 
@@ -791,8 +779,8 @@ int mlx4_change_port_types(struct mlx4_dev *dev,
                        dev->caps.port_type[port] = port_types[port - 1];
                        err = mlx4_SET_PORT(dev, port, -1);
                        if (err) {
-                               mlx4_err(dev, "Failed to set port %d, "
-                                             "aborting\n", port);
+                               mlx4_err(dev, "Failed to set port %d, aborting\n",
+                                        port);
                                goto out;
                        }
                }
@@ -875,9 +863,7 @@ static ssize_t set_port_type(struct device *dev,
                }
        }
        if (err) {
-               mlx4_err(mdev, "Auto sensing is not supported on this HCA. "
-                              "Set only 'eth' or 'ib' for both ports "
-                              "(should be the same)\n");
+               mlx4_err(mdev, "Auto sensing is not supported on this HCA. Set only 'eth' or 'ib' for both ports (should be the same)\n");
                goto out;
        }
 
@@ -982,8 +968,8 @@ static ssize_t set_port_ib_mtu(struct device *dev,
                mlx4_CLOSE_PORT(mdev, port);
                err = mlx4_SET_PORT(mdev, port, -1);
                if (err) {
-                       mlx4_err(mdev, "Failed to set port %d, "
-                                     "aborting\n", port);
+                       mlx4_err(mdev, "Failed to set port %d, aborting\n",
+                                port);
                        goto err_set_port;
                }
        }
@@ -1002,19 +988,19 @@ static int mlx4_load_fw(struct mlx4_dev *dev)
        priv->fw.fw_icm = mlx4_alloc_icm(dev, priv->fw.fw_pages,
                                         GFP_HIGHUSER | __GFP_NOWARN, 0);
        if (!priv->fw.fw_icm) {
-               mlx4_err(dev, "Couldn't allocate FW area, aborting.\n");
+               mlx4_err(dev, "Couldn't allocate FW area, aborting\n");
                return -ENOMEM;
        }
 
        err = mlx4_MAP_FA(dev, priv->fw.fw_icm);
        if (err) {
-               mlx4_err(dev, "MAP_FA command failed, aborting.\n");
+               mlx4_err(dev, "MAP_FA command failed, aborting\n");
                goto err_free;
        }
 
        err = mlx4_RUN_FW(dev);
        if (err) {
-               mlx4_err(dev, "RUN_FW command failed, aborting.\n");
+               mlx4_err(dev, "RUN_FW command failed, aborting\n");
                goto err_unmap_fa;
        }
 
@@ -1098,30 +1084,30 @@ static int mlx4_init_icm(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap,
 
        err = mlx4_SET_ICM_SIZE(dev, icm_size, &aux_pages);
        if (err) {
-               mlx4_err(dev, "SET_ICM_SIZE command failed, aborting.\n");
+               mlx4_err(dev, "SET_ICM_SIZE command failed, aborting\n");
                return err;
        }
 
-       mlx4_dbg(dev, "%lld KB of HCA context requires %lld KB aux memory.\n",
+       mlx4_dbg(dev, "%lld KB of HCA context requires %lld KB aux memory\n",
                 (unsigned long long) icm_size >> 10,
                 (unsigned long long) aux_pages << 2);
 
        priv->fw.aux_icm = mlx4_alloc_icm(dev, aux_pages,
                                          GFP_HIGHUSER | __GFP_NOWARN, 0);
        if (!priv->fw.aux_icm) {
-               mlx4_err(dev, "Couldn't allocate aux memory, aborting.\n");
+               mlx4_err(dev, "Couldn't allocate aux memory, aborting\n");
                return -ENOMEM;
        }
 
        err = mlx4_MAP_ICM_AUX(dev, priv->fw.aux_icm);
        if (err) {
-               mlx4_err(dev, "MAP_ICM_AUX command failed, aborting.\n");
+               mlx4_err(dev, "MAP_ICM_AUX command failed, aborting\n");
                goto err_free_aux;
        }
 
        err = mlx4_init_cmpt_table(dev, init_hca->cmpt_base, dev_cap->cmpt_entry_sz);
        if (err) {
-               mlx4_err(dev, "Failed to map cMPT context memory, aborting.\n");
+               mlx4_err(dev, "Failed to map cMPT context memory, aborting\n");
                goto err_unmap_aux;
        }
 
@@ -1132,7 +1118,7 @@ static int mlx4_init_icm(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap,
                                  init_hca->eqc_base, dev_cap->eqc_entry_sz,
                                  num_eqs, num_eqs, 0, 0);
        if (err) {
-               mlx4_err(dev, "Failed to map EQ context memory, aborting.\n");
+               mlx4_err(dev, "Failed to map EQ context memory, aborting\n");
                goto err_unmap_cmpt;
        }
 
@@ -1153,7 +1139,7 @@ static int mlx4_init_icm(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap,
                                  dev->caps.num_mtts,
                                  dev->caps.reserved_mtts, 1, 0);
        if (err) {
-               mlx4_err(dev, "Failed to map MTT context memory, aborting.\n");
+               mlx4_err(dev, "Failed to map MTT context memory, aborting\n");
                goto err_unmap_eq;
        }
 
@@ -1163,7 +1149,7 @@ static int mlx4_init_icm(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap,
                                  dev->caps.num_mpts,
                                  dev->caps.reserved_mrws, 1, 1);
        if (err) {
-               mlx4_err(dev, "Failed to map dMPT context memory, aborting.\n");
+               mlx4_err(dev, "Failed to map dMPT context memory, aborting\n");
                goto err_unmap_mtt;
        }
 
@@ -1174,7 +1160,7 @@ static int mlx4_init_icm(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap,
                                  dev->caps.reserved_qps_cnt[MLX4_QP_REGION_FW],
                                  0, 0);
        if (err) {
-               mlx4_err(dev, "Failed to map QP context memory, aborting.\n");
+               mlx4_err(dev, "Failed to map QP context memory, aborting\n");
                goto err_unmap_dmpt;
        }
 
@@ -1185,7 +1171,7 @@ static int mlx4_init_icm(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap,
                                  dev->caps.reserved_qps_cnt[MLX4_QP_REGION_FW],
                                  0, 0);
        if (err) {
-               mlx4_err(dev, "Failed to map AUXC context memory, aborting.\n");
+               mlx4_err(dev, "Failed to map AUXC context memory, aborting\n");
                goto err_unmap_qp;
        }
 
@@ -1196,7 +1182,7 @@ static int mlx4_init_icm(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap,
                                  dev->caps.reserved_qps_cnt[MLX4_QP_REGION_FW],
                                  0, 0);
        if (err) {
-               mlx4_err(dev, "Failed to map ALTC context memory, aborting.\n");
+               mlx4_err(dev, "Failed to map ALTC context memory, aborting\n");
                goto err_unmap_auxc;
        }
 
@@ -1217,7 +1203,7 @@ static int mlx4_init_icm(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap,
                                  dev->caps.num_cqs,
                                  dev->caps.reserved_cqs, 0, 0);
        if (err) {
-               mlx4_err(dev, "Failed to map CQ context memory, aborting.\n");
+               mlx4_err(dev, "Failed to map CQ context memory, aborting\n");
                goto err_unmap_rdmarc;
        }
 
@@ -1227,7 +1213,7 @@ static int mlx4_init_icm(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap,
                                  dev->caps.num_srqs,
                                  dev->caps.reserved_srqs, 0, 0);
        if (err) {
-               mlx4_err(dev, "Failed to map SRQ context memory, aborting.\n");
+               mlx4_err(dev, "Failed to map SRQ context memory, aborting\n");
                goto err_unmap_cq;
        }
 
@@ -1245,7 +1231,7 @@ static int mlx4_init_icm(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap,
                                  dev->caps.num_mgms + dev->caps.num_amgms,
                                  0, 0);
        if (err) {
-               mlx4_err(dev, "Failed to map MCG context memory, aborting.\n");
+               mlx4_err(dev, "Failed to map MCG context memory, aborting\n");
                goto err_unmap_srq;
        }
 
@@ -1322,7 +1308,7 @@ static void mlx4_slave_exit(struct mlx4_dev *dev)
 
        mutex_lock(&priv->cmd.slave_cmd_mutex);
        if (mlx4_comm_cmd(dev, MLX4_COMM_CMD_RESET, 0, MLX4_COMM_TIME))
-               mlx4_warn(dev, "Failed to close slave function.\n");
+               mlx4_warn(dev, "Failed to close slave function\n");
        mutex_unlock(&priv->cmd.slave_cmd_mutex);
 }
 
@@ -1420,7 +1406,7 @@ static int mlx4_init_slave(struct mlx4_dev *dev)
        u32 cmd_channel_ver;
 
        if (atomic_read(&pf_loading)) {
-               mlx4_warn(dev, "PF is not ready. Deferring probe\n");
+               mlx4_warn(dev, "PF is not ready - Deferring probe\n");
                return -EPROBE_DEFER;
        }
 
@@ -1433,8 +1419,7 @@ static int mlx4_init_slave(struct mlx4_dev *dev)
         * NUM_OF_RESET_RETRIES times before leaving.*/
        if (ret_from_reset) {
                if (MLX4_DELAY_RESET_SLAVE == ret_from_reset) {
-                       mlx4_warn(dev, "slave is currently in the "
-                                 "middle of FLR. Deferring probe.\n");
+                       mlx4_warn(dev, "slave is currently in the middle of FLR - Deferring probe\n");
                        mutex_unlock(&priv->cmd.slave_cmd_mutex);
                        return -EPROBE_DEFER;
                } else
@@ -1448,8 +1433,7 @@ static int mlx4_init_slave(struct mlx4_dev *dev)
 
        if (MLX4_COMM_GET_IF_REV(cmd_channel_ver) !=
                MLX4_COMM_GET_IF_REV(slave_read)) {
-               mlx4_err(dev, "slave driver version is not supported"
-                        " by the master\n");
+               mlx4_err(dev, "slave driver version is not supported by the master\n");
                goto err;
        }
 
@@ -1527,8 +1511,7 @@ static void choose_steering_mode(struct mlx4_dev *dev,
 
                        if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER ||
                            dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER)
-                               mlx4_warn(dev, "Must have both UC_STEER and MC_STEER flags "
-                                         "set to use B0 steering. Falling back to A0 steering mode.\n");
+                               mlx4_warn(dev, "Must have both UC_STEER and MC_STEER flags set to use B0 steering - falling back to A0 steering mode\n");
                }
                dev->oper_log_mgm_entry_size =
                        mlx4_log_num_mgm_entry_size > 0 ?
@@ -1536,8 +1519,7 @@ static void choose_steering_mode(struct mlx4_dev *dev,
                        MLX4_DEFAULT_MGM_LOG_ENTRY_SIZE;
                dev->caps.num_qp_per_mgm = mlx4_get_qp_per_mgm(dev);
        }
-       mlx4_dbg(dev, "Steering mode is: %s, oper_log_mgm_entry_size = %d, "
-                "modparam log_num_mgm_entry_size = %d\n",
+       mlx4_dbg(dev, "Steering mode is: %s, oper_log_mgm_entry_size = %d, modparam log_num_mgm_entry_size = %d\n",
                 mlx4_steering_mode_str(dev->caps.steering_mode),
                 dev->oper_log_mgm_entry_size,
                 mlx4_log_num_mgm_entry_size);
@@ -1571,15 +1553,15 @@ static int mlx4_init_hca(struct mlx4_dev *dev)
                err = mlx4_QUERY_FW(dev);
                if (err) {
                        if (err == -EACCES)
-                               mlx4_info(dev, "non-primary physical function, skipping.\n");
+                               mlx4_info(dev, "non-primary physical function, skipping\n");
                        else
-                               mlx4_err(dev, "QUERY_FW command failed, aborting.\n");
+                               mlx4_err(dev, "QUERY_FW command failed, aborting\n");
                        return err;
                }
 
                err = mlx4_load_fw(dev);
                if (err) {
-                       mlx4_err(dev, "Failed to start FW, aborting.\n");
+                       mlx4_err(dev, "Failed to start FW, aborting\n");
                        return err;
                }
 
@@ -1591,7 +1573,7 @@ static int mlx4_init_hca(struct mlx4_dev *dev)
 
                err = mlx4_dev_cap(dev, &dev_cap);
                if (err) {
-                       mlx4_err(dev, "QUERY_DEV_CAP command failed, aborting.\n");
+                       mlx4_err(dev, "QUERY_DEV_CAP command failed, aborting\n");
                        goto err_stop_fw;
                }
 
@@ -1632,7 +1614,7 @@ static int mlx4_init_hca(struct mlx4_dev *dev)
 
                err = mlx4_INIT_HCA(dev, &init_hca);
                if (err) {
-                       mlx4_err(dev, "INIT_HCA command failed, aborting.\n");
+                       mlx4_err(dev, "INIT_HCA command failed, aborting\n");
                        goto err_free_icm;
                }
                /*
@@ -1643,7 +1625,7 @@ static int mlx4_init_hca(struct mlx4_dev *dev)
                        memset(&init_hca, 0, sizeof(init_hca));
                        err = mlx4_QUERY_HCA(dev, &init_hca);
                        if (err) {
-                               mlx4_err(dev, "QUERY_HCA command failed, disable timestamp.\n");
+                               mlx4_err(dev, "QUERY_HCA command failed, disable timestamp\n");
                                dev->caps.flags2 &= ~MLX4_DEV_CAP_FLAG2_TS;
                        } else {
                                dev->caps.hca_core_clock =
@@ -1656,14 +1638,14 @@ static int mlx4_init_hca(struct mlx4_dev *dev)
                        if (!dev->caps.hca_core_clock) {
                                dev->caps.flags2 &= ~MLX4_DEV_CAP_FLAG2_TS;
                                mlx4_err(dev,
-                                        "HCA frequency is 0. Timestamping is not supported.");
+                                        "HCA frequency is 0 - timestamping is not supported\n");
                        } else if (map_internal_clock(dev)) {
                                /*
                                 * Map internal clock,
                                 * in case of failure disable timestamping
                                 */
                                dev->caps.flags2 &= ~MLX4_DEV_CAP_FLAG2_TS;
-                               mlx4_err(dev, "Failed to map internal clock. Timestamping is not supported.\n");
+                               mlx4_err(dev, "Failed to map internal clock. Timestamping is not supported\n");
                        }
                }
        } else {
@@ -1690,7 +1672,7 @@ static int mlx4_init_hca(struct mlx4_dev *dev)
 
        err = mlx4_QUERY_ADAPTER(dev, &adapter);
        if (err) {
-               mlx4_err(dev, "QUERY_ADAPTER command failed, aborting.\n");
+               mlx4_err(dev, "QUERY_ADAPTER command failed, aborting\n");
                goto unmap_bf;
        }
 
@@ -1808,79 +1790,69 @@ static int mlx4_setup_hca(struct mlx4_dev *dev)
 
        err = mlx4_init_uar_table(dev);
        if (err) {
-               mlx4_err(dev, "Failed to initialize "
-                        "user access region table, aborting.\n");
-               return err;
+               mlx4_err(dev, "Failed to initialize user access region table, aborting\n");
+                return err;
        }
 
        err = mlx4_uar_alloc(dev, &priv->driver_uar);
        if (err) {
-               mlx4_err(dev, "Failed to allocate driver access region, "
-                        "aborting.\n");
+               mlx4_err(dev, "Failed to allocate driver access region, aborting\n");
                goto err_uar_table_free;
        }
 
        priv->kar = ioremap((phys_addr_t) priv->driver_uar.pfn << PAGE_SHIFT, PAGE_SIZE);
        if (!priv->kar) {
-               mlx4_err(dev, "Couldn't map kernel access region, "
-                        "aborting.\n");
+               mlx4_err(dev, "Couldn't map kernel access region, aborting\n");
                err = -ENOMEM;
                goto err_uar_free;
        }
 
        err = mlx4_init_pd_table(dev);
        if (err) {
-               mlx4_err(dev, "Failed to initialize "
-                        "protection domain table, aborting.\n");
+               mlx4_err(dev, "Failed to initialize protection domain table, aborting\n");
                goto err_kar_unmap;
        }
 
        err = mlx4_init_xrcd_table(dev);
        if (err) {
-               mlx4_err(dev, "Failed to initialize "
-                        "reliable connection domain table, aborting.\n");
+               mlx4_err(dev, "Failed to initialize reliable connection domain table, aborting\n");
                goto err_pd_table_free;
        }
 
        err = mlx4_init_mr_table(dev);
        if (err) {
-               mlx4_err(dev, "Failed to initialize "
-                        "memory region table, aborting.\n");
+               mlx4_err(dev, "Failed to initialize memory region table, aborting\n");
                goto err_xrcd_table_free;
        }
 
        if (!mlx4_is_slave(dev)) {
                err = mlx4_init_mcg_table(dev);
                if (err) {
-                       mlx4_err(dev, "Failed to initialize multicast group table, aborting.\n");
+                       mlx4_err(dev, "Failed to initialize multicast group table, aborting\n");
                        goto err_mr_table_free;
                }
        }
 
        err = mlx4_init_eq_table(dev);
        if (err) {
-               mlx4_err(dev, "Failed to initialize "
-                        "event queue table, aborting.\n");
+               mlx4_err(dev, "Failed to initialize event queue table, aborting\n");
                goto err_mcg_table_free;
        }
 
        err = mlx4_cmd_use_events(dev);
        if (err) {
-               mlx4_err(dev, "Failed to switch to event-driven "
-                        "firmware commands, aborting.\n");
+               mlx4_err(dev, "Failed to switch to event-driven firmware commands, aborting\n");
                goto err_eq_table_free;
        }
 
        err = mlx4_NOP(dev);
        if (err) {
                if (dev->flags & MLX4_FLAG_MSI_X) {
-                       mlx4_warn(dev, "NOP command failed to generate MSI-X "
-                                 "interrupt IRQ %d).\n",
+                       mlx4_warn(dev, "NOP command failed to generate MSI-X interrupt IRQ %d)\n",
                                  priv->eq_table.eq[dev->caps.num_comp_vectors].irq);
-                       mlx4_warn(dev, "Trying again without MSI-X.\n");
+                       mlx4_warn(dev, "Trying again without MSI-X\n");
                } else {
-                       mlx4_err(dev, "NOP command failed to generate interrupt "
-                                "(IRQ %d), aborting.\n",
+                       mlx4_err(dev, "NOP command failed to generate interrupt (IRQ %d), aborting\n",
                                 priv->eq_table.eq[dev->caps.num_comp_vectors].irq);
                        mlx4_err(dev, "BIOS or ACPI interrupt routing problem?\n");
                }
@@ -1892,28 +1864,25 @@ static int mlx4_setup_hca(struct mlx4_dev *dev)
 
        err = mlx4_init_cq_table(dev);
        if (err) {
-               mlx4_err(dev, "Failed to initialize "
-                        "completion queue table, aborting.\n");
+               mlx4_err(dev, "Failed to initialize completion queue table, aborting\n");
                goto err_cmd_poll;
        }
 
        err = mlx4_init_srq_table(dev);
        if (err) {
-               mlx4_err(dev, "Failed to initialize "
-                        "shared receive queue table, aborting.\n");
+               mlx4_err(dev, "Failed to initialize shared receive queue table, aborting\n");
                goto err_cq_table_free;
        }
 
        err = mlx4_init_qp_table(dev);
        if (err) {
-               mlx4_err(dev, "Failed to initialize "
-                        "queue pair table, aborting.\n");
+               mlx4_err(dev, "Failed to initialize queue pair table, aborting\n");
                goto err_srq_table_free;
        }
 
        err = mlx4_init_counters_table(dev);
        if (err && err != -ENOENT) {
-               mlx4_err(dev, "Failed to initialize counters table, aborting.\n");
+               mlx4_err(dev, "Failed to initialize counters table, aborting\n");
                goto err_qp_table_free;
        }
 
@@ -1923,9 +1892,8 @@ static int mlx4_setup_hca(struct mlx4_dev *dev)
                        err = mlx4_get_port_ib_caps(dev, port,
                                                    &ib_port_default_caps);
                        if (err)
-                               mlx4_warn(dev, "failed to get port %d default "
-                                         "ib capabilities (%d). Continuing "
-                                         "with caps = 0\n", port, err);
+                               mlx4_warn(dev, "failed to get port %d default ib capabilities (%d). Continuing with caps = 0\n",
+                                         port, err);
                        dev->caps.ib_port_def_cap[port] = ib_port_default_caps;
 
                        /* initialize per-slave default ib port capabilities */
@@ -1935,7 +1903,7 @@ static int mlx4_setup_hca(struct mlx4_dev *dev)
                                        if (i == mlx4_master_func_num(dev))
                                                continue;
                                        priv->mfunc.master.slave_state[i].ib_cap_mask[port] =
-                                                       ib_port_default_caps;
+                                               ib_port_default_caps;
                                }
                        }
 
@@ -1948,7 +1916,7 @@ static int mlx4_setup_hca(struct mlx4_dev *dev)
                                            dev->caps.pkey_table_len[port] : -1);
                        if (err) {
                                mlx4_err(dev, "Failed to set port %d, aborting\n",
-                                       port);
+                                        port);
                                goto err_counters_table_free;
                        }
                }
@@ -2024,7 +1992,7 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev)
                        kfree(entries);
                        goto no_msi;
                } else if (nreq < MSIX_LEGACY_SZ +
-                                 dev->caps.num_ports * MIN_MSIX_P_PORT) {
+                          dev->caps.num_ports * MIN_MSIX_P_PORT) {
                        /*Working in legacy mode , all EQ's shared*/
                        dev->caps.comp_pool           = 0;
                        dev->caps.num_comp_vectors = nreq - 1;
@@ -2225,8 +2193,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
 
        err = pci_enable_device(pdev);
        if (err) {
-               dev_err(&pdev->dev, "Cannot enable PCI device, "
-                       "aborting.\n");
+               dev_err(&pdev->dev, "Cannot enable PCI device, aborting\n");
                return err;
        }
 
@@ -2273,14 +2240,13 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
         */
        if (!(pci_dev_data & MLX4_PCI_DEV_IS_VF) &&
            !(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
-               dev_err(&pdev->dev, "Missing DCS, aborting."
-                       "(driver_data: 0x%x, pci_resource_flags(pdev, 0):0x%lx)\n",
+               dev_err(&pdev->dev, "Missing DCS, aborting (driver_data: 0x%x, pci_resource_flags(pdev, 0):0x%lx)\n",
                        pci_dev_data, pci_resource_flags(pdev, 0));
                err = -ENODEV;
                goto err_disable_pdev;
        }
        if (!(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) {
-               dev_err(&pdev->dev, "Missing UAR, aborting.\n");
+               dev_err(&pdev->dev, "Missing UAR, aborting\n");
                err = -ENODEV;
                goto err_disable_pdev;
        }
@@ -2295,21 +2261,19 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
 
        err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
        if (err) {
-               dev_warn(&pdev->dev, "Warning: couldn't set 64-bit PCI DMA mask.\n");
+               dev_warn(&pdev->dev, "Warning: couldn't set 64-bit PCI DMA mask\n");
                err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
                if (err) {
-                       dev_err(&pdev->dev, "Can't set PCI DMA mask, aborting.\n");
+                       dev_err(&pdev->dev, "Can't set PCI DMA mask, aborting\n");
                        goto err_release_regions;
                }
        }
        err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
        if (err) {
-               dev_warn(&pdev->dev, "Warning: couldn't set 64-bit "
-                        "consistent PCI DMA mask.\n");
+               dev_warn(&pdev->dev, "Warning: couldn't set 64-bit consistent PCI DMA mask\n");
                err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
                if (err) {
-                       dev_err(&pdev->dev, "Can't set consistent PCI DMA mask, "
-                               "aborting.\n");
+                       dev_err(&pdev->dev, "Can't set consistent PCI DMA mask, aborting\n");
                        goto err_release_regions;
                }
        }
@@ -2340,7 +2304,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
                if (total_vfs) {
                        unsigned vfs_offset = 0;
                        for (i = 0; i < sizeof(nvfs)/sizeof(nvfs[0]) &&
-                            vfs_offset + nvfs[i] < extended_func_num(pdev);
+                                    vfs_offset + nvfs[i] < extended_func_num(pdev);
                             vfs_offset += nvfs[i], i++)
                                ;
                        if (i == sizeof(nvfs)/sizeof(nvfs[0])) {
@@ -2366,8 +2330,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
                        if (err < 0)
                                goto err_free_dev;
                        else {
-                               mlx4_warn(dev, "Multiple PFs not yet supported."
-                                         " Skipping PF.\n");
+                               mlx4_warn(dev, "Multiple PFs not yet supported - Skipping PF\n");
                                err = -EINVAL;
                                goto err_free_dev;
                        }
@@ -2377,8 +2340,8 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
                        mlx4_warn(dev, "Enabling SR-IOV with %d VFs\n",
                                  total_vfs);
                        dev->dev_vfs = kzalloc(
-                                       total_vfs * sizeof(*dev->dev_vfs),
-                                       GFP_KERNEL);
+                               total_vfs * sizeof(*dev->dev_vfs),
+                               GFP_KERNEL);
                        if (NULL == dev->dev_vfs) {
                                mlx4_err(dev, "Failed to allocate memory for VFs\n");
                                err = 0;
@@ -2386,14 +2349,14 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
                                atomic_inc(&pf_loading);
                                err = pci_enable_sriov(pdev, total_vfs);
                                if (err) {
-                                       mlx4_err(dev, "Failed to enable SR-IOV, continuing without SR-IOV (err = %d).\n",
+                                       mlx4_err(dev, "Failed to enable SR-IOV, continuing without SR-IOV (err = %d)\n",
                                                 err);
                                        atomic_dec(&pf_loading);
                                        err = 0;
                                } else {
                                        mlx4_warn(dev, "Running in master mode\n");
                                        dev->flags |= MLX4_FLAG_SRIOV |
-                                                     MLX4_FLAG_MASTER;
+                                               MLX4_FLAG_MASTER;
                                        dev->num_vfs = total_vfs;
                                        sriov_initialized = 1;
                                }
@@ -2410,7 +2373,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
                 */
                err = mlx4_reset(dev);
                if (err) {
-                       mlx4_err(dev, "Failed to reset HCA, aborting.\n");
+                       mlx4_err(dev, "Failed to reset HCA, aborting\n");
                        goto err_rel_own;
                }
        }
@@ -2418,7 +2381,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
 slave_start:
        err = mlx4_cmd_init(dev);
        if (err) {
-               mlx4_err(dev, "Failed to init command interface, aborting.\n");
+               mlx4_err(dev, "Failed to init command interface, aborting\n");
                goto err_sriov;
        }
 
@@ -2432,8 +2395,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
                        dev->num_slaves = 0;
                        err = mlx4_multi_func_init(dev);
                        if (err) {
-                               mlx4_err(dev, "Failed to init slave mfunc"
-                                        " interface, aborting.\n");
+                               mlx4_err(dev, "Failed to init slave mfunc interface, aborting\n");
                                goto err_cmd;
                        }
                }
@@ -2465,8 +2427,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
                unsigned sum = 0;
                err = mlx4_multi_func_init(dev);
                if (err) {
-                       mlx4_err(dev, "Failed to init master mfunc"
-                                "interface, aborting.\n");
+                       mlx4_err(dev, "Failed to init master mfunc interface, aborting\n");
                        goto err_close;
                }
                if (sriov_initialized) {
@@ -2477,10 +2438,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
                        if (ib_ports &&
                            (num_vfs_argc > 1 || probe_vfs_argc > 1)) {
                                mlx4_err(dev,
-                                        "Invalid syntax of num_vfs/probe_vfs "
-                                        "with IB port. Single port VFs syntax"
-                                        " is only supported when all ports "
-                                        "are configured as ethernet\n");
+                                        "Invalid syntax of num_vfs/probe_vfs with IB port - single port VFs syntax is only supported when all ports are configured as ethernet\n");
                                goto err_close;
                        }
                        for (i = 0; i < sizeof(nvfs)/sizeof(nvfs[0]); i++) {
@@ -2506,8 +2464,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data)
        if ((mlx4_is_mfunc(dev)) &&
            !(dev->flags & MLX4_FLAG_MSI_X)) {
                err = -ENOSYS;
-               mlx4_err(dev, "INTx is not supported in multi-function mode."
-                        " aborting.\n");
+               mlx4_err(dev, "INTx is not supported in multi-function mode, aborting\n");
                goto err_free_eq;
        }
 
@@ -2660,7 +2617,7 @@ static void __mlx4_remove_one(struct pci_dev *pdev)
        /* in SRIOV it is not allowed to unload the pf's
         * driver while there are alive vf's */
        if (mlx4_is_master(dev) && mlx4_how_many_lives_vf(dev))
-               printk(KERN_ERR "Removing PF when there are assigned VF's !!!\n");
+               pr_warn("Removing PF when there are assigned VF's !!!\n");
        mlx4_stop_sense(dev);
        mlx4_unregister_device(dev);
 
@@ -2824,7 +2781,7 @@ static struct pci_driver mlx4_driver = {
        .name           = DRV_NAME,
        .id_table       = mlx4_pci_table,
        .probe          = mlx4_init_one,
-       .shutdown       = mlx4_remove_one,
+       .shutdown       = __mlx4_remove_one,
        .remove         = mlx4_remove_one,
        .err_handler    = &mlx4_err_handler,
 };
@@ -2832,33 +2789,36 @@ static struct pci_driver mlx4_driver = {
 static int __init mlx4_verify_params(void)
 {
        if ((log_num_mac < 0) || (log_num_mac > 7)) {
-               pr_warning("mlx4_core: bad num_mac: %d\n", log_num_mac);
+               pr_warn("mlx4_core: bad num_mac: %d\n", log_num_mac);
                return -1;
        }
 
        if (log_num_vlan != 0)
-               pr_warning("mlx4_core: log_num_vlan - obsolete module param, using %d\n",
-                          MLX4_LOG_NUM_VLANS);
+               pr_warn("mlx4_core: log_num_vlan - obsolete module param, using %d\n",
+                       MLX4_LOG_NUM_VLANS);
+
+       if (use_prio != 0)
+               pr_warn("mlx4_core: use_prio - obsolete module param, ignored\n");
 
        if ((log_mtts_per_seg < 1) || (log_mtts_per_seg > 7)) {
-               pr_warning("mlx4_core: bad log_mtts_per_seg: %d\n", log_mtts_per_seg);
+               pr_warn("mlx4_core: bad log_mtts_per_seg: %d\n",
+                       log_mtts_per_seg);
                return -1;
        }
 
        /* Check if module param for ports type has legal combination */
        if (port_type_array[0] == false && port_type_array[1] == true) {
-               printk(KERN_WARNING "Module parameter configuration ETH/IB is not supported. Switching to default configuration IB/IB\n");
+               pr_warn("Module parameter configuration ETH/IB is not supported. Switching to default configuration IB/IB\n");
                port_type_array[0] = true;
        }
 
        if (mlx4_log_num_mgm_entry_size != -1 &&
            (mlx4_log_num_mgm_entry_size < MLX4_MIN_MGM_LOG_ENTRY_SIZE ||
             mlx4_log_num_mgm_entry_size > MLX4_MAX_MGM_LOG_ENTRY_SIZE)) {
-               pr_warning("mlx4_core: mlx4_log_num_mgm_entry_size (%d) not "
-                          "in legal range (-1 or %d..%d)\n",
-                          mlx4_log_num_mgm_entry_size,
-                          MLX4_MIN_MGM_LOG_ENTRY_SIZE,
-                          MLX4_MAX_MGM_LOG_ENTRY_SIZE);
+               pr_warn("mlx4_core: mlx4_log_num_mgm_entry_size (%d) not in legal range (-1 or %d..%d)\n",
+                       mlx4_log_num_mgm_entry_size,
+                       MLX4_MIN_MGM_LOG_ENTRY_SIZE,
+                       MLX4_MAX_MGM_LOG_ENTRY_SIZE);
                return -1;
        }
 
index 80ccb4edf825f8888c6487626f2f380c7b27479b..4c36def8e10f9b518a1ba8a3f05340eb63c4dc0a 100644 (file)
@@ -638,7 +638,7 @@ static int find_entry(struct mlx4_dev *dev, u8 port,
 
                if (!(be32_to_cpu(mgm->members_count) & 0xffffff)) {
                        if (*index != hash) {
-                               mlx4_err(dev, "Found zero MGID in AMGM.\n");
+                               mlx4_err(dev, "Found zero MGID in AMGM\n");
                                err = -EINVAL;
                        }
                        return err;
@@ -874,7 +874,7 @@ static void mlx4_err_rule(struct mlx4_dev *dev, char *str,
        mlx4_err(dev, "%s", buf);
 
        if (len >= BUF_SIZE)
-               mlx4_err(dev, "Network rule error message was truncated, print buffer is too small.\n");
+               mlx4_err(dev, "Network rule error message was truncated, print buffer is too small\n");
 }
 
 int mlx4_flow_attach(struct mlx4_dev *dev,
@@ -897,7 +897,7 @@ int mlx4_flow_attach(struct mlx4_dev *dev,
                ret = parse_trans_rule(dev, cur, mailbox->buf + size);
                if (ret < 0) {
                        mlx4_free_cmd_mailbox(dev, mailbox);
-                       return -EINVAL;
+                       return ret;
                }
                size += ret;
        }
@@ -905,10 +905,10 @@ int mlx4_flow_attach(struct mlx4_dev *dev,
        ret = mlx4_QP_FLOW_STEERING_ATTACH(dev, mailbox, size >> 2, reg_id);
        if (ret == -ENOMEM)
                mlx4_err_rule(dev,
-                             "mcg table is full. Fail to register network rule.\n",
+                             "mcg table is full. Fail to register network rule\n",
                              rule);
        else if (ret)
-               mlx4_err_rule(dev, "Fail to register network rule.\n", rule);
+               mlx4_err_rule(dev, "Fail to register network rule\n", rule);
 
        mlx4_free_cmd_mailbox(dev, mailbox);
 
@@ -994,7 +994,7 @@ int mlx4_qp_attach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
 
        members_count = be32_to_cpu(mgm->members_count) & 0xffffff;
        if (members_count == dev->caps.num_qp_per_mgm) {
-               mlx4_err(dev, "MGM at index %x is full.\n", index);
+               mlx4_err(dev, "MGM at index %x is full\n", index);
                err = -ENOMEM;
                goto out;
        }
@@ -1042,7 +1042,7 @@ int mlx4_qp_attach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
        }
        if (err && link && index != -1) {
                if (index < dev->caps.num_mgms)
-                       mlx4_warn(dev, "Got AMGM index %d < %d",
+                       mlx4_warn(dev, "Got AMGM index %d < %d\n",
                                  index, dev->caps.num_mgms);
                else
                        mlx4_bitmap_free(&priv->mcg_table.bitmap,
@@ -1133,7 +1133,7 @@ int mlx4_qp_detach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
 
                if (amgm_index) {
                        if (amgm_index < dev->caps.num_mgms)
-                               mlx4_warn(dev, "MGM entry %d had AMGM index %d < %d",
+                               mlx4_warn(dev, "MGM entry %d had AMGM index %d < %d\n",
                                          index, amgm_index, dev->caps.num_mgms);
                        else
                                mlx4_bitmap_free(&priv->mcg_table.bitmap,
@@ -1153,7 +1153,7 @@ int mlx4_qp_detach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
                        goto out;
 
                if (index < dev->caps.num_mgms)
-                       mlx4_warn(dev, "entry %d had next AMGM index %d < %d",
+                       mlx4_warn(dev, "entry %d had next AMGM index %d < %d\n",
                                  prev, index, dev->caps.num_mgms);
                else
                        mlx4_bitmap_free(&priv->mcg_table.bitmap,
index 7a0665beebb1929ace5b96d61c3a45e993b45824..1d8af7336807b649feb5a5cdb8c4c32a303d31b2 100644 (file)
@@ -221,18 +221,19 @@ extern int mlx4_debug_level;
 #define mlx4_debug_level       (0)
 #endif /* CONFIG_MLX4_DEBUG */
 
-#define mlx4_dbg(mdev, format, arg...)                                 \
+#define mlx4_dbg(mdev, format, ...)                                    \
 do {                                                                   \
        if (mlx4_debug_level)                                           \
-               dev_printk(KERN_DEBUG, &mdev->pdev->dev, format, ##arg); \
+               dev_printk(KERN_DEBUG, &(mdev)->pdev->dev, format,      \
+                          ##__VA_ARGS__);                              \
 } while (0)
 
-#define mlx4_err(mdev, format, arg...) \
-       dev_err(&mdev->pdev->dev, format, ##arg)
-#define mlx4_info(mdev, format, arg...) \
-       dev_info(&mdev->pdev->dev, format, ##arg)
-#define mlx4_warn(mdev, format, arg...) \
-       dev_warn(&mdev->pdev->dev, format, ##arg)
+#define mlx4_err(mdev, format, ...)                                    \
+       dev_err(&(mdev)->pdev->dev, format, ##__VA_ARGS__)
+#define mlx4_info(mdev, format, ...)                                   \
+       dev_info(&(mdev)->pdev->dev, format, ##__VA_ARGS__)
+#define mlx4_warn(mdev, format, ...)                                   \
+       dev_warn(&(mdev)->pdev->dev, format, ##__VA_ARGS__)
 
 extern int mlx4_log_num_mgm_entry_size;
 extern int log_mtts_per_seg;
index 04d9b6fe3e8000fdb14714b09770b11aa46079e8..0e15295bedd671a0c3fc8c1ebbf0372052c489b7 100644 (file)
@@ -313,6 +313,7 @@ struct mlx4_en_rx_ring {
        unsigned long csum_ok;
        unsigned long csum_none;
        int hwtstamp_rx_filter;
+       cpumask_var_t affinity_mask;
 };
 
 struct mlx4_en_cq {
@@ -830,26 +831,26 @@ __printf(3, 4)
 int en_print(const char *level, const struct mlx4_en_priv *priv,
             const char *format, ...);
 
-#define en_dbg(mlevel, priv, format, arg...)                   \
-do {                                                           \
-       if (NETIF_MSG_##mlevel & priv->msg_enable)              \
-               en_print(KERN_DEBUG, priv, format, ##arg);      \
+#define en_dbg(mlevel, priv, format, ...)                              \
+do {                                                                   \
+       if (NETIF_MSG_##mlevel & (priv)->msg_enable)                    \
+               en_print(KERN_DEBUG, priv, format, ##__VA_ARGS__);      \
 } while (0)
-#define en_warn(priv, format, arg...)                  \
-       en_print(KERN_WARNING, priv, format, ##arg)
-#define en_err(priv, format, arg...)                   \
-       en_print(KERN_ERR, priv, format, ##arg)
-#define en_info(priv, format, arg...)                  \
-       en_print(KERN_INFO, priv, format, ## arg)
-
-#define mlx4_err(mdev, format, arg...)                 \
-       pr_err("%s %s: " format, DRV_NAME,              \
-              dev_name(&mdev->pdev->dev), ##arg)
-#define mlx4_info(mdev, format, arg...)                        \
-       pr_info("%s %s: " format, DRV_NAME,             \
-               dev_name(&mdev->pdev->dev), ##arg)
-#define mlx4_warn(mdev, format, arg...)                        \
-       pr_warning("%s %s: " format, DRV_NAME,          \
-                  dev_name(&mdev->pdev->dev), ##arg)
+#define en_warn(priv, format, ...)                                     \
+       en_print(KERN_WARNING, priv, format, ##__VA_ARGS__)
+#define en_err(priv, format, ...)                                      \
+       en_print(KERN_ERR, priv, format, ##__VA_ARGS__)
+#define en_info(priv, format, ...)                                     \
+       en_print(KERN_INFO, priv, format, ##__VA_ARGS__)
+
+#define mlx4_err(mdev, format, ...)                                    \
+       pr_err(DRV_NAME " %s: " format,                                 \
+              dev_name(&(mdev)->pdev->dev), ##__VA_ARGS__)
+#define mlx4_info(mdev, format, ...)                                   \
+       pr_info(DRV_NAME " %s: " format,                                \
+               dev_name(&(mdev)->pdev->dev), ##__VA_ARGS__)
+#define mlx4_warn(mdev, format, ...)                                   \
+       pr_warn(DRV_NAME " %s: " format,                                \
+               dev_name(&(mdev)->pdev->dev), ##__VA_ARGS__)
 
 #endif
index 4c71dafad2175a11d3da7dfa3b554da2e9964e7c..2839abb878a6ae7d9cc0809de20b621e1865cbbe 100644 (file)
@@ -250,8 +250,8 @@ static void mlx4_free_mtt_range(struct mlx4_dev *dev, u32 offset, int order)
                                                       MLX4_CMD_TIME_CLASS_A,
                                                       MLX4_CMD_WRAPPED);
                if (err)
-                       mlx4_warn(dev, "Failed to free mtt range at:"
-                                 "%d order:%d\n", offset, order);
+                       mlx4_warn(dev, "Failed to free mtt range at:%d order:%d\n",
+                                 offset, order);
                return;
        }
         __mlx4_free_mtt_range(dev, offset, order);
@@ -436,8 +436,8 @@ static int mlx4_mr_free_reserved(struct mlx4_dev *dev, struct mlx4_mr *mr)
                                     key_to_hw_index(mr->key) &
                                     (dev->caps.num_mpts - 1));
                if (err) {
-                       mlx4_warn(dev, "HW2SW_MPT failed (%d),", err);
-                       mlx4_warn(dev, "MR has MWs bound to it.\n");
+                       mlx4_warn(dev, "HW2SW_MPT failed (%d), MR has MWs bound to it\n",
+                                 err);
                        return err;
                }
 
@@ -774,7 +774,7 @@ int mlx4_init_mr_table(struct mlx4_dev *dev)
                        mlx4_alloc_mtt_range(dev,
                                             fls(dev->caps.reserved_mtts - 1));
                if (priv->reserved_mtts < 0) {
-                       mlx4_warn(dev, "MTT table of order %u is too small.\n",
+                       mlx4_warn(dev, "MTT table of order %u is too small\n",
                                  mr_table->mtt_buddy.max_order);
                        err = -ENOMEM;
                        goto err_reserve_mtts;
@@ -955,8 +955,7 @@ void mlx4_fmr_unmap(struct mlx4_dev *dev, struct mlx4_fmr *fmr,
        mailbox = mlx4_alloc_cmd_mailbox(dev);
        if (IS_ERR(mailbox)) {
                err = PTR_ERR(mailbox);
-               printk(KERN_WARNING "mlx4_ib: mlx4_alloc_cmd_mailbox"
-                      " failed (%d)\n", err);
+               pr_warn("mlx4_ib: mlx4_alloc_cmd_mailbox failed (%d)\n", err);
                return;
        }
 
@@ -965,8 +964,7 @@ void mlx4_fmr_unmap(struct mlx4_dev *dev, struct mlx4_fmr *fmr,
                             (dev->caps.num_mpts - 1));
        mlx4_free_cmd_mailbox(dev, mailbox);
        if (err) {
-               printk(KERN_WARNING "mlx4_ib: mlx4_HW2SW_MPT failed (%d)\n",
-                      err);
+               pr_warn("mlx4_ib: mlx4_HW2SW_MPT failed (%d)\n", err);
                return;
        }
        fmr->mr.enabled = MLX4_MPT_EN_SW;
index 5ec6f203c6e61d1c8eaa30aa4b387e51a5db1649..7ab97174886d20fd7c6b02d81c58fc3961790469 100644 (file)
@@ -254,8 +254,8 @@ void __mlx4_unregister_mac(struct mlx4_dev *dev, u8 port, u64 mac)
        if (validate_index(dev, table, index))
                goto out;
        if (--table->refs[index]) {
-               mlx4_dbg(dev, "Have more references for index %d,"
-                        "no need to modify mac table\n", index);
+               mlx4_dbg(dev, "Have more references for index %d, no need to modify mac table\n",
+                        index);
                goto out;
        }
 
@@ -453,9 +453,8 @@ void __mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan)
        }
 
        if (--table->refs[index]) {
-               mlx4_dbg(dev, "Have %d more references for index %d,"
-                        "no need to modify vlan table\n", table->refs[index],
-                        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;
@@ -796,8 +795,7 @@ static int mlx4_common_set_port(struct mlx4_dev *dev, int slave, u32 in_mod,
                                        if (!memcmp(gid_entry_mbox->raw, gid_entry_tbl->raw,
                                                    sizeof(gid_entry_tbl->raw))) {
                                                /* found duplicate */
-                                               mlx4_warn(dev, "requested gid entry for slave:%d "
-                                                         "is a duplicate of gid at index %d\n",
+                                               mlx4_warn(dev, "requested gid entry for slave:%d is a duplicate of gid at index %d\n",
                                                          slave, i);
                                                mutex_unlock(&(priv->port[port].gid_table.mutex));
                                                return -EINVAL;
index 8e0c3cc2a1ec786739de7298fc450c483d8fe37a..14089d9e1667fcc4287fe08ca34590e279f66561 100644 (file)
@@ -164,18 +164,17 @@ u64 mlx4_make_profile(struct mlx4_dev *dev,
                }
 
                if (total_size > dev_cap->max_icm_sz) {
-                       mlx4_err(dev, "Profile requires 0x%llx bytes; "
-                                 "won't fit in 0x%llx bytes of context memory.\n",
-                                 (unsigned long long) total_size,
-                                 (unsigned long long) dev_cap->max_icm_sz);
+                       mlx4_err(dev, "Profile requires 0x%llx bytes; won't fit in 0x%llx bytes of context memory\n",
+                                (unsigned long long) total_size,
+                                (unsigned long long) dev_cap->max_icm_sz);
                        kfree(profile);
                        return -ENOMEM;
                }
 
                if (profile[i].size)
-                       mlx4_dbg(dev, "  profile[%2d] (%6s): 2^%02d entries @ 0x%10llx, "
-                                 "size 0x%10llx\n",
-                                i, res_name[profile[i].type], profile[i].log_num,
+                       mlx4_dbg(dev, "  profile[%2d] (%6s): 2^%02d entries @ 0x%10llx, size 0x%10llx\n",
+                                i, res_name[profile[i].type],
+                                profile[i].log_num,
                                 (unsigned long long) profile[i].start,
                                 (unsigned long long) profile[i].size);
        }
index 40af619479254d10e8db34092c53ab223e259bb8..0dc31d85fc3b8ee4e56eb7c2738239fb7fa86f76 100644 (file)
@@ -264,8 +264,8 @@ void mlx4_qp_release_range(struct mlx4_dev *dev, int base_qpn, int cnt)
                               MLX4_CMD_FREE_RES,
                               MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED);
                if (err) {
-                       mlx4_warn(dev, "Failed to release qp range"
-                                 " base:%d cnt:%d\n", base_qpn, cnt);
+                       mlx4_warn(dev, "Failed to release qp range base:%d cnt:%d\n",
+                                 base_qpn, cnt);
                }
        } else
                 __mlx4_qp_release_range(dev, base_qpn, cnt);
@@ -612,8 +612,7 @@ int mlx4_qp_to_ready(struct mlx4_dev *dev, struct mlx4_mtt *mtt,
                err = mlx4_qp_modify(dev, mtt, states[i], states[i + 1],
                                     context, 0, 0, qp);
                if (err) {
-                       mlx4_err(dev, "Failed to bring QP to state: "
-                                "%d with error: %d\n",
+                       mlx4_err(dev, "Failed to bring QP to state: %d with error: %d\n",
                                 states[i + 1], err);
                        return err;
                }
index dd1b5093d8b170812451fb3f67158e1bb8e2b908..ea1c6d092145a5d7e150e8549577edc87f3b0472 100644 (file)
@@ -72,8 +72,7 @@ int mlx4_reset(struct mlx4_dev *dev)
        hca_header = kmalloc(256, GFP_KERNEL);
        if (!hca_header) {
                err = -ENOMEM;
-               mlx4_err(dev, "Couldn't allocate memory to save HCA "
-                         "PCI header, aborting.\n");
+               mlx4_err(dev, "Couldn't allocate memory to save HCA PCI header, aborting\n");
                goto out;
        }
 
@@ -84,8 +83,7 @@ int mlx4_reset(struct mlx4_dev *dev)
                        continue;
                if (pci_read_config_dword(dev->pdev, i * 4, hca_header + i)) {
                        err = -ENODEV;
-                       mlx4_err(dev, "Couldn't save HCA "
-                                 "PCI header, aborting.\n");
+                       mlx4_err(dev, "Couldn't save HCA PCI header, aborting\n");
                        goto out;
                }
        }
@@ -94,7 +92,7 @@ int mlx4_reset(struct mlx4_dev *dev)
                        MLX4_RESET_SIZE);
        if (!reset) {
                err = -ENOMEM;
-               mlx4_err(dev, "Couldn't map HCA reset register, aborting.\n");
+               mlx4_err(dev, "Couldn't map HCA reset register, aborting\n");
                goto out;
        }
 
@@ -133,8 +131,7 @@ int mlx4_reset(struct mlx4_dev *dev)
 
        if (vendor == 0xffff) {
                err = -ENODEV;
-               mlx4_err(dev, "PCI device did not come back after reset, "
-                         "aborting.\n");
+               mlx4_err(dev, "PCI device did not come back after reset, aborting\n");
                goto out;
        }
 
@@ -144,16 +141,14 @@ int mlx4_reset(struct mlx4_dev *dev)
                if (pcie_capability_write_word(dev->pdev, PCI_EXP_DEVCTL,
                                               devctl)) {
                        err = -ENODEV;
-                       mlx4_err(dev, "Couldn't restore HCA PCI Express "
-                                "Device Control register, aborting.\n");
+                       mlx4_err(dev, "Couldn't restore HCA PCI Express Device Control register, aborting\n");
                        goto out;
                }
                linkctl = hca_header[(pcie_cap + PCI_EXP_LNKCTL) / 4];
                if (pcie_capability_write_word(dev->pdev, PCI_EXP_LNKCTL,
                                               linkctl)) {
                        err = -ENODEV;
-                       mlx4_err(dev, "Couldn't restore HCA PCI Express "
-                                "Link control register, aborting.\n");
+                       mlx4_err(dev, "Couldn't restore HCA PCI Express Link control register, aborting\n");
                        goto out;
                }
        }
@@ -164,8 +159,8 @@ int mlx4_reset(struct mlx4_dev *dev)
 
                if (pci_write_config_dword(dev->pdev, i * 4, hca_header[i])) {
                        err = -ENODEV;
-                       mlx4_err(dev, "Couldn't restore HCA reg %x, "
-                                 "aborting.\n", i);
+                       mlx4_err(dev, "Couldn't restore HCA reg %x, aborting\n",
+                                i);
                        goto out;
                }
        }
@@ -173,8 +168,7 @@ int mlx4_reset(struct mlx4_dev *dev)
        if (pci_write_config_dword(dev->pdev, PCI_COMMAND,
                                   hca_header[PCI_COMMAND / 4])) {
                err = -ENODEV;
-               mlx4_err(dev, "Couldn't restore HCA COMMAND, "
-                         "aborting.\n");
+               mlx4_err(dev, "Couldn't restore HCA COMMAND, aborting\n");
                goto out;
        }
 
index 2ba3b7623960109d25c755adbd7af21b940661a7..0efc1368e5a8c4d25b4c95edf049fcae1bec0efb 100644 (file)
@@ -279,7 +279,7 @@ enum qp_transition {
 };
 
 /* For Debug uses */
-static const char *ResourceType(enum mlx4_resource rt)
+static const char *resource_str(enum mlx4_resource rt)
 {
        switch (rt) {
        case RES_QP: return "RES_QP";
@@ -307,6 +307,7 @@ static inline int mlx4_grant_resource(struct mlx4_dev *dev, int slave,
                &priv->mfunc.master.res_tracker.res_alloc[res_type];
        int err = -EINVAL;
        int allocated, free, reserved, guaranteed, from_free;
+       int from_rsvd;
 
        if (slave > dev->num_vfs)
                return -EINVAL;
@@ -321,11 +322,16 @@ static inline int mlx4_grant_resource(struct mlx4_dev *dev, int slave,
                res_alloc->res_reserved;
        guaranteed = res_alloc->guaranteed[slave];
 
-       if (allocated + count > res_alloc->quota[slave])
+       if (allocated + count > res_alloc->quota[slave]) {
+               mlx4_warn(dev, "VF %d port %d res %s: quota exceeded, count %d alloc %d quota %d\n",
+                         slave, port, resource_str(res_type), count,
+                         allocated, res_alloc->quota[slave]);
                goto out;
+       }
 
        if (allocated + count <= guaranteed) {
                err = 0;
+               from_rsvd = count;
        } else {
                /* portion may need to be obtained from free area */
                if (guaranteed - allocated > 0)
@@ -333,8 +339,14 @@ static inline int mlx4_grant_resource(struct mlx4_dev *dev, int slave,
                else
                        from_free = count;
 
-               if (free - from_free > reserved)
+               from_rsvd = count - from_free;
+
+               if (free - from_free >= reserved)
                        err = 0;
+               else
+                       mlx4_warn(dev, "VF %d port %d res %s: free pool empty, free %d from_free %d rsvd %d\n",
+                                 slave, port, resource_str(res_type), free,
+                                 from_free, reserved);
        }
 
        if (!err) {
@@ -342,9 +354,11 @@ static inline int mlx4_grant_resource(struct mlx4_dev *dev, int slave,
                if (port > 0) {
                        res_alloc->allocated[(port - 1) * (dev->num_vfs + 1) + slave] += count;
                        res_alloc->res_port_free[port - 1] -= count;
+                       res_alloc->res_port_rsvd[port - 1] -= from_rsvd;
                } else {
                        res_alloc->allocated[slave] += count;
                        res_alloc->res_free -= count;
+                       res_alloc->res_reserved -= from_rsvd;
                }
        }
 
@@ -360,17 +374,36 @@ static inline void mlx4_release_resource(struct mlx4_dev *dev, int slave,
        struct mlx4_priv *priv = mlx4_priv(dev);
        struct resource_allocator *res_alloc =
                &priv->mfunc.master.res_tracker.res_alloc[res_type];
+       int allocated, guaranteed, from_rsvd;
 
        if (slave > dev->num_vfs)
                return;
 
        spin_lock(&res_alloc->alloc_lock);
+
+       allocated = (port > 0) ?
+               res_alloc->allocated[(port - 1) * (dev->num_vfs + 1) + slave] :
+               res_alloc->allocated[slave];
+       guaranteed = res_alloc->guaranteed[slave];
+
+       if (allocated - count >= guaranteed) {
+               from_rsvd = 0;
+       } else {
+               /* portion may need to be returned to reserved area */
+               if (allocated - guaranteed > 0)
+                       from_rsvd = count - (allocated - guaranteed);
+               else
+                       from_rsvd = count;
+       }
+
        if (port > 0) {
                res_alloc->allocated[(port - 1) * (dev->num_vfs + 1) + slave] -= count;
                res_alloc->res_port_free[port - 1] += count;
+               res_alloc->res_port_rsvd[port - 1] += from_rsvd;
        } else {
                res_alloc->allocated[slave] -= count;
                res_alloc->res_free += count;
+               res_alloc->res_reserved += from_rsvd;
        }
 
        spin_unlock(&res_alloc->alloc_lock);
@@ -963,7 +996,7 @@ static struct res_common *alloc_tr(u64 id, enum mlx4_resource type, int slave,
                ret = alloc_srq_tr(id);
                break;
        case RES_MAC:
-               printk(KERN_ERR "implementation missing\n");
+               pr_err("implementation missing\n");
                return NULL;
        case RES_COUNTER:
                ret = alloc_counter_tr(id);
@@ -1057,10 +1090,10 @@ static int remove_mtt_ok(struct res_mtt *res, int order)
 {
        if (res->com.state == RES_MTT_BUSY ||
            atomic_read(&res->ref_count)) {
-               printk(KERN_DEBUG "%s-%d: state %s, ref_count %d\n",
-                      __func__, __LINE__,
-                      mtt_states_str(res->com.state),
-                      atomic_read(&res->ref_count));
+               pr_devel("%s-%d: state %s, ref_count %d\n",
+                        __func__, __LINE__,
+                        mtt_states_str(res->com.state),
+                        atomic_read(&res->ref_count));
                return -EBUSY;
        } else if (res->com.state != RES_MTT_ALLOCATED)
                return -EPERM;
@@ -3897,7 +3930,7 @@ static int add_eth_header(struct mlx4_dev *dev, int slave,
                }
        }
        if (!be_mac) {
-               pr_err("Failed adding eth header to FS rule, Can't find matching MAC for port %d .\n",
+               pr_err("Failed adding eth header to FS rule, Can't find matching MAC for port %d\n",
                       port);
                return -EINVAL;
        }
@@ -3994,7 +4027,7 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
        qpn = be32_to_cpu(ctrl->qpn) & 0xffffff;
        err = get_res(dev, slave, qpn, RES_QP, &rqp);
        if (err) {
-               pr_err("Steering rule with qpn 0x%x rejected.\n", qpn);
+               pr_err("Steering rule with qpn 0x%x rejected\n", qpn);
                return err;
        }
        rule_header = (struct _rule_hw *)(ctrl + 1);
@@ -4012,7 +4045,7 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
        case MLX4_NET_TRANS_RULE_ID_IPV4:
        case MLX4_NET_TRANS_RULE_ID_TCP:
        case MLX4_NET_TRANS_RULE_ID_UDP:
-               pr_warn("Can't attach FS rule without L2 headers, adding L2 header.\n");
+               pr_warn("Can't attach FS rule without L2 headers, adding L2 header\n");
                if (add_eth_header(dev, slave, inbox, rlist, header_id)) {
                        err = -EINVAL;
                        goto err_put;
@@ -4021,7 +4054,7 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
                        sizeof(struct mlx4_net_trans_rule_hw_eth) >> 2;
                break;
        default:
-               pr_err("Corrupted mailbox.\n");
+               pr_err("Corrupted mailbox\n");
                err = -EINVAL;
                goto err_put;
        }
@@ -4035,7 +4068,7 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
 
        err = add_res_range(dev, slave, vhcr->out_param, 1, RES_FS_RULE, qpn);
        if (err) {
-               mlx4_err(dev, "Fail to add flow steering resources.\n ");
+               mlx4_err(dev, "Fail to add flow steering resources\n");
                /* detach rule*/
                mlx4_cmd(dev, vhcr->out_param, 0, 0,
                         MLX4_QP_FLOW_STEERING_DETACH, MLX4_CMD_TIME_CLASS_A,
@@ -4073,7 +4106,7 @@ int mlx4_QP_FLOW_STEERING_DETACH_wrapper(struct mlx4_dev *dev, int slave,
 
        err = rem_res_range(dev, slave, vhcr->in_param, 1, RES_FS_RULE, 0);
        if (err) {
-               mlx4_err(dev, "Fail to remove flow steering resources.\n ");
+               mlx4_err(dev, "Fail to remove flow steering resources\n");
                goto out;
        }
 
@@ -4151,7 +4184,7 @@ static int _move_all_busy(struct mlx4_dev *dev, int slave,
                                        if (print)
                                                mlx4_dbg(dev,
                                                         "%s id 0x%llx is busy\n",
-                                                         ResourceType(type),
+                                                         resource_str(type),
                                                          r->res_id);
                                        ++busy;
                                } else {
@@ -4202,8 +4235,8 @@ static void rem_slave_qps(struct mlx4_dev *dev, int slave)
 
        err = move_all_busy(dev, slave, RES_QP);
        if (err)
-               mlx4_warn(dev, "rem_slave_qps: Could not move all qps to busy"
-                         "for slave %d\n", slave);
+               mlx4_warn(dev, "rem_slave_qps: Could not move all qps to busy for slave %d\n",
+                         slave);
 
        spin_lock_irq(mlx4_tlock(dev));
        list_for_each_entry_safe(qp, tmp, qp_list, com.list) {
@@ -4241,10 +4274,8 @@ static void rem_slave_qps(struct mlx4_dev *dev, int slave)
                                                       MLX4_CMD_TIME_CLASS_A,
                                                       MLX4_CMD_NATIVE);
                                        if (err)
-                                               mlx4_dbg(dev, "rem_slave_qps: failed"
-                                                        " to move slave %d qpn %d to"
-                                                        " reset\n", slave,
-                                                        qp->local_qpn);
+                                               mlx4_dbg(dev, "rem_slave_qps: failed to move slave %d qpn %d to reset\n",
+                                                        slave, qp->local_qpn);
                                        atomic_dec(&qp->rcq->ref_count);
                                        atomic_dec(&qp->scq->ref_count);
                                        atomic_dec(&qp->mtt->ref_count);
@@ -4278,8 +4309,8 @@ static void rem_slave_srqs(struct mlx4_dev *dev, int slave)
 
        err = move_all_busy(dev, slave, RES_SRQ);
        if (err)
-               mlx4_warn(dev, "rem_slave_srqs: Could not move all srqs to "
-                         "busy for slave %d\n", slave);
+               mlx4_warn(dev, "rem_slave_srqs: Could not move all srqs - too busy for slave %d\n",
+                         slave);
 
        spin_lock_irq(mlx4_tlock(dev));
        list_for_each_entry_safe(srq, tmp, srq_list, com.list) {
@@ -4309,9 +4340,7 @@ static void rem_slave_srqs(struct mlx4_dev *dev, int slave)
                                                       MLX4_CMD_TIME_CLASS_A,
                                                       MLX4_CMD_NATIVE);
                                        if (err)
-                                               mlx4_dbg(dev, "rem_slave_srqs: failed"
-                                                        " to move slave %d srq %d to"
-                                                        " SW ownership\n",
+                                               mlx4_dbg(dev, "rem_slave_srqs: failed to move slave %d srq %d to SW ownership\n",
                                                         slave, srqn);
 
                                        atomic_dec(&srq->mtt->ref_count);
@@ -4346,8 +4375,8 @@ static void rem_slave_cqs(struct mlx4_dev *dev, int slave)
 
        err = move_all_busy(dev, slave, RES_CQ);
        if (err)
-               mlx4_warn(dev, "rem_slave_cqs: Could not move all cqs to "
-                         "busy for slave %d\n", slave);
+               mlx4_warn(dev, "rem_slave_cqs: Could not move all cqs - too busy for slave %d\n",
+                         slave);
 
        spin_lock_irq(mlx4_tlock(dev));
        list_for_each_entry_safe(cq, tmp, cq_list, com.list) {
@@ -4377,9 +4406,7 @@ static void rem_slave_cqs(struct mlx4_dev *dev, int slave)
                                                       MLX4_CMD_TIME_CLASS_A,
                                                       MLX4_CMD_NATIVE);
                                        if (err)
-                                               mlx4_dbg(dev, "rem_slave_cqs: failed"
-                                                        " to move slave %d cq %d to"
-                                                        " SW ownership\n",
+                                               mlx4_dbg(dev, "rem_slave_cqs: failed to move slave %d cq %d to SW ownership\n",
                                                         slave, cqn);
                                        atomic_dec(&cq->mtt->ref_count);
                                        state = RES_CQ_ALLOCATED;
@@ -4411,8 +4438,8 @@ static void rem_slave_mrs(struct mlx4_dev *dev, int slave)
 
        err = move_all_busy(dev, slave, RES_MPT);
        if (err)
-               mlx4_warn(dev, "rem_slave_mrs: Could not move all mpts to "
-                         "busy for slave %d\n", slave);
+               mlx4_warn(dev, "rem_slave_mrs: Could not move all mpts - too busy for slave %d\n",
+                         slave);
 
        spin_lock_irq(mlx4_tlock(dev));
        list_for_each_entry_safe(mpt, tmp, mpt_list, com.list) {
@@ -4447,9 +4474,7 @@ static void rem_slave_mrs(struct mlx4_dev *dev, int slave)
                                                     MLX4_CMD_TIME_CLASS_A,
                                                     MLX4_CMD_NATIVE);
                                        if (err)
-                                               mlx4_dbg(dev, "rem_slave_mrs: failed"
-                                                        " to move slave %d mpt %d to"
-                                                        " SW ownership\n",
+                                               mlx4_dbg(dev, "rem_slave_mrs: failed to move slave %d mpt %d to SW ownership\n",
                                                         slave, mptn);
                                        if (mpt->mtt)
                                                atomic_dec(&mpt->mtt->ref_count);
@@ -4481,8 +4506,8 @@ static void rem_slave_mtts(struct mlx4_dev *dev, int slave)
 
        err = move_all_busy(dev, slave, RES_MTT);
        if (err)
-               mlx4_warn(dev, "rem_slave_mtts: Could not move all mtts to "
-                         "busy for slave %d\n", slave);
+               mlx4_warn(dev, "rem_slave_mtts: Could not move all mtts  - too busy for slave %d\n",
+                         slave);
 
        spin_lock_irq(mlx4_tlock(dev));
        list_for_each_entry_safe(mtt, tmp, mtt_list, com.list) {
@@ -4584,8 +4609,8 @@ static void rem_slave_eqs(struct mlx4_dev *dev, int slave)
 
        err = move_all_busy(dev, slave, RES_EQ);
        if (err)
-               mlx4_warn(dev, "rem_slave_eqs: Could not move all eqs to "
-                         "busy for slave %d\n", slave);
+               mlx4_warn(dev, "rem_slave_eqs: Could not move all eqs - too busy for slave %d\n",
+                         slave);
 
        spin_lock_irq(mlx4_tlock(dev));
        list_for_each_entry_safe(eq, tmp, eq_list, com.list) {
@@ -4617,9 +4642,8 @@ static void rem_slave_eqs(struct mlx4_dev *dev, int slave)
                                                           MLX4_CMD_TIME_CLASS_A,
                                                           MLX4_CMD_NATIVE);
                                        if (err)
-                                               mlx4_dbg(dev, "rem_slave_eqs: failed"
-                                                        " to move slave %d eqs %d to"
-                                                        " SW ownership\n", slave, eqn);
+                                               mlx4_dbg(dev, "rem_slave_eqs: failed to move slave %d eqs %d to SW ownership\n",
+                                                        slave, eqn);
                                        mlx4_free_cmd_mailbox(dev, mailbox);
                                        atomic_dec(&eq->mtt->ref_count);
                                        state = RES_EQ_RESERVED;
@@ -4648,8 +4672,8 @@ static void rem_slave_counters(struct mlx4_dev *dev, int slave)
 
        err = move_all_busy(dev, slave, RES_COUNTER);
        if (err)
-               mlx4_warn(dev, "rem_slave_counters: Could not move all counters to "
-                         "busy for slave %d\n", slave);
+               mlx4_warn(dev, "rem_slave_counters: Could not move all counters - too busy for slave %d\n",
+                         slave);
 
        spin_lock_irq(mlx4_tlock(dev));
        list_for_each_entry_safe(counter, tmp, counter_list, com.list) {
@@ -4679,8 +4703,8 @@ static void rem_slave_xrcdns(struct mlx4_dev *dev, int slave)
 
        err = move_all_busy(dev, slave, RES_XRCD);
        if (err)
-               mlx4_warn(dev, "rem_slave_xrcdns: Could not move all xrcdns to "
-                         "busy for slave %d\n", slave);
+               mlx4_warn(dev, "rem_slave_xrcdns: Could not move all xrcdns - too busy for slave %d\n",
+                         slave);
 
        spin_lock_irq(mlx4_tlock(dev));
        list_for_each_entry_safe(xrcd, tmp, xrcdn_list, com.list) {
@@ -4825,10 +4849,8 @@ void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work)
                                       0, MLX4_CMD_UPDATE_QP,
                                       MLX4_CMD_TIME_CLASS_C, MLX4_CMD_NATIVE);
                        if (err) {
-                               mlx4_info(dev, "UPDATE_QP failed for slave %d, "
-                                         "port %d, qpn %d (%d)\n",
-                                         work->slave, port, qp->local_qpn,
-                                         err);
+                               mlx4_info(dev, "UPDATE_QP failed for slave %d, port %d, qpn %d (%d)\n",
+                                         work->slave, port, qp->local_qpn, err);
                                errors++;
                        }
                }
index 405c4fbcd0ad1cb56453938012ecb9035de20f23..87d1b018a9c394309a6ee78310640690146bf245 100644 (file)
@@ -620,8 +620,8 @@ static int wait_func(struct mlx5_core_dev *dev, struct mlx5_cmd_work_ent *ent)
                               mlx5_command_str(msg_to_opcode(ent->in)),
                               msg_to_opcode(ent->in));
        }
-       mlx5_core_dbg(dev, "err %d, delivery status %s(%d)\n", err,
-                     deliv_status_to_str(ent->status), ent->status);
+       mlx5_core_dbg(dev, "err %d, delivery status %s(%d)\n",
+                     err, deliv_status_to_str(ent->status), ent->status);
 
        return err;
 }
index 64a61b286b2c959fbb67c72dcc098199d74ff68d..7f39ebcd6ad01b3dc175ffd57b3239f9f7154a8a 100644 (file)
@@ -208,7 +208,8 @@ static int mlx5_eq_int(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
                 */
                rmb();
 
-               mlx5_core_dbg(eq->dev, "eqn %d, eqe type %s\n", eq->eqn, eqe_type_str(eqe->type));
+               mlx5_core_dbg(eq->dev, "eqn %d, eqe type %s\n",
+                             eq->eqn, eqe_type_str(eqe->type));
                switch (eqe->type) {
                case MLX5_EVENT_TYPE_COMP:
                        cqn = be32_to_cpu(eqe->data.comp.cqn) & 0xffffff;
@@ -270,14 +271,16 @@ static int mlx5_eq_int(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
                                u16 func_id = be16_to_cpu(eqe->data.req_pages.func_id);
                                s32 npages = be32_to_cpu(eqe->data.req_pages.num_pages);
 
-                               mlx5_core_dbg(dev, "page request for func 0x%x, napges %d\n", func_id, npages);
+                               mlx5_core_dbg(dev, "page request for func 0x%x, npages %d\n",
+                                             func_id, npages);
                                mlx5_core_req_pages_handler(dev, func_id, npages);
                        }
                        break;
 
 
                default:
-                       mlx5_core_warn(dev, "Unhandled event 0x%x on EQ 0x%x\n", eqe->type, eq->eqn);
+                       mlx5_core_warn(dev, "Unhandled event 0x%x on EQ 0x%x\n",
+                                      eqe->type, eq->eqn);
                        break;
                }
 
index c3eee5f70051e0855abf623ad0b1b10614d38b82..ee24f132e319988daaae5f870466bfc3599e3783 100644 (file)
@@ -66,10 +66,10 @@ static int set_dma_caps(struct pci_dev *pdev)
 
        err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
        if (err) {
-               dev_warn(&pdev->dev, "Warning: couldn't set 64-bit PCI DMA mask.\n");
+               dev_warn(&pdev->dev, "Warning: couldn't set 64-bit PCI DMA mask\n");
                err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
                if (err) {
-                       dev_err(&pdev->dev, "Can't set PCI DMA mask, aborting.\n");
+                       dev_err(&pdev->dev, "Can't set PCI DMA mask, aborting\n");
                        return err;
                }
        }
@@ -77,11 +77,11 @@ static int set_dma_caps(struct pci_dev *pdev)
        err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
        if (err) {
                dev_warn(&pdev->dev,
-                        "Warning: couldn't set 64-bit consistent PCI DMA mask.\n");
+                        "Warning: couldn't set 64-bit consistent PCI DMA mask\n");
                err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
                if (err) {
                        dev_err(&pdev->dev,
-                               "Can't set consistent PCI DMA mask, aborting.\n");
+                               "Can't set consistent PCI DMA mask, aborting\n");
                        return err;
                }
        }
@@ -95,7 +95,7 @@ static int request_bar(struct pci_dev *pdev)
        int err = 0;
 
        if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
-               dev_err(&pdev->dev, "Missing registers BAR, aborting.\n");
+               dev_err(&pdev->dev, "Missing registers BAR, aborting\n");
                return -ENODEV;
        }
 
@@ -319,13 +319,13 @@ int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev)
 
        err = pci_enable_device(pdev);
        if (err) {
-               dev_err(&pdev->dev, "Cannot enable PCI device, aborting.\n");
+               dev_err(&pdev->dev, "Cannot enable PCI device, aborting\n");
                goto err_dbg;
        }
 
        err = request_bar(pdev);
        if (err) {
-               dev_err(&pdev->dev, "error requesting BARs, aborting.\n");
+               dev_err(&pdev->dev, "error requesting BARs, aborting\n");
                goto err_disable;
        }
 
index 68b74e1ae1b016c9b7b9ce5866a466dd131dccac..f0c9f9a7a36142f1a7fded7a88120e1cff213aaa 100644 (file)
 
 extern int mlx5_core_debug_mask;
 
-#define mlx5_core_dbg(dev, format, arg...)                                    \
-pr_debug("%s:%s:%d:(pid %d): " format, (dev)->priv.name, __func__, __LINE__,   \
-        current->pid, ##arg)
+#define mlx5_core_dbg(dev, format, ...)                                        \
+       pr_debug("%s:%s:%d:(pid %d): " format,                          \
+                (dev)->priv.name, __func__, __LINE__, current->pid,    \
+                ##__VA_ARGS__)
 
-#define mlx5_core_dbg_mask(dev, mask, format, arg...)                         \
-do {                                                                          \
-       if ((mask) & mlx5_core_debug_mask)                                     \
-               pr_debug("%s:%s:%d:(pid %d): " format, (dev)->priv.name,       \
-                        __func__, __LINE__, current->pid, ##arg);             \
+#define mlx5_core_dbg_mask(dev, mask, format, ...)                     \
+do {                                                                   \
+       if ((mask) & mlx5_core_debug_mask)                              \
+               mlx5_core_dbg(dev, format, ##__VA_ARGS__);              \
 } while (0)
 
-#define mlx5_core_err(dev, format, arg...) \
-pr_err("%s:%s:%d:(pid %d): " format, (dev)->priv.name, __func__, __LINE__,     \
-       current->pid, ##arg)
+#define mlx5_core_err(dev, format, ...)                                        \
+       pr_err("%s:%s:%d:(pid %d): " format,                            \
+              (dev)->priv.name, __func__, __LINE__, current->pid,      \
+              ##__VA_ARGS__)
 
-#define mlx5_core_warn(dev, format, arg...) \
-pr_warn("%s:%s:%d:(pid %d): " format, (dev)->priv.name, __func__, __LINE__,    \
-       current->pid, ##arg)
+#define mlx5_core_warn(dev, format, ...)                               \
+       pr_warn("%s:%s:%d:(pid %d): " format,                           \
+               (dev)->priv.name, __func__, __LINE__, current->pid,     \
+               ##__VA_ARGS__)
 
 enum {
        MLX5_CMD_DATA, /* print command payload only */
index ac52a0fe2d3af12ccda01d858a4f78d9eb7e2343..ba0401d4af502bc5ad83568b067b96dd5436243e 100644 (file)
@@ -73,7 +73,7 @@ int mlx5_core_create_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr,
        }
 
        if (err) {
-               mlx5_core_dbg(dev, "cmd exec faile %d\n", err);
+               mlx5_core_dbg(dev, "cmd exec failed %d\n", err);
                return err;
        }
 
@@ -195,7 +195,8 @@ int mlx5_core_create_psv(struct mlx5_core_dev *dev, u32 pdn,
        }
 
        if (out.hdr.status) {
-               mlx5_core_err(dev, "create_psv bad status %d\n", out.hdr.status);
+               mlx5_core_err(dev, "create_psv bad status %d\n",
+                             out.hdr.status);
                return mlx5_cmd_status_to_err(&out.hdr);
        }
 
@@ -224,7 +225,8 @@ int mlx5_core_destroy_psv(struct mlx5_core_dev *dev, int psv_num)
        }
 
        if (out.hdr.status) {
-               mlx5_core_err(dev, "destroy_psv bad status %d\n", out.hdr.status);
+               mlx5_core_err(dev, "destroy_psv bad status %d\n",
+                             out.hdr.status);
                err = mlx5_cmd_status_to_err(&out.hdr);
                goto out;
        }
index d59790a82bc3d5c9f3def1e4a664a6531b93ad7d..c2a953ef0e675801827ac9bec57e1aaf399dbcca 100644 (file)
@@ -311,7 +311,8 @@ static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages,
        in->num_entries = cpu_to_be32(npages);
        err = mlx5_cmd_exec(dev, in, inlen, &out, sizeof(out));
        if (err) {
-               mlx5_core_warn(dev, "func_id 0x%x, npages %d, err %d\n", func_id, npages, err);
+               mlx5_core_warn(dev, "func_id 0x%x, npages %d, err %d\n",
+                              func_id, npages, err);
                goto out_alloc;
        }
        dev->priv.fw_pages += npages;
@@ -319,7 +320,8 @@ static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages,
        if (out.hdr.status) {
                err = mlx5_cmd_status_to_err(&out.hdr);
                if (err) {
-                       mlx5_core_warn(dev, "func_id 0x%x, npages %d, status %d\n", func_id, npages, out.hdr.status);
+                       mlx5_core_warn(dev, "func_id 0x%x, npages %d, status %d\n",
+                                      func_id, npages, out.hdr.status);
                        goto out_alloc;
                }
        }
@@ -378,7 +380,7 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages,
        mlx5_core_dbg(dev, "npages %d, outlen %d\n", npages, outlen);
        err = mlx5_cmd_exec(dev, &in, sizeof(in), out, outlen);
        if (err) {
-               mlx5_core_err(dev, "failed recliaming pages\n");
+               mlx5_core_err(dev, "failed reclaiming pages\n");
                goto out_free;
        }
        dev->priv.fw_pages -= npages;
@@ -414,8 +416,8 @@ static void pages_work_handler(struct work_struct *work)
                err = give_pages(dev, req->func_id, req->npages, 1);
 
        if (err)
-               mlx5_core_warn(dev, "%s fail %d\n", req->npages < 0 ?
-                              "reclaim" : "give", err);
+               mlx5_core_warn(dev, "%s fail %d\n",
+                              req->npages < 0 ? "reclaim" : "give", err);
 
        kfree(req);
 }
@@ -487,7 +489,8 @@ int mlx5_reclaim_startup_pages(struct mlx5_core_dev *dev)
                                            optimal_reclaimed_pages(),
                                            &nclaimed);
                        if (err) {
-                               mlx5_core_warn(dev, "failed reclaiming pages (%d)\n", err);
+                               mlx5_core_warn(dev, "failed reclaiming pages (%d)\n",
+                                              err);
                                return err;
                        }
                        if (nclaimed)
index 510576213dd0c8e823a6feecb87d427f275ca90e..8145b4668229d6a483e9b36fdc457387a6c6ab41 100644 (file)
@@ -79,7 +79,7 @@ int mlx5_core_create_qp(struct mlx5_core_dev *dev,
 
        err = mlx5_cmd_exec(dev, in, inlen, &out, sizeof(out));
        if (err) {
-               mlx5_core_warn(dev, "ret %d", err);
+               mlx5_core_warn(dev, "ret %d\n", err);
                return err;
        }
 
@@ -96,7 +96,7 @@ int mlx5_core_create_qp(struct mlx5_core_dev *dev,
        err = radix_tree_insert(&table->tree, qp->qpn, qp);
        spin_unlock_irq(&table->lock);
        if (err) {
-               mlx5_core_warn(dev, "err %d", err);
+               mlx5_core_warn(dev, "err %d\n", err);
                goto err_cmd;
        }
 
index 16435b3cfa9f133a3fe937dc96670853873e7485..6c7c78baedcaf590f6f6c78f5155a1df2fa594e9 100644 (file)
@@ -1504,15 +1504,15 @@ ks8695_probe(struct platform_device *pdev)
        if (ksp->phyiface_regs && ksp->link_irq == -1) {
                ks8695_init_switch(ksp);
                ksp->dtype = KS8695_DTYPE_LAN;
-               SET_ETHTOOL_OPS(ndev, &ks8695_ethtool_ops);
+               ndev->ethtool_ops = &ks8695_ethtool_ops;
        } else if (ksp->phyiface_regs && ksp->link_irq != -1) {
                ks8695_init_wan_phy(ksp);
                ksp->dtype = KS8695_DTYPE_WAN;
-               SET_ETHTOOL_OPS(ndev, &ks8695_wan_ethtool_ops);
+               ndev->ethtool_ops = &ks8695_wan_ethtool_ops;
        } else {
                /* No initialisation since HPNA does not have a PHY */
                ksp->dtype = KS8695_DTYPE_HPNA;
-               SET_ETHTOOL_OPS(ndev, &ks8695_ethtool_ops);
+               ndev->ethtool_ops = &ks8695_ethtool_ops;
        }
 
        /* And bring up the net_device with the net core */
index e0c92e0e5e1d463f0242088d184394608b243cb6..66d4ab703f45a20b7949e1bd3e448c9dc182e68f 100644 (file)
@@ -26,6 +26,8 @@
 #include <linux/regulator/consumer.h>
 
 #include <linux/spi/spi.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
 
 #include "ks8851.h"
 
@@ -85,6 +87,8 @@ union ks8851_tx_hdr {
  * @eeprom_size: Companion eeprom size in Bytes, 0 if no eeprom
  * @eeprom: 93CX6 EEPROM state for accessing on-board EEPROM.
  * @vdd_reg:   Optional regulator supplying the chip
+ * @vdd_io: Optional digital power supply for IO
+ * @gpio: Optional reset_n gpio
  *
  * The @lock ensures that the chip is protected when certain operations are
  * in progress. When the read or write packet transfer is in progress, most
@@ -133,6 +137,8 @@ struct ks8851_net {
 
        struct eeprom_93cx6     eeprom;
        struct regulator        *vdd_reg;
+       struct regulator        *vdd_io;
+       int                     gpio;
 };
 
 static int msg_enable;
@@ -1404,6 +1410,7 @@ static int ks8851_probe(struct spi_device *spi)
        struct ks8851_net *ks;
        int ret;
        unsigned cider;
+       int gpio;
 
        ndev = alloc_etherdev(sizeof(struct ks8851_net));
        if (!ndev)
@@ -1417,20 +1424,53 @@ static int ks8851_probe(struct spi_device *spi)
        ks->spidev = spi;
        ks->tx_space = 6144;
 
-       ks->vdd_reg = regulator_get_optional(&spi->dev, "vdd");
-       if (IS_ERR(ks->vdd_reg)) {
-               ret = PTR_ERR(ks->vdd_reg);
-               if (ret == -EPROBE_DEFER)
-                       goto err_reg;
-       } else {
-               ret = regulator_enable(ks->vdd_reg);
+       gpio = of_get_named_gpio_flags(spi->dev.of_node, "reset-gpios",
+                                      0, NULL);
+       if (gpio == -EPROBE_DEFER) {
+               ret = gpio;
+               goto err_gpio;
+       }
+
+       ks->gpio = gpio;
+       if (gpio_is_valid(gpio)) {
+               ret = devm_gpio_request_one(&spi->dev, gpio,
+                                           GPIOF_OUT_INIT_LOW, "ks8851_rst_n");
                if (ret) {
-                       dev_err(&spi->dev, "regulator enable fail: %d\n",
-                               ret);
-                       goto err_reg_en;
+                       dev_err(&spi->dev, "reset gpio request failed\n");
+                       goto err_gpio;
                }
        }
 
+       ks->vdd_io = devm_regulator_get(&spi->dev, "vdd-io");
+       if (IS_ERR(ks->vdd_io)) {
+               ret = PTR_ERR(ks->vdd_io);
+               goto err_reg_io;
+       }
+
+       ret = regulator_enable(ks->vdd_io);
+       if (ret) {
+               dev_err(&spi->dev, "regulator vdd_io enable fail: %d\n",
+                       ret);
+               goto err_reg_io;
+       }
+
+       ks->vdd_reg = devm_regulator_get(&spi->dev, "vdd");
+       if (IS_ERR(ks->vdd_reg)) {
+               ret = PTR_ERR(ks->vdd_reg);
+               goto err_reg;
+       }
+
+       ret = regulator_enable(ks->vdd_reg);
+       if (ret) {
+               dev_err(&spi->dev, "regulator vdd enable fail: %d\n",
+                       ret);
+               goto err_reg;
+       }
+
+       if (gpio_is_valid(gpio)) {
+               usleep_range(10000, 11000);
+               gpio_set_value(gpio, 1);
+       }
 
        mutex_init(&ks->lock);
        spin_lock_init(&ks->statelock);
@@ -1471,7 +1511,7 @@ static int ks8851_probe(struct spi_device *spi)
 
        skb_queue_head_init(&ks->txq);
 
-       SET_ETHTOOL_OPS(ndev, &ks8851_ethtool_ops);
+       ndev->ethtool_ops = &ks8851_ethtool_ops;
        SET_NETDEV_DEV(ndev, &spi->dev);
 
        spi_set_drvdata(spi, ks);
@@ -1527,13 +1567,14 @@ static int ks8851_probe(struct spi_device *spi)
        free_irq(ndev->irq, ks);
 
 err_irq:
+       if (gpio_is_valid(gpio))
+               gpio_set_value(gpio, 0);
 err_id:
-       if (!IS_ERR(ks->vdd_reg))
-               regulator_disable(ks->vdd_reg);
-err_reg_en:
-       if (!IS_ERR(ks->vdd_reg))
-               regulator_put(ks->vdd_reg);
+       regulator_disable(ks->vdd_reg);
 err_reg:
+       regulator_disable(ks->vdd_io);
+err_reg_io:
+err_gpio:
        free_netdev(ndev);
        return ret;
 }
@@ -1547,18 +1588,24 @@ static int ks8851_remove(struct spi_device *spi)
 
        unregister_netdev(priv->netdev);
        free_irq(spi->irq, priv);
-       if (!IS_ERR(priv->vdd_reg)) {
-               regulator_disable(priv->vdd_reg);
-               regulator_put(priv->vdd_reg);
-       }
+       if (gpio_is_valid(priv->gpio))
+               gpio_set_value(priv->gpio, 0);
+       regulator_disable(priv->vdd_reg);
+       regulator_disable(priv->vdd_io);
        free_netdev(priv->netdev);
 
        return 0;
 }
 
+static const struct of_device_id ks8851_match_table[] = {
+       { .compatible = "micrel,ks8851" },
+       { }
+};
+
 static struct spi_driver ks8851_driver = {
        .driver = {
                .name = "ks8851",
+               .of_match_table = ks8851_match_table,
                .owner = THIS_MODULE,
                .pm = &ks8851_pm_ops,
        },
index 14ac0e2bc09fcbc50f65ceead949ecd7d15d6130..064a48d0c368a267826e2f77bacb2da9fa366e1e 100644 (file)
@@ -4930,7 +4930,7 @@ static void netdev_tx_timeout(struct net_device *dev)
                 * Only reset the hardware if time between calls is long
                 * enough.
                 */
-               if (jiffies - last_reset <= dev->watchdog_timeo)
+               if (time_before_eq(jiffies, last_reset + dev->watchdog_timeo))
                        hw_priv = NULL;
        }
 
@@ -7072,6 +7072,7 @@ static int pcidev_init(struct pci_dev *pdev, const struct pci_device_id *id)
                dev = alloc_etherdev(sizeof(struct dev_priv));
                if (!dev)
                        goto pcidev_init_reg_err;
+               SET_NETDEV_DEV(dev, &pdev->dev);
                info->netdev[i] = dev;
 
                priv = netdev_priv(dev);
@@ -7106,7 +7107,7 @@ static int pcidev_init(struct pci_dev *pdev, const struct pci_device_id *id)
                }
 
                dev->netdev_ops = &netdev_ops;
-               SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
+               dev->ethtool_ops = &netdev_ethtool_ops;
                if (register_netdev(dev))
                        goto pcidev_init_reg_err;
                port_set_power_saving(port, true);
index c7b40aa21f22fe2a909754fe4e2a9a7b8bad5898..b1b5f66b8b6910ad2dc38c2157d8b12aba1668c1 100644 (file)
@@ -1593,7 +1593,7 @@ static int enc28j60_probe(struct spi_device *spi)
        dev->irq = spi->irq;
        dev->netdev_ops = &enc28j60_netdev_ops;
        dev->watchdog_timeo = TX_TIMEOUT;
-       SET_ETHTOOL_OPS(dev, &enc28j60_ethtool_ops);
+       dev->ethtool_ops = &enc28j60_ethtool_ops;
 
        enc28j60_lowpower(priv, true);
 
index 130f6b204efa29cb9c97c98b4e3b0f52b569cd35..f3d5d79f1cd15de8dff66fa4aeab6fccaa25ab8e 100644 (file)
@@ -4112,7 +4112,7 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        setup_timer(&mgp->watchdog_timer, myri10ge_watchdog_timer,
                    (unsigned long)mgp);
 
-       SET_ETHTOOL_OPS(netdev, &myri10ge_ethtool_ops);
+       netdev->ethtool_ops = &myri10ge_ethtool_ops;
        INIT_WORK(&mgp->watchdog_work, myri10ge_watchdog);
        status = register_netdev(netdev);
        if (status != 0) {
index 64ec2a437f46a3280e9377c55d8226a19f82d089..291fba8b9f07351effff8ebbc9b229fc40c0e885 100644 (file)
@@ -927,7 +927,7 @@ static int natsemi_probe1(struct pci_dev *pdev, const struct pci_device_id *ent)
        dev->netdev_ops = &natsemi_netdev_ops;
        dev->watchdog_timeo = TX_TIMEOUT;
 
-       SET_ETHTOOL_OPS(dev, &ethtool_ops);
+       dev->ethtool_ops = &ethtool_ops;
 
        if (mtu)
                dev->mtu = mtu;
index dbccf1de49ecbf7011a167585caefdeb082fa4de..19bb8244b9e3e1056a2835bf2c2434f1e6ae65a3 100644 (file)
@@ -2030,7 +2030,7 @@ static int ns83820_init_one(struct pci_dev *pci_dev,
                pci_dev->subsystem_vendor, pci_dev->subsystem_device);
 
        ndev->netdev_ops = &netdev_ops;
-       SET_ETHTOOL_OPS(ndev, &ops);
+       ndev->ethtool_ops = &ops;
        ndev->watchdog_timeo = 5 * HZ;
        pci_set_drvdata(pci_dev, ndev);
 
index a2844ff322c4c62bed8957a7f3797ad321359cbf..be587647c70657a93f86608ba6e06e731e7aecb6 100644 (file)
@@ -534,15 +534,6 @@ static inline void s2io_start_all_tx_queue(struct s2io_nic *sp)
        netif_tx_start_all_queues(sp->dev);
 }
 
-static inline void s2io_start_tx_queue(struct s2io_nic *sp, int fifo_no)
-{
-       if (!sp->config.multiq)
-               sp->mac_control.fifos[fifo_no].queue_state =
-                       FIFO_QUEUE_START;
-
-       netif_tx_start_all_queues(sp->dev);
-}
-
 static inline void s2io_wake_all_tx_queue(struct s2io_nic *sp)
 {
        if (!sp->config.multiq) {
@@ -5369,8 +5360,8 @@ static int s2io_ethtool_gset(struct net_device *dev, struct ethtool_cmd *info)
                ethtool_cmd_speed_set(info, SPEED_10000);
                info->duplex = DUPLEX_FULL;
        } else {
-               ethtool_cmd_speed_set(info, -1);
-               info->duplex = -1;
+               ethtool_cmd_speed_set(info, SPEED_UNKNOWN);
+               info->duplex = DUPLEX_UNKNOWN;
        }
 
        info->autoneg = AUTONEG_DISABLE;
@@ -7919,7 +7910,7 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre)
 
        /*  Driver entry points */
        dev->netdev_ops = &s2io_netdev_ops;
-       SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
+       dev->ethtool_ops = &netdev_ethtool_ops;
        dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM |
                NETIF_F_TSO | NETIF_F_TSO6 |
                NETIF_F_RXCSUM | NETIF_F_LRO;
index 089b713b9f7be772c5453c26885b29fc17663dcb..2bbd01fcb9b019eaeabc1519a98799e65ea1b51a 100644 (file)
@@ -120,7 +120,6 @@ __vxge_hw_device_register_poll(void __iomem *reg, u64 mask, u32 max_millis)
 {
        u64 val64;
        u32 i = 0;
-       enum vxge_hw_status ret = VXGE_HW_FAIL;
 
        udelay(10);
 
@@ -139,7 +138,7 @@ __vxge_hw_device_register_poll(void __iomem *reg, u64 mask, u32 max_millis)
                mdelay(1);
        } while (++i <= max_millis);
 
-       return ret;
+       return VXGE_HW_FAIL;
 }
 
 static inline enum vxge_hw_status
@@ -1682,12 +1681,10 @@ enum vxge_hw_status vxge_hw_driver_stats_get(
                        struct __vxge_hw_device *hldev,
                        struct vxge_hw_device_stats_sw_info *sw_stats)
 {
-       enum vxge_hw_status status = VXGE_HW_OK;
-
        memcpy(sw_stats, &hldev->stats.sw_dev_info_stats,
                sizeof(struct vxge_hw_device_stats_sw_info));
 
-       return status;
+       return VXGE_HW_OK;
 }
 
 /*
@@ -3228,7 +3225,6 @@ enum vxge_hw_status
 vxge_hw_vpath_strip_fcs_check(struct __vxge_hw_device *hldev, u64 vpath_mask)
 {
        struct vxge_hw_vpmgmt_reg       __iomem *vpmgmt_reg;
-       enum vxge_hw_status status = VXGE_HW_OK;
        int i = 0, j = 0;
 
        for (i = 0; i < VXGE_HW_MAX_VIRTUAL_PATHS; i++) {
@@ -3241,7 +3237,7 @@ vxge_hw_vpath_strip_fcs_check(struct __vxge_hw_device *hldev, u64 vpath_mask)
                                return VXGE_HW_FAIL;
                }
        }
-       return status;
+       return VXGE_HW_OK;
 }
 /*
  * vxge_hw_mgmt_reg_Write - Write Titan register.
@@ -3979,7 +3975,6 @@ __vxge_hw_vpath_mgmt_read(
 {
        u32 i, mtu = 0, max_pyld = 0;
        u64 val64;
-       enum vxge_hw_status status = VXGE_HW_OK;
 
        for (i = 0; i < VXGE_HW_MAC_MAX_MAC_PORT_ID; i++) {
 
@@ -4009,7 +4004,7 @@ __vxge_hw_vpath_mgmt_read(
        else
                VXGE_HW_DEVICE_LINK_STATE_SET(vpath->hldev, VXGE_HW_LINK_DOWN);
 
-       return status;
+       return VXGE_HW_OK;
 }
 
 /*
@@ -4039,14 +4034,13 @@ static enum vxge_hw_status
 __vxge_hw_vpath_reset(struct __vxge_hw_device *hldev, u32 vp_id)
 {
        u64 val64;
-       enum vxge_hw_status status = VXGE_HW_OK;
 
        val64 = VXGE_HW_CMN_RSTHDLR_CFG0_SW_RESET_VPATH(1 << (16 - vp_id));
 
        __vxge_hw_pio_mem_write32_upper((u32)vxge_bVALn(val64, 0, 32),
                                &hldev->common_reg->cmn_rsthdlr_cfg0);
 
-       return status;
+       return VXGE_HW_OK;
 }
 
 /*
@@ -4227,7 +4221,6 @@ static enum vxge_hw_status
 __vxge_hw_vpath_mac_configure(struct __vxge_hw_device *hldev, u32 vp_id)
 {
        u64 val64;
-       enum vxge_hw_status status = VXGE_HW_OK;
        struct __vxge_hw_virtualpath *vpath;
        struct vxge_hw_vp_config *vp_config;
        struct vxge_hw_vpath_reg __iomem *vp_reg;
@@ -4283,7 +4276,7 @@ __vxge_hw_vpath_mac_configure(struct __vxge_hw_device *hldev, u32 vp_id)
 
                writeq(val64, &vp_reg->rxmac_vcfg1);
        }
-       return status;
+       return VXGE_HW_OK;
 }
 
 /*
@@ -4295,7 +4288,6 @@ static enum vxge_hw_status
 __vxge_hw_vpath_tim_configure(struct __vxge_hw_device *hldev, u32 vp_id)
 {
        u64 val64;
-       enum vxge_hw_status status = VXGE_HW_OK;
        struct __vxge_hw_virtualpath *vpath;
        struct vxge_hw_vpath_reg __iomem *vp_reg;
        struct vxge_hw_vp_config *config;
@@ -4545,7 +4537,7 @@ __vxge_hw_vpath_tim_configure(struct __vxge_hw_device *hldev, u32 vp_id)
        val64 |= VXGE_HW_TIM_WRKLD_CLC_CNT_RX_TX(3);
        writeq(val64, &vp_reg->tim_wrkld_clc);
 
-       return status;
+       return VXGE_HW_OK;
 }
 
 /*
index f8f073880f84bccd5f0daedb8c1f08233c43e6c8..b07d552a27d4d486079b329b238235411a103e4e 100644 (file)
@@ -62,8 +62,8 @@ static int vxge_ethtool_gset(struct net_device *dev, struct ethtool_cmd *info)
                ethtool_cmd_speed_set(info, SPEED_10000);
                info->duplex = DUPLEX_FULL;
        } else {
-               ethtool_cmd_speed_set(info, -1);
-               info->duplex = -1;
+               ethtool_cmd_speed_set(info, SPEED_UNKNOWN);
+               info->duplex = DUPLEX_UNKNOWN;
        }
 
        info->autoneg = AUTONEG_DISABLE;
@@ -1128,5 +1128,5 @@ static const struct ethtool_ops vxge_ethtool_ops = {
 
 void vxge_initialize_ethtool_ops(struct net_device *ndev)
 {
-       SET_ETHTOOL_OPS(ndev, &vxge_ethtool_ops);
+       ndev->ethtool_ops = &vxge_ethtool_ops;
 }
index d107bcbb8543035110a98a82a21a7e72c8ec1303..7a0deadd53bf14743e4c530b895c8658e59c8be2 100644 (file)
@@ -2122,7 +2122,7 @@ static int vxge_open_vpaths(struct vxgedev *vdev)
 static void adaptive_coalesce_tx_interrupts(struct vxge_fifo *fifo)
 {
        fifo->interrupt_count++;
-       if (jiffies > fifo->jiffies + HZ / 100) {
+       if (time_before(fifo->jiffies + HZ / 100, jiffies)) {
                struct __vxge_hw_fifo *hw_fifo = fifo->handle;
 
                fifo->jiffies = jiffies;
@@ -2150,7 +2150,7 @@ static void adaptive_coalesce_tx_interrupts(struct vxge_fifo *fifo)
 static void adaptive_coalesce_rx_interrupts(struct vxge_ring *ring)
 {
        ring->interrupt_count++;
-       if (jiffies > ring->jiffies + HZ / 100) {
+       if (time_before(ring->jiffies + HZ / 100, jiffies)) {
                struct __vxge_hw_ring *hw_ring = ring->handle;
 
                ring->jiffies = jiffies;
index fddb464aeab3a517c362d12ad4891eb3e2529cae..9afc536c5734ec264f0d084b88602444cb1b24bd 100644 (file)
@@ -406,7 +406,7 @@ union ring_type {
 
 #define NV_RX_DESCRIPTORVALID  (1<<16)
 #define NV_RX_MISSEDFRAME      (1<<17)
-#define NV_RX_SUBSTRACT1       (1<<18)
+#define NV_RX_SUBTRACT1                (1<<18)
 #define NV_RX_ERROR1           (1<<23)
 #define NV_RX_ERROR2           (1<<24)
 #define NV_RX_ERROR3           (1<<25)
@@ -423,7 +423,7 @@ union ring_type {
 #define NV_RX2_CHECKSUM_IP_TCP (0x14000000)
 #define NV_RX2_CHECKSUM_IP_UDP (0x18000000)
 #define NV_RX2_DESCRIPTORVALID (1<<29)
-#define NV_RX2_SUBSTRACT1      (1<<25)
+#define NV_RX2_SUBTRACT1       (1<<25)
 #define NV_RX2_ERROR1          (1<<18)
 #define NV_RX2_ERROR2          (1<<19)
 #define NV_RX2_ERROR3          (1<<20)
@@ -2832,7 +2832,7 @@ static int nv_rx_process(struct net_device *dev, int limit)
                                        }
                                        /* framing errors are soft errors */
                                        else if ((flags & NV_RX_ERROR_MASK) == NV_RX_FRAMINGERR) {
-                                               if (flags & NV_RX_SUBSTRACT1)
+                                               if (flags & NV_RX_SUBTRACT1)
                                                        len--;
                                        }
                                        /* the rest are hard errors */
@@ -2863,7 +2863,7 @@ static int nv_rx_process(struct net_device *dev, int limit)
                                        }
                                        /* framing errors are soft errors */
                                        else if ((flags & NV_RX2_ERROR_MASK) == NV_RX2_FRAMINGERR) {
-                                               if (flags & NV_RX2_SUBSTRACT1)
+                                               if (flags & NV_RX2_SUBTRACT1)
                                                        len--;
                                        }
                                        /* the rest are hard errors */
@@ -2937,7 +2937,7 @@ static int nv_rx_process_optimized(struct net_device *dev, int limit)
                                }
                                /* framing errors are soft errors */
                                else if ((flags & NV_RX2_ERROR_MASK) == NV_RX2_FRAMINGERR) {
-                                       if (flags & NV_RX2_SUBSTRACT1)
+                                       if (flags & NV_RX2_SUBTRACT1)
                                                len--;
                                }
                                /* the rest are hard errors */
@@ -4285,8 +4285,8 @@ static int nv_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
                if (np->duplex)
                        ecmd->duplex = DUPLEX_FULL;
        } else {
-               speed = -1;
-               ecmd->duplex = -1;
+               speed = SPEED_UNKNOWN;
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
        ethtool_cmd_speed_set(ecmd, speed);
        ecmd->autoneg = np->autoneg;
@@ -5766,7 +5766,7 @@ static int nv_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
                dev->netdev_ops = &nv_netdev_ops_optimized;
 
        netif_napi_add(dev, &np->napi, nv_napi_poll, RX_WORK_PER_LOOP);
-       SET_ETHTOOL_OPS(dev, &ops);
+       dev->ethtool_ops = &ops;
        dev->watchdog_timeo = NV_WATCHDOG_TIMEO;
 
        pci_set_drvdata(pci_dev, dev);
index 422d9b51ac2408da844669b1c2b0f1364e8e093a..8706c0dbd0c36a2c3b7af167eaa4c29cda3905a5 100644 (file)
@@ -1361,7 +1361,7 @@ static int lpc_eth_drv_probe(struct platform_device *pdev)
        __lpc_eth_clock_enable(pldat, true);
 
        /* Map IO space */
-       pldat->net_base = ioremap(res->start, res->end - res->start + 1);
+       pldat->net_base = ioremap(res->start, resource_size(res));
        if (!pldat->net_base) {
                dev_err(&pdev->dev, "failed to map registers\n");
                ret = -ENOMEM;
@@ -1417,10 +1417,8 @@ static int lpc_eth_drv_probe(struct platform_device *pdev)
        }
        pldat->dma_buff_base_p = dma_handle;
 
-       netdev_dbg(ndev, "IO address start     :0x%08x\n",
-                       res->start);
-       netdev_dbg(ndev, "IO address size      :%d\n",
-                       res->end - res->start + 1);
+       netdev_dbg(ndev, "IO address space     :%pR\n", res);
+       netdev_dbg(ndev, "IO address size      :%d\n", resource_size(res));
        netdev_dbg(ndev, "IO address (mapped)  :0x%p\n",
                        pldat->net_base);
        netdev_dbg(ndev, "IRQ number           :%d\n", ndev->irq);
index a588ffde970041def37cae92b215011d88b6eea6..44c8be1c68051ec9a9b79f4ba481060022613e79 100644 (file)
@@ -4,7 +4,7 @@
 
 config PCH_GBE
        tristate "OKI SEMICONDUCTOR IOH(ML7223/ML7831) GbE"
-       depends on PCI && (X86 || COMPILE_TEST)
+       depends on PCI && (X86_32 || COMPILE_TEST)
        select MII
        select PTP_1588_CLOCK_PCH
        ---help---
index 826f0ccdc23c818139d3951c953b56a3b885d6e8..4fe8ea96bd25d24f1f2296d22230ceebda1e8f64 100644 (file)
@@ -91,7 +91,7 @@ static int pch_gbe_get_settings(struct net_device *netdev,
        ecmd->advertising &= ~(ADVERTISED_TP | ADVERTISED_1000baseT_Half);
 
        if (!netif_carrier_ok(adapter->netdev))
-               ethtool_cmd_speed_set(ecmd, -1);
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
        return ret;
 }
 
@@ -508,5 +508,5 @@ static const struct ethtool_ops pch_gbe_ethtool_ops = {
 
 void pch_gbe_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &pch_gbe_ethtool_ops);
+       netdev->ethtool_ops = &pch_gbe_ethtool_ops;
 }
index b6bdeb3c19711ac960646dfdbeef2207b86f366d..9a997e4c3e084a16368ebcae6ed53d42a0c0524c 100644 (file)
@@ -724,10 +724,8 @@ static int hamachi_init_one(struct pci_dev *pdev,
 
        /* The Hamachi-specific entries in the device structure. */
        dev->netdev_ops = &hamachi_netdev_ops;
-       if (chip_tbl[hmp->chip_id].flags & CanHaveMII)
-               SET_ETHTOOL_OPS(dev, &ethtool_ops);
-       else
-               SET_ETHTOOL_OPS(dev, &ethtool_ops_no_mii);
+       dev->ethtool_ops = (chip_tbl[hmp->chip_id].flags & CanHaveMII) ?
+               &ethtool_ops : &ethtool_ops_no_mii;
        dev->watchdog_timeo = TX_TIMEOUT;
        if (mtu)
                dev->mtu = mtu;
index 9a6cb482dcd0b8bf5e26e3e2c784f57b032f5236..69a8dc0950720b7f57a83483d1cf86f0f4f9f35d 100644 (file)
@@ -472,7 +472,7 @@ static int yellowfin_init_one(struct pci_dev *pdev,
 
        /* The Yellowfin-specific entries in the device structure. */
        dev->netdev_ops = &netdev_ops;
-       SET_ETHTOOL_OPS(dev, &ethtool_ops);
+       dev->ethtool_ops = &ethtool_ops;
        dev->watchdog_timeo = TX_TIMEOUT;
 
        if (mtu)
index c14bd3116e454edad88d27f7ab8923cb999ec885..d49cba1290814ecc175d925fca6ae2dd9227cded 100644 (file)
@@ -66,6 +66,17 @@ config QLCNIC_VXLAN
          Say Y here if you want to enable hardware offload support for
          Virtual eXtensible Local Area Network (VXLAN) in the driver.
 
+config QLCNIC_HWMON
+       bool "QLOGIC QLCNIC 82XX and 83XX family HWMON support"
+       depends on QLCNIC && HWMON && !(QLCNIC=y && HWMON=m)
+       default y
+       ---help---
+         This configuration parameter can be used to read the
+         board temperature in Converged Ethernet devices
+         supported by qlcnic.
+
+         This data is available via the hwmon sysfs interface.
+
 config QLGE
        tristate "QLogic QLGE 10Gb Ethernet Driver Support"
        depends on PCI
index f09c35d669b3ec7d8898f96f0ffd9ce7362ddb13..5bf05818a12cfa707d138c420908546daaa502a5 100644 (file)
@@ -1373,7 +1373,7 @@ netxen_setup_netdev(struct netxen_adapter *adapter,
 
        netxen_nic_change_mtu(netdev, netdev->mtu);
 
-       SET_ETHTOOL_OPS(netdev, &netxen_nic_ethtool_ops);
+       netdev->ethtool_ops = &netxen_nic_ethtool_ops;
 
        netdev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
                              NETIF_F_RXCSUM;
index 2eabd44f8914de91ac4eaebbed17d4a08c66c96f..b5d6bc1a8b0024770c919e6ccf1f49a0cc6da1a8 100644 (file)
@@ -3838,7 +3838,7 @@ static int ql3xxx_probe(struct pci_dev *pdev,
 
        /* Set driver entry points */
        ndev->netdev_ops = &ql3xxx_netdev_ops;
-       SET_ETHTOOL_OPS(ndev, &ql3xxx_ethtool_ops);
+       ndev->ethtool_ops = &ql3xxx_ethtool_ops;
        ndev->watchdog_timeo = 5 * HZ;
 
        netif_napi_add(ndev, &qdev->napi, ql_poll, 64);
index f785d01c7d123dde875a774ebf1027800af33425..be618b9e874f6a12c16d5876fc7b5302b73b4a4f 100644 (file)
@@ -39,8 +39,8 @@
 
 #define _QLCNIC_LINUX_MAJOR 5
 #define _QLCNIC_LINUX_MINOR 3
-#define _QLCNIC_LINUX_SUBVERSION 57
-#define QLCNIC_LINUX_VERSIONID  "5.3.57"
+#define _QLCNIC_LINUX_SUBVERSION 60
+#define QLCNIC_LINUX_VERSIONID  "5.3.60"
 #define QLCNIC_DRV_IDC_VER  0x01
 #define QLCNIC_DRIVER_VERSION  ((_QLCNIC_LINUX_MAJOR << 16) |\
                 (_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION))
@@ -441,6 +441,8 @@ struct qlcnic_82xx_dump_template_hdr {
        u32     rsvd1[0];
 };
 
+#define QLC_PEX_DMA_READ_SIZE  (PAGE_SIZE * 16)
+
 struct qlcnic_fw_dump {
        u8      clr;    /* flag to indicate if dump is cleared */
        bool    enable; /* enable/disable dump */
@@ -537,6 +539,7 @@ struct qlcnic_hardware_context {
        u8 phys_port_id[ETH_ALEN];
        u8 lb_mode;
        u16 vxlan_port;
+       struct device *hwmon_dev;
 };
 
 struct qlcnic_adapter_stats {
@@ -1018,6 +1021,8 @@ struct qlcnic_ipaddr {
 #define QLCNIC_DEL_VXLAN_PORT          0x200000
 #endif
 
+#define QLCNIC_VLAN_FILTERING          0x800000
+
 #define QLCNIC_IS_MSI_FAMILY(adapter) \
        ((adapter)->flags & (QLCNIC_MSI_ENABLED | QLCNIC_MSIX_ENABLED))
 #define QLCNIC_IS_TSO_CAPABLE(adapter)  \
@@ -1316,6 +1321,7 @@ struct qlcnic_eswitch {
 #define QL_STATUS_INVALID_PARAM        -1
 
 #define MAX_BW                 100     /* % of link speed */
+#define MIN_BW                 1       /* % of link speed */
 #define MAX_VLAN_ID            4095
 #define MIN_VLAN_ID            2
 #define DEFAULT_MAC_LEARN      1
@@ -1692,7 +1698,7 @@ int qlcnic_read_mac_addr(struct qlcnic_adapter *);
 int qlcnic_setup_netdev(struct qlcnic_adapter *, struct net_device *, int);
 void qlcnic_set_netdev_features(struct qlcnic_adapter *,
                                struct qlcnic_esw_func_cfg *);
-void qlcnic_sriov_vf_schedule_multi(struct net_device *);
+void qlcnic_sriov_vf_set_multi(struct net_device *);
 int qlcnic_is_valid_nic_func(struct qlcnic_adapter *, u8);
 int qlcnic_get_pci_func_type(struct qlcnic_adapter *, u16, u16 *, u16 *,
                             u16 *);
@@ -2338,6 +2344,16 @@ static inline bool qlcnic_83xx_vf_check(struct qlcnic_adapter *adapter)
        return (device == PCI_DEVICE_ID_QLOGIC_VF_QLE834X) ? true : false;
 }
 
+static inline bool qlcnic_sriov_check(struct qlcnic_adapter *adapter)
+{
+       bool status;
+
+       status = (qlcnic_sriov_pf_check(adapter) ||
+                 qlcnic_sriov_vf_check(adapter)) ? true : false;
+
+       return status;
+}
+
 static inline u32 qlcnic_get_vnic_func_count(struct qlcnic_adapter *adapter)
 {
        if (qlcnic_84xx_check(adapter))
@@ -2345,4 +2361,18 @@ static inline u32 qlcnic_get_vnic_func_count(struct qlcnic_adapter *adapter)
        else
                return QLC_DEFAULT_VNIC_COUNT;
 }
+
+#ifdef CONFIG_QLCNIC_HWMON
+void qlcnic_register_hwmon_dev(struct qlcnic_adapter *);
+void qlcnic_unregister_hwmon_dev(struct qlcnic_adapter *);
+#else
+static inline void qlcnic_register_hwmon_dev(struct qlcnic_adapter *adapter)
+{
+       return;
+}
+static inline void qlcnic_unregister_hwmon_dev(struct qlcnic_adapter *adapter)
+{
+       return;
+}
+#endif
 #endif                         /* __QLCNIC_H_ */
index b7cffb46a75dbd8f215752218a6f1f4239c1cc98..a4a4ec0b68f8d5e9d7b0c6f3ed5050b5787a37c4 100644 (file)
@@ -33,6 +33,7 @@ static void qlcnic_83xx_get_beacon_state(struct qlcnic_adapter *);
 #define RSS_HASHTYPE_IP_TCP            0x3
 #define QLC_83XX_FW_MBX_CMD            0
 #define QLC_SKIP_INACTIVE_PCI_REGS     7
+#define QLC_MAX_LEGACY_FUNC_SUPP       8
 
 static const struct qlcnic_mailbox_metadata qlcnic_83xx_mbx_tbl[] = {
        {QLCNIC_CMD_CONFIGURE_IP_ADDR, 6, 1},
@@ -357,8 +358,15 @@ int qlcnic_83xx_setup_intr(struct qlcnic_adapter *adapter)
        if (!ahw->intr_tbl)
                return -ENOMEM;
 
-       if (!(adapter->flags & QLCNIC_MSIX_ENABLED))
+       if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) {
+               if (adapter->ahw->pci_func >= QLC_MAX_LEGACY_FUNC_SUPP) {
+                       dev_err(&adapter->pdev->dev, "PCI function number 8 and higher are not supported with legacy interrupt, func 0x%x\n",
+                               ahw->pci_func);
+                       return -EOPNOTSUPP;
+               }
+
                qlcnic_83xx_enable_legacy(adapter);
+       }
 
        for (i = 0; i < num_msix; i++) {
                if (adapter->flags & QLCNIC_MSIX_ENABLED)
@@ -879,6 +887,9 @@ int qlcnic_83xx_alloc_mbx_args(struct qlcnic_cmd_args *mbx,
                        return 0;
                }
        }
+
+       dev_err(&adapter->pdev->dev, "%s: Invalid mailbox command opcode 0x%x\n",
+               __func__, type);
        return -EINVAL;
 }
 
@@ -3026,19 +3037,18 @@ void qlcnic_83xx_unlock_driver(struct qlcnic_adapter *adapter)
        QLCRDX(adapter->ahw, QLC_83XX_DRV_UNLOCK);
 }
 
-int qlcnic_83xx_ms_mem_write128(struct qlcnic_adapter *adapter, u64 addr,
+int qlcnic_ms_mem_write128(struct qlcnic_adapter *adapter, u64 addr,
                                u32 *data, u32 count)
 {
        int i, j, ret = 0;
        u32 temp;
-       int err = 0;
 
        /* Check alignment */
        if (addr & 0xF)
                return -EIO;
 
        mutex_lock(&adapter->ahw->mem_lock);
-       qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_ADDR_HI, 0);
+       qlcnic_ind_wr(adapter, QLCNIC_MS_ADDR_HI, 0);
 
        for (i = 0; i < count; i++, addr += 16) {
                if (!((ADDR_IN_RANGE(addr, QLCNIC_ADDR_QDR_NET,
@@ -3049,26 +3059,16 @@ int qlcnic_83xx_ms_mem_write128(struct qlcnic_adapter *adapter, u64 addr,
                        return -EIO;
                }
 
-               qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_ADDR_LO, addr);
-               qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_WRTDATA_LO,
-                                            *data++);
-               qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_WRTDATA_HI,
-                                            *data++);
-               qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_WRTDATA_ULO,
-                                            *data++);
-               qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_WRTDATA_UHI,
-                                            *data++);
-               qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_CTRL,
-                                            QLCNIC_TA_WRITE_ENABLE);
-               qlcnic_83xx_wrt_reg_indirect(adapter, QLCNIC_MS_CTRL,
-                                            QLCNIC_TA_WRITE_START);
+               qlcnic_ind_wr(adapter, QLCNIC_MS_ADDR_LO, addr);
+               qlcnic_ind_wr(adapter, QLCNIC_MS_WRTDATA_LO, *data++);
+               qlcnic_ind_wr(adapter, QLCNIC_MS_WRTDATA_HI, *data++);
+               qlcnic_ind_wr(adapter, QLCNIC_MS_WRTDATA_ULO, *data++);
+               qlcnic_ind_wr(adapter, QLCNIC_MS_WRTDATA_UHI, *data++);
+               qlcnic_ind_wr(adapter, QLCNIC_MS_CTRL, QLCNIC_TA_WRITE_ENABLE);
+               qlcnic_ind_wr(adapter, QLCNIC_MS_CTRL, QLCNIC_TA_WRITE_START);
 
                for (j = 0; j < MAX_CTL_CHECK; j++) {
-                       temp = QLCRD32(adapter, QLCNIC_MS_CTRL, &err);
-                       if (err == -EIO) {
-                               mutex_unlock(&adapter->ahw->mem_lock);
-                               return err;
-                       }
+                       temp = qlcnic_ind_rd(adapter, QLCNIC_MS_CTRL);
 
                        if ((temp & TA_CTL_BUSY) == 0)
                                break;
index 88d809c356334675026fb1a71e37107ded60c709..2bf101a47d02db1afbd153e032aab728f2ecb159 100644 (file)
@@ -418,7 +418,6 @@ enum qlcnic_83xx_states {
 #define QLC_83XX_GET_FUNC_MODE_FROM_NPAR_INFO(val)     (val & 0x80000000)
 #define QLC_83XX_GET_LRO_CAPABILITY(val)               (val & 0x20)
 #define QLC_83XX_GET_LSO_CAPABILITY(val)               (val & 0x40)
-#define QLC_83XX_GET_LSO_CAPABILITY(val)               (val & 0x40)
 #define QLC_83XX_GET_HW_LRO_CAPABILITY(val)            (val & 0x400)
 #define QLC_83XX_GET_VLAN_ALIGN_CAPABILITY(val)        (val & 0x4000)
 #define QLC_83XX_GET_FW_LRO_MSS_CAPABILITY(val)        (val & 0x20000)
@@ -560,7 +559,7 @@ void qlcnic_83xx_napi_del(struct qlcnic_adapter *);
 void qlcnic_83xx_napi_enable(struct qlcnic_adapter *);
 void qlcnic_83xx_napi_disable(struct qlcnic_adapter *);
 int qlcnic_83xx_config_led(struct qlcnic_adapter *, u32, u32);
-void qlcnic_ind_wr(struct qlcnic_adapter *, u32, u32);
+int qlcnic_ind_wr(struct qlcnic_adapter *, u32, u32);
 int qlcnic_ind_rd(struct qlcnic_adapter *, u32);
 int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *);
 int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *,
@@ -617,7 +616,6 @@ void qlcnic_83xx_idc_request_reset(struct qlcnic_adapter *, u32);
 int qlcnic_83xx_lock_driver(struct qlcnic_adapter *);
 void qlcnic_83xx_unlock_driver(struct qlcnic_adapter *);
 int qlcnic_83xx_set_default_offload_settings(struct qlcnic_adapter *);
-int qlcnic_83xx_ms_mem_write128(struct qlcnic_adapter *, u64, u32 *, u32);
 int qlcnic_83xx_idc_vnic_pf_entry(struct qlcnic_adapter *);
 int qlcnic_83xx_disable_vnic_mode(struct qlcnic_adapter *, int);
 int qlcnic_83xx_config_vnic_opmode(struct qlcnic_adapter *);
@@ -659,4 +657,5 @@ void qlcnic_83xx_cache_tmpl_hdr_values(struct qlcnic_fw_dump *);
 u32 qlcnic_83xx_get_cap_size(void *, int);
 void qlcnic_83xx_set_sys_info(void *, int, u32);
 void qlcnic_83xx_store_cap_mask(void *, u32);
+int qlcnic_ms_mem_write128(struct qlcnic_adapter *, u64, u32 *, u32);
 #endif
index ba20c721ee97f59d05f18a126471cb4a4a277f0b..f33559b725283cf69b08e80a8179fb488b89acb2 100644 (file)
@@ -1363,8 +1363,8 @@ static int qlcnic_83xx_copy_bootloader(struct qlcnic_adapter *adapter)
                return ret;
        }
        /* 16 byte write to MS memory */
-       ret = qlcnic_83xx_ms_mem_write128(adapter, dest, (u32 *)p_cache,
-                                         size / 16);
+       ret = qlcnic_ms_mem_write128(adapter, dest, (u32 *)p_cache,
+                                    size / 16);
        if (ret) {
                vfree(p_cache);
                return ret;
@@ -1389,8 +1389,8 @@ static int qlcnic_83xx_copy_fw_file(struct qlcnic_adapter *adapter)
        p_cache = (u32 *)fw->data;
        addr = (u64)dest;
 
-       ret = qlcnic_83xx_ms_mem_write128(adapter, addr,
-                                         p_cache, size / 16);
+       ret = qlcnic_ms_mem_write128(adapter, addr,
+                                    p_cache, size / 16);
        if (ret) {
                dev_err(&adapter->pdev->dev, "MS memory write failed\n");
                release_firmware(fw);
@@ -1405,8 +1405,8 @@ static int qlcnic_83xx_copy_fw_file(struct qlcnic_adapter *adapter)
                        data[i] = fw->data[size + i];
                for (; i < 16; i++)
                        data[i] = 0;
-               ret = qlcnic_83xx_ms_mem_write128(adapter, addr,
-                                                 (u32 *)data, 1);
+               ret = qlcnic_ms_mem_write128(adapter, addr,
+                                            (u32 *)data, 1);
                if (ret) {
                        dev_err(&adapter->pdev->dev,
                                "MS memory write failed\n");
@@ -2181,6 +2181,8 @@ int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter)
                max_sds_rings = QLCNIC_MAX_SDS_RINGS;
                max_tx_rings = QLCNIC_MAX_TX_RINGS;
        } else {
+               dev_err(&adapter->pdev->dev, "%s: Invalid opmode %d\n",
+                       __func__, ret);
                return -EIO;
        }
 
index c1e11f5715b056c0e90ba096de8f397eb50fce97..304e247bdf339c59b30c816839da1bb0ccb9a6ac 100644 (file)
@@ -1027,8 +1027,11 @@ int qlcnic_config_port_mirroring(struct qlcnic_adapter *adapter, u8 id,
        u32 arg1;
 
        if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC ||
-           !(adapter->eswitch[id].flags & QLCNIC_SWITCH_ENABLE))
+           !(adapter->eswitch[id].flags & QLCNIC_SWITCH_ENABLE)) {
+               dev_err(&adapter->pdev->dev, "%s: Not a management function\n",
+                       __func__);
                return err;
+       }
 
        arg1 = id | (enable_mirroring ? BIT_4 : 0);
        arg1 |= pci_func << 8;
@@ -1318,8 +1321,12 @@ int qlcnic_config_switch_port(struct qlcnic_adapter *adapter,
        u32 arg1, arg2 = 0;
        u8 pci_func;
 
-       if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC)
+       if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC) {
+               dev_err(&adapter->pdev->dev, "%s: Not a management function\n",
+                       __func__);
                return err;
+       }
+
        pci_func = esw_cfg->pci_func;
        index = qlcnic_is_valid_nic_func(adapter, pci_func);
        if (index < 0)
@@ -1363,6 +1370,8 @@ int qlcnic_config_switch_port(struct qlcnic_adapter *adapter,
                        arg1 &= ~(0x0ffff << 16);
                        break;
        default:
+               dev_err(&adapter->pdev->dev, "%s: Invalid opmode 0x%x\n",
+                       __func__, esw_cfg->op_mode);
                return err;
        }
 
index 5bacf5210aed658abc12c639be38f0649bf96cbf..1b7f3dbae2899d9ade666176a7cfdb0e21a82d6f 100644 (file)
@@ -726,6 +726,11 @@ static int qlcnic_set_channels(struct net_device *dev,
        struct qlcnic_adapter *adapter = netdev_priv(dev);
        int err;
 
+       if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) {
+               netdev_err(dev, "No RSS/TSS support in non MSI-X mode\n");
+               return -EINVAL;
+       }
+
        if (channel->other_count || channel->combined_count)
                return -EINVAL;
 
@@ -734,7 +739,7 @@ static int qlcnic_set_channels(struct net_device *dev,
        if (err)
                return err;
 
-       if (channel->rx_count) {
+       if (adapter->drv_sds_rings != channel->rx_count) {
                err = qlcnic_validate_rings(adapter, channel->rx_count,
                                            QLCNIC_RX_QUEUE);
                if (err) {
@@ -745,7 +750,7 @@ static int qlcnic_set_channels(struct net_device *dev,
                adapter->drv_rss_rings = channel->rx_count;
        }
 
-       if (channel->tx_count) {
+       if (adapter->drv_tx_rings != channel->tx_count) {
                err = qlcnic_validate_rings(adapter, channel->tx_count,
                                            QLCNIC_TX_QUEUE);
                if (err) {
index 9f3adf4e70b5f31a2d143c8d946ce6d88201a096..851cb4a80d50a6d4b2733ac2693fd799cca37885 100644 (file)
@@ -373,12 +373,16 @@ int qlcnic_ind_rd(struct qlcnic_adapter *adapter, u32 addr)
        return data;
 }
 
-void qlcnic_ind_wr(struct qlcnic_adapter *adapter, u32 addr, u32 data)
+int qlcnic_ind_wr(struct qlcnic_adapter *adapter, u32 addr, u32 data)
 {
+       int ret = 0;
+
        if (qlcnic_82xx_check(adapter))
                qlcnic_write_window_reg(addr, adapter->ahw->pci_base0, data);
        else
-               qlcnic_83xx_wrt_reg_indirect(adapter, addr, data);
+               ret = qlcnic_83xx_wrt_reg_indirect(adapter, addr, data);
+
+       return ret;
 }
 
 static int
@@ -567,28 +571,14 @@ static void __qlcnic_set_multi(struct net_device *netdev, u16 vlan)
 void qlcnic_set_multi(struct net_device *netdev)
 {
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
-       struct qlcnic_mac_vlan_list *cur;
-       struct netdev_hw_addr *ha;
-       size_t temp;
 
        if (!test_bit(__QLCNIC_FW_ATTACHED, &adapter->state))
                return;
-       if (qlcnic_sriov_vf_check(adapter)) {
-               if (!netdev_mc_empty(netdev)) {
-                       netdev_for_each_mc_addr(ha, netdev) {
-                               temp = sizeof(struct qlcnic_mac_vlan_list);
-                               cur = kzalloc(temp, GFP_ATOMIC);
-                               if (cur == NULL)
-                                       break;
-                               memcpy(cur->mac_addr,
-                                      ha->addr, ETH_ALEN);
-                               list_add_tail(&cur->list, &adapter->vf_mc_list);
-                       }
-               }
-               qlcnic_sriov_vf_schedule_multi(adapter->netdev);
-               return;
-       }
-       __qlcnic_set_multi(netdev, 0);
+
+       if (qlcnic_sriov_vf_check(adapter))
+               qlcnic_sriov_vf_set_multi(netdev);
+       else
+               __qlcnic_set_multi(netdev, 0);
 }
 
 int qlcnic_82xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32 mode)
@@ -630,7 +620,7 @@ void qlcnic_prune_lb_filters(struct qlcnic_adapter *adapter)
        struct hlist_node *n;
        struct hlist_head *head;
        int i;
-       unsigned long time;
+       unsigned long expires;
        u8 cmd;
 
        for (i = 0; i < adapter->fhash.fbucket_size; i++) {
@@ -638,8 +628,8 @@ void qlcnic_prune_lb_filters(struct qlcnic_adapter *adapter)
                hlist_for_each_entry_safe(tmp_fil, n, head, fnode) {
                        cmd =  tmp_fil->vlan_id ? QLCNIC_MAC_VLAN_DEL :
                                                  QLCNIC_MAC_DEL;
-                       time = tmp_fil->ftime;
-                       if (jiffies > (QLCNIC_FILTER_AGE * HZ + time)) {
+                       expires = tmp_fil->ftime + QLCNIC_FILTER_AGE * HZ;
+                       if (time_before(expires, jiffies)) {
                                qlcnic_sre_macaddr_change(adapter,
                                                          tmp_fil->faddr,
                                                          tmp_fil->vlan_id,
@@ -657,8 +647,8 @@ void qlcnic_prune_lb_filters(struct qlcnic_adapter *adapter)
 
                hlist_for_each_entry_safe(tmp_fil, n, head, fnode)
                {
-                       time = tmp_fil->ftime;
-                       if (jiffies > (QLCNIC_FILTER_AGE * HZ + time)) {
+                       expires = tmp_fil->ftime + QLCNIC_FILTER_AGE * HZ;
+                       if (time_before(expires, jiffies)) {
                                spin_lock_bh(&adapter->rx_mac_learn_lock);
                                adapter->rx_fhash.fnum--;
                                hlist_del(&tmp_fil->fnode);
index 173b3d12991f55a62751d5e6a213d20ee02c3174..e45bf09af0c9fe4dbe9cdc629370af792ab88c01 100644 (file)
@@ -305,7 +305,6 @@ static void qlcnic_send_filter(struct qlcnic_adapter *adapter,
 {
        struct vlan_ethhdr *vh = (struct vlan_ethhdr *)(skb->data);
        struct ethhdr *phdr = (struct ethhdr *)(skb->data);
-       struct net_device *netdev = adapter->netdev;
        u16 protocol = ntohs(skb->protocol);
        struct qlcnic_filter *fil, *tmp_fil;
        struct hlist_head *head;
@@ -314,27 +313,16 @@ static void qlcnic_send_filter(struct qlcnic_adapter *adapter,
        u16 vlan_id = 0;
        u8 hindex, hval;
 
-       if (!qlcnic_sriov_pf_check(adapter)) {
-               if (ether_addr_equal(phdr->h_source, adapter->mac_addr))
-                       return;
-       } else {
+       if (ether_addr_equal(phdr->h_source, adapter->mac_addr))
+               return;
+
+       if (adapter->flags & QLCNIC_VLAN_FILTERING) {
                if (protocol == ETH_P_8021Q) {
                        vh = (struct vlan_ethhdr *)skb->data;
                        vlan_id = ntohs(vh->h_vlan_TCI);
                } else if (vlan_tx_tag_present(skb)) {
                        vlan_id = vlan_tx_tag_get(skb);
                }
-
-               if (ether_addr_equal(phdr->h_source, adapter->mac_addr) &&
-                   !vlan_id)
-                       return;
-       }
-
-       if (adapter->fhash.fnum >= adapter->fhash.fmax) {
-               adapter->stats.mac_filter_limit_overrun++;
-               netdev_info(netdev, "Can not add more than %d mac-vlan filters, configured %d\n",
-                           adapter->fhash.fmax, adapter->fhash.fnum);
-               return;
        }
 
        memcpy(&src_addr, phdr->h_source, ETH_ALEN);
@@ -353,6 +341,11 @@ static void qlcnic_send_filter(struct qlcnic_adapter *adapter,
                }
        }
 
+       if (unlikely(adapter->fhash.fnum >= adapter->fhash.fmax)) {
+               adapter->stats.mac_filter_limit_overrun++;
+               return;
+       }
+
        fil = kzalloc(sizeof(struct qlcnic_filter), GFP_ATOMIC);
        if (!fil)
                return;
@@ -1216,8 +1209,7 @@ qlcnic_process_rcv(struct qlcnic_adapter *adapter,
        if (!skb)
                return buffer;
 
-       if (adapter->drv_mac_learn &&
-           (adapter->flags & QLCNIC_ESWITCH_ENABLED)) {
+       if (adapter->rx_mac_learn) {
                t_vid = 0;
                is_lb_pkt = qlcnic_82xx_is_lb_pkt(sts_data0);
                qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, t_vid);
@@ -1293,8 +1285,7 @@ qlcnic_process_lro(struct qlcnic_adapter *adapter,
        if (!skb)
                return buffer;
 
-       if (adapter->drv_mac_learn &&
-           (adapter->flags & QLCNIC_ESWITCH_ENABLED)) {
+       if (adapter->rx_mac_learn) {
                t_vid = 0;
                is_lb_pkt = qlcnic_82xx_is_lb_pkt(sts_data0);
                qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, t_vid);
index 7e55e88a81bf26ede0928225fa85998254280195..4fc186713b660c8d823744f842cf3551dd660092 100644 (file)
@@ -378,7 +378,8 @@ static int qlcnic_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
        if (!adapter->fdb_mac_learn)
                return ndo_dflt_fdb_del(ndm, tb, netdev, addr);
 
-       if (adapter->flags & QLCNIC_ESWITCH_ENABLED) {
+       if ((adapter->flags & QLCNIC_ESWITCH_ENABLED) ||
+           qlcnic_sriov_check(adapter)) {
                if (is_unicast_ether_addr(addr)) {
                        err = dev_uc_del(netdev, addr);
                        if (!err)
@@ -402,7 +403,8 @@ static int qlcnic_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
        if (!adapter->fdb_mac_learn)
                return ndo_dflt_fdb_add(ndm, tb, netdev, addr, flags);
 
-       if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) {
+       if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED) &&
+           !qlcnic_sriov_check(adapter)) {
                pr_info("%s: FDB e-switch is not enabled\n", __func__);
                return -EOPNOTSUPP;
        }
@@ -432,7 +434,8 @@ static int qlcnic_fdb_dump(struct sk_buff *skb, struct netlink_callback *ncb,
        if (!adapter->fdb_mac_learn)
                return ndo_dflt_fdb_dump(skb, ncb, netdev, idx);
 
-       if (adapter->flags & QLCNIC_ESWITCH_ENABLED)
+       if ((adapter->flags & QLCNIC_ESWITCH_ENABLED) ||
+           qlcnic_sriov_check(adapter))
                idx = ndo_dflt_fdb_dump(skb, ncb, netdev, idx);
 
        return idx;
@@ -522,7 +525,7 @@ static const struct net_device_ops qlcnic_netdev_ops = {
 #endif
 #ifdef CONFIG_QLCNIC_SRIOV
        .ndo_set_vf_mac         = qlcnic_sriov_set_vf_mac,
-       .ndo_set_vf_tx_rate     = qlcnic_sriov_set_vf_tx_rate,
+       .ndo_set_vf_rate        = qlcnic_sriov_set_vf_tx_rate,
        .ndo_get_vf_config      = qlcnic_sriov_get_vf_config,
        .ndo_set_vf_vlan        = qlcnic_sriov_set_vf_vlan,
        .ndo_set_vf_spoofchk    = qlcnic_sriov_set_vf_spoofchk,
@@ -690,10 +693,10 @@ int qlcnic_setup_tss_rss_intr(struct qlcnic_adapter *adapter)
                adapter->msix_entries[vector].entry = vector;
 
 restore:
-       err = pci_enable_msix(pdev, adapter->msix_entries, num_msix);
-       if (err > 0) {
+       err = pci_enable_msix_exact(pdev, adapter->msix_entries, num_msix);
+       if (err == -ENOSPC) {
                if (!adapter->drv_tss_rings && !adapter->drv_rss_rings)
-                       return -ENOSPC;
+                       return err;
 
                netdev_info(adapter->netdev,
                            "Unable to allocate %d MSI-X vectors, Available vectors %d\n",
@@ -1014,6 +1017,8 @@ int qlcnic_init_pci_info(struct qlcnic_adapter *adapter)
 
                if (pfn >= ahw->max_vnic_func) {
                        ret = QL_STATUS_INVALID_PARAM;
+                       dev_err(&adapter->pdev->dev, "%s: Invalid function 0x%x, max 0x%x\n",
+                               __func__, pfn, ahw->max_vnic_func);
                        goto err_eswitch;
                }
 
@@ -1915,8 +1920,6 @@ void __qlcnic_down(struct qlcnic_adapter *adapter, struct net_device *netdev)
        if (!test_and_clear_bit(__QLCNIC_DEV_UP, &adapter->state))
                return;
 
-       if (qlcnic_sriov_vf_check(adapter))
-               qlcnic_sriov_cleanup_async_list(&adapter->ahw->sriov->bc);
        smp_mb();
        netif_carrier_off(netdev);
        adapter->ahw->linkup = 0;
@@ -1928,6 +1931,8 @@ void __qlcnic_down(struct qlcnic_adapter *adapter, struct net_device *netdev)
                qlcnic_delete_lb_filters(adapter);
 
        qlcnic_nic_set_promisc(adapter, QLCNIC_NIU_NON_PROMISC_MODE);
+       if (qlcnic_sriov_vf_check(adapter))
+               qlcnic_sriov_cleanup_async_list(&adapter->ahw->sriov->bc);
 
        qlcnic_napi_disable(adapter);
 
@@ -2052,6 +2057,7 @@ void qlcnic_diag_free_res(struct net_device *netdev, int drv_sds_rings)
 
 static int qlcnic_alloc_adapter_resources(struct qlcnic_adapter *adapter)
 {
+       struct qlcnic_hardware_context *ahw = adapter->ahw;
        int err = 0;
 
        adapter->recv_ctx = kzalloc(sizeof(struct qlcnic_recv_context),
@@ -2061,6 +2067,18 @@ static int qlcnic_alloc_adapter_resources(struct qlcnic_adapter *adapter)
                goto err_out;
        }
 
+       if (qlcnic_83xx_check(adapter)) {
+               ahw->coal.type = QLCNIC_INTR_COAL_TYPE_RX_TX;
+               ahw->coal.tx_time_us = QLCNIC_DEF_INTR_COALESCE_TX_TIME_US;
+               ahw->coal.tx_packets = QLCNIC_DEF_INTR_COALESCE_TX_PACKETS;
+               ahw->coal.rx_time_us = QLCNIC_DEF_INTR_COALESCE_RX_TIME_US;
+               ahw->coal.rx_packets = QLCNIC_DEF_INTR_COALESCE_RX_PACKETS;
+       } else {
+               ahw->coal.type = QLCNIC_INTR_COAL_TYPE_RX;
+               ahw->coal.rx_time_us = QLCNIC_DEF_INTR_COALESCE_RX_TIME_US;
+               ahw->coal.rx_packets = QLCNIC_DEF_INTR_COALESCE_RX_PACKETS;
+       }
+
        /* clear stats */
        memset(&adapter->stats, 0, sizeof(adapter->stats));
 err_out:
@@ -2069,12 +2087,20 @@ static int qlcnic_alloc_adapter_resources(struct qlcnic_adapter *adapter)
 
 static void qlcnic_free_adapter_resources(struct qlcnic_adapter *adapter)
 {
+       struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
+
        kfree(adapter->recv_ctx);
        adapter->recv_ctx = NULL;
 
-       if (adapter->ahw->fw_dump.tmpl_hdr) {
-               vfree(adapter->ahw->fw_dump.tmpl_hdr);
-               adapter->ahw->fw_dump.tmpl_hdr = NULL;
+       if (fw_dump->tmpl_hdr) {
+               vfree(fw_dump->tmpl_hdr);
+               fw_dump->tmpl_hdr = NULL;
+       }
+
+       if (fw_dump->dma_buffer) {
+               dma_free_coherent(&adapter->pdev->dev, QLC_PEX_DMA_READ_SIZE,
+                                 fw_dump->dma_buffer, fw_dump->phys_addr);
+               fw_dump->dma_buffer = NULL;
        }
 
        kfree(adapter->ahw->reset.buff);
@@ -2247,10 +2273,8 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev,
 
        qlcnic_change_mtu(netdev, netdev->mtu);
 
-       if (qlcnic_sriov_vf_check(adapter))
-               SET_ETHTOOL_OPS(netdev, &qlcnic_sriov_vf_ethtool_ops);
-       else
-               SET_ETHTOOL_OPS(netdev, &qlcnic_ethtool_ops);
+       netdev->ethtool_ops = (qlcnic_sriov_vf_check(adapter)) ?
+               &qlcnic_sriov_vf_ethtool_ops : &qlcnic_ethtool_ops;
 
        netdev->features |= (NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
                             NETIF_F_IPV6_CSUM | NETIF_F_GRO |
@@ -2417,9 +2441,6 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        int err, pci_using_dac = -1;
        char board_name[QLCNIC_MAX_BOARD_NAME_LEN + 19]; /* MAC + ": " + name */
 
-       if (pdev->is_virtfn)
-               return -ENODEV;
-
        err = pci_enable_device(pdev);
        if (err)
                return err;
@@ -2552,9 +2573,11 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                        case -ENOMEM:
                                dev_err(&pdev->dev, "Adapter initialization failed. Please reboot\n");
                                goto err_out_free_hw;
+                       case -EOPNOTSUPP:
+                               dev_err(&pdev->dev, "Adapter initialization failed\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");
+                               dev_err(&pdev->dev, "Adapter initialization failed. Driver will load in maintenance mode to recover the adapter using the application\n");
                                goto err_out_maintenance_mode;
                        }
                }
@@ -2628,7 +2651,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                qlcnic_alloc_lb_filters_mem(adapter);
 
        qlcnic_add_sysfs(adapter);
-
+       qlcnic_register_hwmon_dev(adapter);
        return 0;
 
 err_out_disable_mbx_intr:
@@ -2665,7 +2688,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 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);
+       netdev->ethtool_ops = &qlcnic_ethtool_failed_ops;
        ahw->port_type = QLCNIC_XGBE;
 
        if (qlcnic_83xx_check(adapter))
@@ -2698,9 +2721,9 @@ static void qlcnic_remove(struct pci_dev *pdev)
                return;
 
        netdev = adapter->netdev;
-       qlcnic_sriov_pf_disable(adapter);
 
        qlcnic_cancel_idc_work(adapter);
+       qlcnic_sriov_pf_disable(adapter);
        ahw = adapter->ahw;
 
        unregister_netdev(netdev);
@@ -2735,6 +2758,8 @@ static void qlcnic_remove(struct pci_dev *pdev)
 
        qlcnic_remove_sysfs(adapter);
 
+       qlcnic_unregister_hwmon_dev(adapter);
+
        qlcnic_cleanup_pci_map(adapter->ahw);
 
        qlcnic_release_firmware(adapter);
@@ -2828,6 +2853,8 @@ static int qlcnic_close(struct net_device *netdev)
        return 0;
 }
 
+#define QLCNIC_VF_LB_BUCKET_SIZE 1
+
 void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter)
 {
        void *head;
@@ -2843,7 +2870,10 @@ void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter)
        spin_lock_init(&adapter->mac_learn_lock);
        spin_lock_init(&adapter->rx_mac_learn_lock);
 
-       if (qlcnic_82xx_check(adapter)) {
+       if (qlcnic_sriov_vf_check(adapter)) {
+               filter_size = QLCNIC_83XX_SRIOV_VF_MAX_MAC - 1;
+               adapter->fhash.fbucket_size = QLCNIC_VF_LB_BUCKET_SIZE;
+       } else if (qlcnic_82xx_check(adapter)) {
                filter_size = QLCNIC_LB_MAX_FILTERS;
                adapter->fhash.fbucket_size = QLCNIC_LB_BUCKET_SIZE;
        } else {
@@ -3973,16 +4003,6 @@ int qlcnic_validate_rings(struct qlcnic_adapter *adapter, __u32 ring_cnt,
                strcpy(buf, "Tx");
        }
 
-       if (!QLCNIC_IS_MSI_FAMILY(adapter)) {
-               netdev_err(netdev, "No RSS/TSS support in INT-x mode\n");
-               return -EINVAL;
-       }
-
-       if (adapter->flags & QLCNIC_MSI_ENABLED) {
-               netdev_err(netdev, "No RSS/TSS support in MSI mode\n");
-               return -EINVAL;
-       }
-
        if (!is_power_of_2(ring_cnt)) {
                netdev_err(netdev, "%s rings value should be a power of 2\n",
                           buf);
@@ -4122,7 +4142,7 @@ void qlcnic_restore_indev_addr(struct net_device *netdev, unsigned long event)
 
        rcu_read_lock();
        for_each_set_bit(vid, adapter->vlans, VLAN_N_VID) {
-               dev = __vlan_find_dev_deep(netdev, htons(ETH_P_8021Q), vid);
+               dev = __vlan_find_dev_deep_rcu(netdev, htons(ETH_P_8021Q), vid);
                if (!dev)
                        continue;
                qlcnic_config_indev_addr(adapter, dev, event);
index 37b979b1266bc2e0aa552f84dd8a14a149c82665..e46fc39d425d45ee1d0d081b45954801c46f89ed 100644 (file)
@@ -238,6 +238,8 @@ void qlcnic_82xx_cache_tmpl_hdr_values(struct qlcnic_fw_dump *fw_dump)
 
        hdr->drv_cap_mask = hdr->cap_mask;
        fw_dump->cap_mask = hdr->cap_mask;
+
+       fw_dump->use_pex_dma = (hdr->capabilities & BIT_0) ? true : false;
 }
 
 inline u32 qlcnic_82xx_get_cap_size(void *t_hdr, int index)
@@ -276,6 +278,8 @@ inline void qlcnic_83xx_set_saved_state(void *t_hdr, u32 index,
        hdr->saved_state[index] = value;
 }
 
+#define QLCNIC_TEMPLATE_VERSION (0x20001)
+
 void qlcnic_83xx_cache_tmpl_hdr_values(struct qlcnic_fw_dump *fw_dump)
 {
        struct qlcnic_83xx_dump_template_hdr *hdr;
@@ -288,6 +292,9 @@ void qlcnic_83xx_cache_tmpl_hdr_values(struct qlcnic_fw_dump *fw_dump)
 
        hdr->drv_cap_mask = hdr->cap_mask;
        fw_dump->cap_mask = hdr->cap_mask;
+
+       fw_dump->use_pex_dma = (fw_dump->version & 0xfffff) >=
+                              QLCNIC_TEMPLATE_VERSION;
 }
 
 inline u32 qlcnic_83xx_get_cap_size(void *t_hdr, int index)
@@ -653,34 +660,31 @@ static u32 qlcnic_read_memory_test_agent(struct qlcnic_adapter *adapter,
 #define QLC_DMA_CMD_BUFF_ADDR_HI       4
 #define QLC_DMA_CMD_STATUS_CTRL                8
 
-#define QLC_PEX_DMA_READ_SIZE          (PAGE_SIZE * 16)
-
 static int qlcnic_start_pex_dma(struct qlcnic_adapter *adapter,
                                struct __mem *mem)
 {
-       struct qlcnic_83xx_dump_template_hdr *tmpl_hdr;
        struct device *dev = &adapter->pdev->dev;
        u32 dma_no, dma_base_addr, temp_addr;
        int i, ret, dma_sts;
+       void *tmpl_hdr;
 
        tmpl_hdr = adapter->ahw->fw_dump.tmpl_hdr;
-       dma_no = tmpl_hdr->saved_state[QLC_83XX_DMA_ENGINE_INDEX];
+       dma_no = qlcnic_get_saved_state(adapter, tmpl_hdr,
+                                       QLC_83XX_DMA_ENGINE_INDEX);
        dma_base_addr = QLC_DMA_REG_BASE_ADDR(dma_no);
 
        temp_addr = dma_base_addr + QLC_DMA_CMD_BUFF_ADDR_LOW;
-       ret = qlcnic_83xx_wrt_reg_indirect(adapter, temp_addr,
-                                          mem->desc_card_addr);
+       ret = qlcnic_ind_wr(adapter, temp_addr, mem->desc_card_addr);
        if (ret)
                return ret;
 
        temp_addr = dma_base_addr + QLC_DMA_CMD_BUFF_ADDR_HI;
-       ret = qlcnic_83xx_wrt_reg_indirect(adapter, temp_addr, 0);
+       ret = qlcnic_ind_wr(adapter, temp_addr, 0);
        if (ret)
                return ret;
 
        temp_addr = dma_base_addr + QLC_DMA_CMD_STATUS_CTRL;
-       ret = qlcnic_83xx_wrt_reg_indirect(adapter, temp_addr,
-                                          mem->start_dma_cmd);
+       ret = qlcnic_ind_wr(adapter, temp_addr, mem->start_dma_cmd);
        if (ret)
                return ret;
 
@@ -710,15 +714,16 @@ static u32 qlcnic_read_memory_pexdma(struct qlcnic_adapter *adapter,
        struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
        u32 temp, dma_base_addr, size = 0, read_size = 0;
        struct qlcnic_pex_dma_descriptor *dma_descr;
-       struct qlcnic_83xx_dump_template_hdr *tmpl_hdr;
        struct device *dev = &adapter->pdev->dev;
        dma_addr_t dma_phys_addr;
        void *dma_buffer;
+       void *tmpl_hdr;
 
        tmpl_hdr = fw_dump->tmpl_hdr;
 
        /* Check if DMA engine is available */
-       temp = tmpl_hdr->saved_state[QLC_83XX_DMA_ENGINE_INDEX];
+       temp = qlcnic_get_saved_state(adapter, tmpl_hdr,
+                                     QLC_83XX_DMA_ENGINE_INDEX);
        dma_base_addr = QLC_DMA_REG_BASE_ADDR(temp);
        temp = qlcnic_ind_rd(adapter,
                             dma_base_addr + QLC_DMA_CMD_STATUS_CTRL);
@@ -764,8 +769,8 @@ static u32 qlcnic_read_memory_pexdma(struct qlcnic_adapter *adapter,
 
                /* Write DMA descriptor to MS memory*/
                temp = sizeof(struct qlcnic_pex_dma_descriptor) / 16;
-               *ret = qlcnic_83xx_ms_mem_write128(adapter, mem->desc_card_addr,
-                                                  (u32 *)dma_descr, temp);
+               *ret = qlcnic_ms_mem_write128(adapter, mem->desc_card_addr,
+                                             (u32 *)dma_descr, temp);
                if (*ret) {
                        dev_info(dev, "Failed to write DMA descriptor to MS memory at address 0x%x\n",
                                 mem->desc_card_addr);
@@ -1141,8 +1146,6 @@ static int __qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter,
        return err;
 }
 
-#define QLCNIC_TEMPLATE_VERSION (0x20001)
-
 int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter)
 {
        struct qlcnic_hardware_context *ahw;
@@ -1150,6 +1153,7 @@ int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter)
        u32 version, csum, *tmp_buf;
        u8 use_flash_temp = 0;
        u32 temp_size = 0;
+       void *temp_buffer;
        int err;
 
        ahw = adapter->ahw;
@@ -1199,16 +1203,23 @@ int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter)
 
        qlcnic_cache_tmpl_hdr_values(adapter, fw_dump);
 
+       if (fw_dump->use_pex_dma) {
+               fw_dump->dma_buffer = NULL;
+               temp_buffer = dma_alloc_coherent(&adapter->pdev->dev,
+                                                QLC_PEX_DMA_READ_SIZE,
+                                                &fw_dump->phys_addr,
+                                                GFP_KERNEL);
+               if (!temp_buffer)
+                       fw_dump->use_pex_dma = false;
+               else
+                       fw_dump->dma_buffer = temp_buffer;
+       }
+
+
        dev_info(&adapter->pdev->dev,
                 "Default minidump capture mask 0x%x\n",
                 fw_dump->cap_mask);
 
-       if (qlcnic_83xx_check(adapter) &&
-           (fw_dump->version & 0xfffff) >= QLCNIC_TEMPLATE_VERSION)
-               fw_dump->use_pex_dma = true;
-       else
-               fw_dump->use_pex_dma = false;
-
        qlcnic_enable_fw_dump_state(adapter);
 
        return 0;
@@ -1224,7 +1235,7 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
        struct device *dev = &adapter->pdev->dev;
        struct qlcnic_hardware_context *ahw;
        struct qlcnic_dump_entry *entry;
-       void *temp_buffer, *tmpl_hdr;
+       void *tmpl_hdr;
        u32 ocm_window;
        __le32 *buffer;
        char mesg[64];
@@ -1268,16 +1279,6 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
        qlcnic_set_sys_info(adapter, tmpl_hdr, 0, QLCNIC_DRIVER_VERSION);
        qlcnic_set_sys_info(adapter, tmpl_hdr, 1, adapter->fw_version);
 
-       if (fw_dump->use_pex_dma) {
-               temp_buffer = dma_alloc_coherent(dev, QLC_PEX_DMA_READ_SIZE,
-                                                &fw_dump->phys_addr,
-                                                GFP_KERNEL);
-               if (!temp_buffer)
-                       fw_dump->use_pex_dma = false;
-               else
-                       fw_dump->dma_buffer = temp_buffer;
-       }
-
        if (qlcnic_82xx_check(adapter)) {
                ops_cnt = ARRAY_SIZE(qlcnic_fw_dump_ops);
                fw_dump_ops = qlcnic_fw_dump_ops;
@@ -1335,10 +1336,6 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
        /* 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);
-
        return 0;
 }
 
index 396bd1fd1d277deb56d70e185280ed3172902a49..4677b2edccca79fa1072e8a67315add790271c93 100644 (file)
@@ -52,6 +52,7 @@ enum qlcnic_bc_commands {
        QLCNIC_BC_CMD_CFG_GUEST_VLAN = 0x3,
 };
 
+#define QLCNIC_83XX_SRIOV_VF_MAX_MAC 2
 #define QLC_BC_CMD 1
 
 struct qlcnic_trans_list {
@@ -151,13 +152,14 @@ struct qlcnic_vf_info {
        struct qlcnic_trans_list        rcv_pend;
        struct qlcnic_adapter           *adapter;
        struct qlcnic_vport             *vp;
-       struct mutex                    vlan_list_lock; /* Lock for VLAN list */
+       spinlock_t                      vlan_list_lock; /* Lock for VLAN list */
 };
 
 struct qlcnic_async_work_list {
        struct list_head        list;
        struct work_struct      work;
        void                    *ptr;
+       struct qlcnic_cmd_args  *cmd;
 };
 
 struct qlcnic_back_channel {
@@ -231,7 +233,7 @@ bool qlcnic_sriov_soft_flr_check(struct qlcnic_adapter *,
 void qlcnic_sriov_pf_reset(struct qlcnic_adapter *);
 int qlcnic_sriov_pf_reinit(struct qlcnic_adapter *);
 int qlcnic_sriov_set_vf_mac(struct net_device *, int, u8 *);
-int qlcnic_sriov_set_vf_tx_rate(struct net_device *, int, int);
+int qlcnic_sriov_set_vf_tx_rate(struct net_device *, int, int, int);
 int qlcnic_sriov_get_vf_config(struct net_device *, int ,
                               struct ifla_vf_info *);
 int qlcnic_sriov_set_vf_vlan(struct net_device *, int, u16, u8);
index 6afe9c1f5ab9337a3fa7c64e7863664af3db99b7..1659c804f1d5f4fc530f7d9b2ac11c7f9f023c6b 100644 (file)
@@ -39,6 +39,8 @@ static int qlcnic_sriov_channel_cfg_cmd(struct qlcnic_adapter *, u8);
 static void qlcnic_sriov_process_bc_cmd(struct work_struct *);
 static int qlcnic_sriov_vf_shutdown(struct pci_dev *);
 static int qlcnic_sriov_vf_resume(struct qlcnic_adapter *);
+static int qlcnic_sriov_async_issue_cmd(struct qlcnic_adapter *,
+                                       struct qlcnic_cmd_args *);
 
 static struct qlcnic_hardware_ops qlcnic_sriov_vf_hw_ops = {
        .read_crb                       = qlcnic_83xx_read_crb,
@@ -181,7 +183,7 @@ int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs)
                vf->adapter = adapter;
                vf->pci_func = qlcnic_sriov_virtid_fn(adapter, i);
                mutex_init(&vf->send_cmd_lock);
-               mutex_init(&vf->vlan_list_lock);
+               spin_lock_init(&vf->vlan_list_lock);
                INIT_LIST_HEAD(&vf->rcv_act.wait_list);
                INIT_LIST_HEAD(&vf->rcv_pend.wait_list);
                spin_lock_init(&vf->rcv_act.lock);
@@ -197,8 +199,10 @@ int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs)
                                goto qlcnic_destroy_async_wq;
                        }
                        sriov->vf_info[i].vp = vp;
+                       vp->vlan_mode = QLC_GUEST_VLAN_MODE;
                        vp->max_tx_bw = MAX_BW;
-                       vp->spoofchk = true;
+                       vp->min_tx_bw = MIN_BW;
+                       vp->spoofchk = false;
                        random_ether_addr(vp->mac);
                        dev_info(&adapter->pdev->dev,
                                 "MAC Address %pM is configured for VF %d\n",
@@ -454,6 +458,7 @@ static int qlcnic_sriov_get_vf_acl(struct qlcnic_adapter *adapter)
        struct qlcnic_cmd_args cmd;
        int ret = 0;
 
+       memset(&cmd, 0, sizeof(cmd));
        ret = qlcnic_sriov_alloc_bc_mbx_args(&cmd, QLCNIC_BC_CMD_GET_ACL);
        if (ret)
                return ret;
@@ -515,6 +520,8 @@ static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter,
 {
        int err;
 
+       adapter->flags |= QLCNIC_VLAN_FILTERING;
+       adapter->ahw->total_nic_func = 1;
        INIT_LIST_HEAD(&adapter->vf_mc_list);
        if (!qlcnic_use_msi_x && !!qlcnic_use_msi)
                dev_warn(&adapter->pdev->dev,
@@ -770,6 +777,7 @@ static int qlcnic_sriov_prepare_bc_hdr(struct qlcnic_bc_trans *trans,
                cmd->req.arg = (u32 *)trans->req_pay;
                cmd->rsp.arg = (u32 *)trans->rsp_pay;
                cmd_op = cmd->req.arg[0] & 0xff;
+               cmd->cmd_op = cmd_op;
                remainder = (trans->rsp_pay_size) % (bc_pay_sz);
                num_frags = (trans->rsp_pay_size) / (bc_pay_sz);
                if (remainder)
@@ -1356,7 +1364,7 @@ static int qlcnic_sriov_retry_bc_cmd(struct qlcnic_adapter *adapter,
        return -EIO;
 }
 
-static int qlcnic_sriov_issue_cmd(struct qlcnic_adapter *adapter,
+static int __qlcnic_sriov_issue_cmd(struct qlcnic_adapter *adapter,
                                  struct qlcnic_cmd_args *cmd)
 {
        struct qlcnic_hardware_context *ahw = adapter->ahw;
@@ -1408,12 +1416,17 @@ static int qlcnic_sriov_issue_cmd(struct qlcnic_adapter *adapter,
            (mbx_err_code == QLCNIC_MBX_PORT_RSP_OK)) {
                rsp = QLCNIC_RCODE_SUCCESS;
        } else {
-               rsp = mbx_err_code;
-               if (!rsp)
-                       rsp = 1;
-               dev_err(dev,
-                       "MBX command 0x%x failed with err:0x%x for VF %d\n",
-                       opcode, mbx_err_code, func);
+               if (cmd->type == QLC_83XX_MBX_CMD_NO_WAIT) {
+                       rsp = QLCNIC_RCODE_SUCCESS;
+               } else {
+                       rsp = mbx_err_code;
+                       if (!rsp)
+                               rsp = 1;
+
+                       dev_err(dev,
+                               "MBX command 0x%x failed with err:0x%x for VF %d\n",
+                               opcode, mbx_err_code, func);
+               }
        }
 
 err_out:
@@ -1435,12 +1448,23 @@ static int qlcnic_sriov_issue_cmd(struct qlcnic_adapter *adapter,
        return rsp;
 }
 
+
+static int qlcnic_sriov_issue_cmd(struct qlcnic_adapter *adapter,
+                                 struct qlcnic_cmd_args *cmd)
+{
+       if (cmd->type == QLC_83XX_MBX_CMD_NO_WAIT)
+               return qlcnic_sriov_async_issue_cmd(adapter, cmd);
+       else
+               return __qlcnic_sriov_issue_cmd(adapter, cmd);
+}
+
 static int qlcnic_sriov_channel_cfg_cmd(struct qlcnic_adapter *adapter, u8 cmd_op)
 {
        struct qlcnic_cmd_args cmd;
        struct qlcnic_vf_info *vf = &adapter->ahw->sriov->vf_info[0];
        int ret;
 
+       memset(&cmd, 0, sizeof(cmd));
        if (qlcnic_sriov_alloc_bc_mbx_args(&cmd, cmd_op))
                return -ENOMEM;
 
@@ -1465,58 +1489,28 @@ static int qlcnic_sriov_channel_cfg_cmd(struct qlcnic_adapter *adapter, u8 cmd_o
        return ret;
 }
 
-static void qlcnic_vf_add_mc_list(struct net_device *netdev)
+static void qlcnic_vf_add_mc_list(struct net_device *netdev, const u8 *mac)
 {
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
        struct qlcnic_sriov *sriov = adapter->ahw->sriov;
-       struct qlcnic_mac_vlan_list *cur;
-       struct list_head *head, tmp_list;
        struct qlcnic_vf_info *vf;
        u16 vlan_id;
        int i;
 
-       static const u8 bcast_addr[ETH_ALEN] = {
-               0xff, 0xff, 0xff, 0xff, 0xff, 0xff
-       };
-
        vf = &adapter->ahw->sriov->vf_info[0];
-       INIT_LIST_HEAD(&tmp_list);
-       head = &adapter->vf_mc_list;
-       netif_addr_lock_bh(netdev);
 
-       while (!list_empty(head)) {
-               cur = list_entry(head->next, struct qlcnic_mac_vlan_list, list);
-               list_move(&cur->list, &tmp_list);
-       }
-
-       netif_addr_unlock_bh(netdev);
-
-       while (!list_empty(&tmp_list)) {
-               cur = list_entry((&tmp_list)->next,
-                                struct qlcnic_mac_vlan_list, list);
-               if (!qlcnic_sriov_check_any_vlan(vf)) {
-                       qlcnic_nic_add_mac(adapter, bcast_addr, 0);
-                       qlcnic_nic_add_mac(adapter, cur->mac_addr, 0);
-               } else {
-                       mutex_lock(&vf->vlan_list_lock);
-                       for (i = 0; i < sriov->num_allowed_vlans; i++) {
-                               vlan_id = vf->sriov_vlans[i];
-                               if (vlan_id) {
-                                       qlcnic_nic_add_mac(adapter, bcast_addr,
-                                                          vlan_id);
-                                       qlcnic_nic_add_mac(adapter,
-                                                          cur->mac_addr,
-                                                          vlan_id);
-                               }
-                       }
-                       mutex_unlock(&vf->vlan_list_lock);
-                       if (qlcnic_84xx_check(adapter)) {
-                               qlcnic_nic_add_mac(adapter, bcast_addr, 0);
-                               qlcnic_nic_add_mac(adapter, cur->mac_addr, 0);
-                       }
+       if (!qlcnic_sriov_check_any_vlan(vf)) {
+               qlcnic_nic_add_mac(adapter, mac, 0);
+       } else {
+               spin_lock(&vf->vlan_list_lock);
+               for (i = 0; i < sriov->num_allowed_vlans; i++) {
+                       vlan_id = vf->sriov_vlans[i];
+                       if (vlan_id)
+                               qlcnic_nic_add_mac(adapter, mac, vlan_id);
                }
-               list_del(&cur->list);
-               kfree(cur);
+               spin_unlock(&vf->vlan_list_lock);
+               if (qlcnic_84xx_check(adapter))
+                       qlcnic_nic_add_mac(adapter, mac, 0);
        }
 }
 
@@ -1525,6 +1519,7 @@ void qlcnic_sriov_cleanup_async_list(struct qlcnic_back_channel *bc)
        struct list_head *head = &bc->async_list;
        struct qlcnic_async_work_list *entry;
 
+       flush_workqueue(bc->bc_async_wq);
        while (!list_empty(head)) {
                entry = list_entry(head->next, struct qlcnic_async_work_list,
                                   list);
@@ -1534,10 +1529,14 @@ void qlcnic_sriov_cleanup_async_list(struct qlcnic_back_channel *bc)
        }
 }
 
-static void qlcnic_sriov_vf_set_multi(struct net_device *netdev)
+void qlcnic_sriov_vf_set_multi(struct net_device *netdev)
 {
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
        struct qlcnic_hardware_context *ahw = adapter->ahw;
+       static const u8 bcast_addr[ETH_ALEN] = {
+               0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+       };
+       struct netdev_hw_addr *ha;
        u32 mode = VPORT_MISS_MODE_DROP;
 
        if (!test_bit(__QLCNIC_FW_ATTACHED, &adapter->state))
@@ -1549,23 +1548,49 @@ static void qlcnic_sriov_vf_set_multi(struct net_device *netdev)
        } else if ((netdev->flags & IFF_ALLMULTI) ||
                   (netdev_mc_count(netdev) > ahw->max_mc_count)) {
                mode = VPORT_MISS_MODE_ACCEPT_MULTI;
+       } else {
+               qlcnic_vf_add_mc_list(netdev, bcast_addr);
+               if (!netdev_mc_empty(netdev)) {
+                       netdev_for_each_mc_addr(ha, netdev)
+                               qlcnic_vf_add_mc_list(netdev, ha->addr);
+               }
        }
 
-       if (qlcnic_sriov_vf_check(adapter))
-               qlcnic_vf_add_mc_list(netdev);
+       /* configure unicast MAC address, if there is not sufficient space
+        * to store all the unicast addresses then enable promiscuous mode
+        */
+       if (netdev_uc_count(netdev) > ahw->max_uc_count) {
+               mode = VPORT_MISS_MODE_ACCEPT_ALL;
+       } else if (!netdev_uc_empty(netdev)) {
+               netdev_for_each_uc_addr(ha, netdev)
+                       qlcnic_vf_add_mc_list(netdev, ha->addr);
+       }
+
+       if (adapter->pdev->is_virtfn) {
+               if (mode == VPORT_MISS_MODE_ACCEPT_ALL &&
+                   !adapter->fdb_mac_learn) {
+                       qlcnic_alloc_lb_filters_mem(adapter);
+                       adapter->drv_mac_learn = 1;
+                       adapter->rx_mac_learn = true;
+               } else {
+                       adapter->drv_mac_learn = 0;
+                       adapter->rx_mac_learn = false;
+               }
+       }
 
        qlcnic_nic_set_promisc(adapter, mode);
 }
 
-static void qlcnic_sriov_handle_async_multi(struct work_struct *work)
+static void qlcnic_sriov_handle_async_issue_cmd(struct work_struct *work)
 {
        struct qlcnic_async_work_list *entry;
-       struct net_device *netdev;
+       struct qlcnic_adapter *adapter;
+       struct qlcnic_cmd_args *cmd;
 
        entry = container_of(work, struct qlcnic_async_work_list, work);
-       netdev = (struct net_device *)entry->ptr;
-
-       qlcnic_sriov_vf_set_multi(netdev);
+       adapter = entry->ptr;
+       cmd = entry->cmd;
+       __qlcnic_sriov_issue_cmd(adapter, cmd);
        return;
 }
 
@@ -1595,8 +1620,9 @@ qlcnic_sriov_get_free_node_async_work(struct qlcnic_back_channel *bc)
        return entry;
 }
 
-static void qlcnic_sriov_schedule_bc_async_work(struct qlcnic_back_channel *bc,
-                                               work_func_t func, void *data)
+static void qlcnic_sriov_schedule_async_cmd(struct qlcnic_back_channel *bc,
+                                           work_func_t func, void *data,
+                                           struct qlcnic_cmd_args *cmd)
 {
        struct qlcnic_async_work_list *entry = NULL;
 
@@ -1605,21 +1631,23 @@ static void qlcnic_sriov_schedule_bc_async_work(struct qlcnic_back_channel *bc,
                return;
 
        entry->ptr = data;
+       entry->cmd = cmd;
        INIT_WORK(&entry->work, func);
        queue_work(bc->bc_async_wq, &entry->work);
 }
 
-void qlcnic_sriov_vf_schedule_multi(struct net_device *netdev)
+static int qlcnic_sriov_async_issue_cmd(struct qlcnic_adapter *adapter,
+                                       struct qlcnic_cmd_args *cmd)
 {
 
-       struct qlcnic_adapter *adapter = netdev_priv(netdev);
        struct qlcnic_back_channel *bc = &adapter->ahw->sriov->bc;
 
        if (adapter->need_fw_reset)
-               return;
+               return -EIO;
 
-       qlcnic_sriov_schedule_bc_async_work(bc, qlcnic_sriov_handle_async_multi,
-                                           netdev);
+       qlcnic_sriov_schedule_async_cmd(bc, qlcnic_sriov_handle_async_issue_cmd,
+                                       adapter, cmd);
+       return 0;
 }
 
 static int qlcnic_sriov_vf_reinit_driver(struct qlcnic_adapter *adapter)
@@ -1843,6 +1871,12 @@ static int qlcnic_sriov_vf_idc_unknown_state(struct qlcnic_adapter *adapter)
        return 0;
 }
 
+static void qlcnic_sriov_vf_periodic_tasks(struct qlcnic_adapter *adapter)
+{
+       if (adapter->fhash.fnum)
+               qlcnic_prune_lb_filters(adapter);
+}
+
 static void qlcnic_sriov_vf_poll_dev_state(struct work_struct *work)
 {
        struct qlcnic_adapter *adapter;
@@ -1874,6 +1908,8 @@ static void qlcnic_sriov_vf_poll_dev_state(struct work_struct *work)
        }
 
        idc->prev_state = idc->curr_state;
+       qlcnic_sriov_vf_periodic_tasks(adapter);
+
        if (!ret && test_bit(QLC_83XX_MODULE_LOADED, &idc->status))
                qlcnic_schedule_work(adapter, qlcnic_sriov_vf_poll_dev_state,
                                     idc->delay);
@@ -1897,7 +1933,7 @@ static int qlcnic_sriov_check_vlan_id(struct qlcnic_sriov *sriov,
        if (!vf->sriov_vlans)
                return err;
 
-       mutex_lock(&vf->vlan_list_lock);
+       spin_lock_bh(&vf->vlan_list_lock);
 
        for (i = 0; i < sriov->num_allowed_vlans; i++) {
                if (vf->sriov_vlans[i] == vlan_id) {
@@ -1906,7 +1942,7 @@ static int qlcnic_sriov_check_vlan_id(struct qlcnic_sriov *sriov,
                }
        }
 
-       mutex_unlock(&vf->vlan_list_lock);
+       spin_unlock_bh(&vf->vlan_list_lock);
        return err;
 }
 
@@ -1915,12 +1951,12 @@ static int qlcnic_sriov_validate_num_vlans(struct qlcnic_sriov *sriov,
 {
        int err = 0;
 
-       mutex_lock(&vf->vlan_list_lock);
+       spin_lock_bh(&vf->vlan_list_lock);
 
        if (vf->num_vlan >= sriov->num_allowed_vlans)
                err = -EINVAL;
 
-       mutex_unlock(&vf->vlan_list_lock);
+       spin_unlock_bh(&vf->vlan_list_lock);
        return err;
 }
 
@@ -1973,7 +2009,7 @@ static void qlcnic_sriov_vlan_operation(struct qlcnic_vf_info *vf, u16 vlan_id,
        if (!vf->sriov_vlans)
                return;
 
-       mutex_lock(&vf->vlan_list_lock);
+       spin_lock_bh(&vf->vlan_list_lock);
 
        switch (opcode) {
        case QLC_VLAN_ADD:
@@ -1986,7 +2022,7 @@ static void qlcnic_sriov_vlan_operation(struct qlcnic_vf_info *vf, u16 vlan_id,
                netdev_err(adapter->netdev, "Invalid VLAN operation\n");
        }
 
-       mutex_unlock(&vf->vlan_list_lock);
+       spin_unlock_bh(&vf->vlan_list_lock);
        return;
 }
 
@@ -1994,10 +2030,12 @@ int qlcnic_sriov_cfg_vf_guest_vlan(struct qlcnic_adapter *adapter,
                                   u16 vid, u8 enable)
 {
        struct qlcnic_sriov *sriov = adapter->ahw->sriov;
+       struct net_device *netdev = adapter->netdev;
        struct qlcnic_vf_info *vf;
        struct qlcnic_cmd_args cmd;
        int ret;
 
+       memset(&cmd, 0, sizeof(cmd));
        if (vid == 0)
                return 0;
 
@@ -2019,14 +2057,18 @@ int qlcnic_sriov_cfg_vf_guest_vlan(struct qlcnic_adapter *adapter,
                dev_err(&adapter->pdev->dev,
                        "Failed to configure guest VLAN, err=%d\n", ret);
        } else {
+               netif_addr_lock_bh(netdev);
                qlcnic_free_mac_list(adapter);
+               netif_addr_unlock_bh(netdev);
 
                if (enable)
                        qlcnic_sriov_vlan_operation(vf, vid, QLC_VLAN_ADD);
                else
                        qlcnic_sriov_vlan_operation(vf, vid, QLC_VLAN_DELETE);
 
-               qlcnic_set_multi(adapter->netdev);
+               netif_addr_lock_bh(netdev);
+               qlcnic_set_multi(netdev);
+               netif_addr_unlock_bh(netdev);
        }
 
        qlcnic_free_mbx_args(&cmd);
@@ -2157,11 +2199,11 @@ bool qlcnic_sriov_check_any_vlan(struct qlcnic_vf_info *vf)
 {
        bool err = false;
 
-       mutex_lock(&vf->vlan_list_lock);
+       spin_lock_bh(&vf->vlan_list_lock);
 
        if (vf->num_vlan)
                err = true;
 
-       mutex_unlock(&vf->vlan_list_lock);
+       spin_unlock_bh(&vf->vlan_list_lock);
        return err;
 }
index 2801379915447dc54683719c40058d9d89f8387f..a29538b86edfcac99010cedbea1fff0a29db799b 100644 (file)
@@ -16,6 +16,7 @@
 #define QLC_VF_FLOOD_BIT       BIT_16
 #define QLC_FLOOD_MODE         0x5
 #define QLC_SRIOV_ALLOW_VLAN0  BIT_19
+#define QLC_INTR_COAL_TYPE_MASK        0x7
 
 static int qlcnic_sriov_pf_get_vport_handle(struct qlcnic_adapter *, u8);
 
@@ -83,7 +84,7 @@ static int qlcnic_sriov_pf_cal_res_limit(struct qlcnic_adapter *adapter,
        info->max_tx_ques = res->num_tx_queues / max;
 
        if (qlcnic_83xx_pf_check(adapter))
-               num_macs = 1;
+               num_macs = QLCNIC_83XX_SRIOV_VF_MAX_MAC;
 
        info->max_rx_mcast_mac_filters = res->num_rx_mcast_mac_filters;
 
@@ -337,9 +338,12 @@ static int qlcnic_sriov_pf_cfg_vlan_filtering(struct qlcnic_adapter *adapter,
 
        cmd.req.arg[1] = 0x4;
        if (enable) {
+               adapter->flags |= QLCNIC_VLAN_FILTERING;
                cmd.req.arg[1] |= BIT_16;
                if (qlcnic_84xx_check(adapter))
                        cmd.req.arg[1] |= QLC_SRIOV_ALLOW_VLAN0;
+       } else {
+               adapter->flags &= ~QLCNIC_VLAN_FILTERING;
        }
 
        err = qlcnic_issue_cmd(adapter, &cmd);
@@ -471,12 +475,12 @@ static int qlcnic_pci_sriov_disable(struct qlcnic_adapter *adapter)
                return -EPERM;
        }
 
+       qlcnic_sriov_pf_disable(adapter);
+
        rtnl_lock();
        if (netif_running(netdev))
                __qlcnic_down(adapter, netdev);
 
-       qlcnic_sriov_pf_disable(adapter);
-
        qlcnic_sriov_free_vlans(adapter);
 
        qlcnic_sriov_pf_cleanup(adapter);
@@ -595,7 +599,6 @@ static int __qlcnic_pci_sriov_enable(struct qlcnic_adapter *adapter,
 
        qlcnic_sriov_alloc_vlans(adapter);
 
-       err = qlcnic_sriov_pf_enable(adapter, num_vfs);
        return err;
 
 del_flr_queue:
@@ -626,25 +629,36 @@ static int qlcnic_pci_sriov_enable(struct qlcnic_adapter *adapter, int num_vfs)
                __qlcnic_down(adapter, netdev);
 
        err = __qlcnic_pci_sriov_enable(adapter, num_vfs);
-       if (err) {
-               netdev_info(netdev, "Failed to enable SR-IOV on port %d\n",
-                           adapter->portnum);
+       if (err)
+               goto error;
 
-               err = -EIO;
-               if (qlcnic_83xx_configure_opmode(adapter))
-                       goto error;
-       } else {
+       if (netif_running(netdev))
+               __qlcnic_up(adapter, netdev);
+
+       rtnl_unlock();
+       err = qlcnic_sriov_pf_enable(adapter, num_vfs);
+       if (!err) {
                netdev_info(netdev,
                            "SR-IOV is enabled successfully on port %d\n",
                            adapter->portnum);
                /* Return number of vfs enabled */
-               err = num_vfs;
+               return num_vfs;
        }
+
+       rtnl_lock();
        if (netif_running(netdev))
-               __qlcnic_up(adapter, netdev);
+               __qlcnic_down(adapter, netdev);
 
 error:
+       if (!qlcnic_83xx_configure_opmode(adapter)) {
+               if (netif_running(netdev))
+                       __qlcnic_up(adapter, netdev);
+       }
+
        rtnl_unlock();
+       netdev_info(netdev, "Failed to enable SR-IOV on port %d\n",
+                   adapter->portnum);
+
        return err;
 }
 
@@ -773,7 +787,7 @@ static int qlcnic_sriov_cfg_vf_def_mac(struct qlcnic_adapter *adapter,
                                       struct qlcnic_vf_info *vf,
                                       u16 vlan, u8 op)
 {
-       struct qlcnic_cmd_args cmd;
+       struct qlcnic_cmd_args *cmd;
        struct qlcnic_macvlan_mbx mv;
        struct qlcnic_vport *vp;
        u8 *addr;
@@ -783,21 +797,27 @@ static int qlcnic_sriov_cfg_vf_def_mac(struct qlcnic_adapter *adapter,
 
        vp = vf->vp;
 
-       if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_MAC_VLAN))
+       cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC);
+       if (!cmd)
                return -ENOMEM;
 
+       err = qlcnic_alloc_mbx_args(cmd, adapter, QLCNIC_CMD_CONFIG_MAC_VLAN);
+       if (err)
+               goto free_cmd;
+
+       cmd->type = QLC_83XX_MBX_CMD_NO_WAIT;
        vpid = qlcnic_sriov_pf_get_vport_handle(adapter, vf->pci_func);
        if (vpid < 0) {
                err = -EINVAL;
-               goto out;
+               goto free_args;
        }
 
        if (vlan)
                op = ((op == QLCNIC_MAC_ADD || op == QLCNIC_MAC_VLAN_ADD) ?
                      QLCNIC_MAC_VLAN_ADD : QLCNIC_MAC_VLAN_DEL);
 
-       cmd.req.arg[1] = op | (1 << 8) | (3 << 6);
-       cmd.req.arg[1] |= ((vpid & 0xffff) << 16) | BIT_31;
+       cmd->req.arg[1] = op | (1 << 8) | (3 << 6);
+       cmd->req.arg[1] |= ((vpid & 0xffff) << 16) | BIT_31;
 
        addr = vp->mac;
        mv.vlan = vlan;
@@ -807,18 +827,18 @@ static int qlcnic_sriov_cfg_vf_def_mac(struct qlcnic_adapter *adapter,
        mv.mac_addr3 = addr[3];
        mv.mac_addr4 = addr[4];
        mv.mac_addr5 = addr[5];
-       buf = &cmd.req.arg[2];
+       buf = &cmd->req.arg[2];
        memcpy(buf, &mv, sizeof(struct qlcnic_macvlan_mbx));
 
-       err = qlcnic_issue_cmd(adapter, &cmd);
+       err = qlcnic_issue_cmd(adapter, cmd);
 
-       if (err)
-               dev_err(&adapter->pdev->dev,
-                       "MAC-VLAN %s to CAM failed, err=%d.\n",
-                       ((op == 1) ? "add " : "delete "), err);
+       if (!err)
+               return err;
 
-out:
-       qlcnic_free_mbx_args(&cmd);
+free_args:
+       qlcnic_free_mbx_args(cmd);
+free_cmd:
+       kfree(cmd);
        return err;
 }
 
@@ -840,7 +860,7 @@ static void qlcnic_83xx_cfg_default_mac_vlan(struct qlcnic_adapter *adapter,
 
        sriov = adapter->ahw->sriov;
 
-       mutex_lock(&vf->vlan_list_lock);
+       spin_lock_bh(&vf->vlan_list_lock);
        if (vf->num_vlan) {
                for (i = 0; i < sriov->num_allowed_vlans; i++) {
                        vlan = vf->sriov_vlans[i];
@@ -849,7 +869,7 @@ static void qlcnic_83xx_cfg_default_mac_vlan(struct qlcnic_adapter *adapter,
                                                            opcode);
                }
        }
-       mutex_unlock(&vf->vlan_list_lock);
+       spin_unlock_bh(&vf->vlan_list_lock);
 
        if (vf->vp->vlan_mode != QLC_PVID_MODE) {
                if (qlcnic_83xx_pf_check(adapter) &&
@@ -1178,19 +1198,41 @@ static int qlcnic_sriov_validate_cfg_intrcoal(struct qlcnic_adapter *adapter,
 {
        struct qlcnic_nic_intr_coalesce *coal = &adapter->ahw->coal;
        u16 ctx_id, pkts, time;
+       int err = -EINVAL;
+       u8 type;
 
+       type = cmd->req.arg[1] & QLC_INTR_COAL_TYPE_MASK;
        ctx_id = cmd->req.arg[1] >> 16;
        pkts = cmd->req.arg[2] & 0xffff;
        time = cmd->req.arg[2] >> 16;
 
-       if (ctx_id != vf->rx_ctx_id)
-               return -EINVAL;
-       if (pkts > coal->rx_packets)
-               return -EINVAL;
-       if (time < coal->rx_time_us)
-               return -EINVAL;
+       switch (type) {
+       case QLCNIC_INTR_COAL_TYPE_RX:
+               if (ctx_id != vf->rx_ctx_id || pkts > coal->rx_packets ||
+                   time < coal->rx_time_us)
+                       goto err_label;
+               break;
+       case QLCNIC_INTR_COAL_TYPE_TX:
+               if (ctx_id != vf->tx_ctx_id || pkts > coal->tx_packets ||
+                   time < coal->tx_time_us)
+                       goto err_label;
+               break;
+       default:
+               netdev_err(adapter->netdev, "Invalid coalescing type 0x%x received\n",
+                          type);
+               return err;
+       }
 
        return 0;
+
+err_label:
+       netdev_err(adapter->netdev, "Expected: rx_ctx_id 0x%x rx_packets 0x%x rx_time_us 0x%x tx_ctx_id 0x%x tx_packets 0x%x tx_time_us 0x%x\n",
+                  vf->rx_ctx_id, coal->rx_packets, coal->rx_time_us,
+                  vf->tx_ctx_id, coal->tx_packets, coal->tx_time_us);
+       netdev_err(adapter->netdev, "Received: ctx_id 0x%x packets 0x%x time_us 0x%x type 0x%x\n",
+                  ctx_id, pkts, time, type);
+
+       return err;
 }
 
 static int qlcnic_sriov_pf_cfg_intrcoal_cmd(struct qlcnic_bc_trans *tran,
@@ -1214,7 +1256,6 @@ static int qlcnic_sriov_validate_cfg_macvlan(struct qlcnic_adapter *adapter,
                                             struct qlcnic_vf_info *vf,
                                             struct qlcnic_cmd_args *cmd)
 {
-       struct qlcnic_macvlan_mbx *macvlan;
        struct qlcnic_vport *vp = vf->vp;
        u8 op, new_op;
 
@@ -1224,14 +1265,6 @@ static int qlcnic_sriov_validate_cfg_macvlan(struct qlcnic_adapter *adapter,
        cmd->req.arg[1] |= (vf->vp->handle << 16);
        cmd->req.arg[1] |= BIT_31;
 
-       macvlan = (struct qlcnic_macvlan_mbx *)&cmd->req.arg[2];
-       if (!(macvlan->mac_addr0 & BIT_0)) {
-               dev_err(&adapter->pdev->dev,
-                       "MAC address change is not allowed from VF %d",
-                       vf->pci_func);
-               return -EINVAL;
-       }
-
        if (vp->vlan_mode == QLC_PVID_MODE) {
                op = cmd->req.arg[1] & 0x7;
                cmd->req.arg[1] &= ~0x7;
@@ -1815,7 +1848,8 @@ int qlcnic_sriov_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
        return 0;
 }
 
-int qlcnic_sriov_set_vf_tx_rate(struct net_device *netdev, int vf, int tx_rate)
+int qlcnic_sriov_set_vf_tx_rate(struct net_device *netdev, int vf,
+                               int min_tx_rate, int max_tx_rate)
 {
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
        struct qlcnic_sriov *sriov = adapter->ahw->sriov;
@@ -1830,35 +1864,52 @@ int qlcnic_sriov_set_vf_tx_rate(struct net_device *netdev, int vf, int tx_rate)
        if (vf >= sriov->num_vfs)
                return -EINVAL;
 
-       if (tx_rate >= 10000 || tx_rate < 100) {
+       vf_info = &sriov->vf_info[vf];
+       vp = vf_info->vp;
+       vpid = vp->handle;
+
+       if (!min_tx_rate)
+               min_tx_rate = QLC_VF_MIN_TX_RATE;
+
+       if (max_tx_rate &&
+           (max_tx_rate >= 10000 || max_tx_rate < min_tx_rate)) {
                netdev_err(netdev,
-                          "Invalid Tx rate, allowed range is [%d - %d]",
-                          QLC_VF_MIN_TX_RATE, QLC_VF_MAX_TX_RATE);
+                          "Invalid max Tx rate, allowed range is [%d - %d]",
+                          min_tx_rate, QLC_VF_MAX_TX_RATE);
                return -EINVAL;
        }
 
-       if (tx_rate == 0)
-               tx_rate = 10000;
+       if (!max_tx_rate)
+               max_tx_rate = 10000;
 
-       vf_info = &sriov->vf_info[vf];
-       vp = vf_info->vp;
-       vpid = vp->handle;
+       if (min_tx_rate &&
+           (min_tx_rate > max_tx_rate || min_tx_rate < QLC_VF_MIN_TX_RATE)) {
+               netdev_err(netdev,
+                          "Invalid min Tx rate, allowed range is [%d - %d]",
+                          QLC_VF_MIN_TX_RATE, max_tx_rate);
+               return -EINVAL;
+       }
 
        if (test_bit(QLC_BC_VF_STATE, &vf_info->state)) {
                if (qlcnic_sriov_get_vf_vport_info(adapter, &nic_info, vpid))
                        return -EIO;
 
-               nic_info.max_tx_bw = tx_rate / 100;
+               nic_info.max_tx_bw = max_tx_rate / 100;
+               nic_info.min_tx_bw = min_tx_rate / 100;
                nic_info.bit_offsets = BIT_0;
 
                if (qlcnic_sriov_pf_set_vport_info(adapter, &nic_info, vpid))
                        return -EIO;
        }
 
-       vp->max_tx_bw = tx_rate / 100;
+       vp->max_tx_bw = max_tx_rate / 100;
        netdev_info(netdev,
-                   "Setting Tx rate %d (Mbps), %d %% of PF bandwidth, for VF %d\n",
-                   tx_rate, vp->max_tx_bw, vf);
+                   "Setting Max Tx rate %d (Mbps), %d %% of PF bandwidth, for VF %d\n",
+                   max_tx_rate, vp->max_tx_bw, vf);
+       vp->min_tx_bw = min_tx_rate / 100;
+       netdev_info(netdev,
+                   "Setting Min Tx rate %d (Mbps), %d %% of PF bandwidth, for VF %d\n",
+                   min_tx_rate, vp->min_tx_bw, vf);
        return 0;
 }
 
@@ -1957,9 +2008,13 @@ int qlcnic_sriov_get_vf_config(struct net_device *netdev,
        ivi->qos = vp->qos;
        ivi->spoofchk = vp->spoofchk;
        if (vp->max_tx_bw == MAX_BW)
-               ivi->tx_rate = 0;
+               ivi->max_tx_rate = 0;
+       else
+               ivi->max_tx_rate = vp->max_tx_bw * 100;
+       if (vp->min_tx_bw == MIN_BW)
+               ivi->min_tx_rate = 0;
        else
-               ivi->tx_rate = vp->max_tx_bw * 100;
+               ivi->min_tx_rate = vp->min_tx_bw * 100;
 
        ivi->vf = vf;
        return 0;
index cd346e27f2e1270078a7580c5e563bc178d5ef37..f5786d5792df06fe16db6f7ffd2276f9bdabe96f 100644 (file)
 #include <linux/sysfs.h>
 #include <linux/aer.h>
 #include <linux/log2.h>
+#ifdef CONFIG_QLCNIC_HWMON
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#endif
 
 #define QLC_STATUS_UNSUPPORTED_CMD     -2
 
@@ -358,6 +362,8 @@ int qlcnic_is_valid_nic_func(struct qlcnic_adapter *adapter, u8 pci_func)
                if (adapter->npars[i].pci_func == pci_func)
                        return i;
        }
+
+       dev_err(&adapter->pdev->dev, "%s: Invalid nic function\n", __func__);
        return -EINVAL;
 }
 
@@ -1243,6 +1249,68 @@ static struct bin_attribute bin_attr_flash = {
        .write = qlcnic_83xx_sysfs_flash_write_handler,
 };
 
+#ifdef CONFIG_QLCNIC_HWMON
+
+static ssize_t qlcnic_hwmon_show_temp(struct device *dev,
+                                     struct device_attribute *dev_attr,
+                                     char *buf)
+{
+       struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
+       unsigned int temperature = 0, value = 0;
+
+       if (qlcnic_83xx_check(adapter))
+               value = QLCRDX(adapter->ahw, QLC_83XX_ASIC_TEMP);
+       else if (qlcnic_82xx_check(adapter))
+               value = QLC_SHARED_REG_RD32(adapter, QLCNIC_ASIC_TEMP);
+
+       temperature = qlcnic_get_temp_val(value);
+       /* display millidegree celcius */
+       temperature *= 1000;
+       return sprintf(buf, "%u\n", temperature);
+}
+
+/* hwmon-sysfs attributes */
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
+                         qlcnic_hwmon_show_temp, NULL, 1);
+
+static struct attribute *qlcnic_hwmon_attrs[] = {
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       NULL
+};
+
+ATTRIBUTE_GROUPS(qlcnic_hwmon);
+
+void qlcnic_register_hwmon_dev(struct qlcnic_adapter *adapter)
+{
+       struct device *dev = &adapter->pdev->dev;
+       struct device *hwmon_dev;
+
+       /* Skip hwmon registration for a VF device */
+       if (qlcnic_sriov_vf_check(adapter)) {
+               adapter->ahw->hwmon_dev = NULL;
+               return;
+       }
+       hwmon_dev = hwmon_device_register_with_groups(dev, qlcnic_driver_name,
+                                                     adapter,
+                                                     qlcnic_hwmon_groups);
+       if (IS_ERR(hwmon_dev)) {
+               dev_err(dev, "Cannot register with hwmon, err=%ld\n",
+                       PTR_ERR(hwmon_dev));
+               hwmon_dev = NULL;
+       }
+       adapter->ahw->hwmon_dev = hwmon_dev;
+}
+
+void qlcnic_unregister_hwmon_dev(struct qlcnic_adapter *adapter)
+{
+       struct device *hwmon_dev = adapter->ahw->hwmon_dev;
+       if (hwmon_dev) {
+               hwmon_device_unregister(hwmon_dev);
+               adapter->ahw->hwmon_dev = NULL;
+       }
+}
+#endif
+
 void qlcnic_create_sysfs_entries(struct qlcnic_adapter *adapter)
 {
        struct device *dev = &adapter->pdev->dev;
index 0a1d76acab8171929e3c6f9ad6139b48484ebbcc..b40050e03a56f7524e19dc7c215165d0a0c75585 100644 (file)
@@ -3595,7 +3595,7 @@ static int ql_request_irq(struct ql_adapter *qdev)
        }
        return status;
 err_irq:
-       netif_err(qdev, ifup, qdev->ndev, "Failed to get the interrupts!!!/n");
+       netif_err(qdev, ifup, qdev->ndev, "Failed to get the interrupts!!!\n");
        ql_free_irq(qdev);
        return status;
 }
@@ -4770,7 +4770,7 @@ static int qlge_probe(struct pci_dev *pdev,
        ndev->irq = pdev->irq;
 
        ndev->netdev_ops = &qlge_netdev_ops;
-       SET_ETHTOOL_OPS(ndev, &qlge_ethtool_ops);
+       ndev->ethtool_ops = &qlge_ethtool_ops;
        ndev->watchdog_timeo = 10 * HZ;
 
        err = register_netdev(ndev);
index aa1c079f231dc6f2cfc017cd4950f9ebb8e1e9da..be425ad5e82487e94a91b7371466f7f93ab558eb 100644 (file)
@@ -7125,7 +7125,7 @@ rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        for (i = 0; i < ETH_ALEN; i++)
                dev->dev_addr[i] = RTL_R8(MAC0 + i);
 
-       SET_ETHTOOL_OPS(dev, &rtl8169_ethtool_ops);
+       dev->ethtool_ops = &rtl8169_ethtool_ops;
        dev->watchdog_timeo = RTL8169_TX_TIMEOUT;
 
        netif_napi_add(dev, &tp->napi, rtl8169_poll, R8169_NAPI_WEIGHT);
index 6a9509ccd33b29dd84ddcd0b48269f1c24b8da63..7622213beef167e04a3a837c63c890935a8d703b 100644 (file)
@@ -307,6 +307,27 @@ static const u16 sh_eth_offset_fast_sh4[SH_ETH_MAX_REGISTER_OFFSET] = {
 };
 
 static const u16 sh_eth_offset_fast_sh3_sh2[SH_ETH_MAX_REGISTER_OFFSET] = {
+       [EDMR]          = 0x0000,
+       [EDTRR]         = 0x0004,
+       [EDRRR]         = 0x0008,
+       [TDLAR]         = 0x000c,
+       [RDLAR]         = 0x0010,
+       [EESR]          = 0x0014,
+       [EESIPR]        = 0x0018,
+       [TRSCER]        = 0x001c,
+       [RMFCR]         = 0x0020,
+       [TFTR]          = 0x0024,
+       [FDR]           = 0x0028,
+       [RMCR]          = 0x002c,
+       [EDOCR]         = 0x0030,
+       [FCFTR]         = 0x0034,
+       [RPADIR]        = 0x0038,
+       [TRIMD]         = 0x003c,
+       [RBWAR]         = 0x0040,
+       [RDFAR]         = 0x0044,
+       [TBRAR]         = 0x004c,
+       [TDFAR]         = 0x0050,
+
        [ECMR]          = 0x0160,
        [ECSR]          = 0x0164,
        [ECSIPR]        = 0x0168,
@@ -546,7 +567,6 @@ 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     = 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 |
@@ -624,7 +644,6 @@ 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     = RMCR_RNC,
 
        .irq_flags      = IRQF_SHARED,
        .apr            = 1,
@@ -752,7 +771,6 @@ static struct sh_eth_cpu_data r8a7740_data = {
                          EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE |
                          EESR_TDE | EESR_ECI,
        .fdr_value      = 0x0000070f,
-       .rmcr_value     = RMCR_RNC,
 
        .apr            = 1,
        .mpr            = 1,
@@ -784,7 +802,6 @@ static struct sh_eth_cpu_data r7s72100_data = {
                          EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE |
                          EESR_TDE | EESR_ECI,
        .fdr_value      = 0x0000070f,
-       .rmcr_value     = RMCR_RNC,
 
        .no_psr         = 1,
        .apr            = 1,
@@ -833,9 +850,6 @@ static void sh_eth_set_default_cpu_data(struct sh_eth_cpu_data *cd)
        if (!cd->fdr_value)
                cd->fdr_value = DEFAULT_FDR_INIT;
 
-       if (!cd->rmcr_value)
-               cd->rmcr_value = DEFAULT_RMCR_VALUE;
-
        if (!cd->tx_check)
                cd->tx_check = DEFAULT_TX_CHECK;
 
@@ -1287,8 +1301,8 @@ static int sh_eth_dev_init(struct net_device *ndev, bool start)
        sh_eth_write(ndev, mdp->cd->fdr_value, FDR);
        sh_eth_write(ndev, 0, TFTR);
 
-       /* Frame recv control */
-       sh_eth_write(ndev, mdp->cd->rmcr_value, RMCR);
+       /* Frame recv control (enable multiple-packets per rx irq) */
+       sh_eth_write(ndev, RMCR_RNC, RMCR);
 
        sh_eth_write(ndev, DESC_I_RINT8 | DESC_I_RINT5 | DESC_I_TINT2, TRSCER);
 
@@ -1385,7 +1399,6 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
        int entry = mdp->cur_rx % mdp->num_rx_ring;
        int boguscnt = (mdp->dirty_rx + mdp->num_rx_ring) - mdp->cur_rx;
        struct sk_buff *skb;
-       int exceeded = 0;
        u16 pkt_len = 0;
        u32 desc_status;
 
@@ -1397,10 +1410,9 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
                if (--boguscnt < 0)
                        break;
 
-               if (*quota <= 0) {
-                       exceeded = 1;
+               if (*quota <= 0)
                        break;
-               }
+
                (*quota)--;
 
                if (!(desc_status & RDFEND))
@@ -1448,7 +1460,6 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
                        ndev->stats.rx_packets++;
                        ndev->stats.rx_bytes += pkt_len;
                }
-               rxdesc->status |= cpu_to_edmac(mdp, RD_RACT);
                entry = (++mdp->cur_rx) % mdp->num_rx_ring;
                rxdesc = &mdp->rx_ring[entry];
        }
@@ -1494,7 +1505,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
                sh_eth_write(ndev, EDRRR_R, EDRRR);
        }
 
-       return exceeded;
+       return *quota <= 0;
 }
 
 static void sh_eth_rcv_snd_disable(struct net_device *ndev)
@@ -2627,8 +2638,8 @@ static int sh_mdio_init(struct sh_eth_private *mdp,
                 pdev->name, pdev->id);
 
        /* PHY IRQ */
-       mdp->mii_bus->irq = devm_kzalloc(dev, sizeof(int) * PHY_MAX_ADDR,
-                                        GFP_KERNEL);
+       mdp->mii_bus->irq = devm_kmalloc_array(dev, PHY_MAX_ADDR, sizeof(int),
+                                              GFP_KERNEL);
        if (!mdp->mii_bus->irq) {
                ret = -ENOMEM;
                goto out_free_bus;
@@ -2843,7 +2854,7 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
                ndev->netdev_ops = &sh_eth_netdev_ops_tsu;
        else
                ndev->netdev_ops = &sh_eth_netdev_ops;
-       SET_ETHTOOL_OPS(ndev, &sh_eth_ethtool_ops);
+       ndev->ethtool_ops = &sh_eth_ethtool_ops;
        ndev->watchdog_timeo = TX_TIMEOUT;
 
        /* debug message level */
index d55e37cd5fec04abcd6bc4e52827351e74d5fbdb..b37c427144ee0a1010794ff2beff42bc298fd1e9 100644 (file)
@@ -319,7 +319,6 @@ enum TD_STS_BIT {
 enum RMCR_BIT {
        RMCR_RNC = 0x00000001,
 };
-#define DEFAULT_RMCR_VALUE     0x00000000
 
 /* ECMR */
 enum FELIC_MODE_BIT {
@@ -466,7 +465,6 @@ struct sh_eth_cpu_data {
        unsigned long fdr_value;
        unsigned long fcftr_value;
        unsigned long rpadir_value;
-       unsigned long rmcr_value;
 
        /* interrupt checking mask */
        unsigned long tx_check;
index 0415fa50eeb77b82376214708419f6e4186e876f..c0981ae45874acc5c247a2b37d84a90296e1196b 100644 (file)
@@ -520,5 +520,5 @@ static const struct ethtool_ops sxgbe_ethtool_ops = {
 
 void sxgbe_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &sxgbe_ethtool_ops);
+       netdev->ethtool_ops = &sxgbe_ethtool_ops;
 }
index 82a9a983869fe6ae711c5b4503941611f1f32657..698494481d18072ca00ddecb825ea92bd4e86c56 100644 (file)
@@ -425,8 +425,8 @@ static int init_tx_ring(struct device *dev, u8 queue_no,
  * @rx_rsize: ring size
  * Description:  this function initializes the DMA RX descriptor
  */
-void free_rx_ring(struct device *dev, struct sxgbe_rx_queue *rx_ring,
-                 int rx_rsize)
+static void free_rx_ring(struct device *dev, struct sxgbe_rx_queue *rx_ring,
+                        int rx_rsize)
 {
        dma_free_coherent(dev, rx_rsize * sizeof(struct sxgbe_rx_norm_desc),
                          rx_ring->dma_rx, rx_ring->dma_rx_phy);
@@ -519,8 +519,8 @@ static int init_rx_ring(struct net_device *dev, u8 queue_no,
  * @tx_rsize: ring size
  * Description:  this function initializes the DMA TX descriptor
  */
-void free_tx_ring(struct device *dev, struct sxgbe_tx_queue *tx_ring,
-                 int tx_rsize)
+static void free_tx_ring(struct device *dev, struct sxgbe_tx_queue *tx_ring,
+                        int tx_rsize)
 {
        dma_free_coherent(dev, tx_rsize * sizeof(struct sxgbe_tx_norm_desc),
                          tx_ring->dma_tx, tx_ring->dma_tx_phy);
@@ -1221,11 +1221,10 @@ static int sxgbe_release(struct net_device *dev)
 
        return 0;
 }
-
 /* Prepare first Tx descriptor for doing TSO operation */
-void sxgbe_tso_prepare(struct sxgbe_priv_data *priv,
-                      struct sxgbe_tx_norm_desc *first_desc,
-                      struct sk_buff *skb)
+static void sxgbe_tso_prepare(struct sxgbe_priv_data *priv,
+                             struct sxgbe_tx_norm_desc *first_desc,
+                             struct sk_buff *skb)
 {
        unsigned int total_hdr_len, tcp_hdr_len;
 
@@ -1914,40 +1913,6 @@ static void sxgbe_set_rx_mode(struct net_device *dev)
                   readl(ioaddr + SXGBE_HASH_LOW));
 }
 
-/**
- * sxgbe_config - entry point for changing configuration mode passed on by
- * ifconfig
- * @dev : pointer to the device structure
- * @map : pointer to the device mapping structure
- * Description:
- * This function is a driver entry point which gets called by the kernel
- * whenever some device configuration is changed.
- * Return value:
- * This function returns 0 if success and appropriate error otherwise.
- */
-static int sxgbe_config(struct net_device *dev, struct ifmap *map)
-{
-       struct sxgbe_priv_data *priv = netdev_priv(dev);
-
-       /* Can't act on a running interface */
-       if (dev->flags & IFF_UP)
-               return -EBUSY;
-
-       /* Don't allow changing the I/O address */
-       if (map->base_addr != (unsigned long)priv->ioaddr) {
-               netdev_warn(dev, "can't change I/O address\n");
-               return -EOPNOTSUPP;
-       }
-
-       /* Don't allow changing the IRQ */
-       if (map->irq != priv->irq) {
-               netdev_warn(dev, "not change IRQ number %d\n", priv->irq);
-               return -EOPNOTSUPP;
-       }
-
-       return 0;
-}
-
 #ifdef CONFIG_NET_POLL_CONTROLLER
 /**
  * sxgbe_poll_controller - entry point for polling receive by device
@@ -2009,7 +1974,6 @@ static const struct net_device_ops sxgbe_netdev_ops = {
        .ndo_set_rx_mode        = sxgbe_set_rx_mode,
        .ndo_tx_timeout         = sxgbe_tx_timeout,
        .ndo_do_ioctl           = sxgbe_ioctl,
-       .ndo_set_config         = sxgbe_config,
 #ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller    = sxgbe_poll_controller,
 #endif
index 56f8bf5a3f1b99564a2b055830810fe3d156e6c4..81437d91df99ded30f4d34594b03126ef034d42d 100644 (file)
 
 /* L3/L4 function registers */
 #define SXGBE_CORE_L34_ADDCTL_REG      0x0C00
-#define SXGBE_CORE_L34_ADDCTL_REG      0x0C00
 #define SXGBE_CORE_L34_DATA_REG                0x0C04
 
 /* ARP registers */
index 63d595fd3cc5f5a9df298dfdd2583abcec9a3a03..1e274045970fa011c6dacb6baeb77db95178b164 100644 (file)
@@ -2248,7 +2248,7 @@ static int efx_register_netdev(struct efx_nic *efx)
        } else {
                net_dev->netdev_ops = &efx_farch_netdev_ops;
        }
-       SET_ETHTOOL_OPS(net_dev, &efx_ethtool_ops);
+       net_dev->ethtool_ops = &efx_ethtool_ops;
        net_dev->gso_max_segs = EFX_TSO_MAX_SEGS;
 
        rtnl_lock();
index 0de8b07c24c2bf39cd62a06021a42c217a3cba4c..74739c4b9997e433b69571f99886290abed49098 100644 (file)
@@ -1033,7 +1033,7 @@ static u32 efx_ethtool_get_rxfh_indir_size(struct net_device *net_dev)
                0 : ARRAY_SIZE(efx->rx_indir_table));
 }
 
-static int efx_ethtool_get_rxfh_indir(struct net_device *net_dev, u32 *indir)
+static int efx_ethtool_get_rxfh(struct net_device *net_dev, u32 *indir, u8 *key)
 {
        struct efx_nic *efx = netdev_priv(net_dev);
 
@@ -1041,8 +1041,8 @@ static int efx_ethtool_get_rxfh_indir(struct net_device *net_dev, u32 *indir)
        return 0;
 }
 
-static int efx_ethtool_set_rxfh_indir(struct net_device *net_dev,
-                                     const u32 *indir)
+static int efx_ethtool_set_rxfh(struct net_device *net_dev,
+                               const u32 *indir, const u8 *key)
 {
        struct efx_nic *efx = netdev_priv(net_dev);
 
@@ -1125,8 +1125,8 @@ const struct ethtool_ops efx_ethtool_ops = {
        .get_rxnfc              = efx_ethtool_get_rxnfc,
        .set_rxnfc              = efx_ethtool_set_rxnfc,
        .get_rxfh_indir_size    = efx_ethtool_get_rxfh_indir_size,
-       .get_rxfh_indir         = efx_ethtool_get_rxfh_indir,
-       .set_rxfh_indir         = efx_ethtool_set_rxfh_indir,
+       .get_rxfh               = efx_ethtool_get_rxfh,
+       .set_rxfh               = efx_ethtool_set_rxfh,
        .get_ts_info            = efx_ethtool_get_ts_info,
        .get_module_info        = efx_ethtool_get_module_info,
        .get_module_eeprom      = efx_ethtool_get_module_eeprom,
index 4d3f119b67b38ec35719ebac40d59f49ea9b844f..afb94aa2c15e9f8eb8ec5ebfc8a02ef8f8bc505e 100644 (file)
 #define EFX_USE_QWORD_IO 1
 #endif
 
+/* Hardware issue requires that only 64-bit naturally aligned writes
+ * are seen by hardware. Its not strictly necessary to restrict to
+ * x86_64 arch, but done for safety since unusual write combining behaviour
+ * can break PIO.
+ */
+#ifdef CONFIG_X86_64
 /* PIO is a win only if write-combining is possible */
 #ifdef ARCH_HAS_IOREMAP_WC
 #define EFX_USE_PIO 1
 #endif
+#endif
 
 #ifdef EFX_USE_QWORD_IO
 static inline void _efx_writeq(struct efx_nic *efx, __le64 value,
index 9a9205e778964186d09094128a933179926f8006..43d2e64546ed1d924c91d26900b37d1b100dd994 100644 (file)
@@ -1633,7 +1633,8 @@ int efx_sriov_get_vf_config(struct net_device *net_dev, int vf_i,
 
        ivi->vf = vf_i;
        ether_addr_copy(ivi->mac, vf->addr.mac_addr);
-       ivi->tx_rate = 0;
+       ivi->max_tx_rate = 0;
+       ivi->min_tx_rate = 0;
        tci = ntohs(vf->addr.tci);
        ivi->vlan = tci & VLAN_VID_MASK;
        ivi->qos = (tci >> VLAN_PRIO_SHIFT) & 0x7;
index fa9475300411507e447fd51e21dfbd4c5f600aa3..ede8dcca0ff3516c33a2aee884952b7514924676 100644 (file)
@@ -189,6 +189,18 @@ struct efx_short_copy_buffer {
        u8 buf[L1_CACHE_BYTES];
 };
 
+/* Copy in explicit 64-bit writes. */
+static void efx_memcpy_64(void __iomem *dest, void *src, size_t len)
+{
+       u64 *src64 = src;
+       u64 __iomem *dest64 = dest;
+       size_t l64 = len / 8;
+       size_t i;
+
+       for (i = 0; i < l64; i++)
+               writeq(src64[i], &dest64[i]);
+}
+
 /* Copy to PIO, respecting that writes to PIO buffers must be dword aligned.
  * Advances piobuf pointer. Leaves additional data in the copy buffer.
  */
@@ -198,7 +210,7 @@ static void efx_memcpy_toio_aligned(struct efx_nic *efx, u8 __iomem **piobuf,
 {
        int block_len = len & ~(sizeof(copy_buf->buf) - 1);
 
-       memcpy_toio(*piobuf, data, block_len);
+       efx_memcpy_64(*piobuf, data, block_len);
        *piobuf += block_len;
        len -= block_len;
 
@@ -230,7 +242,7 @@ static void efx_memcpy_toio_aligned_cb(struct efx_nic *efx, u8 __iomem **piobuf,
                if (copy_buf->used < sizeof(copy_buf->buf))
                        return;
 
-               memcpy_toio(*piobuf, copy_buf->buf, sizeof(copy_buf->buf));
+               efx_memcpy_64(*piobuf, copy_buf->buf, sizeof(copy_buf->buf));
                *piobuf += sizeof(copy_buf->buf);
                data += copy_to_buf;
                len -= copy_to_buf;
@@ -245,7 +257,7 @@ static void efx_flush_copy_buffer(struct efx_nic *efx, u8 __iomem *piobuf,
 {
        /* 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));
+               efx_memcpy_64(piobuf, copy_buf->buf, sizeof(copy_buf->buf));
 }
 
 /* Traverse skb structure and copy fragments in to PIO buffer.
@@ -304,8 +316,8 @@ efx_enqueue_skb_pio(struct efx_tx_queue *tx_queue, struct sk_buff *skb)
                 */
                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_memcpy_64(tx_queue->piobuf, skb->data,
+                             ALIGN(skb->len, L1_CACHE_BYTES));
        }
 
        EFX_POPULATE_QWORD_5(buffer->option,
index acbbe48a519c0c673ff8de55c31b1690c3976cb5..a86339903b9b0662a835df431f2b79d0677aa93f 100644 (file)
@@ -1877,7 +1877,7 @@ static int sis190_init_one(struct pci_dev *pdev,
 
        dev->netdev_ops = &sis190_netdev_ops;
 
-       SET_ETHTOOL_OPS(dev, &sis190_ethtool_ops);
+       dev->ethtool_ops = &sis190_ethtool_ops;
        dev->watchdog_timeo = SIS190_TX_TIMEOUT;
 
        spin_lock_init(&tp->lock);
index c7a4868571f9f81f1607dc50225dec8bdaf6f017..6b33127ab352a43ed6a787af7eedde554241e1b3 100644 (file)
@@ -318,7 +318,7 @@ static int smc91c92_probe(struct pcmcia_device *link)
 
     /* The SMC91c92-specific entries in the device structure. */
     dev->netdev_ops = &smc_netdev_ops;
-    SET_ETHTOOL_OPS(dev, &ethtool_ops);
+    dev->ethtool_ops = &ethtool_ops;
     dev->watchdog_timeo = TX_TIMEOUT;
 
     smc->mii_if.dev = dev;
index a0fc151da40db13c20681e04c07d5e923566a14b..5e13fa5524ae911f5516f060a5acdb729fec7be0 100644 (file)
@@ -2477,6 +2477,8 @@ static int smsc911x_drv_probe(struct platform_device *pdev)
                goto out_disable_resources;
        }
 
+       netif_carrier_off(dev);
+
        retval = register_netdev(dev);
        if (retval) {
                SMSC_WARN(pdata, probe, "Error %i registering device", retval);
index c5f9cb85c8ef9c31c2ce05894faffa2a484a8093..c62e67f3c2f0692f85b39c42a69de3fc5ed37b22 100644 (file)
@@ -322,9 +322,7 @@ static int stmmac_ethtool_getsettings(struct net_device *dev,
                return -EBUSY;
        }
        cmd->transceiver = XCVR_INTERNAL;
-       spin_lock_irq(&priv->lock);
        rc = phy_ethtool_gset(phy, cmd);
-       spin_unlock_irq(&priv->lock);
        return rc;
 }
 
@@ -431,8 +429,6 @@ stmmac_get_pauseparam(struct net_device *netdev,
        if (priv->pcs)  /* FIXME */
                return;
 
-       spin_lock(&priv->lock);
-
        pause->rx_pause = 0;
        pause->tx_pause = 0;
        pause->autoneg = priv->phydev->autoneg;
@@ -442,7 +438,6 @@ stmmac_get_pauseparam(struct net_device *netdev,
        if (priv->flow_ctrl & FLOW_TX)
                pause->tx_pause = 1;
 
-       spin_unlock(&priv->lock);
 }
 
 static int
@@ -457,8 +452,6 @@ stmmac_set_pauseparam(struct net_device *netdev,
        if (priv->pcs)  /* FIXME */
                return -EOPNOTSUPP;
 
-       spin_lock(&priv->lock);
-
        if (pause->rx_pause)
                new_pause |= FLOW_RX;
        if (pause->tx_pause)
@@ -473,7 +466,6 @@ stmmac_set_pauseparam(struct net_device *netdev,
        } else
                priv->hw->mac->flow_ctrl(priv->ioaddr, phy->duplex,
                                         priv->flow_ctrl, priv->pause);
-       spin_unlock(&priv->lock);
        return ret;
 }
 
@@ -784,5 +776,5 @@ static const struct ethtool_ops stmmac_ethtool_ops = {
 
 void stmmac_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &stmmac_ethtool_ops);
+       netdev->ethtool_ops = &stmmac_ethtool_ops;
 }
index 0f4841d2e8dc9b556bde072a1786697ea7041460..057a1208e5947f9c6ae473c0ab2d6d978091fff7 100644 (file)
@@ -1753,7 +1753,7 @@ static int stmmac_open(struct net_device *dev)
        }
 
        /* Request the IRQ lines */
-       if (priv->lpi_irq != -ENXIO) {
+       if (priv->lpi_irq > 0) {
                ret = request_irq(priv->lpi_irq, stmmac_interrupt, IRQF_SHARED,
                                  dev->name, dev);
                if (unlikely(ret < 0)) {
@@ -1813,7 +1813,7 @@ static int stmmac_release(struct net_device *dev)
        free_irq(dev->irq, dev);
        if (priv->wol_irq != dev->irq)
                free_irq(priv->wol_irq, dev);
-       if (priv->lpi_irq != -ENXIO)
+       if (priv->lpi_irq > 0)
                free_irq(priv->lpi_irq, dev);
 
        /* Stop TX/RX DMA and clear the descriptors */
@@ -2212,27 +2212,6 @@ static void stmmac_tx_timeout(struct net_device *dev)
        stmmac_tx_err(priv);
 }
 
-/* Configuration changes (passed on by ifconfig) */
-static int stmmac_config(struct net_device *dev, struct ifmap *map)
-{
-       if (dev->flags & IFF_UP)        /* can't act on a running interface */
-               return -EBUSY;
-
-       /* Don't allow changing the I/O address */
-       if (map->base_addr != dev->base_addr) {
-               pr_warn("%s: can't change I/O address\n", dev->name);
-               return -EOPNOTSUPP;
-       }
-
-       /* Don't allow changing the IRQ */
-       if (map->irq != dev->irq) {
-               pr_warn("%s: not change IRQ number %d\n", dev->name, dev->irq);
-               return -EOPNOTSUPP;
-       }
-
-       return 0;
-}
-
 /**
  *  stmmac_set_rx_mode - entry point for multicast addressing
  *  @dev : pointer to the device structure
@@ -2598,7 +2577,6 @@ static const struct net_device_ops stmmac_netdev_ops = {
        .ndo_set_rx_mode = stmmac_set_rx_mode,
        .ndo_tx_timeout = stmmac_tx_timeout,
        .ndo_do_ioctl = stmmac_ioctl,
-       .ndo_set_config = stmmac_config,
 #ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller = stmmac_poll_controller,
 #endif
index a468eb10782361e31fd0b0af5ce6be33d68a2578..a5b1e1b776fe3313c5c9852062ed66ea1547c285 100644 (file)
@@ -205,10 +205,13 @@ int stmmac_mdio_register(struct net_device *ndev)
        if (new_bus == NULL)
                return -ENOMEM;
 
-       if (mdio_bus_data->irqs)
+       if (mdio_bus_data->irqs) {
                irqlist = mdio_bus_data->irqs;
-       else
+       } else {
+               for (addr = 0; addr < PHY_MAX_ADDR; addr++)
+                       priv->mii_irq[addr] = PHY_POLL;
                irqlist = priv->mii_irq;
+       }
 
 #ifdef CONFIG_OF
        if (priv->device->of_node)
index 46aef5108bea47c7d6e49b891937d05ac2610c85..ea7a65be1f9af4208748d808128a344bfdb74377 100644 (file)
@@ -237,10 +237,12 @@ static int stmmac_pltfr_probe(struct platform_device *pdev)
 
        /* Get the MAC information */
        priv->dev->irq = platform_get_irq_byname(pdev, "macirq");
-       if (priv->dev->irq == -ENXIO) {
-               pr_err("%s: ERROR: MAC IRQ configuration "
-                      "information not found\n", __func__);
-               return -ENXIO;
+       if (priv->dev->irq < 0) {
+               if (priv->dev->irq != -EPROBE_DEFER) {
+                       netdev_err(priv->dev,
+                                  "MAC IRQ configuration information not found\n");
+               }
+               return priv->dev->irq;
        }
 
        /*
@@ -252,10 +254,15 @@ static int stmmac_pltfr_probe(struct platform_device *pdev)
         * so the driver will continue to use the mac irq (ndev->irq)
         */
        priv->wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq");
-       if (priv->wol_irq == -ENXIO)
+       if (priv->wol_irq < 0) {
+               if (priv->wol_irq == -EPROBE_DEFER)
+                       return -EPROBE_DEFER;
                priv->wol_irq = priv->dev->irq;
+       }
 
        priv->lpi_irq = platform_get_irq_byname(pdev, "eth_lpi");
+       if (priv->lpi_irq == -EPROBE_DEFER)
+               return -EPROBE_DEFER;
 
        platform_set_drvdata(pdev, priv->dev);
 
index 2ead87759ab411819be6cb03fa8de3cef8ea2a1f..38da73a2a886b52b3753d3b9dd31f214d345e4fd 100644 (file)
@@ -2413,7 +2413,7 @@ static void bdx_set_ethtool_ops(struct net_device *netdev)
                .get_ethtool_stats = bdx_get_ethtool_stats,
        };
 
-       SET_ETHTOOL_OPS(netdev, &bdx_ethtool_ops);
+       netdev->ethtool_ops = &bdx_ethtool_ops;
 }
 
 /**
index 73f74f369437174144b59a8f1426cbbcbaf21c34..7399a52f7c260aa7a037cbe134b35ca82b6cf840 100644 (file)
@@ -313,19 +313,6 @@ static int mii_irqs[PHY_MAX_ADDR] = { PHY_POLL, };
 
 static struct mii_bus *cpmac_mii;
 
-static int cpmac_config(struct net_device *dev, struct ifmap *map)
-{
-       if (dev->flags & IFF_UP)
-               return -EBUSY;
-
-       /* Don't allow changing the I/O address */
-       if (map->base_addr != dev->base_addr)
-               return -EOPNOTSUPP;
-
-       /* ignore other fields */
-       return 0;
-}
-
 static void cpmac_set_multicast_list(struct net_device *dev)
 {
        struct netdev_hw_addr *ha;
@@ -1100,7 +1087,6 @@ static const struct net_device_ops cpmac_netdev_ops = {
        .ndo_tx_timeout         = cpmac_tx_timeout,
        .ndo_set_rx_mode        = cpmac_set_multicast_list,
        .ndo_do_ioctl           = cpmac_ioctl,
-       .ndo_set_config         = cpmac_config,
        .ndo_change_mtu         = eth_change_mtu,
        .ndo_validate_addr      = eth_validate_addr,
        .ndo_set_mac_address    = eth_mac_addr,
index 148da9ae83666ce7cd2284c1cc75f18e9957f15f..aa8bf45e53dc9b0ddab4265b2e527067339a00fc 100644 (file)
@@ -29,6 +29,8 @@
 #define AM33XX_GMII_SEL_RMII2_IO_CLK_EN        BIT(7)
 #define AM33XX_GMII_SEL_RMII1_IO_CLK_EN        BIT(6)
 
+#define GMII_SEL_MODE_MASK             0x3
+
 struct cpsw_phy_sel_priv {
        struct device   *dev;
        u32 __iomem     *gmii_sel;
@@ -65,7 +67,7 @@ static void cpsw_gmii_sel_am3352(struct cpsw_phy_sel_priv *priv,
                break;
        };
 
-       mask = 0x3 << (slave * 2) | BIT(slave + 6);
+       mask = GMII_SEL_MODE_MASK << (slave * 2) | BIT(slave + 6);
        mode <<= slave * 2;
 
        if (priv->rmii_clock_external) {
@@ -81,6 +83,55 @@ static void cpsw_gmii_sel_am3352(struct cpsw_phy_sel_priv *priv,
        writel(reg, priv->gmii_sel);
 }
 
+static void cpsw_gmii_sel_dra7xx(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;
+       };
+
+       switch (slave) {
+       case 0:
+               mask = GMII_SEL_MODE_MASK;
+               break;
+       case 1:
+               mask = GMII_SEL_MODE_MASK << 4;
+               mode <<= 4;
+               break;
+       default:
+               dev_err(priv->dev, "invalid slave number...\n");
+               return;
+       }
+
+       if (priv->rmii_clock_external)
+               dev_err(priv->dev, "RMII External clock is not supported\n");
+
+       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)
 {
@@ -112,6 +163,14 @@ static const struct of_device_id cpsw_phy_sel_id_table[] = {
                .compatible     = "ti,am3352-cpsw-phy-sel",
                .data           = &cpsw_gmii_sel_am3352,
        },
+       {
+               .compatible     = "ti,dra7xx-cpsw-phy-sel",
+               .data           = &cpsw_gmii_sel_dra7xx,
+       },
+       {
+               .compatible     = "ti,am43xx-cpsw-phy-sel",
+               .data           = &cpsw_gmii_sel_am3352,
+       },
        {}
 };
 MODULE_DEVICE_TABLE(of, cpsw_phy_sel_id_table);
@@ -132,6 +191,7 @@ static int cpsw_phy_sel_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
+       priv->dev = &pdev->dev;
        priv->cpsw_phy_sel = of_id->data;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gmii-sel");
index c331b7ebc8124585f989d6fb3dcdbaf8a3d7d3ed..ff380dac6629d16f4d3642f9ed6789ec2da7f2ab 100644 (file)
@@ -143,13 +143,13 @@ do {                                                              \
                u32 i;          \
                for (i = 0; i < priv->num_irqs; i++) \
                        enable_irq(priv->irqs_table[i]); \
-       } while (0);
+       } while (0)
 #define cpsw_disable_irq(priv) \
        do {                    \
                u32 i;          \
                for (i = 0; i < priv->num_irqs; i++) \
                        disable_irq_nosync(priv->irqs_table[i]); \
-       } while (0);
+       } while (0)
 
 #define cpsw_slave_index(priv)                         \
                ((priv->data.dual_emac) ? priv->emac_port :     \
@@ -248,20 +248,31 @@ struct cpsw_ss_regs {
 #define TS_131              (1<<11) /* Time Sync Dest IP Addr 131 enable */
 #define TS_130              (1<<10) /* Time Sync Dest IP Addr 130 enable */
 #define TS_129              (1<<9)  /* Time Sync Dest IP Addr 129 enable */
-#define TS_BIT8             (1<<8)  /* ts_ttl_nonzero? */
+#define TS_TTL_NONZERO      (1<<8)  /* Time Sync Time To Live Non-zero enable */
+#define TS_ANNEX_F_EN       (1<<6)  /* Time Sync Annex F enable */
 #define TS_ANNEX_D_EN       (1<<4)  /* Time Sync Annex D enable */
 #define TS_LTYPE2_EN        (1<<3)  /* Time Sync LTYPE 2 enable */
 #define TS_LTYPE1_EN        (1<<2)  /* Time Sync LTYPE 1 enable */
 #define TS_TX_EN            (1<<1)  /* Time Sync Transmit Enable */
 #define TS_RX_EN            (1<<0)  /* Time Sync Receive Enable */
 
-#define CTRL_TS_BITS \
-       (TS_320 | TS_319 | TS_132 | TS_131 | TS_130 | TS_129 | TS_BIT8 | \
-        TS_ANNEX_D_EN | TS_LTYPE1_EN)
+#define CTRL_V2_TS_BITS \
+       (TS_320 | TS_319 | TS_132 | TS_131 | TS_130 | TS_129 |\
+        TS_TTL_NONZERO  | TS_ANNEX_D_EN | TS_LTYPE1_EN)
+
+#define CTRL_V2_ALL_TS_MASK (CTRL_V2_TS_BITS | TS_TX_EN | TS_RX_EN)
+#define CTRL_V2_TX_TS_BITS  (CTRL_V2_TS_BITS | TS_TX_EN)
+#define CTRL_V2_RX_TS_BITS  (CTRL_V2_TS_BITS | TS_RX_EN)
+
 
-#define CTRL_ALL_TS_MASK (CTRL_TS_BITS | TS_TX_EN | TS_RX_EN)
-#define CTRL_TX_TS_BITS  (CTRL_TS_BITS | TS_TX_EN)
-#define CTRL_RX_TS_BITS  (CTRL_TS_BITS | TS_RX_EN)
+#define CTRL_V3_TS_BITS \
+       (TS_320 | TS_319 | TS_132 | TS_131 | TS_130 | TS_129 |\
+        TS_TTL_NONZERO | TS_ANNEX_F_EN | TS_ANNEX_D_EN |\
+        TS_LTYPE1_EN)
+
+#define CTRL_V3_ALL_TS_MASK (CTRL_V3_TS_BITS | TS_TX_EN | TS_RX_EN)
+#define CTRL_V3_TX_TS_BITS  (CTRL_V3_TS_BITS | TS_TX_EN)
+#define CTRL_V3_RX_TS_BITS  (CTRL_V3_TS_BITS | TS_RX_EN)
 
 /* Bit definitions for the CPSW2_TS_SEQ_MTYPE register */
 #define TS_SEQ_ID_OFFSET_SHIFT   (16)    /* Time Sync Sequence ID Offset */
@@ -1376,13 +1387,27 @@ static void cpsw_hwtstamp_v2(struct cpsw_priv *priv)
                slave = &priv->slaves[priv->data.active_slave];
 
        ctrl = slave_read(slave, CPSW2_CONTROL);
-       ctrl &= ~CTRL_ALL_TS_MASK;
+       switch (priv->version) {
+       case CPSW_VERSION_2:
+               ctrl &= ~CTRL_V2_ALL_TS_MASK;
 
-       if (priv->cpts->tx_enable)
-               ctrl |= CTRL_TX_TS_BITS;
+               if (priv->cpts->tx_enable)
+                       ctrl |= CTRL_V2_TX_TS_BITS;
 
-       if (priv->cpts->rx_enable)
-               ctrl |= CTRL_RX_TS_BITS;
+               if (priv->cpts->rx_enable)
+                       ctrl |= CTRL_V2_RX_TS_BITS;
+       break;
+       case CPSW_VERSION_3:
+       default:
+               ctrl &= ~CTRL_V3_ALL_TS_MASK;
+
+               if (priv->cpts->tx_enable)
+                       ctrl |= CTRL_V3_TX_TS_BITS;
+
+               if (priv->cpts->rx_enable)
+                       ctrl |= CTRL_V3_RX_TS_BITS;
+       break;
+       }
 
        mtype = (30 << TS_SEQ_ID_OFFSET_SHIFT) | EVENT_MSG_BITS;
 
@@ -1398,7 +1423,8 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
        struct hwtstamp_config cfg;
 
        if (priv->version != CPSW_VERSION_1 &&
-           priv->version != CPSW_VERSION_2)
+           priv->version != CPSW_VERSION_2 &&
+           priv->version != CPSW_VERSION_3)
                return -EOPNOTSUPP;
 
        if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
@@ -1443,6 +1469,7 @@ static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
                cpsw_hwtstamp_v1(priv);
                break;
        case CPSW_VERSION_2:
+       case CPSW_VERSION_3:
                cpsw_hwtstamp_v2(priv);
                break;
        default:
@@ -1459,7 +1486,8 @@ static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
        struct hwtstamp_config cfg;
 
        if (priv->version != CPSW_VERSION_1 &&
-           priv->version != CPSW_VERSION_2)
+           priv->version != CPSW_VERSION_2 &&
+           priv->version != CPSW_VERSION_3)
                return -EOPNOTSUPP;
 
        cfg.flags = 0;
@@ -1780,25 +1808,25 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
                return -EINVAL;
 
        if (of_property_read_u32(node, "slaves", &prop)) {
-               pr_err("Missing slaves property in the DT.\n");
+               dev_err(&pdev->dev, "Missing slaves property in the DT.\n");
                return -EINVAL;
        }
        data->slaves = prop;
 
        if (of_property_read_u32(node, "active_slave", &prop)) {
-               pr_err("Missing active_slave property in the DT.\n");
+               dev_err(&pdev->dev, "Missing active_slave property in the DT.\n");
                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");
+               dev_err(&pdev->dev, "Missing cpts_clock_mult property in the DT.\n");
                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");
+               dev_err(&pdev->dev, "Missing cpts_clock_shift property in the DT.\n");
                return -EINVAL;
        }
        data->cpts_clock_shift = prop;
@@ -1810,31 +1838,31 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
                return -ENOMEM;
 
        if (of_property_read_u32(node, "cpdma_channels", &prop)) {
-               pr_err("Missing cpdma_channels property in the DT.\n");
+               dev_err(&pdev->dev, "Missing cpdma_channels property in the DT.\n");
                return -EINVAL;
        }
        data->channels = prop;
 
        if (of_property_read_u32(node, "ale_entries", &prop)) {
-               pr_err("Missing ale_entries property in the DT.\n");
+               dev_err(&pdev->dev, "Missing ale_entries property in the DT.\n");
                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");
+               dev_err(&pdev->dev, "Missing bd_ram_size property in the DT.\n");
                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");
+               dev_err(&pdev->dev, "Missing rx_descs property in the DT.\n");
                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");
+               dev_err(&pdev->dev, "Missing mac_control property in the DT.\n");
                return -EINVAL;
        }
        data->mac_control = prop;
@@ -1848,7 +1876,7 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
        ret = of_platform_populate(node, NULL, NULL, &pdev->dev);
        /* We do not want to force this, as in some cases may not have child */
        if (ret)
-               pr_warn("Doesn't have any child node\n");
+               dev_warn(&pdev->dev, "Doesn't have any child node\n");
 
        for_each_child_of_node(node, slave_node) {
                struct cpsw_slave_data *slave_data = data->slave_data + i;
@@ -1865,7 +1893,7 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
 
                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);
+                       dev_err(&pdev->dev, "Missing slave[%d] phy_id property\n", i);
                        return -EINVAL;
                }
                mdio_node = of_find_node_by_phandle(be32_to_cpup(parp));
@@ -1885,18 +1913,18 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
 
                slave_data->phy_if = of_get_phy_mode(slave_node);
                if (slave_data->phy_if < 0) {
-                       pr_err("Missing or malformed slave[%d] phy-mode property\n",
-                              i);
+                       dev_err(&pdev->dev, "Missing or malformed slave[%d] phy-mode property\n",
+                               i);
                        return slave_data->phy_if;
                }
 
                if (data->dual_emac) {
                        if (of_property_read_u32(slave_node, "dual_emac_res_vlan",
                                                 &prop)) {
-                               pr_err("Missing dual_emac_res_vlan in DT.\n");
+                               dev_err(&pdev->dev, "Missing dual_emac_res_vlan in DT.\n");
                                slave_data->dual_emac_res_vlan = i+1;
-                               pr_err("Using %d as Reserved VLAN for %d slave\n",
-                                      slave_data->dual_emac_res_vlan, i);
+                               dev_err(&pdev->dev, "Using %d as Reserved VLAN for %d slave\n",
+                                       slave_data->dual_emac_res_vlan, i);
                        } else {
                                slave_data->dual_emac_res_vlan = prop;
                        }
@@ -1920,7 +1948,7 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev,
 
        ndev = alloc_etherdev(sizeof(struct cpsw_priv));
        if (!ndev) {
-               pr_err("cpsw: error allocating net_device\n");
+               dev_err(&pdev->dev, "cpsw: error allocating net_device\n");
                return -ENOMEM;
        }
 
@@ -1936,10 +1964,10 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev,
        if (is_valid_ether_addr(data->slave_data[1].mac_addr)) {
                memcpy(priv_sl2->mac_addr, data->slave_data[1].mac_addr,
                        ETH_ALEN);
-               pr_info("cpsw: Detected MACID = %pM\n", priv_sl2->mac_addr);
+               dev_info(&pdev->dev, "cpsw: Detected MACID = %pM\n", priv_sl2->mac_addr);
        } else {
                random_ether_addr(priv_sl2->mac_addr);
-               pr_info("cpsw: Random MACID = %pM\n", priv_sl2->mac_addr);
+               dev_info(&pdev->dev, "cpsw: Random MACID = %pM\n", priv_sl2->mac_addr);
        }
        memcpy(ndev->dev_addr, priv_sl2->mac_addr, ETH_ALEN);
 
@@ -1970,14 +1998,14 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev,
        ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
 
        ndev->netdev_ops = &cpsw_netdev_ops;
-       SET_ETHTOOL_OPS(ndev, &cpsw_ethtool_ops);
+       ndev->ethtool_ops = &cpsw_ethtool_ops;
        netif_napi_add(ndev, &priv_sl2->napi, cpsw_poll, CPSW_POLL_WEIGHT);
 
        /* register the network device */
        SET_NETDEV_DEV(ndev, &pdev->dev);
        ret = register_netdev(ndev);
        if (ret) {
-               pr_err("cpsw: error registering net device\n");
+               dev_err(&pdev->dev, "cpsw: error registering net device\n");
                free_netdev(ndev);
                ret = -ENODEV;
        }
@@ -1999,7 +2027,7 @@ static int cpsw_probe(struct platform_device *pdev)
 
        ndev = alloc_etherdev(sizeof(struct cpsw_priv));
        if (!ndev) {
-               pr_err("error allocating net_device\n");
+               dev_err(&pdev->dev, "error allocating net_device\n");
                return -ENOMEM;
        }
 
@@ -2014,7 +2042,7 @@ static int cpsw_probe(struct platform_device *pdev)
        priv->cpts = devm_kzalloc(&pdev->dev, sizeof(struct cpts), GFP_KERNEL);
        priv->irq_enabled = true;
        if (!priv->cpts) {
-               pr_err("error allocating cpts\n");
+               dev_err(&pdev->dev, "error allocating cpts\n");
                goto clean_ndev_ret;
        }
 
@@ -2027,7 +2055,7 @@ static int cpsw_probe(struct platform_device *pdev)
        pinctrl_pm_select_default_state(&pdev->dev);
 
        if (cpsw_probe_dt(&priv->data, pdev)) {
-               pr_err("cpsw: platform data missing\n");
+               dev_err(&pdev->dev, "cpsw: platform data missing\n");
                ret = -ENODEV;
                goto clean_runtime_disable_ret;
        }
@@ -2035,10 +2063,10 @@ static int cpsw_probe(struct platform_device *pdev)
 
        if (is_valid_ether_addr(data->slave_data[0].mac_addr)) {
                memcpy(priv->mac_addr, data->slave_data[0].mac_addr, ETH_ALEN);
-               pr_info("Detected MACID = %pM\n", priv->mac_addr);
+               dev_info(&pdev->dev, "Detected MACID = %pM\n", priv->mac_addr);
        } else {
                eth_random_addr(priv->mac_addr);
-               pr_info("Random MACID = %pM\n", priv->mac_addr);
+               dev_info(&pdev->dev, "Random MACID = %pM\n", priv->mac_addr);
        }
 
        memcpy(ndev->dev_addr, priv->mac_addr, ETH_ALEN);
@@ -2199,7 +2227,7 @@ static int cpsw_probe(struct platform_device *pdev)
        ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
 
        ndev->netdev_ops = &cpsw_netdev_ops;
-       SET_ETHTOOL_OPS(ndev, &cpsw_ethtool_ops);
+       ndev->ethtool_ops = &cpsw_ethtool_ops;
        netif_napi_add(ndev, &priv->napi, cpsw_poll, CPSW_POLL_WEIGHT);
 
        /* register the network device */
index 243513980b51511a9c3054d71c8a270d63949cb1..6b56f85951e581826afc152109d0eee4b53dd08d 100644 (file)
@@ -236,13 +236,11 @@ static void cpts_overflow_check(struct work_struct *work)
        schedule_delayed_work(&cpts->overflow_work, CPTS_OVERFLOW_PERIOD);
 }
 
-#define CPTS_REF_CLOCK_NAME "cpsw_cpts_rft_clk"
-
-static void cpts_clk_init(struct cpts *cpts)
+static void cpts_clk_init(struct device *dev, struct cpts *cpts)
 {
-       cpts->refclk = clk_get(NULL, CPTS_REF_CLOCK_NAME);
+       cpts->refclk = devm_clk_get(dev, "cpts");
        if (IS_ERR(cpts->refclk)) {
-               pr_err("Failed to clk_get %s\n", CPTS_REF_CLOCK_NAME);
+               dev_err(dev, "Failed to get cpts refclk\n");
                cpts->refclk = NULL;
                return;
        }
@@ -252,7 +250,6 @@ static void cpts_clk_init(struct cpts *cpts)
 static void cpts_clk_release(struct cpts *cpts)
 {
        clk_disable(cpts->refclk);
-       clk_put(cpts->refclk);
 }
 
 static int cpts_match(struct sk_buff *skb, unsigned int ptp_class,
@@ -390,7 +387,7 @@ int cpts_register(struct device *dev, struct cpts *cpts,
        for (i = 0; i < CPTS_MAX_EVENTS; i++)
                list_add(&cpts->pool_data[i].list, &cpts->pool);
 
-       cpts_clk_init(cpts);
+       cpts_clk_init(dev, cpts);
        cpts_write32(cpts, CPTS_EN, control);
        cpts_write32(cpts, TS_PEND_EN, int_enable);
 
index 88ef27067bf24a8b2569f533b63ac223d52280fe..4a000f6dd6fc35d274f585a8536bd325666fdcb2 100644 (file)
@@ -158,9 +158,9 @@ cpdma_desc_pool_create(struct device *dev, u32 phys, u32 hw_addr,
        int bitmap_size;
        struct cpdma_desc_pool *pool;
 
-       pool = kzalloc(sizeof(*pool), GFP_KERNEL);
+       pool = devm_kzalloc(dev, sizeof(*pool), GFP_KERNEL);
        if (!pool)
-               return NULL;
+               goto fail;
 
        spin_lock_init(&pool->lock);
 
@@ -170,7 +170,7 @@ cpdma_desc_pool_create(struct device *dev, u32 phys, u32 hw_addr,
        pool->num_desc  = size / pool->desc_size;
 
        bitmap_size  = (pool->num_desc / BITS_PER_LONG) * sizeof(long);
-       pool->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
+       pool->bitmap = devm_kzalloc(dev, bitmap_size, GFP_KERNEL);
        if (!pool->bitmap)
                goto fail;
 
@@ -187,10 +187,7 @@ cpdma_desc_pool_create(struct device *dev, u32 phys, u32 hw_addr,
 
        if (pool->iomap)
                return pool;
-
 fail:
-       kfree(pool->bitmap);
-       kfree(pool);
        return NULL;
 }
 
@@ -203,7 +200,6 @@ static void cpdma_desc_pool_destroy(struct cpdma_desc_pool *pool)
 
        spin_lock_irqsave(&pool->lock, flags);
        WARN_ON(pool->used_desc);
-       kfree(pool->bitmap);
        if (pool->cpumap) {
                dma_free_coherent(pool->dev, pool->mem_size, pool->cpumap,
                                  pool->phys);
@@ -211,7 +207,6 @@ static void cpdma_desc_pool_destroy(struct cpdma_desc_pool *pool)
                iounmap(pool->iomap);
        }
        spin_unlock_irqrestore(&pool->lock, flags);
-       kfree(pool);
 }
 
 static inline dma_addr_t desc_phys(struct cpdma_desc_pool *pool,
@@ -276,7 +271,7 @@ struct cpdma_ctlr *cpdma_ctlr_create(struct cpdma_params *params)
 {
        struct cpdma_ctlr *ctlr;
 
-       ctlr = kzalloc(sizeof(*ctlr), GFP_KERNEL);
+       ctlr = devm_kzalloc(params->dev, sizeof(*ctlr), GFP_KERNEL);
        if (!ctlr)
                return NULL;
 
@@ -290,10 +285,8 @@ struct cpdma_ctlr *cpdma_ctlr_create(struct cpdma_params *params)
                                            ctlr->params.desc_hw_addr,
                                            ctlr->params.desc_mem_size,
                                            ctlr->params.desc_align);
-       if (!ctlr->pool) {
-               kfree(ctlr);
+       if (!ctlr->pool)
                return NULL;
-       }
 
        if (WARN_ON(ctlr->num_chan > CPDMA_MAX_CHANNELS))
                ctlr->num_chan = CPDMA_MAX_CHANNELS;
@@ -468,7 +461,6 @@ int cpdma_ctlr_destroy(struct cpdma_ctlr *ctlr)
 
        cpdma_desc_pool_destroy(ctlr->pool);
        spin_unlock_irqrestore(&ctlr->lock, flags);
-       kfree(ctlr);
        return ret;
 }
 EXPORT_SYMBOL_GPL(cpdma_ctlr_destroy);
@@ -507,21 +499,22 @@ struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
                                     cpdma_handler_fn handler)
 {
        struct cpdma_chan *chan;
-       int ret, offset = (chan_num % CPDMA_MAX_CHANNELS) * 4;
+       int offset = (chan_num % CPDMA_MAX_CHANNELS) * 4;
        unsigned long flags;
 
        if (__chan_linear(chan_num) >= ctlr->num_chan)
                return NULL;
 
-       ret = -ENOMEM;
-       chan = kzalloc(sizeof(*chan), GFP_KERNEL);
+       chan = devm_kzalloc(ctlr->dev, sizeof(*chan), GFP_KERNEL);
        if (!chan)
-               goto err_chan_alloc;
+               return ERR_PTR(-ENOMEM);
 
        spin_lock_irqsave(&ctlr->lock, flags);
-       ret = -EBUSY;
-       if (ctlr->channels[chan_num])
-               goto err_chan_busy;
+       if (ctlr->channels[chan_num]) {
+               spin_unlock_irqrestore(&ctlr->lock, flags);
+               devm_kfree(ctlr->dev, chan);
+               return ERR_PTR(-EBUSY);
+       }
 
        chan->ctlr      = ctlr;
        chan->state     = CPDMA_STATE_IDLE;
@@ -551,12 +544,6 @@ struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num,
        ctlr->channels[chan_num] = chan;
        spin_unlock_irqrestore(&ctlr->lock, flags);
        return chan;
-
-err_chan_busy:
-       spin_unlock_irqrestore(&ctlr->lock, flags);
-       kfree(chan);
-err_chan_alloc:
-       return ERR_PTR(ret);
 }
 EXPORT_SYMBOL_GPL(cpdma_chan_create);
 
index 8f0e69ce07ca3e03cfbf4cf1b22c92c9af27beeb..35a139e9a833520a195d16a7c620d423638eb90c 100644 (file)
@@ -1567,7 +1567,6 @@ static int emac_dev_open(struct net_device *ndev)
        while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ,
                                            res_num))) {
                for (irq_num = res->start; irq_num <= res->end; irq_num++) {
-                       dev_err(emac_dev, "Request IRQ %d\n", irq_num);
                        if (request_irq(irq_num, emac_irq, 0, ndev->name,
                                        ndev)) {
                                dev_err(emac_dev,
@@ -1865,7 +1864,6 @@ static int davinci_emac_probe(struct platform_device *pdev)
        struct emac_priv *priv;
        unsigned long hw_ram_addr;
        struct emac_platform_data *pdata;
-       struct device *emac_dev;
        struct cpdma_params dma_params;
        struct clk *emac_clk;
        unsigned long emac_bus_frequency;
@@ -1911,7 +1909,6 @@ static int davinci_emac_probe(struct platform_device *pdev)
        priv->coal_intvl = 0;
        priv->bus_freq_mhz = (u32)(emac_bus_frequency / 1000000);
 
-       emac_dev = &ndev->dev;
        /* Get EMAC platform data */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        priv->emac_base_phys = res->start + pdata->ctrl_reg_offset;
@@ -1930,7 +1927,7 @@ static int davinci_emac_probe(struct platform_device *pdev)
                hw_ram_addr = (u32 __force)res->start + pdata->ctrl_ram_offset;
 
        memset(&dma_params, 0, sizeof(dma_params));
-       dma_params.dev                  = emac_dev;
+       dma_params.dev                  = &pdev->dev;
        dma_params.dmaregs              = priv->emac_base;
        dma_params.rxthresh             = priv->emac_base + 0x120;
        dma_params.rxfree               = priv->emac_base + 0x140;
@@ -1980,7 +1977,7 @@ static int davinci_emac_probe(struct platform_device *pdev)
        }
 
        ndev->netdev_ops = &emac_netdev_ops;
-       SET_ETHTOOL_OPS(ndev, &ethtool_ops);
+       ndev->ethtool_ops = &ethtool_ops;
        netif_napi_add(ndev, &priv->napi, emac_poll, EMAC_POLL_WEIGHT);
 
        /* register the network device */
@@ -1994,7 +1991,7 @@ static int davinci_emac_probe(struct platform_device *pdev)
 
 
        if (netif_msg_probe(priv)) {
-               dev_notice(emac_dev, "DaVinci EMAC Probe found device "\
+               dev_notice(&pdev->dev, "DaVinci EMAC Probe found device "
                           "(regs: %p, irq: %d)\n",
                           (void *)priv->emac_base_phys, ndev->irq);
        }
index 0cca9dec5d8277542a4439aed4bb8953f3943b29..735dc53d4b0163be05eec83c83a4fb8bb4612497 100644 (file)
@@ -303,7 +303,7 @@ static int davinci_mdio_probe_dt(struct mdio_platform_data *data,
                return -EINVAL;
 
        if (of_property_read_u32(node, "bus_freq", &prop)) {
-               pr_err("Missing bus_freq property in the DT.\n");
+               dev_err(&pdev->dev, "Missing bus_freq property in the DT.\n");
                return -EINVAL;
        }
        data->bus_freq = prop;
@@ -321,15 +321,14 @@ static int davinci_mdio_probe(struct platform_device *pdev)
        struct phy_device *phy;
        int ret, addr;
 
-       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
 
-       data->bus = mdiobus_alloc();
+       data->bus = devm_mdiobus_alloc(dev);
        if (!data->bus) {
                dev_err(dev, "failed to alloc mii bus\n");
-               ret = -ENOMEM;
-               goto bail_out;
+               return -ENOMEM;
        }
 
        if (dev->of_node) {
@@ -349,12 +348,9 @@ static int davinci_mdio_probe(struct platform_device *pdev)
        data->bus->parent       = dev;
        data->bus->priv         = data;
 
-       /* Select default pin state */
-       pinctrl_pm_select_default_state(&pdev->dev);
-
        pm_runtime_enable(&pdev->dev);
        pm_runtime_get_sync(&pdev->dev);
-       data->clk = clk_get(&pdev->dev, "fck");
+       data->clk = devm_clk_get(dev, "fck");
        if (IS_ERR(data->clk)) {
                dev_err(dev, "failed to get device clock\n");
                ret = PTR_ERR(data->clk);
@@ -367,24 +363,9 @@ static int davinci_mdio_probe(struct platform_device *pdev)
        spin_lock_init(&data->lock);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               dev_err(dev, "could not find register map resource\n");
-               ret = -ENOENT;
-               goto bail_out;
-       }
-
-       res = devm_request_mem_region(dev, res->start, resource_size(res),
-                                           dev_name(dev));
-       if (!res) {
-               dev_err(dev, "could not allocate register map resource\n");
-               ret = -ENXIO;
-               goto bail_out;
-       }
-
-       data->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
-       if (!data->regs) {
-               dev_err(dev, "could not map mdio registers\n");
-               ret = -ENOMEM;
+       data->regs = devm_ioremap_resource(dev, res);
+       if (IS_ERR(data->regs)) {
+               ret = PTR_ERR(data->regs);
                goto bail_out;
        }
 
@@ -406,16 +387,9 @@ static int davinci_mdio_probe(struct platform_device *pdev)
        return 0;
 
 bail_out:
-       if (data->bus)
-               mdiobus_free(data->bus);
-
-       if (data->clk)
-               clk_put(data->clk);
        pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
 
-       kfree(data);
-
        return ret;
 }
 
@@ -423,18 +397,12 @@ static int davinci_mdio_remove(struct platform_device *pdev)
 {
        struct davinci_mdio_data *data = platform_get_drvdata(pdev);
 
-       if (data->bus) {
+       if (data->bus)
                mdiobus_unregister(data->bus);
-               mdiobus_free(data->bus);
-       }
 
-       if (data->clk)
-               clk_put(data->clk);
        pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
 
-       kfree(data);
-
        return 0;
 }
 
index 449011b0e007d6b798a91728eaa845e05e581305..14389f841d431c1236f5ea90d0d3eb995a41c39c 100644 (file)
@@ -2192,7 +2192,6 @@ static void tile_net_dev_init(const char *name, const uint8_t *mac)
 {
        int ret;
        int i;
-       int nz_addr = 0;
        struct net_device *dev;
        struct tile_net_priv *priv;
 
@@ -2212,7 +2211,6 @@ static void tile_net_dev_init(const char *name, const uint8_t *mac)
 
        /* Initialize "priv". */
        priv = netdev_priv(dev);
-       memset(priv, 0, sizeof(*priv));
        priv->dev = dev;
        priv->channel = -1;
        priv->loopify_channel = -1;
@@ -2223,15 +2221,10 @@ static void tile_net_dev_init(const char *name, const uint8_t *mac)
         * be done before the device is opened.  If the MAC is all zeroes,
         * we use a random address, since we're probably on the simulator.
         */
-       for (i = 0; i < 6; i++)
-               nz_addr |= mac[i];
-
-       if (nz_addr) {
-               memcpy(dev->dev_addr, mac, ETH_ALEN);
-               dev->addr_len = 6;
-       } else {
+       if (!is_zero_ether_addr(mac))
+               ether_addr_copy(dev->dev_addr, mac);
+       else
                eth_hw_addr_random(dev);
-       }
 
        /* Register the network device. */
        ret = register_netdev(dev);
index d899d0072ae050d481b9d4510956bfb6146efac2..bb79928046645d62fbb238614777b00600b62b0d 100644 (file)
@@ -1561,7 +1561,7 @@ static struct gelic_card *gelic_alloc_card_net(struct net_device **netdev)
         * alloc netdev
         */
        *netdev = alloc_etherdev(sizeof(struct gelic_port));
-       if (!netdev) {
+       if (!*netdev) {
                kfree(card->unalign);
                return NULL;
        }
index 8a049a2b44742aabe24221b12a6bcb2a4caf357f..f66ddaee0c877f1620ad123fd9ef8402703aa1a6 100644 (file)
@@ -19,7 +19,7 @@ if NET_VENDOR_VIA
 
 config VIA_RHINE
        tristate "VIA Rhine support"
-       depends on PCI
+       depends on (PCI || USE_OF)
        select CRC32
        select MII
        ---help---
index f61dc2b72bb2f43780ace58a503bd2e3b89f61a9..2d72f96a9e2cf93d4f1dca3aad98e780bcfc5bca 100644 (file)
@@ -94,6 +94,10 @@ static const int multicast_filter_limit = 32;
 #include <linux/ioport.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -116,13 +120,6 @@ static const int multicast_filter_limit = 32;
 static const char version[] =
        "v1.10-LK" DRV_VERSION " " DRV_RELDATE " Written by Donald Becker";
 
-/* This driver was written to use PCI memory space. Some early versions
-   of the Rhine may only work correctly with I/O space accesses. */
-#ifdef CONFIG_VIA_RHINE_MMIO
-#define USE_MMIO
-#else
-#endif
-
 MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
 MODULE_DESCRIPTION("VIA Rhine PCI Fast Ethernet driver");
 MODULE_LICENSE("GPL");
@@ -260,6 +257,12 @@ enum rhine_quirks {
        rq6patterns     = 0x0040,       /* 6 instead of 4 patterns for WOL */
        rqStatusWBRace  = 0x0080,       /* Tx Status Writeback Error possible */
        rqRhineI        = 0x0100,       /* See comment below */
+       rqIntPHY        = 0x0200,       /* Integrated PHY */
+       rqMgmt          = 0x0400,       /* Management adapter */
+       rqNeedEnMMIO    = 0x0800,       /* Whether the core needs to be
+                                        * switched from PIO mode to MMIO
+                                        * (only applies to PCI)
+                                        */
 };
 /*
  * rqRhineI: VT86C100A (aka Rhine-I) uses different bits to enable
@@ -279,6 +282,15 @@ static DEFINE_PCI_DEVICE_TABLE(rhine_pci_tbl) = {
 };
 MODULE_DEVICE_TABLE(pci, rhine_pci_tbl);
 
+/* OpenFirmware identifiers for platform-bus devices
+ * The .data field is currently only used to store quirks
+ */
+static u32 vt8500_quirks = rqWOL | rqForceReset | rq6patterns;
+static struct of_device_id rhine_of_tbl[] = {
+       { .compatible = "via,vt8500-rhine", .data = &vt8500_quirks },
+       { }     /* terminate list */
+};
+MODULE_DEVICE_TABLE(of, rhine_of_tbl);
 
 /* Offsets to the device registers. */
 enum register_offsets {
@@ -338,13 +350,11 @@ enum bcr1_bits {
        BCR1_MED1=0x80,         /* for VT6102 */
 };
 
-#ifdef USE_MMIO
 /* Registers we check that mmio and reg are the same. */
 static const int mmio_verify_registers[] = {
        RxConfig, TxConfig, IntrEnable, ConfigA, ConfigB, ConfigC, ConfigD,
        0
 };
-#endif
 
 /* Bits in the interrupt status/mask registers. */
 enum intr_status_bits {
@@ -446,7 +456,7 @@ struct rhine_private {
        unsigned char *tx_bufs;
        dma_addr_t tx_bufs_dma;
 
-       struct pci_dev *pdev;
+       int irq;
        long pioaddr;
        struct net_device *dev;
        struct napi_struct napi;
@@ -649,20 +659,46 @@ static void rhine_chip_reset(struct net_device *dev)
                   "failed" : "succeeded");
 }
 
-#ifdef USE_MMIO
 static void enable_mmio(long pioaddr, u32 quirks)
 {
        int n;
-       if (quirks & rqRhineI) {
-               /* More recent docs say that this bit is reserved ... */
-               n = inb(pioaddr + ConfigA) | 0x20;
-               outb(n, pioaddr + ConfigA);
-       } else {
-               n = inb(pioaddr + ConfigD) | 0x80;
-               outb(n, pioaddr + ConfigD);
+
+       if (quirks & rqNeedEnMMIO) {
+               if (quirks & rqRhineI) {
+                       /* More recent docs say that this bit is reserved */
+                       n = inb(pioaddr + ConfigA) | 0x20;
+                       outb(n, pioaddr + ConfigA);
+               } else {
+                       n = inb(pioaddr + ConfigD) | 0x80;
+                       outb(n, pioaddr + ConfigD);
+               }
        }
 }
-#endif
+
+static inline int verify_mmio(struct device *hwdev,
+                             long pioaddr,
+                             void __iomem *ioaddr,
+                             u32 quirks)
+{
+       if (quirks & rqNeedEnMMIO) {
+               int i = 0;
+
+               /* Check that selected MMIO registers match the PIO ones */
+               while (mmio_verify_registers[i]) {
+                       int reg = mmio_verify_registers[i++];
+                       unsigned char a = inb(pioaddr+reg);
+                       unsigned char b = readb(ioaddr+reg);
+
+                       if (a != b) {
+                               dev_err(hwdev,
+                                       "MMIO do not match PIO [%02x] (%02x != %02x)\n",
+                                       reg, a, b);
+                               return -EIO;
+                       }
+               }
+       }
+       return 0;
+}
 
 /*
  * Loads bytes 0x00-0x05, 0x6E-0x6F, 0x78-0x7B from EEPROM
@@ -682,14 +718,12 @@ static void rhine_reload_eeprom(long pioaddr, struct net_device *dev)
        if (i > 512)
                pr_info("%4d cycles used @ %s:%d\n", i, __func__, __LINE__);
 
-#ifdef USE_MMIO
        /*
         * Reloading from EEPROM overwrites ConfigA-D, so we must re-enable
         * MMIO. If reloading EEPROM was done first this could be avoided, but
         * it is not known if that still works with the "win98-reboot" problem.
         */
        enable_mmio(pioaddr, rp->quirks);
-#endif
 
        /* Turn off EEPROM-controlled wake-up (magic packet) */
        if (rp->quirks & rqWOL)
@@ -701,7 +735,7 @@ static void rhine_reload_eeprom(long pioaddr, struct net_device *dev)
 static void rhine_poll(struct net_device *dev)
 {
        struct rhine_private *rp = netdev_priv(dev);
-       const int irq = rp->pdev->irq;
+       const int irq = rp->irq;
 
        disable_irq(irq);
        rhine_interrupt(irq, dev);
@@ -846,7 +880,8 @@ static void rhine_hw_init(struct net_device *dev, long pioaddr)
                msleep(5);
 
        /* Reload EEPROM controlled bytes cleared by soft reset */
-       rhine_reload_eeprom(pioaddr, dev);
+       if (dev_is_pci(dev->dev.parent))
+               rhine_reload_eeprom(pioaddr, dev);
 }
 
 static const struct net_device_ops rhine_netdev_ops = {
@@ -867,125 +902,37 @@ static const struct net_device_ops rhine_netdev_ops = {
 #endif
 };
 
-static int rhine_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+static int rhine_init_one_common(struct device *hwdev, u32 quirks,
+                                long pioaddr, void __iomem *ioaddr, int irq)
 {
        struct net_device *dev;
        struct rhine_private *rp;
-       int i, rc;
-       u32 quirks;
-       long pioaddr;
-       long memaddr;
-       void __iomem *ioaddr;
-       int io_size, phy_id;
+       int i, rc, phy_id;
        const char *name;
-#ifdef USE_MMIO
-       int bar = 1;
-#else
-       int bar = 0;
-#endif
-
-/* when built into the kernel, we only print version if device is found */
-#ifndef MODULE
-       pr_info_once("%s\n", version);
-#endif
-
-       io_size = 256;
-       phy_id = 0;
-       quirks = 0;
-       name = "Rhine";
-       if (pdev->revision < VTunknown0) {
-               quirks = rqRhineI;
-               io_size = 128;
-       }
-       else if (pdev->revision >= VT6102) {
-               quirks = rqWOL | rqForceReset;
-               if (pdev->revision < VT6105) {
-                       name = "Rhine II";
-                       quirks |= rqStatusWBRace;       /* Rhine-II exclusive */
-               }
-               else {
-                       phy_id = 1;     /* Integrated PHY, phy_id fixed to 1 */
-                       if (pdev->revision >= VT6105_B0)
-                               quirks |= rq6patterns;
-                       if (pdev->revision < VT6105M)
-                               name = "Rhine III";
-                       else
-                               name = "Rhine III (Management Adapter)";
-               }
-       }
-
-       rc = pci_enable_device(pdev);
-       if (rc)
-               goto err_out;
 
        /* this should always be supported */
-       rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+       rc = dma_set_mask(hwdev, DMA_BIT_MASK(32));
        if (rc) {
-               dev_err(&pdev->dev,
-                       "32-bit PCI DMA addresses not supported by the card!?\n");
-               goto err_out_pci_disable;
-       }
-
-       /* sanity check */
-       if ((pci_resource_len(pdev, 0) < io_size) ||
-           (pci_resource_len(pdev, 1) < io_size)) {
-               rc = -EIO;
-               dev_err(&pdev->dev, "Insufficient PCI resources, aborting\n");
-               goto err_out_pci_disable;
+               dev_err(hwdev, "32-bit DMA addresses not supported by the card!?\n");
+               goto err_out;
        }
 
-       pioaddr = pci_resource_start(pdev, 0);
-       memaddr = pci_resource_start(pdev, 1);
-
-       pci_set_master(pdev);
-
        dev = alloc_etherdev(sizeof(struct rhine_private));
        if (!dev) {
                rc = -ENOMEM;
-               goto err_out_pci_disable;
+               goto err_out;
        }
-       SET_NETDEV_DEV(dev, &pdev->dev);
+       SET_NETDEV_DEV(dev, hwdev);
 
        rp = netdev_priv(dev);
        rp->dev = dev;
        rp->quirks = quirks;
        rp->pioaddr = pioaddr;
-       rp->pdev = pdev;
+       rp->base = ioaddr;
+       rp->irq = irq;
        rp->msg_enable = netif_msg_init(debug, RHINE_MSG_DEFAULT);
 
-       rc = pci_request_regions(pdev, DRV_NAME);
-       if (rc)
-               goto err_out_free_netdev;
-
-       ioaddr = pci_iomap(pdev, bar, io_size);
-       if (!ioaddr) {
-               rc = -EIO;
-               dev_err(&pdev->dev,
-                       "ioremap failed for device %s, region 0x%X @ 0x%lX\n",
-                       pci_name(pdev), io_size, memaddr);
-               goto err_out_free_res;
-       }
-
-#ifdef USE_MMIO
-       enable_mmio(pioaddr, quirks);
-
-       /* Check that selected MMIO registers match the PIO ones */
-       i = 0;
-       while (mmio_verify_registers[i]) {
-               int reg = mmio_verify_registers[i++];
-               unsigned char a = inb(pioaddr+reg);
-               unsigned char b = readb(ioaddr+reg);
-               if (a != b) {
-                       rc = -EIO;
-                       dev_err(&pdev->dev,
-                               "MMIO do not match PIO [%02x] (%02x != %02x)\n",
-                               reg, a, b);
-                       goto err_out_unmap;
-               }
-       }
-#endif /* USE_MMIO */
-
-       rp->base = ioaddr;
+       phy_id = rp->quirks & rqIntPHY ? 1 : 0;
 
        u64_stats_init(&rp->tx_stats.syncp);
        u64_stats_init(&rp->rx_stats.syncp);
@@ -1030,7 +977,7 @@ static int rhine_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (rp->quirks & rqRhineI)
                dev->features |= NETIF_F_SG|NETIF_F_HW_CSUM;
 
-       if (pdev->revision >= VT6105M)
+       if (rp->quirks & rqMgmt)
                dev->features |= NETIF_F_HW_VLAN_CTAG_TX |
                                 NETIF_F_HW_VLAN_CTAG_RX |
                                 NETIF_F_HW_VLAN_CTAG_FILTER;
@@ -1038,18 +985,21 @@ static int rhine_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        /* dev->name not defined before register_netdev()! */
        rc = register_netdev(dev);
        if (rc)
-               goto err_out_unmap;
+               goto err_out_free_netdev;
+
+       if (rp->quirks & rqRhineI)
+               name = "Rhine";
+       else if (rp->quirks & rqStatusWBRace)
+               name = "Rhine II";
+       else if (rp->quirks & rqMgmt)
+               name = "Rhine III (Management Adapter)";
+       else
+               name = "Rhine III";
 
        netdev_info(dev, "VIA %s at 0x%lx, %pM, IRQ %d\n",
-                   name,
-#ifdef USE_MMIO
-                   memaddr,
-#else
-                   (long)ioaddr,
-#endif
-                   dev->dev_addr, pdev->irq);
+                   name, (long)ioaddr, dev->dev_addr, rp->irq);
 
-       pci_set_drvdata(pdev, dev);
+       dev_set_drvdata(hwdev, dev);
 
        {
                u16 mii_cmd;
@@ -1078,41 +1028,158 @@ static int rhine_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        return 0;
 
+err_out_free_netdev:
+       free_netdev(dev);
+err_out:
+       return rc;
+}
+
+static int rhine_init_one_pci(struct pci_dev *pdev,
+                             const struct pci_device_id *ent)
+{
+       struct device *hwdev = &pdev->dev;
+       int rc;
+       long pioaddr, memaddr;
+       void __iomem *ioaddr;
+       int io_size = pdev->revision < VTunknown0 ? 128 : 256;
+
+/* This driver was written to use PCI memory space. Some early versions
+ * of the Rhine may only work correctly with I/O space accesses.
+ * TODO: determine for which revisions this is true and assign the flag
+ *      in code as opposed to this Kconfig option (???)
+ */
+#ifdef CONFIG_VIA_RHINE_MMIO
+       u32 quirks = rqNeedEnMMIO;
+#else
+       u32 quirks = 0;
+#endif
+
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+       pr_info_once("%s\n", version);
+#endif
+
+       rc = pci_enable_device(pdev);
+       if (rc)
+               goto err_out;
+
+       if (pdev->revision < VTunknown0) {
+               quirks |= rqRhineI;
+       } else if (pdev->revision >= VT6102) {
+               quirks |= rqWOL | rqForceReset;
+               if (pdev->revision < VT6105) {
+                       quirks |= rqStatusWBRace;
+               } else {
+                       quirks |= rqIntPHY;
+                       if (pdev->revision >= VT6105_B0)
+                               quirks |= rq6patterns;
+                       if (pdev->revision >= VT6105M)
+                               quirks |= rqMgmt;
+               }
+       }
+
+       /* sanity check */
+       if ((pci_resource_len(pdev, 0) < io_size) ||
+           (pci_resource_len(pdev, 1) < io_size)) {
+               rc = -EIO;
+               dev_err(hwdev, "Insufficient PCI resources, aborting\n");
+               goto err_out_pci_disable;
+       }
+
+       pioaddr = pci_resource_start(pdev, 0);
+       memaddr = pci_resource_start(pdev, 1);
+
+       pci_set_master(pdev);
+
+       rc = pci_request_regions(pdev, DRV_NAME);
+       if (rc)
+               goto err_out_pci_disable;
+
+       ioaddr = pci_iomap(pdev, (quirks & rqNeedEnMMIO ? 1 : 0), io_size);
+       if (!ioaddr) {
+               rc = -EIO;
+               dev_err(hwdev,
+                       "ioremap failed for device %s, region 0x%X @ 0x%lX\n",
+                       dev_name(hwdev), io_size, memaddr);
+               goto err_out_free_res;
+       }
+
+       enable_mmio(pioaddr, quirks);
+
+       rc = verify_mmio(hwdev, pioaddr, ioaddr, quirks);
+       if (rc)
+               goto err_out_unmap;
+
+       rc = rhine_init_one_common(&pdev->dev, quirks,
+                                  pioaddr, ioaddr, pdev->irq);
+       if (!rc)
+               return 0;
+
 err_out_unmap:
        pci_iounmap(pdev, ioaddr);
 err_out_free_res:
        pci_release_regions(pdev);
-err_out_free_netdev:
-       free_netdev(dev);
 err_out_pci_disable:
        pci_disable_device(pdev);
 err_out:
        return rc;
 }
 
+static int rhine_init_one_platform(struct platform_device *pdev)
+{
+       const struct of_device_id *match;
+       const u32 *quirks;
+       int irq;
+       struct resource *res;
+       void __iomem *ioaddr;
+
+       match = of_match_device(rhine_of_tbl, &pdev->dev);
+       if (!match)
+               return -EINVAL;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       ioaddr = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(ioaddr))
+               return PTR_ERR(ioaddr);
+
+       irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+       if (!irq)
+               return -EINVAL;
+
+       quirks = match->data;
+       if (!quirks)
+               return -EINVAL;
+
+       return rhine_init_one_common(&pdev->dev, *quirks,
+                                    (long)ioaddr, ioaddr, irq);
+}
+
 static int alloc_ring(struct net_device* dev)
 {
        struct rhine_private *rp = netdev_priv(dev);
+       struct device *hwdev = dev->dev.parent;
        void *ring;
        dma_addr_t ring_dma;
 
-       ring = pci_alloc_consistent(rp->pdev,
-                                   RX_RING_SIZE * sizeof(struct rx_desc) +
-                                   TX_RING_SIZE * sizeof(struct tx_desc),
-                                   &ring_dma);
+       ring = dma_alloc_coherent(hwdev,
+                                 RX_RING_SIZE * sizeof(struct rx_desc) +
+                                 TX_RING_SIZE * sizeof(struct tx_desc),
+                                 &ring_dma,
+                                 GFP_ATOMIC);
        if (!ring) {
                netdev_err(dev, "Could not allocate DMA memory\n");
                return -ENOMEM;
        }
        if (rp->quirks & rqRhineI) {
-               rp->tx_bufs = pci_alloc_consistent(rp->pdev,
-                                                  PKT_BUF_SZ * TX_RING_SIZE,
-                                                  &rp->tx_bufs_dma);
+               rp->tx_bufs = dma_alloc_coherent(hwdev,
+                                                PKT_BUF_SZ * TX_RING_SIZE,
+                                                &rp->tx_bufs_dma,
+                                                GFP_ATOMIC);
                if (rp->tx_bufs == NULL) {
-                       pci_free_consistent(rp->pdev,
-                                   RX_RING_SIZE * sizeof(struct rx_desc) +
-                                   TX_RING_SIZE * sizeof(struct tx_desc),
-                                   ring, ring_dma);
+                       dma_free_coherent(hwdev,
+                                         RX_RING_SIZE * sizeof(struct rx_desc) +
+                                         TX_RING_SIZE * sizeof(struct tx_desc),
+                                         ring, ring_dma);
                        return -ENOMEM;
                }
        }
@@ -1128,16 +1195,17 @@ static int alloc_ring(struct net_device* dev)
 static void free_ring(struct net_device* dev)
 {
        struct rhine_private *rp = netdev_priv(dev);
+       struct device *hwdev = dev->dev.parent;
 
-       pci_free_consistent(rp->pdev,
-                           RX_RING_SIZE * sizeof(struct rx_desc) +
-                           TX_RING_SIZE * sizeof(struct tx_desc),
-                           rp->rx_ring, rp->rx_ring_dma);
+       dma_free_coherent(hwdev,
+                         RX_RING_SIZE * sizeof(struct rx_desc) +
+                         TX_RING_SIZE * sizeof(struct tx_desc),
+                         rp->rx_ring, rp->rx_ring_dma);
        rp->tx_ring = NULL;
 
        if (rp->tx_bufs)
-               pci_free_consistent(rp->pdev, PKT_BUF_SZ * TX_RING_SIZE,
-                                   rp->tx_bufs, rp->tx_bufs_dma);
+               dma_free_coherent(hwdev, PKT_BUF_SZ * TX_RING_SIZE,
+                                 rp->tx_bufs, rp->tx_bufs_dma);
 
        rp->tx_bufs = NULL;
 
@@ -1146,6 +1214,7 @@ static void free_ring(struct net_device* dev)
 static void alloc_rbufs(struct net_device *dev)
 {
        struct rhine_private *rp = netdev_priv(dev);
+       struct device *hwdev = dev->dev.parent;
        dma_addr_t next;
        int i;
 
@@ -1174,9 +1243,9 @@ static void alloc_rbufs(struct net_device *dev)
                        break;
 
                rp->rx_skbuff_dma[i] =
-                       pci_map_single(rp->pdev, skb->data, rp->rx_buf_sz,
-                                      PCI_DMA_FROMDEVICE);
-               if (dma_mapping_error(&rp->pdev->dev, rp->rx_skbuff_dma[i])) {
+                       dma_map_single(hwdev, skb->data, rp->rx_buf_sz,
+                                      DMA_FROM_DEVICE);
+               if (dma_mapping_error(hwdev, rp->rx_skbuff_dma[i])) {
                        rp->rx_skbuff_dma[i] = 0;
                        dev_kfree_skb(skb);
                        break;
@@ -1190,6 +1259,7 @@ static void alloc_rbufs(struct net_device *dev)
 static void free_rbufs(struct net_device* dev)
 {
        struct rhine_private *rp = netdev_priv(dev);
+       struct device *hwdev = dev->dev.parent;
        int i;
 
        /* Free all the skbuffs in the Rx queue. */
@@ -1197,9 +1267,9 @@ static void free_rbufs(struct net_device* dev)
                rp->rx_ring[i].rx_status = 0;
                rp->rx_ring[i].addr = cpu_to_le32(0xBADF00D0); /* An invalid address. */
                if (rp->rx_skbuff[i]) {
-                       pci_unmap_single(rp->pdev,
+                       dma_unmap_single(hwdev,
                                         rp->rx_skbuff_dma[i],
-                                        rp->rx_buf_sz, PCI_DMA_FROMDEVICE);
+                                        rp->rx_buf_sz, DMA_FROM_DEVICE);
                        dev_kfree_skb(rp->rx_skbuff[i]);
                }
                rp->rx_skbuff[i] = NULL;
@@ -1230,6 +1300,7 @@ static void alloc_tbufs(struct net_device* dev)
 static void free_tbufs(struct net_device* dev)
 {
        struct rhine_private *rp = netdev_priv(dev);
+       struct device *hwdev = dev->dev.parent;
        int i;
 
        for (i = 0; i < TX_RING_SIZE; i++) {
@@ -1238,10 +1309,10 @@ static void free_tbufs(struct net_device* dev)
                rp->tx_ring[i].addr = cpu_to_le32(0xBADF00D0); /* An invalid address. */
                if (rp->tx_skbuff[i]) {
                        if (rp->tx_skbuff_dma[i]) {
-                               pci_unmap_single(rp->pdev,
+                               dma_unmap_single(hwdev,
                                                 rp->tx_skbuff_dma[i],
                                                 rp->tx_skbuff[i]->len,
-                                                PCI_DMA_TODEVICE);
+                                                DMA_TO_DEVICE);
                        }
                        dev_kfree_skb(rp->tx_skbuff[i]);
                }
@@ -1278,8 +1349,9 @@ static void rhine_set_carrier(struct mii_if_info *mii)
                /* autoneg is off: Link is always assumed to be up */
                if (!netif_carrier_ok(dev))
                        netif_carrier_on(dev);
-       } else  /* Let MMI library update carrier status */
-               rhine_check_media(dev, 0);
+       }
+
+       rhine_check_media(dev, 0);
 
        netif_info(rp, link, dev, "force_media %d, carrier %d\n",
                   mii->force_media, netif_carrier_ok(dev));
@@ -1469,7 +1541,7 @@ static void init_registers(struct net_device *dev)
 
        rhine_set_rx_mode(dev);
 
-       if (rp->pdev->revision >= VT6105M)
+       if (rp->quirks & rqMgmt)
                rhine_init_cam_filter(dev);
 
        napi_enable(&rp->napi);
@@ -1581,16 +1653,15 @@ static int rhine_open(struct net_device *dev)
        void __iomem *ioaddr = rp->base;
        int rc;
 
-       rc = request_irq(rp->pdev->irq, rhine_interrupt, IRQF_SHARED, dev->name,
-                       dev);
+       rc = request_irq(rp->irq, rhine_interrupt, IRQF_SHARED, dev->name, dev);
        if (rc)
                return rc;
 
-       netif_dbg(rp, ifup, dev, "%s() irq %d\n", __func__, rp->pdev->irq);
+       netif_dbg(rp, ifup, dev, "%s() irq %d\n", __func__, rp->irq);
 
        rc = alloc_ring(dev);
        if (rc) {
-               free_irq(rp->pdev->irq, dev);
+               free_irq(rp->irq, dev);
                return rc;
        }
        alloc_rbufs(dev);
@@ -1659,6 +1730,7 @@ static netdev_tx_t rhine_start_tx(struct sk_buff *skb,
                                  struct net_device *dev)
 {
        struct rhine_private *rp = netdev_priv(dev);
+       struct device *hwdev = dev->dev.parent;
        void __iomem *ioaddr = rp->base;
        unsigned entry;
 
@@ -1695,9 +1767,9 @@ static netdev_tx_t rhine_start_tx(struct sk_buff *skb,
                                                       rp->tx_bufs));
        } else {
                rp->tx_skbuff_dma[entry] =
-                       pci_map_single(rp->pdev, skb->data, skb->len,
-                                      PCI_DMA_TODEVICE);
-               if (dma_mapping_error(&rp->pdev->dev, rp->tx_skbuff_dma[entry])) {
+                       dma_map_single(hwdev, skb->data, skb->len,
+                                      DMA_TO_DEVICE);
+               if (dma_mapping_error(hwdev, rp->tx_skbuff_dma[entry])) {
                        dev_kfree_skb_any(skb);
                        rp->tx_skbuff_dma[entry] = 0;
                        dev->stats.tx_dropped++;
@@ -1788,6 +1860,7 @@ static irqreturn_t rhine_interrupt(int irq, void *dev_instance)
 static void rhine_tx(struct net_device *dev)
 {
        struct rhine_private *rp = netdev_priv(dev);
+       struct device *hwdev = dev->dev.parent;
        int txstatus = 0, entry = rp->dirty_tx % TX_RING_SIZE;
 
        /* find and cleanup dirty tx descriptors */
@@ -1831,10 +1904,10 @@ static void rhine_tx(struct net_device *dev)
                }
                /* Free the original skb. */
                if (rp->tx_skbuff_dma[entry]) {
-                       pci_unmap_single(rp->pdev,
+                       dma_unmap_single(hwdev,
                                         rp->tx_skbuff_dma[entry],
                                         rp->tx_skbuff[entry]->len,
-                                        PCI_DMA_TODEVICE);
+                                        DMA_TO_DEVICE);
                }
                dev_consume_skb_any(rp->tx_skbuff[entry]);
                rp->tx_skbuff[entry] = NULL;
@@ -1863,6 +1936,7 @@ static inline u16 rhine_get_vlan_tci(struct sk_buff *skb, int data_size)
 static int rhine_rx(struct net_device *dev, int limit)
 {
        struct rhine_private *rp = netdev_priv(dev);
+       struct device *hwdev = dev->dev.parent;
        int count;
        int entry = rp->cur_rx % RX_RING_SIZE;
 
@@ -1924,19 +1998,19 @@ static int rhine_rx(struct net_device *dev, int limit)
                        if (pkt_len < rx_copybreak)
                                skb = netdev_alloc_skb_ip_align(dev, pkt_len);
                        if (skb) {
-                               pci_dma_sync_single_for_cpu(rp->pdev,
-                                                           rp->rx_skbuff_dma[entry],
-                                                           rp->rx_buf_sz,
-                                                           PCI_DMA_FROMDEVICE);
+                               dma_sync_single_for_cpu(hwdev,
+                                                       rp->rx_skbuff_dma[entry],
+                                                       rp->rx_buf_sz,
+                                                       DMA_FROM_DEVICE);
 
                                skb_copy_to_linear_data(skb,
                                                 rp->rx_skbuff[entry]->data,
                                                 pkt_len);
                                skb_put(skb, pkt_len);
-                               pci_dma_sync_single_for_device(rp->pdev,
-                                                              rp->rx_skbuff_dma[entry],
-                                                              rp->rx_buf_sz,
-                                                              PCI_DMA_FROMDEVICE);
+                               dma_sync_single_for_device(hwdev,
+                                                          rp->rx_skbuff_dma[entry],
+                                                          rp->rx_buf_sz,
+                                                          DMA_FROM_DEVICE);
                        } else {
                                skb = rp->rx_skbuff[entry];
                                if (skb == NULL) {
@@ -1945,10 +2019,10 @@ static int rhine_rx(struct net_device *dev, int limit)
                                }
                                rp->rx_skbuff[entry] = NULL;
                                skb_put(skb, pkt_len);
-                               pci_unmap_single(rp->pdev,
+                               dma_unmap_single(hwdev,
                                                 rp->rx_skbuff_dma[entry],
                                                 rp->rx_buf_sz,
-                                                PCI_DMA_FROMDEVICE);
+                                                DMA_FROM_DEVICE);
                        }
 
                        if (unlikely(desc_length & DescTag))
@@ -1979,10 +2053,11 @@ static int rhine_rx(struct net_device *dev, int limit)
                        if (skb == NULL)
                                break;  /* Better luck next round. */
                        rp->rx_skbuff_dma[entry] =
-                               pci_map_single(rp->pdev, skb->data,
+                               dma_map_single(hwdev, skb->data,
                                               rp->rx_buf_sz,
-                                              PCI_DMA_FROMDEVICE);
-                       if (dma_mapping_error(&rp->pdev->dev, rp->rx_skbuff_dma[entry])) {
+                                              DMA_FROM_DEVICE);
+                       if (dma_mapping_error(hwdev,
+                                             rp->rx_skbuff_dma[entry])) {
                                dev_kfree_skb(skb);
                                rp->rx_skbuff_dma[entry] = 0;
                                break;
@@ -2103,7 +2178,7 @@ static void rhine_set_rx_mode(struct net_device *dev)
                /* Too many to match, or accept all multicasts. */
                iowrite32(0xffffffff, ioaddr + MulticastFilter0);
                iowrite32(0xffffffff, ioaddr + MulticastFilter1);
-       } else if (rp->pdev->revision >= VT6105M) {
+       } else if (rp->quirks & rqMgmt) {
                int i = 0;
                u32 mCAMmask = 0;       /* 32 mCAMs (6105M and better) */
                netdev_for_each_mc_addr(ha, dev) {
@@ -2125,7 +2200,7 @@ static void rhine_set_rx_mode(struct net_device *dev)
                iowrite32(mc_filter[1], ioaddr + MulticastFilter1);
        }
        /* enable/disable VLAN receive filtering */
-       if (rp->pdev->revision >= VT6105M) {
+       if (rp->quirks & rqMgmt) {
                if (dev->flags & IFF_PROMISC)
                        BYTE_REG_BITS_OFF(BCR1_VIDFR, ioaddr + PCIBusConfig1);
                else
@@ -2136,11 +2211,11 @@ static void rhine_set_rx_mode(struct net_device *dev)
 
 static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
 {
-       struct rhine_private *rp = netdev_priv(dev);
+       struct device *hwdev = dev->dev.parent;
 
        strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
        strlcpy(info->version, DRV_VERSION, sizeof(info->version));
-       strlcpy(info->bus_info, pci_name(rp->pdev), sizeof(info->bus_info));
+       strlcpy(info->bus_info, dev_name(hwdev), sizeof(info->bus_info));
 }
 
 static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
@@ -2277,7 +2352,7 @@ static int rhine_close(struct net_device *dev)
        /* Stop the chip's Tx and Rx processes. */
        iowrite16(CmdStop, ioaddr + ChipCmd);
 
-       free_irq(rp->pdev->irq, dev);
+       free_irq(rp->irq, dev);
        free_rbufs(dev);
        free_tbufs(dev);
        free_ring(dev);
@@ -2286,7 +2361,7 @@ static int rhine_close(struct net_device *dev)
 }
 
 
-static void rhine_remove_one(struct pci_dev *pdev)
+static void rhine_remove_one_pci(struct pci_dev *pdev)
 {
        struct net_device *dev = pci_get_drvdata(pdev);
        struct rhine_private *rp = netdev_priv(dev);
@@ -2300,7 +2375,21 @@ static void rhine_remove_one(struct pci_dev *pdev)
        pci_disable_device(pdev);
 }
 
-static void rhine_shutdown (struct pci_dev *pdev)
+static int rhine_remove_one_platform(struct platform_device *pdev)
+{
+       struct net_device *dev = platform_get_drvdata(pdev);
+       struct rhine_private *rp = netdev_priv(dev);
+
+       unregister_netdev(dev);
+
+       iounmap(rp->base);
+
+       free_netdev(dev);
+
+       return 0;
+}
+
+static void rhine_shutdown_pci(struct pci_dev *pdev)
 {
        struct net_device *dev = pci_get_drvdata(pdev);
        struct rhine_private *rp = netdev_priv(dev);
@@ -2354,8 +2443,7 @@ static void rhine_shutdown (struct pci_dev *pdev)
 #ifdef CONFIG_PM_SLEEP
 static int rhine_suspend(struct device *device)
 {
-       struct pci_dev *pdev = to_pci_dev(device);
-       struct net_device *dev = pci_get_drvdata(pdev);
+       struct net_device *dev = dev_get_drvdata(device);
        struct rhine_private *rp = netdev_priv(dev);
 
        if (!netif_running(dev))
@@ -2367,23 +2455,21 @@ static int rhine_suspend(struct device *device)
 
        netif_device_detach(dev);
 
-       rhine_shutdown(pdev);
+       if (dev_is_pci(device))
+               rhine_shutdown_pci(to_pci_dev(device));
 
        return 0;
 }
 
 static int rhine_resume(struct device *device)
 {
-       struct pci_dev *pdev = to_pci_dev(device);
-       struct net_device *dev = pci_get_drvdata(pdev);
+       struct net_device *dev = dev_get_drvdata(device);
        struct rhine_private *rp = netdev_priv(dev);
 
        if (!netif_running(dev))
                return 0;
 
-#ifdef USE_MMIO
        enable_mmio(rp->pioaddr, rp->quirks);
-#endif
        rhine_power_init(dev);
        free_tbufs(dev);
        free_rbufs(dev);
@@ -2408,15 +2494,26 @@ static SIMPLE_DEV_PM_OPS(rhine_pm_ops, rhine_suspend, rhine_resume);
 
 #endif /* !CONFIG_PM_SLEEP */
 
-static struct pci_driver rhine_driver = {
+static struct pci_driver rhine_driver_pci = {
        .name           = DRV_NAME,
        .id_table       = rhine_pci_tbl,
-       .probe          = rhine_init_one,
-       .remove         = rhine_remove_one,
-       .shutdown       = rhine_shutdown,
+       .probe          = rhine_init_one_pci,
+       .remove         = rhine_remove_one_pci,
+       .shutdown       = rhine_shutdown_pci,
        .driver.pm      = RHINE_PM_OPS,
 };
 
+static struct platform_driver rhine_driver_platform = {
+       .probe          = rhine_init_one_platform,
+       .remove         = rhine_remove_one_platform,
+       .driver = {
+               .name   = DRV_NAME,
+               .owner  = THIS_MODULE,
+               .of_match_table = rhine_of_tbl,
+               .pm             = RHINE_PM_OPS,
+       }
+};
+
 static struct dmi_system_id rhine_dmi_table[] __initdata = {
        {
                .ident = "EPIA-M",
@@ -2437,6 +2534,8 @@ static struct dmi_system_id rhine_dmi_table[] __initdata = {
 
 static int __init rhine_init(void)
 {
+       int ret_pci, ret_platform;
+
 /* when a module, this is printed whether or not devices are found in probe */
 #ifdef MODULE
        pr_info("%s\n", version);
@@ -2449,13 +2548,19 @@ static int __init rhine_init(void)
        else if (avoid_D3)
                pr_info("avoid_D3 set\n");
 
-       return pci_register_driver(&rhine_driver);
+       ret_pci = pci_register_driver(&rhine_driver_pci);
+       ret_platform = platform_driver_register(&rhine_driver_platform);
+       if ((ret_pci < 0) && (ret_platform < 0))
+               return ret_pci;
+
+       return 0;
 }
 
 
 static void __exit rhine_cleanup(void)
 {
-       pci_unregister_driver(&rhine_driver);
+       platform_driver_unregister(&rhine_driver_platform);
+       pci_unregister_driver(&rhine_driver_pci);
 }
 
 
index fa193c4688da78719257ac982af8be1f81b270c1..4ef818a7a6c623719f0507cfc64b56ef3de709d9 100644 (file)
@@ -75,7 +75,7 @@ int temac_indirect_busywait(struct temac_local *lp)
        long end = jiffies + 2;
 
        while (!(temac_ior(lp, XTE_RDY0_OFFSET) & XTE_RDY0_HARD_ACS_RDY_MASK)) {
-               if (end - jiffies <= 0) {
+               if (time_before_eq(end, jiffies)) {
                        WARN_ON(1);
                        return -ETIMEDOUT;
                }
index 64b4639f43b6bea6b0e69155a7cb7043a14abcc4..d4abf478e2bbf6ae25f5925f406d27923b2b949c 100644 (file)
@@ -22,7 +22,7 @@ int axienet_mdio_wait_until_ready(struct axienet_local *lp)
        long end = jiffies + 2;
        while (!(axienet_ior(lp, XAE_MDIO_MCR_OFFSET) &
                 XAE_MDIO_MCR_READY_MASK)) {
-               if (end - jiffies <= 0) {
+               if (time_before_eq(end, jiffies)) {
                        WARN_ON(1);
                        return -ETIMEDOUT;
                }
index 0d87c67a5ff7208e807a980c406a934214c9d4a6..8c4aed3053ebc0a3a3757dcae408f25249f8e630 100644 (file)
@@ -702,7 +702,7 @@ static int xemaclite_mdio_wait(struct net_local *lp)
        */
        while (__raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET) &
                        XEL_MDIOCTRL_MDIOSTS_MASK) {
-               if (end - jiffies <= 0) {
+               if (time_before_eq(end, jiffies)) {
                        WARN_ON(1);
                        return -ETIMEDOUT;
                }
index d18f711d0b0cd45ae50cc30cc09a9468d4e40d22..6cc37c15e0bf98341131a8229bd7fbd2567994a8 100644 (file)
 #include <linux/hyperv.h>
 #include <linux/rndis.h>
 
-/* Fwd declaration */
-struct hv_netvsc_packet;
-struct ndis_tcp_ip_checksum_info;
+/* RSS related */
+#define OID_GEN_RECEIVE_SCALE_CAPABILITIES 0x00010203  /* query only */
+#define OID_GEN_RECEIVE_SCALE_PARAMETERS 0x00010204  /* query and set */
 
-/* Represent the xfer page packet which contains 1 or more netvsc packet */
-struct xferpage_packet {
-       struct list_head list_ent;
-       u32 status;
+#define NDIS_OBJECT_TYPE_RSS_CAPABILITIES 0x88
+#define NDIS_OBJECT_TYPE_RSS_PARAMETERS 0x89
 
-       /* # of netvsc packets this xfer packet contains */
-       u32 count;
+#define NDIS_RECEIVE_SCALE_CAPABILITIES_REVISION_2 2
+#define NDIS_RECEIVE_SCALE_PARAMETERS_REVISION_2 2
+
+struct ndis_obj_header {
+       u8 type;
+       u8 rev;
+       u16 size;
+} __packed;
+
+/* ndis_recv_scale_cap/cap_flag */
+#define NDIS_RSS_CAPS_MESSAGE_SIGNALED_INTERRUPTS 0x01000000
+#define NDIS_RSS_CAPS_CLASSIFICATION_AT_ISR       0x02000000
+#define NDIS_RSS_CAPS_CLASSIFICATION_AT_DPC       0x04000000
+#define NDIS_RSS_CAPS_USING_MSI_X                 0x08000000
+#define NDIS_RSS_CAPS_RSS_AVAILABLE_ON_PORTS      0x10000000
+#define NDIS_RSS_CAPS_SUPPORTS_MSI_X              0x20000000
+#define NDIS_RSS_CAPS_HASH_TYPE_TCP_IPV4          0x00000100
+#define NDIS_RSS_CAPS_HASH_TYPE_TCP_IPV6          0x00000200
+#define NDIS_RSS_CAPS_HASH_TYPE_TCP_IPV6_EX       0x00000400
+
+struct ndis_recv_scale_cap { /* NDIS_RECEIVE_SCALE_CAPABILITIES */
+       struct ndis_obj_header hdr;
+       u32 cap_flag;
+       u32 num_int_msg;
+       u32 num_recv_que;
+       u16 num_indirect_tabent;
+} __packed;
+
+
+/* ndis_recv_scale_param flags */
+#define NDIS_RSS_PARAM_FLAG_BASE_CPU_UNCHANGED     0x0001
+#define NDIS_RSS_PARAM_FLAG_HASH_INFO_UNCHANGED    0x0002
+#define NDIS_RSS_PARAM_FLAG_ITABLE_UNCHANGED       0x0004
+#define NDIS_RSS_PARAM_FLAG_HASH_KEY_UNCHANGED     0x0008
+#define NDIS_RSS_PARAM_FLAG_DISABLE_RSS            0x0010
+
+/* Hash info bits */
+#define NDIS_HASH_FUNC_TOEPLITZ 0x00000001
+#define NDIS_HASH_IPV4          0x00000100
+#define NDIS_HASH_TCP_IPV4      0x00000200
+#define NDIS_HASH_IPV6          0x00000400
+#define NDIS_HASH_IPV6_EX       0x00000800
+#define NDIS_HASH_TCP_IPV6      0x00001000
+#define NDIS_HASH_TCP_IPV6_EX   0x00002000
+
+#define NDIS_RSS_INDIRECTION_TABLE_MAX_SIZE_REVISION_2 (128 * 4)
+#define NDIS_RSS_HASH_SECRET_KEY_MAX_SIZE_REVISION_2   40
+
+#define ITAB_NUM 128
+#define HASH_KEYLEN NDIS_RSS_HASH_SECRET_KEY_MAX_SIZE_REVISION_2
+extern u8 netvsc_hash_key[];
+
+struct ndis_recv_scale_param { /* NDIS_RECEIVE_SCALE_PARAMETERS */
+       struct ndis_obj_header hdr;
+
+       /* Qualifies the rest of the information */
+       u16 flag;
+
+       /* The base CPU number to do receive processing. not used */
+       u16 base_cpu_number;
+
+       /* This describes the hash function and type being enabled */
+       u32 hashinfo;
+
+       /* The size of indirection table array */
+       u16 indirect_tabsize;
+
+       /* The offset of the indirection table from the beginning of this
+        * structure
+        */
+       u32 indirect_taboffset;
+
+       /* The size of the hash secret key */
+       u16 hashkey_size;
+
+       /* The offset of the secret key from the beginning of this structure */
+       u32 kashkey_offset;
+
+       u32 processor_masks_offset;
+       u32 num_processor_masks;
+       u32 processor_masks_entry_size;
 };
 
+/* Fwd declaration */
+struct ndis_tcp_ip_checksum_info;
+
 /*
  * Represent netvsc packet which contains 1 RNDIS and 1 ethernet frame
  * within the RNDIS
  */
 struct hv_netvsc_packet {
        /* Bookkeeping stuff */
-       struct list_head list_ent;
        u32 status;
 
        struct hv_device *device;
        bool is_data_pkt;
        u16 vlan_tci;
 
-       /*
-        * Valid only for receives when we break a xfer page packet
-        * into multiple netvsc packets
-        */
-       struct xferpage_packet *xfer_page_pkt;
+       u16 q_idx;
+       struct vmbus_channel *channel;
 
-       union {
-               struct {
-                       u64 recv_completion_tid;
-                       void *recv_completion_ctx;
-                       void (*recv_completion)(void *context);
-               } recv;
-               struct {
-                       u64 send_completion_tid;
-                       void *send_completion_ctx;
-                       void (*send_completion)(void *context);
-               } send;
-       } completion;
+       u64 send_completion_tid;
+       void *send_completion_ctx;
+       void (*send_completion)(void *context);
+
+       u32 send_buf_index;
 
        /* This points to the memory after page_buf */
        struct rndis_message *rndis_msg;
@@ -120,6 +189,7 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj,
 int netvsc_recv_callback(struct hv_device *device_obj,
                        struct hv_netvsc_packet *packet,
                        struct ndis_tcp_ip_checksum_info *csum_info);
+void netvsc_channel_cb(void *context);
 int rndis_filter_open(struct hv_device *dev);
 int rndis_filter_close(struct hv_device *dev);
 int rndis_filter_device_add(struct hv_device *dev,
@@ -514,14 +584,16 @@ struct nvsp_message {
 
 #define NETVSC_RECEIVE_BUFFER_SIZE             (1024*1024*16)  /* 16MB */
 #define NETVSC_RECEIVE_BUFFER_SIZE_LEGACY      (1024*1024*15)  /* 15MB */
+#define NETVSC_SEND_BUFFER_SIZE                        (1024 * 1024)   /* 1MB */
+#define NETVSC_INVALID_INDEX                   -1
 
-#define NETVSC_RECEIVE_BUFFER_ID               0xcafe
 
-/* Preallocated receive packets */
-#define NETVSC_RECEIVE_PACKETLIST_COUNT                256
+#define NETVSC_RECEIVE_BUFFER_ID               0xcafe
 
 #define NETVSC_PACKET_SIZE                      2048
 
+#define VRSS_SEND_TAB_SIZE 16
+
 /* Per netvsc channel-specific */
 struct netvsc_device {
        struct hv_device *dev;
@@ -532,12 +604,6 @@ struct netvsc_device {
        wait_queue_head_t wait_drain;
        bool start_remove;
        bool destroy;
-       /*
-        * List of free preallocated hv_netvsc_packet to represent receive
-        * packet
-        */
-       struct list_head recv_pkt_list;
-       spinlock_t recv_pkt_list_lock;
 
        /* Receive buffer allocated by us but manages by NetVSP */
        void *recv_buf;
@@ -546,6 +612,15 @@ struct netvsc_device {
        u32 recv_section_cnt;
        struct nvsp_1_receive_buffer_section *recv_section;
 
+       /* Send buffer allocated by us */
+       void *send_buf;
+       u32 send_buf_size;
+       u32 send_buf_gpadl_handle;
+       u32 send_section_cnt;
+       u32 send_section_size;
+       unsigned long *send_section_map;
+       int map_words;
+
        /* Used for NetVSP initialization protocol */
        struct completion channel_init_wait;
        struct nvsp_message channel_init_pkt;
@@ -555,10 +630,20 @@ struct netvsc_device {
 
        struct net_device *ndev;
 
+       struct vmbus_channel *chn_table[NR_CPUS];
+       u32 send_table[VRSS_SEND_TAB_SIZE];
+       u32 num_chn;
+       atomic_t queue_sends[NR_CPUS];
+
        /* Holds rndis device info */
        void *extension;
-       /* The recive buffer for this device */
+
+       int ring_size;
+
+       /* The primary channel callback buffer */
        unsigned char cb_buffer[NETVSC_PACKET_SIZE];
+       /* The sub channel callback buffer */
+       unsigned char *sub_cb_buf;
 };
 
 /* NdisInitialize message */
@@ -706,6 +791,7 @@ enum ndis_per_pkt_info_type {
        IEEE_8021Q_INFO,
        ORIGINAL_PKTINFO,
        PACKET_CANCEL_ID,
+       NBL_HASH_VALUE = PACKET_CANCEL_ID,
        ORIGINAL_NET_BUFLIST,
        CACHED_NET_BUFLIST,
        SHORT_PKT_PADINFO,
@@ -852,6 +938,9 @@ struct ndis_tcp_lso_info {
 #define NDIS_LSO_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \
                sizeof(struct ndis_tcp_lso_info))
 
+#define NDIS_HASH_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \
+               sizeof(u32))
+
 /* Format of Information buffer passed in a SetRequest for the OID */
 /* OID_GEN_RNDIS_CONFIG_PARAMETER. */
 struct rndis_config_parameter_info {
index f7629ecefa84a6d9c2d1cbb7b308a3d46a359915..c041f63a6d3053f51d5e3f6651bc1db0d0d6d25c 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/slab.h>
 #include <linux/netdevice.h>
 #include <linux/if_ether.h>
+#include <asm/sync_bitops.h>
 
 #include "hyperv_net.h"
 
@@ -80,7 +81,7 @@ static struct netvsc_device *get_inbound_net_device(struct hv_device *device)
 }
 
 
-static int netvsc_destroy_recv_buf(struct netvsc_device *net_device)
+static int netvsc_destroy_buf(struct netvsc_device *net_device)
 {
        struct nvsp_message *revoke_packet;
        int ret = 0;
@@ -146,10 +147,62 @@ static int netvsc_destroy_recv_buf(struct netvsc_device *net_device)
                net_device->recv_section = NULL;
        }
 
+       /* Deal with the send buffer we may have setup.
+        * If we got a  send section size, it means we received a
+        * SendsendBufferComplete msg (ie sent
+        * NvspMessage1TypeSendReceiveBuffer msg) therefore, we need
+        * to send a revoke msg here
+        */
+       if (net_device->send_section_size) {
+               /* Send the revoke receive buffer */
+               revoke_packet = &net_device->revoke_packet;
+               memset(revoke_packet, 0, sizeof(struct nvsp_message));
+
+               revoke_packet->hdr.msg_type =
+                       NVSP_MSG1_TYPE_REVOKE_SEND_BUF;
+               revoke_packet->msg.v1_msg.revoke_recv_buf.id = 0;
+
+               ret = vmbus_sendpacket(net_device->dev->channel,
+                                      revoke_packet,
+                                      sizeof(struct nvsp_message),
+                                      (unsigned long)revoke_packet,
+                                      VM_PKT_DATA_INBAND, 0);
+               /* If we failed here, we might as well return and
+                * have a leak rather than continue and a bugchk
+                */
+               if (ret != 0) {
+                       netdev_err(ndev, "unable to send "
+                                  "revoke send buffer to netvsp\n");
+                       return ret;
+               }
+       }
+       /* Teardown the gpadl on the vsp end */
+       if (net_device->send_buf_gpadl_handle) {
+               ret = vmbus_teardown_gpadl(net_device->dev->channel,
+                                          net_device->send_buf_gpadl_handle);
+
+               /* If we failed here, we might as well return and have a leak
+                * rather than continue and a bugchk
+                */
+               if (ret != 0) {
+                       netdev_err(ndev,
+                                  "unable to teardown send buffer's gpadl\n");
+                       return ret;
+               }
+               net_device->recv_buf_gpadl_handle = 0;
+       }
+       if (net_device->send_buf) {
+               /* Free up the receive buffer */
+               free_pages((unsigned long)net_device->send_buf,
+                          get_order(net_device->send_buf_size));
+               net_device->send_buf = NULL;
+       }
+       kfree(net_device->send_section_map);
+
        return ret;
 }
 
-static int netvsc_init_recv_buf(struct hv_device *device)
+static int netvsc_init_buf(struct hv_device *device)
 {
        int ret = 0;
        int t;
@@ -248,10 +301,90 @@ static int netvsc_init_recv_buf(struct hv_device *device)
                goto cleanup;
        }
 
+       /* Now setup the send buffer.
+        */
+       net_device->send_buf =
+               (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO,
+                                        get_order(net_device->send_buf_size));
+       if (!net_device->send_buf) {
+               netdev_err(ndev, "unable to allocate send "
+                          "buffer of size %d\n", net_device->send_buf_size);
+               ret = -ENOMEM;
+               goto cleanup;
+       }
+
+       /* Establish the gpadl handle for this buffer on this
+        * channel.  Note: This call uses the vmbus connection rather
+        * than the channel to establish the gpadl handle.
+        */
+       ret = vmbus_establish_gpadl(device->channel, net_device->send_buf,
+                                   net_device->send_buf_size,
+                                   &net_device->send_buf_gpadl_handle);
+       if (ret != 0) {
+               netdev_err(ndev,
+                          "unable to establish send buffer's gpadl\n");
+               goto cleanup;
+       }
+
+       /* Notify the NetVsp of the gpadl handle */
+       init_packet = &net_device->channel_init_pkt;
+       memset(init_packet, 0, sizeof(struct nvsp_message));
+       init_packet->hdr.msg_type = NVSP_MSG1_TYPE_SEND_SEND_BUF;
+       init_packet->msg.v1_msg.send_recv_buf.gpadl_handle =
+               net_device->send_buf_gpadl_handle;
+       init_packet->msg.v1_msg.send_recv_buf.id = 0;
+
+       /* Send the gpadl notification request */
+       ret = vmbus_sendpacket(device->channel, init_packet,
+                              sizeof(struct nvsp_message),
+                              (unsigned long)init_packet,
+                              VM_PKT_DATA_INBAND,
+                              VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+       if (ret != 0) {
+               netdev_err(ndev,
+                          "unable to send send buffer's gpadl to netvsp\n");
+               goto cleanup;
+       }
+
+       t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ);
+       BUG_ON(t == 0);
+
+       /* Check the response */
+       if (init_packet->msg.v1_msg.
+           send_send_buf_complete.status != NVSP_STAT_SUCCESS) {
+               netdev_err(ndev, "Unable to complete send buffer "
+                          "initialization with NetVsp - status %d\n",
+                          init_packet->msg.v1_msg.
+                          send_recv_buf_complete.status);
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       /* Parse the response */
+       net_device->send_section_size = init_packet->msg.
+                               v1_msg.send_send_buf_complete.section_size;
+
+       /* Section count is simply the size divided by the section size.
+        */
+       net_device->send_section_cnt =
+               net_device->send_buf_size/net_device->send_section_size;
+
+       dev_info(&device->device, "Send section size: %d, Section count:%d\n",
+                net_device->send_section_size, net_device->send_section_cnt);
+
+       /* Setup state for managing the send buffer. */
+       net_device->map_words = DIV_ROUND_UP(net_device->send_section_cnt,
+                                            BITS_PER_LONG);
+
+       net_device->send_section_map =
+               kzalloc(net_device->map_words * sizeof(ulong), GFP_KERNEL);
+       if (net_device->send_section_map == NULL)
+               goto cleanup;
+
        goto exit;
 
 cleanup:
-       netvsc_destroy_recv_buf(net_device);
+       netvsc_destroy_buf(net_device);
 
 exit:
        return ret;
@@ -369,8 +502,9 @@ static int netvsc_connect_vsp(struct hv_device *device)
                net_device->recv_buf_size = NETVSC_RECEIVE_BUFFER_SIZE_LEGACY;
        else
                net_device->recv_buf_size = NETVSC_RECEIVE_BUFFER_SIZE;
+       net_device->send_buf_size = NETVSC_SEND_BUFFER_SIZE;
 
-       ret = netvsc_init_recv_buf(device);
+       ret = netvsc_init_buf(device);
 
 cleanup:
        return ret;
@@ -378,7 +512,7 @@ static int netvsc_connect_vsp(struct hv_device *device)
 
 static void netvsc_disconnect_vsp(struct netvsc_device *net_device)
 {
-       netvsc_destroy_recv_buf(net_device);
+       netvsc_destroy_buf(net_device);
 }
 
 /*
@@ -387,7 +521,6 @@ static void netvsc_disconnect_vsp(struct netvsc_device *net_device)
 int netvsc_device_remove(struct hv_device *device)
 {
        struct netvsc_device *net_device;
-       struct hv_netvsc_packet *netvsc_packet, *pos;
        unsigned long flags;
 
        net_device = hv_get_drvdata(device);
@@ -416,11 +549,8 @@ int netvsc_device_remove(struct hv_device *device)
        vmbus_close(device->channel);
 
        /* Release all resources */
-       list_for_each_entry_safe(netvsc_packet, pos,
-                                &net_device->recv_pkt_list, list_ent) {
-               list_del(&netvsc_packet->list_ent);
-               kfree(netvsc_packet);
-       }
+       if (net_device->sub_cb_buf)
+               vfree(net_device->sub_cb_buf);
 
        kfree(net_device);
        return 0;
@@ -444,6 +574,12 @@ static inline u32 hv_ringbuf_avail_percent(
        return avail_write * 100 / ring_info->ring_datasize;
 }
 
+static inline void netvsc_free_send_slot(struct netvsc_device *net_device,
+                                        u32 index)
+{
+       sync_change_bit(index, net_device->send_section_map);
+}
+
 static void netvsc_send_completion(struct netvsc_device *net_device,
                                   struct hv_device *device,
                                   struct vmpacket_descriptor *packet)
@@ -451,6 +587,7 @@ static void netvsc_send_completion(struct netvsc_device *net_device,
        struct nvsp_message *nvsp_packet;
        struct hv_netvsc_packet *nvsc_packet;
        struct net_device *ndev;
+       u32 send_index;
 
        ndev = net_device->ndev;
 
@@ -461,7 +598,9 @@ static void netvsc_send_completion(struct netvsc_device *net_device,
            (nvsp_packet->hdr.msg_type ==
             NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE) ||
            (nvsp_packet->hdr.msg_type ==
-            NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE)) {
+            NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE) ||
+           (nvsp_packet->hdr.msg_type ==
+            NVSP_MSG5_TYPE_SUBCHANNEL)) {
                /* Copy the response back */
                memcpy(&net_device->channel_init_pkt, nvsp_packet,
                       sizeof(struct nvsp_message));
@@ -469,28 +608,39 @@ static void netvsc_send_completion(struct netvsc_device *net_device,
        } else if (nvsp_packet->hdr.msg_type ==
                   NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE) {
                int num_outstanding_sends;
+               u16 q_idx = 0;
+               struct vmbus_channel *channel = device->channel;
+               int queue_sends;
 
                /* Get the send context */
                nvsc_packet = (struct hv_netvsc_packet *)(unsigned long)
                        packet->trans_id;
 
                /* Notify the layer above us */
-               if (nvsc_packet)
-                       nvsc_packet->completion.send.send_completion(
-                               nvsc_packet->completion.send.
-                               send_completion_ctx);
+               if (nvsc_packet) {
+                       send_index = nvsc_packet->send_buf_index;
+                       if (send_index != NETVSC_INVALID_INDEX)
+                               netvsc_free_send_slot(net_device, send_index);
+                       q_idx = nvsc_packet->q_idx;
+                       channel = nvsc_packet->channel;
+                       nvsc_packet->send_completion(nvsc_packet->
+                                                    send_completion_ctx);
+               }
 
                num_outstanding_sends =
                        atomic_dec_return(&net_device->num_outstanding_sends);
+               queue_sends = atomic_dec_return(&net_device->
+                                               queue_sends[q_idx]);
 
                if (net_device->destroy && num_outstanding_sends == 0)
                        wake_up(&net_device->wait_drain);
 
-               if (netif_queue_stopped(ndev) && !net_device->start_remove &&
-                       (hv_ringbuf_avail_percent(&device->channel->outbound)
-                       > RING_AVAIL_PERCENT_HIWATER ||
-                       num_outstanding_sends < 1))
-                               netif_wake_queue(ndev);
+               if (netif_tx_queue_stopped(netdev_get_tx_queue(ndev, q_idx)) &&
+                   !net_device->start_remove &&
+                   (hv_ringbuf_avail_percent(&channel->outbound) >
+                    RING_AVAIL_PERCENT_HIWATER || queue_sends < 1))
+                               netif_tx_wake_queue(netdev_get_tx_queue(
+                                                   ndev, q_idx));
        } else {
                netdev_err(ndev, "Unknown send completion packet type- "
                           "%d received!!\n", nvsp_packet->hdr.msg_type);
@@ -498,6 +648,52 @@ static void netvsc_send_completion(struct netvsc_device *net_device,
 
 }
 
+static u32 netvsc_get_next_send_section(struct netvsc_device *net_device)
+{
+       unsigned long index;
+       u32 max_words = net_device->map_words;
+       unsigned long *map_addr = (unsigned long *)net_device->send_section_map;
+       u32 section_cnt = net_device->send_section_cnt;
+       int ret_val = NETVSC_INVALID_INDEX;
+       int i;
+       int prev_val;
+
+       for (i = 0; i < max_words; i++) {
+               if (!~(map_addr[i]))
+                       continue;
+               index = ffz(map_addr[i]);
+               prev_val = sync_test_and_set_bit(index, &map_addr[i]);
+               if (prev_val)
+                       continue;
+               if ((index + (i * BITS_PER_LONG)) >= section_cnt)
+                       break;
+               ret_val = (index + (i * BITS_PER_LONG));
+               break;
+       }
+       return ret_val;
+}
+
+u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device,
+                           unsigned int section_index,
+                           struct hv_netvsc_packet *packet)
+{
+       char *start = net_device->send_buf;
+       char *dest = (start + (section_index * net_device->send_section_size));
+       int i;
+       u32 msg_size = 0;
+
+       for (i = 0; i < packet->page_buf_cnt; i++) {
+               char *src = phys_to_virt(packet->page_buf[i].pfn << PAGE_SHIFT);
+               u32 offset = packet->page_buf[i].offset;
+               u32 len = packet->page_buf[i].len;
+
+               memcpy(dest, (src + offset), len);
+               msg_size += len;
+               dest += len;
+       }
+       return msg_size;
+}
+
 int netvsc_send(struct hv_device *device,
                        struct hv_netvsc_packet *packet)
 {
@@ -505,7 +701,12 @@ int netvsc_send(struct hv_device *device,
        int ret = 0;
        struct nvsp_message sendMessage;
        struct net_device *ndev;
+       struct vmbus_channel *out_channel = NULL;
        u64 req_id;
+       unsigned int section_index = NETVSC_INVALID_INDEX;
+       u32 msg_size = 0;
+       struct sk_buff *skb;
+
 
        net_device = get_outbound_net_device(device);
        if (!net_device)
@@ -521,25 +722,46 @@ int netvsc_send(struct hv_device *device,
                sendMessage.msg.v1_msg.send_rndis_pkt.channel_type = 1;
        }
 
-       /* Not using send buffer section */
+       /* Attempt to send via sendbuf */
+       if (packet->total_data_buflen < net_device->send_section_size) {
+               section_index = netvsc_get_next_send_section(net_device);
+               if (section_index != NETVSC_INVALID_INDEX) {
+                       msg_size = netvsc_copy_to_send_buf(net_device,
+                                                          section_index,
+                                                          packet);
+                       skb = (struct sk_buff *)
+                             (unsigned long)packet->send_completion_tid;
+                       if (skb)
+                               dev_kfree_skb_any(skb);
+                       packet->page_buf_cnt = 0;
+               }
+       }
+       packet->send_buf_index = section_index;
+
+
        sendMessage.msg.v1_msg.send_rndis_pkt.send_buf_section_index =
-               0xFFFFFFFF;
-       sendMessage.msg.v1_msg.send_rndis_pkt.send_buf_section_size = 0;
+               section_index;
+       sendMessage.msg.v1_msg.send_rndis_pkt.send_buf_section_size = msg_size;
 
-       if (packet->completion.send.send_completion)
+       if (packet->send_completion)
                req_id = (ulong)packet;
        else
                req_id = 0;
 
+       out_channel = net_device->chn_table[packet->q_idx];
+       if (out_channel == NULL)
+               out_channel = device->channel;
+       packet->channel = out_channel;
+
        if (packet->page_buf_cnt) {
-               ret = vmbus_sendpacket_pagebuffer(device->channel,
+               ret = vmbus_sendpacket_pagebuffer(out_channel,
                                                  packet->page_buf,
                                                  packet->page_buf_cnt,
                                                  &sendMessage,
                                                  sizeof(struct nvsp_message),
                                                  req_id);
        } else {
-               ret = vmbus_sendpacket(device->channel, &sendMessage,
+               ret = vmbus_sendpacket(out_channel, &sendMessage,
                                sizeof(struct nvsp_message),
                                req_id,
                                VM_PKT_DATA_INBAND,
@@ -548,17 +770,24 @@ int netvsc_send(struct hv_device *device,
 
        if (ret == 0) {
                atomic_inc(&net_device->num_outstanding_sends);
-               if (hv_ringbuf_avail_percent(&device->channel->outbound) <
+               atomic_inc(&net_device->queue_sends[packet->q_idx]);
+
+               if (hv_ringbuf_avail_percent(&out_channel->outbound) <
                        RING_AVAIL_PERCENT_LOWATER) {
-                       netif_stop_queue(ndev);
+                       netif_tx_stop_queue(netdev_get_tx_queue(
+                                           ndev, packet->q_idx));
+
                        if (atomic_read(&net_device->
-                               num_outstanding_sends) < 1)
-                               netif_wake_queue(ndev);
+                               queue_sends[packet->q_idx]) < 1)
+                               netif_tx_wake_queue(netdev_get_tx_queue(
+                                                   ndev, packet->q_idx));
                }
        } else if (ret == -EAGAIN) {
-               netif_stop_queue(ndev);
-               if (atomic_read(&net_device->num_outstanding_sends) < 1) {
-                       netif_wake_queue(ndev);
+               netif_tx_stop_queue(netdev_get_tx_queue(
+                                   ndev, packet->q_idx));
+               if (atomic_read(&net_device->queue_sends[packet->q_idx]) < 1) {
+                       netif_tx_wake_queue(netdev_get_tx_queue(
+                                           ndev, packet->q_idx));
                        ret = -ENOSPC;
                }
        } else {
@@ -570,6 +799,7 @@ int netvsc_send(struct hv_device *device,
 }
 
 static void netvsc_send_recv_completion(struct hv_device *device,
+                                       struct vmbus_channel *channel,
                                        struct netvsc_device *net_device,
                                        u64 transaction_id, u32 status)
 {
@@ -587,7 +817,7 @@ static void netvsc_send_recv_completion(struct hv_device *device,
 
 retry_send_cmplt:
        /* Send the completion */
-       ret = vmbus_sendpacket(device->channel, &recvcompMessage,
+       ret = vmbus_sendpacket(channel, &recvcompMessage,
                               sizeof(struct nvsp_message), transaction_id,
                               VM_PKT_COMP, 0);
        if (ret == 0) {
@@ -613,76 +843,20 @@ static void netvsc_send_recv_completion(struct hv_device *device,
        }
 }
 
-/* Send a receive completion packet to RNDIS device (ie NetVsp) */
-static void netvsc_receive_completion(void *context)
-{
-       struct hv_netvsc_packet *packet = context;
-       struct hv_device *device = packet->device;
-       struct netvsc_device *net_device;
-       u64 transaction_id = 0;
-       bool fsend_receive_comp = false;
-       unsigned long flags;
-       struct net_device *ndev;
-       u32 status = NVSP_STAT_NONE;
-
-       /*
-        * Even though it seems logical to do a GetOutboundNetDevice() here to
-        * send out receive completion, we are using GetInboundNetDevice()
-        * since we may have disable outbound traffic already.
-        */
-       net_device = get_inbound_net_device(device);
-       if (!net_device)
-               return;
-       ndev = net_device->ndev;
-
-       /* Overloading use of the lock. */
-       spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags);
-
-       if (packet->status != NVSP_STAT_SUCCESS)
-               packet->xfer_page_pkt->status = NVSP_STAT_FAIL;
-
-       packet->xfer_page_pkt->count--;
-
-       /*
-        * Last one in the line that represent 1 xfer page packet.
-        * Return the xfer page packet itself to the freelist
-        */
-       if (packet->xfer_page_pkt->count == 0) {
-               fsend_receive_comp = true;
-               transaction_id = packet->completion.recv.recv_completion_tid;
-               status = packet->xfer_page_pkt->status;
-               list_add_tail(&packet->xfer_page_pkt->list_ent,
-                             &net_device->recv_pkt_list);
-
-       }
-
-       /* Put the packet back */
-       list_add_tail(&packet->list_ent, &net_device->recv_pkt_list);
-       spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, flags);
-
-       /* Send a receive completion for the xfer page packet */
-       if (fsend_receive_comp)
-               netvsc_send_recv_completion(device, net_device, transaction_id,
-                                       status);
-
-}
-
 static void netvsc_receive(struct netvsc_device *net_device,
+                       struct vmbus_channel *channel,
                        struct hv_device *device,
                        struct vmpacket_descriptor *packet)
 {
        struct vmtransfer_page_packet_header *vmxferpage_packet;
        struct nvsp_message *nvsp_packet;
-       struct hv_netvsc_packet *netvsc_packet = NULL;
-       /* struct netvsc_driver *netvscDriver; */
-       struct xferpage_packet *xferpage_packet = NULL;
+       struct hv_netvsc_packet nv_pkt;
+       struct hv_netvsc_packet *netvsc_packet = &nv_pkt;
+       u32 status = NVSP_STAT_SUCCESS;
        int i;
        int count = 0;
-       unsigned long flags;
        struct net_device *ndev;
 
-       LIST_HEAD(listHead);
-
        ndev = net_device->ndev;
 
        /*
@@ -715,77 +889,14 @@ static void netvsc_receive(struct netvsc_device *net_device,
                return;
        }
 
-       /*
-        * Grab free packets (range count + 1) to represent this xfer
-        * page packet. +1 to represent the xfer page packet itself.
-        * We grab it here so that we know exactly how many we can
-        * fulfil
-        */
-       spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags);
-       while (!list_empty(&net_device->recv_pkt_list)) {
-               list_move_tail(net_device->recv_pkt_list.next, &listHead);
-               if (++count == vmxferpage_packet->range_cnt + 1)
-                       break;
-       }
-       spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, flags);
-
-       /*
-        * We need at least 2 netvsc pkts (1 to represent the xfer
-        * page and at least 1 for the range) i.e. we can handled
-        * some of the xfer page packet ranges...
-        */
-       if (count < 2) {
-               netdev_err(ndev, "Got only %d netvsc pkt...needed "
-                       "%d pkts. Dropping this xfer page packet completely!\n",
-                       count, vmxferpage_packet->range_cnt + 1);
-
-               /* Return it to the freelist */
-               spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags);
-               for (i = count; i != 0; i--) {
-                       list_move_tail(listHead.next,
-                                      &net_device->recv_pkt_list);
-               }
-               spin_unlock_irqrestore(&net_device->recv_pkt_list_lock,
-                                      flags);
-
-               netvsc_send_recv_completion(device, net_device,
-                                           vmxferpage_packet->d.trans_id,
-                                           NVSP_STAT_FAIL);
-
-               return;
-       }
-
-       /* Remove the 1st packet to represent the xfer page packet itself */
-       xferpage_packet = (struct xferpage_packet *)listHead.next;
-       list_del(&xferpage_packet->list_ent);
-       xferpage_packet->status = NVSP_STAT_SUCCESS;
-
-       /* This is how much we can satisfy */
-       xferpage_packet->count = count - 1;
-
-       if (xferpage_packet->count != vmxferpage_packet->range_cnt) {
-               netdev_err(ndev, "Needed %d netvsc pkts to satisfy "
-                       "this xfer page...got %d\n",
-                       vmxferpage_packet->range_cnt, xferpage_packet->count);
-       }
+       count = vmxferpage_packet->range_cnt;
+       netvsc_packet->device = device;
+       netvsc_packet->channel = channel;
 
        /* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */
-       for (i = 0; i < (count - 1); i++) {
-               netvsc_packet = (struct hv_netvsc_packet *)listHead.next;
-               list_del(&netvsc_packet->list_ent);
-
+       for (i = 0; i < count; i++) {
                /* Initialize the netvsc packet */
                netvsc_packet->status = NVSP_STAT_SUCCESS;
-               netvsc_packet->xfer_page_pkt = xferpage_packet;
-               netvsc_packet->completion.recv.recv_completion =
-                                       netvsc_receive_completion;
-               netvsc_packet->completion.recv.recv_completion_ctx =
-                                       netvsc_packet;
-               netvsc_packet->device = device;
-               /* Save this so that we can send it back */
-               netvsc_packet->completion.recv.recv_completion_tid =
-                                       vmxferpage_packet->d.trans_id;
-
                netvsc_packet->data = (void *)((unsigned long)net_device->
                        recv_buf + vmxferpage_packet->ranges[i].byte_offset);
                netvsc_packet->total_data_buflen =
@@ -794,16 +905,53 @@ static void netvsc_receive(struct netvsc_device *net_device,
                /* Pass it to the upper layer */
                rndis_filter_receive(device, netvsc_packet);
 
-               netvsc_receive_completion(netvsc_packet->
-                               completion.recv.recv_completion_ctx);
+               if (netvsc_packet->status != NVSP_STAT_SUCCESS)
+                       status = NVSP_STAT_FAIL;
+       }
+
+       netvsc_send_recv_completion(device, channel, net_device,
+                                   vmxferpage_packet->d.trans_id, status);
+}
+
+
+static void netvsc_send_table(struct hv_device *hdev,
+                             struct vmpacket_descriptor *vmpkt)
+{
+       struct netvsc_device *nvscdev;
+       struct net_device *ndev;
+       struct nvsp_message *nvmsg;
+       int i;
+       u32 count, *tab;
+
+       nvscdev = get_outbound_net_device(hdev);
+       if (!nvscdev)
+               return;
+       ndev = nvscdev->ndev;
+
+       nvmsg = (struct nvsp_message *)((unsigned long)vmpkt +
+                                       (vmpkt->offset8 << 3));
+
+       if (nvmsg->hdr.msg_type != NVSP_MSG5_TYPE_SEND_INDIRECTION_TABLE)
+               return;
+
+       count = nvmsg->msg.v5_msg.send_table.count;
+       if (count != VRSS_SEND_TAB_SIZE) {
+               netdev_err(ndev, "Received wrong send-table size:%u\n", count);
+               return;
        }
 
+       tab = (u32 *)((unsigned long)&nvmsg->msg.v5_msg.send_table +
+                     nvmsg->msg.v5_msg.send_table.offset);
+
+       for (i = 0; i < count; i++)
+               nvscdev->send_table[i] = tab[i];
 }
 
-static void netvsc_channel_cb(void *context)
+void netvsc_channel_cb(void *context)
 {
        int ret;
-       struct hv_device *device = context;
+       struct vmbus_channel *channel = (struct vmbus_channel *)context;
+       struct hv_device *device;
        struct netvsc_device *net_device;
        u32 bytes_recvd;
        u64 request_id;
@@ -812,14 +960,19 @@ static void netvsc_channel_cb(void *context)
        int bufferlen = NETVSC_PACKET_SIZE;
        struct net_device *ndev;
 
+       if (channel->primary_channel != NULL)
+               device = channel->primary_channel->device_obj;
+       else
+               device = channel->device_obj;
+
        net_device = get_inbound_net_device(device);
        if (!net_device)
                return;
        ndev = net_device->ndev;
-       buffer = net_device->cb_buffer;
+       buffer = get_per_channel_state(channel);
 
        do {
-               ret = vmbus_recvpacket_raw(device->channel, buffer, bufferlen,
+               ret = vmbus_recvpacket_raw(channel, buffer, bufferlen,
                                           &bytes_recvd, &request_id);
                if (ret == 0) {
                        if (bytes_recvd > 0) {
@@ -831,8 +984,12 @@ static void netvsc_channel_cb(void *context)
                                        break;
 
                                case VM_PKT_DATA_USING_XFER_PAGES:
-                                       netvsc_receive(net_device,
-                                                       device, desc);
+                                       netvsc_receive(net_device, channel,
+                                                      device, desc);
+                                       break;
+
+                               case VM_PKT_DATA_INBAND:
+                                       netvsc_send_table(device, desc);
                                        break;
 
                                default:
@@ -880,11 +1037,9 @@ static void netvsc_channel_cb(void *context)
 int netvsc_device_add(struct hv_device *device, void *additional_info)
 {
        int ret = 0;
-       int i;
        int ring_size =
        ((struct netvsc_device_info *)additional_info)->ring_size;
        struct netvsc_device *net_device;
-       struct hv_netvsc_packet *packet, *pos;
        struct net_device *ndev;
 
        net_device = alloc_net_device(device);
@@ -893,6 +1048,8 @@ int netvsc_device_add(struct hv_device *device, void *additional_info)
                goto cleanup;
        }
 
+       net_device->ring_size = ring_size;
+
        /*
         * Coming into this function, struct net_device * is
         * registered as the driver private data.
@@ -903,24 +1060,14 @@ int netvsc_device_add(struct hv_device *device, void *additional_info)
        ndev = net_device->ndev;
 
        /* Initialize the NetVSC channel extension */
-       spin_lock_init(&net_device->recv_pkt_list_lock);
-
-       INIT_LIST_HEAD(&net_device->recv_pkt_list);
-
-       for (i = 0; i < NETVSC_RECEIVE_PACKETLIST_COUNT; i++) {
-               packet = kzalloc(sizeof(struct hv_netvsc_packet), GFP_KERNEL);
-               if (!packet)
-                       break;
-
-               list_add_tail(&packet->list_ent,
-                             &net_device->recv_pkt_list);
-       }
        init_completion(&net_device->channel_init_wait);
 
+       set_per_channel_state(device->channel, net_device->cb_buffer);
+
        /* Open the channel */
        ret = vmbus_open(device->channel, ring_size * PAGE_SIZE,
                         ring_size * PAGE_SIZE, NULL, 0,
-                        netvsc_channel_cb, device);
+                        netvsc_channel_cb, device->channel);
 
        if (ret != 0) {
                netdev_err(ndev, "unable to open channel: %d\n", ret);
@@ -930,6 +1077,8 @@ int netvsc_device_add(struct hv_device *device, void *additional_info)
        /* Channel is opened */
        pr_info("hv_netvsc channel opened successfully\n");
 
+       net_device->chn_table[0] = device->channel;
+
        /* Connect with the NetVsp */
        ret = netvsc_connect_vsp(device);
        if (ret != 0) {
@@ -946,16 +1095,8 @@ int netvsc_device_add(struct hv_device *device, void *additional_info)
 
 cleanup:
 
-       if (net_device) {
-               list_for_each_entry_safe(packet, pos,
-                                        &net_device->recv_pkt_list,
-                                        list_ent) {
-                       list_del(&packet->list_ent);
-                       kfree(packet);
-               }
-
+       if (net_device)
                kfree(net_device);
-       }
 
        return ret;
 }
index 7918d5132c1f14764cda43daf9f98d7f7c01f310..4fd71b75e666418ab7063447160d561a294a0966 100644 (file)
@@ -101,7 +101,7 @@ static int netvsc_open(struct net_device *net)
                return ret;
        }
 
-       netif_start_queue(net);
+       netif_tx_start_all_queues(net);
 
        nvdev = hv_get_drvdata(device_obj);
        rdev = nvdev->extension;
@@ -149,15 +149,100 @@ static void *init_ppi_data(struct rndis_message *msg, u32 ppi_size,
        return ppi;
 }
 
+union sub_key {
+       u64 k;
+       struct {
+               u8 pad[3];
+               u8 kb;
+               u32 ka;
+       };
+};
+
+/* Toeplitz hash function
+ * data: network byte order
+ * return: host byte order
+ */
+static u32 comp_hash(u8 *key, int klen, u8 *data, int dlen)
+{
+       union sub_key subk;
+       int k_next = 4;
+       u8 dt;
+       int i, j;
+       u32 ret = 0;
+
+       subk.k = 0;
+       subk.ka = ntohl(*(u32 *)key);
+
+       for (i = 0; i < dlen; i++) {
+               subk.kb = key[k_next];
+               k_next = (k_next + 1) % klen;
+               dt = data[i];
+               for (j = 0; j < 8; j++) {
+                       if (dt & 0x80)
+                               ret ^= subk.ka;
+                       dt <<= 1;
+                       subk.k <<= 1;
+               }
+       }
+
+       return ret;
+}
+
+static bool netvsc_set_hash(u32 *hash, struct sk_buff *skb)
+{
+       struct iphdr *iphdr;
+       int data_len;
+       bool ret = false;
+
+       if (eth_hdr(skb)->h_proto != htons(ETH_P_IP))
+               return false;
+
+       iphdr = ip_hdr(skb);
+
+       if (iphdr->version == 4) {
+               if (iphdr->protocol == IPPROTO_TCP)
+                       data_len = 12;
+               else
+                       data_len = 8;
+               *hash = comp_hash(netvsc_hash_key, HASH_KEYLEN,
+                                 (u8 *)&iphdr->saddr, data_len);
+               ret = true;
+       }
+
+       return ret;
+}
+
+static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb,
+                       void *accel_priv, select_queue_fallback_t fallback)
+{
+       struct net_device_context *net_device_ctx = netdev_priv(ndev);
+       struct hv_device *hdev =  net_device_ctx->device_ctx;
+       struct netvsc_device *nvsc_dev = hv_get_drvdata(hdev);
+       u32 hash;
+       u16 q_idx = 0;
+
+       if (nvsc_dev == NULL || ndev->real_num_tx_queues <= 1)
+               return 0;
+
+       if (netvsc_set_hash(&hash, skb)) {
+               q_idx = nvsc_dev->send_table[hash % VRSS_SEND_TAB_SIZE] %
+                       ndev->real_num_tx_queues;
+               skb_set_hash(skb, hash, PKT_HASH_TYPE_L3);
+       }
+
+       return q_idx;
+}
+
 static void netvsc_xmit_completion(void *context)
 {
        struct hv_netvsc_packet *packet = (struct hv_netvsc_packet *)context;
        struct sk_buff *skb = (struct sk_buff *)
-               (unsigned long)packet->completion.send.send_completion_tid;
+               (unsigned long)packet->send_completion_tid;
+       u32 index = packet->send_buf_index;
 
        kfree(packet);
 
-       if (skb)
+       if (skb && (index == NETVSC_INVALID_INDEX))
                dev_kfree_skb_any(skb);
 }
 
@@ -301,6 +386,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
        struct ndis_tcp_lso_info *lso_info;
        int  hdr_offset;
        u32 net_trans_info;
+       u32 hash;
 
 
        /* We will atmost need two pages to describe the rndis
@@ -319,9 +405,8 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
        packet = kzalloc(sizeof(struct hv_netvsc_packet) +
                         (num_data_pgs * sizeof(struct hv_page_buffer)) +
                         sizeof(struct rndis_message) +
-                        NDIS_VLAN_PPI_SIZE +
-                        NDIS_CSUM_PPI_SIZE +
-                        NDIS_LSO_PPI_SIZE, GFP_ATOMIC);
+                        NDIS_VLAN_PPI_SIZE + NDIS_CSUM_PPI_SIZE +
+                        NDIS_LSO_PPI_SIZE + NDIS_HASH_PPI_SIZE, GFP_ATOMIC);
        if (!packet) {
                /* out of memory, drop packet */
                netdev_err(net, "unable to allocate hv_netvsc_packet\n");
@@ -333,6 +418,8 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
 
        packet->vlan_tci = skb->vlan_tci;
 
+       packet->q_idx = skb_get_queue_mapping(skb);
+
        packet->is_data_pkt = true;
        packet->total_data_buflen = skb->len;
 
@@ -341,9 +428,9 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
                                (num_data_pgs * sizeof(struct hv_page_buffer)));
 
        /* Set the completion routine */
-       packet->completion.send.send_completion = netvsc_xmit_completion;
-       packet->completion.send.send_completion_ctx = packet;
-       packet->completion.send.send_completion_tid = (unsigned long)skb;
+       packet->send_completion = netvsc_xmit_completion;
+       packet->send_completion_ctx = packet;
+       packet->send_completion_tid = (unsigned long)skb;
 
        isvlan = packet->vlan_tci & VLAN_TAG_PRESENT;
 
@@ -358,6 +445,14 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
 
        rndis_msg_size = RNDIS_MESSAGE_SIZE(struct rndis_packet);
 
+       hash = skb_get_hash_raw(skb);
+       if (hash != 0 && net->real_num_tx_queues > 1) {
+               rndis_msg_size += NDIS_HASH_PPI_SIZE;
+               ppi = init_ppi_data(rndis_msg, NDIS_HASH_PPI_SIZE,
+                                   NBL_HASH_VALUE);
+               *(u32 *)((void *)ppi + ppi->ppi_offset) = hash;
+       }
+
        if (isvlan) {
                struct ndis_pkt_8021q_info *vlan;
 
@@ -558,6 +653,9 @@ int netvsc_recv_callback(struct hv_device *device_obj,
                __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
                                       packet->vlan_tci);
 
+       skb_record_rx_queue(skb, packet->channel->
+                           offermsg.offer.sub_channel_index);
+
        net->stats.rx_packets++;
        net->stats.rx_bytes += packet->total_data_buflen;
 
@@ -606,7 +704,7 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu)
        hv_set_drvdata(hdev, ndev);
        device_info.ring_size = ring_size;
        rndis_filter_device_add(hdev, &device_info);
-       netif_wake_queue(ndev);
+       netif_tx_wake_all_queues(ndev);
 
        return 0;
 }
@@ -652,6 +750,7 @@ static const struct net_device_ops device_ops = {
        .ndo_change_mtu =               netvsc_change_mtu,
        .ndo_validate_addr =            eth_validate_addr,
        .ndo_set_mac_address =          netvsc_set_mac_addr,
+       .ndo_select_queue =             netvsc_select_queue,
 };
 
 /*
@@ -698,9 +797,11 @@ static int netvsc_probe(struct hv_device *dev,
        struct net_device *net = NULL;
        struct net_device_context *net_device_ctx;
        struct netvsc_device_info device_info;
+       struct netvsc_device *nvdev;
        int ret;
 
-       net = alloc_etherdev(sizeof(struct net_device_context));
+       net = alloc_etherdev_mq(sizeof(struct net_device_context),
+                               num_online_cpus());
        if (!net)
                return -ENOMEM;
 
@@ -719,7 +820,7 @@ static int netvsc_probe(struct hv_device *dev,
        net->features = NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_SG | NETIF_F_RXCSUM |
                        NETIF_F_IP_CSUM | NETIF_F_TSO;
 
-       SET_ETHTOOL_OPS(net, &ethtool_ops);
+       net->ethtool_ops = &ethtool_ops;
        SET_NETDEV_DEV(net, &dev->device);
 
        /* Notify the netvsc driver of the new device */
@@ -733,6 +834,10 @@ static int netvsc_probe(struct hv_device *dev,
        }
        memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN);
 
+       nvdev = hv_get_drvdata(dev);
+       netif_set_real_num_tx_queues(net, nvdev->num_chn);
+       netif_set_real_num_rx_queues(net, nvdev->num_chn);
+
        ret = register_netdev(net);
        if (ret != 0) {
                pr_err("Unable to register netdev.\n");
index 143a98caf618a90cd45ac002005c506f5ca14a33..99c527adae5bf1ee154b2a02eb0f93a6df32e333 100644 (file)
@@ -31,7 +31,7 @@
 #include "hyperv_net.h"
 
 
-#define RNDIS_EXT_LEN 100
+#define RNDIS_EXT_LEN PAGE_SIZE
 struct rndis_request {
        struct list_head list_ent;
        struct completion  wait_event;
@@ -94,6 +94,8 @@ static struct rndis_request *get_rndis_request(struct rndis_device *dev,
        rndis_msg->ndis_msg_type = msg_type;
        rndis_msg->msg_len = msg_len;
 
+       request->pkt.q_idx = 0;
+
        /*
         * Set the request id. This field is always after the rndis header for
         * request/response packet types so we just used the SetRequest as a
@@ -234,7 +236,7 @@ static int rndis_filter_send_request(struct rndis_device *dev,
                        packet->page_buf[0].len;
        }
 
-       packet->completion.send.send_completion = NULL;
+       packet->send_completion = NULL;
 
        ret = netvsc_send(dev->net_dev->dev, packet);
        return ret;
@@ -399,8 +401,6 @@ static void rndis_filter_receive_data(struct rndis_device *dev,
        pkt->total_data_buflen = rndis_pkt->data_len;
        pkt->data = (void *)((unsigned long)pkt->data + data_offset);
 
-       pkt->is_data_pkt = true;
-
        vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO);
        if (vlan) {
                pkt->vlan_tci = VLAN_TAG_PRESENT | vlan->vlanid |
@@ -509,6 +509,19 @@ static int rndis_filter_query_device(struct rndis_device *dev, u32 oid,
        query->info_buflen = 0;
        query->dev_vc_handle = 0;
 
+       if (oid == OID_GEN_RECEIVE_SCALE_CAPABILITIES) {
+               struct ndis_recv_scale_cap *cap;
+
+               request->request_msg.msg_len +=
+                       sizeof(struct ndis_recv_scale_cap);
+               query->info_buflen = sizeof(struct ndis_recv_scale_cap);
+               cap = (struct ndis_recv_scale_cap *)((unsigned long)query +
+                                                    query->info_buf_offset);
+               cap->hdr.type = NDIS_OBJECT_TYPE_RSS_CAPABILITIES;
+               cap->hdr.rev = NDIS_RECEIVE_SCALE_CAPABILITIES_REVISION_2;
+               cap->hdr.size = sizeof(struct ndis_recv_scale_cap);
+       }
+
        ret = rndis_filter_send_request(dev, request);
        if (ret != 0)
                goto cleanup;
@@ -695,6 +708,89 @@ int rndis_filter_set_offload_params(struct hv_device *hdev,
        return ret;
 }
 
+u8 netvsc_hash_key[HASH_KEYLEN] = {
+       0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
+       0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
+       0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
+       0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
+       0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
+};
+
+int rndis_filter_set_rss_param(struct rndis_device *rdev, int num_queue)
+{
+       struct net_device *ndev = rdev->net_dev->ndev;
+       struct rndis_request *request;
+       struct rndis_set_request *set;
+       struct rndis_set_complete *set_complete;
+       u32 extlen = sizeof(struct ndis_recv_scale_param) +
+                    4*ITAB_NUM + HASH_KEYLEN;
+       struct ndis_recv_scale_param *rssp;
+       u32 *itab;
+       u8 *keyp;
+       int i, t, ret;
+
+       request = get_rndis_request(
+                       rdev, RNDIS_MSG_SET,
+                       RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
+       if (!request)
+               return -ENOMEM;
+
+       set = &request->request_msg.msg.set_req;
+       set->oid = OID_GEN_RECEIVE_SCALE_PARAMETERS;
+       set->info_buflen = extlen;
+       set->info_buf_offset = sizeof(struct rndis_set_request);
+       set->dev_vc_handle = 0;
+
+       rssp = (struct ndis_recv_scale_param *)(set + 1);
+       rssp->hdr.type = NDIS_OBJECT_TYPE_RSS_PARAMETERS;
+       rssp->hdr.rev = NDIS_RECEIVE_SCALE_PARAMETERS_REVISION_2;
+       rssp->hdr.size = sizeof(struct ndis_recv_scale_param);
+       rssp->flag = 0;
+       rssp->hashinfo = NDIS_HASH_FUNC_TOEPLITZ | NDIS_HASH_IPV4 |
+                        NDIS_HASH_TCP_IPV4;
+       rssp->indirect_tabsize = 4*ITAB_NUM;
+       rssp->indirect_taboffset = sizeof(struct ndis_recv_scale_param);
+       rssp->hashkey_size = HASH_KEYLEN;
+       rssp->kashkey_offset = rssp->indirect_taboffset +
+                              rssp->indirect_tabsize;
+
+       /* Set indirection table entries */
+       itab = (u32 *)(rssp + 1);
+       for (i = 0; i < ITAB_NUM; i++)
+               itab[i] = i % num_queue;
+
+       /* Set hask key values */
+       keyp = (u8 *)((unsigned long)rssp + rssp->kashkey_offset);
+       for (i = 0; i < HASH_KEYLEN; i++)
+               keyp[i] = netvsc_hash_key[i];
+
+
+       ret = rndis_filter_send_request(rdev, request);
+       if (ret != 0)
+               goto cleanup;
+
+       t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
+       if (t == 0) {
+               netdev_err(ndev, "timeout before we got a set response...\n");
+               /* can't put_rndis_request, since we may still receive a
+                * send-completion.
+                */
+               return -ETIMEDOUT;
+       } else {
+               set_complete = &request->response_msg.msg.set_complete;
+               if (set_complete->status != RNDIS_STATUS_SUCCESS) {
+                       netdev_err(ndev, "Fail to set RSS parameters:0x%x\n",
+                                  set_complete->status);
+                       ret = -EINVAL;
+               }
+       }
+
+cleanup:
+       put_rndis_request(rdev, request);
+       return ret;
+}
+
+
 static int rndis_filter_query_device_link_status(struct rndis_device *dev)
 {
        u32 size = sizeof(u32);
@@ -886,6 +982,28 @@ static int rndis_filter_close_device(struct rndis_device *dev)
        return ret;
 }
 
+static void netvsc_sc_open(struct vmbus_channel *new_sc)
+{
+       struct netvsc_device *nvscdev;
+       u16 chn_index = new_sc->offermsg.offer.sub_channel_index;
+       int ret;
+
+       nvscdev = hv_get_drvdata(new_sc->primary_channel->device_obj);
+
+       if (chn_index >= nvscdev->num_chn)
+               return;
+
+       set_per_channel_state(new_sc, nvscdev->sub_cb_buf + (chn_index - 1) *
+                             NETVSC_PACKET_SIZE);
+
+       ret = vmbus_open(new_sc, nvscdev->ring_size * PAGE_SIZE,
+                        nvscdev->ring_size * PAGE_SIZE, NULL, 0,
+                        netvsc_channel_cb, new_sc);
+
+       if (ret == 0)
+               nvscdev->chn_table[chn_index] = new_sc;
+}
+
 int rndis_filter_device_add(struct hv_device *dev,
                                  void *additional_info)
 {
@@ -894,6 +1012,10 @@ int rndis_filter_device_add(struct hv_device *dev,
        struct rndis_device *rndis_device;
        struct netvsc_device_info *device_info = additional_info;
        struct ndis_offload_params offloads;
+       struct nvsp_message *init_packet;
+       int t;
+       struct ndis_recv_scale_cap rsscap;
+       u32 rsscap_size = sizeof(struct ndis_recv_scale_cap);
 
        rndis_device = get_rndis_device();
        if (!rndis_device)
@@ -913,6 +1035,7 @@ int rndis_filter_device_add(struct hv_device *dev,
 
        /* Initialize the rndis device */
        net_device = hv_get_drvdata(dev);
+       net_device->num_chn = 1;
 
        net_device->extension = rndis_device;
        rndis_device->net_dev = net_device;
@@ -952,7 +1075,6 @@ int rndis_filter_device_add(struct hv_device *dev,
        if (ret)
                goto err_dev_remv;
 
-
        rndis_filter_query_device_link_status(rndis_device);
 
        device_info->link_state = rndis_device->link_state;
@@ -961,7 +1083,66 @@ int rndis_filter_device_add(struct hv_device *dev,
                 rndis_device->hw_mac_adr,
                 device_info->link_state ? "down" : "up");
 
-       return ret;
+       if (net_device->nvsp_version < NVSP_PROTOCOL_VERSION_5)
+               return 0;
+
+       /* vRSS setup */
+       memset(&rsscap, 0, rsscap_size);
+       ret = rndis_filter_query_device(rndis_device,
+                                       OID_GEN_RECEIVE_SCALE_CAPABILITIES,
+                                       &rsscap, &rsscap_size);
+       if (ret || rsscap.num_recv_que < 2)
+               goto out;
+
+       net_device->num_chn = (num_online_cpus() < rsscap.num_recv_que) ?
+                              num_online_cpus() : rsscap.num_recv_que;
+       if (net_device->num_chn == 1)
+               goto out;
+
+       net_device->sub_cb_buf = vzalloc((net_device->num_chn - 1) *
+                                        NETVSC_PACKET_SIZE);
+       if (!net_device->sub_cb_buf) {
+               net_device->num_chn = 1;
+               dev_info(&dev->device, "No memory for subchannels.\n");
+               goto out;
+       }
+
+       vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open);
+
+       init_packet = &net_device->channel_init_pkt;
+       memset(init_packet, 0, sizeof(struct nvsp_message));
+       init_packet->hdr.msg_type = NVSP_MSG5_TYPE_SUBCHANNEL;
+       init_packet->msg.v5_msg.subchn_req.op = NVSP_SUBCHANNEL_ALLOCATE;
+       init_packet->msg.v5_msg.subchn_req.num_subchannels =
+                                               net_device->num_chn - 1;
+       ret = vmbus_sendpacket(dev->channel, init_packet,
+                              sizeof(struct nvsp_message),
+                              (unsigned long)init_packet,
+                              VM_PKT_DATA_INBAND,
+                              VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+       if (ret)
+               goto out;
+       t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ);
+       if (t == 0) {
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+       if (init_packet->msg.v5_msg.subchn_comp.status !=
+           NVSP_STAT_SUCCESS) {
+               ret = -ENODEV;
+               goto out;
+       }
+       net_device->num_chn = 1 +
+               init_packet->msg.v5_msg.subchn_comp.num_subchannels;
+
+       vmbus_are_subchannels_present(dev->channel);
+
+       ret = rndis_filter_set_rss_param(rndis_device, net_device->num_chn);
+
+out:
+       if (ret)
+               net_device->num_chn = 1;
+       return 0; /* return 0 because primary channel can be used alone */
 
 err_dev_remv:
        rndis_filter_device_remove(dev);
index e36f194673a45a2035a15830571e4e2c02039839..4517b149ed0786946e44eb1a699fe232c2fbd166 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/interrupt.h>
+#include <linux/irq.h>
 #include <linux/gpio.h>
 #include <linux/delay.h>
 #include <linux/mutex.h>
@@ -692,10 +693,7 @@ at86rf230_xmit(struct ieee802154_dev *dev, struct sk_buff *skb)
        if (rc < 0)
                goto err_rx;
 
-       rc = at86rf230_start(dev);
-
-       return rc;
-
+       return at86rf230_start(dev);
 err_rx:
        at86rf230_start(dev);
 err:
@@ -963,33 +961,24 @@ static irqreturn_t at86rf230_isr_level(int irq, void *data)
        return at86rf230_isr(irq, data);
 }
 
-static int at86rf230_irq_polarity(struct at86rf230_local *lp, int pol)
-{
-       return at86rf230_write_subreg(lp, SR_IRQ_POLARITY, pol);
-}
-
 static int at86rf230_hw_init(struct at86rf230_local *lp)
 {
-       struct at86rf230_platform_data *pdata = lp->spi->dev.platform_data;
-       int rc, irq_pol;
-       u8 status;
+       int rc, irq_pol, irq_type;
+       u8 dvdd;
        u8 csma_seed[2];
 
-       rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status);
-       if (rc)
-               return rc;
-
        rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_FORCE_TRX_OFF);
        if (rc)
                return rc;
 
+       irq_type = irq_get_trigger_type(lp->spi->irq);
        /* configure irq polarity, defaults to high active */
-       if (pdata->irq_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW))
+       if (irq_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW))
                irq_pol = IRQ_ACTIVE_LOW;
        else
                irq_pol = IRQ_ACTIVE_HIGH;
 
-       rc = at86rf230_irq_polarity(lp, irq_pol);
+       rc = at86rf230_write_subreg(lp, SR_IRQ_POLARITY, irq_pol);
        if (rc)
                return rc;
 
@@ -1017,10 +1006,10 @@ static int at86rf230_hw_init(struct at86rf230_local *lp)
        /* Wait the next SLEEP cycle */
        msleep(100);
 
-       rc = at86rf230_read_subreg(lp, SR_DVDD_OK, &status);
+       rc = at86rf230_read_subreg(lp, SR_DVDD_OK, &dvdd);
        if (rc)
                return rc;
-       if (!status) {
+       if (!dvdd) {
                dev_err(&lp->spi->dev, "DVDD error\n");
                return -EINVAL;
        }
@@ -1032,7 +1021,6 @@ static struct at86rf230_platform_data *
 at86rf230_get_pdata(struct spi_device *spi)
 {
        struct at86rf230_platform_data *pdata;
-       const char *irq_type;
 
        if (!IS_ENABLED(CONFIG_OF) || !spi->dev.of_node)
                return spi->dev.platform_data;
@@ -1044,19 +1032,6 @@ at86rf230_get_pdata(struct spi_device *spi)
        pdata->rstn = of_get_named_gpio(spi->dev.of_node, "reset-gpio", 0);
        pdata->slp_tr = of_get_named_gpio(spi->dev.of_node, "sleep-gpio", 0);
 
-       pdata->irq_type = IRQF_TRIGGER_RISING;
-       of_property_read_string(spi->dev.of_node, "irq-type", &irq_type);
-       if (!strcmp(irq_type, "level-high"))
-               pdata->irq_type = IRQF_TRIGGER_HIGH;
-       else if (!strcmp(irq_type, "level-low"))
-               pdata->irq_type = IRQF_TRIGGER_LOW;
-       else if (!strcmp(irq_type, "edge-rising"))
-               pdata->irq_type = IRQF_TRIGGER_RISING;
-       else if (!strcmp(irq_type, "edge-falling"))
-               pdata->irq_type = IRQF_TRIGGER_FALLING;
-       else
-               dev_warn(&spi->dev, "wrong irq-type specified using edge-rising\n");
-
        spi->dev.platform_data = pdata;
 done:
        return pdata;
@@ -1071,7 +1046,7 @@ static int at86rf230_probe(struct spi_device *spi)
        u8 part = 0, version = 0, status;
        irq_handler_t irq_handler;
        work_func_t irq_worker;
-       int rc;
+       int rc, irq_type;
        const char *chip;
        struct ieee802154_ops *ops = NULL;
 
@@ -1087,27 +1062,17 @@ static int at86rf230_probe(struct spi_device *spi)
        }
 
        if (gpio_is_valid(pdata->rstn)) {
-               rc = gpio_request(pdata->rstn, "rstn");
+               rc = devm_gpio_request_one(&spi->dev, pdata->rstn,
+                                          GPIOF_OUT_INIT_HIGH, "rstn");
                if (rc)
                        return rc;
        }
 
        if (gpio_is_valid(pdata->slp_tr)) {
-               rc = gpio_request(pdata->slp_tr, "slp_tr");
-               if (rc)
-                       goto err_slp_tr;
-       }
-
-       if (gpio_is_valid(pdata->rstn)) {
-               rc = gpio_direction_output(pdata->rstn, 1);
-               if (rc)
-                       goto err_gpio_dir;
-       }
-
-       if (gpio_is_valid(pdata->slp_tr)) {
-               rc = gpio_direction_output(pdata->slp_tr, 0);
+               rc = devm_gpio_request_one(&spi->dev, pdata->slp_tr,
+                                          GPIOF_OUT_INIT_LOW, "slp_tr");
                if (rc)
-                       goto err_gpio_dir;
+                       return rc;
        }
 
        /* Reset */
@@ -1121,13 +1086,12 @@ static int at86rf230_probe(struct spi_device *spi)
 
        rc = __at86rf230_detect_device(spi, &man_id, &part, &version);
        if (rc < 0)
-               goto err_gpio_dir;
+               return rc;
 
        if (man_id != 0x001f) {
                dev_err(&spi->dev, "Non-Atmel dev found (MAN_ID %02x %02x)\n",
                        man_id >> 8, man_id & 0xFF);
-               rc = -EINVAL;
-               goto err_gpio_dir;
+               return -EINVAL;
        }
 
        switch (part) {
@@ -1154,16 +1118,12 @@ static int at86rf230_probe(struct spi_device *spi)
        }
 
        dev_info(&spi->dev, "Detected %s chip version %d\n", chip, version);
-       if (!ops) {
-               rc = -ENOTSUPP;
-               goto err_gpio_dir;
-       }
+       if (!ops)
+               return -ENOTSUPP;
 
        dev = ieee802154_alloc_device(sizeof(*lp), ops);
-       if (!dev) {
-               rc = -ENOMEM;
-               goto err_gpio_dir;
-       }
+       if (!dev)
+               return -ENOMEM;
 
        lp = dev->priv;
        lp->dev = dev;
@@ -1176,7 +1136,8 @@ static int at86rf230_probe(struct spi_device *spi)
        dev->extra_tx_headroom = 0;
        dev->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK;
 
-       if (pdata->irq_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
+       irq_type = irq_get_trigger_type(spi->irq);
+       if (irq_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
                irq_worker = at86rf230_irqwork;
                irq_handler = at86rf230_isr;
        } else {
@@ -1202,75 +1163,65 @@ static int at86rf230_probe(struct spi_device *spi)
        if (rc)
                goto err_hw_init;
 
-       rc = request_irq(spi->irq, irq_handler,
-                        IRQF_SHARED | pdata->irq_type,
-                        dev_name(&spi->dev), lp);
+       /* Read irq status register to reset irq line */
+       rc = at86rf230_read_subreg(lp, RG_IRQ_STATUS, 0xff, 0, &status);
        if (rc)
                goto err_hw_init;
 
-       /* Read irq status register to reset irq line */
-       rc = at86rf230_read_subreg(lp, RG_IRQ_STATUS, 0xff, 0, &status);
+       rc = devm_request_irq(&spi->dev, spi->irq, irq_handler, IRQF_SHARED,
+                             dev_name(&spi->dev), lp);
        if (rc)
-               goto err_irq;
+               goto err_hw_init;
 
        rc = ieee802154_register_device(lp->dev);
        if (rc)
-               goto err_irq;
+               goto err_hw_init;
 
        return rc;
 
-err_irq:
-       free_irq(spi->irq, lp);
 err_hw_init:
        flush_work(&lp->irqwork);
-       spi_set_drvdata(spi, NULL);
        mutex_destroy(&lp->bmux);
        ieee802154_free_device(lp->dev);
 
-err_gpio_dir:
-       if (gpio_is_valid(pdata->slp_tr))
-               gpio_free(pdata->slp_tr);
-err_slp_tr:
-       if (gpio_is_valid(pdata->rstn))
-               gpio_free(pdata->rstn);
        return rc;
 }
 
 static int at86rf230_remove(struct spi_device *spi)
 {
        struct at86rf230_local *lp = spi_get_drvdata(spi);
-       struct at86rf230_platform_data *pdata = spi->dev.platform_data;
 
        /* mask all at86rf230 irq's */
        at86rf230_write_subreg(lp, SR_IRQ_MASK, 0);
        ieee802154_unregister_device(lp->dev);
-
-       free_irq(spi->irq, lp);
        flush_work(&lp->irqwork);
-
-       if (gpio_is_valid(pdata->slp_tr))
-               gpio_free(pdata->slp_tr);
-       if (gpio_is_valid(pdata->rstn))
-               gpio_free(pdata->rstn);
-
        mutex_destroy(&lp->bmux);
        ieee802154_free_device(lp->dev);
-
        dev_dbg(&spi->dev, "unregistered at86rf230\n");
+
        return 0;
 }
 
-#if IS_ENABLED(CONFIG_OF)
-static struct of_device_id at86rf230_of_match[] = {
+static const struct of_device_id at86rf230_of_match[] = {
        { .compatible = "atmel,at86rf230", },
        { .compatible = "atmel,at86rf231", },
        { .compatible = "atmel,at86rf233", },
        { .compatible = "atmel,at86rf212", },
        { },
 };
-#endif
+MODULE_DEVICE_TABLE(of, at86rf230_of_match);
+
+static const struct spi_device_id at86rf230_device_id[] = {
+       { .name = "at86rf230", },
+       { .name = "at86rf231", },
+       { .name = "at86rf233", },
+       { .name = "at86rf212", },
+       { },
+};
+MODULE_DEVICE_TABLE(spi, at86rf230_device_id);
 
 static struct spi_driver at86rf230_driver = {
+       .id_table = at86rf230_device_id,
        .driver = {
                .of_match_table = of_match_ptr(at86rf230_of_match),
                .name   = "at86rf230",
index b8d22173925dee1aed62df3ccd4d23deb44b976b..27d83207d24ce0de722144c74e98ce65601fcfc0 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/timer.h>
 #include <linux/platform_device.h>
 #include <linux/netdevice.h>
+#include <linux/device.h>
 #include <linux/spinlock.h>
 #include <net/mac802154.h>
 #include <net/wpan-phy.h>
@@ -228,7 +229,8 @@ static int fakelb_probe(struct platform_device *pdev)
        int err = -ENOMEM;
        int i;
 
-       priv = kzalloc(sizeof(struct fakelb_priv), GFP_KERNEL);
+       priv = devm_kzalloc(&pdev->dev, sizeof(struct fakelb_priv),
+                           GFP_KERNEL);
        if (!priv)
                goto err_alloc;
 
@@ -248,7 +250,6 @@ static int fakelb_probe(struct platform_device *pdev)
 err_slave:
        list_for_each_entry(dp, &priv->list, list)
                fakelb_del(dp);
-       kfree(priv);
 err_alloc:
        return err;
 }
@@ -260,7 +261,6 @@ static int fakelb_remove(struct platform_device *pdev)
 
        list_for_each_entry_safe(dp, temp, &priv->list, list)
                fakelb_del(dp);
-       kfree(priv);
 
        return 0;
 }
index 78a6552ed7072d04672b98d7731c0ffd4af6117b..4048062011ba29bdafad313f06ef23aaf7be01c8 100644 (file)
@@ -618,12 +618,12 @@ static int mrf24j40_probe(struct spi_device *spi)
 
        printk(KERN_INFO "mrf24j40: probe(). IRQ: %d\n", spi->irq);
 
-       devrec = kzalloc(sizeof(struct mrf24j40), GFP_KERNEL);
+       devrec = devm_kzalloc(&spi->dev, sizeof(struct mrf24j40), GFP_KERNEL);
        if (!devrec)
-               goto err_devrec;
-       devrec->buf = kzalloc(3, GFP_KERNEL);
+               goto err_ret;
+       devrec->buf = devm_kzalloc(&spi->dev, 3, GFP_KERNEL);
        if (!devrec->buf)
-               goto err_buf;
+               goto err_ret;
 
        spi->mode = SPI_MODE_0; /* TODO: Is this appropriate for right here? */
        if (spi->max_speed_hz > MAX_SPI_SPEED_HZ)
@@ -638,7 +638,7 @@ static int mrf24j40_probe(struct spi_device *spi)
 
        devrec->dev = ieee802154_alloc_device(0, &mrf24j40_ops);
        if (!devrec->dev)
-               goto err_alloc_dev;
+               goto err_ret;
 
        devrec->dev->priv = devrec;
        devrec->dev->parent = &devrec->spi->dev;
@@ -676,12 +676,13 @@ static int mrf24j40_probe(struct spi_device *spi)
        val &= ~0x3; /* Clear RX mode (normal) */
        write_short_reg(devrec, REG_RXMCR, val);
 
-       ret = request_threaded_irq(spi->irq,
-                                  NULL,
-                                  mrf24j40_isr,
-                                  IRQF_TRIGGER_LOW|IRQF_ONESHOT,
-                                  dev_name(&spi->dev),
-                                  devrec);
+       ret = devm_request_threaded_irq(&spi->dev,
+                                       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");
@@ -695,11 +696,7 @@ static int mrf24j40_probe(struct spi_device *spi)
        ieee802154_unregister_device(devrec->dev);
 err_register_device:
        ieee802154_free_device(devrec->dev);
-err_alloc_dev:
-       kfree(devrec->buf);
-err_buf:
-       kfree(devrec);
-err_devrec:
+err_ret:
        return ret;
 }
 
@@ -709,15 +706,11 @@ static int mrf24j40_remove(struct spi_device *spi)
 
        dev_dbg(printdev(devrec), "remove\n");
 
-       free_irq(spi->irq, devrec);
        ieee802154_unregister_device(devrec->dev);
        ieee802154_free_device(devrec->dev);
        /* TODO: Will ieee802154_free_device() wait until ->xmit() is
         * complete? */
 
-       /* Clean up the SPI stuff. */
-       kfree(devrec->buf);
-       kfree(devrec);
        return 0;
 }
 
index 3da44d5d91497801a141b373c60f8cd5890a1bf9..8d101d63abca9a48466edfed2db11ab54df32d63 100644 (file)
@@ -396,7 +396,8 @@ config MCS_FIR
 
 config SH_IRDA
        tristate "SuperH IrDA driver"
-       depends on IRDA && ARCH_SHMOBILE
+       depends on IRDA
+       depends on ARCH_SHMOBILE || COMPILE_TEST
        help
          Say Y here if your want to enable SuperH IrDA devices.
 
index 2900af091c2d5f718bd14a083fb42cf11a72af3b..998bb89ede71c75ea1d67d4c07cbc77963d79c72 100644 (file)
@@ -510,10 +510,8 @@ static void via_hw_init(struct via_ircc_cb *self)
  */
 static int via_ircc_read_dongle_id(int iobase)
 {
-       int dongle_id = 9;      /* Default to IBM */
-
        IRDA_ERROR("via-ircc: dongle probing not supported, please specify dongle_id module parameter.\n");
-       return dongle_id;
+       return 9;       /* Default to IBM */
 }
 
 /*
@@ -926,7 +924,6 @@ static int via_ircc_dma_xmit(struct via_ircc_cb *self, u16 iobase)
 static int via_ircc_dma_xmit_complete(struct via_ircc_cb *self)
 {
        int iobase;
-       int ret = TRUE;
        u8 Tx_status;
 
        IRDA_DEBUG(3, "%s()\n", __func__);
@@ -983,7 +980,7 @@ F01_E*/
        // Tell the network layer, that we can accept more frames 
        netif_wake_queue(self->netdev);
 //F01   }
-       return ret;
+       return TRUE;
 }
 
 /*
index e641bb2403624fdd91fb565ea646d70d03c2c5fd..11dbdf36d9c1b328ff70b6e5d0e5d2f7729dbb2a 100644 (file)
 #include "w83977af.h"
 #include "w83977af_ir.h"
 
-#ifdef  CONFIG_ARCH_NETWINDER            /* Adjust to NetWinder differences */
-#undef  CONFIG_NETWINDER_TX_DMA_PROBLEMS /* Not needed */
-#define CONFIG_NETWINDER_RX_DMA_PROBLEMS /* Must have this one! */
-#endif
 #define CONFIG_USE_W977_PNP        /* Currently needed */
 #define PIO_MAX_SPEED       115200 
 
@@ -332,7 +328,7 @@ static int w83977af_probe(int iobase, int irq, int dma)
                w977_write_reg(0x74, dma+1, efbase[i]);
 #else
                w977_write_reg(0x74, dma, efbase[i]);   
-#endif /*CONFIG_ARCH_NETWINDER */
+#endif /* CONFIG_ARCH_NETWINDER */
                w977_write_reg(0x75, 0x04, efbase[i]);  /* Disable Tx DMA */
        
                /* Set append hardware CRC, enable IR bank selection */ 
@@ -563,10 +559,6 @@ static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb,
 static void w83977af_dma_write(struct w83977af_ir *self, int iobase)
 {
        __u8 set;
-#ifdef CONFIG_NETWINDER_TX_DMA_PROBLEMS
-       unsigned long flags;
-       __u8 hcr;
-#endif
         IRDA_DEBUG(4, "%s(), len=%d\n", __func__ , self->tx_buff.len);
 
        /* Save current set */
@@ -579,30 +571,13 @@ static void w83977af_dma_write(struct w83977af_ir *self, int iobase)
        /* Choose transmit DMA channel  */ 
        switch_bank(iobase, SET2);
        outb(ADCR1_D_CHSW|/*ADCR1_DMA_F|*/ADCR1_ADV_SL, iobase+ADCR1);
-#ifdef CONFIG_NETWINDER_TX_DMA_PROBLEMS
-       spin_lock_irqsave(&self->lock, flags);
-
-       disable_dma(self->io.dma);
-       clear_dma_ff(self->io.dma);
-       set_dma_mode(self->io.dma, DMA_MODE_READ);
-       set_dma_addr(self->io.dma, self->tx_buff_dma);
-       set_dma_count(self->io.dma, self->tx_buff.len);
-#else
        irda_setup_dma(self->io.dma, self->tx_buff_dma, self->tx_buff.len,
                       DMA_MODE_WRITE); 
-#endif
        self->io.direction = IO_XMIT;
        
        /* Enable DMA */
        switch_bank(iobase, SET0);
-#ifdef CONFIG_NETWINDER_TX_DMA_PROBLEMS
-       hcr = inb(iobase+HCR);
-       outb(hcr | HCR_EN_DMA, iobase+HCR);
-       enable_dma(self->io.dma);
-       spin_unlock_irqrestore(&self->lock, flags);
-#else  
        outb(inb(iobase+HCR) | HCR_EN_DMA | HCR_TX_WT, iobase+HCR);
-#endif
 
        /* Restore set register */
        outb(set, iobase+SSR);
@@ -711,7 +686,7 @@ static int w83977af_dma_receive(struct w83977af_ir *self)
 {
        int iobase;
        __u8 set;
-#ifdef CONFIG_NETWINDER_RX_DMA_PROBLEMS
+#ifdef CONFIG_ARCH_NETWINDER
        unsigned long flags;
        __u8 hcr;
 #endif
@@ -736,7 +711,7 @@ static int w83977af_dma_receive(struct w83977af_ir *self)
        self->io.direction = IO_RECV;
        self->rx_buff.data = self->rx_buff.head;
 
-#ifdef CONFIG_NETWINDER_RX_DMA_PROBLEMS
+#ifdef CONFIG_ARCH_NETWINDER
        spin_lock_irqsave(&self->lock, flags);
 
        disable_dma(self->io.dma);
@@ -759,7 +734,7 @@ static int w83977af_dma_receive(struct w83977af_ir *self)
        
        /* Enable DMA */
        switch_bank(iobase, SET0);
-#ifdef CONFIG_NETWINDER_RX_DMA_PROBLEMS
+#ifdef CONFIG_ARCH_NETWINDER
        hcr = inb(iobase+HCR);
        outb(hcr | HCR_EN_DMA, iobase+HCR);
        enable_dma(self->io.dma);
index d53e299ae1d97ca39c4f3af511b97c5dacb32827..958df383068a534d01bf9632463a22689184ea50 100644 (file)
 #include <linux/if_link.h>
 #include <linux/if_macvlan.h>
 #include <linux/hash.h>
+#include <linux/workqueue.h>
 #include <net/rtnetlink.h>
 #include <net/xfrm.h>
+#include <linux/netpoll.h>
 
 #define MACVLAN_HASH_SIZE      (1 << BITS_PER_BYTE)
 
@@ -40,10 +42,19 @@ struct macvlan_port {
        struct hlist_head       vlan_hash[MACVLAN_HASH_SIZE];
        struct list_head        vlans;
        struct rcu_head         rcu;
+       struct sk_buff_head     bc_queue;
+       struct work_struct      bc_work;
        bool                    passthru;
-       int                     count;
 };
 
+#define MACVLAN_PORT_IS_EMPTY(port)    list_empty(&port->vlans)
+
+struct macvlan_skb_cb {
+       const struct macvlan_dev *src;
+};
+
+#define MACVLAN_SKB_CB(__skb) ((struct macvlan_skb_cb *)&((__skb)->cb[0]))
+
 static void macvlan_port_destroy(struct net_device *dev);
 
 static struct macvlan_port *macvlan_port_get_rcu(const struct net_device *dev)
@@ -120,7 +131,7 @@ static int macvlan_broadcast_one(struct sk_buff *skb,
        struct net_device *dev = vlan->dev;
 
        if (local)
-               return dev_forward_skb(dev, skb);
+               return __dev_forward_skb(dev, skb);
 
        skb->dev = dev;
        if (ether_addr_equal_64bits(eth->h_dest, dev->broadcast))
@@ -128,7 +139,7 @@ static int macvlan_broadcast_one(struct sk_buff *skb,
        else
                skb->pkt_type = PACKET_MULTICAST;
 
-       return netif_rx(skb);
+       return 0;
 }
 
 static u32 macvlan_hash_mix(const struct macvlan_dev *vlan)
@@ -175,32 +186,32 @@ static void macvlan_broadcast(struct sk_buff *skb,
                        if (likely(nskb))
                                err = macvlan_broadcast_one(
                                        nskb, vlan, eth,
-                                       mode == MACVLAN_MODE_BRIDGE);
+                                       mode == MACVLAN_MODE_BRIDGE) ?:
+                                     netif_rx_ni(nskb);
                        macvlan_count_rx(vlan, skb->len + ETH_HLEN,
                                         err == NET_RX_SUCCESS, 1);
                }
        }
 }
 
-/* called under rcu_read_lock() from netif_receive_skb */
-static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
+static void macvlan_process_broadcast(struct work_struct *w)
 {
-       struct macvlan_port *port;
-       struct sk_buff *skb = *pskb;
-       const struct ethhdr *eth = eth_hdr(skb);
-       const struct macvlan_dev *vlan;
-       const struct macvlan_dev *src;
-       struct net_device *dev;
-       unsigned int len = 0;
-       int ret = NET_RX_DROP;
+       struct macvlan_port *port = container_of(w, struct macvlan_port,
+                                                bc_work);
+       struct sk_buff *skb;
+       struct sk_buff_head list;
+
+       skb_queue_head_init(&list);
+
+       spin_lock_bh(&port->bc_queue.lock);
+       skb_queue_splice_tail_init(&port->bc_queue, &list);
+       spin_unlock_bh(&port->bc_queue.lock);
+
+       while ((skb = __skb_dequeue(&list))) {
+               const struct macvlan_dev *src = MACVLAN_SKB_CB(skb)->src;
+
+               rcu_read_lock();
 
-       port = macvlan_port_get_rcu(skb->dev);
-       if (is_multicast_ether_addr(eth->h_dest)) {
-               skb = ip_check_defrag(skb, IP_DEFRAG_MACVLAN);
-               if (!skb)
-                       return RX_HANDLER_CONSUMED;
-               eth = eth_hdr(skb);
-               src = macvlan_hash_lookup(port, eth->h_source);
                if (!src)
                        /* frame comes from an external address */
                        macvlan_broadcast(skb, port, NULL,
@@ -213,20 +224,80 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
                        macvlan_broadcast(skb, port, src->dev,
                                          MACVLAN_MODE_VEPA |
                                          MACVLAN_MODE_BRIDGE);
-               else if (src->mode == MACVLAN_MODE_BRIDGE)
+               else
                        /*
                         * flood only to VEPA ports, bridge ports
                         * already saw the frame on the way out.
                         */
                        macvlan_broadcast(skb, port, src->dev,
                                          MACVLAN_MODE_VEPA);
-               else {
+
+               rcu_read_unlock();
+
+               kfree_skb(skb);
+       }
+}
+
+static void macvlan_broadcast_enqueue(struct macvlan_port *port,
+                                     struct sk_buff *skb)
+{
+       struct sk_buff *nskb;
+       int err = -ENOMEM;
+
+       nskb = skb_clone(skb, GFP_ATOMIC);
+       if (!nskb)
+               goto err;
+
+       spin_lock(&port->bc_queue.lock);
+       if (skb_queue_len(&port->bc_queue) < skb->dev->tx_queue_len) {
+               __skb_queue_tail(&port->bc_queue, nskb);
+               err = 0;
+       }
+       spin_unlock(&port->bc_queue.lock);
+
+       if (err)
+               goto free_nskb;
+
+       schedule_work(&port->bc_work);
+       return;
+
+free_nskb:
+       kfree_skb(nskb);
+err:
+       atomic_long_inc(&skb->dev->rx_dropped);
+}
+
+/* called under rcu_read_lock() from netif_receive_skb */
+static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)
+{
+       struct macvlan_port *port;
+       struct sk_buff *skb = *pskb;
+       const struct ethhdr *eth = eth_hdr(skb);
+       const struct macvlan_dev *vlan;
+       const struct macvlan_dev *src;
+       struct net_device *dev;
+       unsigned int len = 0;
+       int ret = NET_RX_DROP;
+
+       port = macvlan_port_get_rcu(skb->dev);
+       if (is_multicast_ether_addr(eth->h_dest)) {
+               skb = ip_check_defrag(skb, IP_DEFRAG_MACVLAN);
+               if (!skb)
+                       return RX_HANDLER_CONSUMED;
+               eth = eth_hdr(skb);
+               src = macvlan_hash_lookup(port, eth->h_source);
+               if (src && src->mode != MACVLAN_MODE_VEPA &&
+                   src->mode != MACVLAN_MODE_BRIDGE) {
                        /* forward to original port. */
                        vlan = src;
-                       ret = macvlan_broadcast_one(skb, vlan, eth, 0);
+                       ret = macvlan_broadcast_one(skb, vlan, eth, 0) ?:
+                             netif_rx(skb);
                        goto out;
                }
 
+               MACVLAN_SKB_CB(skb)->src = src;
+               macvlan_broadcast_enqueue(port, skb);
+
                return RX_HANDLER_PASS;
        }
 
@@ -287,12 +358,26 @@ static int macvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev)
        return dev_queue_xmit(skb);
 }
 
+static inline netdev_tx_t macvlan_netpoll_send_skb(struct macvlan_dev *vlan, struct sk_buff *skb)
+{
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       if (vlan->netpoll)
+               netpoll_send_skb(vlan->netpoll, skb);
+#else
+       BUG();
+#endif
+       return NETDEV_TX_OK;
+}
+
 static netdev_tx_t macvlan_start_xmit(struct sk_buff *skb,
                                      struct net_device *dev)
 {
        unsigned int len = skb->len;
        int ret;
-       const struct macvlan_dev *vlan = netdev_priv(dev);
+       struct macvlan_dev *vlan = netdev_priv(dev);
+
+       if (unlikely(netpoll_tx_running(dev)))
+               return macvlan_netpoll_send_skb(vlan, skb);
 
        if (vlan->fwd_priv) {
                skb->dev = vlan->lowerdev;
@@ -424,35 +509,49 @@ static int macvlan_stop(struct net_device *dev)
        return 0;
 }
 
-static int macvlan_set_mac_address(struct net_device *dev, void *p)
+static int macvlan_sync_address(struct net_device *dev, unsigned char *addr)
 {
        struct macvlan_dev *vlan = netdev_priv(dev);
        struct net_device *lowerdev = vlan->lowerdev;
-       struct sockaddr *addr = p;
        int err;
 
-       if (!is_valid_ether_addr(addr->sa_data))
-               return -EADDRNOTAVAIL;
-
        if (!(dev->flags & IFF_UP)) {
                /* Just copy in the new address */
-               memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+               ether_addr_copy(dev->dev_addr, addr);
        } else {
                /* Rehash and update the device filters */
-               if (macvlan_addr_busy(vlan->port, addr->sa_data))
+               if (macvlan_addr_busy(vlan->port, addr))
                        return -EBUSY;
 
-               err = dev_uc_add(lowerdev, addr->sa_data);
-               if (err)
-                       return err;
+               if (!vlan->port->passthru) {
+                       err = dev_uc_add(lowerdev, addr);
+                       if (err)
+                               return err;
 
-               dev_uc_del(lowerdev, dev->dev_addr);
+                       dev_uc_del(lowerdev, dev->dev_addr);
+               }
 
-               macvlan_hash_change_addr(vlan, addr->sa_data);
+               macvlan_hash_change_addr(vlan, addr);
        }
        return 0;
 }
 
+static int macvlan_set_mac_address(struct net_device *dev, void *p)
+{
+       struct macvlan_dev *vlan = netdev_priv(dev);
+       struct sockaddr *addr = p;
+
+       if (!is_valid_ether_addr(addr->sa_data))
+               return -EADDRNOTAVAIL;
+
+       if (vlan->mode == MACVLAN_MODE_PASSTHRU) {
+               dev_set_mac_address(vlan->lowerdev, addr);
+               return 0;
+       }
+
+       return macvlan_sync_address(dev, addr->sa_data);
+}
+
 static void macvlan_change_rx_flags(struct net_device *dev, int change)
 {
        struct macvlan_dev *vlan = netdev_priv(dev);
@@ -567,8 +666,7 @@ static void macvlan_uninit(struct net_device *dev)
 
        free_percpu(vlan->pcpu_stats);
 
-       port->count -= 1;
-       if (!port->count)
+       if (MACVLAN_PORT_IS_EMPTY(port))
                macvlan_port_destroy(port->dev);
 }
 
@@ -705,6 +803,50 @@ static netdev_features_t macvlan_fix_features(struct net_device *dev,
        return features;
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void macvlan_dev_poll_controller(struct net_device *dev)
+{
+       return;
+}
+
+static int macvlan_dev_netpoll_setup(struct net_device *dev, struct netpoll_info *npinfo)
+{
+       struct macvlan_dev *vlan = netdev_priv(dev);
+       struct net_device *real_dev = vlan->lowerdev;
+       struct netpoll *netpoll;
+       int err = 0;
+
+       netpoll = kzalloc(sizeof(*netpoll), GFP_KERNEL);
+       err = -ENOMEM;
+       if (!netpoll)
+               goto out;
+
+       err = __netpoll_setup(netpoll, real_dev);
+       if (err) {
+               kfree(netpoll);
+               goto out;
+       }
+
+       vlan->netpoll = netpoll;
+
+out:
+       return err;
+}
+
+static void macvlan_dev_netpoll_cleanup(struct net_device *dev)
+{
+       struct macvlan_dev *vlan = netdev_priv(dev);
+       struct netpoll *netpoll = vlan->netpoll;
+
+       if (!netpoll)
+               return;
+
+       vlan->netpoll = NULL;
+
+       __netpoll_free_async(netpoll);
+}
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
 static const struct ethtool_ops macvlan_ethtool_ops = {
        .get_link               = ethtool_op_get_link,
        .get_settings           = macvlan_ethtool_get_settings,
@@ -730,6 +872,11 @@ static const struct net_device_ops macvlan_netdev_ops = {
        .ndo_fdb_del            = macvlan_fdb_del,
        .ndo_fdb_dump           = ndo_dflt_fdb_dump,
        .ndo_get_lock_subclass  = macvlan_get_nest_level,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = macvlan_dev_poll_controller,
+       .ndo_netpoll_setup      = macvlan_dev_netpoll_setup,
+       .ndo_netpoll_cleanup    = macvlan_dev_netpoll_cleanup,
+#endif
 };
 
 void macvlan_common_setup(struct net_device *dev)
@@ -770,6 +917,9 @@ static int macvlan_port_create(struct net_device *dev)
        for (i = 0; i < MACVLAN_HASH_SIZE; i++)
                INIT_HLIST_HEAD(&port->vlan_hash[i]);
 
+       skb_queue_head_init(&port->bc_queue);
+       INIT_WORK(&port->bc_work, macvlan_process_broadcast);
+
        err = netdev_rx_handler_register(dev, macvlan_handle_frame, port);
        if (err)
                kfree(port);
@@ -782,6 +932,7 @@ static void macvlan_port_destroy(struct net_device *dev)
 {
        struct macvlan_port *port = macvlan_port_get_rtnl(dev);
 
+       cancel_work_sync(&port->bc_work);
        dev->priv_flags &= ~IFF_MACVLAN_PORT;
        netdev_rx_handler_unregister(dev);
        kfree_rcu(port, rcu);
@@ -868,13 +1019,12 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,
                vlan->flags = nla_get_u16(data[IFLA_MACVLAN_FLAGS]);
 
        if (vlan->mode == MACVLAN_MODE_PASSTHRU) {
-               if (port->count)
+               if (!MACVLAN_PORT_IS_EMPTY(port))
                        return -EINVAL;
                port->passthru = true;
                eth_hw_addr_inherit(dev, lowerdev);
        }
 
-       port->count += 1;
        err = register_netdevice(dev);
        if (err < 0)
                goto destroy_port;
@@ -892,8 +1042,7 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,
 unregister_netdev:
        unregister_netdevice(dev);
 destroy_port:
-       port->count -= 1;
-       if (!port->count)
+       if (MACVLAN_PORT_IS_EMPTY(port))
                macvlan_port_destroy(lowerdev);
 
        return err;
@@ -1027,6 +1176,25 @@ static int macvlan_device_event(struct notifier_block *unused,
                        vlan->dev->gso_max_size = dev->gso_max_size;
                        netdev_update_features(vlan->dev);
                }
+               break;
+       case NETDEV_CHANGEMTU:
+               list_for_each_entry(vlan, &port->vlans, list) {
+                       if (vlan->dev->mtu <= dev->mtu)
+                               continue;
+                       dev_set_mtu(vlan->dev, dev->mtu);
+               }
+               break;
+       case NETDEV_CHANGEADDR:
+               if (!port->passthru)
+                       return NOTIFY_DONE;
+
+               vlan = list_first_entry_or_null(&port->vlans,
+                                               struct macvlan_dev,
+                                               list);
+
+               if (macvlan_sync_address(vlan->dev, dev->dev_addr))
+                       return NOTIFY_BAD;
+
                break;
        case NETDEV_UNREGISTER:
                /* twiddle thumbs on netns device moves */
@@ -1036,11 +1204,17 @@ static int macvlan_device_event(struct notifier_block *unused,
                list_for_each_entry_safe(vlan, next, &port->vlans, list)
                        vlan->dev->rtnl_link_ops->dellink(vlan->dev, &list_kill);
                unregister_netdevice_many(&list_kill);
-               list_del(&list_kill);
                break;
        case NETDEV_PRE_TYPE_CHANGE:
                /* Forbid underlaying device to change its type. */
                return NOTIFY_BAD;
+
+       case NETDEV_NOTIFY_PEERS:
+       case NETDEV_BONDING_FAILOVER:
+       case NETDEV_RESEND_IGMP:
+               /* Propagate to all vlans */
+               list_for_each_entry(vlan, &port->vlans, list)
+                       call_netdevice_notifiers(event, vlan->dev);
        }
        return NOTIFY_DONE;
 }
index 63aa9d9e34c52b1c2150fa7876ce3658a7c11fe5..5a7e6397440ab18487a87dd9951aa8ed12caf21a 100644 (file)
@@ -298,7 +298,6 @@ static int ntb_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 {
        cmd->supported = SUPPORTED_Backplane;
        cmd->advertising = ADVERTISED_Backplane;
-       cmd->speed = SPEED_UNKNOWN;
        ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
        cmd->duplex = DUPLEX_FULL;
        cmd->port = PORT_OTHER;
@@ -348,7 +347,7 @@ static int ntb_netdev_probe(struct pci_dev *pdev)
        memcpy(ndev->dev_addr, ndev->perm_addr, ndev->addr_len);
 
        ndev->netdev_ops = &ntb_netdev_ops;
-       SET_ETHTOOL_OPS(ndev, &ntb_ethtool_ops);
+       ndev->ethtool_ops = &ntb_ethtool_ops;
 
        dev->qp = ntb_transport_create_queue(ndev, pdev, &ntb_netdev_handlers);
        if (!dev->qp) {
index 6a17f92153b31b3b2fe95a5d7e087eeb3373032b..65de0cab8d07795c5c2255d7397a95e3720477fc 100644 (file)
@@ -24,6 +24,12 @@ config AMD_PHY
        ---help---
          Currently supports the am79c874
 
+config AMD_XGBE_PHY
+       tristate "Driver for the AMD 10GbE (amd-xgbe) PHYs"
+       depends on OF
+       ---help---
+         Currently supports the AMD 10GbE PHY
+
 config MARVELL_PHY
        tristate "Drivers for Marvell PHYs"
        ---help---
index 07d24024863e0805af4127c72e4663174a47babb..7dc3d5b304cfc250a2618e096abecf428ed05bad 100644 (file)
@@ -33,3 +33,4 @@ 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
+obj-$(CONFIG_AMD_XGBE_PHY)     += amd-xgbe-phy.o
diff --git a/drivers/net/phy/amd-xgbe-phy.c b/drivers/net/phy/amd-xgbe-phy.c
new file mode 100644 (file)
index 0000000..b57c224
--- /dev/null
@@ -0,0 +1,1357 @@
+/*
+ * AMD 10Gb Ethernet PHY driver
+ *
+ * This file is available to you under your choice of the following two
+ * licenses:
+ *
+ * License 1: GPLv2
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ *
+ * This file is free software; you may copy, redistribute 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 file 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, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * License 2: Modified BSD
+ *
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ * 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 of Advanced Micro Devices, Inc. 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 <COPYRIGHT HOLDER> 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+#include <linux/mdio.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+#include <linux/uaccess.h>
+#include <asm/irq.h>
+
+
+MODULE_AUTHOR("Tom Lendacky <thomas.lendacky@amd.com>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION("1.0.0-a");
+MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
+
+#define XGBE_PHY_ID    0x000162d0
+#define XGBE_PHY_MASK  0xfffffff0
+
+#define XGBE_AN_INT_CMPLT              0x01
+#define XGBE_AN_INC_LINK               0x02
+#define XGBE_AN_PG_RCV                 0x04
+
+#define XNP_MCF_NULL_MESSAGE           0x001
+#define XNP_ACK_PROCESSED              (1 << 12)
+#define XNP_MP_FORMATTED               (1 << 13)
+#define XNP_NP_EXCHANGE                        (1 << 15)
+
+#ifndef MDIO_PMA_10GBR_PMD_CTRL
+#define MDIO_PMA_10GBR_PMD_CTRL                0x0096
+#endif
+#ifndef MDIO_PMA_10GBR_FEC_CTRL
+#define MDIO_PMA_10GBR_FEC_CTRL                0x00ab
+#endif
+#ifndef MDIO_AN_XNP
+#define MDIO_AN_XNP                    0x0016
+#endif
+
+#ifndef MDIO_AN_INTMASK
+#define MDIO_AN_INTMASK                        0x8001
+#endif
+#ifndef MDIO_AN_INT
+#define MDIO_AN_INT                    0x8002
+#endif
+
+#ifndef MDIO_CTRL1_SPEED1G
+#define MDIO_CTRL1_SPEED1G             (MDIO_CTRL1_SPEED10G & ~BMCR_SPEED100)
+#endif
+
+/* SerDes integration register offsets */
+#define SIR0_STATUS                    0x0040
+#define SIR1_SPEED                     0x0000
+
+/* SerDes integration register entry bit positions and sizes */
+#define SIR0_STATUS_RX_READY_INDEX     0
+#define SIR0_STATUS_RX_READY_WIDTH     1
+#define SIR0_STATUS_TX_READY_INDEX     8
+#define SIR0_STATUS_TX_READY_WIDTH     1
+#define SIR1_SPEED_DATARATE_INDEX      4
+#define SIR1_SPEED_DATARATE_WIDTH      2
+#define SIR1_SPEED_PI_SPD_SEL_INDEX    12
+#define SIR1_SPEED_PI_SPD_SEL_WIDTH    4
+#define SIR1_SPEED_PLLSEL_INDEX                3
+#define SIR1_SPEED_PLLSEL_WIDTH                1
+#define SIR1_SPEED_RATECHANGE_INDEX    6
+#define SIR1_SPEED_RATECHANGE_WIDTH    1
+#define SIR1_SPEED_TXAMP_INDEX         8
+#define SIR1_SPEED_TXAMP_WIDTH         4
+#define SIR1_SPEED_WORDMODE_INDEX      0
+#define SIR1_SPEED_WORDMODE_WIDTH      3
+
+#define SPEED_10000_CDR                        0x7
+#define SPEED_10000_PLL                        0x1
+#define SPEED_10000_RATE               0x0
+#define SPEED_10000_TXAMP              0xa
+#define SPEED_10000_WORD               0x7
+
+#define SPEED_2500_CDR                 0x2
+#define SPEED_2500_PLL                 0x0
+#define SPEED_2500_RATE                        0x2
+#define SPEED_2500_TXAMP               0xf
+#define SPEED_2500_WORD                        0x1
+
+#define SPEED_1000_CDR                 0x2
+#define SPEED_1000_PLL                 0x0
+#define SPEED_1000_RATE                        0x3
+#define SPEED_1000_TXAMP               0xf
+#define SPEED_1000_WORD                        0x1
+
+
+/* SerDes RxTx register offsets */
+#define RXTX_REG20                     0x0050
+#define RXTX_REG114                    0x01c8
+
+/* SerDes RxTx register entry bit positions and sizes */
+#define RXTX_REG20_BLWC_ENA_INDEX      2
+#define RXTX_REG20_BLWC_ENA_WIDTH      1
+#define RXTX_REG114_PQ_REG_INDEX       9
+#define RXTX_REG114_PQ_REG_WIDTH       7
+
+#define RXTX_10000_BLWC                        0
+#define RXTX_10000_PQ                  0x1e
+
+#define RXTX_2500_BLWC                 1
+#define RXTX_2500_PQ                   0xa
+
+#define RXTX_1000_BLWC                 1
+#define RXTX_1000_PQ                   0xa
+
+/* Bit setting and getting macros
+ *  The get macro will extract the current bit field value from within
+ *  the variable
+ *
+ *  The set macro will clear the current bit field value within the
+ *  variable and then set the bit field of the variable to the
+ *  specified value
+ */
+#define GET_BITS(_var, _index, _width)                                 \
+       (((_var) >> (_index)) & ((0x1 << (_width)) - 1))
+
+#define SET_BITS(_var, _index, _width, _val)                           \
+do {                                                                   \
+       (_var) &= ~(((0x1 << (_width)) - 1) << (_index));               \
+       (_var) |= (((_val) & ((0x1 << (_width)) - 1)) << (_index));     \
+} while (0)
+
+/* Macros for reading or writing SerDes integration registers
+ *  The ioread macros will get bit fields or full values using the
+ *  register definitions formed using the input names
+ *
+ *  The iowrite macros will set bit fields or full values using the
+ *  register definitions formed using the input names
+ */
+#define XSIR0_IOREAD(_priv, _reg)                                      \
+       ioread16((_priv)->sir0_regs + _reg)
+
+#define XSIR0_IOREAD_BITS(_priv, _reg, _field)                         \
+       GET_BITS(XSIR0_IOREAD((_priv), _reg),                           \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH)
+
+#define XSIR0_IOWRITE(_priv, _reg, _val)                               \
+       iowrite16((_val), (_priv)->sir0_regs + _reg)
+
+#define XSIR0_IOWRITE_BITS(_priv, _reg, _field, _val)                  \
+do {                                                                   \
+       u16 reg_val = XSIR0_IOREAD((_priv), _reg);                      \
+       SET_BITS(reg_val,                                               \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH, (_val));                      \
+       XSIR0_IOWRITE((_priv), _reg, reg_val);                          \
+} while (0)
+
+#define XSIR1_IOREAD(_priv, _reg)                                      \
+       ioread16((_priv)->sir1_regs + _reg)
+
+#define XSIR1_IOREAD_BITS(_priv, _reg, _field)                         \
+       GET_BITS(XSIR1_IOREAD((_priv), _reg),                           \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH)
+
+#define XSIR1_IOWRITE(_priv, _reg, _val)                               \
+       iowrite16((_val), (_priv)->sir1_regs + _reg)
+
+#define XSIR1_IOWRITE_BITS(_priv, _reg, _field, _val)                  \
+do {                                                                   \
+       u16 reg_val = XSIR1_IOREAD((_priv), _reg);                      \
+       SET_BITS(reg_val,                                               \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH, (_val));                      \
+       XSIR1_IOWRITE((_priv), _reg, reg_val);                          \
+} while (0)
+
+
+/* Macros for reading or writing SerDes RxTx registers
+ *  The ioread macros will get bit fields or full values using the
+ *  register definitions formed using the input names
+ *
+ *  The iowrite macros will set bit fields or full values using the
+ *  register definitions formed using the input names
+ */
+#define XRXTX_IOREAD(_priv, _reg)                                      \
+       ioread16((_priv)->rxtx_regs + _reg)
+
+#define XRXTX_IOREAD_BITS(_priv, _reg, _field)                         \
+       GET_BITS(XRXTX_IOREAD((_priv), _reg),                           \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH)
+
+#define XRXTX_IOWRITE(_priv, _reg, _val)                               \
+       iowrite16((_val), (_priv)->rxtx_regs + _reg)
+
+#define XRXTX_IOWRITE_BITS(_priv, _reg, _field, _val)                  \
+do {                                                                   \
+       u16 reg_val = XRXTX_IOREAD((_priv), _reg);                      \
+       SET_BITS(reg_val,                                               \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH, (_val));                      \
+       XRXTX_IOWRITE((_priv), _reg, reg_val);                          \
+} while (0)
+
+
+enum amd_xgbe_phy_an {
+       AMD_XGBE_AN_READY = 0,
+       AMD_XGBE_AN_START,
+       AMD_XGBE_AN_EVENT,
+       AMD_XGBE_AN_PAGE_RECEIVED,
+       AMD_XGBE_AN_INCOMPAT_LINK,
+       AMD_XGBE_AN_COMPLETE,
+       AMD_XGBE_AN_NO_LINK,
+       AMD_XGBE_AN_EXIT,
+       AMD_XGBE_AN_ERROR,
+};
+
+enum amd_xgbe_phy_rx {
+       AMD_XGBE_RX_READY = 0,
+       AMD_XGBE_RX_BPA,
+       AMD_XGBE_RX_XNP,
+       AMD_XGBE_RX_COMPLETE,
+};
+
+enum amd_xgbe_phy_mode {
+       AMD_XGBE_MODE_KR,
+       AMD_XGBE_MODE_KX,
+};
+
+struct amd_xgbe_phy_priv {
+       struct platform_device *pdev;
+       struct device *dev;
+
+       struct phy_device *phydev;
+
+       /* SerDes related mmio resources */
+       struct resource *rxtx_res;
+       struct resource *sir0_res;
+       struct resource *sir1_res;
+
+       /* SerDes related mmio registers */
+       void __iomem *rxtx_regs;        /* SerDes Rx/Tx CSRs */
+       void __iomem *sir0_regs;        /* SerDes integration registers (1/2) */
+       void __iomem *sir1_regs;        /* SerDes integration registers (2/2) */
+
+       /* Maintain link status for re-starting auto-negotiation */
+       unsigned int link;
+       enum amd_xgbe_phy_mode mode;
+
+       /* Auto-negotiation state machine support */
+       struct mutex an_mutex;
+       enum amd_xgbe_phy_an an_result;
+       enum amd_xgbe_phy_an an_state;
+       enum amd_xgbe_phy_rx kr_state;
+       enum amd_xgbe_phy_rx kx_state;
+       struct work_struct an_work;
+       struct workqueue_struct *an_workqueue;
+};
+
+static int amd_xgbe_an_enable_kr_training(struct phy_device *phydev)
+{
+       int ret;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL);
+       if (ret < 0)
+               return ret;
+
+       ret |= 0x02;
+       phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret);
+
+       return 0;
+}
+
+static int amd_xgbe_an_disable_kr_training(struct phy_device *phydev)
+{
+       int ret;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL);
+       if (ret < 0)
+               return ret;
+
+       ret &= ~0x02;
+       phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret);
+
+       return 0;
+}
+
+static int amd_xgbe_phy_pcs_power_cycle(struct phy_device *phydev)
+{
+       int ret;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
+       if (ret < 0)
+               return ret;
+
+       ret |= MDIO_CTRL1_LPOWER;
+       phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
+
+       usleep_range(75, 100);
+
+       ret &= ~MDIO_CTRL1_LPOWER;
+       phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
+
+       return 0;
+}
+
+static void amd_xgbe_phy_serdes_start_ratechange(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+
+       /* Assert Rx and Tx ratechange */
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, RATECHANGE, 1);
+}
+
+static void amd_xgbe_phy_serdes_complete_ratechange(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+
+       /* Release Rx and Tx ratechange */
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, RATECHANGE, 0);
+
+       /* Wait for Rx and Tx ready */
+       while (!XSIR0_IOREAD_BITS(priv, SIR0_STATUS, RX_READY) &&
+              !XSIR0_IOREAD_BITS(priv, SIR0_STATUS, TX_READY))
+               usleep_range(10, 20);
+}
+
+static int amd_xgbe_phy_xgmii_mode(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       int ret;
+
+       /* Enable KR training */
+       ret = amd_xgbe_an_enable_kr_training(phydev);
+       if (ret < 0)
+               return ret;
+
+       /* Set PCS to KR/10G speed */
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
+       if (ret < 0)
+               return ret;
+
+       ret &= ~MDIO_PCS_CTRL2_TYPE;
+       ret |= MDIO_PCS_CTRL2_10GBR;
+       phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2, ret);
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
+       if (ret < 0)
+               return ret;
+
+       ret &= ~MDIO_CTRL1_SPEEDSEL;
+       ret |= MDIO_CTRL1_SPEED10G;
+       phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
+
+       ret = amd_xgbe_phy_pcs_power_cycle(phydev);
+       if (ret < 0)
+               return ret;
+
+       /* Set SerDes to 10G speed */
+       amd_xgbe_phy_serdes_start_ratechange(phydev);
+
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_10000_RATE);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_10000_WORD);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, SPEED_10000_TXAMP);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_10000_PLL);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_10000_CDR);
+
+       XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, RXTX_10000_BLWC);
+       XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_10000_PQ);
+
+       amd_xgbe_phy_serdes_complete_ratechange(phydev);
+
+       priv->mode = AMD_XGBE_MODE_KR;
+
+       return 0;
+}
+
+static int amd_xgbe_phy_gmii_2500_mode(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       int ret;
+
+       /* Disable KR training */
+       ret = amd_xgbe_an_disable_kr_training(phydev);
+       if (ret < 0)
+               return ret;
+
+       /* Set PCS to KX/1G speed */
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
+       if (ret < 0)
+               return ret;
+
+       ret &= ~MDIO_PCS_CTRL2_TYPE;
+       ret |= MDIO_PCS_CTRL2_10GBX;
+       phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2, ret);
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
+       if (ret < 0)
+               return ret;
+
+       ret &= ~MDIO_CTRL1_SPEEDSEL;
+       ret |= MDIO_CTRL1_SPEED1G;
+       phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
+
+       ret = amd_xgbe_phy_pcs_power_cycle(phydev);
+       if (ret < 0)
+               return ret;
+
+       /* Set SerDes to 2.5G speed */
+       amd_xgbe_phy_serdes_start_ratechange(phydev);
+
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_2500_RATE);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_2500_WORD);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, SPEED_2500_TXAMP);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_2500_PLL);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_2500_CDR);
+
+       XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, RXTX_2500_BLWC);
+       XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_2500_PQ);
+
+       amd_xgbe_phy_serdes_complete_ratechange(phydev);
+
+       priv->mode = AMD_XGBE_MODE_KX;
+
+       return 0;
+}
+
+static int amd_xgbe_phy_gmii_mode(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       int ret;
+
+       /* Disable KR training */
+       ret = amd_xgbe_an_disable_kr_training(phydev);
+       if (ret < 0)
+               return ret;
+
+       /* Set PCS to KX/1G speed */
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
+       if (ret < 0)
+               return ret;
+
+       ret &= ~MDIO_PCS_CTRL2_TYPE;
+       ret |= MDIO_PCS_CTRL2_10GBX;
+       phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2, ret);
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
+       if (ret < 0)
+               return ret;
+
+       ret &= ~MDIO_CTRL1_SPEEDSEL;
+       ret |= MDIO_CTRL1_SPEED1G;
+       phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
+
+       ret = amd_xgbe_phy_pcs_power_cycle(phydev);
+       if (ret < 0)
+               return ret;
+
+       /* Set SerDes to 1G speed */
+       amd_xgbe_phy_serdes_start_ratechange(phydev);
+
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_1000_RATE);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_1000_WORD);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, SPEED_1000_TXAMP);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_1000_PLL);
+       XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_1000_CDR);
+
+       XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, RXTX_1000_BLWC);
+       XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_1000_PQ);
+
+       amd_xgbe_phy_serdes_complete_ratechange(phydev);
+
+       priv->mode = AMD_XGBE_MODE_KX;
+
+       return 0;
+}
+
+static int amd_xgbe_phy_switch_mode(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       int ret;
+
+       /* If we are in KR switch to KX, and vice-versa */
+       if (priv->mode == AMD_XGBE_MODE_KR)
+               ret = amd_xgbe_phy_gmii_mode(phydev);
+       else
+               ret = amd_xgbe_phy_xgmii_mode(phydev);
+
+       return ret;
+}
+
+static enum amd_xgbe_phy_an amd_xgbe_an_switch_mode(struct phy_device *phydev)
+{
+       int ret;
+
+       ret = amd_xgbe_phy_switch_mode(phydev);
+       if (ret < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       return AMD_XGBE_AN_START;
+}
+
+static enum amd_xgbe_phy_an amd_xgbe_an_tx_training(struct phy_device *phydev,
+                                                   enum amd_xgbe_phy_rx *state)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       int ad_reg, lp_reg, ret;
+
+       *state = AMD_XGBE_RX_COMPLETE;
+
+       /* If we're in KX mode then we're done */
+       if (priv->mode == AMD_XGBE_MODE_KX)
+               return AMD_XGBE_AN_EVENT;
+
+       /* Enable/Disable FEC */
+       ad_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2);
+       if (ad_reg < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       lp_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA + 2);
+       if (lp_reg < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FEC_CTRL);
+       if (ret < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       if ((ad_reg & 0xc000) && (lp_reg & 0xc000))
+               ret |= 0x01;
+       else
+               ret &= ~0x01;
+
+       phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FEC_CTRL, ret);
+
+       /* Start KR training */
+       ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL);
+       if (ret < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       ret |= 0x01;
+       phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret);
+
+       return AMD_XGBE_AN_EVENT;
+}
+
+static enum amd_xgbe_phy_an amd_xgbe_an_tx_xnp(struct phy_device *phydev,
+                                              enum amd_xgbe_phy_rx *state)
+{
+       u16 msg;
+
+       *state = AMD_XGBE_RX_XNP;
+
+       msg = XNP_MCF_NULL_MESSAGE;
+       msg |= XNP_MP_FORMATTED;
+
+       phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_XNP + 2, 0);
+       phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_XNP + 1, 0);
+       phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_XNP, msg);
+
+       return AMD_XGBE_AN_EVENT;
+}
+
+static enum amd_xgbe_phy_an amd_xgbe_an_rx_bpa(struct phy_device *phydev,
+                                              enum amd_xgbe_phy_rx *state)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       unsigned int link_support;
+       int ret, ad_reg, lp_reg;
+
+       /* Read Base Ability register 2 first */
+       ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA + 1);
+       if (ret < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       /* Check for a supported mode, otherwise restart in a different one */
+       link_support = (priv->mode == AMD_XGBE_MODE_KR) ? 0x80 : 0x20;
+       if (!(ret & link_support))
+               return amd_xgbe_an_switch_mode(phydev);
+
+       /* Check Extended Next Page support */
+       ad_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
+       if (ad_reg < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       lp_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA);
+       if (lp_reg < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       return ((ad_reg & XNP_NP_EXCHANGE) || (lp_reg & XNP_NP_EXCHANGE)) ?
+              amd_xgbe_an_tx_xnp(phydev, state) :
+              amd_xgbe_an_tx_training(phydev, state);
+}
+
+static enum amd_xgbe_phy_an amd_xgbe_an_rx_xnp(struct phy_device *phydev,
+                                              enum amd_xgbe_phy_rx *state)
+{
+       int ad_reg, lp_reg;
+
+       /* Check Extended Next Page support */
+       ad_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
+       if (ad_reg < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       lp_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA);
+       if (lp_reg < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       return ((ad_reg & XNP_NP_EXCHANGE) || (lp_reg & XNP_NP_EXCHANGE)) ?
+              amd_xgbe_an_tx_xnp(phydev, state) :
+              amd_xgbe_an_tx_training(phydev, state);
+}
+
+static enum amd_xgbe_phy_an amd_xgbe_an_start(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       int ret;
+
+       /* Be sure we aren't looping trying to negotiate */
+       if (priv->mode == AMD_XGBE_MODE_KR) {
+               if (priv->kr_state != AMD_XGBE_RX_READY)
+                       return AMD_XGBE_AN_NO_LINK;
+               priv->kr_state = AMD_XGBE_RX_BPA;
+       } else {
+               if (priv->kx_state != AMD_XGBE_RX_READY)
+                       return AMD_XGBE_AN_NO_LINK;
+               priv->kx_state = AMD_XGBE_RX_BPA;
+       }
+
+       /* Set up Advertisement register 3 first */
+       ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2);
+       if (ret < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       if (phydev->supported & SUPPORTED_10000baseR_FEC)
+               ret |= 0xc000;
+       else
+               ret &= ~0xc000;
+
+       phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2, ret);
+
+       /* Set up Advertisement register 2 next */
+       ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1);
+       if (ret < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       if (phydev->supported & SUPPORTED_10000baseKR_Full)
+               ret |= 0x80;
+       else
+               ret &= ~0x80;
+
+       if (phydev->supported & SUPPORTED_1000baseKX_Full)
+               ret |= 0x20;
+       else
+               ret &= ~0x20;
+
+       phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1, ret);
+
+       /* Set up Advertisement register 1 last */
+       ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
+       if (ret < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       if (phydev->supported & SUPPORTED_Pause)
+               ret |= 0x400;
+       else
+               ret &= ~0x400;
+
+       if (phydev->supported & SUPPORTED_Asym_Pause)
+               ret |= 0x800;
+       else
+               ret &= ~0x800;
+
+       /* We don't intend to perform XNP */
+       ret &= ~XNP_NP_EXCHANGE;
+
+       phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE, ret);
+
+       /* Enable and start auto-negotiation */
+       phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0);
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
+       if (ret < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       ret |= MDIO_AN_CTRL1_ENABLE;
+       ret |= MDIO_AN_CTRL1_RESTART;
+       phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, ret);
+
+       return AMD_XGBE_AN_EVENT;
+}
+
+static enum amd_xgbe_phy_an amd_xgbe_an_event(struct phy_device *phydev)
+{
+       enum amd_xgbe_phy_an new_state;
+       int ret;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT);
+       if (ret < 0)
+               return AMD_XGBE_AN_ERROR;
+
+       new_state = AMD_XGBE_AN_EVENT;
+       if (ret & XGBE_AN_PG_RCV)
+               new_state = AMD_XGBE_AN_PAGE_RECEIVED;
+       else if (ret & XGBE_AN_INC_LINK)
+               new_state = AMD_XGBE_AN_INCOMPAT_LINK;
+       else if (ret & XGBE_AN_INT_CMPLT)
+               new_state = AMD_XGBE_AN_COMPLETE;
+
+       if (new_state != AMD_XGBE_AN_EVENT)
+               phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0);
+
+       return new_state;
+}
+
+static enum amd_xgbe_phy_an amd_xgbe_an_page_received(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       enum amd_xgbe_phy_rx *state;
+       int ret;
+
+       state = (priv->mode == AMD_XGBE_MODE_KR) ? &priv->kr_state
+                                                : &priv->kx_state;
+
+       switch (*state) {
+       case AMD_XGBE_RX_BPA:
+               ret = amd_xgbe_an_rx_bpa(phydev, state);
+               break;
+
+       case AMD_XGBE_RX_XNP:
+               ret = amd_xgbe_an_rx_xnp(phydev, state);
+               break;
+
+       default:
+               ret = AMD_XGBE_AN_ERROR;
+       }
+
+       return ret;
+}
+
+static enum amd_xgbe_phy_an amd_xgbe_an_incompat_link(struct phy_device *phydev)
+{
+       return amd_xgbe_an_switch_mode(phydev);
+}
+
+static void amd_xgbe_an_state_machine(struct work_struct *work)
+{
+       struct amd_xgbe_phy_priv *priv = container_of(work,
+                                                     struct amd_xgbe_phy_priv,
+                                                     an_work);
+       struct phy_device *phydev = priv->phydev;
+       enum amd_xgbe_phy_an cur_state;
+       int sleep;
+
+       while (1) {
+               mutex_lock(&priv->an_mutex);
+
+               cur_state = priv->an_state;
+
+               switch (priv->an_state) {
+               case AMD_XGBE_AN_START:
+                       priv->an_state = amd_xgbe_an_start(phydev);
+                       break;
+
+               case AMD_XGBE_AN_EVENT:
+                       priv->an_state = amd_xgbe_an_event(phydev);
+                       break;
+
+               case AMD_XGBE_AN_PAGE_RECEIVED:
+                       priv->an_state = amd_xgbe_an_page_received(phydev);
+                       break;
+
+               case AMD_XGBE_AN_INCOMPAT_LINK:
+                       priv->an_state = amd_xgbe_an_incompat_link(phydev);
+                       break;
+
+               case AMD_XGBE_AN_COMPLETE:
+               case AMD_XGBE_AN_NO_LINK:
+               case AMD_XGBE_AN_EXIT:
+                       goto exit_unlock;
+
+               default:
+                       priv->an_state = AMD_XGBE_AN_ERROR;
+               }
+
+               if (priv->an_state == AMD_XGBE_AN_ERROR) {
+                       netdev_err(phydev->attached_dev,
+                                  "error during auto-negotiation, state=%u\n",
+                                  cur_state);
+                       goto exit_unlock;
+               }
+
+               sleep = (priv->an_state == AMD_XGBE_AN_EVENT) ? 1 : 0;
+
+               mutex_unlock(&priv->an_mutex);
+
+               if (sleep)
+                       usleep_range(20, 50);
+       }
+
+exit_unlock:
+       priv->an_result = priv->an_state;
+       priv->an_state = AMD_XGBE_AN_READY;
+
+       mutex_unlock(&priv->an_mutex);
+}
+
+static int amd_xgbe_phy_soft_reset(struct phy_device *phydev)
+{
+       int count, ret;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
+       if (ret < 0)
+               return ret;
+
+       ret |= MDIO_CTRL1_RESET;
+       phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
+
+       count = 50;
+       do {
+               msleep(20);
+               ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
+               if (ret < 0)
+                       return ret;
+       } while ((ret & MDIO_CTRL1_RESET) && --count);
+
+       if (ret & MDIO_CTRL1_RESET)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int amd_xgbe_phy_config_init(struct phy_device *phydev)
+{
+       /* Initialize supported features */
+       phydev->supported = SUPPORTED_Autoneg;
+       phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+       phydev->supported |= SUPPORTED_Backplane;
+       phydev->supported |= SUPPORTED_1000baseKX_Full |
+                            SUPPORTED_2500baseX_Full;
+       phydev->supported |= SUPPORTED_10000baseKR_Full |
+                            SUPPORTED_10000baseR_FEC;
+       phydev->advertising = phydev->supported;
+
+       /* Turn off and clear interrupts */
+       phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INTMASK, 0);
+       phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0);
+
+       return 0;
+}
+
+static int amd_xgbe_phy_setup_forced(struct phy_device *phydev)
+{
+       int ret;
+
+       /* Disable auto-negotiation */
+       ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
+       if (ret < 0)
+               return ret;
+
+       ret &= ~MDIO_AN_CTRL1_ENABLE;
+       phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, ret);
+
+       /* Validate/Set specified speed */
+       switch (phydev->speed) {
+       case SPEED_10000:
+               ret = amd_xgbe_phy_xgmii_mode(phydev);
+               break;
+
+       case SPEED_2500:
+               ret = amd_xgbe_phy_gmii_2500_mode(phydev);
+               break;
+
+       case SPEED_1000:
+               ret = amd_xgbe_phy_gmii_mode(phydev);
+               break;
+
+       default:
+               ret = -EINVAL;
+       }
+
+       if (ret < 0)
+               return ret;
+
+       /* Validate duplex mode */
+       if (phydev->duplex != DUPLEX_FULL)
+               return -EINVAL;
+
+       phydev->pause = 0;
+       phydev->asym_pause = 0;
+
+       return 0;
+}
+
+static int amd_xgbe_phy_config_aneg(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       u32 mmd_mask = phydev->c45_ids.devices_in_package;
+       int ret;
+
+       if (phydev->autoneg != AUTONEG_ENABLE)
+               return amd_xgbe_phy_setup_forced(phydev);
+
+       /* Make sure we have the AN MMD present */
+       if (!(mmd_mask & MDIO_DEVS_AN))
+               return -EINVAL;
+
+       /* Get the current speed mode */
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
+       if (ret < 0)
+               return ret;
+
+       /* Start/Restart the auto-negotiation state machine */
+       mutex_lock(&priv->an_mutex);
+       priv->an_result = AMD_XGBE_AN_READY;
+       priv->an_state = AMD_XGBE_AN_START;
+       priv->kr_state = AMD_XGBE_RX_READY;
+       priv->kx_state = AMD_XGBE_RX_READY;
+       mutex_unlock(&priv->an_mutex);
+
+       queue_work(priv->an_workqueue, &priv->an_work);
+
+       return 0;
+}
+
+static int amd_xgbe_phy_aneg_done(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       enum amd_xgbe_phy_an state;
+
+       mutex_lock(&priv->an_mutex);
+       state = priv->an_result;
+       mutex_unlock(&priv->an_mutex);
+
+       return (state == AMD_XGBE_AN_COMPLETE);
+}
+
+static int amd_xgbe_phy_update_link(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       enum amd_xgbe_phy_an state;
+       unsigned int check_again, autoneg;
+       int ret;
+
+       /* If we're doing auto-negotiation don't report link down */
+       mutex_lock(&priv->an_mutex);
+       state = priv->an_state;
+       mutex_unlock(&priv->an_mutex);
+
+       if (state != AMD_XGBE_AN_READY) {
+               phydev->link = 1;
+               return 0;
+       }
+
+       /* Since the device can be in the wrong mode when a link is
+        * (re-)established (cable connected after the interface is
+        * up, etc.), the link status may report no link. If there
+        * is no link, try switching modes and checking the status
+        * again.
+        */
+       check_again = 1;
+again:
+       /* Link status is latched low, so read once to clear
+        * and then read again to get current state
+        */
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1);
+       if (ret < 0)
+               return ret;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1);
+       if (ret < 0)
+               return ret;
+
+       phydev->link = (ret & MDIO_STAT1_LSTATUS) ? 1 : 0;
+
+       if (!phydev->link) {
+               ret = amd_xgbe_phy_switch_mode(phydev);
+               if (check_again) {
+                       check_again = 0;
+                       goto again;
+               }
+       }
+
+       autoneg = (phydev->link && !priv->link) ? 1 : 0;
+       priv->link = phydev->link;
+       if (autoneg) {
+               /* Link is (back) up, re-start auto-negotiation */
+               ret = amd_xgbe_phy_config_aneg(phydev);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int amd_xgbe_phy_read_status(struct phy_device *phydev)
+{
+       u32 mmd_mask = phydev->c45_ids.devices_in_package;
+       int ret, mode, ad_ret, lp_ret;
+
+       ret = amd_xgbe_phy_update_link(phydev);
+       if (ret)
+               return ret;
+
+       mode = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
+       if (mode < 0)
+               return mode;
+       mode &= MDIO_PCS_CTRL2_TYPE;
+
+       if (phydev->autoneg == AUTONEG_ENABLE) {
+               if (!(mmd_mask & MDIO_DEVS_AN))
+                       return -EINVAL;
+
+               if (!amd_xgbe_phy_aneg_done(phydev))
+                       return 0;
+
+               /* Compare Advertisement and Link Partner register 1 */
+               ad_ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
+               if (ad_ret < 0)
+                       return ad_ret;
+               lp_ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA);
+               if (lp_ret < 0)
+                       return lp_ret;
+
+               ad_ret &= lp_ret;
+               phydev->pause = (ad_ret & 0x400) ? 1 : 0;
+               phydev->asym_pause = (ad_ret & 0x800) ? 1 : 0;
+
+               /* Compare Advertisement and Link Partner register 2 */
+               ad_ret = phy_read_mmd(phydev, MDIO_MMD_AN,
+                                     MDIO_AN_ADVERTISE + 1);
+               if (ad_ret < 0)
+                       return ad_ret;
+               lp_ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA + 1);
+               if (lp_ret < 0)
+                       return lp_ret;
+
+               ad_ret &= lp_ret;
+               if (ad_ret & 0x80) {
+                       phydev->speed = SPEED_10000;
+                       if (mode != MDIO_PCS_CTRL2_10GBR) {
+                               ret = amd_xgbe_phy_xgmii_mode(phydev);
+                               if (ret < 0)
+                                       return ret;
+                       }
+               } else {
+                       phydev->speed = SPEED_1000;
+                       if (mode == MDIO_PCS_CTRL2_10GBR) {
+                               ret = amd_xgbe_phy_gmii_mode(phydev);
+                               if (ret < 0)
+                                       return ret;
+                       }
+               }
+
+               phydev->duplex = DUPLEX_FULL;
+       } else {
+               phydev->speed = (mode == MDIO_PCS_CTRL2_10GBR) ? SPEED_10000
+                                                              : SPEED_1000;
+               phydev->duplex = DUPLEX_FULL;
+               phydev->pause = 0;
+               phydev->asym_pause = 0;
+       }
+
+       return 0;
+}
+
+static int amd_xgbe_phy_suspend(struct phy_device *phydev)
+{
+       int ret;
+
+       mutex_lock(&phydev->lock);
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
+       if (ret < 0)
+               goto unlock;
+
+       ret |= MDIO_CTRL1_LPOWER;
+       phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
+
+       ret = 0;
+
+unlock:
+       mutex_unlock(&phydev->lock);
+
+       return ret;
+}
+
+static int amd_xgbe_phy_resume(struct phy_device *phydev)
+{
+       int ret;
+
+       mutex_lock(&phydev->lock);
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
+       if (ret < 0)
+               goto unlock;
+
+       ret &= ~MDIO_CTRL1_LPOWER;
+       phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
+
+       ret = 0;
+
+unlock:
+       mutex_unlock(&phydev->lock);
+
+       return ret;
+}
+
+static int amd_xgbe_phy_probe(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv;
+       struct platform_device *pdev;
+       struct device *dev;
+       char *wq_name;
+       int ret;
+
+       if (!phydev->dev.of_node)
+               return -EINVAL;
+
+       pdev = of_find_device_by_node(phydev->dev.of_node);
+       if (!pdev)
+               return -EINVAL;
+       dev = &pdev->dev;
+
+       wq_name = kasprintf(GFP_KERNEL, "%s-amd-xgbe-phy", phydev->bus->name);
+       if (!wq_name) {
+               ret = -ENOMEM;
+               goto err_pdev;
+       }
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               ret = -ENOMEM;
+               goto err_name;
+       }
+
+       priv->pdev = pdev;
+       priv->dev = dev;
+       priv->phydev = phydev;
+
+       /* Get the device mmio areas */
+       priv->rxtx_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       priv->rxtx_regs = devm_ioremap_resource(dev, priv->rxtx_res);
+       if (IS_ERR(priv->rxtx_regs)) {
+               dev_err(dev, "rxtx ioremap failed\n");
+               ret = PTR_ERR(priv->rxtx_regs);
+               goto err_priv;
+       }
+
+       priv->sir0_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       priv->sir0_regs = devm_ioremap_resource(dev, priv->sir0_res);
+       if (IS_ERR(priv->sir0_regs)) {
+               dev_err(dev, "sir0 ioremap failed\n");
+               ret = PTR_ERR(priv->sir0_regs);
+               goto err_rxtx;
+       }
+
+       priv->sir1_res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+       priv->sir1_regs = devm_ioremap_resource(dev, priv->sir1_res);
+       if (IS_ERR(priv->sir1_regs)) {
+               dev_err(dev, "sir1 ioremap failed\n");
+               ret = PTR_ERR(priv->sir1_regs);
+               goto err_sir0;
+       }
+
+       priv->link = 1;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
+       if (ret < 0)
+               goto err_sir1;
+       if ((ret & MDIO_PCS_CTRL2_TYPE) == MDIO_PCS_CTRL2_10GBR)
+               priv->mode = AMD_XGBE_MODE_KR;
+       else
+               priv->mode = AMD_XGBE_MODE_KX;
+
+       mutex_init(&priv->an_mutex);
+       INIT_WORK(&priv->an_work, amd_xgbe_an_state_machine);
+       priv->an_workqueue = create_singlethread_workqueue(wq_name);
+       if (!priv->an_workqueue) {
+               ret = -ENOMEM;
+               goto err_sir1;
+       }
+
+       phydev->priv = priv;
+
+       kfree(wq_name);
+       of_dev_put(pdev);
+
+       return 0;
+
+err_sir1:
+       devm_iounmap(dev, priv->sir1_regs);
+       devm_release_mem_region(dev, priv->sir1_res->start,
+                               resource_size(priv->sir1_res));
+
+err_sir0:
+       devm_iounmap(dev, priv->sir0_regs);
+       devm_release_mem_region(dev, priv->sir0_res->start,
+                               resource_size(priv->sir0_res));
+
+err_rxtx:
+       devm_iounmap(dev, priv->rxtx_regs);
+       devm_release_mem_region(dev, priv->rxtx_res->start,
+                               resource_size(priv->rxtx_res));
+
+err_priv:
+       devm_kfree(dev, priv);
+
+err_name:
+       kfree(wq_name);
+
+err_pdev:
+       of_dev_put(pdev);
+
+       return ret;
+}
+
+static void amd_xgbe_phy_remove(struct phy_device *phydev)
+{
+       struct amd_xgbe_phy_priv *priv = phydev->priv;
+       struct device *dev = priv->dev;
+
+       /* Stop any in process auto-negotiation */
+       mutex_lock(&priv->an_mutex);
+       priv->an_state = AMD_XGBE_AN_EXIT;
+       mutex_unlock(&priv->an_mutex);
+
+       flush_workqueue(priv->an_workqueue);
+       destroy_workqueue(priv->an_workqueue);
+
+       /* Release resources */
+       devm_iounmap(dev, priv->sir1_regs);
+       devm_release_mem_region(dev, priv->sir1_res->start,
+                               resource_size(priv->sir1_res));
+
+       devm_iounmap(dev, priv->sir0_regs);
+       devm_release_mem_region(dev, priv->sir0_res->start,
+                               resource_size(priv->sir0_res));
+
+       devm_iounmap(dev, priv->rxtx_regs);
+       devm_release_mem_region(dev, priv->rxtx_res->start,
+                               resource_size(priv->rxtx_res));
+
+       devm_kfree(dev, priv);
+}
+
+static int amd_xgbe_match_phy_device(struct phy_device *phydev)
+{
+       return phydev->c45_ids.device_ids[MDIO_MMD_PCS] == XGBE_PHY_ID;
+}
+
+static struct phy_driver amd_xgbe_phy_driver[] = {
+       {
+               .phy_id                 = XGBE_PHY_ID,
+               .phy_id_mask            = XGBE_PHY_MASK,
+               .name                   = "AMD XGBE PHY",
+               .features               = 0,
+               .probe                  = amd_xgbe_phy_probe,
+               .remove                 = amd_xgbe_phy_remove,
+               .soft_reset             = amd_xgbe_phy_soft_reset,
+               .config_init            = amd_xgbe_phy_config_init,
+               .suspend                = amd_xgbe_phy_suspend,
+               .resume                 = amd_xgbe_phy_resume,
+               .config_aneg            = amd_xgbe_phy_config_aneg,
+               .aneg_done              = amd_xgbe_phy_aneg_done,
+               .read_status            = amd_xgbe_phy_read_status,
+               .match_phy_device       = amd_xgbe_match_phy_device,
+               .driver                 = {
+                       .owner = THIS_MODULE,
+               },
+       },
+};
+
+static int __init amd_xgbe_phy_init(void)
+{
+       return phy_drivers_register(amd_xgbe_phy_driver,
+                                   ARRAY_SIZE(amd_xgbe_phy_driver));
+}
+
+static void __exit amd_xgbe_phy_exit(void)
+{
+       phy_drivers_unregister(amd_xgbe_phy_driver,
+                              ARRAY_SIZE(amd_xgbe_phy_driver));
+}
+
+module_init(amd_xgbe_phy_init);
+module_exit(amd_xgbe_phy_exit);
+
+static struct mdio_device_id __maybe_unused amd_xgbe_phy_ids[] = {
+       { XGBE_PHY_ID, XGBE_PHY_MASK },
+       { }
+};
+MODULE_DEVICE_TABLE(mdio, amd_xgbe_phy_ids);
index 643464d5a727b3b937fbe803072861de15a1414f..6c622aedbae111842b0e8ec86aede54653b84ec6 100644 (file)
@@ -144,41 +144,11 @@ static int at803x_resume(struct phy_device *phydev)
 
 static int at803x_config_init(struct phy_device *phydev)
 {
-       int val;
        int ret;
-       u32 features;
-
-       features = SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_AUI |
-                  SUPPORTED_FIBRE | SUPPORTED_BNC;
-
-       val = phy_read(phydev, MII_BMSR);
-       if (val < 0)
-               return val;
-
-       if (val & BMSR_ANEGCAPABLE)
-               features |= SUPPORTED_Autoneg;
-       if (val & BMSR_100FULL)
-               features |= SUPPORTED_100baseT_Full;
-       if (val & BMSR_100HALF)
-               features |= SUPPORTED_100baseT_Half;
-       if (val & BMSR_10FULL)
-               features |= SUPPORTED_10baseT_Full;
-       if (val & BMSR_10HALF)
-               features |= SUPPORTED_10baseT_Half;
-
-       if (val & BMSR_ESTATEN) {
-               val = phy_read(phydev, MII_ESTATUS);
-               if (val < 0)
-                       return val;
-
-               if (val & ESTATUS_1000_TFULL)
-                       features |= SUPPORTED_1000baseT_Full;
-               if (val & ESTATUS_1000_THALF)
-                       features |= SUPPORTED_1000baseT_Half;
-       }
 
-       phydev->supported = features;
-       phydev->advertising = features;
+       ret = genphy_config_init(phydev);
+       if (ret < 0)
+               return ret;
 
        if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
                ret = phy_write(phydev, AT803X_DEBUG_ADDR,
@@ -283,8 +253,7 @@ static int __init atheros_init(void)
 
 static void __exit atheros_exit(void)
 {
-       return phy_drivers_unregister(at803x_driver,
-                                     ARRAY_SIZE(at803x_driver));
+       phy_drivers_unregister(at803x_driver, ARRAY_SIZE(at803x_driver));
 }
 
 module_init(atheros_init);
index ba55adfc7aaef00b7cfee22fe0fd2ff8eb67ffe8..d60d875cb4450ab6cf72114c35b4c816e2e031fd 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/phy_fixed.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/of.h>
 
 #define MII_REGS_NUM 29
 
@@ -31,7 +32,7 @@ struct fixed_mdio_bus {
 };
 
 struct fixed_phy {
-       int id;
+       int addr;
        u16 regs[MII_REGS_NUM];
        struct phy_device *phydev;
        struct fixed_phy_status status;
@@ -104,8 +105,8 @@ static int fixed_phy_update_regs(struct fixed_phy *fp)
        if (fp->status.asym_pause)
                lpa |= LPA_PAUSE_ASYM;
 
-       fp->regs[MII_PHYSID1] = fp->id >> 16;
-       fp->regs[MII_PHYSID2] = fp->id;
+       fp->regs[MII_PHYSID1] = 0;
+       fp->regs[MII_PHYSID2] = 0;
 
        fp->regs[MII_BMSR] = bmsr;
        fp->regs[MII_BMCR] = bmcr;
@@ -115,7 +116,7 @@ static int fixed_phy_update_regs(struct fixed_phy *fp)
        return 0;
 }
 
-static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num)
+static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
 {
        struct fixed_mdio_bus *fmb = bus->priv;
        struct fixed_phy *fp;
@@ -124,7 +125,7 @@ static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num)
                return -1;
 
        list_for_each_entry(fp, &fmb->phys, node) {
-               if (fp->id == phy_id) {
+               if (fp->addr == phy_addr) {
                        /* Issue callback if user registered it. */
                        if (fp->link_update) {
                                fp->link_update(fp->phydev->attached_dev,
@@ -138,7 +139,7 @@ static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num)
        return 0xFFFF;
 }
 
-static int fixed_mdio_write(struct mii_bus *bus, int phy_id, int reg_num,
+static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num,
                            u16 val)
 {
        return 0;
@@ -160,7 +161,7 @@ int fixed_phy_set_link_update(struct phy_device *phydev,
                return -EINVAL;
 
        list_for_each_entry(fp, &fmb->phys, node) {
-               if (fp->id == phydev->phy_id) {
+               if (fp->addr == phydev->addr) {
                        fp->link_update = link_update;
                        fp->phydev = phydev;
                        return 0;
@@ -171,7 +172,7 @@ int fixed_phy_set_link_update(struct phy_device *phydev,
 }
 EXPORT_SYMBOL_GPL(fixed_phy_set_link_update);
 
-int fixed_phy_add(unsigned int irq, int phy_id,
+int fixed_phy_add(unsigned int irq, int phy_addr,
                  struct fixed_phy_status *status)
 {
        int ret;
@@ -184,9 +185,9 @@ int fixed_phy_add(unsigned int irq, int phy_id,
 
        memset(fp->regs, 0xFF,  sizeof(fp->regs[0]) * MII_REGS_NUM);
 
-       fmb->irqs[phy_id] = irq;
+       fmb->irqs[phy_addr] = irq;
 
-       fp->id = phy_id;
+       fp->addr = phy_addr;
        fp->status = *status;
 
        ret = fixed_phy_update_regs(fp);
@@ -203,6 +204,66 @@ int fixed_phy_add(unsigned int irq, int phy_id,
 }
 EXPORT_SYMBOL_GPL(fixed_phy_add);
 
+void fixed_phy_del(int phy_addr)
+{
+       struct fixed_mdio_bus *fmb = &platform_fmb;
+       struct fixed_phy *fp, *tmp;
+
+       list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {
+               if (fp->addr == phy_addr) {
+                       list_del(&fp->node);
+                       kfree(fp);
+                       return;
+               }
+       }
+}
+EXPORT_SYMBOL_GPL(fixed_phy_del);
+
+static int phy_fixed_addr;
+static DEFINE_SPINLOCK(phy_fixed_addr_lock);
+
+int fixed_phy_register(unsigned int irq,
+                      struct fixed_phy_status *status,
+                      struct device_node *np)
+{
+       struct fixed_mdio_bus *fmb = &platform_fmb;
+       struct phy_device *phy;
+       int phy_addr;
+       int ret;
+
+       /* Get the next available PHY address, up to PHY_MAX_ADDR */
+       spin_lock(&phy_fixed_addr_lock);
+       if (phy_fixed_addr == PHY_MAX_ADDR) {
+               spin_unlock(&phy_fixed_addr_lock);
+               return -ENOSPC;
+       }
+       phy_addr = phy_fixed_addr++;
+       spin_unlock(&phy_fixed_addr_lock);
+
+       ret = fixed_phy_add(PHY_POLL, phy_addr, status);
+       if (ret < 0)
+               return ret;
+
+       phy = get_phy_device(fmb->mii_bus, phy_addr, false);
+       if (!phy || IS_ERR(phy)) {
+               fixed_phy_del(phy_addr);
+               return -EINVAL;
+       }
+
+       of_node_get(np);
+       phy->dev.of_node = np;
+
+       ret = phy_device_register(phy);
+       if (ret) {
+               phy_device_free(phy);
+               of_node_put(np);
+               fixed_phy_del(phy_addr);
+               return ret;
+       }
+
+       return 0;
+}
+
 static int __init fixed_mdio_bus_init(void)
 {
        struct fixed_mdio_bus *fmb = &platform_fmb;
index 76f54b32a120832f2ce212c129592a6f30ab83df..2e58aa54484c9ca4e3154e231a3af2766bc84933 100644 (file)
@@ -69,6 +69,73 @@ struct mii_bus *mdiobus_alloc_size(size_t size)
 }
 EXPORT_SYMBOL(mdiobus_alloc_size);
 
+static void _devm_mdiobus_free(struct device *dev, void *res)
+{
+       mdiobus_free(*(struct mii_bus **)res);
+}
+
+static int devm_mdiobus_match(struct device *dev, void *res, void *data)
+{
+       struct mii_bus **r = res;
+
+       if (WARN_ON(!r || !*r))
+               return 0;
+
+       return *r == data;
+}
+
+/**
+ * devm_mdiobus_alloc_size - Resource-managed mdiobus_alloc_size()
+ * @dev:               Device to allocate mii_bus for
+ * @sizeof_priv:       Space to allocate for private structure.
+ *
+ * Managed mdiobus_alloc_size. mii_bus allocated with this function is
+ * automatically freed on driver detach.
+ *
+ * If an mii_bus allocated with this function needs to be freed separately,
+ * devm_mdiobus_free() must be used.
+ *
+ * RETURNS:
+ * Pointer to allocated mii_bus on success, NULL on failure.
+ */
+struct mii_bus *devm_mdiobus_alloc_size(struct device *dev, int sizeof_priv)
+{
+       struct mii_bus **ptr, *bus;
+
+       ptr = devres_alloc(_devm_mdiobus_free, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return NULL;
+
+       /* use raw alloc_dr for kmalloc caller tracing */
+       bus = mdiobus_alloc_size(sizeof_priv);
+       if (bus) {
+               *ptr = bus;
+               devres_add(dev, ptr);
+       } else {
+               devres_free(ptr);
+       }
+
+       return bus;
+}
+EXPORT_SYMBOL_GPL(devm_mdiobus_alloc_size);
+
+/**
+ * devm_mdiobus_free - Resource-managed mdiobus_free()
+ * @dev:               Device this mii_bus belongs to
+ * @bus:               the mii_bus associated with the device
+ *
+ * Free mii_bus allocated with devm_mdiobus_alloc_size().
+ */
+void devm_mdiobus_free(struct device *dev, struct mii_bus *bus)
+{
+       int rc;
+
+       rc = devres_release(dev, _devm_mdiobus_free,
+                           devm_mdiobus_match, bus);
+       WARN_ON(rc);
+}
+EXPORT_SYMBOL_GPL(devm_mdiobus_free);
+
 /**
  * mdiobus_release - mii_bus device release callback
  * @d: the target struct device that contains the mii_bus
@@ -233,6 +300,12 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr)
        if (IS_ERR(phydev) || phydev == NULL)
                return phydev;
 
+       /*
+        * For DT, see if the auto-probed phy has a correspoding child
+        * in the bus node, and set the of_node pointer in this case.
+        */
+       of_mdiobus_link_phydev(bus, phydev);
+
        err = phy_device_register(phydev);
        if (err) {
                phy_device_free(phydev);
index d849684231c14919727cb66d5e6e37b14278943d..bc7c7d2f75f26e41ccd205eeaa6402eee25e0233 100644 (file)
@@ -283,6 +283,110 @@ static int ksz9021_config_init(struct phy_device *phydev)
        return 0;
 }
 
+#define MII_KSZ9031RN_MMD_CTRL_REG     0x0d
+#define MII_KSZ9031RN_MMD_REGDATA_REG  0x0e
+#define OP_DATA                                1
+#define KSZ9031_PS_TO_REG              60
+
+/* Extended registers */
+#define MII_KSZ9031RN_CONTROL_PAD_SKEW 4
+#define MII_KSZ9031RN_RX_DATA_PAD_SKEW 5
+#define MII_KSZ9031RN_TX_DATA_PAD_SKEW 6
+#define MII_KSZ9031RN_CLK_PAD_SKEW     8
+
+static int ksz9031_extended_write(struct phy_device *phydev,
+                                 u8 mode, u32 dev_addr, u32 regnum, u16 val)
+{
+       phy_write(phydev, MII_KSZ9031RN_MMD_CTRL_REG, dev_addr);
+       phy_write(phydev, MII_KSZ9031RN_MMD_REGDATA_REG, regnum);
+       phy_write(phydev, MII_KSZ9031RN_MMD_CTRL_REG, (mode << 14) | dev_addr);
+       return phy_write(phydev, MII_KSZ9031RN_MMD_REGDATA_REG, val);
+}
+
+static int ksz9031_extended_read(struct phy_device *phydev,
+                                u8 mode, u32 dev_addr, u32 regnum)
+{
+       phy_write(phydev, MII_KSZ9031RN_MMD_CTRL_REG, dev_addr);
+       phy_write(phydev, MII_KSZ9031RN_MMD_REGDATA_REG, regnum);
+       phy_write(phydev, MII_KSZ9031RN_MMD_CTRL_REG, (mode << 14) | dev_addr);
+       return phy_read(phydev, MII_KSZ9031RN_MMD_REGDATA_REG);
+}
+
+static int ksz9031_of_load_skew_values(struct phy_device *phydev,
+                                      struct device_node *of_node,
+                                      u16 reg, size_t field_sz,
+                                      char *field[], u8 numfields)
+{
+       int val[4] = {-1, -2, -3, -4};
+       int matches = 0;
+       u16 mask;
+       u16 maxval;
+       u16 newval;
+       int i;
+
+       for (i = 0; i < numfields; i++)
+               if (!of_property_read_u32(of_node, field[i], val + i))
+                       matches++;
+
+       if (!matches)
+               return 0;
+
+       if (matches < numfields)
+               newval = ksz9031_extended_read(phydev, OP_DATA, 2, reg);
+       else
+               newval = 0;
+
+       maxval = (field_sz == 4) ? 0xf : 0x1f;
+       for (i = 0; i < numfields; i++)
+               if (val[i] != -(i + 1)) {
+                       mask = 0xffff;
+                       mask ^= maxval << (field_sz * i);
+                       newval = (newval & mask) |
+                               (((val[i] / KSZ9031_PS_TO_REG) & maxval)
+                                       << (field_sz * i));
+               }
+
+       return ksz9031_extended_write(phydev, OP_DATA, 2, reg, newval);
+}
+
+static int ksz9031_config_init(struct phy_device *phydev)
+{
+       struct device *dev = &phydev->dev;
+       struct device_node *of_node = dev->of_node;
+       char *clk_skews[2] = {"rxc-skew-ps", "txc-skew-ps"};
+       char *rx_data_skews[4] = {
+               "rxd0-skew-ps", "rxd1-skew-ps",
+               "rxd2-skew-ps", "rxd3-skew-ps"
+       };
+       char *tx_data_skews[4] = {
+               "txd0-skew-ps", "txd1-skew-ps",
+               "txd2-skew-ps", "txd3-skew-ps"
+       };
+       char *control_skews[2] = {"txen-skew-ps", "rxdv-skew-ps"};
+
+       if (!of_node && dev->parent->of_node)
+               of_node = dev->parent->of_node;
+
+       if (of_node) {
+               ksz9031_of_load_skew_values(phydev, of_node,
+                               MII_KSZ9031RN_CLK_PAD_SKEW, 5,
+                               clk_skews, 2);
+
+               ksz9031_of_load_skew_values(phydev, of_node,
+                               MII_KSZ9031RN_CONTROL_PAD_SKEW, 4,
+                               control_skews, 2);
+
+               ksz9031_of_load_skew_values(phydev, of_node,
+                               MII_KSZ9031RN_RX_DATA_PAD_SKEW, 4,
+                               rx_data_skews, 4);
+
+               ksz9031_of_load_skew_values(phydev, of_node,
+                               MII_KSZ9031RN_TX_DATA_PAD_SKEW, 4,
+                               tx_data_skews, 4);
+       }
+       return 0;
+}
+
 #define KSZ8873MLL_GLOBAL_CONTROL_4    0x06
 #define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX     (1 << 6)
 #define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED      (1 << 4)
@@ -469,7 +573,7 @@ static struct phy_driver ksphy_driver[] = {
        .features       = (PHY_GBIT_FEATURES | SUPPORTED_Pause
                                | SUPPORTED_Asym_Pause),
        .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
-       .config_init    = kszphy_config_init,
+       .config_init    = ksz9031_config_init,
        .config_aneg    = genphy_config_aneg,
        .read_status    = genphy_read_status,
        .ack_interrupt  = kszphy_ack_interrupt,
index 4987a1c6dc52e63220edb6fc94eea925a9fdf122..35d753d22f78b91d643548029df5b2e6eea64d49 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/mdio.h>
 #include <linux/io.h>
 #include <linux/uaccess.h>
+#include <linux/of.h>
 
 #include <asm/irq.h>
 
@@ -1067,14 +1068,11 @@ int genphy_soft_reset(struct phy_device *phydev)
 }
 EXPORT_SYMBOL(genphy_soft_reset);
 
-static int genphy_config_init(struct phy_device *phydev)
+int genphy_config_init(struct phy_device *phydev)
 {
        int val;
        u32 features;
 
-       /* For now, I'll claim that the generic driver supports
-        * all possible port types
-        */
        features = (SUPPORTED_TP | SUPPORTED_MII
                        | SUPPORTED_AUI | SUPPORTED_FIBRE |
                        SUPPORTED_BNC);
@@ -1107,8 +1105,8 @@ static int genphy_config_init(struct phy_device *phydev)
                        features |= SUPPORTED_1000baseT_Half;
        }
 
-       phydev->supported = features;
-       phydev->advertising = features;
+       phydev->supported &= features;
+       phydev->advertising &= features;
 
        return 0;
 }
@@ -1118,6 +1116,7 @@ static int gen10g_soft_reset(struct phy_device *phydev)
        /* Do nothing for now */
        return 0;
 }
+EXPORT_SYMBOL(genphy_config_init);
 
 static int gen10g_config_init(struct phy_device *phydev)
 {
@@ -1168,6 +1167,38 @@ static int gen10g_resume(struct phy_device *phydev)
        return 0;
 }
 
+static void of_set_phy_supported(struct phy_device *phydev)
+{
+       struct device_node *node = phydev->dev.of_node;
+       u32 max_speed;
+
+       if (!IS_ENABLED(CONFIG_OF_MDIO))
+               return;
+
+       if (!node)
+               return;
+
+       if (!of_property_read_u32(node, "max-speed", &max_speed)) {
+               /* The default values for phydev->supported are provided by the PHY
+                * driver "features" member, we want to reset to sane defaults fist
+                * before supporting higher speeds.
+                */
+               phydev->supported &= PHY_DEFAULT_FEATURES;
+
+               switch (max_speed) {
+               default:
+                       return;
+
+               case SPEED_1000:
+                       phydev->supported |= PHY_1000BT_FEATURES;
+               case SPEED_100:
+                       phydev->supported |= PHY_100BT_FEATURES;
+               case SPEED_10:
+                       phydev->supported |= PHY_10BT_FEATURES;
+               }
+       }
+}
+
 /**
  * phy_probe - probe and init a PHY device
  * @dev: device to probe and init
@@ -1202,7 +1233,8 @@ static int phy_probe(struct device *dev)
         * or both of these values
         */
        phydev->supported = phydrv->features;
-       phydev->advertising = phydrv->features;
+       of_set_phy_supported(phydev);
+       phydev->advertising = phydev->supported;
 
        /* Set the state to READY by default */
        phydev->state = PHY_READY;
@@ -1295,7 +1327,9 @@ static struct phy_driver genphy_driver[] = {
        .name           = "Generic PHY",
        .soft_reset     = genphy_soft_reset,
        .config_init    = genphy_config_init,
-       .features       = 0,
+       .features       = PHY_GBIT_FEATURES | SUPPORTED_MII |
+                         SUPPORTED_AUI | SUPPORTED_FIBRE |
+                         SUPPORTED_BNC,
        .config_aneg    = genphy_config_aneg,
        .aneg_done      = genphy_aneg_done,
        .read_status    = genphy_read_status,
index fa1d69a38ccf9cd374f0292d653e4c587b1ecb88..45483fdfbe063647c620f3b46db9772c0c17bd7a 100644 (file)
@@ -64,65 +64,51 @@ static int rtl8211e_config_intr(struct phy_device *phydev)
        return err;
 }
 
-/* RTL8201CP */
-static struct phy_driver rtl8201cp_driver = {
-       .phy_id         = 0x00008201,
-       .name           = "RTL8201CP Ethernet",
-       .phy_id_mask    = 0x0000ffff,
-       .features       = PHY_BASIC_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
-       .config_aneg    = &genphy_config_aneg,
-       .read_status    = &genphy_read_status,
-       .driver         = { .owner = THIS_MODULE,},
-};
-
-/* RTL8211B */
-static struct phy_driver rtl8211b_driver = {
-       .phy_id         = 0x001cc912,
-       .name           = "RTL8211B Gigabit Ethernet",
-       .phy_id_mask    = 0x001fffff,
-       .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
-       .config_aneg    = &genphy_config_aneg,
-       .read_status    = &genphy_read_status,
-       .ack_interrupt  = &rtl821x_ack_interrupt,
-       .config_intr    = &rtl8211b_config_intr,
-       .driver         = { .owner = THIS_MODULE,},
-};
-
-/* RTL8211E */
-static struct phy_driver rtl8211e_driver = {
-       .phy_id         = 0x001cc915,
-       .name           = "RTL8211E Gigabit Ethernet",
-       .phy_id_mask    = 0x001fffff,
-       .features       = PHY_GBIT_FEATURES,
-       .flags          = PHY_HAS_INTERRUPT,
-       .config_aneg    = &genphy_config_aneg,
-       .read_status    = &genphy_read_status,
-       .ack_interrupt  = &rtl821x_ack_interrupt,
-       .config_intr    = &rtl8211e_config_intr,
-       .suspend        = genphy_suspend,
-       .resume         = genphy_resume,
-       .driver         = { .owner = THIS_MODULE,},
+static struct phy_driver realtek_drvs[] = {
+       {
+               .phy_id         = 0x00008201,
+               .name           = "RTL8201CP Ethernet",
+               .phy_id_mask    = 0x0000ffff,
+               .features       = PHY_BASIC_FEATURES,
+               .flags          = PHY_HAS_INTERRUPT,
+               .config_aneg    = &genphy_config_aneg,
+               .read_status    = &genphy_read_status,
+               .driver         = { .owner = THIS_MODULE,},
+       }, {
+               .phy_id         = 0x001cc912,
+               .name           = "RTL8211B Gigabit Ethernet",
+               .phy_id_mask    = 0x001fffff,
+               .features       = PHY_GBIT_FEATURES,
+               .flags          = PHY_HAS_INTERRUPT,
+               .config_aneg    = &genphy_config_aneg,
+               .read_status    = &genphy_read_status,
+               .ack_interrupt  = &rtl821x_ack_interrupt,
+               .config_intr    = &rtl8211b_config_intr,
+               .driver         = { .owner = THIS_MODULE,},
+       }, {
+               .phy_id         = 0x001cc915,
+               .name           = "RTL8211E Gigabit Ethernet",
+               .phy_id_mask    = 0x001fffff,
+               .features       = PHY_GBIT_FEATURES,
+               .flags          = PHY_HAS_INTERRUPT,
+               .config_aneg    = &genphy_config_aneg,
+               .read_status    = &genphy_read_status,
+               .ack_interrupt  = &rtl821x_ack_interrupt,
+               .config_intr    = &rtl8211e_config_intr,
+               .suspend        = genphy_suspend,
+               .resume         = genphy_resume,
+               .driver         = { .owner = THIS_MODULE,},
+       },
 };
 
 static int __init realtek_init(void)
 {
-       int ret;
-
-       ret = phy_driver_register(&rtl8201cp_driver);
-       if (ret < 0)
-               return -ENODEV;
-       ret = phy_driver_register(&rtl8211b_driver);
-       if (ret < 0)
-               return -ENODEV;
-       return phy_driver_register(&rtl8211e_driver);
+       return phy_drivers_register(realtek_drvs, ARRAY_SIZE(realtek_drvs));
 }
 
 static void __exit realtek_exit(void)
 {
-       phy_driver_unregister(&rtl8211b_driver);
-       phy_driver_unregister(&rtl8211e_driver);
+       phy_drivers_unregister(realtek_drvs, ARRAY_SIZE(realtek_drvs));
 }
 
 module_init(realtek_init);
index 11f34813e23fb5423ccf90cf9d25e208ed2275f3..180c49479c42f9b4a19f070056b782923de5084c 100644 (file)
@@ -249,8 +249,7 @@ static int __init smsc_init(void)
 
 static void __exit smsc_exit(void)
 {
-       return phy_drivers_unregister(smsc_phy_driver,
-               ARRAY_SIZE(smsc_phy_driver));
+       phy_drivers_unregister(smsc_phy_driver, ARRAY_SIZE(smsc_phy_driver));
 }
 
 MODULE_DESCRIPTION("SMSC PHY driver");
index 14372c65a7e8209b5f97da6416ef80d1f41a522c..5dc0935da99c52a07ba7c09da27503b8ee0b2695 100644 (file)
@@ -319,8 +319,7 @@ static int __init vsc82xx_init(void)
 
 static void __exit vsc82xx_exit(void)
 {
-       return phy_drivers_unregister(vsc82xx_driver,
-               ARRAY_SIZE(vsc82xx_driver));
+       phy_drivers_unregister(vsc82xx_driver, ARRAY_SIZE(vsc82xx_driver));
 }
 
 module_init(vsc82xx_init);
index e3923ebb693fccc276db45de7c7d9551ee6bc208..91d6c1272fcf0ae4a655fa74ca6b91fb578a6108 100644 (file)
@@ -757,7 +757,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
                err = get_filter(argp, &code);
                if (err >= 0) {
-                       struct sock_fprog fprog = {
+                       struct sock_fprog_kern fprog = {
                                .len = err,
                                .filter = code,
                        };
@@ -778,7 +778,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
                err = get_filter(argp, &code);
                if (err >= 0) {
-                       struct sock_fprog fprog = {
+                       struct sock_fprog_kern fprog = {
                                .len = err,
                                .filter = code,
                        };
index 01805319e1e0335a70f86109e058cac44d669ee0..1aff970be33ec88ec8ff6781467155f68cf5b3b4 100644 (file)
@@ -281,7 +281,7 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
        nf_reset(skb);
 
        skb->ip_summed = CHECKSUM_NONE;
-       ip_select_ident(skb, &rt->dst, NULL);
+       ip_select_ident(skb, NULL);
        ip_send_check(iph);
 
        ip_local_out(skb);
index a8497183ff8b079985b7930fd5eb26dc9fb44813..dac7a0d9bb46e5d9d2385250f990a2a0acf5996c 100644 (file)
@@ -494,7 +494,7 @@ static int rionet_setup_netdev(struct rio_mport *mport, struct net_device *ndev)
        ndev->mtu = RIO_MAX_MSG_SIZE - 14;
        ndev->features = NETIF_F_LLTX;
        SET_NETDEV_DEV(ndev, &mport->dev);
-       SET_ETHTOOL_OPS(ndev, &rionet_ethtool_ops);
+       ndev->ethtool_ops = &rionet_ethtool_ops;
 
        spin_lock_init(&rnet->lock);
        spin_lock_init(&rnet->tx_lock);
index ce4989be86d96940401cbae318a75931a0d0b60c..b4958c7ffa8415a84921995ce6123081e6596348 100644 (file)
@@ -968,7 +968,7 @@ static void team_port_disable(struct team *team,
 static void __team_compute_features(struct team *team)
 {
        struct team_port *port;
-       u32 vlan_features = TEAM_VLAN_FEATURES;
+       u32 vlan_features = TEAM_VLAN_FEATURES & NETIF_F_ALL_FOR_ALL;
        unsigned short max_hard_header_len = ETH_HLEN;
        unsigned int flags, dst_release_flag = IFF_XMIT_DST_RELEASE;
 
index dbde3412ee5eafdeb39cd238e8af4f19368917c6..a58dfebb5512326db7065a433c37676c99820407 100644 (file)
@@ -49,7 +49,7 @@ struct lb_port_mapping {
 struct lb_priv_ex {
        struct team *team;
        struct lb_port_mapping tx_hash_to_port_mapping[LB_TX_HASHTABLE_SIZE];
-       struct sock_fprog *orig_fprog;
+       struct sock_fprog_kern *orig_fprog;
        struct {
                unsigned int refresh_interval; /* in tenths of second */
                struct delayed_work refresh_dw;
@@ -241,15 +241,15 @@ static int lb_bpf_func_get(struct team *team, struct team_gsetter_ctx *ctx)
        return 0;
 }
 
-static int __fprog_create(struct sock_fprog **pfprog, u32 data_len,
+static int __fprog_create(struct sock_fprog_kern **pfprog, u32 data_len,
                          const void *data)
 {
-       struct sock_fprog *fprog;
+       struct sock_fprog_kern *fprog;
        struct sock_filter *filter = (struct sock_filter *) data;
 
        if (data_len % sizeof(struct sock_filter))
                return -EINVAL;
-       fprog = kmalloc(sizeof(struct sock_fprog), GFP_KERNEL);
+       fprog = kmalloc(sizeof(*fprog), GFP_KERNEL);
        if (!fprog)
                return -ENOMEM;
        fprog->filter = kmemdup(filter, data_len, GFP_KERNEL);
@@ -262,7 +262,7 @@ static int __fprog_create(struct sock_fprog **pfprog, u32 data_len,
        return 0;
 }
 
-static void __fprog_destroy(struct sock_fprog *fprog)
+static void __fprog_destroy(struct sock_fprog_kern *fprog)
 {
        kfree(fprog->filter);
        kfree(fprog);
@@ -273,7 +273,7 @@ static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx)
        struct lb_priv *lb_priv = get_lb_priv(team);
        struct sk_filter *fp = NULL;
        struct sk_filter *orig_fp;
-       struct sock_fprog *fprog = NULL;
+       struct sock_fprog_kern *fprog = NULL;
        int err;
 
        if (ctx->data.bin_val.len) {
index ee328ba101e72a9e3f150d8c24068182be86abc5..98bad1fb1bfb1ce66ea4219c2e767256f05d6cbb 100644 (file)
@@ -498,12 +498,12 @@ static void tun_detach_all(struct net_device *dev)
        for (i = 0; i < n; i++) {
                tfile = rtnl_dereference(tun->tfiles[i]);
                BUG_ON(!tfile);
-               wake_up_all(&tfile->wq.wait);
+               tfile->socket.sk->sk_data_ready(tfile->socket.sk);
                RCU_INIT_POINTER(tfile->tun, NULL);
                --tun->numqueues;
        }
        list_for_each_entry(tfile, &tun->disabled, next) {
-               wake_up_all(&tfile->wq.wait);
+               tfile->socket.sk->sk_data_ready(tfile->socket.sk);
                RCU_INIT_POINTER(tfile->tun, NULL);
        }
        BUG_ON(tun->numqueues != 0);
@@ -807,8 +807,7 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
        /* Notify and wake up reader process */
        if (tfile->flags & TUN_FASYNC)
                kill_fasync(&tfile->fasync, SIGIO, POLL_IN);
-       wake_up_interruptible_poll(&tfile->wq.wait, POLLIN |
-                                  POLLRDNORM | POLLRDBAND);
+       tfile->socket.sk->sk_data_ready(tfile->socket.sk);
 
        rcu_read_unlock();
        return NETDEV_TX_OK;
@@ -965,7 +964,7 @@ static unsigned int tun_chr_poll(struct file *file, poll_table *wait)
 
        tun_debug(KERN_INFO, tun, "tun_chr_poll\n");
 
-       poll_wait(file, &tfile->wq.wait, wait);
+       poll_wait(file, sk_sleep(sk), wait);
 
        if (!skb_queue_empty(&sk->sk_receive_queue))
                mask |= POLLIN | POLLRDNORM;
@@ -1330,47 +1329,26 @@ static ssize_t tun_put_user(struct tun_struct *tun,
 static ssize_t tun_do_read(struct tun_struct *tun, struct tun_file *tfile,
                           const struct iovec *iv, ssize_t len, int noblock)
 {
-       DECLARE_WAITQUEUE(wait, current);
        struct sk_buff *skb;
        ssize_t ret = 0;
+       int peeked, err, off = 0;
 
        tun_debug(KERN_INFO, tun, "tun_do_read\n");
 
-       if (unlikely(!noblock))
-               add_wait_queue(&tfile->wq.wait, &wait);
-       while (len) {
-               if (unlikely(!noblock))
-                       current->state = TASK_INTERRUPTIBLE;
+       if (!len)
+               return ret;
 
-               /* Read frames from the queue */
-               if (!(skb = skb_dequeue(&tfile->socket.sk->sk_receive_queue))) {
-                       if (noblock) {
-                               ret = -EAGAIN;
-                               break;
-                       }
-                       if (signal_pending(current)) {
-                               ret = -ERESTARTSYS;
-                               break;
-                       }
-                       if (tun->dev->reg_state != NETREG_REGISTERED) {
-                               ret = -EIO;
-                               break;
-                       }
-
-                       /* Nothing to read, let's sleep */
-                       schedule();
-                       continue;
-               }
+       if (tun->dev->reg_state != NETREG_REGISTERED)
+               return -EIO;
 
+       /* Read frames from queue */
+       skb = __skb_recv_datagram(tfile->socket.sk, noblock ? MSG_DONTWAIT : 0,
+                                 &peeked, &off, &err);
+       if (skb) {
                ret = tun_put_user(tun, tfile, skb, iv, len);
                kfree_skb(skb);
-               break;
-       }
-
-       if (unlikely(!noblock)) {
-               current->state = TASK_RUNNING;
-               remove_wait_queue(&tfile->wq.wait, &wait);
-       }
+       } else
+               ret = err;
 
        return ret;
 }
@@ -2199,8 +2177,8 @@ static int tun_chr_open(struct inode *inode, struct file * file)
        tfile->flags = 0;
        tfile->ifindex = 0;
 
-       rcu_assign_pointer(tfile->socket.wq, &tfile->wq);
        init_waitqueue_head(&tfile->wq.wait);
+       RCU_INIT_POINTER(tfile->socket.wq, &tfile->wq);
 
        tfile->socket.file = file;
        tfile->socket.ops = &tun_socket_ops;
index 630caf48f63aab7c2023d4cadb7044d23a2af86d..8cfc3bb0c6a672a288784ab0dd5f09597265c39d 100644 (file)
@@ -793,7 +793,7 @@ static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id
 
        netdev->netdev_ops = &catc_netdev_ops;
        netdev->watchdog_timeo = TX_TIMEOUT;
-       SET_ETHTOOL_OPS(netdev, &ops);
+       netdev->ethtool_ops = &ops;
 
        catc->usbdev = usbdev;
        catc->netdev = netdev;
index 2e025ddcef210bc888dd8a8ff81473a32c3dd154..5ee7a1dbc023833a5907a6db48695600bb728fd9 100644 (file)
 #include <net/ipv6.h>
 #include <net/addrconf.h>
 
+/* alternative VLAN for IP session 0 if not untagged */
+#define MBIM_IPS0_VID  4094
+
 /* driver specific data - must match cdc_ncm usage */
 struct cdc_mbim_state {
        struct cdc_ncm_ctx *ctx;
        atomic_t pmcount;
        struct usb_driver *subdriver;
-       struct usb_interface *control;
-       struct usb_interface *data;
+       unsigned long _unused;
+       unsigned long flags;
+};
+
+/* flags for the cdc_mbim_state.flags field */
+enum cdc_mbim_flags {
+       FLAG_IPS0_VLAN = 1 << 0,        /* IP session 0 is tagged  */
 };
 
 /* using a counter to merge subdriver requests with our own into a combined state */
@@ -62,16 +70,91 @@ static int cdc_mbim_wdm_manage_power(struct usb_interface *intf, int status)
        return cdc_mbim_manage_power(dev, status);
 }
 
+static int cdc_mbim_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid)
+{
+       struct usbnet *dev = netdev_priv(netdev);
+       struct cdc_mbim_state *info = (void *)&dev->data;
+
+       /* creation of this VLAN is a request to tag IP session 0 */
+       if (vid == MBIM_IPS0_VID)
+               info->flags |= FLAG_IPS0_VLAN;
+       else
+               if (vid >= 512) /* we don't map these to MBIM session */
+                       return -EINVAL;
+       return 0;
+}
+
+static int cdc_mbim_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid)
+{
+       struct usbnet *dev = netdev_priv(netdev);
+       struct cdc_mbim_state *info = (void *)&dev->data;
+
+       /* this is a request for an untagged IP session 0 */
+       if (vid == MBIM_IPS0_VID)
+               info->flags &= ~FLAG_IPS0_VLAN;
+       return 0;
+}
+
+static const struct net_device_ops cdc_mbim_netdev_ops = {
+       .ndo_open             = usbnet_open,
+       .ndo_stop             = usbnet_stop,
+       .ndo_start_xmit       = usbnet_start_xmit,
+       .ndo_tx_timeout       = usbnet_tx_timeout,
+       .ndo_change_mtu       = usbnet_change_mtu,
+       .ndo_set_mac_address  = eth_mac_addr,
+       .ndo_validate_addr    = eth_validate_addr,
+       .ndo_vlan_rx_add_vid  = cdc_mbim_rx_add_vid,
+       .ndo_vlan_rx_kill_vid = cdc_mbim_rx_kill_vid,
+};
+
+/* Change the control interface altsetting and update the .driver_info
+ * pointer if the matching entry after changing class codes points to
+ * a different struct
+ */
+static int cdc_mbim_set_ctrlalt(struct usbnet *dev, struct usb_interface *intf, u8 alt)
+{
+       struct usb_driver *driver = to_usb_driver(intf->dev.driver);
+       const struct usb_device_id *id;
+       struct driver_info *info;
+       int ret;
+
+       ret = usb_set_interface(dev->udev,
+                               intf->cur_altsetting->desc.bInterfaceNumber,
+                               alt);
+       if (ret)
+               return ret;
+
+       id = usb_match_id(intf, driver->id_table);
+       if (!id)
+               return -ENODEV;
+
+       info = (struct driver_info *)id->driver_info;
+       if (info != dev->driver_info) {
+               dev_dbg(&intf->dev, "driver_info updated to '%s'\n",
+                       info->description);
+               dev->driver_info = info;
+       }
+       return 0;
+}
 
 static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf)
 {
        struct cdc_ncm_ctx *ctx;
        struct usb_driver *subdriver = ERR_PTR(-ENODEV);
        int ret = -ENODEV;
-       u8 data_altsetting = cdc_ncm_select_altsetting(dev, intf);
+       u8 data_altsetting = 1;
        struct cdc_mbim_state *info = (void *)&dev->data;
 
-       /* Probably NCM, defer for cdc_ncm_bind */
+       /* should we change control altsetting on a NCM/MBIM function? */
+       if (cdc_ncm_select_altsetting(intf) == CDC_NCM_COMM_ALTSETTING_MBIM) {
+               data_altsetting = CDC_NCM_DATA_ALTSETTING_MBIM;
+               ret = cdc_mbim_set_ctrlalt(dev, intf, CDC_NCM_COMM_ALTSETTING_MBIM);
+               if (ret)
+                       goto err;
+               ret = -ENODEV;
+       }
+
+       /* we will hit this for NCM/MBIM functions if prefer_mbim is false */
        if (!cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting))
                goto err;
 
@@ -101,7 +184,10 @@ static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf)
        dev->net->flags |= IFF_NOARP;
 
        /* no need to put the VLAN tci in the packet headers */
-       dev->net->features |= NETIF_F_HW_VLAN_CTAG_TX;
+       dev->net->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_FILTER;
+
+       /* monitor VLAN additions and removals */
+       dev->net->netdev_ops = &cdc_mbim_netdev_ops;
 err:
        return ret;
 }
@@ -164,12 +250,24 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb
                        skb_pull(skb, ETH_HLEN);
                }
 
+               /* Is IP session <0> tagged too? */
+               if (info->flags & FLAG_IPS0_VLAN) {
+                       /* drop all untagged packets */
+                       if (!tci)
+                               goto error;
+                       /* map MBIM_IPS0_VID to IPS<0> */
+                       if (tci == MBIM_IPS0_VID)
+                               tci = 0;
+               }
+
                /* mapping VLANs to MBIM sessions:
-                *   no tag     => IPS session <0>
+                *   no tag     => IPS session <0> if !FLAG_IPS0_VLAN
                 *   1 - 255    => IPS session <vlanid>
                 *   256 - 511  => DSS session <vlanid - 256>
-                *   512 - 4095 => unsupported, drop
+                *   512 - 4093 => unsupported, drop
+                *   4094       => IPS session <0> if FLAG_IPS0_VLAN
                 */
+
                switch (tci & 0x0f00) {
                case 0x0000: /* VLAN ID 0 - 255 */
                        if (!is_ip)
@@ -178,6 +276,8 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb
                        c[3] = tci;
                        break;
                case 0x0100: /* VLAN ID 256 - 511 */
+                       if (is_ip)
+                               goto error;
                        sign = cpu_to_le32(USB_CDC_MBIM_NDP16_DSS_SIGN);
                        c = (u8 *)&sign;
                        c[3] = tci;
@@ -223,8 +323,8 @@ static void do_neigh_solicit(struct usbnet *dev, u8 *buf, u16 tci)
        /* need to send the NA on the VLAN dev, if any */
        rcu_read_lock();
        if (tci) {
-               netdev = __vlan_find_dev_deep(dev->net, htons(ETH_P_8021Q),
-                                             tci);
+               netdev = __vlan_find_dev_deep_rcu(dev->net, htons(ETH_P_8021Q),
+                                                 tci);
                if (!netdev) {
                        rcu_read_unlock();
                        return;
@@ -268,7 +368,7 @@ static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_
        __be16 proto = htons(ETH_P_802_3);
        struct sk_buff *skb = NULL;
 
-       if (tci < 256) { /* IPS session? */
+       if (tci < 256 || tci == MBIM_IPS0_VID) { /* IPS session? */
                if (len < sizeof(struct iphdr))
                        goto err;
 
@@ -320,6 +420,7 @@ static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
        struct usb_cdc_ncm_dpe16 *dpe16;
        int ndpoffset;
        int loopcount = 50; /* arbitrary max preventing infinite loop */
+       u32 payload = 0;
        u8 *c;
        u16 tci;
 
@@ -338,6 +439,9 @@ static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
        case cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN):
                c = (u8 *)&ndp16->dwSignature;
                tci = c[3];
+               /* tag IPS<0> packets too if MBIM_IPS0_VID exists */
+               if (!tci && info->flags & FLAG_IPS0_VLAN)
+                       tci = MBIM_IPS0_VID;
                break;
        case cpu_to_le32(USB_CDC_MBIM_NDP16_DSS_SIGN):
                c = (u8 *)&ndp16->dwSignature;
@@ -379,6 +483,7 @@ static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
                        if (!skb)
                                goto error;
                        usbnet_skb_return(dev, skb);
+                       payload += len; /* count payload bytes in this NTB */
                }
        }
 err_ndp:
@@ -387,6 +492,10 @@ static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
        if (ndpoffset && loopcount--)
                goto next_ndp;
 
+       /* update stats */
+       ctx->rx_overhead += skb_in->len - payload;
+       ctx->rx_ntbs++;
+
        return 1;
 error:
        return 0;
index 9a2bd11943ebf391a5678c7ded07ee0bc7b50f5e..80a844e0ae0383303d8fa4a6d4147fc99337f268 100644 (file)
@@ -65,19 +65,384 @@ 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 int cdc_ncm_setup(struct usbnet *dev)
+struct cdc_ncm_stats {
+       char stat_string[ETH_GSTRING_LEN];
+       int sizeof_stat;
+       int stat_offset;
+};
+
+#define CDC_NCM_STAT(str, m) { \
+               .stat_string = str, \
+               .sizeof_stat = sizeof(((struct cdc_ncm_ctx *)0)->m), \
+               .stat_offset = offsetof(struct cdc_ncm_ctx, m) }
+#define CDC_NCM_SIMPLE_STAT(m) CDC_NCM_STAT(__stringify(m), m)
+
+static const struct cdc_ncm_stats cdc_ncm_gstrings_stats[] = {
+       CDC_NCM_SIMPLE_STAT(tx_reason_ntb_full),
+       CDC_NCM_SIMPLE_STAT(tx_reason_ndp_full),
+       CDC_NCM_SIMPLE_STAT(tx_reason_timeout),
+       CDC_NCM_SIMPLE_STAT(tx_reason_max_datagram),
+       CDC_NCM_SIMPLE_STAT(tx_overhead),
+       CDC_NCM_SIMPLE_STAT(tx_ntbs),
+       CDC_NCM_SIMPLE_STAT(rx_overhead),
+       CDC_NCM_SIMPLE_STAT(rx_ntbs),
+};
+
+static int cdc_ncm_get_sset_count(struct net_device __always_unused *netdev, int sset)
+{
+       switch (sset) {
+       case ETH_SS_STATS:
+               return ARRAY_SIZE(cdc_ncm_gstrings_stats);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static void cdc_ncm_get_ethtool_stats(struct net_device *netdev,
+                                   struct ethtool_stats __always_unused *stats,
+                                   u64 *data)
+{
+       struct usbnet *dev = netdev_priv(netdev);
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       int i;
+       char *p = NULL;
+
+       for (i = 0; i < ARRAY_SIZE(cdc_ncm_gstrings_stats); i++) {
+               p = (char *)ctx + cdc_ncm_gstrings_stats[i].stat_offset;
+               data[i] = (cdc_ncm_gstrings_stats[i].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+       }
+}
+
+static void cdc_ncm_get_strings(struct net_device __always_unused *netdev, u32 stringset, u8 *data)
+{
+       u8 *p = data;
+       int i;
+
+       switch (stringset) {
+       case ETH_SS_STATS:
+               for (i = 0; i < ARRAY_SIZE(cdc_ncm_gstrings_stats); i++) {
+                       memcpy(p, cdc_ncm_gstrings_stats[i].stat_string, ETH_GSTRING_LEN);
+                       p += ETH_GSTRING_LEN;
+               }
+       }
+}
+
+static void cdc_ncm_update_rxtx_max(struct usbnet *dev, u32 new_rx, u32 new_tx);
+
+static const struct ethtool_ops cdc_ncm_ethtool_ops = {
+       .get_settings      = usbnet_get_settings,
+       .set_settings      = usbnet_set_settings,
+       .get_link          = usbnet_get_link,
+       .nway_reset        = usbnet_nway_reset,
+       .get_drvinfo       = usbnet_get_drvinfo,
+       .get_msglevel      = usbnet_get_msglevel,
+       .set_msglevel      = usbnet_set_msglevel,
+       .get_ts_info       = ethtool_op_get_ts_info,
+       .get_sset_count    = cdc_ncm_get_sset_count,
+       .get_strings       = cdc_ncm_get_strings,
+       .get_ethtool_stats = cdc_ncm_get_ethtool_stats,
+};
+
+static u32 cdc_ncm_check_rx_max(struct usbnet *dev, u32 new_rx)
+{
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       u32 val, max, min;
+
+       /* clamp new_rx to sane values */
+       min = USB_CDC_NCM_NTB_MIN_IN_SIZE;
+       max = min_t(u32, CDC_NCM_NTB_MAX_SIZE_RX, le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize));
+
+       /* dwNtbInMaxSize spec violation? Use MIN size for both limits */
+       if (max < min) {
+               dev_warn(&dev->intf->dev, "dwNtbInMaxSize=%u is too small. Using %u\n",
+                        le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize), min);
+               max = min;
+       }
+
+       val = clamp_t(u32, new_rx, min, max);
+       if (val != new_rx)
+               dev_dbg(&dev->intf->dev, "rx_max must be in the [%u, %u] range\n", min, max);
+
+       return val;
+}
+
+static u32 cdc_ncm_check_tx_max(struct usbnet *dev, u32 new_tx)
+{
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       u32 val, max, min;
+
+       /* clamp new_tx to sane values */
+       min = ctx->max_datagram_size + ctx->max_ndp_size + sizeof(struct usb_cdc_ncm_nth16);
+       max = min_t(u32, CDC_NCM_NTB_MAX_SIZE_TX, le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize));
+
+       /* some devices set dwNtbOutMaxSize too low for the above default */
+       min = min(min, max);
+
+       val = clamp_t(u32, new_tx, min, max);
+       if (val != new_tx)
+               dev_dbg(&dev->intf->dev, "tx_max must be in the [%u, %u] range\n", min, max);
+
+       return val;
+}
+
+static ssize_t cdc_ncm_show_min_tx_pkt(struct device *d, struct device_attribute *attr, char *buf)
+{
+       struct usbnet *dev = netdev_priv(to_net_dev(d));
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+
+       return sprintf(buf, "%u\n", ctx->min_tx_pkt);
+}
+
+static ssize_t cdc_ncm_show_rx_max(struct device *d, struct device_attribute *attr, char *buf)
+{
+       struct usbnet *dev = netdev_priv(to_net_dev(d));
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+
+       return sprintf(buf, "%u\n", ctx->rx_max);
+}
+
+static ssize_t cdc_ncm_show_tx_max(struct device *d, struct device_attribute *attr, char *buf)
+{
+       struct usbnet *dev = netdev_priv(to_net_dev(d));
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+
+       return sprintf(buf, "%u\n", ctx->tx_max);
+}
+
+static ssize_t cdc_ncm_show_tx_timer_usecs(struct device *d, struct device_attribute *attr, char *buf)
+{
+       struct usbnet *dev = netdev_priv(to_net_dev(d));
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+
+       return sprintf(buf, "%u\n", ctx->timer_interval / (u32)NSEC_PER_USEC);
+}
+
+static ssize_t cdc_ncm_store_min_tx_pkt(struct device *d,  struct device_attribute *attr, const char *buf, size_t len)
+{
+       struct usbnet *dev = netdev_priv(to_net_dev(d));
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       unsigned long val;
+
+       /* no need to restrict values - anything from 0 to infinity is OK */
+       if (kstrtoul(buf, 0, &val))
+               return -EINVAL;
+
+       ctx->min_tx_pkt = val;
+       return len;
+}
+
+static ssize_t cdc_ncm_store_rx_max(struct device *d,  struct device_attribute *attr, const char *buf, size_t len)
+{
+       struct usbnet *dev = netdev_priv(to_net_dev(d));
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       unsigned long val;
+
+       if (kstrtoul(buf, 0, &val) || cdc_ncm_check_rx_max(dev, val) != val)
+               return -EINVAL;
+
+       cdc_ncm_update_rxtx_max(dev, val, ctx->tx_max);
+       return len;
+}
+
+static ssize_t cdc_ncm_store_tx_max(struct device *d,  struct device_attribute *attr, const char *buf, size_t len)
 {
+       struct usbnet *dev = netdev_priv(to_net_dev(d));
        struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       unsigned long val;
+
+       if (kstrtoul(buf, 0, &val) || cdc_ncm_check_tx_max(dev, val) != val)
+               return -EINVAL;
+
+       cdc_ncm_update_rxtx_max(dev, ctx->rx_max, val);
+       return len;
+}
+
+static ssize_t cdc_ncm_store_tx_timer_usecs(struct device *d,  struct device_attribute *attr, const char *buf, size_t len)
+{
+       struct usbnet *dev = netdev_priv(to_net_dev(d));
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       ssize_t ret;
+       unsigned long val;
+
+       ret = kstrtoul(buf, 0, &val);
+       if (ret)
+               return ret;
+       if (val && (val < CDC_NCM_TIMER_INTERVAL_MIN || val > CDC_NCM_TIMER_INTERVAL_MAX))
+               return -EINVAL;
+
+       spin_lock_bh(&ctx->mtx);
+       ctx->timer_interval = val * NSEC_PER_USEC;
+       if (!ctx->timer_interval)
+               ctx->tx_timer_pending = 0;
+       spin_unlock_bh(&ctx->mtx);
+       return len;
+}
+
+static DEVICE_ATTR(min_tx_pkt, S_IRUGO | S_IWUSR, cdc_ncm_show_min_tx_pkt, cdc_ncm_store_min_tx_pkt);
+static DEVICE_ATTR(rx_max, S_IRUGO | S_IWUSR, cdc_ncm_show_rx_max, cdc_ncm_store_rx_max);
+static DEVICE_ATTR(tx_max, S_IRUGO | S_IWUSR, cdc_ncm_show_tx_max, cdc_ncm_store_tx_max);
+static DEVICE_ATTR(tx_timer_usecs, S_IRUGO | S_IWUSR, cdc_ncm_show_tx_timer_usecs, cdc_ncm_store_tx_timer_usecs);
+
+#define NCM_PARM_ATTR(name, format, tocpu)                             \
+static ssize_t cdc_ncm_show_##name(struct device *d, struct device_attribute *attr, char *buf) \
+{ \
+       struct usbnet *dev = netdev_priv(to_net_dev(d)); \
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; \
+       return sprintf(buf, format "\n", tocpu(ctx->ncm_parm.name));    \
+} \
+static DEVICE_ATTR(name, S_IRUGO, cdc_ncm_show_##name, NULL)
+
+NCM_PARM_ATTR(bmNtbFormatsSupported, "0x%04x", le16_to_cpu);
+NCM_PARM_ATTR(dwNtbInMaxSize, "%u", le32_to_cpu);
+NCM_PARM_ATTR(wNdpInDivisor, "%u", le16_to_cpu);
+NCM_PARM_ATTR(wNdpInPayloadRemainder, "%u", le16_to_cpu);
+NCM_PARM_ATTR(wNdpInAlignment, "%u", le16_to_cpu);
+NCM_PARM_ATTR(dwNtbOutMaxSize, "%u", le32_to_cpu);
+NCM_PARM_ATTR(wNdpOutDivisor, "%u", le16_to_cpu);
+NCM_PARM_ATTR(wNdpOutPayloadRemainder, "%u", le16_to_cpu);
+NCM_PARM_ATTR(wNdpOutAlignment, "%u", le16_to_cpu);
+NCM_PARM_ATTR(wNtbOutMaxDatagrams, "%u", le16_to_cpu);
+
+static struct attribute *cdc_ncm_sysfs_attrs[] = {
+       &dev_attr_min_tx_pkt.attr,
+       &dev_attr_rx_max.attr,
+       &dev_attr_tx_max.attr,
+       &dev_attr_tx_timer_usecs.attr,
+       &dev_attr_bmNtbFormatsSupported.attr,
+       &dev_attr_dwNtbInMaxSize.attr,
+       &dev_attr_wNdpInDivisor.attr,
+       &dev_attr_wNdpInPayloadRemainder.attr,
+       &dev_attr_wNdpInAlignment.attr,
+       &dev_attr_dwNtbOutMaxSize.attr,
+       &dev_attr_wNdpOutDivisor.attr,
+       &dev_attr_wNdpOutPayloadRemainder.attr,
+       &dev_attr_wNdpOutAlignment.attr,
+       &dev_attr_wNtbOutMaxDatagrams.attr,
+       NULL,
+};
+
+static struct attribute_group cdc_ncm_sysfs_attr_group = {
+       .name = "cdc_ncm",
+       .attrs = cdc_ncm_sysfs_attrs,
+};
+
+/* handle rx_max and tx_max changes */
+static void cdc_ncm_update_rxtx_max(struct usbnet *dev, u32 new_rx, u32 new_tx)
+{
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       u8 iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;
        u32 val;
-       u8 flags;
-       u8 iface_no;
-       int err;
-       int eth_hlen;
-       u16 mbim_mtu;
-       u16 ntb_fmt_supported;
-       __le16 max_datagram_size;
 
-       iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;
+       val = cdc_ncm_check_rx_max(dev, new_rx);
+
+       /* inform device about NTB input size changes */
+       if (val != ctx->rx_max) {
+               __le32 dwNtbInMaxSize = cpu_to_le32(val);
+
+               dev_info(&dev->intf->dev, "setting rx_max = %u\n", val);
+
+               /* tell device to use new size */
+               if (usbnet_write_cmd(dev, USB_CDC_SET_NTB_INPUT_SIZE,
+                                    USB_TYPE_CLASS | USB_DIR_OUT
+                                    | USB_RECIP_INTERFACE,
+                                    0, iface_no, &dwNtbInMaxSize, 4) < 0)
+                       dev_dbg(&dev->intf->dev, "Setting NTB Input Size failed\n");
+               else
+                       ctx->rx_max = val;
+       }
+
+       /* usbnet use these values for sizing rx queues */
+       if (dev->rx_urb_size != ctx->rx_max) {
+               dev->rx_urb_size = ctx->rx_max;
+               if (netif_running(dev->net))
+                       usbnet_unlink_rx_urbs(dev);
+       }
+
+       val = cdc_ncm_check_tx_max(dev, new_tx);
+       if (val != ctx->tx_max)
+               dev_info(&dev->intf->dev, "setting tx_max = %u\n", val);
+
+       /* Adding a pad byte here if necessary simplifies the handling
+        * in cdc_ncm_fill_tx_frame, making tx_max always represent
+        * the real skb max size.
+        *
+        * We cannot use dev->maxpacket here because this is called from
+        * .bind which is called before usbnet sets up dev->maxpacket
+        */
+       if (val != le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize) &&
+           val % usb_maxpacket(dev->udev, dev->out, 1) == 0)
+               val++;
+
+       /* we might need to flush any pending tx buffers if running */
+       if (netif_running(dev->net) && val > ctx->tx_max) {
+               netif_tx_lock_bh(dev->net);
+               usbnet_start_xmit(NULL, dev->net);
+               /* make sure tx_curr_skb is reallocated if it was empty */
+               if (ctx->tx_curr_skb) {
+                       dev_kfree_skb_any(ctx->tx_curr_skb);
+                       ctx->tx_curr_skb = NULL;
+               }
+               ctx->tx_max = val;
+               netif_tx_unlock_bh(dev->net);
+       } else {
+               ctx->tx_max = val;
+       }
+
+       dev->hard_mtu = ctx->tx_max;
+
+       /* max qlen depend on hard_mtu and rx_urb_size */
+       usbnet_update_max_qlen(dev);
+
+       /* never pad more than 3 full USB packets per transfer */
+       ctx->min_tx_pkt = clamp_t(u16, ctx->tx_max - 3 * usb_maxpacket(dev->udev, dev->out, 1),
+                                 CDC_NCM_MIN_TX_PKT, ctx->tx_max);
+}
+
+/* helpers for NCM and MBIM differences */
+static u8 cdc_ncm_flags(struct usbnet *dev)
+{
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+
+       if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting) && ctx->mbim_desc)
+               return ctx->mbim_desc->bmNetworkCapabilities;
+       if (ctx->func_desc)
+               return ctx->func_desc->bmNetworkCapabilities;
+       return 0;
+}
+
+static int cdc_ncm_eth_hlen(struct usbnet *dev)
+{
+       if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting))
+               return 0;
+       return ETH_HLEN;
+}
+
+static u32 cdc_ncm_min_dgram_size(struct usbnet *dev)
+{
+       if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting))
+               return CDC_MBIM_MIN_DATAGRAM_SIZE;
+       return CDC_NCM_MIN_DATAGRAM_SIZE;
+}
+
+static u32 cdc_ncm_max_dgram_size(struct usbnet *dev)
+{
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+
+       if (cdc_ncm_comm_intf_is_mbim(dev->intf->cur_altsetting) && ctx->mbim_desc)
+               return le16_to_cpu(ctx->mbim_desc->wMaxSegmentSize);
+       if (ctx->ether_desc)
+               return le16_to_cpu(ctx->ether_desc->wMaxSegmentSize);
+       return CDC_NCM_MAX_DATAGRAM_SIZE;
+}
+
+/* initial one-time device setup.  MUST be called with the data interface
+ * in altsetting 0
+ */
+static int cdc_ncm_init(struct usbnet *dev)
+{
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       u8 iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;
+       int err;
 
        err = usbnet_read_cmd(dev, USB_CDC_GET_NTB_PARAMETERS,
                              USB_TYPE_CLASS | USB_DIR_IN
@@ -89,7 +454,36 @@ static int cdc_ncm_setup(struct usbnet *dev)
                return err; /* GET_NTB_PARAMETERS is required */
        }
 
-       /* read correct set of parameters according to device mode */
+       /* set CRC Mode */
+       if (cdc_ncm_flags(dev) & USB_CDC_NCM_NCAP_CRC_MODE) {
+               dev_dbg(&dev->intf->dev, "Setting CRC mode off\n");
+               err = usbnet_write_cmd(dev, USB_CDC_SET_CRC_MODE,
+                                      USB_TYPE_CLASS | USB_DIR_OUT
+                                      | USB_RECIP_INTERFACE,
+                                      USB_CDC_NCM_CRC_NOT_APPENDED,
+                                      iface_no, NULL, 0);
+               if (err < 0)
+                       dev_err(&dev->intf->dev, "SET_CRC_MODE failed\n");
+       }
+
+       /* set NTB format, if both formats are supported.
+        *
+        * "The host shall only send this command while the NCM Data
+        *  Interface is in alternate setting 0."
+        */
+       if (le16_to_cpu(ctx->ncm_parm.bmNtbFormatsSupported) &
+                                               USB_CDC_NCM_NTB32_SUPPORTED) {
+               dev_dbg(&dev->intf->dev, "Setting NTB format to 16-bit\n");
+               err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT,
+                                      USB_TYPE_CLASS | USB_DIR_OUT
+                                      | USB_RECIP_INTERFACE,
+                                      USB_CDC_NCM_NTB16_FORMAT,
+                                      iface_no, NULL, 0);
+               if (err < 0)
+                       dev_err(&dev->intf->dev, "SET_NTB_FORMAT failed\n");
+       }
+
+       /* set initial device values */
        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);
@@ -97,72 +491,79 @@ static int cdc_ncm_setup(struct usbnet *dev)
        ctx->tx_ndp_modulus = le16_to_cpu(ctx->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);
-
-       /* 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;
-               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 {
-               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;
-       }
-
-       /* 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);
+               ctx->tx_ndp_modulus, ctx->tx_max_datagrams, cdc_ncm_flags(dev));
 
        /* max count of tx datagrams */
        if ((ctx->tx_max_datagrams == 0) ||
                        (ctx->tx_max_datagrams > CDC_NCM_DPT_DATAGRAMS_MAX))
                ctx->tx_max_datagrams = CDC_NCM_DPT_DATAGRAMS_MAX;
 
-       /* verify maximum size of received NTB in bytes */
-       if (ctx->rx_max < 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;
-       }
+       /* set up maximum NDP size */
+       ctx->max_ndp_size = sizeof(struct usb_cdc_ncm_ndp16) + (ctx->tx_max_datagrams + 1) * sizeof(struct usb_cdc_ncm_dpe16);
 
-       if (ctx->rx_max > 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;
-       }
+       /* initial coalescing timer interval */
+       ctx->timer_interval = CDC_NCM_TIMER_INTERVAL_USEC * NSEC_PER_USEC;
 
-       /* inform device about NTB input size changes */
-       if (ctx->rx_max != le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize)) {
-               __le32 dwNtbInMaxSize = cpu_to_le32(ctx->rx_max);
+       return 0;
+}
 
-               err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_INPUT_SIZE,
-                                      USB_TYPE_CLASS | USB_DIR_OUT
-                                      | USB_RECIP_INTERFACE,
-                                      0, iface_no, &dwNtbInMaxSize, 4);
-               if (err < 0)
-                       dev_dbg(&dev->intf->dev, "Setting NTB Input Size failed\n");
+/* set a new max datagram size */
+static void cdc_ncm_set_dgram_size(struct usbnet *dev, int new_size)
+{
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       u8 iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;
+       __le16 max_datagram_size;
+       u16 mbim_mtu;
+       int err;
+
+       /* set default based on descriptors */
+       ctx->max_datagram_size = clamp_t(u32, new_size,
+                                        cdc_ncm_min_dgram_size(dev),
+                                        CDC_NCM_MAX_DATAGRAM_SIZE);
+
+       /* inform the device about the selected Max Datagram Size? */
+       if (!(cdc_ncm_flags(dev) & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE))
+               goto out;
+
+       /* 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;
        }
 
-       /* verify maximum size of transmitted NTB in bytes */
-       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;
+       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 */
+       dev->net->mtu = min_t(int, dev->net->mtu, ctx->max_datagram_size - cdc_ncm_eth_hlen(dev));
+
+       /* do not exceed operater preferred MTU */
+       if (ctx->mbim_extended_desc) {
+               mbim_mtu = le16_to_cpu(ctx->mbim_extended_desc->wMTU);
+               if (mbim_mtu != 0 && mbim_mtu < dev->net->mtu)
+                       dev->net->mtu = mbim_mtu;
        }
+}
+
+static void cdc_ncm_fix_modulus(struct usbnet *dev)
+{
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       u32 val;
 
        /*
         * verify that the structure alignment is:
@@ -199,68 +600,31 @@ static int cdc_ncm_setup(struct usbnet *dev)
        }
 
        /* adjust TX-remainder according to NCM specification. */
-       ctx->tx_remainder = ((ctx->tx_remainder - eth_hlen) &
+       ctx->tx_remainder = ((ctx->tx_remainder - cdc_ncm_eth_hlen(dev)) &
                             (ctx->tx_modulus - 1));
+}
 
-       /* additional configuration */
-
-       /* set CRC Mode */
-       if (flags & USB_CDC_NCM_NCAP_CRC_MODE) {
-               err = usbnet_write_cmd(dev, USB_CDC_SET_CRC_MODE,
-                                      USB_TYPE_CLASS | USB_DIR_OUT
-                                      | USB_RECIP_INTERFACE,
-                                      USB_CDC_NCM_CRC_NOT_APPENDED,
-                                      iface_no, NULL, 0);
-               if (err < 0)
-                       dev_dbg(&dev->intf->dev, "Setting CRC mode off failed\n");
-       }
-
-       /* set NTB format, if both formats are supported */
-       if (ntb_fmt_supported & USB_CDC_NCM_NTH32_SIGN) {
-               err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT,
-                                      USB_TYPE_CLASS | USB_DIR_OUT
-                                      | USB_RECIP_INTERFACE,
-                                      USB_CDC_NCM_NTB16_FORMAT,
-                                      iface_no, NULL, 0);
-               if (err < 0)
-                       dev_dbg(&dev->intf->dev, "Setting NTB format to 16-bit failed\n");
-       }
-
-       /* inform the device about the selected Max Datagram Size */
-       if (!(flags & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE))
-               goto out;
-
-       /* 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;
-       }
-
-       if (le16_to_cpu(max_datagram_size) == ctx->max_datagram_size)
-               goto out;
+static int cdc_ncm_setup(struct usbnet *dev)
+{
+       struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+       u32 def_rx, def_tx;
 
-       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");
+       /* be conservative when selecting intial buffer size to
+        * increase the number of hosts this will work for
+        */
+       def_rx = min_t(u32, CDC_NCM_NTB_DEF_SIZE_RX,
+                      le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize));
+       def_tx = min_t(u32, CDC_NCM_NTB_DEF_SIZE_TX,
+                      le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize));
 
-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;
+       /* clamp rx_max and tx_max and inform device */
+       cdc_ncm_update_rxtx_max(dev, def_rx, def_tx);
 
-       /* do not exceed operater preferred MTU */
-       if (ctx->mbim_extended_desc) {
-               mbim_mtu = le16_to_cpu(ctx->mbim_extended_desc->wMTU);
-               if (mbim_mtu != 0 && mbim_mtu < dev->net->mtu)
-                       dev->net->mtu = mbim_mtu;
-       }
+       /* sanitize the modulus and remainder values */
+       cdc_ncm_fix_modulus(dev);
 
+       /* set max datagram size */
+       cdc_ncm_set_dgram_size(dev, cdc_ncm_max_dgram_size(dev));
        return 0;
 }
 
@@ -424,10 +788,21 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_
        }
 
        /* check if we got everything */
-       if (!ctx->data || (!ctx->mbim_desc && !ctx->ether_desc)) {
-               dev_dbg(&intf->dev, "CDC descriptors missing\n");
+       if (!ctx->data) {
+               dev_dbg(&intf->dev, "CDC Union missing and no IAD found\n");
                goto error;
        }
+       if (cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) {
+               if (!ctx->mbim_desc) {
+                       dev_dbg(&intf->dev, "MBIM functional descriptor missing\n");
+                       goto error;
+               }
+       } else {
+               if (!ctx->ether_desc || !ctx->func_desc) {
+                       dev_dbg(&intf->dev, "NCM or ECM functional descriptors missing\n");
+                       goto error;
+               }
+       }
 
        /* claim data interface, if different from control */
        if (ctx->data != ctx->control) {
@@ -447,8 +822,8 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_
                goto error2;
        }
 
-       /* initialize data interface */
-       if (cdc_ncm_setup(dev))
+       /* initialize basic device settings */
+       if (cdc_ncm_init(dev))
                goto error2;
 
        /* configure data interface */
@@ -477,18 +852,14 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_
                dev_info(&intf->dev, "MAC-Address: %pM\n", dev->net->dev_addr);
        }
 
-       /* usbnet use these values for sizing tx/rx queues */
-       dev->hard_mtu = ctx->tx_max;
-       dev->rx_urb_size = ctx->rx_max;
+       /* finish setting up the device specific data */
+       cdc_ncm_setup(dev);
 
-       /* cdc_ncm_setup will override dwNtbOutMaxSize if it is
-        * outside the sane range. Adding a pad byte here if necessary
-        * simplifies the handling in cdc_ncm_fill_tx_frame, making
-        * tx_max always represent the real skb max size.
-        */
-       if (ctx->tx_max != le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize) &&
-           ctx->tx_max % usb_maxpacket(dev->udev, dev->out, 1) == 0)
-               ctx->tx_max++;
+       /* override ethtool_ops */
+       dev->net->ethtool_ops = &cdc_ncm_ethtool_ops;
+
+       /* add our sysfs attrs */
+       dev->net->sysfs_groups[0] = &cdc_ncm_sysfs_attr_group;
 
        return 0;
 
@@ -541,10 +912,10 @@ void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf)
 }
 EXPORT_SYMBOL_GPL(cdc_ncm_unbind);
 
-/* Select the MBIM altsetting iff it is preferred and available,
- * returning the number of the corresponding data interface altsetting
+/* Return the number of the MBIM control interface altsetting iff it
+ * is preferred and available,
  */
-u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf)
+u8 cdc_ncm_select_altsetting(struct usb_interface *intf)
 {
        struct usb_host_interface *alt;
 
@@ -563,15 +934,15 @@ u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf)
         *   the rules given in section 6 (USB Device Model) of this
         *   specification."
         */
-       if (prefer_mbim && intf->num_altsetting == 2) {
+       if (intf->num_altsetting < 2)
+               return intf->cur_altsetting->desc.bAlternateSetting;
+
+       if (prefer_mbim) {
                alt = usb_altnum_to_altsetting(intf, CDC_NCM_COMM_ALTSETTING_MBIM);
-               if (alt && cdc_ncm_comm_intf_is_mbim(alt) &&
-                   !usb_set_interface(dev->udev,
-                                      intf->cur_altsetting->desc.bInterfaceNumber,
-                                      CDC_NCM_COMM_ALTSETTING_MBIM))
-                       return CDC_NCM_DATA_ALTSETTING_MBIM;
+               if (alt && cdc_ncm_comm_intf_is_mbim(alt))
+                       return CDC_NCM_COMM_ALTSETTING_MBIM;
        }
-       return CDC_NCM_DATA_ALTSETTING_NCM;
+       return CDC_NCM_COMM_ALTSETTING_NCM;
 }
 EXPORT_SYMBOL_GPL(cdc_ncm_select_altsetting);
 
@@ -580,12 +951,11 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)
        int ret;
 
        /* MBIM backwards compatible function? */
-       cdc_ncm_select_altsetting(dev, intf);
-       if (cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting))
+       if (cdc_ncm_select_altsetting(intf) != CDC_NCM_COMM_ALTSETTING_NCM)
                return -ENODEV;
 
-       /* NCM data altsetting is always 1 */
-       ret = cdc_ncm_bind_common(dev, intf, 1);
+       /* The NCM data altsetting is fixed */
+       ret = cdc_ncm_bind_common(dev, intf, CDC_NCM_DATA_ALTSETTING_NCM);
 
        /*
         * We should get an event when network connection is "connected" or
@@ -628,7 +998,7 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_
        cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_max);
 
        /* verify that there is room for the NDP and the datagram (reserve) */
-       if ((ctx->tx_max - skb->len - reserve) < CDC_NCM_NDP_SIZE)
+       if ((ctx->tx_max - skb->len - reserve) < ctx->max_ndp_size)
                return NULL;
 
        /* link to it */
@@ -638,7 +1008,7 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_
                nth16->wNdpIndex = cpu_to_le16(skb->len);
 
        /* push a new empty NDP */
-       ndp16 = (struct usb_cdc_ncm_ndp16 *)memset(skb_put(skb, CDC_NCM_NDP_SIZE), 0, CDC_NCM_NDP_SIZE);
+       ndp16 = (struct usb_cdc_ncm_ndp16 *)memset(skb_put(skb, ctx->max_ndp_size), 0, ctx->max_ndp_size);
        ndp16->dwSignature = sign;
        ndp16->wLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_ndp16) + sizeof(struct usb_cdc_ncm_dpe16));
        return ndp16;
@@ -683,6 +1053,9 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
 
                /* count total number of frames in this NTB */
                ctx->tx_curr_frame_num = 0;
+
+               /* recent payload counter for this skb_out */
+               ctx->tx_curr_frame_payload = 0;
        }
 
        for (n = ctx->tx_curr_frame_num; n < ctx->tx_max_datagrams; n++) {
@@ -720,6 +1093,7 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
                                ctx->tx_rem_sign = sign;
                                skb = NULL;
                                ready2send = 1;
+                               ctx->tx_reason_ntb_full++;      /* count reason for transmitting */
                        }
                        break;
                }
@@ -733,12 +1107,14 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
                ndp16->dpe16[index].wDatagramIndex = cpu_to_le16(skb_out->len);
                ndp16->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe16));
                memcpy(skb_put(skb_out, skb->len), skb->data, skb->len);
+               ctx->tx_curr_frame_payload += skb->len; /* count real tx payload data */
                dev_kfree_skb_any(skb);
                skb = NULL;
 
                /* send now if this NDP is full */
                if (index >= CDC_NCM_DPT_DATAGRAMS_MAX) {
                        ready2send = 1;
+                       ctx->tx_reason_ndp_full++;      /* count reason for transmitting */
                        break;
                }
        }
@@ -758,7 +1134,7 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
                ctx->tx_curr_skb = skb_out;
                goto exit_no_skb;
 
-       } else if ((n < ctx->tx_max_datagrams) && (ready2send == 0)) {
+       } else if ((n < ctx->tx_max_datagrams) && (ready2send == 0) && (ctx->timer_interval > 0)) {
                /* wait for more frames */
                /* push variables */
                ctx->tx_curr_skb = skb_out;
@@ -768,11 +1144,13 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
                goto exit_no_skb;
 
        } else {
+               if (n == ctx->tx_max_datagrams)
+                       ctx->tx_reason_max_datagram++;  /* count reason for transmitting */
                /* frame goes out */
                /* variables will be reset at next call */
        }
 
-       /* If collected data size is less or equal CDC_NCM_MIN_TX_PKT
+       /* If collected data size is less or equal ctx->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
@@ -782,7 +1160,7 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
         * a ZLP after full sized NTBs.
         */
        if (!(dev->driver_info->flags & FLAG_SEND_ZLP) &&
-           skb_out->len > CDC_NCM_MIN_TX_PKT)
+           skb_out->len > ctx->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 < ctx->tx_max && (skb_out->len % dev->maxpacket) == 0)
@@ -795,11 +1173,22 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
        /* return skb */
        ctx->tx_curr_skb = NULL;
        dev->net->stats.tx_packets += ctx->tx_curr_frame_num;
+
+       /* keep private stats: framing overhead and number of NTBs */
+       ctx->tx_overhead += skb_out->len - ctx->tx_curr_frame_payload;
+       ctx->tx_ntbs++;
+
+       /* usbnet has already counted all the framing overhead.
+        * Adjust the stats so that the tx_bytes counter show real
+        * payload data instead.
+        */
+       dev->net->stats.tx_bytes -= skb_out->len - ctx->tx_curr_frame_payload;
+
        return skb_out;
 
 exit_no_skb:
-       /* Start timer, if there is a remaining skb */
-       if (ctx->tx_curr_skb != NULL)
+       /* Start timer, if there is a remaining non-empty skb */
+       if (ctx->tx_curr_skb != NULL && n > 0)
                cdc_ncm_tx_timeout_start(ctx);
        return NULL;
 }
@@ -810,7 +1199,7 @@ static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx)
        /* start timer, if not already started */
        if (!(hrtimer_active(&ctx->tx_timer) || atomic_read(&ctx->stop)))
                hrtimer_start(&ctx->tx_timer,
-                               ktime_set(0, CDC_NCM_TIMER_INTERVAL),
+                               ktime_set(0, ctx->timer_interval),
                                HRTIMER_MODE_REL);
 }
 
@@ -835,6 +1224,7 @@ static void cdc_ncm_txpath_bh(unsigned long param)
                cdc_ncm_tx_timeout_start(ctx);
                spin_unlock_bh(&ctx->mtx);
        } else if (dev->net != NULL) {
+               ctx->tx_reason_timeout++;       /* count reason for transmitting */
                spin_unlock_bh(&ctx->mtx);
                netif_tx_lock_bh(dev->net);
                usbnet_start_xmit(NULL, dev->net);
@@ -970,6 +1360,7 @@ int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
        struct usb_cdc_ncm_dpe16 *dpe16;
        int ndpoffset;
        int loopcount = 50; /* arbitrary max preventing infinite loop */
+       u32 payload = 0;
 
        ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in);
        if (ndpoffset < 0)
@@ -1015,13 +1406,13 @@ int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
                        break;
 
                } else {
-                       skb = skb_clone(skb_in, GFP_ATOMIC);
+                       /* create a fresh copy to reduce truesize */
+                       skb = netdev_alloc_skb_ip_align(dev->net,  len);
                        if (!skb)
                                goto error;
-                       skb->len = len;
-                       skb->data = ((u8 *)skb_in->data) + offset;
-                       skb_set_tail_pointer(skb, len);
+                       memcpy(skb_put(skb, len), skb_in->data + offset, len);
                        usbnet_skb_return(dev, skb);
+                       payload += len; /* count payload bytes in this NTB */
                }
        }
 err_ndp:
@@ -1030,6 +1421,10 @@ int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
        if (ndpoffset && loopcount--)
                goto next_ndp;
 
+       /* update stats */
+       ctx->rx_overhead += skb_in->len - payload;
+       ctx->rx_ntbs++;
+
        return 1;
 error:
        return 0;
@@ -1049,14 +1444,14 @@ cdc_ncm_speed_change(struct usbnet *dev,
         */
        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));
+                          "%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));
+                          "%u kbit/s downlink %u kbit/s uplink\n",
+                          (unsigned int)(rx_speed / 1000U),
+                          (unsigned int)(tx_speed / 1000U));
        }
 }
 
@@ -1086,11 +1481,10 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)
                 * USB_CDC_NOTIFY_NETWORK_CONNECTION notification shall be
                 * sent by device after USB_CDC_NOTIFY_SPEED_CHANGE.
                 */
-               ctx->connected = le16_to_cpu(event->wValue);
                netif_info(dev, link, dev->net,
                           "network connection: %sconnected\n",
-                          ctx->connected ? "" : "dis");
-               usbnet_link_change(dev, ctx->connected, 0);
+                          !!event->wValue ? "" : "dis");
+               usbnet_link_change(dev, !!event->wValue, 0);
                break;
 
        case USB_CDC_NOTIFY_SPEED_CHANGE:
@@ -1110,23 +1504,11 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)
        }
 }
 
-static int cdc_ncm_check_connect(struct usbnet *dev)
-{
-       struct cdc_ncm_ctx *ctx;
-
-       ctx = (struct cdc_ncm_ctx *)dev->data[0];
-       if (ctx == NULL)
-               return 1;       /* disconnected */
-
-       return !ctx->connected;
-}
-
 static const struct driver_info cdc_ncm_info = {
        .description = "CDC NCM",
        .flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET,
        .bind = cdc_ncm_bind,
        .unbind = cdc_ncm_unbind,
-       .check_connect = cdc_ncm_check_connect,
        .manage_power = usbnet_manage_power,
        .status = cdc_ncm_status,
        .rx_fixup = cdc_ncm_rx_fixup,
@@ -1140,7 +1522,6 @@ static const struct driver_info wwan_info = {
                        | FLAG_WWAN,
        .bind = cdc_ncm_bind,
        .unbind = cdc_ncm_unbind,
-       .check_connect = cdc_ncm_check_connect,
        .manage_power = usbnet_manage_power,
        .status = cdc_ncm_status,
        .rx_fixup = cdc_ncm_rx_fixup,
@@ -1154,7 +1535,6 @@ static const struct driver_info wwan_noarp_info = {
                        | FLAG_WWAN | FLAG_NOARP,
        .bind = cdc_ncm_bind,
        .unbind = cdc_ncm_unbind,
-       .check_connect = cdc_ncm_check_connect,
        .manage_power = usbnet_manage_power,
        .status = cdc_ncm_status,
        .rx_fixup = cdc_ncm_rx_fixup,
index 660bd5ea9fc0b311918812af8d3959c830b96cf4..a3a05869309df6a1ac34cdb00c6ff4d031dc921b 100644 (file)
@@ -2425,7 +2425,7 @@ static void hso_net_init(struct net_device *net)
        net->type = ARPHRD_NONE;
        net->mtu = DEFAULT_MTU - 14;
        net->tx_queue_len = 10;
-       SET_ETHTOOL_OPS(net, &ops);
+       net->ethtool_ops = &ops;
 
        /* and initialize the semaphore */
        spin_lock_init(&hso_net->net_lock);
index 312178d7b698e06a6bc97ede9c7c1eb49510c322..f9822bc75425a9bacc2dcd8915344c6373778a2c 100644 (file)
@@ -172,24 +172,11 @@ static int huawei_cdc_ncm_resume(struct usb_interface *intf)
        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,
index 973275fef25047402f31f105af87311b4637a532..76465b117b72aaf0c2fdd36f3d78b15eed204b0e 100644 (file)
@@ -534,7 +534,7 @@ static int ipheth_probe(struct usb_interface *intf,
        usb_set_intfdata(intf, dev);
 
        SET_NETDEV_DEV(netdev, &intf->dev);
-       SET_ETHTOOL_OPS(netdev, &ops);
+       netdev->ethtool_ops = &ops;
 
        retval = register_netdev(netdev);
        if (retval) {
index a359d3bb7c5b125422cf59dea69c8a905099fed7..dcb6d33141e0640f545555848434d8efd7822878 100644 (file)
@@ -1171,7 +1171,7 @@ static int kaweth_probe(
        netdev->netdev_ops = &kaweth_netdev_ops;
        netdev->watchdog_timeo = KAWETH_TX_TIMEOUT;
        netdev->mtu = le16_to_cpu(kaweth->configuration.segment_size);
-       SET_ETHTOOL_OPS(netdev, &ops);
+       netdev->ethtool_ops = &ops;
 
        /* kaweth is zeroed as part of alloc_netdev */
        INIT_DELAYED_WORK(&kaweth->lowmem_work, kaweth_resubmit_tl);
index 03e8a15d7deb74d328d5e563ed2077f62c8d9687..f840802159158b56a5b840d7abb83152fcd0c83f 100644 (file)
@@ -1159,7 +1159,7 @@ static int pegasus_probe(struct usb_interface *intf,
 
        net->watchdog_timeo = PEGASUS_TX_TIMEOUT;
        net->netdev_ops = &pegasus_netdev_ops;
-       SET_ETHTOOL_OPS(net, &ops);
+       net->ethtool_ops = &ops;
        pegasus->mii.dev = net;
        pegasus->mii.mdio_read = mdio_read;
        pegasus->mii.mdio_write = mdio_write;
index dc4bf06948c7224eaecd0b57742096a8440edfe6..cf62d7e8329f11858859257c170b605c6b9a0940 100644 (file)
@@ -763,7 +763,12 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x2357, 0x9000, 4)},    /* TP-LINK MA260 */
        {QMI_FIXED_INTF(0x1bc7, 0x1200, 5)},    /* Telit LE920 */
        {QMI_FIXED_INTF(0x1bc7, 0x1201, 2)},    /* Telit LE920 */
-       {QMI_FIXED_INTF(0x0b3c, 0xc005, 6)},    /* Olivetti Olicard 200 */
+       {QMI_FIXED_INTF(0x0b3c, 0xc000, 4)},    /* Olivetti Olicard 100 */
+       {QMI_FIXED_INTF(0x0b3c, 0xc001, 4)},    /* Olivetti Olicard 120 */
+       {QMI_FIXED_INTF(0x0b3c, 0xc002, 4)},    /* Olivetti Olicard 140 */
+       {QMI_FIXED_INTF(0x0b3c, 0xc004, 6)},    /* Olivetti Olicard 155 */
+       {QMI_FIXED_INTF(0x0b3c, 0xc005, 6)},    /* Olivetti Olicard 200 */
+       {QMI_FIXED_INTF(0x0b3c, 0xc00a, 6)},    /* Olivetti Olicard 160 */
        {QMI_FIXED_INTF(0x0b3c, 0xc00b, 4)},    /* Olivetti Olicard 500 */
        {QMI_FIXED_INTF(0x1e2d, 0x0060, 4)},    /* Cinterion PLxx */
        {QMI_FIXED_INTF(0x1e2d, 0x0053, 4)},    /* Cinterion PHxx,PXxx */
index 3fbfb0869030aeeb6c540766bdbf924d959abcd6..25431965a625a63d99fd3fa6d3167183ea45606a 100644 (file)
@@ -630,12 +630,10 @@ int set_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data)
        int ret;
        void *tmp;
 
-       tmp = kmalloc(size, GFP_KERNEL);
+       tmp = kmemdup(data, size, GFP_KERNEL);
        if (!tmp)
                return -ENOMEM;
 
-       memcpy(tmp, data, size);
-
        ret = usb_control_msg(tp->udev, usb_sndctrlpipe(tp->udev, 0),
                               RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE,
                               value, index, tmp, size, 500);
@@ -3452,7 +3450,7 @@ static int rtl8152_probe(struct usb_interface *intf,
                              NETIF_F_TSO | NETIF_F_FRAGLIST |
                              NETIF_F_IPV6_CSUM | NETIF_F_TSO6;
 
-       SET_ETHTOOL_OPS(netdev, &ops);
+       netdev->ethtool_ops = &ops;
        netif_set_gso_max_size(netdev, RTL_LIMITED_TSO_SIZE);
 
        tp->mii.dev = netdev;
index da2c4583bd2d9d4894ea64ae566a028e9da96b2b..6e87e5710048cf952c1505950d954a493b6b8878 100644 (file)
@@ -878,7 +878,7 @@ static int rtl8150_probe(struct usb_interface *intf,
        dev->netdev = netdev;
        netdev->netdev_ops = &rtl8150_netdev_ops;
        netdev->watchdog_timeo = RTL8150_TX_TIMEOUT;
-       SET_ETHTOOL_OPS(netdev, &ops);
+       netdev->ethtool_ops = &ops;
        dev->intr_interval = 100;       /* 100ms */
 
        if (!alloc_all_urbs(dev)) {
index 8a852b5f215f1c5b3b42c3bc93cf6655a1178b68..7d9f84a91f37dd96fbd835d170dc8ed24c281b6c 100644 (file)
@@ -1646,7 +1646,7 @@ static int virtnet_probe(struct virtio_device *vdev)
        dev->netdev_ops = &virtnet_netdev;
        dev->features = NETIF_F_HIGHDMA;
 
-       SET_ETHTOOL_OPS(dev, &virtnet_ethtool_ops);
+       dev->ethtool_ops = &virtnet_ethtool_ops;
        SET_NETDEV_DEV(dev, &vdev->dev);
 
        /* Do we support "hardware" checksums? */
@@ -1724,6 +1724,13 @@ static int virtnet_probe(struct virtio_device *vdev)
        if (virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ))
                vi->has_cvq = true;
 
+       if (vi->any_header_sg) {
+               if (vi->mergeable_rx_bufs)
+                       dev->needed_headroom = sizeof(struct virtio_net_hdr_mrg_rxbuf);
+               else
+                       dev->needed_headroom = sizeof(struct virtio_net_hdr);
+       }
+
        /* Use single tx/rx queue pair as default */
        vi->curr_queue_pairs = 1;
        vi->max_queue_pairs = max_queue_pairs;
index 600ab56c0008bab49251505e38ca911b611da2c1..40c1c7b0d9e02adb9b25b6e16a15ad8e4fac3a7c 100644 (file)
@@ -431,8 +431,8 @@ vmxnet3_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
                ethtool_cmd_speed_set(ecmd, adapter->link_speed);
                ecmd->duplex = DUPLEX_FULL;
        } else {
-               ethtool_cmd_speed_set(ecmd, -1);
-               ecmd->duplex = -1;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
        }
        return 0;
 }
@@ -579,7 +579,7 @@ vmxnet3_get_rss_indir_size(struct net_device *netdev)
 }
 
 static int
-vmxnet3_get_rss_indir(struct net_device *netdev, u32 *p)
+vmxnet3_get_rss(struct net_device *netdev, u32 *p, u8 *key)
 {
        struct vmxnet3_adapter *adapter = netdev_priv(netdev);
        struct UPT1_RSSConf *rssConf = adapter->rss_conf;
@@ -592,7 +592,7 @@ vmxnet3_get_rss_indir(struct net_device *netdev, u32 *p)
 }
 
 static int
-vmxnet3_set_rss_indir(struct net_device *netdev, const u32 *p)
+vmxnet3_set_rss(struct net_device *netdev, const u32 *p, const u8 *key)
 {
        unsigned int i;
        unsigned long flags;
@@ -628,12 +628,12 @@ static const struct ethtool_ops vmxnet3_ethtool_ops = {
        .get_rxnfc         = vmxnet3_get_rxnfc,
 #ifdef VMXNET3_RSS
        .get_rxfh_indir_size = vmxnet3_get_rss_indir_size,
-       .get_rxfh_indir    = vmxnet3_get_rss_indir,
-       .set_rxfh_indir    = vmxnet3_set_rss_indir,
+       .get_rxfh          = vmxnet3_get_rss,
+       .set_rxfh          = vmxnet3_set_rss,
 #endif
 };
 
 void vmxnet3_set_ethtool_ops(struct net_device *netdev)
 {
-       SET_ETHTOOL_OPS(netdev, &vmxnet3_ethtool_ops);
+       netdev->ethtool_ops = &vmxnet3_ethtool_ops;
 }
index 4dbb2ed85b972492e1dbbbdc51a81e32652f975b..1610d51dbb5c5621499077114ad0b35cd762d19a 100644 (file)
@@ -127,6 +127,7 @@ struct vxlan_dev {
        struct list_head  next;         /* vxlan's per namespace list */
        struct vxlan_sock *vn_sock;     /* listening socket */
        struct net_device *dev;
+       struct net        *net;         /* netns for packet i/o */
        struct vxlan_rdst default_dst;  /* default destination */
        union vxlan_addr  saddr;        /* source address */
        __be16            dst_port;
@@ -134,7 +135,7 @@ struct vxlan_dev {
        __u16             port_max;
        __u8              tos;          /* TOS override */
        __u8              ttl;
-       u32               flags;        /* VXLAN_F_* below */
+       u32               flags;        /* VXLAN_F_* in vxlan.h */
 
        struct work_struct sock_work;
        struct work_struct igmp_join;
@@ -149,13 +150,6 @@ struct vxlan_dev {
        struct hlist_head fdb_head[FDB_HASH_SIZE];
 };
 
-#define VXLAN_F_LEARN  0x01
-#define VXLAN_F_PROXY  0x02
-#define VXLAN_F_RSC    0x04
-#define VXLAN_F_L2MISS 0x08
-#define VXLAN_F_L3MISS 0x10
-#define VXLAN_F_IPV6   0x20 /* internal flag */
-
 /* salt for hash table */
 static u32 vxlan_salt __read_mostly;
 static struct workqueue_struct *vxlan_wq;
@@ -571,6 +565,7 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, struct sk_buff
                        goto out;
        }
        skb_gro_pull(skb, sizeof(struct vxlanhdr)); /* pull vxlan header */
+       skb_gro_postpull_rcsum(skb, vh, sizeof(struct vxlanhdr));
 
        off_eth = skb_gro_offset(skb);
        hlen = off_eth + sizeof(*eh);
@@ -605,6 +600,7 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, struct sk_buff
        }
 
        skb_gro_pull(skb, sizeof(*eh)); /* pull inner eth header */
+       skb_gro_postpull_rcsum(skb, eh, sizeof(*eh));
        pp = ptype->callbacks.gro_receive(head, skb);
 
 out_unlock:
@@ -1203,6 +1199,7 @@ static void vxlan_rcv(struct vxlan_sock *vs,
 
        remote_ip = &vxlan->default_dst.remote_ip;
        skb_reset_mac_header(skb);
+       skb_scrub_packet(skb, !net_eq(vxlan->net, dev_net(vxlan->dev)));
        skb->protocol = eth_type_trans(skb, vxlan->dev);
 
        /* Ignore packet loops (and multicast echo) */
@@ -1599,18 +1596,11 @@ __be16 vxlan_src_port(__u16 port_min, __u16 port_max, struct sk_buff *skb)
 }
 EXPORT_SYMBOL_GPL(vxlan_src_port);
 
-static int handle_offloads(struct sk_buff *skb)
+static inline struct sk_buff *vxlan_handle_offloads(struct sk_buff *skb,
+                                                   bool udp_csum)
 {
-       if (skb_is_gso(skb)) {
-               int err = skb_unclone(skb, GFP_ATOMIC);
-               if (unlikely(err))
-                       return err;
-
-               skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL;
-       } else if (skb->ip_summed != CHECKSUM_PARTIAL)
-               skb->ip_summed = CHECKSUM_NONE;
-
-       return 0;
+       int type = udp_csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
+       return iptunnel_handle_offloads(skb, udp_csum, type);
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
@@ -1618,7 +1608,8 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
                           struct dst_entry *dst, struct sk_buff *skb,
                           struct net_device *dev, struct in6_addr *saddr,
                           struct in6_addr *daddr, __u8 prio, __u8 ttl,
-                          __be16 src_port, __be16 dst_port, __be32 vni)
+                          __be16 src_port, __be16 dst_port, __be32 vni,
+                          bool xnet)
 {
        struct ipv6hdr *ip6h;
        struct vxlanhdr *vxh;
@@ -1626,12 +1617,11 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
        int min_headroom;
        int err;
 
-       if (!skb->encapsulation) {
-               skb_reset_inner_headers(skb);
-               skb->encapsulation = 1;
-       }
+       skb = vxlan_handle_offloads(skb, !udp_get_no_check6_tx(vs->sock->sk));
+       if (IS_ERR(skb))
+               return -EINVAL;
 
-       skb_scrub_packet(skb, false);
+       skb_scrub_packet(skb, xnet);
 
        min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len
                        + VXLAN_HLEN + sizeof(struct ipv6hdr)
@@ -1663,27 +1653,14 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
        uh->source = src_port;
 
        uh->len = htons(skb->len);
-       uh->check = 0;
 
        memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
        IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
                              IPSKB_REROUTED);
        skb_dst_set(skb, dst);
 
-       if (!skb_is_gso(skb) && !(dst->dev->features & NETIF_F_IPV6_CSUM)) {
-               __wsum csum = skb_checksum(skb, 0, skb->len, 0);
-               skb->ip_summed = CHECKSUM_UNNECESSARY;
-               uh->check = csum_ipv6_magic(saddr, daddr, skb->len,
-                                           IPPROTO_UDP, csum);
-               if (uh->check == 0)
-                       uh->check = CSUM_MANGLED_0;
-       } else {
-               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(saddr, daddr,
-                                            skb->len, IPPROTO_UDP, 0);
-       }
+       udp6_set_csum(udp_get_no_check6_tx(vs->sock->sk), skb,
+                     saddr, daddr, skb->len);
 
        __skb_push(skb, sizeof(*ip6h));
        skb_reset_network_header(skb);
@@ -1699,10 +1676,6 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
        ip6h->daddr       = *daddr;
        ip6h->saddr       = *saddr;
 
-       err = handle_offloads(skb);
-       if (err)
-               return err;
-
        ip6tunnel_xmit(skb, dev);
        return 0;
 }
@@ -1711,17 +1684,16 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
 int vxlan_xmit_skb(struct vxlan_sock *vs,
                   struct rtable *rt, struct sk_buff *skb,
                   __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df,
-                  __be16 src_port, __be16 dst_port, __be32 vni)
+                  __be16 src_port, __be16 dst_port, __be32 vni, bool xnet)
 {
        struct vxlanhdr *vxh;
        struct udphdr *uh;
        int min_headroom;
        int err;
 
-       if (!skb->encapsulation) {
-               skb_reset_inner_headers(skb);
-               skb->encapsulation = 1;
-       }
+       skb = vxlan_handle_offloads(skb, !vs->sock->sk->sk_no_check_tx);
+       if (IS_ERR(skb))
+               return -EINVAL;
 
        min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len
                        + VXLAN_HLEN + sizeof(struct iphdr)
@@ -1753,14 +1725,12 @@ int vxlan_xmit_skb(struct vxlan_sock *vs,
        uh->source = src_port;
 
        uh->len = htons(skb->len);
-       uh->check = 0;
 
-       err = handle_offloads(skb);
-       if (err)
-               return err;
+       udp_set_csum(vs->sock->sk->sk_no_check_tx, skb,
+                    src, dst, skb->len);
 
        return iptunnel_xmit(vs->sock->sk, rt, skb, src, dst, IPPROTO_UDP,
-                            tos, ttl, df, false);
+                            tos, ttl, df, xnet);
 }
 EXPORT_SYMBOL_GPL(vxlan_xmit_skb);
 
@@ -1853,7 +1823,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                fl4.daddr = dst->sin.sin_addr.s_addr;
                fl4.saddr = vxlan->saddr.sin.sin_addr.s_addr;
 
-               rt = ip_route_output_key(dev_net(dev), &fl4);
+               rt = ip_route_output_key(vxlan->net, &fl4);
                if (IS_ERR(rt)) {
                        netdev_dbg(dev, "no route to %pI4\n",
                                   &dst->sin.sin_addr.s_addr);
@@ -1874,7 +1844,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                        struct vxlan_dev *dst_vxlan;
 
                        ip_rt_put(rt);
-                       dst_vxlan = vxlan_find_vni(dev_net(dev), vni, dst_port);
+                       dst_vxlan = vxlan_find_vni(vxlan->net, vni, dst_port);
                        if (!dst_vxlan)
                                goto tx_error;
                        vxlan_encap_bypass(skb, vxlan, dst_vxlan);
@@ -1887,7 +1857,8 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                err = vxlan_xmit_skb(vxlan->vn_sock, rt, skb,
                                     fl4.saddr, dst->sin.sin_addr.s_addr,
                                     tos, ttl, df, src_port, dst_port,
-                                    htonl(vni << 8));
+                                    htonl(vni << 8),
+                                    !net_eq(vxlan->net, dev_net(vxlan->dev)));
 
                if (err < 0)
                        goto rt_tx_error;
@@ -1927,7 +1898,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                        struct vxlan_dev *dst_vxlan;
 
                        dst_release(ndst);
-                       dst_vxlan = vxlan_find_vni(dev_net(dev), vni, dst_port);
+                       dst_vxlan = vxlan_find_vni(vxlan->net, vni, dst_port);
                        if (!dst_vxlan)
                                goto tx_error;
                        vxlan_encap_bypass(skb, vxlan, dst_vxlan);
@@ -1938,7 +1909,8 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
 
                err = vxlan6_xmit_skb(vxlan->vn_sock, ndst, skb,
                                      dev, &fl6.saddr, &fl6.daddr, 0, ttl,
-                                     src_port, dst_port, htonl(vni << 8));
+                                     src_port, dst_port, htonl(vni << 8),
+                                     !net_eq(vxlan->net, dev_net(vxlan->dev)));
 #endif
        }
 
@@ -2082,7 +2054,7 @@ static void vxlan_vs_add_dev(struct vxlan_sock *vs, struct vxlan_dev *vxlan)
 static int vxlan_init(struct net_device *dev)
 {
        struct vxlan_dev *vxlan = netdev_priv(dev);
-       struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id);
+       struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id);
        struct vxlan_sock *vs;
 
        dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
@@ -2090,7 +2062,7 @@ static int vxlan_init(struct net_device *dev)
                return -ENOMEM;
 
        spin_lock(&vn->sock_lock);
-       vs = vxlan_find_sock(dev_net(dev), vxlan->dst_port);
+       vs = vxlan_find_sock(vxlan->net, vxlan->dst_port);
        if (vs) {
                /* If we have a socket with same port already, reuse it */
                atomic_inc(&vs->refcnt);
@@ -2172,8 +2144,8 @@ static void vxlan_flush(struct vxlan_dev *vxlan)
 /* Cleanup timer and forwarding table on shutdown */
 static int vxlan_stop(struct net_device *dev)
 {
-       struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id);
        struct vxlan_dev *vxlan = netdev_priv(dev);
+       struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id);
        struct vxlan_sock *vs = vxlan->vn_sock;
 
        if (vs && vxlan_addr_multicast(&vxlan->default_dst.remote_ip) &&
@@ -2202,7 +2174,7 @@ static int vxlan_change_mtu(struct net_device *dev, int new_mtu)
        struct net_device *lowerdev;
        int max_mtu;
 
-       lowerdev = __dev_get_by_index(dev_net(dev), dst->remote_ifindex);
+       lowerdev = __dev_get_by_index(vxlan->net, dst->remote_ifindex);
        if (lowerdev == NULL)
                return eth_change_mtu(dev, new_mtu);
 
@@ -2285,7 +2257,6 @@ static void vxlan_setup(struct net_device *dev)
 
        dev->tx_queue_len = 0;
        dev->features   |= NETIF_F_LLTX;
-       dev->features   |= NETIF_F_NETNS_LOCAL;
        dev->features   |= NETIF_F_SG | NETIF_F_HW_CSUM;
        dev->features   |= NETIF_F_RXCSUM;
        dev->features   |= NETIF_F_GSO_SOFTWARE;
@@ -2401,7 +2372,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 struct socket *create_v6_sock(struct net *net, __be16 port)
+static struct socket *create_v6_sock(struct net *net, __be16 port, u32 flags)
 {
        struct sock *sk;
        struct socket *sock;
@@ -2438,18 +2409,25 @@ static struct socket *create_v6_sock(struct net *net, __be16 port)
 
        /* Disable multicast loopback */
        inet_sk(sk)->mc_loop = 0;
+
+       if (flags & VXLAN_F_UDP_ZERO_CSUM6_TX)
+               udp_set_no_check6_tx(sk, true);
+
+       if (flags & VXLAN_F_UDP_ZERO_CSUM6_RX)
+               udp_set_no_check6_rx(sk, true);
+
        return sock;
 }
 
 #else
 
-static struct socket *create_v6_sock(struct net *net, __be16 port)
+static struct socket *create_v6_sock(struct net *net, __be16 port, u32 flags)
 {
                return ERR_PTR(-EPFNOSUPPORT);
 }
 #endif
 
-static struct socket *create_v4_sock(struct net *net, __be16 port)
+static struct socket *create_v4_sock(struct net *net, __be16 port, u32 flags)
 {
        struct sock *sk;
        struct socket *sock;
@@ -2482,18 +2460,24 @@ static struct socket *create_v4_sock(struct net *net, __be16 port)
 
        /* Disable multicast loopback */
        inet_sk(sk)->mc_loop = 0;
+
+       if (!(flags & VXLAN_F_UDP_CSUM))
+               sock->sk->sk_no_check_tx = 1;
+
        return sock;
 }
 
 /* Create new listen socket if needed */
 static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
-                                             vxlan_rcv_t *rcv, void *data, bool ipv6)
+                                             vxlan_rcv_t *rcv, void *data,
+                                             u32 flags)
 {
        struct vxlan_net *vn = net_generic(net, vxlan_net_id);
        struct vxlan_sock *vs;
        struct socket *sock;
        struct sock *sk;
        unsigned int h;
+       bool ipv6 = !!(flags & VXLAN_F_IPV6);
 
        vs = kzalloc(sizeof(*vs), GFP_KERNEL);
        if (!vs)
@@ -2505,9 +2489,9 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
        INIT_WORK(&vs->del_work, vxlan_del_work);
 
        if (ipv6)
-               sock = create_v6_sock(net, port);
+               sock = create_v6_sock(net, port, flags);
        else
-               sock = create_v4_sock(net, port);
+               sock = create_v4_sock(net, port, flags);
        if (IS_ERR(sock)) {
                kfree(vs);
                return ERR_CAST(sock);
@@ -2545,12 +2529,12 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
 
 struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port,
                                  vxlan_rcv_t *rcv, void *data,
-                                 bool no_share, bool ipv6)
+                                 bool no_share, u32 flags)
 {
        struct vxlan_net *vn = net_generic(net, vxlan_net_id);
        struct vxlan_sock *vs;
 
-       vs = vxlan_socket_create(net, port, rcv, data, ipv6);
+       vs = vxlan_socket_create(net, port, rcv, data, flags);
        if (!IS_ERR(vs))
                return vs;
 
@@ -2578,12 +2562,12 @@ EXPORT_SYMBOL_GPL(vxlan_sock_add);
 static void vxlan_sock_work(struct work_struct *work)
 {
        struct vxlan_dev *vxlan = container_of(work, struct vxlan_dev, sock_work);
-       struct net *net = dev_net(vxlan->dev);
+       struct net *net = vxlan->net;
        struct vxlan_net *vn = net_generic(net, vxlan_net_id);
        __be16 port = vxlan->dst_port;
        struct vxlan_sock *nvs;
 
-       nvs = vxlan_sock_add(net, port, vxlan_rcv, NULL, false, vxlan->flags & VXLAN_F_IPV6);
+       nvs = vxlan_sock_add(net, port, vxlan_rcv, NULL, false, vxlan->flags);
        spin_lock(&vn->sock_lock);
        if (!IS_ERR(nvs))
                vxlan_vs_add_dev(nvs, vxlan);
@@ -2605,6 +2589,8 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
        if (!data[IFLA_VXLAN_ID])
                return -EINVAL;
 
+       vxlan->net = dev_net(dev);
+
        vni = nla_get_u32(data[IFLA_VXLAN_ID]);
        dst->remote_vni = vni;
 
@@ -2705,12 +2691,23 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
        if (data[IFLA_VXLAN_PORT])
                vxlan->dst_port = nla_get_be16(data[IFLA_VXLAN_PORT]);
 
+       if (data[IFLA_VXLAN_UDP_CSUM] && nla_get_u8(data[IFLA_VXLAN_UDP_CSUM]))
+               vxlan->flags |= VXLAN_F_UDP_CSUM;
+
+       if (data[IFLA_VXLAN_UDP_ZERO_CSUM6_TX] &&
+           nla_get_u8(data[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]))
+               vxlan->flags |= VXLAN_F_UDP_ZERO_CSUM6_TX;
+
+       if (data[IFLA_VXLAN_UDP_ZERO_CSUM6_RX] &&
+           nla_get_u8(data[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]))
+               vxlan->flags |= VXLAN_F_UDP_ZERO_CSUM6_RX;
+
        if (vxlan_find_vni(net, vni, vxlan->dst_port)) {
                pr_info("duplicate VNI %u\n", vni);
                return -EEXIST;
        }
 
-       SET_ETHTOOL_OPS(dev, &vxlan_ethtool_ops);
+       dev->ethtool_ops = &vxlan_ethtool_ops;
 
        /* create an fdb entry for a valid default destination */
        if (!vxlan_addr_any(&vxlan->default_dst.remote_ip)) {
@@ -2739,8 +2736,8 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
 
 static void vxlan_dellink(struct net_device *dev, struct list_head *head)
 {
-       struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id);
        struct vxlan_dev *vxlan = netdev_priv(dev);
+       struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id);
 
        spin_lock(&vn->sock_lock);
        if (!hlist_unhashed(&vxlan->hlist))
@@ -2768,7 +2765,10 @@ static size_t vxlan_get_size(const struct net_device *dev)
                nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_AGEING */
                nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_LIMIT */
                nla_total_size(sizeof(struct ifla_vxlan_port_range)) +
-               nla_total_size(sizeof(__be16))+ /* IFLA_VXLAN_PORT */
+               nla_total_size(sizeof(__be16)) + /* IFLA_VXLAN_PORT */
+               nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_UDP_CSUM */
+               nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_UDP_ZERO_CSUM6_TX */
+               nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_UDP_ZERO_CSUM6_RX */
                0;
 }
 
@@ -2828,7 +2828,13 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
                        !!(vxlan->flags & VXLAN_F_L3MISS)) ||
            nla_put_u32(skb, IFLA_VXLAN_AGEING, vxlan->age_interval) ||
            nla_put_u32(skb, IFLA_VXLAN_LIMIT, vxlan->addrmax) ||
-           nla_put_be16(skb, IFLA_VXLAN_PORT, vxlan->dst_port))
+           nla_put_be16(skb, IFLA_VXLAN_PORT, vxlan->dst_port) ||
+           nla_put_u8(skb, IFLA_VXLAN_UDP_CSUM,
+                       !!(vxlan->flags & VXLAN_F_UDP_CSUM)) ||
+           nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
+                       !!(vxlan->flags & VXLAN_F_UDP_ZERO_CSUM6_TX)) ||
+           nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
+                       !!(vxlan->flags & VXLAN_F_UDP_ZERO_CSUM6_RX)))
                goto nla_put_failure;
 
        if (nla_put(skb, IFLA_VXLAN_PORT_RANGE, sizeof(ports), &ports))
@@ -2905,8 +2911,33 @@ static __net_init int vxlan_init_net(struct net *net)
        return 0;
 }
 
+static void __net_exit vxlan_exit_net(struct net *net)
+{
+       struct vxlan_net *vn = net_generic(net, vxlan_net_id);
+       struct vxlan_dev *vxlan, *next;
+       struct net_device *dev, *aux;
+       LIST_HEAD(list);
+
+       rtnl_lock();
+       for_each_netdev_safe(net, dev, aux)
+               if (dev->rtnl_link_ops == &vxlan_link_ops)
+                       unregister_netdevice_queue(dev, &list);
+
+       list_for_each_entry_safe(vxlan, next, &vn->vxlan_list, next) {
+               /* If vxlan->dev is in the same netns, it has already been added
+                * to the list by the previous loop.
+                */
+               if (!net_eq(dev_net(vxlan->dev), net))
+                       unregister_netdevice_queue(dev, &list);
+       }
+
+       unregister_netdevice_many(&list);
+       rtnl_unlock();
+}
+
 static struct pernet_operations vxlan_net_ops = {
        .init = vxlan_init_net,
+       .exit = vxlan_exit_net,
        .id   = &vxlan_net_id,
        .size = sizeof(struct vxlan_net),
 };
index bcfff0d62de4f2070d5ac644a3becfedb74e3462..93ace042d0aa71b007ec80ab638a5cdbcaa790dd 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/ioport.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
+#include <linux/delay.h>
 #include <linux/if.h>
 #include <linux/hdlc.h>
 #include <asm/io.h>
@@ -678,7 +679,6 @@ static inline void
 fst_cpureset(struct fst_card_info *card)
 {
        unsigned char interrupt_line_register;
-       unsigned long j = jiffies + 1;
        unsigned int regval;
 
        if (card->family == FST_FAMILY_TXU) {
@@ -696,16 +696,12 @@ fst_cpureset(struct fst_card_info *card)
                /*
                 * We are delaying here to allow the 9054 to reset itself
                 */
-               j = jiffies + 1;
-               while (jiffies < j)
-                       /* Do nothing */ ;
+               usleep_range(10, 20);
                outw(0x240f, card->pci_conf + CNTRL_9054 + 2);
                /*
                 * We are delaying here to allow the 9054 to reload its eeprom
                 */
-               j = jiffies + 1;
-               while (jiffies < j)
-                       /* Do nothing */ ;
+               usleep_range(10, 20);
                outw(0x040f, card->pci_conf + CNTRL_9054 + 2);
 
                if (pci_write_config_byte
@@ -886,20 +882,18 @@ fst_rx_dma_complete(struct fst_card_info *card, struct fst_port_info *port,
  *      Receive a frame through the DMA
  */
 static inline void
-fst_rx_dma(struct fst_card_info *card, dma_addr_t skb,
-          dma_addr_t mem, int len)
+fst_rx_dma(struct fst_card_info *card, dma_addr_t dma, u32 mem, int len)
 {
        /*
         * This routine will setup the DMA and start it
         */
 
-       dbg(DBG_RX, "In fst_rx_dma %lx %lx %d\n",
-           (unsigned long) skb, (unsigned long) mem, len);
+       dbg(DBG_RX, "In fst_rx_dma %x %x %d\n", (u32)dma, mem, len);
        if (card->dmarx_in_progress) {
                dbg(DBG_ASS, "In fst_rx_dma while dma in progress\n");
        }
 
-       outl(skb, card->pci_conf + DMAPADR0);   /* Copy to here */
+       outl(dma, card->pci_conf + DMAPADR0);   /* Copy to here */
        outl(mem, card->pci_conf + DMALADR0);   /* from here */
        outl(len, card->pci_conf + DMASIZ0);    /* for this length */
        outl(0x00000000c, card->pci_conf + DMADPR0);    /* In this direction */
@@ -915,20 +909,19 @@ fst_rx_dma(struct fst_card_info *card, dma_addr_t skb,
  *      Send a frame through the DMA
  */
 static inline void
-fst_tx_dma(struct fst_card_info *card, unsigned char *skb,
-          unsigned char *mem, int len)
+fst_tx_dma(struct fst_card_info *card, dma_addr_t dma, u32 mem, int len)
 {
        /*
         * This routine will setup the DMA and start it.
         */
 
-       dbg(DBG_TX, "In fst_tx_dma %p %p %d\n", skb, mem, len);
+       dbg(DBG_TX, "In fst_tx_dma %x %x %d\n", (u32)dma, mem, len);
        if (card->dmatx_in_progress) {
                dbg(DBG_ASS, "In fst_tx_dma while dma in progress\n");
        }
 
-       outl((unsigned long) skb, card->pci_conf + DMAPADR1);   /* Copy from here */
-       outl((unsigned long) mem, card->pci_conf + DMALADR1);   /* to here */
+       outl(dma, card->pci_conf + DMAPADR1);   /* Copy from here */
+       outl(mem, card->pci_conf + DMALADR1);   /* to here */
        outl(len, card->pci_conf + DMASIZ1);    /* for this length */
        outl(0x000000004, card->pci_conf + DMADPR1);    /* In this direction */
 
@@ -1405,9 +1398,7 @@ do_bottom_half_tx(struct fst_card_info *card)
                                        card->dma_len_tx = skb->len;
                                        card->dma_txpos = port->txpos;
                                        fst_tx_dma(card,
-                                                  (char *) card->
-                                                  tx_dma_handle_card,
-                                                  (char *)
+                                                  card->tx_dma_handle_card,
                                                   BUF_OFFSET(txBuffer[pi]
                                                              [port->txpos][0]),
                                                   skb->len);
index de3bbf43fc5ac14f41ee9d76eeacb1f059eeaab3..cdd45fb8a1f6892587abddf2abbf0963bd7a5653 100644 (file)
@@ -1322,10 +1322,6 @@ NOTE:  This is rather a useless action right now, as the
 
 static int sdla_change_mtu(struct net_device *dev, int new_mtu)
 {
-       struct frad_local *flp;
-
-       flp = netdev_priv(dev);
-
        if (netif_running(dev))
                return -EBUSY;
 
index 4a01e5c7fe098e43def42b3d06d091abbabd8795..4c417903e9be9f5bb74a7901f179488e812d8038 100644 (file)
@@ -1061,7 +1061,7 @@ int i2400m_firmware_check(struct i2400m *i2400m)
                goto error_bad_major;
        }
        result = 0;
-       if (minor < I2400M_HDIv_MINOR_2 && minor > I2400M_HDIv_MINOR)
+       if (minor > I2400M_HDIv_MINOR_2 || minor < I2400M_HDIv_MINOR)
                dev_warn(dev, "untested minor fw version %u.%u.%u\n",
                         major, minor, branch);
        /* Yes, we ignore the branch -- we don't have to track it */
index 9c34d2fccfac61508705a4021f436e9a9024e936..9c78090e72f87dae118dff22fd366d5ce11bacd8 100644 (file)
@@ -500,26 +500,23 @@ int i2400m_pm_notifier(struct notifier_block *notifier,
  */
 int i2400m_pre_reset(struct i2400m *i2400m)
 {
-       int result;
        struct device *dev = i2400m_dev(i2400m);
 
        d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
        d_printf(1, dev, "pre-reset shut down\n");
 
-       result = 0;
        mutex_lock(&i2400m->init_mutex);
        if (i2400m->updown) {
                netif_tx_disable(i2400m->wimax_dev.net_dev);
                __i2400m_dev_stop(i2400m);
-               result = 0;
                /* down't set updown to zero -- this way
                 * post_reset can restore properly */
        }
        mutex_unlock(&i2400m->init_mutex);
        if (i2400m->bus_release)
                i2400m->bus_release(i2400m);
-       d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result);
-       return result;
+       d_fnend(3, dev, "(i2400m %p) = 0\n", i2400m);
+       return 0;
 }
 EXPORT_SYMBOL_GPL(i2400m_pre_reset);
 
index 99b3bfa717d55403b45effb585b0293b46f03ced..d48776e4f343e050787510923bc01e689a2bd9cc 100644 (file)
@@ -365,15 +365,15 @@ static inline unsigned long at76_get_timeout(struct dfu_status *s)
 static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size,
                                int manifest_sync_timeout)
 {
-       u8 *block;
-       struct dfu_status dfu_stat_buf;
        int ret = 0;
        int need_dfu_state = 1;
        int is_done = 0;
-       u8 dfu_state = 0;
        u32 dfu_timeout = 0;
        int bsize = 0;
        int blockno = 0;
+       struct dfu_status *dfu_stat_buf = NULL;
+       u8 *dfu_state = NULL;
+       u8 *block = NULL;
 
        at76_dbg(DBG_DFU, "%s( %p, %u, %d)", __func__, buf, size,
                 manifest_sync_timeout);
@@ -383,13 +383,28 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size,
                return -EINVAL;
        }
 
+       dfu_stat_buf = kmalloc(sizeof(struct dfu_status), GFP_KERNEL);
+       if (!dfu_stat_buf) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
        block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL);
-       if (!block)
-               return -ENOMEM;
+       if (!block) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
+       dfu_state = kmalloc(sizeof(u8), GFP_KERNEL);
+       if (!dfu_state) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+       *dfu_state = 0;
 
        do {
                if (need_dfu_state) {
-                       ret = at76_dfu_get_state(udev, &dfu_state);
+                       ret = at76_dfu_get_state(udev, dfu_state);
                        if (ret < 0) {
                                dev_err(&udev->dev,
                                        "cannot get DFU state: %d\n", ret);
@@ -398,13 +413,13 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size,
                        need_dfu_state = 0;
                }
 
-               switch (dfu_state) {
+               switch (*dfu_state) {
                case STATE_DFU_DOWNLOAD_SYNC:
                        at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_SYNC");
-                       ret = at76_dfu_get_status(udev, &dfu_stat_buf);
+                       ret = at76_dfu_get_status(udev, dfu_stat_buf);
                        if (ret >= 0) {
-                               dfu_state = dfu_stat_buf.state;
-                               dfu_timeout = at76_get_timeout(&dfu_stat_buf);
+                               *dfu_state = dfu_stat_buf->state;
+                               dfu_timeout = at76_get_timeout(dfu_stat_buf);
                                need_dfu_state = 0;
                        } else
                                dev_err(&udev->dev,
@@ -447,12 +462,12 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size,
                case STATE_DFU_MANIFEST_SYNC:
                        at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_SYNC");
 
-                       ret = at76_dfu_get_status(udev, &dfu_stat_buf);
+                       ret = at76_dfu_get_status(udev, dfu_stat_buf);
                        if (ret < 0)
                                break;
 
-                       dfu_state = dfu_stat_buf.state;
-                       dfu_timeout = at76_get_timeout(&dfu_stat_buf);
+                       *dfu_state = dfu_stat_buf->state;
+                       dfu_timeout = at76_get_timeout(dfu_stat_buf);
                        need_dfu_state = 0;
 
                        /* override the timeout from the status response,
@@ -484,14 +499,17 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size,
                        break;
 
                default:
-                       at76_dbg(DBG_DFU, "DFU UNKNOWN STATE (%d)", dfu_state);
+                       at76_dbg(DBG_DFU, "DFU UNKNOWN STATE (%d)", *dfu_state);
                        ret = -EINVAL;
                        break;
                }
        } while (!is_done && (ret >= 0));
 
 exit:
+       kfree(dfu_state);
        kfree(block);
+       kfree(dfu_stat_buf);
+
        if (ret >= 0)
                ret = 0;
 
@@ -1277,6 +1295,7 @@ static int at76_load_external_fw(struct usb_device *udev, struct fwentry *fwe)
                        dev_err(&udev->dev,
                                "loading %dth firmware block failed: %d\n",
                                blockno, ret);
+                       ret = -EIO;
                        goto exit;
                }
                buf += bsize;
@@ -1410,6 +1429,8 @@ static int at76_startup_device(struct at76_priv *priv)
        /* remove BSSID from previous run */
        memset(priv->bssid, 0, ETH_ALEN);
 
+       priv->scanning = false;
+
        if (at76_set_radio(priv, 1) == 1)
                at76_wait_completion(priv, CMD_RADIO_ON);
 
@@ -1483,6 +1504,52 @@ static void at76_work_submit_rx(struct work_struct *work)
        mutex_unlock(&priv->mtx);
 }
 
+/* This is a workaround to make scan working:
+ * currently mac80211 does not process frames with no frequency
+ * information.
+ * However during scan the HW performs a sweep by itself, and we
+ * are unable to know where the radio is actually tuned.
+ * This function tries to do its best to guess this information..
+ * During scan, If the current frame is a beacon or a probe response,
+ * the channel information is extracted from it.
+ * When not scanning, for other frames, or if it happens that for
+ * whatever reason we fail to parse beacons and probe responses, this
+ * function returns the priv->channel information, that should be correct
+ * at least when we are not scanning.
+ */
+static inline int at76_guess_freq(struct at76_priv *priv)
+{
+       size_t el_off;
+       const u8 *el;
+       int channel = priv->channel;
+       int len = priv->rx_skb->len;
+       struct ieee80211_hdr *hdr = (void *)priv->rx_skb->data;
+
+       if (!priv->scanning)
+               goto exit;
+
+       if (len < 24)
+               goto exit;
+
+       if (ieee80211_is_probe_resp(hdr->frame_control)) {
+               el_off = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+               el = ((struct ieee80211_mgmt *)hdr)->u.probe_resp.variable;
+       } else if (ieee80211_is_beacon(hdr->frame_control)) {
+               el_off = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+               el = ((struct ieee80211_mgmt *)hdr)->u.beacon.variable;
+       } else {
+               goto exit;
+       }
+       len -= el_off;
+
+       el = cfg80211_find_ie(WLAN_EID_DS_PARAMS, el, len);
+       if (el && el[1] > 0)
+               channel = el[2];
+
+exit:
+       return ieee80211_channel_to_frequency(channel, IEEE80211_BAND_2GHZ);
+}
+
 static void at76_rx_tasklet(unsigned long param)
 {
        struct urb *urb = (struct urb *)param;
@@ -1523,6 +1590,8 @@ static void at76_rx_tasklet(unsigned long param)
        rx_status.signal = buf->rssi;
        rx_status.flag |= RX_FLAG_DECRYPTED;
        rx_status.flag |= RX_FLAG_IV_STRIPPED;
+       rx_status.band = IEEE80211_BAND_2GHZ;
+       rx_status.freq = at76_guess_freq(priv);
 
        at76_dbg(DBG_MAC80211, "calling ieee80211_rx_irqsafe(): %d/%d",
                 priv->rx_skb->len, priv->rx_skb->data_len);
@@ -1875,6 +1944,8 @@ static void at76_dwork_hw_scan(struct work_struct *work)
        if (is_valid_ether_addr(priv->bssid))
                at76_join(priv);
 
+       priv->scanning = false;
+
        mutex_unlock(&priv->mtx);
 
        ieee80211_scan_completed(priv->hw, false);
@@ -1929,6 +2000,7 @@ static int at76_hw_scan(struct ieee80211_hw *hw,
                goto exit;
        }
 
+       priv->scanning = true;
        ieee80211_queue_delayed_work(priv->hw, &priv->dwork_hw_scan,
                                     SCAN_POLL_INTERVAL);
 
@@ -2020,6 +2092,44 @@ static void at76_configure_filter(struct ieee80211_hw *hw,
        ieee80211_queue_work(hw, &priv->work_set_promisc);
 }
 
+static int at76_set_wep(struct at76_priv *priv)
+{
+       int ret = 0;
+       struct mib_mac_wep *mib_data = &priv->mib_buf.data.wep_mib;
+
+       priv->mib_buf.type = MIB_MAC_WEP;
+       priv->mib_buf.size = sizeof(struct mib_mac_wep);
+       priv->mib_buf.index = 0;
+
+       memset(mib_data, 0, sizeof(*mib_data));
+
+       if (priv->wep_enabled) {
+               if (priv->wep_keys_len[priv->wep_key_id] > WEP_SMALL_KEY_LEN)
+                       mib_data->encryption_level = 2;
+               else
+                       mib_data->encryption_level = 1;
+
+               /* always exclude unencrypted if WEP is active */
+               mib_data->exclude_unencrypted = 1;
+       } else {
+               mib_data->exclude_unencrypted = 0;
+               mib_data->encryption_level = 0;
+       }
+
+       mib_data->privacy_invoked = priv->wep_enabled;
+       mib_data->wep_default_key_id = priv->wep_key_id;
+       memcpy(mib_data->wep_default_keyvalue, priv->wep_keys,
+              sizeof(priv->wep_keys));
+
+       ret = at76_set_mib(priv, &priv->mib_buf);
+
+       if (ret < 0)
+               wiphy_err(priv->hw->wiphy,
+                         "set_mib (wep) failed: %d\n", ret);
+
+       return ret;
+}
+
 static int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                        struct ieee80211_vif *vif, struct ieee80211_sta *sta,
                        struct ieee80211_key_conf *key)
@@ -2062,7 +2172,7 @@ static int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                        priv->wep_enabled = 1;
        }
 
-       at76_startup_device(priv);
+       at76_set_wep(priv);
 
        mutex_unlock(&priv->mtx);
 
@@ -2330,16 +2440,22 @@ static int at76_probe(struct usb_interface *interface,
        struct usb_device *udev;
        int op_mode;
        int need_ext_fw = 0;
-       struct mib_fw_version fwv;
+       struct mib_fw_version *fwv = NULL;
        int board_type = (int)id->driver_info;
 
        udev = usb_get_dev(interface_to_usbdev(interface));
 
+       fwv = kmalloc(sizeof(*fwv), GFP_KERNEL);
+       if (!fwv) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
        /* Load firmware into kernel memory */
        fwe = at76_load_firmware(udev, board_type);
        if (!fwe) {
                ret = -ENOENT;
-               goto error;
+               goto exit;
        }
 
        op_mode = at76_get_op_mode(udev);
@@ -2353,7 +2469,7 @@ static int at76_probe(struct usb_interface *interface,
                dev_err(&interface->dev,
                        "cannot handle a device in HW_CONFIG_MODE\n");
                ret = -EBUSY;
-               goto error;
+               goto exit;
        }
 
        if (op_mode != OPMODE_NORMAL_NIC_WITH_FLASH
@@ -2366,10 +2482,10 @@ static int at76_probe(struct usb_interface *interface,
                        dev_err(&interface->dev,
                                "error %d downloading internal firmware\n",
                                ret);
-                       goto error;
+                       goto exit;
                }
                usb_put_dev(udev);
-               return ret;
+               goto exit;
        }
 
        /* Internal firmware already inside the device.  Get firmware
@@ -2382,8 +2498,8 @@ static int at76_probe(struct usb_interface *interface,
         * query the device for the fw version */
        if ((fwe->fw_version.major > 0 || fwe->fw_version.minor >= 100)
            || (op_mode == OPMODE_NORMAL_NIC_WITH_FLASH)) {
-               ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv));
-               if (ret < 0 || (fwv.major | fwv.minor) == 0)
+               ret = at76_get_mib(udev, MIB_FW_VERSION, fwv, sizeof(*fwv));
+               if (ret < 0 || (fwv->major | fwv->minor) == 0)
                        need_ext_fw = 1;
        } else
                /* No way to check firmware version, reload to be sure */
@@ -2394,37 +2510,37 @@ static int at76_probe(struct usb_interface *interface,
                           "downloading external firmware\n");
 
                ret = at76_load_external_fw(udev, fwe);
-               if (ret)
-                       goto error;
+               if (ret < 0)
+                       goto exit;
 
                /* Re-check firmware version */
-               ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv));
+               ret = at76_get_mib(udev, MIB_FW_VERSION, fwv, sizeof(*fwv));
                if (ret < 0) {
                        dev_err(&interface->dev,
                                "error %d getting firmware version\n", ret);
-                       goto error;
+                       goto exit;
                }
        }
 
        priv = at76_alloc_new_device(udev);
        if (!priv) {
                ret = -ENOMEM;
-               goto error;
+               goto exit;
        }
 
        usb_set_intfdata(interface, priv);
 
-       memcpy(&priv->fw_version, &fwv, sizeof(struct mib_fw_version));
+       memcpy(&priv->fw_version, fwv, sizeof(struct mib_fw_version));
        priv->board_type = board_type;
 
        ret = at76_init_new_device(priv, interface);
        if (ret < 0)
                at76_delete_device(priv);
 
-       return ret;
-
-error:
-       usb_put_dev(udev);
+exit:
+       kfree(fwv);
+       if (ret < 0)
+               usb_put_dev(udev);
        return ret;
 }
 
index f14a65473fe8466062bf70a156dd620038787fb5..55090a38ac9549eb546a747144b9f274ae1b2faf 100644 (file)
@@ -219,18 +219,6 @@ struct at76_req_join {
        u8 reserved;
 } __packed;
 
-struct set_mib_buffer {
-       u8 type;
-       u8 size;
-       u8 index;
-       u8 reserved;
-       union {
-               u8 byte;
-               __le16 word;
-               u8 addr[ETH_ALEN];
-       } data;
-} __packed;
-
 struct mib_local {
        u16 reserved0;
        u8 beacon_enable;
@@ -334,6 +322,19 @@ struct mib_mdomain {
        u8 channel_list[14];    /* 0 for invalid channels */
 } __packed;
 
+struct set_mib_buffer {
+       u8 type;
+       u8 size;
+       u8 index;
+       u8 reserved;
+       union {
+               u8 byte;
+               __le16 word;
+               u8 addr[ETH_ALEN];
+               struct mib_mac_wep wep_mib;
+       } data;
+} __packed;
+
 struct at76_fw_header {
        __le32 crc;             /* CRC32 of the whole image */
        __le32 board_type;      /* firmware compatibility code */
@@ -417,6 +418,7 @@ struct at76_priv {
        int scan_max_time;      /* scan max channel time */
        int scan_mode;          /* SCAN_TYPE_ACTIVE, SCAN_TYPE_PASSIVE */
        int scan_need_any;      /* if set, need to scan for any ESSID */
+       bool scanning;          /* if set, the scan is running */
 
        u16 assoc_id;           /* current association ID, if associated */
 
index 507d9a9ee69ad4b61ece2d334434691801682efe..f92050617ae682e02bb48b6676a16298ae2dfa4f 100644 (file)
@@ -1090,7 +1090,8 @@ static int ar5523_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
        return ret;
 }
 
-static void ar5523_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+static void ar5523_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                        u32 queues, bool drop)
 {
        struct ar5523 *ar = hw->priv;
 
index a1f0996288508e3cad8ecd8e03f33153980e593e..17d221abd58c0bb70d72a6fbf14f1e7422f8ced5 100644 (file)
@@ -175,7 +175,7 @@ int ath10k_bmi_write_memory(struct ath10k *ar,
        return 0;
 }
 
-int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param)
+int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 param, u32 *result)
 {
        struct bmi_cmd cmd;
        union bmi_resp resp;
@@ -184,7 +184,7 @@ int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param)
        int ret;
 
        ath10k_dbg(ATH10K_DBG_BMI, "bmi execute address 0x%x param 0x%x\n",
-                  address, *param);
+                  address, param);
 
        if (ar->bmi.done_sent) {
                ath10k_warn("command disallowed\n");
@@ -193,7 +193,7 @@ int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param)
 
        cmd.id            = __cpu_to_le32(BMI_EXECUTE);
        cmd.execute.addr  = __cpu_to_le32(address);
-       cmd.execute.param = __cpu_to_le32(*param);
+       cmd.execute.param = __cpu_to_le32(param);
 
        ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
        if (ret) {
@@ -204,10 +204,13 @@ int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param)
        if (resplen < sizeof(resp.execute)) {
                ath10k_warn("invalid execute response length (%d)\n",
                            resplen);
-               return ret;
+               return -EIO;
        }
 
-       *param = __le32_to_cpu(resp.execute.result);
+       *result = __le32_to_cpu(resp.execute.result);
+
+       ath10k_dbg(ATH10K_DBG_BMI, "bmi execute result 0x%x\n", *result);
+
        return 0;
 }
 
index 8d81ce1cec216c7b55fa1c0ab47b65cc41cafb0d..111ab701465c0e2dfa731b3e5409278151545a3a 100644 (file)
@@ -201,7 +201,8 @@ int ath10k_bmi_write_memory(struct ath10k *ar, u32 address,
                                                                        \
                addr = host_interest_item_address(HI_ITEM(item));       \
                ret = ath10k_bmi_read_memory(ar, addr, (u8 *)&tmp, 4); \
-               *val = __le32_to_cpu(tmp);                              \
+               if (!ret)                                               \
+                       *val = __le32_to_cpu(tmp);                      \
                ret;                                                    \
         })
 
@@ -217,7 +218,7 @@ int ath10k_bmi_write_memory(struct ath10k *ar, u32 address,
                ret;                                                    \
        })
 
-int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param);
+int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 param, u32 *result);
 int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address);
 int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length);
 int ath10k_bmi_fast_download(struct ath10k *ar, u32 address,
index a79499c8235009f701073c83b3974d66d183e001..d185dc0cd12b2f739535a6bc6fe77020b0764133 100644 (file)
@@ -329,6 +329,33 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
        return ret;
 }
 
+void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe)
+{
+       struct ath10k *ar = pipe->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ath10k_ce_ring *src_ring = pipe->src_ring;
+       u32 ctrl_addr = pipe->ctrl_addr;
+
+       lockdep_assert_held(&ar_pci->ce_lock);
+
+       /*
+        * This function must be called only if there is an incomplete
+        * scatter-gather transfer (before index register is updated)
+        * that needs to be cleaned up.
+        */
+       if (WARN_ON_ONCE(src_ring->write_index == src_ring->sw_index))
+               return;
+
+       if (WARN_ON_ONCE(src_ring->write_index ==
+                        ath10k_ce_src_ring_write_index_get(ar, ctrl_addr)))
+               return;
+
+       src_ring->write_index--;
+       src_ring->write_index &= src_ring->nentries_mask;
+
+       src_ring->per_transfer_context[src_ring->write_index] = NULL;
+}
+
 int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
                   void *per_transfer_context,
                   u32 buffer,
@@ -840,35 +867,17 @@ void ath10k_ce_recv_cb_register(struct ath10k_ce_pipe *ce_state,
 
 static int ath10k_ce_init_src_ring(struct ath10k *ar,
                                   unsigned int ce_id,
-                                  struct ath10k_ce_pipe *ce_state,
                                   const struct ce_attr *attr)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       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);
-       dma_addr_t base_addr;
-       char *ptr;
-
-       nentries = roundup_pow_of_two(nentries);
-
-       if (ce_state->src_ring) {
-               WARN_ON(ce_state->src_ring->nentries != nentries);
-               return 0;
-       }
-
-       ce_nbytes = sizeof(struct ath10k_ce_ring) + (nentries * sizeof(void *));
-       ptr = kzalloc(ce_nbytes, GFP_KERNEL);
-       if (ptr == NULL)
-               return -ENOMEM;
+       struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
+       struct ath10k_ce_ring *src_ring = ce_state->src_ring;
+       u32 nentries, ctrl_addr = ath10k_ce_base_address(ce_id);
 
-       ce_state->src_ring = (struct ath10k_ce_ring *)ptr;
-       src_ring = ce_state->src_ring;
+       nentries = roundup_pow_of_two(attr->src_nentries);
 
-       ptr += sizeof(struct ath10k_ce_ring);
-       src_ring->nentries = nentries;
-       src_ring->nentries_mask = nentries - 1;
+       memset(src_ring->per_transfer_context, 0,
+              nentries * sizeof(*src_ring->per_transfer_context));
 
        src_ring->sw_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
        src_ring->sw_index &= src_ring->nentries_mask;
@@ -878,21 +887,87 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar,
                ath10k_ce_src_ring_write_index_get(ar, ctrl_addr);
        src_ring->write_index &= src_ring->nentries_mask;
 
-       src_ring->per_transfer_context = (void **)ptr;
+       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);
+       ath10k_ce_src_ring_dmax_set(ar, ctrl_addr, attr->src_sz_max);
+       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_dbg(ATH10K_DBG_BOOT,
+                  "boot init 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,
+                                   const struct ce_attr *attr)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
+       struct ath10k_ce_ring *dest_ring = ce_state->dest_ring;
+       u32 nentries, ctrl_addr = ath10k_ce_base_address(ce_id);
+
+       nentries = roundup_pow_of_two(attr->dest_nentries);
+
+       memset(dest_ring->per_transfer_context, 0,
+              nentries * sizeof(*dest_ring->per_transfer_context));
+
+       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_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_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 ath10k_ce_ring *
+ath10k_ce_alloc_src_ring(struct ath10k *ar, unsigned int ce_id,
+                        const struct ce_attr *attr)
+{
+       struct ath10k_ce_ring *src_ring;
+       u32 nentries = attr->src_nentries;
+       dma_addr_t base_addr;
+
+       nentries = roundup_pow_of_two(nentries);
+
+       src_ring = kzalloc(sizeof(*src_ring) +
+                          (nentries *
+                           sizeof(*src_ring->per_transfer_context)),
+                          GFP_KERNEL);
+       if (src_ring == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       src_ring->nentries = nentries;
+       src_ring->nentries_mask = nentries - 1;
 
        /*
         * Legacy platforms that do not support cache
         * coherent DMA are unsupported
         */
        src_ring->base_addr_owner_space_unaligned =
-               pci_alloc_consistent(ar_pci->pdev,
-                                    (nentries * sizeof(struct ce_desc) +
-                                     CE_DESC_RING_ALIGN),
-                                    &base_addr);
+               dma_alloc_coherent(ar->dev,
+                                  (nentries * sizeof(struct ce_desc) +
+                                   CE_DESC_RING_ALIGN),
+                                  &base_addr, GFP_KERNEL);
        if (!src_ring->base_addr_owner_space_unaligned) {
-               kfree(ce_state->src_ring);
-               ce_state->src_ring = NULL;
-               return -ENOMEM;
+               kfree(src_ring);
+               return ERR_PTR(-ENOMEM);
        }
 
        src_ring->base_addr_ce_space_unaligned = base_addr;
@@ -912,88 +987,54 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar,
                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;
+               dma_free_coherent(ar->dev,
+                                 (nentries * sizeof(struct ce_desc) +
+                                  CE_DESC_RING_ALIGN),
+                                 src_ring->base_addr_owner_space,
+                                 src_ring->base_addr_ce_space);
+               kfree(src_ring);
+               return ERR_PTR(-ENOMEM);
        }
 
        src_ring->shadow_base = PTR_ALIGN(
                        src_ring->shadow_base_unaligned,
                        CE_DESC_RING_ALIGN);
 
-       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);
-       ath10k_ce_src_ring_dmax_set(ar, ctrl_addr, attr->src_sz_max);
-       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_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;
+       return src_ring;
 }
 
-static int ath10k_ce_init_dest_ring(struct ath10k *ar,
-                                   unsigned int ce_id,
-                                   struct ath10k_ce_pipe *ce_state,
-                                   const struct ce_attr *attr)
+static struct ath10k_ce_ring *
+ath10k_ce_alloc_dest_ring(struct ath10k *ar, unsigned int ce_id,
+                         const struct ce_attr *attr)
 {
-       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        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);
+       u32 nentries;
        dma_addr_t base_addr;
-       char *ptr;
 
-       nentries = roundup_pow_of_two(nentries);
+       nentries = roundup_pow_of_two(attr->dest_nentries);
 
-       if (ce_state->dest_ring) {
-               WARN_ON(ce_state->dest_ring->nentries != nentries);
-               return 0;
-       }
-
-       ce_nbytes = sizeof(struct ath10k_ce_ring) + (nentries * sizeof(void *));
-       ptr = kzalloc(ce_nbytes, GFP_KERNEL);
-       if (ptr == NULL)
-               return -ENOMEM;
+       dest_ring = kzalloc(sizeof(*dest_ring) +
+                           (nentries *
+                            sizeof(*dest_ring->per_transfer_context)),
+                           GFP_KERNEL);
+       if (dest_ring == NULL)
+               return ERR_PTR(-ENOMEM);
 
-       ce_state->dest_ring = (struct ath10k_ce_ring *)ptr;
-       dest_ring = ce_state->dest_ring;
-
-       ptr += sizeof(struct ath10k_ce_ring);
        dest_ring->nentries = nentries;
        dest_ring->nentries_mask = nentries - 1;
 
-       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;
-
-       dest_ring->per_transfer_context = (void **)ptr;
-
        /*
         * Legacy platforms that do not support cache
         * coherent DMA are unsupported
         */
        dest_ring->base_addr_owner_space_unaligned =
-               pci_alloc_consistent(ar_pci->pdev,
-                                    (nentries * sizeof(struct ce_desc) +
-                                     CE_DESC_RING_ALIGN),
-                                    &base_addr);
+               dma_alloc_coherent(ar->dev,
+                                  (nentries * sizeof(struct ce_desc) +
+                                   CE_DESC_RING_ALIGN),
+                                  &base_addr, GFP_KERNEL);
        if (!dest_ring->base_addr_owner_space_unaligned) {
-               kfree(ce_state->dest_ring);
-               ce_state->dest_ring = NULL;
-               return -ENOMEM;
+               kfree(dest_ring);
+               return ERR_PTR(-ENOMEM);
        }
 
        dest_ring->base_addr_ce_space_unaligned = base_addr;
@@ -1012,39 +1053,7 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar,
                        dest_ring->base_addr_ce_space_unaligned,
                        CE_DESC_RING_ALIGN);
 
-       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_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 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 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);
-
-       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);
-
-       return ce_state;
+       return dest_ring;
 }
 
 /*
@@ -1054,11 +1063,11 @@ static struct ath10k_ce_pipe *ath10k_ce_init_state(struct ath10k *ar,
  * initialization. It may be that only one side or the other is
  * initialized by software/firmware.
  */
-struct ath10k_ce_pipe *ath10k_ce_init(struct ath10k *ar,
-                               unsigned int ce_id,
-                               const struct ce_attr *attr)
+int ath10k_ce_init_pipe(struct ath10k *ar, unsigned int ce_id,
+                       const struct ce_attr *attr)
 {
-       struct ath10k_ce_pipe *ce_state;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
        int ret;
 
        /*
@@ -1074,64 +1083,128 @@ struct ath10k_ce_pipe *ath10k_ce_init(struct ath10k *ar,
 
        ret = ath10k_pci_wake(ar);
        if (ret)
-               return NULL;
+               return ret;
 
-       ce_state = ath10k_ce_init_state(ar, ce_id, attr);
-       if (!ce_state) {
-               ath10k_err("Failed to initialize CE state for ID: %d\n", ce_id);
-               goto out;
-       }
+       spin_lock_bh(&ar_pci->ce_lock);
+       ce_state->ar = ar;
+       ce_state->id = ce_id;
+       ce_state->ctrl_addr = ath10k_ce_base_address(ce_id);
+       ce_state->attr_flags = attr->flags;
+       ce_state->src_sz_max = attr->src_sz_max;
+       spin_unlock_bh(&ar_pci->ce_lock);
 
        if (attr->src_nentries) {
-               ret = ath10k_ce_init_src_ring(ar, ce_id, ce_state, attr);
+               ret = ath10k_ce_init_src_ring(ar, ce_id, attr);
                if (ret) {
                        ath10k_err("Failed to initialize CE src ring for ID: %d (%d)\n",
                                   ce_id, ret);
-                       ath10k_ce_deinit(ce_state);
-                       ce_state = NULL;
                        goto out;
                }
        }
 
        if (attr->dest_nentries) {
-               ret = ath10k_ce_init_dest_ring(ar, ce_id, ce_state, attr);
+               ret = ath10k_ce_init_dest_ring(ar, ce_id, attr);
                if (ret) {
                        ath10k_err("Failed to initialize CE dest ring for ID: %d (%d)\n",
                                   ce_id, ret);
-                       ath10k_ce_deinit(ce_state);
-                       ce_state = NULL;
                        goto out;
                }
        }
 
 out:
        ath10k_pci_sleep(ar);
-       return ce_state;
+       return ret;
 }
 
-void ath10k_ce_deinit(struct ath10k_ce_pipe *ce_state)
+static void ath10k_ce_deinit_src_ring(struct ath10k *ar, unsigned int ce_id)
+{
+       u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+
+       ath10k_ce_src_ring_base_addr_set(ar, ctrl_addr, 0);
+       ath10k_ce_src_ring_size_set(ar, ctrl_addr, 0);
+       ath10k_ce_src_ring_dmax_set(ar, ctrl_addr, 0);
+       ath10k_ce_src_ring_highmark_set(ar, ctrl_addr, 0);
+}
+
+static void ath10k_ce_deinit_dest_ring(struct ath10k *ar, unsigned int ce_id)
+{
+       u32 ctrl_addr = ath10k_ce_base_address(ce_id);
+
+       ath10k_ce_dest_ring_base_addr_set(ar, ctrl_addr, 0);
+       ath10k_ce_dest_ring_size_set(ar, ctrl_addr, 0);
+       ath10k_ce_dest_ring_highmark_set(ar, ctrl_addr, 0);
+}
+
+void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id)
+{
+       int ret;
+
+       ret = ath10k_pci_wake(ar);
+       if (ret)
+               return;
+
+       ath10k_ce_deinit_src_ring(ar, ce_id);
+       ath10k_ce_deinit_dest_ring(ar, ce_id);
+
+       ath10k_pci_sleep(ar);
+}
+
+int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
+                        const struct ce_attr *attr)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
+       int ret;
+
+       if (attr->src_nentries) {
+               ce_state->src_ring = ath10k_ce_alloc_src_ring(ar, ce_id, attr);
+               if (IS_ERR(ce_state->src_ring)) {
+                       ret = PTR_ERR(ce_state->src_ring);
+                       ath10k_err("failed to allocate copy engine source ring %d: %d\n",
+                                  ce_id, ret);
+                       ce_state->src_ring = NULL;
+                       return ret;
+               }
+       }
+
+       if (attr->dest_nentries) {
+               ce_state->dest_ring = ath10k_ce_alloc_dest_ring(ar, ce_id,
+                                                               attr);
+               if (IS_ERR(ce_state->dest_ring)) {
+                       ret = PTR_ERR(ce_state->dest_ring);
+                       ath10k_err("failed to allocate copy engine destination ring %d: %d\n",
+                                  ce_id, ret);
+                       ce_state->dest_ring = NULL;
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+void ath10k_ce_free_pipe(struct ath10k *ar, int ce_id)
 {
-       struct ath10k *ar = ce_state->ar;
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
 
        if (ce_state->src_ring) {
                kfree(ce_state->src_ring->shadow_base_unaligned);
-               pci_free_consistent(ar_pci->pdev,
-                                   (ce_state->src_ring->nentries *
-                                    sizeof(struct ce_desc) +
-                                    CE_DESC_RING_ALIGN),
-                                   ce_state->src_ring->base_addr_owner_space,
-                                   ce_state->src_ring->base_addr_ce_space);
+               dma_free_coherent(ar->dev,
+                                 (ce_state->src_ring->nentries *
+                                  sizeof(struct ce_desc) +
+                                  CE_DESC_RING_ALIGN),
+                                 ce_state->src_ring->base_addr_owner_space,
+                                 ce_state->src_ring->base_addr_ce_space);
                kfree(ce_state->src_ring);
        }
 
        if (ce_state->dest_ring) {
-               pci_free_consistent(ar_pci->pdev,
-                                   (ce_state->dest_ring->nentries *
-                                    sizeof(struct ce_desc) +
-                                    CE_DESC_RING_ALIGN),
-                                   ce_state->dest_ring->base_addr_owner_space,
-                                   ce_state->dest_ring->base_addr_ce_space);
+               dma_free_coherent(ar->dev,
+                                 (ce_state->dest_ring->nentries *
+                                  sizeof(struct ce_desc) +
+                                  CE_DESC_RING_ALIGN),
+                                 ce_state->dest_ring->base_addr_owner_space,
+                                 ce_state->dest_ring->base_addr_ce_space);
                kfree(ce_state->dest_ring);
        }
 
index 8eb7f99ed992277b0efb3e7ae4f971b8e4eb7557..7a5a36fc59c1ad19c51e2c69496660f75aeca073 100644 (file)
@@ -104,7 +104,8 @@ struct ath10k_ce_ring {
        void *shadow_base_unaligned;
        struct ce_desc *shadow_base;
 
-       void **per_transfer_context;
+       /* keep last */
+       void *per_transfer_context[0];
 };
 
 struct ath10k_ce_pipe {
@@ -159,6 +160,8 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
                          unsigned int transfer_id,
                          unsigned int flags);
 
+void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe);
+
 void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state,
                                void (*send_cb)(struct ath10k_ce_pipe *),
                                int disable_interrupts);
@@ -210,10 +213,12 @@ int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state,
 
 /*==================CE Engine Initialization=======================*/
 
-/* Initialize an instance of a CE */
-struct ath10k_ce_pipe *ath10k_ce_init(struct ath10k *ar,
-                               unsigned int ce_id,
-                               const struct ce_attr *attr);
+int ath10k_ce_init_pipe(struct ath10k *ar, unsigned int ce_id,
+                       const struct ce_attr *attr);
+void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id);
+int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
+                         const struct ce_attr *attr);
+void ath10k_ce_free_pipe(struct ath10k *ar, int ce_id);
 
 /*==================CE Engine Shutdown=======================*/
 /*
@@ -236,8 +241,6 @@ int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state,
                               unsigned int *nbytesp,
                               unsigned int *transfer_idp);
 
-void ath10k_ce_deinit(struct ath10k_ce_pipe *ce_state);
-
 /*==================CE Interrupt Handlers====================*/
 void ath10k_ce_per_engine_service_any(struct ath10k *ar);
 void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id);
index ebc5fc2ede75cbac75da3a2f789a946d4e1274ad..82017f56e6613a484b224a2d9d41f8aa796f16c9 100644 (file)
@@ -58,36 +58,6 @@ static void ath10k_send_suspend_complete(struct ath10k *ar)
        complete(&ar->target_suspend);
 }
 
-static int ath10k_init_connect_htc(struct ath10k *ar)
-{
-       int status;
-
-       status = ath10k_wmi_connect_htc_service(ar);
-       if (status)
-               goto conn_fail;
-
-       /* Start HTC */
-       status = ath10k_htc_start(&ar->htc);
-       if (status)
-               goto conn_fail;
-
-       /* Wait for WMI event to be ready */
-       status = ath10k_wmi_wait_for_service_ready(ar);
-       if (status <= 0) {
-               ath10k_warn("wmi service ready event not received");
-               status = -ETIMEDOUT;
-               goto timeout;
-       }
-
-       ath10k_dbg(ATH10K_DBG_BOOT, "boot wmi ready\n");
-       return 0;
-
-timeout:
-       ath10k_htc_stop(&ar->htc);
-conn_fail:
-       return status;
-}
-
 static int ath10k_init_configure_target(struct ath10k *ar)
 {
        u32 param_host;
@@ -249,30 +219,40 @@ static int ath10k_download_board_data(struct ath10k *ar)
 
 static int ath10k_download_and_run_otp(struct ath10k *ar)
 {
-       u32 address = ar->hw_params.patch_load_addr;
-       u32 exec_param;
+       u32 result, address = ar->hw_params.patch_load_addr;
        int ret;
 
        /* OTP is optional */
 
-       if (!ar->otp_data || !ar->otp_len)
+       if (!ar->otp_data || !ar->otp_len) {
+               ath10k_warn("Not running otp, calibration will be incorrect (otp-data %p otp_len %zd)!\n",
+                           ar->otp_data, ar->otp_len);
                return 0;
+       }
+
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot upload otp to 0x%x len %zd\n",
+                  address, ar->otp_len);
 
        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;
+               return ret;
        }
 
-       exec_param = 0;
-       ret = ath10k_bmi_execute(ar, address, &exec_param);
+       ret = ath10k_bmi_execute(ar, address, 0, &result);
        if (ret) {
                ath10k_err("could not execute otp (%d)\n", ret);
-               goto exit;
+               return ret;
        }
 
-exit:
-       return ret;
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot otp execute result %d\n", result);
+
+       if (result != 0) {
+               ath10k_err("otp calibration failed: %d", result);
+               return -EINVAL;
+       }
+
+       return 0;
 }
 
 static int ath10k_download_fw(struct ath10k *ar)
@@ -389,8 +369,8 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name)
        /* 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));
+               ath10k_err("could not fetch firmware file '%s/%s': %ld\n",
+                          ar->hw_params.fw.dir, name, PTR_ERR(ar->firmware));
                return PTR_ERR(ar->firmware);
        }
 
@@ -401,14 +381,14 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name)
        magic_len = strlen(ATH10K_FIRMWARE_MAGIC) + 1;
 
        if (len < magic_len) {
-               ath10k_err("firmware image too small to contain magic: %zu\n",
-                          len);
+               ath10k_err("firmware file '%s/%s' too small to contain magic: %zu\n",
+                          ar->hw_params.fw.dir, name, len);
                ret = -EINVAL;
                goto err;
        }
 
        if (memcmp(data, ATH10K_FIRMWARE_MAGIC, magic_len) != 0) {
-               ath10k_err("Invalid firmware magic\n");
+               ath10k_err("invalid firmware magic\n");
                ret = -EINVAL;
                goto err;
        }
@@ -430,7 +410,7 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name)
                data += sizeof(*hdr);
 
                if (len < ie_len) {
-                       ath10k_err("Invalid length for FW IE %d (%zu < %zu)\n",
+                       ath10k_err("invalid length for FW IE %d (%zu < %zu)\n",
                                   ie_id, len, ie_len);
                        ret = -EINVAL;
                        goto err;
@@ -513,8 +493,8 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name)
        }
 
        if (!ar->firmware_data || !ar->firmware_len) {
-               ath10k_warn("No ATH10K_FW_IE_FW_IMAGE found from %s, skipping\n",
-                           name);
+               ath10k_warn("No ATH10K_FW_IE_FW_IMAGE found from '%s/%s', skipping\n",
+                           ar->hw_params.fw.dir, name);
                ret = -ENOMEDIUM;
                goto err;
        }
@@ -531,7 +511,9 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name)
                                         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);
+               ath10k_err("could not fetch board data '%s/%s' (%d)\n",
+                          ar->hw_params.fw.dir, ar->hw_params.fw.board,
+                          ret);
                goto err;
        }
 
@@ -549,19 +531,21 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
 {
        int ret;
 
+       ar->fw_api = 2;
+       ath10k_dbg(ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api);
+
        ret = ath10k_core_fetch_firmware_api_n(ar, ATH10K_FW_API2_FILE);
-       if (ret == 0) {
-               ar->fw_api = 2;
-               goto out;
-       }
+       if (ret == 0)
+               goto success;
+
+       ar->fw_api = 1;
+       ath10k_dbg(ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api);
 
        ret = ath10k_core_fetch_firmware_api_1(ar);
        if (ret)
                return ret;
 
-       ar->fw_api = 1;
-
-out:
+success:
        ath10k_dbg(ATH10K_DBG_BOOT, "using fw api %d\n", ar->fw_api);
 
        return 0;
@@ -572,16 +556,22 @@ static int ath10k_init_download_firmware(struct ath10k *ar)
        int ret;
 
        ret = ath10k_download_board_data(ar);
-       if (ret)
+       if (ret) {
+               ath10k_err("failed to download board data: %d\n", ret);
                return ret;
+       }
 
        ret = ath10k_download_and_run_otp(ar);
-       if (ret)
+       if (ret) {
+               ath10k_err("failed to run otp: %d\n", ret);
                return ret;
+       }
 
        ret = ath10k_download_fw(ar);
-       if (ret)
+       if (ret) {
+               ath10k_err("failed to download firmware: %d\n", ret);
                return ret;
+       }
 
        return ret;
 }
@@ -660,8 +650,9 @@ static void ath10k_core_restart(struct work_struct *work)
 
        switch (ar->state) {
        case ATH10K_STATE_ON:
-               ath10k_halt(ar);
                ar->state = ATH10K_STATE_RESTARTING;
+               del_timer_sync(&ar->scan.timeout);
+               ath10k_reset_scan((unsigned long)ar);
                ieee80211_restart_hw(ar->hw);
                break;
        case ATH10K_STATE_OFF:
@@ -670,6 +661,8 @@ static void ath10k_core_restart(struct work_struct *work)
                ath10k_warn("cannot restart a device that hasn't been started\n");
                break;
        case ATH10K_STATE_RESTARTING:
+               /* hw restart might be requested from multiple places */
+               break;
        case ATH10K_STATE_RESTARTED:
                ar->state = ATH10K_STATE_WEDGED;
                /* fall through */
@@ -681,70 +674,6 @@ static void ath10k_core_restart(struct work_struct *work)
        mutex_unlock(&ar->conf_mutex);
 }
 
-struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
-                                 const struct ath10k_hif_ops *hif_ops)
-{
-       struct ath10k *ar;
-
-       ar = ath10k_mac_create();
-       if (!ar)
-               return NULL;
-
-       ar->ath_common.priv = ar;
-       ar->ath_common.hw = ar->hw;
-
-       ar->p2p = !!ath10k_p2p;
-       ar->dev = dev;
-
-       ar->hif.priv = hif_priv;
-       ar->hif.ops = hif_ops;
-
-       init_completion(&ar->scan.started);
-       init_completion(&ar->scan.completed);
-       init_completion(&ar->scan.on_channel);
-       init_completion(&ar->target_suspend);
-
-       init_completion(&ar->install_key_done);
-       init_completion(&ar->vdev_setup_done);
-
-       setup_timer(&ar->scan.timeout, ath10k_reset_scan, (unsigned long)ar);
-
-       ar->workqueue = create_singlethread_workqueue("ath10k_wq");
-       if (!ar->workqueue)
-               goto err_wq;
-
-       mutex_init(&ar->conf_mutex);
-       spin_lock_init(&ar->data_lock);
-
-       INIT_LIST_HEAD(&ar->peers);
-       init_waitqueue_head(&ar->peer_mapping_wq);
-
-       init_completion(&ar->offchan_tx_completed);
-       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_WORK(&ar->restart_work, ath10k_core_restart);
-
-       return ar;
-
-err_wq:
-       ath10k_mac_destroy(ar);
-       return NULL;
-}
-EXPORT_SYMBOL(ath10k_core_create);
-
-void ath10k_core_destroy(struct ath10k *ar)
-{
-       flush_workqueue(ar->workqueue);
-       destroy_workqueue(ar->workqueue);
-
-       ath10k_mac_destroy(ar);
-}
-EXPORT_SYMBOL(ath10k_core_destroy);
-
 int ath10k_core_start(struct ath10k *ar)
 {
        int status;
@@ -785,10 +714,28 @@ int ath10k_core_start(struct ath10k *ar)
                goto err;
        }
 
+       status = ath10k_htt_init(ar);
+       if (status) {
+               ath10k_err("failed to init htt: %d\n", status);
+               goto err_wmi_detach;
+       }
+
+       status = ath10k_htt_tx_alloc(&ar->htt);
+       if (status) {
+               ath10k_err("failed to alloc htt tx: %d\n", status);
+               goto err_wmi_detach;
+       }
+
+       status = ath10k_htt_rx_alloc(&ar->htt);
+       if (status) {
+               ath10k_err("failed to alloc htt rx: %d\n", status);
+               goto err_htt_tx_detach;
+       }
+
        status = ath10k_hif_start(ar);
        if (status) {
                ath10k_err("could not start HIF: %d\n", status);
-               goto err_wmi_detach;
+               goto err_htt_rx_detach;
        }
 
        status = ath10k_htc_wait_target(&ar->htc);
@@ -797,15 +744,30 @@ int ath10k_core_start(struct ath10k *ar)
                goto err_hif_stop;
        }
 
-       status = ath10k_htt_attach(ar);
+       status = ath10k_htt_connect(&ar->htt);
        if (status) {
-               ath10k_err("could not attach htt (%d)\n", status);
+               ath10k_err("failed to connect htt (%d)\n", status);
                goto err_hif_stop;
        }
 
-       status = ath10k_init_connect_htc(ar);
-       if (status)
-               goto err_htt_detach;
+       status = ath10k_wmi_connect(ar);
+       if (status) {
+               ath10k_err("could not connect wmi: %d\n", status);
+               goto err_hif_stop;
+       }
+
+       status = ath10k_htc_start(&ar->htc);
+       if (status) {
+               ath10k_err("failed to start htc: %d\n", status);
+               goto err_hif_stop;
+       }
+
+       status = ath10k_wmi_wait_for_service_ready(ar);
+       if (status <= 0) {
+               ath10k_warn("wmi service ready event not received");
+               status = -ETIMEDOUT;
+               goto err_htc_stop;
+       }
 
        ath10k_dbg(ATH10K_DBG_BOOT, "firmware %s booted\n",
                   ar->hw->wiphy->fw_version);
@@ -813,31 +775,36 @@ int ath10k_core_start(struct ath10k *ar)
        status = ath10k_wmi_cmd_init(ar);
        if (status) {
                ath10k_err("could not send WMI init command (%d)\n", status);
-               goto err_disconnect_htc;
+               goto err_htc_stop;
        }
 
        status = ath10k_wmi_wait_for_unified_ready(ar);
        if (status <= 0) {
                ath10k_err("wmi unified ready event not received\n");
                status = -ETIMEDOUT;
-               goto err_disconnect_htc;
+               goto err_htc_stop;
        }
 
-       status = ath10k_htt_attach_target(&ar->htt);
-       if (status)
-               goto err_disconnect_htc;
+       status = ath10k_htt_setup(&ar->htt);
+       if (status) {
+               ath10k_err("failed to setup htt: %d\n", status);
+               goto err_htc_stop;
+       }
 
        status = ath10k_debug_start(ar);
        if (status)
-               goto err_disconnect_htc;
+               goto err_htc_stop;
 
        ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1;
        INIT_LIST_HEAD(&ar->arvifs);
 
        if (!test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags))
-               ath10k_info("%s (0x%x) fw %s api %d htt %d.%d\n",
-                           ar->hw_params.name, ar->target_version,
-                           ar->hw->wiphy->fw_version, ar->fw_api,
+               ath10k_info("%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d\n",
+                           ar->hw_params.name,
+                           ar->target_version,
+                           ar->chip_id,
+                           ar->hw->wiphy->fw_version,
+                           ar->fw_api,
                            ar->htt.target_version_major,
                            ar->htt.target_version_minor);
 
@@ -845,12 +812,14 @@ int ath10k_core_start(struct ath10k *ar)
 
        return 0;
 
-err_disconnect_htc:
+err_htc_stop:
        ath10k_htc_stop(&ar->htc);
-err_htt_detach:
-       ath10k_htt_detach(&ar->htt);
 err_hif_stop:
        ath10k_hif_stop(ar);
+err_htt_rx_detach:
+       ath10k_htt_rx_free(&ar->htt);
+err_htt_tx_detach:
+       ath10k_htt_tx_free(&ar->htt);
 err_wmi_detach:
        ath10k_wmi_detach(ar);
 err:
@@ -885,10 +854,14 @@ void ath10k_core_stop(struct ath10k *ar)
        lockdep_assert_held(&ar->conf_mutex);
 
        /* try to suspend target */
-       ath10k_wait_for_suspend(ar, WMI_PDEV_SUSPEND_AND_DISABLE_INTR);
+       if (ar->state != ATH10K_STATE_RESTARTING)
+               ath10k_wait_for_suspend(ar, WMI_PDEV_SUSPEND_AND_DISABLE_INTR);
+
        ath10k_debug_stop(ar);
        ath10k_htc_stop(&ar->htc);
-       ath10k_htt_detach(&ar->htt);
+       ath10k_hif_stop(ar);
+       ath10k_htt_tx_free(&ar->htt);
+       ath10k_htt_rx_free(&ar->htt);
        ath10k_wmi_detach(ar);
 }
 EXPORT_SYMBOL(ath10k_core_stop);
@@ -980,22 +953,15 @@ static int ath10k_core_check_chip_id(struct ath10k *ar)
        return 0;
 }
 
-int ath10k_core_register(struct ath10k *ar, u32 chip_id)
+static void ath10k_core_register_work(struct work_struct *work)
 {
+       struct ath10k *ar = container_of(work, struct ath10k, register_work);
        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);
-               return status;
+               goto err;
        }
 
        status = ath10k_mac_register(ar);
@@ -1010,18 +976,43 @@ int ath10k_core_register(struct ath10k *ar, u32 chip_id)
                goto err_unregister_mac;
        }
 
-       return 0;
+       set_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags);
+       return;
 
 err_unregister_mac:
        ath10k_mac_unregister(ar);
 err_release_fw:
        ath10k_core_free_firmware_files(ar);
-       return status;
+err:
+       device_release_driver(ar->dev);
+       return;
+}
+
+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;
+       }
+
+       queue_work(ar->workqueue, &ar->register_work);
+
+       return 0;
 }
 EXPORT_SYMBOL(ath10k_core_register);
 
 void ath10k_core_unregister(struct ath10k *ar)
 {
+       cancel_work_sync(&ar->register_work);
+
+       if (!test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags))
+               return;
+
        /* We must unregister from mac80211 before we stop HTC and HIF.
         * Otherwise we will fail to submit commands to FW and mac80211 will be
         * unhappy about callback failures. */
@@ -1033,6 +1024,71 @@ void ath10k_core_unregister(struct ath10k *ar)
 }
 EXPORT_SYMBOL(ath10k_core_unregister);
 
+struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
+                                 const struct ath10k_hif_ops *hif_ops)
+{
+       struct ath10k *ar;
+
+       ar = ath10k_mac_create();
+       if (!ar)
+               return NULL;
+
+       ar->ath_common.priv = ar;
+       ar->ath_common.hw = ar->hw;
+
+       ar->p2p = !!ath10k_p2p;
+       ar->dev = dev;
+
+       ar->hif.priv = hif_priv;
+       ar->hif.ops = hif_ops;
+
+       init_completion(&ar->scan.started);
+       init_completion(&ar->scan.completed);
+       init_completion(&ar->scan.on_channel);
+       init_completion(&ar->target_suspend);
+
+       init_completion(&ar->install_key_done);
+       init_completion(&ar->vdev_setup_done);
+
+       setup_timer(&ar->scan.timeout, ath10k_reset_scan, (unsigned long)ar);
+
+       ar->workqueue = create_singlethread_workqueue("ath10k_wq");
+       if (!ar->workqueue)
+               goto err_wq;
+
+       mutex_init(&ar->conf_mutex);
+       spin_lock_init(&ar->data_lock);
+
+       INIT_LIST_HEAD(&ar->peers);
+       init_waitqueue_head(&ar->peer_mapping_wq);
+
+       init_completion(&ar->offchan_tx_completed);
+       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_WORK(&ar->register_work, ath10k_core_register_work);
+       INIT_WORK(&ar->restart_work, ath10k_core_restart);
+
+       return ar;
+
+err_wq:
+       ath10k_mac_destroy(ar);
+       return NULL;
+}
+EXPORT_SYMBOL(ath10k_core_create);
+
+void ath10k_core_destroy(struct ath10k *ar)
+{
+       flush_workqueue(ar->workqueue);
+       destroy_workqueue(ar->workqueue);
+
+       ath10k_mac_destroy(ar);
+}
+EXPORT_SYMBOL(ath10k_core_destroy);
+
 MODULE_AUTHOR("Qualcomm Atheros");
 MODULE_DESCRIPTION("Core module for QCA988X PCIe devices.");
 MODULE_LICENSE("Dual BSD/GPL");
index 0e71979d837cf90888c74e4d85035c2a4d6fd4ef..68ceef61933db6b769122ab2500fa524619ac465 100644 (file)
@@ -119,6 +119,7 @@ struct ath10k_peer_stat {
        u8 peer_macaddr[ETH_ALEN];
        u32 peer_rssi;
        u32 peer_tx_rate;
+       u32 peer_rx_rate; /* 10x only */
 };
 
 struct ath10k_target_stats {
@@ -130,6 +131,12 @@ struct ath10k_target_stats {
        u32 cycle_count;
        u32 phy_err_count;
        u32 chan_tx_power;
+       u32 ack_rx_bad;
+       u32 rts_bad;
+       u32 rts_good;
+       u32 fcs_bad;
+       u32 no_beacons;
+       u32 mib_int_count;
 
        /* PDEV TX stats */
        s32 comp_queued;
@@ -260,6 +267,8 @@ struct ath10k_vif {
        u8 fixed_rate;
        u8 fixed_nss;
        u8 force_sgi;
+       bool use_cts_prot;
+       int num_legacy_stations;
 };
 
 struct ath10k_vif_iter {
@@ -326,6 +335,7 @@ enum ath10k_dev_flags {
        /* Indicates that ath10k device is during CAC phase of DFS */
        ATH10K_CAC_RUNNING,
        ATH10K_FLAG_FIRST_BOOT_DONE,
+       ATH10K_FLAG_CORE_REGISTERED,
 };
 
 struct ath10k {
@@ -419,13 +429,24 @@ struct ath10k {
        struct cfg80211_chan_def chandef;
 
        int free_vdev_map;
+       bool promisc;
+       bool monitor;
        int monitor_vdev_id;
-       bool monitor_enabled;
-       bool monitor_present;
+       bool monitor_started;
        unsigned int filter_flags;
        unsigned long dev_flags;
        u32 dfs_block_radar_events;
 
+       /* protected by conf_mutex */
+       bool radar_enabled;
+       int num_started_vdevs;
+
+       /* Protected by conf-mutex */
+       u8 supp_tx_chainmask;
+       u8 supp_rx_chainmask;
+       u8 cfg_tx_chainmask;
+       u8 cfg_rx_chainmask;
+
        struct wmi_pdev_set_wmm_params_arg wmm_params;
        struct completion install_key_done;
 
@@ -456,6 +477,7 @@ struct ath10k {
 
        enum ath10k_state state;
 
+       struct work_struct register_work;
        struct work_struct restart_work;
 
        /* cycle count is reported twice for each visited channel during scan.
index 6debd281350aeb840978606212655fba6d6fb7a3..1b7ff4ba122ce42af61265eae30d8fb98d97201e 100644 (file)
@@ -161,7 +161,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
        u8 *tmp = ev->data;
        struct ath10k_target_stats *stats;
        int num_pdev_stats, num_vdev_stats, num_peer_stats;
-       struct wmi_pdev_stats *ps;
+       struct wmi_pdev_stats_10x *ps;
        int i;
 
        spin_lock_bh(&ar->data_lock);
@@ -173,7 +173,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
        num_peer_stats = __le32_to_cpu(ev->num_peer_stats); /* 0 or max peers */
 
        if (num_pdev_stats) {
-               ps = (struct wmi_pdev_stats *)tmp;
+               ps = (struct wmi_pdev_stats_10x *)tmp;
 
                stats->ch_noise_floor = __le32_to_cpu(ps->chan_nf);
                stats->tx_frame_count = __le32_to_cpu(ps->tx_frame_count);
@@ -228,7 +228,18 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
                stats->phy_err_drop = __le32_to_cpu(ps->wal.rx.phy_err_drop);
                stats->mpdu_errs = __le32_to_cpu(ps->wal.rx.mpdu_errs);
 
-               tmp += sizeof(struct wmi_pdev_stats);
+               if (test_bit(ATH10K_FW_FEATURE_WMI_10X,
+                            ar->fw_features)) {
+                       stats->ack_rx_bad = __le32_to_cpu(ps->ack_rx_bad);
+                       stats->rts_bad = __le32_to_cpu(ps->rts_bad);
+                       stats->rts_good = __le32_to_cpu(ps->rts_good);
+                       stats->fcs_bad = __le32_to_cpu(ps->fcs_bad);
+                       stats->no_beacons = __le32_to_cpu(ps->no_beacons);
+                       stats->mib_int_count = __le32_to_cpu(ps->mib_int_count);
+                       tmp += sizeof(struct wmi_pdev_stats_10x);
+               } else {
+                       tmp += sizeof(struct wmi_pdev_stats_old);
+               }
        }
 
        /* 0 or max vdevs */
@@ -243,22 +254,29 @@ void ath10k_debug_read_target_stats(struct ath10k *ar,
        }
 
        if (num_peer_stats) {
-               struct wmi_peer_stats *peer_stats;
+               struct wmi_peer_stats_10x *peer_stats;
                struct ath10k_peer_stat *s;
 
                stats->peers = num_peer_stats;
 
                for (i = 0; i < num_peer_stats; i++) {
-                       peer_stats = (struct wmi_peer_stats *)tmp;
+                       peer_stats = (struct wmi_peer_stats_10x *)tmp;
                        s = &stats->peer_stat[i];
 
-                       WMI_MAC_ADDR_TO_CHAR_ARRAY(&peer_stats->peer_macaddr,
-                                                  s->peer_macaddr);
+                       memcpy(s->peer_macaddr, &peer_stats->peer_macaddr.addr,
+                              ETH_ALEN);
                        s->peer_rssi = __le32_to_cpu(peer_stats->peer_rssi);
                        s->peer_tx_rate =
                                __le32_to_cpu(peer_stats->peer_tx_rate);
-
-                       tmp += sizeof(struct wmi_peer_stats);
+                       if (test_bit(ATH10K_FW_FEATURE_WMI_10X,
+                                    ar->fw_features)) {
+                               s->peer_rx_rate =
+                                       __le32_to_cpu(peer_stats->peer_rx_rate);
+                               tmp += sizeof(struct wmi_peer_stats_10x);
+
+                       } else {
+                               tmp += sizeof(struct wmi_peer_stats_old);
+                       }
                }
        }
 
@@ -272,7 +290,7 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
        struct ath10k *ar = file->private_data;
        struct ath10k_target_stats *fw_stats;
        char *buf = NULL;
-       unsigned int len = 0, buf_len = 2500;
+       unsigned int len = 0, buf_len = 8000;
        ssize_t ret_cnt = 0;
        long left;
        int i;
@@ -320,6 +338,16 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
                         "Cycle count", fw_stats->cycle_count);
        len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
                         "PHY error count", fw_stats->phy_err_count);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "RTS bad count", fw_stats->rts_bad);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "RTS good count", fw_stats->rts_good);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "FCS bad count", fw_stats->fcs_bad);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "No beacon count", fw_stats->no_beacons);
+       len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+                        "MIB int count", fw_stats->mib_int_count);
 
        len += scnprintf(buf + len, buf_len - len, "\n");
        len += scnprintf(buf + len, buf_len - len, "%30s\n",
@@ -411,8 +439,8 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
                         "MPDU errors (FCS, MIC, ENC)", fw_stats->mpdu_errs);
 
        len += scnprintf(buf + len, buf_len - len, "\n");
-       len += scnprintf(buf + len, buf_len - len, "%30s\n",
-                        "ath10k PEER stats");
+       len += scnprintf(buf + len, buf_len - len, "%30s (%d)\n",
+                        "ath10k PEER stats", fw_stats->peers);
        len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
                                 "=================");
 
@@ -425,6 +453,9 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
                len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
                                 "Peer TX rate",
                                 fw_stats->peer_stat[i].peer_tx_rate);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "Peer RX rate",
+                                fw_stats->peer_stat[i].peer_rx_rate);
                len += scnprintf(buf + len, buf_len - len, "\n");
        }
        spin_unlock_bh(&ar->data_lock);
@@ -451,27 +482,37 @@ static ssize_t ath10k_read_simulate_fw_crash(struct file *file,
                                             char __user *user_buf,
                                             size_t count, loff_t *ppos)
 {
-       const char buf[] = "To simulate firmware crash write the keyword"
-                          " `crash` to this file.\nThis will force firmware"
-                          " to report a crash to the host system.\n";
+       const char buf[] = "To simulate firmware crash write one of the"
+                          " keywords to this file:\n `soft` - this will send"
+                          " WMI_FORCE_FW_HANG_ASSERT to firmware if FW"
+                          " supports that command.\n `hard` - this will send"
+                          " to firmware command with illegal parameters"
+                          " causing firmware crash.\n";
+
        return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
 }
 
+/* Simulate firmware crash:
+ * 'soft': Call wmi command causing firmware hang. This firmware hang is
+ * recoverable by warm firmware reset.
+ * 'hard': Force firmware crash by setting any vdev parameter for not allowed
+ * vdev id. This is hard firmware crash because it is recoverable only by cold
+ * firmware reset.
+ */
 static ssize_t ath10k_write_simulate_fw_crash(struct file *file,
                                              const char __user *user_buf,
                                              size_t count, loff_t *ppos)
 {
        struct ath10k *ar = file->private_data;
-       char buf[32] = {};
+       char buf[32];
        int ret;
 
        mutex_lock(&ar->conf_mutex);
 
        simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
-       if (strcmp(buf, "crash") && strcmp(buf, "crash\n")) {
-               ret = -EINVAL;
-               goto exit;
-       }
+
+       /* make sure that buf is null terminated */
+       buf[sizeof(buf) - 1] = 0;
 
        if (ar->state != ATH10K_STATE_ON &&
            ar->state != ATH10K_STATE_RESTARTED) {
@@ -479,14 +520,30 @@ static ssize_t ath10k_write_simulate_fw_crash(struct file *file,
                goto exit;
        }
 
-       ath10k_info("simulating firmware crash\n");
+       /* drop the possible '\n' from the end */
+       if (buf[count - 1] == '\n') {
+               buf[count - 1] = 0;
+               count--;
+       }
 
-       ret = ath10k_wmi_force_fw_hang(ar, WMI_FORCE_FW_HANG_ASSERT, 0);
-       if (ret)
-               ath10k_warn("failed to force fw hang (%d)\n", ret);
+       if (!strcmp(buf, "soft")) {
+               ath10k_info("simulating soft firmware crash\n");
+               ret = ath10k_wmi_force_fw_hang(ar, WMI_FORCE_FW_HANG_ASSERT, 0);
+       } else if (!strcmp(buf, "hard")) {
+               ath10k_info("simulating hard firmware crash\n");
+               ret = ath10k_wmi_vdev_set_param(ar, TARGET_NUM_VDEVS + 1,
+                                       ar->wmi.vdev_param->rts_threshold, 0);
+       } else {
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       if (ret) {
+               ath10k_warn("failed to simulate firmware crash: %d\n", ret);
+               goto exit;
+       }
 
-       if (ret == 0)
-               ret = count;
+       ret = count;
 
 exit:
        mutex_unlock(&ar->conf_mutex);
index 7f1bccd3597f1bb2a3b40d5a482f308e99e9e27c..e493db4b4a41fade30415e85c3dede5d4ea3f584 100644 (file)
@@ -157,6 +157,9 @@ int ath10k_htc_send(struct ath10k_htc *htc,
                        goto err_pull;
                }
                ep->tx_credits -= credits;
+               ath10k_dbg(ATH10K_DBG_HTC,
+                          "htc ep %d consumed %d credits (total %d)\n",
+                          eid, credits, ep->tx_credits);
                spin_unlock_bh(&htc->tx_lock);
        }
 
@@ -185,6 +188,9 @@ int ath10k_htc_send(struct ath10k_htc *htc,
        if (ep->tx_credit_flow_enabled) {
                spin_lock_bh(&htc->tx_lock);
                ep->tx_credits += credits;
+               ath10k_dbg(ATH10K_DBG_HTC,
+                          "htc ep %d reverted %d credits back (total %d)\n",
+                          eid, credits, ep->tx_credits);
                spin_unlock_bh(&htc->tx_lock);
 
                if (ep->ep_ops.ep_tx_credits)
@@ -234,12 +240,12 @@ ath10k_htc_process_credit_report(struct ath10k_htc *htc,
                if (report->eid >= ATH10K_HTC_EP_COUNT)
                        break;
 
-               ath10k_dbg(ATH10K_DBG_HTC, "ep %d got %d credits\n",
-                          report->eid, report->credits);
-
                ep = &htc->endpoint[report->eid];
                ep->tx_credits += report->credits;
 
+               ath10k_dbg(ATH10K_DBG_HTC, "htc ep %d got %d credits (total %d)\n",
+                          report->eid, report->credits, ep->tx_credits);
+
                if (ep->ep_ops.ep_tx_credits) {
                        spin_unlock_bh(&htc->tx_lock);
                        ep->ep_ops.ep_tx_credits(htc->ar);
@@ -824,17 +830,11 @@ int ath10k_htc_start(struct ath10k_htc *htc)
        return 0;
 }
 
-/*
- * stop HTC communications, i.e. stop interrupt reception, and flush all
- * queued buffers
- */
 void ath10k_htc_stop(struct ath10k_htc *htc)
 {
        spin_lock_bh(&htc->tx_lock);
        htc->stopped = true;
        spin_unlock_bh(&htc->tx_lock);
-
-       ath10k_hif_stop(htc->ar);
 }
 
 /* registered target arrival callback from the HIF layer */
index 69697af59ce06f9ab070cf01562e240607c9fc93..19c12cc8d66317ebbb8123b4a54b6654c5705c26 100644 (file)
@@ -22,7 +22,7 @@
 #include "core.h"
 #include "debug.h"
 
-static int ath10k_htt_htc_attach(struct ath10k_htt *htt)
+int ath10k_htt_connect(struct ath10k_htt *htt)
 {
        struct ath10k_htc_svc_conn_req conn_req;
        struct ath10k_htc_svc_conn_resp conn_resp;
@@ -48,38 +48,13 @@ static int ath10k_htt_htc_attach(struct ath10k_htt *htt)
        return 0;
 }
 
-int ath10k_htt_attach(struct ath10k *ar)
+int ath10k_htt_init(struct ath10k *ar)
 {
        struct ath10k_htt *htt = &ar->htt;
-       int ret;
 
        htt->ar = ar;
        htt->max_throughput_mbps = 800;
 
-       /*
-        * Connect to HTC service.
-        * This has to be done before calling ath10k_htt_rx_attach,
-        * since ath10k_htt_rx_attach involves sending a rx ring configure
-        * message to the target.
-        */
-       ret = ath10k_htt_htc_attach(htt);
-       if (ret) {
-               ath10k_err("could not attach htt htc (%d)\n", ret);
-               goto err_htc_attach;
-       }
-
-       ret = ath10k_htt_tx_attach(htt);
-       if (ret) {
-               ath10k_err("could not attach htt tx (%d)\n", ret);
-               goto err_htc_attach;
-       }
-
-       ret = ath10k_htt_rx_attach(htt);
-       if (ret) {
-               ath10k_err("could not attach htt rx (%d)\n", ret);
-               goto err_rx_attach;
-       }
-
        /*
         * Prefetch enough data to satisfy target
         * classification engine.
@@ -93,11 +68,6 @@ int ath10k_htt_attach(struct ath10k *ar)
                2; /* ip4 dscp or ip6 priority */
 
        return 0;
-
-err_rx_attach:
-       ath10k_htt_tx_detach(htt);
-err_htc_attach:
-       return ret;
 }
 
 #define HTT_TARGET_VERSION_TIMEOUT_HZ (3*HZ)
@@ -117,7 +87,7 @@ static int ath10k_htt_verify_version(struct ath10k_htt *htt)
        return 0;
 }
 
-int ath10k_htt_attach_target(struct ath10k_htt *htt)
+int ath10k_htt_setup(struct ath10k_htt *htt)
 {
        int status;
 
@@ -140,9 +110,3 @@ int ath10k_htt_attach_target(struct ath10k_htt *htt)
 
        return ath10k_htt_send_rx_ring_cfg_ll(htt);
 }
-
-void ath10k_htt_detach(struct ath10k_htt *htt)
-{
-       ath10k_htt_rx_detach(htt);
-       ath10k_htt_tx_detach(htt);
-}
index 654867fc1ae73bbd7a13cf4dc61f8ac89a0b7823..9a263462c79353fa4b6a3c646ff6d025087c9463 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/bug.h>
 #include <linux/interrupt.h>
 #include <linux/dmapool.h>
+#include <net/mac80211.h>
 
 #include "htc.h"
 #include "rx_desc.h"
@@ -1172,23 +1173,6 @@ struct htt_peer_unmap_event {
        u16 peer_id;
 };
 
-struct htt_rx_info {
-       struct sk_buff *skb;
-       enum htt_rx_mpdu_status status;
-       enum htt_rx_mpdu_encrypt_type encrypt_type;
-       s8 signal;
-       struct {
-               u8 info0;
-               u32 info1;
-               u32 info2;
-       } rate;
-
-       u32 tsf;
-       bool fcs_err;
-       bool amsdu_more;
-       bool mic_err;
-};
-
 struct ath10k_htt_txbuf {
        struct htt_data_tx_desc_frag frags[2];
        struct ath10k_htc_hdr htc_hdr;
@@ -1289,6 +1273,9 @@ struct ath10k_htt {
        struct tasklet_struct txrx_compl_task;
        struct sk_buff_head tx_compl_q;
        struct sk_buff_head rx_compl_q;
+
+       /* rx_status template */
+       struct ieee80211_rx_status rx_status;
 };
 
 #define RX_HTT_HDR_STATUS_LEN 64
@@ -1341,14 +1328,16 @@ struct htt_rx_desc {
 #define HTT_LOG2_MAX_CACHE_LINE_SIZE 7 /* 2^7 = 128 */
 #define HTT_MAX_CACHE_LINE_SIZE_MASK ((1 << HTT_LOG2_MAX_CACHE_LINE_SIZE) - 1)
 
-int ath10k_htt_attach(struct ath10k *ar);
-int ath10k_htt_attach_target(struct ath10k_htt *htt);
-void ath10k_htt_detach(struct ath10k_htt *htt);
+int ath10k_htt_connect(struct ath10k_htt *htt);
+int ath10k_htt_init(struct ath10k *ar);
+int ath10k_htt_setup(struct ath10k_htt *htt);
+
+int ath10k_htt_tx_alloc(struct ath10k_htt *htt);
+void ath10k_htt_tx_free(struct ath10k_htt *htt);
+
+int ath10k_htt_rx_alloc(struct ath10k_htt *htt);
+void ath10k_htt_rx_free(struct ath10k_htt *htt);
 
-int ath10k_htt_tx_attach(struct ath10k_htt *htt);
-void ath10k_htt_tx_detach(struct ath10k_htt *htt);
-int ath10k_htt_rx_attach(struct ath10k_htt *htt);
-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);
index cdcbe2de95f97d602cb086c301f0778aad5bc49c..6c102b1312ff955db686022aa76e1a7ccc6e42b3 100644 (file)
@@ -225,10 +225,26 @@ static void ath10k_htt_rx_ring_refill_retry(unsigned long arg)
        ath10k_htt_rx_msdu_buff_replenish(htt);
 }
 
-void ath10k_htt_rx_detach(struct ath10k_htt *htt)
+static void ath10k_htt_rx_ring_clean_up(struct ath10k_htt *htt)
 {
-       int sw_rd_idx = htt->rx_ring.sw_rd_idx.msdu_payld;
+       struct sk_buff *skb;
+       int i;
+
+       for (i = 0; i < htt->rx_ring.size; i++) {
+               skb = htt->rx_ring.netbufs_ring[i];
+               if (!skb)
+                       continue;
 
+               dma_unmap_single(htt->ar->dev, ATH10K_SKB_CB(skb)->paddr,
+                                skb->len + skb_tailroom(skb),
+                                DMA_FROM_DEVICE);
+               dev_kfree_skb_any(skb);
+               htt->rx_ring.netbufs_ring[i] = NULL;
+       }
+}
+
+void ath10k_htt_rx_free(struct ath10k_htt *htt)
+{
        del_timer_sync(&htt->rx_ring.refill_retry_timer);
        tasklet_kill(&htt->rx_replenish_task);
        tasklet_kill(&htt->txrx_compl_task);
@@ -236,18 +252,7 @@ void ath10k_htt_rx_detach(struct ath10k_htt *htt)
        skb_queue_purge(&htt->tx_compl_q);
        skb_queue_purge(&htt->rx_compl_q);
 
-       while (sw_rd_idx != __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr))) {
-               struct sk_buff *skb =
-                               htt->rx_ring.netbufs_ring[sw_rd_idx];
-               struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);
-
-               dma_unmap_single(htt->ar->dev, cb->paddr,
-                                skb->len + skb_tailroom(skb),
-                                DMA_FROM_DEVICE);
-               dev_kfree_skb_any(htt->rx_ring.netbufs_ring[sw_rd_idx]);
-               sw_rd_idx++;
-               sw_rd_idx &= htt->rx_ring.size_mask;
-       }
+       ath10k_htt_rx_ring_clean_up(htt);
 
        dma_free_coherent(htt->ar->dev,
                          (htt->rx_ring.size *
@@ -277,6 +282,7 @@ static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt)
 
        idx = htt->rx_ring.sw_rd_idx.msdu_payld;
        msdu = htt->rx_ring.netbufs_ring[idx];
+       htt->rx_ring.netbufs_ring[idx] = NULL;
 
        idx++;
        idx &= htt->rx_ring.size_mask;
@@ -297,6 +303,7 @@ static void ath10k_htt_rx_free_msdu_chain(struct sk_buff *skb)
        }
 }
 
+/* return: < 0 fatal error, 0 - non chained msdu, 1 chained msdu */
 static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
                                   u8 **fw_desc, int *fw_desc_len,
                                   struct sk_buff **head_msdu,
@@ -305,12 +312,13 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
        int msdu_len, msdu_chaining = 0;
        struct sk_buff *msdu;
        struct htt_rx_desc *rx_desc;
+       bool corrupted = false;
 
        lockdep_assert_held(&htt->rx_ring.lock);
 
        if (htt->rx_confused) {
                ath10k_warn("htt is confused. refusing rx\n");
-               return 0;
+               return -1;
        }
 
        msdu = *head_msdu = ath10k_htt_rx_netbuf_pop(htt);
@@ -398,7 +406,6 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
                msdu_len = MS(__le32_to_cpu(rx_desc->msdu_start.info0),
                              RX_MSDU_START_INFO0_MSDU_LENGTH);
                msdu_chained = rx_desc->frag_info.ring2_more_count;
-               msdu_chaining = msdu_chained;
 
                if (msdu_len_invalid)
                        msdu_len = 0;
@@ -426,11 +433,15 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
 
                        msdu->next = next;
                        msdu = next;
+                       msdu_chaining = 1;
                }
 
                last_msdu = __le32_to_cpu(rx_desc->msdu_end.info0) &
                                RX_MSDU_END_INFO0_LAST_MSDU;
 
+               if (msdu_chaining && !last_msdu)
+                       corrupted = true;
+
                if (last_msdu) {
                        msdu->next = NULL;
                        break;
@@ -442,6 +453,23 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
        }
        *tail_msdu = msdu;
 
+       if (*head_msdu == NULL)
+               msdu_chaining = -1;
+
+       /*
+        * Apparently FW sometimes reports weird chained MSDU sequences with
+        * more than one rx descriptor. This seems like a bug but needs more
+        * analyzing. For the time being fix it by dropping such sequences to
+        * avoid blowing up the host system.
+        */
+       if (corrupted) {
+               ath10k_warn("failed to pop chained msdus, dropping\n");
+               ath10k_htt_rx_free_msdu_chain(*head_msdu);
+               *head_msdu = NULL;
+               *tail_msdu = NULL;
+               msdu_chaining = -EINVAL;
+       }
+
        /*
         * Don't refill the ring yet.
         *
@@ -464,7 +492,7 @@ static void ath10k_htt_rx_replenish_task(unsigned long ptr)
        ath10k_htt_rx_msdu_buff_replenish(htt);
 }
 
-int ath10k_htt_rx_attach(struct ath10k_htt *htt)
+int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
 {
        dma_addr_t paddr;
        void *vaddr;
@@ -490,7 +518,7 @@ int ath10k_htt_rx_attach(struct ath10k_htt *htt)
        htt->rx_ring.fill_level = ath10k_htt_rx_ring_fill_level(htt);
 
        htt->rx_ring.netbufs_ring =
-               kmalloc(htt->rx_ring.size * sizeof(struct sk_buff *),
+               kzalloc(htt->rx_ring.size * sizeof(struct sk_buff *),
                        GFP_KERNEL);
        if (!htt->rx_ring.netbufs_ring)
                goto err_netbuf;
@@ -636,6 +664,203 @@ struct amsdu_subframe_hdr {
        __be16 len;
 } __packed;
 
+static const u8 rx_legacy_rate_idx[] = {
+       3,      /* 0x00  - 11Mbps  */
+       2,      /* 0x01  - 5.5Mbps */
+       1,      /* 0x02  - 2Mbps   */
+       0,      /* 0x03  - 1Mbps   */
+       3,      /* 0x04  - 11Mbps  */
+       2,      /* 0x05  - 5.5Mbps */
+       1,      /* 0x06  - 2Mbps   */
+       0,      /* 0x07  - 1Mbps   */
+       10,     /* 0x08  - 48Mbps  */
+       8,      /* 0x09  - 24Mbps  */
+       6,      /* 0x0A  - 12Mbps  */
+       4,      /* 0x0B  - 6Mbps   */
+       11,     /* 0x0C  - 54Mbps  */
+       9,      /* 0x0D  - 36Mbps  */
+       7,      /* 0x0E  - 18Mbps  */
+       5,      /* 0x0F  - 9Mbps   */
+};
+
+static void ath10k_htt_rx_h_rates(struct ath10k *ar,
+                                 enum ieee80211_band band,
+                                 u8 info0, u32 info1, u32 info2,
+                                 struct ieee80211_rx_status *status)
+{
+       u8 cck, rate, rate_idx, bw, sgi, mcs, nss;
+       u8 preamble = 0;
+
+       /* Check if valid fields */
+       if (!(info0 & HTT_RX_INDICATION_INFO0_START_VALID))
+               return;
+
+       preamble = MS(info1, HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE);
+
+       switch (preamble) {
+       case HTT_RX_LEGACY:
+               cck = info0 & HTT_RX_INDICATION_INFO0_LEGACY_RATE_CCK;
+               rate = MS(info0, HTT_RX_INDICATION_INFO0_LEGACY_RATE);
+               rate_idx = 0;
+
+               if (rate < 0x08 || rate > 0x0F)
+                       break;
+
+               switch (band) {
+               case IEEE80211_BAND_2GHZ:
+                       if (cck)
+                               rate &= ~BIT(3);
+                       rate_idx = rx_legacy_rate_idx[rate];
+                       break;
+               case IEEE80211_BAND_5GHZ:
+                       rate_idx = rx_legacy_rate_idx[rate];
+                       /* We are using same rate table registering
+                          HW - ath10k_rates[]. In case of 5GHz skip
+                          CCK rates, so -4 here */
+                       rate_idx -= 4;
+                       break;
+               default:
+                       break;
+               }
+
+               status->rate_idx = rate_idx;
+               break;
+       case HTT_RX_HT:
+       case HTT_RX_HT_WITH_TXBF:
+               /* HT-SIG - Table 20-11 in info1 and info2 */
+               mcs = info1 & 0x1F;
+               nss = mcs >> 3;
+               bw = (info1 >> 7) & 1;
+               sgi = (info2 >> 7) & 1;
+
+               status->rate_idx = mcs;
+               status->flag |= RX_FLAG_HT;
+               if (sgi)
+                       status->flag |= RX_FLAG_SHORT_GI;
+               if (bw)
+                       status->flag |= RX_FLAG_40MHZ;
+               break;
+       case HTT_RX_VHT:
+       case HTT_RX_VHT_WITH_TXBF:
+               /* VHT-SIG-A1 in info 1, VHT-SIG-A2 in info2
+                  TODO check this */
+               mcs = (info2 >> 4) & 0x0F;
+               nss = ((info1 >> 10) & 0x07) + 1;
+               bw = info1 & 3;
+               sgi = info2 & 1;
+
+               status->rate_idx = mcs;
+               status->vht_nss = nss;
+
+               if (sgi)
+                       status->flag |= RX_FLAG_SHORT_GI;
+
+               switch (bw) {
+               /* 20MHZ */
+               case 0:
+                       break;
+               /* 40MHZ */
+               case 1:
+                       status->flag |= RX_FLAG_40MHZ;
+                       break;
+               /* 80MHZ */
+               case 2:
+                       status->vht_flag |= RX_VHT_FLAG_80MHZ;
+               }
+
+               status->flag |= RX_FLAG_VHT;
+               break;
+       default:
+               break;
+       }
+}
+
+static void ath10k_htt_rx_h_protected(struct ath10k_htt *htt,
+                                     struct ieee80211_rx_status *rx_status,
+                                     struct sk_buff *skb,
+                                     enum htt_rx_mpdu_encrypt_type enctype,
+                                     enum rx_msdu_decap_format fmt,
+                                     bool dot11frag)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+       rx_status->flag &= ~(RX_FLAG_DECRYPTED |
+                            RX_FLAG_IV_STRIPPED |
+                            RX_FLAG_MMIC_STRIPPED);
+
+       if (enctype == HTT_RX_MPDU_ENCRYPT_NONE)
+               return;
+
+       /*
+        * There's no explicit rx descriptor flag to indicate whether a given
+        * frame has been decrypted or not. We're forced to use the decap
+        * format as an implicit indication. However fragmentation rx is always
+        * raw and it probably never reports undecrypted raws.
+        *
+        * This makes sure sniffed frames are reported as-is without stripping
+        * the protected flag.
+        */
+       if (fmt == RX_MSDU_DECAP_RAW && !dot11frag)
+               return;
+
+       rx_status->flag |= RX_FLAG_DECRYPTED |
+                          RX_FLAG_IV_STRIPPED |
+                          RX_FLAG_MMIC_STRIPPED;
+       hdr->frame_control = __cpu_to_le16(__le16_to_cpu(hdr->frame_control) &
+                                          ~IEEE80211_FCTL_PROTECTED);
+}
+
+static bool ath10k_htt_rx_h_channel(struct ath10k *ar,
+                                   struct ieee80211_rx_status *status)
+{
+       struct ieee80211_channel *ch;
+
+       spin_lock_bh(&ar->data_lock);
+       ch = ar->scan_channel;
+       if (!ch)
+               ch = ar->rx_channel;
+       spin_unlock_bh(&ar->data_lock);
+
+       if (!ch)
+               return false;
+
+       status->band = ch->band;
+       status->freq = ch->center_freq;
+
+       return true;
+}
+
+static void ath10k_process_rx(struct ath10k *ar,
+                             struct ieee80211_rx_status *rx_status,
+                             struct sk_buff *skb)
+{
+       struct ieee80211_rx_status *status;
+
+       status = IEEE80211_SKB_RXCB(skb);
+       *status = *rx_status;
+
+       ath10k_dbg(ATH10K_DBG_DATA,
+                  "rx skb %p len %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %imic-err %i\n",
+                  skb,
+                  skb->len,
+                  status->flag == 0 ? "legacy" : "",
+                  status->flag & RX_FLAG_HT ? "ht" : "",
+                  status->flag & RX_FLAG_VHT ? "vht" : "",
+                  status->flag & RX_FLAG_40MHZ ? "40" : "",
+                  status->vht_flag & RX_VHT_FLAG_80MHZ ? "80" : "",
+                  status->flag & RX_FLAG_SHORT_GI ? "sgi " : "",
+                  status->rate_idx,
+                  status->vht_nss,
+                  status->freq,
+                  status->band, status->flag,
+                  !!(status->flag & RX_FLAG_FAILED_FCS_CRC),
+                  !!(status->flag & RX_FLAG_MMIC_ERROR));
+       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "rx skb: ",
+                       skb->data, skb->len);
+
+       ieee80211_rx(ar->hw, skb);
+}
+
 static int ath10k_htt_rx_nwifi_hdrlen(struct ieee80211_hdr *hdr)
 {
        /* nwifi header is padded to 4 bytes. this fixes 4addr rx */
@@ -643,11 +868,12 @@ static int ath10k_htt_rx_nwifi_hdrlen(struct ieee80211_hdr *hdr)
 }
 
 static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
-                               struct htt_rx_info *info)
+                               struct ieee80211_rx_status *rx_status,
+                               struct sk_buff *skb_in)
 {
        struct htt_rx_desc *rxd;
+       struct sk_buff *skb = skb_in;
        struct sk_buff *first;
-       struct sk_buff *skb = info->skb;
        enum rx_msdu_decap_format fmt;
        enum htt_rx_mpdu_encrypt_type enctype;
        struct ieee80211_hdr *hdr;
@@ -728,24 +954,28 @@ static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
                        break;
                }
 
-               info->skb = skb;
-               info->encrypt_type = enctype;
+               skb_in = skb;
+               ath10k_htt_rx_h_protected(htt, rx_status, skb_in, enctype, fmt,
+                                         false);
                skb = skb->next;
-               info->skb->next = NULL;
+               skb_in->next = NULL;
 
                if (skb)
-                       info->amsdu_more = true;
+                       rx_status->flag |= RX_FLAG_AMSDU_MORE;
+               else
+                       rx_status->flag &= ~RX_FLAG_AMSDU_MORE;
 
-               ath10k_process_rx(htt->ar, info);
+               ath10k_process_rx(htt->ar, rx_status, skb_in);
        }
 
        /* FIXME: It might be nice to re-assemble the A-MSDU when there's a
         * monitor interface active for sniffing purposes. */
 }
 
-static void ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
+static void ath10k_htt_rx_msdu(struct ath10k_htt *htt,
+                              struct ieee80211_rx_status *rx_status,
+                              struct sk_buff *skb)
 {
-       struct sk_buff *skb = info->skb;
        struct htt_rx_desc *rxd;
        struct ieee80211_hdr *hdr;
        enum rx_msdu_decap_format fmt;
@@ -808,66 +1038,9 @@ static void ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
                break;
        }
 
-       info->skb = skb;
-       info->encrypt_type = enctype;
+       ath10k_htt_rx_h_protected(htt, rx_status, skb, enctype, fmt, false);
 
-       ath10k_process_rx(htt->ar, info);
-}
-
-static bool ath10k_htt_rx_has_decrypt_err(struct sk_buff *skb)
-{
-       struct htt_rx_desc *rxd;
-       u32 flags;
-
-       rxd = (void *)skb->data - sizeof(*rxd);
-       flags = __le32_to_cpu(rxd->attention.flags);
-
-       if (flags & RX_ATTENTION_FLAGS_DECRYPT_ERR)
-               return true;
-
-       return false;
-}
-
-static bool ath10k_htt_rx_has_fcs_err(struct sk_buff *skb)
-{
-       struct htt_rx_desc *rxd;
-       u32 flags;
-
-       rxd = (void *)skb->data - sizeof(*rxd);
-       flags = __le32_to_cpu(rxd->attention.flags);
-
-       if (flags & RX_ATTENTION_FLAGS_FCS_ERR)
-               return true;
-
-       return false;
-}
-
-static bool ath10k_htt_rx_has_mic_err(struct sk_buff *skb)
-{
-       struct htt_rx_desc *rxd;
-       u32 flags;
-
-       rxd = (void *)skb->data - sizeof(*rxd);
-       flags = __le32_to_cpu(rxd->attention.flags);
-
-       if (flags & RX_ATTENTION_FLAGS_TKIP_MIC_ERR)
-               return true;
-
-       return false;
-}
-
-static bool ath10k_htt_rx_is_mgmt(struct sk_buff *skb)
-{
-       struct htt_rx_desc *rxd;
-       u32 flags;
-
-       rxd = (void *)skb->data - sizeof(*rxd);
-       flags = __le32_to_cpu(rxd->attention.flags);
-
-       if (flags & RX_ATTENTION_FLAGS_MGMT_TYPE)
-               return true;
-
-       return false;
+       ath10k_process_rx(htt->ar, rx_status, skb);
 }
 
 static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb)
@@ -952,21 +1125,73 @@ static int ath10k_unchain_msdu(struct sk_buff *msdu_head)
        return 0;
 }
 
+static bool ath10k_htt_rx_amsdu_allowed(struct ath10k_htt *htt,
+                                       struct sk_buff *head,
+                                       enum htt_rx_mpdu_status status,
+                                       bool channel_set,
+                                       u32 attention)
+{
+       if (head->len == 0) {
+               ath10k_dbg(ATH10K_DBG_HTT,
+                          "htt rx dropping due to zero-len\n");
+               return false;
+       }
+
+       if (attention & RX_ATTENTION_FLAGS_DECRYPT_ERR) {
+               ath10k_dbg(ATH10K_DBG_HTT,
+                          "htt rx dropping due to decrypt-err\n");
+               return false;
+       }
+
+       if (!channel_set) {
+               ath10k_warn("no channel configured; ignoring frame!\n");
+               return false;
+       }
+
+       /* Skip mgmt frames while we handle this in WMI */
+       if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL ||
+           attention & RX_ATTENTION_FLAGS_MGMT_TYPE) {
+               ath10k_dbg(ATH10K_DBG_HTT, "htt rx mgmt ctrl\n");
+               return false;
+       }
+
+       if (status != HTT_RX_IND_MPDU_STATUS_OK &&
+           status != HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR &&
+           status != HTT_RX_IND_MPDU_STATUS_ERR_INV_PEER &&
+           !htt->ar->monitor_started) {
+               ath10k_dbg(ATH10K_DBG_HTT,
+                          "htt rx ignoring frame w/ status %d\n",
+                          status);
+               return false;
+       }
+
+       if (test_bit(ATH10K_CAC_RUNNING, &htt->ar->dev_flags)) {
+               ath10k_dbg(ATH10K_DBG_HTT,
+                          "htt rx CAC running\n");
+               return false;
+       }
+
+       return true;
+}
+
 static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
                                  struct htt_rx_indication *rx)
 {
-       struct htt_rx_info info;
+       struct ieee80211_rx_status *rx_status = &htt->rx_status;
        struct htt_rx_indication_mpdu_range *mpdu_ranges;
+       struct htt_rx_desc *rxd;
+       enum htt_rx_mpdu_status status;
        struct ieee80211_hdr *hdr;
        int num_mpdu_ranges;
+       u32 attention;
        int fw_desc_len;
        u8 *fw_desc;
+       bool channel_set;
        int i, j;
+       int ret;
 
        lockdep_assert_held(&htt->rx_ring.lock);
 
-       memset(&info, 0, sizeof(info));
-
        fw_desc_len = __le16_to_cpu(rx->prefix.fw_rx_desc_bytes);
        fw_desc = (u8 *)&rx->fw_desc;
 
@@ -974,106 +1199,90 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
                             HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES);
        mpdu_ranges = htt_rx_ind_get_mpdu_ranges(rx);
 
+       /* Fill this once, while this is per-ppdu */
+       if (rx->ppdu.info0 & HTT_RX_INDICATION_INFO0_START_VALID) {
+               memset(rx_status, 0, sizeof(*rx_status));
+               rx_status->signal  = ATH10K_DEFAULT_NOISE_FLOOR +
+                                    rx->ppdu.combined_rssi;
+       }
+
+       if (rx->ppdu.info0 & HTT_RX_INDICATION_INFO0_END_VALID) {
+               /* TSF available only in 32-bit */
+               rx_status->mactime = __le32_to_cpu(rx->ppdu.tsf) & 0xffffffff;
+               rx_status->flag |= RX_FLAG_MACTIME_END;
+       }
+
+       channel_set = ath10k_htt_rx_h_channel(htt->ar, rx_status);
+
+       if (channel_set) {
+               ath10k_htt_rx_h_rates(htt->ar, rx_status->band,
+                                     rx->ppdu.info0,
+                                     __le32_to_cpu(rx->ppdu.info1),
+                                     __le32_to_cpu(rx->ppdu.info2),
+                                     rx_status);
+       }
+
        ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx ind: ",
                        rx, sizeof(*rx) +
                        (sizeof(struct htt_rx_indication_mpdu_range) *
                                num_mpdu_ranges));
 
        for (i = 0; i < num_mpdu_ranges; i++) {
-               info.status = mpdu_ranges[i].mpdu_range_status;
+               status = mpdu_ranges[i].mpdu_range_status;
 
                for (j = 0; j < mpdu_ranges[i].mpdu_count; j++) {
                        struct sk_buff *msdu_head, *msdu_tail;
-                       enum htt_rx_mpdu_status status;
-                       int msdu_chaining;
 
                        msdu_head = NULL;
                        msdu_tail = NULL;
-                       msdu_chaining = ath10k_htt_rx_amsdu_pop(htt,
-                                                        &fw_desc,
-                                                        &fw_desc_len,
-                                                        &msdu_head,
-                                                        &msdu_tail);
-
-                       if (!msdu_head) {
-                               ath10k_warn("htt rx no data!\n");
-                               continue;
-                       }
-
-                       if (msdu_head->len == 0) {
-                               ath10k_dbg(ATH10K_DBG_HTT,
-                                          "htt rx dropping due to zero-len\n");
+                       ret = ath10k_htt_rx_amsdu_pop(htt,
+                                                     &fw_desc,
+                                                     &fw_desc_len,
+                                                     &msdu_head,
+                                                     &msdu_tail);
+
+                       if (ret < 0) {
+                               ath10k_warn("failed to pop amsdu from htt rx ring %d\n",
+                                           ret);
                                ath10k_htt_rx_free_msdu_chain(msdu_head);
                                continue;
                        }
 
-                       if (ath10k_htt_rx_has_decrypt_err(msdu_head)) {
-                               ath10k_dbg(ATH10K_DBG_HTT,
-                                          "htt rx dropping due to decrypt-err\n");
-                               ath10k_htt_rx_free_msdu_chain(msdu_head);
-                               continue;
-                       }
+                       rxd = container_of((void *)msdu_head->data,
+                                          struct htt_rx_desc,
+                                          msdu_payload);
+                       attention = __le32_to_cpu(rxd->attention.flags);
 
-                       status = info.status;
-
-                       /* Skip mgmt frames while we handle this in WMI */
-                       if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL ||
-                           ath10k_htt_rx_is_mgmt(msdu_head)) {
-                               ath10k_dbg(ATH10K_DBG_HTT, "htt rx mgmt ctrl\n");
+                       if (!ath10k_htt_rx_amsdu_allowed(htt, msdu_head,
+                                                        status,
+                                                        channel_set,
+                                                        attention)) {
                                ath10k_htt_rx_free_msdu_chain(msdu_head);
                                continue;
                        }
 
-                       if (status != HTT_RX_IND_MPDU_STATUS_OK &&
-                           status != HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR &&
-                           status != HTT_RX_IND_MPDU_STATUS_ERR_INV_PEER &&
-                           !htt->ar->monitor_enabled) {
-                               ath10k_dbg(ATH10K_DBG_HTT,
-                                          "htt rx ignoring frame w/ status %d\n",
-                                          status);
+                       if (ret > 0 &&
+                           ath10k_unchain_msdu(msdu_head) < 0) {
                                ath10k_htt_rx_free_msdu_chain(msdu_head);
                                continue;
                        }
 
-                       if (test_bit(ATH10K_CAC_RUNNING, &htt->ar->dev_flags)) {
-                               ath10k_dbg(ATH10K_DBG_HTT,
-                                          "htt rx CAC running\n");
-                               ath10k_htt_rx_free_msdu_chain(msdu_head);
-                               continue;
-                       }
-
-                       if (msdu_chaining &&
-                           (ath10k_unchain_msdu(msdu_head) < 0)) {
-                               ath10k_htt_rx_free_msdu_chain(msdu_head);
-                               continue;
-                       }
-
-                       info.skb     = msdu_head;
-                       info.fcs_err = ath10k_htt_rx_has_fcs_err(msdu_head);
-                       info.mic_err = ath10k_htt_rx_has_mic_err(msdu_head);
-
-                       if (info.fcs_err)
-                               ath10k_dbg(ATH10K_DBG_HTT,
-                                          "htt rx has FCS err\n");
-
-                       if (info.mic_err)
-                               ath10k_dbg(ATH10K_DBG_HTT,
-                                          "htt rx has MIC err\n");
-
-                       info.signal  = ATH10K_DEFAULT_NOISE_FLOOR;
-                       info.signal += rx->ppdu.combined_rssi;
+                       if (attention & RX_ATTENTION_FLAGS_FCS_ERR)
+                               rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
+                       else
+                               rx_status->flag &= ~RX_FLAG_FAILED_FCS_CRC;
 
-                       info.rate.info0 = rx->ppdu.info0;
-                       info.rate.info1 = __le32_to_cpu(rx->ppdu.info1);
-                       info.rate.info2 = __le32_to_cpu(rx->ppdu.info2);
-                       info.tsf = __le32_to_cpu(rx->ppdu.tsf);
+                       if (attention & RX_ATTENTION_FLAGS_TKIP_MIC_ERR)
+                               rx_status->flag |= RX_FLAG_MMIC_ERROR;
+                       else
+                               rx_status->flag &= ~RX_FLAG_MMIC_ERROR;
 
                        hdr = ath10k_htt_rx_skb_get_hdr(msdu_head);
 
                        if (ath10k_htt_rx_hdr_is_amsdu(hdr))
-                               ath10k_htt_rx_amsdu(htt, &info);
+                               ath10k_htt_rx_amsdu(htt, rx_status, msdu_head);
                        else
-                               ath10k_htt_rx_msdu(htt, &info);
+                               ath10k_htt_rx_msdu(htt, rx_status, msdu_head);
                }
        }
 
@@ -1084,11 +1293,12 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
                                struct htt_rx_fragment_indication *frag)
 {
        struct sk_buff *msdu_head, *msdu_tail;
+       enum htt_rx_mpdu_encrypt_type enctype;
        struct htt_rx_desc *rxd;
        enum rx_msdu_decap_format fmt;
-       struct htt_rx_info info = {};
+       struct ieee80211_rx_status *rx_status = &htt->rx_status;
        struct ieee80211_hdr *hdr;
-       int msdu_chaining;
+       int ret;
        bool tkip_mic_err;
        bool decrypt_err;
        u8 *fw_desc;
@@ -1102,24 +1312,21 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
        msdu_tail = NULL;
 
        spin_lock_bh(&htt->rx_ring.lock);
-       msdu_chaining = ath10k_htt_rx_amsdu_pop(htt, &fw_desc, &fw_desc_len,
-                                               &msdu_head, &msdu_tail);
+       ret = ath10k_htt_rx_amsdu_pop(htt, &fw_desc, &fw_desc_len,
+                                     &msdu_head, &msdu_tail);
        spin_unlock_bh(&htt->rx_ring.lock);
 
        ath10k_dbg(ATH10K_DBG_HTT_DUMP, "htt rx frag ahead\n");
 
-       if (!msdu_head) {
-               ath10k_warn("htt rx frag no data\n");
-               return;
-       }
-
-       if (msdu_chaining || msdu_head != msdu_tail) {
-               ath10k_warn("aggregation with fragmentation?!\n");
+       if (ret) {
+               ath10k_warn("failed to pop amsdu from httr rx ring for fragmented rx %d\n",
+                           ret);
                ath10k_htt_rx_free_msdu_chain(msdu_head);
                return;
        }
 
        /* FIXME: implement signal strength */
+       rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
 
        hdr = (struct ieee80211_hdr *)msdu_head->data;
        rxd = (void *)msdu_head->data - sizeof(*rxd);
@@ -1136,57 +1343,55 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
                goto end;
        }
 
-       info.skb = msdu_head;
-       info.status = HTT_RX_IND_MPDU_STATUS_OK;
-       info.encrypt_type = MS(__le32_to_cpu(rxd->mpdu_start.info0),
-                               RX_MPDU_START_INFO0_ENCRYPT_TYPE);
-       info.skb->ip_summed = ath10k_htt_rx_get_csum_state(info.skb);
+       enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
+                    RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+       ath10k_htt_rx_h_protected(htt, rx_status, msdu_head, enctype, fmt,
+                                 true);
+       msdu_head->ip_summed = ath10k_htt_rx_get_csum_state(msdu_head);
 
-       if (tkip_mic_err) {
+       if (tkip_mic_err)
                ath10k_warn("tkip mic error\n");
-               info.status = HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR;
-       }
 
        if (decrypt_err) {
                ath10k_warn("decryption err in fragmented rx\n");
-               dev_kfree_skb_any(info.skb);
+               dev_kfree_skb_any(msdu_head);
                goto end;
        }
 
-       if (info.encrypt_type != HTT_RX_MPDU_ENCRYPT_NONE) {
+       if (enctype != HTT_RX_MPDU_ENCRYPT_NONE) {
                hdrlen = ieee80211_hdrlen(hdr->frame_control);
-               paramlen = ath10k_htt_rx_crypto_param_len(info.encrypt_type);
+               paramlen = ath10k_htt_rx_crypto_param_len(enctype);
 
                /* It is more efficient to move the header than the payload */
-               memmove((void *)info.skb->data + paramlen,
-                       (void *)info.skb->data,
+               memmove((void *)msdu_head->data + paramlen,
+                       (void *)msdu_head->data,
                        hdrlen);
-               skb_pull(info.skb, paramlen);
-               hdr = (struct ieee80211_hdr *)info.skb->data;
+               skb_pull(msdu_head, paramlen);
+               hdr = (struct ieee80211_hdr *)msdu_head->data;
        }
 
        /* remove trailing FCS */
        trim  = 4;
 
        /* remove crypto trailer */
-       trim += ath10k_htt_rx_crypto_tail_len(info.encrypt_type);
+       trim += ath10k_htt_rx_crypto_tail_len(enctype);
 
        /* last fragment of TKIP frags has MIC */
        if (!ieee80211_has_morefrags(hdr->frame_control) &&
-           info.encrypt_type == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
+           enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
                trim += 8;
 
-       if (trim > info.skb->len) {
+       if (trim > msdu_head->len) {
                ath10k_warn("htt rx fragment: trailer longer than the frame itself? drop\n");
-               dev_kfree_skb_any(info.skb);
+               dev_kfree_skb_any(msdu_head);
                goto end;
        }
 
-       skb_trim(info.skb, info.skb->len - trim);
+       skb_trim(msdu_head, msdu_head->len - trim);
 
        ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx frag mpdu: ",
-                       info.skb->data, info.skb->len);
-       ath10k_process_rx(htt->ar, &info);
+                       msdu_head->data, msdu_head->len);
+       ath10k_process_rx(htt->ar, rx_status, msdu_head);
 
 end:
        if (fw_desc_len > 0) {
index 7a3e2e40dd5c587215b94e859705f9a24da9acb5..7064354d1f4f0aa82f4624a7d878dea220aa4b2a 100644 (file)
@@ -83,7 +83,7 @@ void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id)
        __clear_bit(msdu_id, htt->used_msdu_ids);
 }
 
-int ath10k_htt_tx_attach(struct ath10k_htt *htt)
+int ath10k_htt_tx_alloc(struct ath10k_htt *htt)
 {
        spin_lock_init(&htt->tx_lock);
        init_waitqueue_head(&htt->empty_tx_wq);
@@ -120,7 +120,7 @@ int ath10k_htt_tx_attach(struct ath10k_htt *htt)
        return 0;
 }
 
-static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt)
+static void ath10k_htt_tx_free_pending(struct ath10k_htt *htt)
 {
        struct htt_tx_done tx_done = {0};
        int msdu_id;
@@ -141,9 +141,9 @@ static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt)
        spin_unlock_bh(&htt->tx_lock);
 }
 
-void ath10k_htt_tx_detach(struct ath10k_htt *htt)
+void ath10k_htt_tx_free(struct ath10k_htt *htt)
 {
-       ath10k_htt_tx_cleanup_pending(htt);
+       ath10k_htt_tx_free_pending(htt);
        kfree(htt->pending_tx);
        kfree(htt->used_msdu_ids);
        dma_pool_destroy(htt->tx_pool);
index 35fc44e281f57968171283d7d336cce5b20eddac..007e855f4ba99f9067725a11b85fdeadb3412483 100644 (file)
@@ -28,6 +28,7 @@
 #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_FW_2_FILE       "firmware-2.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
index 511a2f81e7afc190419623235cdbefe9a66e4039..a21080028c54eeedff71b9b00a327620d9780dc3 100644 (file)
@@ -54,7 +54,10 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
        switch (key->cipher) {
        case WLAN_CIPHER_SUITE_CCMP:
                arg.key_cipher = WMI_CIPHER_AES_CCM;
-               key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
+               if (arvif->vdev_type == WMI_VDEV_TYPE_AP)
+                       key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT;
+               else
+                       key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
                break;
        case WLAN_CIPHER_SUITE_TKIP:
                arg.key_cipher = WMI_CIPHER_TKIP;
@@ -165,7 +168,7 @@ static int ath10k_clear_peer_keys(struct ath10k_vif *arvif,
                        first_errno = ret;
 
                if (ret)
-                       ath10k_warn("could not remove peer wep key %d (%d)\n",
+                       ath10k_warn("failed to remove peer wep key %d: %d\n",
                                    i, ret);
 
                peer->keys[i] = NULL;
@@ -213,7 +216,8 @@ static int ath10k_clear_vdev_key(struct ath10k_vif *arvif,
                        first_errno = ret;
 
                if (ret)
-                       ath10k_warn("could not remove key for %pM\n", addr);
+                       ath10k_warn("failed to remove key for %pM: %d\n",
+                                   addr, ret);
        }
 
        return first_errno;
@@ -323,14 +327,14 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
 
        ret = ath10k_wmi_peer_create(ar, vdev_id, addr);
        if (ret) {
-               ath10k_warn("Failed to create wmi peer %pM on vdev %i: %i\n",
+               ath10k_warn("failed to create wmi peer %pM on vdev %i: %i\n",
                            addr, vdev_id, ret);
                return ret;
        }
 
        ret = ath10k_wait_for_peer_created(ar, vdev_id, addr);
        if (ret) {
-               ath10k_warn("Failed to wait for created wmi peer %pM on vdev %i: %i\n",
+               ath10k_warn("failed to wait for created wmi peer %pM on vdev %i: %i\n",
                            addr, vdev_id, ret);
                return ret;
        }
@@ -351,7 +355,7 @@ static int ath10k_mac_set_kickout(struct ath10k_vif *arvif)
        ret = ath10k_wmi_pdev_set_param(ar, param,
                                        ATH10K_KICKOUT_THRESHOLD);
        if (ret) {
-               ath10k_warn("Failed to set kickout threshold on vdev %i: %d\n",
+               ath10k_warn("failed to set kickout threshold on vdev %i: %d\n",
                            arvif->vdev_id, ret);
                return ret;
        }
@@ -360,7 +364,7 @@ static int ath10k_mac_set_kickout(struct ath10k_vif *arvif)
        ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
                                        ATH10K_KEEPALIVE_MIN_IDLE);
        if (ret) {
-               ath10k_warn("Failed to set keepalive minimum idle time on vdev %i : %d\n",
+               ath10k_warn("failed to set keepalive minimum idle time on vdev %i: %d\n",
                            arvif->vdev_id, ret);
                return ret;
        }
@@ -369,7 +373,7 @@ static int ath10k_mac_set_kickout(struct ath10k_vif *arvif)
        ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
                                        ATH10K_KEEPALIVE_MAX_IDLE);
        if (ret) {
-               ath10k_warn("Failed to set keepalive maximum idle time on vdev %i: %d\n",
+               ath10k_warn("failed to set keepalive maximum idle time on vdev %i: %d\n",
                            arvif->vdev_id, ret);
                return ret;
        }
@@ -378,7 +382,7 @@ static int ath10k_mac_set_kickout(struct ath10k_vif *arvif)
        ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
                                        ATH10K_KEEPALIVE_MAX_UNRESPONSIVE);
        if (ret) {
-               ath10k_warn("Failed to set keepalive maximum unresponsive time on vdev %i: %d\n",
+               ath10k_warn("failed to set keepalive maximum unresponsive time on vdev %i: %d\n",
                            arvif->vdev_id, ret);
                return ret;
        }
@@ -488,92 +492,20 @@ static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
        return 0;
 }
 
-static int ath10k_vdev_start(struct ath10k_vif *arvif)
+static bool ath10k_monitor_is_enabled(struct ath10k *ar)
 {
-       struct ath10k *ar = arvif->ar;
-       struct cfg80211_chan_def *chandef = &ar->chandef;
-       struct wmi_vdev_start_request_arg arg = {};
-       int ret = 0;
-
        lockdep_assert_held(&ar->conf_mutex);
 
-       reinit_completion(&ar->vdev_setup_done);
-
-       arg.vdev_id = arvif->vdev_id;
-       arg.dtim_period = arvif->dtim_period;
-       arg.bcn_intval = arvif->beacon_interval;
-
-       arg.channel.freq = chandef->chan->center_freq;
-       arg.channel.band_center_freq1 = chandef->center_freq1;
-       arg.channel.mode = chan_to_phymode(chandef);
-
-       arg.channel.min_power = 0;
-       arg.channel.max_power = chandef->chan->max_power * 2;
-       arg.channel.max_reg_power = chandef->chan->max_reg_power * 2;
-       arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain * 2;
-
-       if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
-               arg.ssid = arvif->u.ap.ssid;
-               arg.ssid_len = arvif->u.ap.ssid_len;
-               arg.hidden_ssid = arvif->u.ap.hidden_ssid;
-
-               /* For now allow DFS for AP mode */
-               arg.channel.chan_radar =
-                       !!(chandef->chan->flags & IEEE80211_CHAN_RADAR);
-       } else if (arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
-               arg.ssid = arvif->vif->bss_conf.ssid;
-               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));
+                  "mac monitor refs: promisc %d monitor %d cac %d\n",
+                  ar->promisc, ar->monitor,
+                  test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags));
 
-       ret = ath10k_wmi_vdev_start(ar, &arg);
-       if (ret) {
-               ath10k_warn("WMI vdev %i start failed: ret %d\n",
-                           arg.vdev_id, ret);
-               return ret;
-       }
-
-       ret = ath10k_vdev_setup_sync(ar);
-       if (ret) {
-               ath10k_warn("vdev %i setup failed %d\n",
-                           arg.vdev_id, ret);
-               return ret;
-       }
-
-       return ret;
+       return ar->promisc || ar->monitor ||
+              test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
 }
 
-static int ath10k_vdev_stop(struct ath10k_vif *arvif)
-{
-       struct ath10k *ar = arvif->ar;
-       int ret;
-
-       lockdep_assert_held(&ar->conf_mutex);
-
-       reinit_completion(&ar->vdev_setup_done);
-
-       ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id);
-       if (ret) {
-               ath10k_warn("WMI vdev %i stop failed: ret %d\n",
-                           arvif->vdev_id, ret);
-               return ret;
-       }
-
-       ret = ath10k_vdev_setup_sync(ar);
-       if (ret) {
-               ath10k_warn("vdev %i setup sync failed %d\n",
-                           arvif->vdev_id, ret);
-               return ret;
-       }
-
-       return ret;
-}
-
-static int ath10k_monitor_start(struct ath10k *ar, int vdev_id)
+static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id)
 {
        struct cfg80211_chan_def *chandef = &ar->chandef;
        struct ieee80211_channel *channel = chandef->chan;
@@ -582,11 +514,6 @@ static int ath10k_monitor_start(struct ath10k *ar, int vdev_id)
 
        lockdep_assert_held(&ar->conf_mutex);
 
-       if (!ar->monitor_present) {
-               ath10k_warn("mac montor stop -- monitor is not present\n");
-               return -EINVAL;
-       }
-
        arg.vdev_id = vdev_id;
        arg.channel.freq = channel->center_freq;
        arg.channel.band_center_freq1 = chandef->center_freq1;
@@ -604,88 +531,75 @@ static int ath10k_monitor_start(struct ath10k *ar, int vdev_id)
 
        ret = ath10k_wmi_vdev_start(ar, &arg);
        if (ret) {
-               ath10k_warn("Monitor vdev %i start failed: ret %d\n",
+               ath10k_warn("failed to request monitor vdev %i start: %d\n",
                            vdev_id, ret);
                return ret;
        }
 
        ret = ath10k_vdev_setup_sync(ar);
        if (ret) {
-               ath10k_warn("Monitor vdev %i setup failed %d\n",
+               ath10k_warn("failed to synchronize setup for monitor vdev %i: %d\n",
                            vdev_id, ret);
                return ret;
        }
 
        ret = ath10k_wmi_vdev_up(ar, vdev_id, 0, ar->mac_addr);
        if (ret) {
-               ath10k_warn("Monitor vdev %i up failed: %d\n",
+               ath10k_warn("failed to put up monitor vdev %i: %d\n",
                            vdev_id, ret);
                goto vdev_stop;
        }
 
        ar->monitor_vdev_id = vdev_id;
-       ar->monitor_enabled = true;
 
+       ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %i started\n",
+                  ar->monitor_vdev_id);
        return 0;
 
 vdev_stop:
        ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
        if (ret)
-               ath10k_warn("Monitor vdev %i stop failed: %d\n",
+               ath10k_warn("failed to stop monitor vdev %i after start failure: %d\n",
                            ar->monitor_vdev_id, ret);
 
        return ret;
 }
 
-static int ath10k_monitor_stop(struct ath10k *ar)
+static int ath10k_monitor_vdev_stop(struct ath10k *ar)
 {
        int ret = 0;
 
        lockdep_assert_held(&ar->conf_mutex);
 
-       if (!ar->monitor_present) {
-               ath10k_warn("mac montor stop -- monitor is not present\n");
-               return -EINVAL;
-       }
-
-       if (!ar->monitor_enabled) {
-               ath10k_warn("mac montor stop -- monitor is not enabled\n");
-               return -EINVAL;
-       }
-
        ret = ath10k_wmi_vdev_down(ar, ar->monitor_vdev_id);
        if (ret)
-               ath10k_warn("Monitor vdev %i down failed: %d\n",
+               ath10k_warn("failed to put down monitor vdev %i: %d\n",
                            ar->monitor_vdev_id, ret);
 
        ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
        if (ret)
-               ath10k_warn("Monitor vdev %i stop failed: %d\n",
+               ath10k_warn("failed to to request monitor vdev %i stop: %d\n",
                            ar->monitor_vdev_id, ret);
 
        ret = ath10k_vdev_setup_sync(ar);
        if (ret)
-               ath10k_warn("Monitor_down sync failed, vdev %i: %d\n",
+               ath10k_warn("failed to synchronise monitor vdev %i: %d\n",
                            ar->monitor_vdev_id, ret);
 
-       ar->monitor_enabled = false;
+       ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %i stopped\n",
+                  ar->monitor_vdev_id);
        return ret;
 }
 
-static int ath10k_monitor_create(struct ath10k *ar)
+static int ath10k_monitor_vdev_create(struct ath10k *ar)
 {
        int bit, ret = 0;
 
        lockdep_assert_held(&ar->conf_mutex);
 
-       if (ar->monitor_present) {
-               ath10k_warn("Monitor mode already enabled\n");
-               return 0;
-       }
-
        bit = ffs(ar->free_vdev_map);
        if (bit == 0) {
-               ath10k_warn("No free VDEV slots\n");
+               ath10k_warn("failed to find free vdev id for monitor vdev\n");
                return -ENOMEM;
        }
 
@@ -696,7 +610,7 @@ static int ath10k_monitor_create(struct ath10k *ar)
                                     WMI_VDEV_TYPE_MONITOR,
                                     0, ar->mac_addr);
        if (ret) {
-               ath10k_warn("WMI vdev %i monitor create failed: ret %d\n",
+               ath10k_warn("failed to request monitor vdev %i creation: %d\n",
                            ar->monitor_vdev_id, ret);
                goto vdev_fail;
        }
@@ -704,7 +618,6 @@ static int ath10k_monitor_create(struct ath10k *ar)
        ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %d created\n",
                   ar->monitor_vdev_id);
 
-       ar->monitor_present = true;
        return 0;
 
 vdev_fail:
@@ -715,48 +628,123 @@ static int ath10k_monitor_create(struct ath10k *ar)
        return ret;
 }
 
-static int ath10k_monitor_destroy(struct ath10k *ar)
+static int ath10k_monitor_vdev_delete(struct ath10k *ar)
 {
        int ret = 0;
 
        lockdep_assert_held(&ar->conf_mutex);
 
-       if (!ar->monitor_present)
-               return 0;
-
        ret = ath10k_wmi_vdev_delete(ar, ar->monitor_vdev_id);
        if (ret) {
-               ath10k_warn("WMI vdev %i monitor delete failed: %d\n",
+               ath10k_warn("failed to request wmi monitor vdev %i removal: %d\n",
                            ar->monitor_vdev_id, ret);
                return ret;
        }
 
        ar->free_vdev_map |= 1 << (ar->monitor_vdev_id);
-       ar->monitor_present = false;
 
        ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %d deleted\n",
                   ar->monitor_vdev_id);
        return ret;
 }
 
-static int ath10k_start_cac(struct ath10k *ar)
+static int ath10k_monitor_start(struct ath10k *ar)
 {
        int ret;
 
        lockdep_assert_held(&ar->conf_mutex);
 
-       set_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+       if (!ath10k_monitor_is_enabled(ar)) {
+               ath10k_warn("trying to start monitor with no references\n");
+               return 0;
+       }
+
+       if (ar->monitor_started) {
+               ath10k_dbg(ATH10K_DBG_MAC, "mac monitor already started\n");
+               return 0;
+       }
 
-       ret = ath10k_monitor_create(ar);
+       ret = ath10k_monitor_vdev_create(ar);
        if (ret) {
-               clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+               ath10k_warn("failed to create monitor vdev: %d\n", ret);
                return ret;
        }
 
-       ret = ath10k_monitor_start(ar, ar->monitor_vdev_id);
+       ret = ath10k_monitor_vdev_start(ar, ar->monitor_vdev_id);
+       if (ret) {
+               ath10k_warn("failed to start monitor vdev: %d\n", ret);
+               ath10k_monitor_vdev_delete(ar);
+               return ret;
+       }
+
+       ar->monitor_started = true;
+       ath10k_dbg(ATH10K_DBG_MAC, "mac monitor started\n");
+
+       return 0;
+}
+
+static void ath10k_monitor_stop(struct ath10k *ar)
+{
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       if (ath10k_monitor_is_enabled(ar)) {
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "mac monitor will be stopped later\n");
+               return;
+       }
+
+       if (!ar->monitor_started) {
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "mac monitor probably failed to start earlier\n");
+               return;
+       }
+
+       ret = ath10k_monitor_vdev_stop(ar);
+       if (ret)
+               ath10k_warn("failed to stop monitor vdev: %d\n", ret);
+
+       ret = ath10k_monitor_vdev_delete(ar);
+       if (ret)
+               ath10k_warn("failed to delete monitor vdev: %d\n", ret);
+
+       ar->monitor_started = false;
+       ath10k_dbg(ATH10K_DBG_MAC, "mac monitor stopped\n");
+}
+
+static int ath10k_recalc_rtscts_prot(struct ath10k_vif *arvif)
+{
+       struct ath10k *ar = arvif->ar;
+       u32 vdev_param, rts_cts = 0;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       vdev_param = ar->wmi.vdev_param->enable_rtscts;
+
+       if (arvif->use_cts_prot || arvif->num_legacy_stations > 0)
+               rts_cts |= SM(WMI_RTSCTS_ENABLED, WMI_RTSCTS_SET);
+
+       if (arvif->num_legacy_stations > 0)
+               rts_cts |= SM(WMI_RTSCTS_ACROSS_SW_RETRIES,
+                             WMI_RTSCTS_PROFILE);
+
+       return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
+                                        rts_cts);
+}
+
+static int ath10k_start_cac(struct ath10k *ar)
+{
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       set_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+
+       ret = ath10k_monitor_start(ar);
        if (ret) {
+               ath10k_warn("failed to start monitor (cac): %d\n", ret);
                clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
-               ath10k_monitor_destroy(ar);
                return ret;
        }
 
@@ -774,58 +762,26 @@ static int ath10k_stop_cac(struct ath10k *ar)
        if (!test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags))
                return 0;
 
-       ath10k_monitor_stop(ar);
-       ath10k_monitor_destroy(ar);
        clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+       ath10k_monitor_stop(ar);
 
        ath10k_dbg(ATH10K_DBG_MAC, "mac cac finished\n");
 
        return 0;
 }
 
-static const char *ath10k_dfs_state(enum nl80211_dfs_state dfs_state)
+static void ath10k_recalc_radar_detection(struct ath10k *ar)
 {
-       switch (dfs_state) {
-       case NL80211_DFS_USABLE:
-               return "USABLE";
-       case NL80211_DFS_UNAVAILABLE:
-               return "UNAVAILABLE";
-       case NL80211_DFS_AVAILABLE:
-               return "AVAILABLE";
-       default:
-               WARN_ON(1);
-               return "bug";
-       }
-}
-
-static void ath10k_config_radar_detection(struct ath10k *ar)
-{
-       struct ieee80211_channel *chan = ar->hw->conf.chandef.chan;
-       bool radar = ar->hw->conf.radar_enabled;
-       bool chan_radar = !!(chan->flags & IEEE80211_CHAN_RADAR);
-       enum nl80211_dfs_state dfs_state = chan->dfs_state;
        int ret;
 
        lockdep_assert_held(&ar->conf_mutex);
 
-       ath10k_dbg(ATH10K_DBG_MAC,
-                  "mac radar config update: chan %dMHz radar %d chan radar %d chan state %s\n",
-                  chan->center_freq, radar, chan_radar,
-                  ath10k_dfs_state(dfs_state));
-
-       /*
-        * It's safe to call it even if CAC is not started.
-        * This call here guarantees changing channel, etc. will stop CAC.
-        */
        ath10k_stop_cac(ar);
 
-       if (!radar)
-               return;
-
-       if (!chan_radar)
+       if (!ar->radar_enabled)
                return;
 
-       if (dfs_state != NL80211_DFS_USABLE)
+       if (ar->num_started_vdevs > 0)
                return;
 
        ret = ath10k_start_cac(ar);
@@ -835,11 +791,106 @@ static void ath10k_config_radar_detection(struct ath10k *ar)
                 * radiation is not allowed, make this channel DFS_UNAVAILABLE
                 * by indicating that radar was detected.
                 */
-               ath10k_warn("failed to start CAC (%d)\n", ret);
+               ath10k_warn("failed to start CAC: %d\n", ret);
                ieee80211_radar_detected(ar->hw);
        }
 }
 
+static int ath10k_vdev_start(struct ath10k_vif *arvif)
+{
+       struct ath10k *ar = arvif->ar;
+       struct cfg80211_chan_def *chandef = &ar->chandef;
+       struct wmi_vdev_start_request_arg arg = {};
+       int ret = 0;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       reinit_completion(&ar->vdev_setup_done);
+
+       arg.vdev_id = arvif->vdev_id;
+       arg.dtim_period = arvif->dtim_period;
+       arg.bcn_intval = arvif->beacon_interval;
+
+       arg.channel.freq = chandef->chan->center_freq;
+       arg.channel.band_center_freq1 = chandef->center_freq1;
+       arg.channel.mode = chan_to_phymode(chandef);
+
+       arg.channel.min_power = 0;
+       arg.channel.max_power = chandef->chan->max_power * 2;
+       arg.channel.max_reg_power = chandef->chan->max_reg_power * 2;
+       arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain * 2;
+
+       if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
+               arg.ssid = arvif->u.ap.ssid;
+               arg.ssid_len = arvif->u.ap.ssid_len;
+               arg.hidden_ssid = arvif->u.ap.hidden_ssid;
+
+               /* For now allow DFS for AP mode */
+               arg.channel.chan_radar =
+                       !!(chandef->chan->flags & IEEE80211_CHAN_RADAR);
+       } else if (arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
+               arg.ssid = arvif->vif->bss_conf.ssid;
+               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("failed to start WMI vdev %i: %d\n",
+                           arg.vdev_id, ret);
+               return ret;
+       }
+
+       ret = ath10k_vdev_setup_sync(ar);
+       if (ret) {
+               ath10k_warn("failed to synchronise setup for vdev %i: %d\n",
+                           arg.vdev_id, ret);
+               return ret;
+       }
+
+       ar->num_started_vdevs++;
+       ath10k_recalc_radar_detection(ar);
+
+       return ret;
+}
+
+static int ath10k_vdev_stop(struct ath10k_vif *arvif)
+{
+       struct ath10k *ar = arvif->ar;
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       reinit_completion(&ar->vdev_setup_done);
+
+       ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id);
+       if (ret) {
+               ath10k_warn("failed to stop WMI vdev %i: %d\n",
+                           arvif->vdev_id, ret);
+               return ret;
+       }
+
+       ret = ath10k_vdev_setup_sync(ar);
+       if (ret) {
+               ath10k_warn("failed to syncronise setup for vdev %i: %d\n",
+                           arvif->vdev_id, ret);
+               return ret;
+       }
+
+       WARN_ON(ar->num_started_vdevs == 0);
+
+       if (ar->num_started_vdevs != 0) {
+               ar->num_started_vdevs--;
+               ath10k_recalc_radar_detection(ar);
+       }
+
+       return ret;
+}
+
 static void ath10k_control_beaconing(struct ath10k_vif *arvif,
                                struct ieee80211_bss_conf *info)
 {
@@ -880,7 +931,7 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif,
        ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
                                 arvif->bssid);
        if (ret) {
-               ath10k_warn("Failed to bring up vdev %d: %i\n",
+               ath10k_warn("failed to bring up vdev %d: %i\n",
                            arvif->vdev_id, ret);
                ath10k_vdev_stop(arvif);
                return;
@@ -904,7 +955,7 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif,
        if (!info->ibss_joined) {
                ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, self_peer);
                if (ret)
-                       ath10k_warn("Failed to delete IBSS self peer:%pM for VDEV:%d ret:%d\n",
+                       ath10k_warn("failed to delete IBSS self peer %pM for vdev %d: %d\n",
                                    self_peer, arvif->vdev_id, ret);
 
                if (is_zero_ether_addr(arvif->bssid))
@@ -913,7 +964,7 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif,
                ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id,
                                         arvif->bssid);
                if (ret) {
-                       ath10k_warn("Failed to delete IBSS BSSID peer:%pM for VDEV:%d ret:%d\n",
+                       ath10k_warn("failed to delete IBSS BSSID peer %pM for vdev %d: %d\n",
                                    arvif->bssid, arvif->vdev_id, ret);
                        return;
                }
@@ -925,7 +976,7 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif,
 
        ret = ath10k_peer_create(arvif->ar, arvif->vdev_id, self_peer);
        if (ret) {
-               ath10k_warn("Failed to create IBSS self peer:%pM for VDEV:%d ret:%d\n",
+               ath10k_warn("failed to create IBSS self peer %pM for vdev %d: %d\n",
                            self_peer, arvif->vdev_id, ret);
                return;
        }
@@ -934,7 +985,7 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif,
        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",
+               ath10k_warn("failed to set IBSS ATIM for vdev %d: %d\n",
                            arvif->vdev_id, ret);
 }
 
@@ -961,7 +1012,7 @@ static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif)
                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: %i\n",
+                       ath10k_warn("failed to set inactivity time for vdev %d: %i\n",
                                    arvif->vdev_id, ret);
                        return ret;
                }
@@ -974,8 +1025,8 @@ static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif)
 
        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);
+               ath10k_warn("failed to set PS Mode %d for vdev %d: %d\n",
+                           psmode, arvif->vdev_id, ret);
                return ret;
        }
 
@@ -1429,7 +1480,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
 
        ap_sta = ieee80211_find_sta(vif, bss_conf->bssid);
        if (!ap_sta) {
-               ath10k_warn("Failed to find station entry for %pM, vdev %i\n",
+               ath10k_warn("failed to find station entry for bss %pM vdev %i\n",
                            bss_conf->bssid, arvif->vdev_id);
                rcu_read_unlock();
                return;
@@ -1442,7 +1493,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
        ret = ath10k_peer_assoc_prepare(ar, arvif, ap_sta,
                                        bss_conf, &peer_arg);
        if (ret) {
-               ath10k_warn("Peer assoc prepare failed for %pM vdev %i\n: %d",
+               ath10k_warn("failed to prepare peer assoc for %pM vdev %i: %d\n",
                            bss_conf->bssid, arvif->vdev_id, ret);
                rcu_read_unlock();
                return;
@@ -1452,7 +1503,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
 
        ret = ath10k_wmi_peer_assoc(ar, &peer_arg);
        if (ret) {
-               ath10k_warn("Peer assoc failed for %pM vdev %i\n: %d",
+               ath10k_warn("failed to run peer assoc for %pM vdev %i: %d\n",
                            bss_conf->bssid, arvif->vdev_id, ret);
                return;
        }
@@ -1473,7 +1524,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
 
        ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, arvif->aid, arvif->bssid);
        if (ret) {
-               ath10k_warn("VDEV: %d up failed: ret %d\n",
+               ath10k_warn("failed to set vdev %d up: %d\n",
                            arvif->vdev_id, ret);
                return;
        }
@@ -1524,7 +1575,7 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
 }
 
 static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
-                               struct ieee80211_sta *sta)
+                               struct ieee80211_sta *sta, bool reassoc)
 {
        struct wmi_peer_assoc_complete_arg peer_arg;
        int ret = 0;
@@ -1533,34 +1584,46 @@ static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
 
        ret = ath10k_peer_assoc_prepare(ar, arvif, sta, NULL, &peer_arg);
        if (ret) {
-               ath10k_warn("WMI peer assoc prepare failed for %pM vdev %i: %i\n",
+               ath10k_warn("failed to prepare WMI peer assoc for %pM vdev %i: %i\n",
                            sta->addr, arvif->vdev_id, ret);
                return ret;
        }
 
+       peer_arg.peer_reassoc = reassoc;
        ret = ath10k_wmi_peer_assoc(ar, &peer_arg);
        if (ret) {
-               ath10k_warn("Peer assoc failed for STA %pM vdev %i: %d\n",
+               ath10k_warn("failed to run peer assoc for STA %pM vdev %i: %d\n",
                            sta->addr, arvif->vdev_id, ret);
                return ret;
        }
 
        ret = ath10k_setup_peer_smps(ar, arvif, sta->addr, &sta->ht_cap);
        if (ret) {
-               ath10k_warn("failed to setup peer SMPS for vdev: %d\n", ret);
+               ath10k_warn("failed to setup peer SMPS for vdev %d: %d\n",
+                           arvif->vdev_id, ret);
                return ret;
        }
 
+       if (!sta->wme) {
+               arvif->num_legacy_stations++;
+               ret  = ath10k_recalc_rtscts_prot(arvif);
+               if (ret) {
+                       ath10k_warn("failed to recalculate rts/cts prot for vdev %d: %d\n",
+                                   arvif->vdev_id, ret);
+                       return ret;
+               }
+       }
+
        ret = ath10k_install_peer_wep_keys(arvif, sta->addr);
        if (ret) {
-               ath10k_warn("could not install peer wep keys for vdev %i: %d\n",
+               ath10k_warn("failed to install peer wep keys for vdev %i: %d\n",
                            arvif->vdev_id, ret);
                return ret;
        }
 
        ret = ath10k_peer_assoc_qos_ap(ar, arvif, sta);
        if (ret) {
-               ath10k_warn("could not set qos params for STA %pM for vdev %i: %d\n",
+               ath10k_warn("failed to set qos params for STA %pM for vdev %i: %d\n",
                            sta->addr, arvif->vdev_id, ret);
                return ret;
        }
@@ -1575,9 +1638,19 @@ static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif,
 
        lockdep_assert_held(&ar->conf_mutex);
 
+       if (!sta->wme) {
+               arvif->num_legacy_stations--;
+               ret = ath10k_recalc_rtscts_prot(arvif);
+               if (ret) {
+                       ath10k_warn("failed to recalculate rts/cts prot for vdev %d: %d\n",
+                                   arvif->vdev_id, ret);
+                       return ret;
+               }
+       }
+
        ret = ath10k_clear_peer_keys(arvif, sta->addr);
        if (ret) {
-               ath10k_warn("could not clear all peer wep keys for vdev %i: %d\n",
+               ath10k_warn("failed to clear all peer wep keys for vdev %i: %d\n",
                            arvif->vdev_id, ret);
                return ret;
        }
@@ -1685,19 +1758,44 @@ static int ath10k_update_channel_list(struct ath10k *ar)
        return ret;
 }
 
+static enum wmi_dfs_region
+ath10k_mac_get_dfs_region(enum nl80211_dfs_regions dfs_region)
+{
+       switch (dfs_region) {
+       case NL80211_DFS_UNSET:
+               return WMI_UNINIT_DFS_DOMAIN;
+       case NL80211_DFS_FCC:
+               return WMI_FCC_DFS_DOMAIN;
+       case NL80211_DFS_ETSI:
+               return WMI_ETSI_DFS_DOMAIN;
+       case NL80211_DFS_JP:
+               return WMI_MKK4_DFS_DOMAIN;
+       }
+       return WMI_UNINIT_DFS_DOMAIN;
+}
+
 static void ath10k_regd_update(struct ath10k *ar)
 {
        struct reg_dmn_pair_mapping *regpair;
        int ret;
+       enum wmi_dfs_region wmi_dfs_reg;
+       enum nl80211_dfs_regions nl_dfs_reg;
 
        lockdep_assert_held(&ar->conf_mutex);
 
        ret = ath10k_update_channel_list(ar);
        if (ret)
-               ath10k_warn("could not update channel list (%d)\n", ret);
+               ath10k_warn("failed to update channel list: %d\n", ret);
 
        regpair = ar->ath_common.regulatory.regpair;
 
+       if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED) && ar->dfs_detector) {
+               nl_dfs_reg = ar->dfs_detector->region;
+               wmi_dfs_reg = ath10k_mac_get_dfs_region(nl_dfs_reg);
+       } else {
+               wmi_dfs_reg = WMI_UNINIT_DFS_DOMAIN;
+       }
+
        /* Target allows setting up per-band regdomain but ath_common provides
         * a combined one only */
        ret = ath10k_wmi_pdev_set_regdomain(ar,
@@ -1705,9 +1803,10 @@ static void ath10k_regd_update(struct ath10k *ar)
                                            regpair->reg_domain, /* 2ghz */
                                            regpair->reg_domain, /* 5ghz */
                                            regpair->reg_2ghz_ctl,
-                                           regpair->reg_5ghz_ctl);
+                                           regpair->reg_5ghz_ctl,
+                                           wmi_dfs_reg);
        if (ret)
-               ath10k_warn("could not set pdev regdomain (%d)\n", ret);
+               ath10k_warn("failed to set pdev regdomain: %d\n", ret);
 }
 
 static void ath10k_reg_notifier(struct wiphy *wiphy,
@@ -1725,7 +1824,7 @@ static void ath10k_reg_notifier(struct wiphy *wiphy,
                result = ar->dfs_detector->set_dfs_domain(ar->dfs_detector,
                                                          request->dfs_region);
                if (!result)
-                       ath10k_warn("dfs region 0x%X not supported, will trigger radar for every pulse\n",
+                       ath10k_warn("DFS region 0x%X not supported, will trigger radar for every pulse\n",
                                    request->dfs_region);
        }
 
@@ -1759,10 +1858,10 @@ static u8 ath10k_tx_h_get_vdev_id(struct ath10k *ar,
        if (info->control.vif)
                return ath10k_vif_to_arvif(info->control.vif)->vdev_id;
 
-       if (ar->monitor_enabled)
+       if (ar->monitor_started)
                return ar->monitor_vdev_id;
 
-       ath10k_warn("could not resolve vdev id\n");
+       ath10k_warn("failed to resolve vdev id\n");
        return 0;
 }
 
@@ -1792,8 +1891,13 @@ static void ath10k_tx_wep_key_work(struct work_struct *work)
                                                wep_key_work);
        int ret, keyidx = arvif->def_wep_key_newidx;
 
+       mutex_lock(&arvif->ar->conf_mutex);
+
+       if (arvif->ar->state != ATH10K_STATE_ON)
+               goto unlock;
+
        if (arvif->def_wep_key_idx == keyidx)
-               return;
+               goto unlock;
 
        ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n",
                   arvif->vdev_id, keyidx);
@@ -1803,11 +1907,16 @@ static void ath10k_tx_wep_key_work(struct work_struct *work)
                                        arvif->ar->wmi.vdev_param->def_keyid,
                                        keyidx);
        if (ret) {
-               ath10k_warn("could not update wep keyidx (%d)\n", ret);
-               return;
+               ath10k_warn("failed to update wep key index for vdev %d: %d\n",
+                           arvif->vdev_id,
+                           ret);
+               goto unlock;
        }
 
        arvif->def_wep_key_idx = keyidx;
+
+unlock:
+       mutex_unlock(&arvif->ar->conf_mutex);
 }
 
 static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
@@ -1879,7 +1988,7 @@ static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
                             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");
+                               ath10k_warn("reached WMI management tranmist queue limit\n");
                                ret = -EBUSY;
                                goto exit;
                        }
@@ -1903,7 +2012,7 @@ static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
 
 exit:
        if (ret) {
-               ath10k_warn("tx failed (%d). dropping packet.\n", ret);
+               ath10k_warn("failed to transmit packet, dropping: %d\n", ret);
                ieee80211_free_txskb(ar->hw, skb);
        }
 }
@@ -1964,7 +2073,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)
                if (!peer) {
                        ret = ath10k_peer_create(ar, vdev_id, peer_addr);
                        if (ret)
-                               ath10k_warn("peer %pM on vdev %d not created (%d)\n",
+                               ath10k_warn("failed to create peer %pM on vdev %d: %d\n",
                                            peer_addr, vdev_id, ret);
                }
 
@@ -1984,7 +2093,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)
                if (!peer) {
                        ret = ath10k_peer_delete(ar, vdev_id, peer_addr);
                        if (ret)
-                               ath10k_warn("peer %pM on vdev %d not deleted (%d)\n",
+                               ath10k_warn("failed to delete peer %pM on vdev %d: %d\n",
                                            peer_addr, vdev_id, ret);
                }
 
@@ -2018,7 +2127,8 @@ void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work)
 
                ret = ath10k_wmi_mgmt_tx(ar, skb);
                if (ret) {
-                       ath10k_warn("wmi mgmt_tx failed (%d)\n", ret);
+                       ath10k_warn("failed to transmit management frame via WMI: %d\n",
+                                   ret);
                        ieee80211_free_txskb(ar->hw, skb);
                }
        }
@@ -2043,7 +2153,7 @@ void ath10k_reset_scan(unsigned long ptr)
                return;
        }
 
-       ath10k_warn("scan timeout. resetting. fw issue?\n");
+       ath10k_warn("scan timed out, firmware problem?\n");
 
        if (ar->scan.is_roc)
                ieee80211_remain_on_channel_expired(ar->hw);
@@ -2079,7 +2189,7 @@ static int ath10k_abort_scan(struct ath10k *ar)
 
        ret = ath10k_wmi_stop_scan(ar, &arg);
        if (ret) {
-               ath10k_warn("could not submit wmi stop scan (%d)\n", ret);
+               ath10k_warn("failed to stop wmi scan: %d\n", ret);
                spin_lock_bh(&ar->data_lock);
                ar->scan.in_progress = false;
                ath10k_offchan_tx_purge(ar);
@@ -2099,7 +2209,7 @@ static int ath10k_abort_scan(struct ath10k *ar)
 
        spin_lock_bh(&ar->data_lock);
        if (ar->scan.in_progress) {
-               ath10k_warn("could not stop scan. its still in progress\n");
+               ath10k_warn("failed to stop scan, it's still in progress\n");
                ar->scan.in_progress = false;
                ath10k_offchan_tx_purge(ar);
                ret = -ETIMEDOUT;
@@ -2187,72 +2297,171 @@ static void ath10k_tx(struct ieee80211_hw *hw,
        ath10k_tx_htt(ar, skb);
 }
 
-/*
- * Initialize various parameters with default vaules.
- */
+/* Must not be called with conf_mutex held as workers can use that also. */
+static void ath10k_drain_tx(struct ath10k *ar)
+{
+       /* make sure rcu-protected mac80211 tx path itself is drained */
+       synchronize_net();
+
+       ath10k_offchan_tx_purge(ar);
+       ath10k_mgmt_over_wmi_tx_purge(ar);
+
+       cancel_work_sync(&ar->offchan_tx_work);
+       cancel_work_sync(&ar->wmi_mgmt_tx_work);
+}
+
 void ath10k_halt(struct ath10k *ar)
 {
+       struct ath10k_vif *arvif;
+
        lockdep_assert_held(&ar->conf_mutex);
 
-       ath10k_stop_cac(ar);
+       if (ath10k_monitor_is_enabled(ar)) {
+               clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+               ar->promisc = false;
+               ar->monitor = false;
+               ath10k_monitor_stop(ar);
+       }
+
        del_timer_sync(&ar->scan.timeout);
-       ath10k_offchan_tx_purge(ar);
-       ath10k_mgmt_over_wmi_tx_purge(ar);
+       ath10k_reset_scan((unsigned long)ar);
        ath10k_peer_cleanup_all(ar);
        ath10k_core_stop(ar);
        ath10k_hif_power_down(ar);
 
        spin_lock_bh(&ar->data_lock);
-       if (ar->scan.in_progress) {
-               del_timer(&ar->scan.timeout);
-               ar->scan.in_progress = false;
-               ieee80211_scan_completed(ar->hw, true);
+       list_for_each_entry(arvif, &ar->arvifs, list) {
+               if (!arvif->beacon)
+                       continue;
+
+               dma_unmap_single(arvif->ar->dev,
+                                ATH10K_SKB_CB(arvif->beacon)->paddr,
+                                arvif->beacon->len, DMA_TO_DEVICE);
+               dev_kfree_skb_any(arvif->beacon);
+               arvif->beacon = NULL;
        }
        spin_unlock_bh(&ar->data_lock);
 }
 
+static int ath10k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
+{
+       struct ath10k *ar = hw->priv;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (ar->cfg_tx_chainmask) {
+               *tx_ant = ar->cfg_tx_chainmask;
+               *rx_ant = ar->cfg_rx_chainmask;
+       } else {
+               *tx_ant = ar->supp_tx_chainmask;
+               *rx_ant = ar->supp_rx_chainmask;
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+
+       return 0;
+}
+
+static int __ath10k_set_antenna(struct ath10k *ar, u32 tx_ant, u32 rx_ant)
+{
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       ar->cfg_tx_chainmask = tx_ant;
+       ar->cfg_rx_chainmask = rx_ant;
+
+       if ((ar->state != ATH10K_STATE_ON) &&
+           (ar->state != ATH10K_STATE_RESTARTED))
+               return 0;
+
+       ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->tx_chain_mask,
+                                       tx_ant);
+       if (ret) {
+               ath10k_warn("failed to set tx-chainmask: %d, req 0x%x\n",
+                           ret, tx_ant);
+               return ret;
+       }
+
+       ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->rx_chain_mask,
+                                       rx_ant);
+       if (ret) {
+               ath10k_warn("failed to set rx-chainmask: %d, req 0x%x\n",
+                           ret, rx_ant);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int ath10k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
+{
+       struct ath10k *ar = hw->priv;
+       int ret;
+
+       mutex_lock(&ar->conf_mutex);
+       ret = __ath10k_set_antenna(ar, tx_ant, rx_ant);
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
 static int ath10k_start(struct ieee80211_hw *hw)
 {
        struct ath10k *ar = hw->priv;
        int ret = 0;
 
+       /*
+        * This makes sense only when restarting hw. It is harmless to call
+        * uncoditionally. This is necessary to make sure no HTT/WMI tx
+        * commands will be submitted while restarting.
+        */
+       ath10k_drain_tx(ar);
+
        mutex_lock(&ar->conf_mutex);
 
-       if (ar->state != ATH10K_STATE_OFF &&
-           ar->state != ATH10K_STATE_RESTARTING) {
+       switch (ar->state) {
+       case ATH10K_STATE_OFF:
+               ar->state = ATH10K_STATE_ON;
+               break;
+       case ATH10K_STATE_RESTARTING:
+               ath10k_halt(ar);
+               ar->state = ATH10K_STATE_RESTARTED;
+               break;
+       case ATH10K_STATE_ON:
+       case ATH10K_STATE_RESTARTED:
+       case ATH10K_STATE_WEDGED:
+               WARN_ON(1);
                ret = -EINVAL;
-               goto exit;
+               goto err;
        }
 
        ret = ath10k_hif_power_up(ar);
        if (ret) {
-               ath10k_err("could not init hif (%d)\n", ret);
-               ar->state = ATH10K_STATE_OFF;
-               goto exit;
+               ath10k_err("Could not init hif: %d\n", ret);
+               goto err_off;
        }
 
        ret = ath10k_core_start(ar);
        if (ret) {
-               ath10k_err("could not init core (%d)\n", ret);
-               ath10k_hif_power_down(ar);
-               ar->state = ATH10K_STATE_OFF;
-               goto exit;
+               ath10k_err("Could not init core: %d\n", ret);
+               goto err_power_down;
        }
 
-       if (ar->state == ATH10K_STATE_OFF)
-               ar->state = ATH10K_STATE_ON;
-       else if (ar->state == ATH10K_STATE_RESTARTING)
-               ar->state = ATH10K_STATE_RESTARTED;
-
        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);
+       if (ret) {
+               ath10k_warn("failed to enable PMF QOS: %d\n", ret);
+               goto err_core_stop;
+       }
 
        ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->dynamic_bw, 1);
-       if (ret)
-               ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n",
-                           ret);
+       if (ret) {
+               ath10k_warn("failed to enable dynamic BW: %d\n", ret);
+               goto err_core_stop;
+       }
+
+       if (ar->cfg_tx_chainmask)
+               __ath10k_set_antenna(ar, ar->cfg_tx_chainmask,
+                                    ar->cfg_rx_chainmask);
 
        /*
         * By default FW set ARP frames ac to voice (6). In that case ARP
@@ -2266,15 +2475,27 @@ static int ath10k_start(struct ieee80211_hw *hw)
        ret = ath10k_wmi_pdev_set_param(ar,
                                        ar->wmi.pdev_param->arp_ac_override, 0);
        if (ret) {
-               ath10k_warn("could not set arp ac override parameter: %d\n",
+               ath10k_warn("failed to set arp ac override parameter: %d\n",
                            ret);
-               goto exit;
+               goto err_core_stop;
        }
 
+       ar->num_started_vdevs = 0;
        ath10k_regd_update(ar);
-       ret = 0;
 
-exit:
+       mutex_unlock(&ar->conf_mutex);
+       return 0;
+
+err_core_stop:
+       ath10k_core_stop(ar);
+
+err_power_down:
+       ath10k_hif_power_down(ar);
+
+err_off:
+       ar->state = ATH10K_STATE_OFF;
+
+err:
        mutex_unlock(&ar->conf_mutex);
        return ret;
 }
@@ -2283,19 +2504,15 @@ static void ath10k_stop(struct ieee80211_hw *hw)
 {
        struct ath10k *ar = hw->priv;
 
+       ath10k_drain_tx(ar);
+
        mutex_lock(&ar->conf_mutex);
-       if (ar->state == ATH10K_STATE_ON ||
-           ar->state == ATH10K_STATE_RESTARTED ||
-           ar->state == ATH10K_STATE_WEDGED)
+       if (ar->state != ATH10K_STATE_OFF) {
                ath10k_halt(ar);
-
-       ar->state = ATH10K_STATE_OFF;
+               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);
 }
 
@@ -2309,7 +2526,7 @@ static int ath10k_config_ps(struct ath10k *ar)
        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);
+                       ath10k_warn("failed to setup powersave: %d\n", ret);
                        break;
                }
        }
@@ -2343,7 +2560,6 @@ static const char *chandef_get_width(enum nl80211_chan_width width)
 static void ath10k_config_chan(struct ath10k *ar)
 {
        struct ath10k_vif *arvif;
-       bool monitor_was_enabled;
        int ret;
 
        lockdep_assert_held(&ar->conf_mutex);
@@ -2357,10 +2573,8 @@ static void ath10k_config_chan(struct ath10k *ar)
 
        /* First stop monitor interface. Some FW versions crash if there's a
         * lone monitor interface. */
-       monitor_was_enabled = ar->monitor_enabled;
-
-       if (ar->monitor_enabled)
-               ath10k_monitor_stop(ar);
+       if (ar->monitor_started)
+               ath10k_monitor_vdev_stop(ar);
 
        list_for_each_entry(arvif, &ar->arvifs, list) {
                if (!arvif->is_started)
@@ -2371,7 +2585,7 @@ static void ath10k_config_chan(struct ath10k *ar)
 
                ret = ath10k_vdev_stop(arvif);
                if (ret) {
-                       ath10k_warn("could not stop vdev %d (%d)\n",
+                       ath10k_warn("failed to stop vdev %d: %d\n",
                                    arvif->vdev_id, ret);
                        continue;
                }
@@ -2388,7 +2602,7 @@ static void ath10k_config_chan(struct ath10k *ar)
 
                ret = ath10k_vdev_start(arvif);
                if (ret) {
-                       ath10k_warn("could not start vdev %d (%d)\n",
+                       ath10k_warn("failed to start vdev %d: %d\n",
                                    arvif->vdev_id, ret);
                        continue;
                }
@@ -2399,14 +2613,14 @@ static void ath10k_config_chan(struct ath10k *ar)
                ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
                                         arvif->bssid);
                if (ret) {
-                       ath10k_warn("could not bring vdev up %d (%d)\n",
+                       ath10k_warn("failed to bring vdev up %d: %d\n",
                                    arvif->vdev_id, ret);
                        continue;
                }
        }
 
-       if (monitor_was_enabled)
-               ath10k_monitor_start(ar, ar->monitor_vdev_id);
+       if (ath10k_monitor_is_enabled(ar))
+               ath10k_monitor_vdev_start(ar, ar->monitor_vdev_id);
 }
 
 static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
@@ -2420,15 +2634,17 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
 
        if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
                ath10k_dbg(ATH10K_DBG_MAC,
-                          "mac config channel %d mhz flags 0x%x\n",
+                          "mac config channel %dMHz flags 0x%x radar %d\n",
                           conf->chandef.chan->center_freq,
-                          conf->chandef.chan->flags);
+                          conf->chandef.chan->flags,
+                          conf->radar_enabled);
 
                spin_lock_bh(&ar->data_lock);
                ar->rx_channel = conf->chandef.chan;
                spin_unlock_bh(&ar->data_lock);
 
-               ath10k_config_radar_detection(ar);
+               ar->radar_enabled = conf->radar_enabled;
+               ath10k_recalc_radar_detection(ar);
 
                if (!cfg80211_chandef_identical(&ar->chandef, &conf->chandef)) {
                        ar->chandef = conf->chandef;
@@ -2444,14 +2660,14 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
                ret = ath10k_wmi_pdev_set_param(ar, param,
                                                hw->conf.power_level * 2);
                if (ret)
-                       ath10k_warn("mac failed to set 2g txpower %d (%d)\n",
+                       ath10k_warn("failed to set 2g txpower %d: %d\n",
                                    hw->conf.power_level, ret);
 
                param = ar->wmi.pdev_param->txpower_limit5g;
                ret = ath10k_wmi_pdev_set_param(ar, param,
                                                hw->conf.power_level * 2);
                if (ret)
-                       ath10k_warn("mac failed to set 5g txpower %d (%d)\n",
+                       ath10k_warn("failed to set 5g txpower %d: %d\n",
                                    hw->conf.power_level, ret);
        }
 
@@ -2459,10 +2675,19 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
                ath10k_config_ps(ar);
 
        if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
-               if (conf->flags & IEEE80211_CONF_MONITOR)
-                       ret = ath10k_monitor_create(ar);
-               else
-                       ret = ath10k_monitor_destroy(ar);
+               if (conf->flags & IEEE80211_CONF_MONITOR && !ar->monitor) {
+                       ar->monitor = true;
+                       ret = ath10k_monitor_start(ar);
+                       if (ret) {
+                               ath10k_warn("failed to start monitor (config): %d\n",
+                                           ret);
+                               ar->monitor = false;
+                       }
+               } else if (!(conf->flags & IEEE80211_CONF_MONITOR) &&
+                          ar->monitor) {
+                       ar->monitor = false;
+                       ath10k_monitor_stop(ar);
+               }
        }
 
        mutex_unlock(&ar->conf_mutex);
@@ -2497,12 +2722,6 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
        INIT_WORK(&arvif->wep_key_work, ath10k_tx_wep_key_work);
        INIT_LIST_HEAD(&arvif->list);
 
-       if ((vif->type == NL80211_IFTYPE_MONITOR) && ar->monitor_present) {
-               ath10k_warn("Only one monitor interface allowed\n");
-               ret = -EBUSY;
-               goto err;
-       }
-
        bit = ffs(ar->free_vdev_map);
        if (bit == 0) {
                ret = -EBUSY;
@@ -2545,7 +2764,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
        ret = ath10k_wmi_vdev_create(ar, arvif->vdev_id, arvif->vdev_type,
                                     arvif->vdev_subtype, vif->addr);
        if (ret) {
-               ath10k_warn("WMI vdev %i create failed: ret %d\n",
+               ath10k_warn("failed to create WMI vdev %i: %d\n",
                            arvif->vdev_id, ret);
                goto err;
        }
@@ -2557,7 +2776,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
        ret = ath10k_wmi_vdev_set_param(ar, 0, vdev_param,
                                        arvif->def_wep_key_idx);
        if (ret) {
-               ath10k_warn("Failed to set vdev %i default keyid: %d\n",
+               ath10k_warn("failed to set vdev %i default key id: %d\n",
                            arvif->vdev_id, ret);
                goto err_vdev_delete;
        }
@@ -2567,7 +2786,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
                                        ATH10K_HW_TXRX_NATIVE_WIFI);
        /* 10.X firmware does not support this VDEV parameter. Do not warn */
        if (ret && ret != -EOPNOTSUPP) {
-               ath10k_warn("Failed to set vdev %i TX encap: %d\n",
+               ath10k_warn("failed to set vdev %i TX encapsulation: %d\n",
                            arvif->vdev_id, ret);
                goto err_vdev_delete;
        }
@@ -2575,14 +2794,14 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
        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 vdev %i peer for AP: %d\n",
+                       ath10k_warn("failed to create vdev %i peer for AP: %d\n",
                                    arvif->vdev_id, ret);
                        goto err_vdev_delete;
                }
 
                ret = ath10k_mac_set_kickout(arvif);
                if (ret) {
-                       ath10k_warn("Failed to set vdev %i kickout parameters: %d\n",
+                       ath10k_warn("failed to set vdev %i kickout parameters: %d\n",
                                    arvif->vdev_id, ret);
                        goto err_peer_delete;
                }
@@ -2594,7 +2813,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
                ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
                                                  param, value);
                if (ret) {
-                       ath10k_warn("Failed to set vdev %i RX wake policy: %d\n",
+                       ath10k_warn("failed to set vdev %i RX wake policy: %d\n",
                                    arvif->vdev_id, ret);
                        goto err_peer_delete;
                }
@@ -2604,7 +2823,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
                ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
                                                  param, value);
                if (ret) {
-                       ath10k_warn("Failed to set vdev %i TX wake thresh: %d\n",
+                       ath10k_warn("failed to set vdev %i TX wake thresh: %d\n",
                                    arvif->vdev_id, ret);
                        goto err_peer_delete;
                }
@@ -2614,7 +2833,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
                ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
                                                  param, value);
                if (ret) {
-                       ath10k_warn("Failed to set vdev %i PSPOLL count: %d\n",
+                       ath10k_warn("failed to set vdev %i PSPOLL count: %d\n",
                                    arvif->vdev_id, ret);
                        goto err_peer_delete;
                }
@@ -2622,21 +2841,18 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
 
        ret = ath10k_mac_set_rts(arvif, ar->hw->wiphy->rts_threshold);
        if (ret) {
-               ath10k_warn("failed to set rts threshold for vdev %d (%d)\n",
+               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) {
-               ath10k_warn("failed to set frag threshold for vdev %d (%d)\n",
+               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;
-
        mutex_unlock(&ar->conf_mutex);
        return 0;
 
@@ -2668,6 +2884,9 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
 
        spin_lock_bh(&ar->data_lock);
        if (arvif->beacon) {
+               dma_unmap_single(arvif->ar->dev,
+                                ATH10K_SKB_CB(arvif->beacon)->paddr,
+                                arvif->beacon->len, DMA_TO_DEVICE);
                dev_kfree_skb_any(arvif->beacon);
                arvif->beacon = NULL;
        }
@@ -2679,7 +2898,7 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
        if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
                ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, vif->addr);
                if (ret)
-                       ath10k_warn("Failed to remove peer for AP vdev %i: %d\n",
+                       ath10k_warn("failed to remove peer for AP vdev %i: %d\n",
                                    arvif->vdev_id, ret);
 
                kfree(arvif->u.ap.noa_data);
@@ -2690,12 +2909,9 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
 
        ret = ath10k_wmi_vdev_delete(ar, arvif->vdev_id);
        if (ret)
-               ath10k_warn("WMI vdev %i delete failed: %d\n",
+               ath10k_warn("failed to delete WMI vdev %i: %d\n",
                            arvif->vdev_id, ret);
 
-       if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
-               ar->monitor_present = false;
-
        ath10k_peer_cleanup(ar, arvif->vdev_id);
 
        mutex_unlock(&ar->conf_mutex);
@@ -2728,28 +2944,17 @@ static void ath10k_configure_filter(struct ieee80211_hw *hw,
        *total_flags &= SUPPORTED_FILTERS;
        ar->filter_flags = *total_flags;
 
-       /* Monitor must not be started if it wasn't created first.
-        * Promiscuous mode may be started on a non-monitor interface - in
-        * such case the monitor vdev is not created so starting the
-        * monitor makes no sense. Since ath10k uses no special RX filters
-        * (only BSS filter in STA mode) there's no need for any special
-        * action here. */
-       if ((ar->filter_flags & FIF_PROMISC_IN_BSS) &&
-           !ar->monitor_enabled && ar->monitor_present) {
-               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 if (!(ar->filter_flags & FIF_PROMISC_IN_BSS) &&
-                  ar->monitor_enabled && ar->monitor_present) {
-               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");
+       if (ar->filter_flags & FIF_PROMISC_IN_BSS && !ar->promisc) {
+               ar->promisc = true;
+               ret = ath10k_monitor_start(ar);
+               if (ret) {
+                       ath10k_warn("failed to start monitor (promisc): %d\n",
+                                   ret);
+                       ar->promisc = false;
+               }
+       } else if (!(ar->filter_flags & FIF_PROMISC_IN_BSS) && ar->promisc) {
+               ar->promisc = false;
+               ath10k_monitor_stop(ar);
        }
 
        mutex_unlock(&ar->conf_mutex);
@@ -2780,7 +2985,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                           arvif->vdev_id, arvif->beacon_interval);
 
                if (ret)
-                       ath10k_warn("Failed to set beacon interval for vdev %d: %i\n",
+                       ath10k_warn("failed to set beacon interval for vdev %d: %i\n",
                                    arvif->vdev_id, ret);
        }
 
@@ -2793,7 +2998,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                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: %i\n",
+                       ath10k_warn("failed to set beacon mode for vdev %d: %i\n",
                                    arvif->vdev_id, ret);
        }
 
@@ -2808,7 +3013,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                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: %i\n",
+                       ath10k_warn("failed to set dtim period for vdev %d: %i\n",
                                    arvif->vdev_id, ret);
        }
 
@@ -2820,7 +3025,12 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                arvif->u.ap.hidden_ssid = info->hidden_ssid;
        }
 
-       if (changed & BSS_CHANGED_BSSID) {
+       /*
+        * Firmware manages AP self-peer internally so make sure to not create
+        * it in driver. Otherwise AP self-peer deletion may timeout later.
+        */
+       if (changed & BSS_CHANGED_BSSID &&
+           vif->type != NL80211_IFTYPE_AP) {
                if (!is_zero_ether_addr(info->bssid)) {
                        ath10k_dbg(ATH10K_DBG_MAC,
                                   "mac vdev %d create peer %pM\n",
@@ -2829,7 +3039,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                        ret = ath10k_peer_create(ar, arvif->vdev_id,
                                                 info->bssid);
                        if (ret)
-                               ath10k_warn("Failed to add peer %pM for vdev %d when changing bssid: %i\n",
+                               ath10k_warn("failed to add peer %pM for vdev %d when changing bssid: %i\n",
                                            info->bssid, arvif->vdev_id, ret);
 
                        if (vif->type == NL80211_IFTYPE_STATION) {
@@ -2868,20 +3078,13 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                ath10k_control_beaconing(arvif, info);
 
        if (changed & BSS_CHANGED_ERP_CTS_PROT) {
-               u32 cts_prot;
-               if (info->use_cts_prot)
-                       cts_prot = 1;
-               else
-                       cts_prot = 0;
-
+               arvif->use_cts_prot = info->use_cts_prot;
                ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d cts_prot %d\n",
-                          arvif->vdev_id, cts_prot);
+                          arvif->vdev_id, info->use_cts_prot);
 
-               vdev_param = ar->wmi.vdev_param->enable_rtscts;
-               ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
-                                               cts_prot);
+               ret = ath10k_recalc_rtscts_prot(arvif);
                if (ret)
-                       ath10k_warn("Failed to set CTS prot for vdev %d: %d\n",
+                       ath10k_warn("failed to recalculate rts/cts prot for vdev %d: %d\n",
                                    arvif->vdev_id, ret);
        }
 
@@ -2900,7 +3103,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                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: %i\n",
+                       ath10k_warn("failed to set erp slot for vdev %d: %i\n",
                                    arvif->vdev_id, ret);
        }
 
@@ -2919,7 +3122,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
                                                preamble);
                if (ret)
-                       ath10k_warn("Failed to set preamble for vdev %d: %i\n",
+                       ath10k_warn("failed to set preamble for vdev %d: %i\n",
                                    arvif->vdev_id, ret);
        }
 
@@ -2990,7 +3193,7 @@ static int ath10k_hw_scan(struct ieee80211_hw *hw,
 
        ret = ath10k_start_scan(ar, &arg);
        if (ret) {
-               ath10k_warn("could not start hw scan (%d)\n", ret);
+               ath10k_warn("failed to start hw scan: %d\n", ret);
                spin_lock_bh(&ar->data_lock);
                ar->scan.in_progress = false;
                spin_unlock_bh(&ar->data_lock);
@@ -3010,8 +3213,7 @@ static void ath10k_cancel_hw_scan(struct ieee80211_hw *hw,
        mutex_lock(&ar->conf_mutex);
        ret = ath10k_abort_scan(ar);
        if (ret) {
-               ath10k_warn("couldn't abort scan (%d). forcefully sending scan completion to mac80211\n",
-                           ret);
+               ath10k_warn("failed to abort scan: %d\n", ret);
                ieee80211_scan_completed(hw, 1 /* aborted */);
        }
        mutex_unlock(&ar->conf_mutex);
@@ -3089,7 +3291,7 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 
        if (!peer) {
                if (cmd == SET_KEY) {
-                       ath10k_warn("cannot install key for non-existent peer %pM\n",
+                       ath10k_warn("failed to install key for non-existent peer %pM\n",
                                    peer_addr);
                        ret = -EOPNOTSUPP;
                        goto exit;
@@ -3112,7 +3314,7 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 
        ret = ath10k_install_key(arvif, key, cmd, peer_addr);
        if (ret) {
-               ath10k_warn("key installation failed for vdev %i peer %pM: %d\n",
+               ath10k_warn("failed to install key for vdev %i peer %pM: %d\n",
                            arvif->vdev_id, peer_addr, ret);
                goto exit;
        }
@@ -3127,7 +3329,7 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                peer->keys[key->keyidx] = NULL;
        else if (peer == NULL)
                /* impossible unless FW goes crazy */
-               ath10k_warn("peer %pM disappeared!\n", peer_addr);
+               ath10k_warn("Peer %pM disappeared!\n", peer_addr);
        spin_unlock_bh(&ar->data_lock);
 
 exit:
@@ -3195,6 +3397,16 @@ static void ath10k_sta_rc_update_wk(struct work_struct *wk)
                                    sta->addr, smps, err);
        }
 
+       if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) {
+               ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM supp rates\n",
+                          sta->addr);
+
+               err = ath10k_station_assoc(ar, arvif, sta, true);
+               if (err)
+                       ath10k_warn("failed to reassociate station: %pM\n",
+                                   sta->addr);
+       }
+
        mutex_unlock(&ar->conf_mutex);
 }
 
@@ -3236,7 +3448,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                        max_num_peers = TARGET_NUM_PEERS;
 
                if (ar->num_peers >= max_num_peers) {
-                       ath10k_warn("Number of peers exceeded: peers number %d (max peers %d)\n",
+                       ath10k_warn("number of peers exceeded: peers number %d (max peers %d)\n",
                                    ar->num_peers, max_num_peers);
                        ret = -ENOBUFS;
                        goto exit;
@@ -3248,7 +3460,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
 
                ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr);
                if (ret)
-                       ath10k_warn("Failed to add peer %pM for vdev %d when adding a new sta: %i\n",
+                       ath10k_warn("failed to add peer %pM for vdev %d when adding a new sta: %i\n",
                                    sta->addr, arvif->vdev_id, ret);
        } else if ((old_state == IEEE80211_STA_NONE &&
                    new_state == IEEE80211_STA_NOTEXIST)) {
@@ -3260,7 +3472,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                           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: %i\n",
+                       ath10k_warn("failed to delete peer %pM for vdev %d: %i\n",
                                    sta->addr, arvif->vdev_id, ret);
 
                if (vif->type == NL80211_IFTYPE_STATION)
@@ -3275,9 +3487,9 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                ath10k_dbg(ATH10K_DBG_MAC, "mac sta %pM associated\n",
                           sta->addr);
 
-               ret = ath10k_station_assoc(ar, arvif, sta);
+               ret = ath10k_station_assoc(ar, arvif, sta, false);
                if (ret)
-                       ath10k_warn("Failed to associate station %pM for vdev %i: %i\n",
+                       ath10k_warn("failed to associate station %pM for vdev %i: %i\n",
                                    sta->addr, arvif->vdev_id, ret);
        } else if (old_state == IEEE80211_STA_ASSOC &&
                   new_state == IEEE80211_STA_AUTH &&
@@ -3291,7 +3503,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
 
                ret = ath10k_station_disassoc(ar, arvif, sta);
                if (ret)
-                       ath10k_warn("Failed to disassociate station: %pM vdev %i ret %i\n",
+                       ath10k_warn("failed to disassociate station: %pM vdev %i: %i\n",
                                    sta->addr, arvif->vdev_id, ret);
        }
 exit:
@@ -3339,7 +3551,7 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif,
                                          WMI_STA_PS_PARAM_UAPSD,
                                          arvif->u.sta.uapsd);
        if (ret) {
-               ath10k_warn("could not set uapsd params %d\n", ret);
+               ath10k_warn("failed to set uapsd params: %d\n", ret);
                goto exit;
        }
 
@@ -3352,7 +3564,7 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif,
                                          WMI_STA_PS_PARAM_RX_WAKE_POLICY,
                                          value);
        if (ret)
-               ath10k_warn("could not set rx wake param %d\n", ret);
+               ath10k_warn("failed to set rx wake param: %d\n", ret);
 
 exit:
        return ret;
@@ -3402,13 +3614,13 @@ static int ath10k_conf_tx(struct ieee80211_hw *hw,
        /* FIXME: FW accepts wmm params per hw, not per vif */
        ret = ath10k_wmi_pdev_set_wmm_params(ar, &ar->wmm_params);
        if (ret) {
-               ath10k_warn("could not set wmm params %d\n", ret);
+               ath10k_warn("failed to set wmm params: %d\n", ret);
                goto exit;
        }
 
        ret = ath10k_conf_tx_uapsd(ar, vif, ac, params->uapsd);
        if (ret)
-               ath10k_warn("could not set sta uapsd %d\n", ret);
+               ath10k_warn("failed to set sta uapsd: %d\n", ret);
 
 exit:
        mutex_unlock(&ar->conf_mutex);
@@ -3461,7 +3673,7 @@ static int ath10k_remain_on_channel(struct ieee80211_hw *hw,
 
        ret = ath10k_start_scan(ar, &arg);
        if (ret) {
-               ath10k_warn("could not start roc scan (%d)\n", ret);
+               ath10k_warn("failed to start roc scan: %d\n", ret);
                spin_lock_bh(&ar->data_lock);
                ar->scan.in_progress = false;
                spin_unlock_bh(&ar->data_lock);
@@ -3470,7 +3682,7 @@ static int ath10k_remain_on_channel(struct ieee80211_hw *hw,
 
        ret = wait_for_completion_timeout(&ar->scan.on_channel, 3*HZ);
        if (ret == 0) {
-               ath10k_warn("could not switch to channel for roc scan\n");
+               ath10k_warn("failed to switch to channel for roc scan\n");
                ath10k_abort_scan(ar);
                ret = -ETIMEDOUT;
                goto exit;
@@ -3511,7 +3723,7 @@ static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
 
                ret = ath10k_mac_set_rts(arvif, value);
                if (ret) {
-                       ath10k_warn("could not set rts threshold for vdev %d (%d)\n",
+                       ath10k_warn("failed to set rts threshold for vdev %d: %d\n",
                                    arvif->vdev_id, ret);
                        break;
                }
@@ -3534,7 +3746,7 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
 
                ret = ath10k_mac_set_rts(arvif, value);
                if (ret) {
-                       ath10k_warn("could not set fragmentation threshold for vdev %d (%d)\n",
+                       ath10k_warn("failed to set fragmentation threshold for vdev %d: %d\n",
                                    arvif->vdev_id, ret);
                        break;
                }
@@ -3544,7 +3756,8 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
        return ret;
 }
 
-static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+static void ath10k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                        u32 queues, bool drop)
 {
        struct ath10k *ar = hw->priv;
        bool skip;
@@ -3573,7 +3786,7 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
                }), ATH10K_FLUSH_TIMEOUT_HZ);
 
        if (ret <= 0 || skip)
-               ath10k_warn("tx not flushed (skip %i ar-state %i): %i\n",
+               ath10k_warn("failed to flush transmit queue (skip %i ar-state %i): %i\n",
                            skip, ar->state, ret);
 
 skip:
@@ -3608,7 +3821,7 @@ static int ath10k_suspend(struct ieee80211_hw *hw,
 
        ret = ath10k_hif_suspend(ar);
        if (ret) {
-               ath10k_warn("could not suspend hif (%d)\n", ret);
+               ath10k_warn("failed to suspend hif: %d\n", ret);
                goto resume;
        }
 
@@ -3617,7 +3830,7 @@ static int ath10k_suspend(struct ieee80211_hw *hw,
 resume:
        ret = ath10k_wmi_pdev_resume_target(ar);
        if (ret)
-               ath10k_warn("could not resume target (%d)\n", ret);
+               ath10k_warn("failed to resume target: %d\n", ret);
 
        ret = 1;
 exit:
@@ -3634,14 +3847,14 @@ static int ath10k_resume(struct ieee80211_hw *hw)
 
        ret = ath10k_hif_resume(ar);
        if (ret) {
-               ath10k_warn("could not resume hif (%d)\n", ret);
+               ath10k_warn("failed to resume hif: %d\n", ret);
                ret = 1;
                goto exit;
        }
 
        ret = ath10k_wmi_pdev_resume_target(ar);
        if (ret) {
-               ath10k_warn("could not resume target (%d)\n", ret);
+               ath10k_warn("failed to resume target: %d\n", ret);
                ret = 1;
                goto exit;
        }
@@ -3964,7 +4177,7 @@ static int ath10k_set_fixed_rate_param(struct ath10k_vif *arvif,
        ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
                                        vdev_param, fixed_rate);
        if (ret) {
-               ath10k_warn("Could not set fixed_rate param 0x%02x: %d\n",
+               ath10k_warn("failed to set fixed rate param 0x%02x: %d\n",
                            fixed_rate, ret);
                ret = -EINVAL;
                goto exit;
@@ -3977,7 +4190,7 @@ static int ath10k_set_fixed_rate_param(struct ath10k_vif *arvif,
                                        vdev_param, fixed_nss);
 
        if (ret) {
-               ath10k_warn("Could not set fixed_nss param %d: %d\n",
+               ath10k_warn("failed to set fixed nss param %d: %d\n",
                            fixed_nss, ret);
                ret = -EINVAL;
                goto exit;
@@ -3990,7 +4203,7 @@ static int ath10k_set_fixed_rate_param(struct ath10k_vif *arvif,
                                        force_sgi);
 
        if (ret) {
-               ath10k_warn("Could not set sgi param %d: %d\n",
+               ath10k_warn("failed to set sgi param %d: %d\n",
                            force_sgi, ret);
                ret = -EINVAL;
                goto exit;
@@ -4026,7 +4239,7 @@ static int ath10k_set_bitrate_mask(struct ieee80211_hw *hw,
        }
 
        if (fixed_rate == WMI_FIXED_RATE_NONE && force_sgi) {
-               ath10k_warn("Could not force SGI usage for default rate settings\n");
+               ath10k_warn("failed to force SGI usage for default rate settings\n");
                return -EINVAL;
        }
 
@@ -4034,14 +4247,6 @@ static int ath10k_set_bitrate_mask(struct ieee80211_hw *hw,
                                           fixed_nss, force_sgi);
 }
 
-static void ath10k_channel_switch_beacon(struct ieee80211_hw *hw,
-                                        struct ieee80211_vif *vif,
-                                        struct cfg80211_chan_def *chandef)
-{
-       /* there's no need to do anything here. vif->csa_active is enough */
-       return;
-}
-
 static void ath10k_sta_rc_update(struct ieee80211_hw *hw,
                                 struct ieee80211_vif *vif,
                                 struct ieee80211_sta *sta,
@@ -4072,8 +4277,8 @@ static void ath10k_sta_rc_update(struct ieee80211_hw *hw,
                        bw = WMI_PEER_CHWIDTH_80MHZ;
                        break;
                case IEEE80211_STA_RX_BW_160:
-                       ath10k_warn("mac sta rc update for %pM: invalid bw %d\n",
-                                   sta->addr, sta->bandwidth);
+                       ath10k_warn("Invalid bandwith %d in rc update for %pM\n",
+                                   sta->bandwidth, sta->addr);
                        bw = WMI_PEER_CHWIDTH_20MHZ;
                        break;
                }
@@ -4099,8 +4304,8 @@ static void ath10k_sta_rc_update(struct ieee80211_hw *hw,
                        smps = WMI_PEER_SMPS_DYNAMIC;
                        break;
                case IEEE80211_SMPS_NUM_MODES:
-                       ath10k_warn("mac sta rc update for %pM: invalid smps: %d\n",
-                                   sta->addr, sta->smps_mode);
+                       ath10k_warn("Invalid smps %d in sta rc update for %pM\n",
+                                   sta->smps_mode, sta->addr);
                        smps = WMI_PEER_SMPS_PS_NONE;
                        break;
                }
@@ -4108,15 +4313,6 @@ static void ath10k_sta_rc_update(struct ieee80211_hw *hw,
                arsta->smps = smps;
        }
 
-       if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) {
-               /* FIXME: Not implemented. Probably the only way to do it would
-                * be to re-assoc the peer. */
-               changed &= ~IEEE80211_RC_SUPP_RATES_CHANGED;
-               ath10k_dbg(ATH10K_DBG_MAC,
-                          "mac sta rc update for %pM: changing supported rates not implemented\n",
-                          sta->addr);
-       }
-
        arsta->changed |= changed;
 
        spin_unlock_bh(&ar->data_lock);
@@ -4154,10 +4350,11 @@ static const struct ieee80211_ops ath10k_ops = {
        .set_frag_threshold             = ath10k_set_frag_threshold,
        .flush                          = ath10k_flush,
        .tx_last_beacon                 = ath10k_tx_last_beacon,
+       .set_antenna                    = ath10k_set_antenna,
+       .get_antenna                    = ath10k_get_antenna,
        .restart_complete               = ath10k_restart_complete,
        .get_survey                     = ath10k_get_survey,
        .set_bitrate_mask               = ath10k_set_bitrate_mask,
-       .channel_switch_beacon          = ath10k_channel_switch_beacon,
        .sta_rc_update                  = ath10k_sta_rc_update,
        .get_tsf                        = ath10k_get_tsf,
 #ifdef CONFIG_PM
@@ -4503,6 +4700,18 @@ int ath10k_mac_register(struct ath10k *ar)
                BIT(NL80211_IFTYPE_ADHOC) |
                BIT(NL80211_IFTYPE_AP);
 
+       if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
+               /* TODO:  Have to deal with 2x2 chips if/when the come out. */
+               ar->supp_tx_chainmask = TARGET_10X_TX_CHAIN_MASK;
+               ar->supp_rx_chainmask = TARGET_10X_RX_CHAIN_MASK;
+       } else {
+               ar->supp_tx_chainmask = TARGET_TX_CHAIN_MASK;
+               ar->supp_rx_chainmask = TARGET_RX_CHAIN_MASK;
+       }
+
+       ar->hw->wiphy->available_antennas_rx = ar->supp_rx_chainmask;
+       ar->hw->wiphy->available_antennas_tx = ar->supp_tx_chainmask;
+
        if (!test_bit(ATH10K_FW_FEATURE_NO_P2P, ar->fw_features))
                ar->hw->wiphy->interface_modes |=
                        BIT(NL80211_IFTYPE_P2P_CLIENT) |
@@ -4516,7 +4725,6 @@ int ath10k_mac_register(struct ath10k *ar)
                        IEEE80211_HW_REPORTS_TX_ACK_STATUS |
                        IEEE80211_HW_HAS_RATE_CONTROL |
                        IEEE80211_HW_SUPPORTS_STATIC_SMPS |
-                       IEEE80211_HW_WANT_MONITOR_VIF |
                        IEEE80211_HW_AP_LINK_PS |
                        IEEE80211_HW_SPECTRUM_MGMT;
 
@@ -4570,19 +4778,19 @@ int ath10k_mac_register(struct ath10k *ar)
                                                             NL80211_DFS_UNSET);
 
                if (!ar->dfs_detector)
-                       ath10k_warn("dfs pattern detector init failed\n");
+                       ath10k_warn("failed to initialise DFS pattern detector\n");
        }
 
        ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy,
                            ath10k_reg_notifier);
        if (ret) {
-               ath10k_err("Regulatory initialization failed: %i\n", ret);
+               ath10k_err("failed to initialise regulatory: %i\n", ret);
                goto err_free;
        }
 
        ret = ieee80211_register_hw(ar->hw);
        if (ret) {
-               ath10k_err("ieee80211 registration failed: %d\n", ret);
+               ath10k_err("failed to register ieee80211: %d\n", ret);
                goto err_free;
        }
 
index 9d242d801d9d354f772b74257826427f0a598180..d0004d59c97ec6c9292266662d9fd93cdd9eaf5e 100644 (file)
@@ -39,15 +39,28 @@ enum ath10k_pci_irq_mode {
        ATH10K_PCI_IRQ_MSI = 2,
 };
 
-static unsigned int ath10k_target_ps;
+enum ath10k_pci_reset_mode {
+       ATH10K_PCI_RESET_AUTO = 0,
+       ATH10K_PCI_RESET_WARM_ONLY = 1,
+};
+
+static unsigned int ath10k_pci_target_ps;
 static unsigned int ath10k_pci_irq_mode = ATH10K_PCI_IRQ_AUTO;
+static unsigned int ath10k_pci_reset_mode = ATH10K_PCI_RESET_AUTO;
 
-module_param(ath10k_target_ps, uint, 0644);
-MODULE_PARM_DESC(ath10k_target_ps, "Enable ath10k Target (SoC) PS option");
+module_param_named(target_ps, ath10k_pci_target_ps, uint, 0644);
+MODULE_PARM_DESC(target_ps, "Enable ath10k Target (SoC) PS option");
 
 module_param_named(irq_mode, ath10k_pci_irq_mode, uint, 0644);
 MODULE_PARM_DESC(irq_mode, "0: auto, 1: legacy, 2: msi (default: 0)");
 
+module_param_named(reset_mode, ath10k_pci_reset_mode, uint, 0644);
+MODULE_PARM_DESC(reset_mode, "0: auto, 1: warm only (default: 0)");
+
+/* how long wait to wait for target to initialise, in ms */
+#define ATH10K_PCI_TARGET_WAIT 3000
+#define ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS 3
+
 #define QCA988X_2_0_DEVICE_ID  (0x003c)
 
 static DEFINE_PCI_DEVICE_TABLE(ath10k_pci_id_table) = {
@@ -346,9 +359,10 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
         *   2) Buffer in DMA-able space
         */
        orig_nbytes = nbytes;
-       data_buf = (unsigned char *)pci_alloc_consistent(ar_pci->pdev,
-                                                        orig_nbytes,
-                                                        &ce_data_base);
+       data_buf = (unsigned char *)dma_alloc_coherent(ar->dev,
+                                                      orig_nbytes,
+                                                      &ce_data_base,
+                                                      GFP_ATOMIC);
 
        if (!data_buf) {
                ret = -ENOMEM;
@@ -442,12 +456,12 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
                                __le32_to_cpu(((__le32 *)data_buf)[i]);
                }
        } else
-               ath10k_dbg(ATH10K_DBG_PCI, "%s failure (0x%x)\n",
-                          __func__, address);
+               ath10k_warn("failed to read diag value at 0x%x: %d\n",
+                           address, ret);
 
        if (data_buf)
-               pci_free_consistent(ar_pci->pdev, orig_nbytes,
-                                   data_buf, ce_data_base);
+               dma_free_coherent(ar->dev, orig_nbytes, data_buf,
+                                 ce_data_base);
 
        return ret;
 }
@@ -490,9 +504,10 @@ static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
         *   2) Buffer in DMA-able space
         */
        orig_nbytes = nbytes;
-       data_buf = (unsigned char *)pci_alloc_consistent(ar_pci->pdev,
-                                                        orig_nbytes,
-                                                        &ce_data_base);
+       data_buf = (unsigned char *)dma_alloc_coherent(ar->dev,
+                                                      orig_nbytes,
+                                                      &ce_data_base,
+                                                      GFP_ATOMIC);
        if (!data_buf) {
                ret = -ENOMEM;
                goto done;
@@ -588,13 +603,13 @@ static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
 
 done:
        if (data_buf) {
-               pci_free_consistent(ar_pci->pdev, orig_nbytes, data_buf,
-                                   ce_data_base);
+               dma_free_coherent(ar->dev, orig_nbytes, data_buf,
+                                 ce_data_base);
        }
 
        if (ret != 0)
-               ath10k_dbg(ATH10K_DBG_PCI, "%s failure (0x%x)\n", __func__,
-                          address);
+               ath10k_warn("failed to write diag value at 0x%x: %d\n",
+                           address, ret);
 
        return ret;
 }
@@ -747,17 +762,21 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
        struct ath10k_pci_pipe *pci_pipe = &ar_pci->pipe_info[pipe_id];
        struct ath10k_ce_pipe *ce_pipe = pci_pipe->ce_hdl;
        struct ath10k_ce_ring *src_ring = ce_pipe->src_ring;
-       unsigned int nentries_mask = src_ring->nentries_mask;
-       unsigned int sw_index = src_ring->sw_index;
-       unsigned int write_index = src_ring->write_index;
-       int err, i;
+       unsigned int nentries_mask;
+       unsigned int sw_index;
+       unsigned int write_index;
+       int err, i = 0;
 
        spin_lock_bh(&ar_pci->ce_lock);
 
+       nentries_mask = src_ring->nentries_mask;
+       sw_index = src_ring->sw_index;
+       write_index = src_ring->write_index;
+
        if (unlikely(CE_RING_DELTA(nentries_mask,
                                   write_index, sw_index - 1) < n_items)) {
                err = -ENOBUFS;
-               goto unlock;
+               goto err;
        }
 
        for (i = 0; i < n_items - 1; i++) {
@@ -774,7 +793,7 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
                                            items[i].transfer_id,
                                            CE_SEND_FLAG_GATHER);
                if (err)
-                       goto unlock;
+                       goto err;
        }
 
        /* `i` is equal to `n_items -1` after for() */
@@ -792,10 +811,15 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
                                    items[i].transfer_id,
                                    0);
        if (err)
-               goto unlock;
+               goto err;
+
+       spin_unlock_bh(&ar_pci->ce_lock);
+       return 0;
+
+err:
+       for (; i > 0; i--)
+               __ath10k_ce_send_revert(ce_pipe);
 
-       err = 0;
-unlock:
        spin_unlock_bh(&ar_pci->ce_lock);
        return err;
 }
@@ -803,6 +827,9 @@ static int ath10k_pci_hif_tx_sg(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);
+
+       ath10k_dbg(ATH10K_DBG_PCI, "pci hif get free queue number\n");
+
        return ath10k_ce_num_free_src_entries(ar_pci->pipe_info[pipe].ce_hdl);
 }
 
@@ -854,6 +881,8 @@ static void ath10k_pci_hif_dump_area(struct ath10k *ar)
 static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
                                               int force)
 {
+       ath10k_dbg(ATH10K_DBG_PCI, "pci hif send complete check\n");
+
        if (!force) {
                int resources;
                /*
@@ -880,7 +909,7 @@ static void ath10k_pci_hif_set_callbacks(struct ath10k *ar,
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
 
-       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+       ath10k_dbg(ATH10K_DBG_PCI, "pci hif set callbacks\n");
 
        memcpy(&ar_pci->msg_callbacks_current, callbacks,
               sizeof(ar_pci->msg_callbacks_current));
@@ -938,6 +967,8 @@ static int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar,
 {
        int ret = 0;
 
+       ath10k_dbg(ATH10K_DBG_PCI, "pci hif map service\n");
+
        /* polling for received messages not supported */
        *dl_is_polled = 0;
 
@@ -997,6 +1028,8 @@ static void ath10k_pci_hif_get_default_pipe(struct ath10k *ar,
 {
        int ul_is_polled, dl_is_polled;
 
+       ath10k_dbg(ATH10K_DBG_PCI, "pci hif get default pipe\n");
+
        (void)ath10k_pci_hif_map_service_to_pipe(ar,
                                                 ATH10K_HTC_SVC_ID_RSVD_CTRL,
                                                 ul_pipe,
@@ -1098,6 +1131,8 @@ static int ath10k_pci_hif_start(struct ath10k *ar)
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        int ret, ret_early;
 
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot hif start\n");
+
        ath10k_pci_free_early_irq(ar);
        ath10k_pci_kill_tasklet(ar);
 
@@ -1233,18 +1268,10 @@ 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 ath10k_pci_pipe *pipe_info;
-       int pipe_num;
+       int i;
 
-       for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) {
-               pipe_info = &ar_pci->pipe_info[pipe_num];
-               if (pipe_info->ce_hdl) {
-                       ath10k_ce_deinit(pipe_info->ce_hdl);
-                       pipe_info->ce_hdl = NULL;
-                       pipe_info->buf_sz = 0;
-               }
-       }
+       for (i = 0; i < CE_COUNT; i++)
+               ath10k_ce_deinit_pipe(ar, i);
 }
 
 static void ath10k_pci_hif_stop(struct ath10k *ar)
@@ -1252,7 +1279,10 @@ static void ath10k_pci_hif_stop(struct ath10k *ar)
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        int ret;
 
-       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot hif stop\n");
+
+       if (WARN_ON(!ar_pci->started))
+               return;
 
        ret = ath10k_ce_disable_interrupts(ar);
        if (ret)
@@ -1697,30 +1727,49 @@ static int ath10k_pci_init_config(struct ath10k *ar)
        return 0;
 }
 
+static int ath10k_pci_alloc_ce(struct ath10k *ar)
+{
+       int i, ret;
+
+       for (i = 0; i < CE_COUNT; i++) {
+               ret = ath10k_ce_alloc_pipe(ar, i, &host_ce_config_wlan[i]);
+               if (ret) {
+                       ath10k_err("failed to allocate copy engine pipe %d: %d\n",
+                                  i, ret);
+                       return ret;
+               }
+       }
 
+       return 0;
+}
+
+static void ath10k_pci_free_ce(struct ath10k *ar)
+{
+       int i;
+
+       for (i = 0; i < CE_COUNT; i++)
+               ath10k_ce_free_pipe(ar, i);
+}
 
 static int ath10k_pci_ce_init(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        struct ath10k_pci_pipe *pipe_info;
        const struct ce_attr *attr;
-       int pipe_num;
+       int pipe_num, ret;
 
        for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) {
                pipe_info = &ar_pci->pipe_info[pipe_num];
+               pipe_info->ce_hdl = &ar_pci->ce_states[pipe_num];
                pipe_info->pipe_num = pipe_num;
                pipe_info->hif_ce_state = ar;
                attr = &host_ce_config_wlan[pipe_num];
 
-               pipe_info->ce_hdl = ath10k_ce_init(ar, pipe_num, attr);
-               if (pipe_info->ce_hdl == NULL) {
-                       ath10k_err("failed to initialize CE for pipe: %d\n",
-                                  pipe_num);
-
-                       /* It is safe to call it here. It checks if ce_hdl is
-                        * valid for each pipe */
-                       ath10k_pci_ce_deinit(ar);
-                       return -1;
+               ret = ath10k_ce_init_pipe(ar, pipe_num, attr);
+               if (ret) {
+                       ath10k_err("failed to initialize copy engine pipe %d: %d\n",
+                                  pipe_num, ret);
+                       return ret;
                }
 
                if (pipe_num == CE_COUNT - 1) {
@@ -1741,16 +1790,15 @@ static int ath10k_pci_ce_init(struct ath10k *ar)
 static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       u32 fw_indicator_address, fw_indicator;
+       u32 fw_indicator;
 
        ath10k_pci_wake(ar);
 
-       fw_indicator_address = ar_pci->fw_indicator_address;
-       fw_indicator = ath10k_pci_read32(ar, fw_indicator_address);
+       fw_indicator = ath10k_pci_read32(ar, FW_INDICATOR_ADDRESS);
 
        if (fw_indicator & FW_IND_EVENT_PENDING) {
                /* ACK: clear Target-side pending event */
-               ath10k_pci_write32(ar, fw_indicator_address,
+               ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS,
                                   fw_indicator & ~FW_IND_EVENT_PENDING);
 
                if (ar_pci->started) {
@@ -1767,13 +1815,32 @@ static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar)
        ath10k_pci_sleep(ar);
 }
 
+/* this function effectively clears target memory controller assert line */
+static void ath10k_pci_warm_reset_si0(struct ath10k *ar)
+{
+       u32 val;
+
+       val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS);
+       ath10k_pci_soc_write32(ar, SOC_RESET_CONTROL_ADDRESS,
+                              val | SOC_RESET_CONTROL_SI0_RST_MASK);
+       val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS);
+
+       msleep(10);
+
+       val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS);
+       ath10k_pci_soc_write32(ar, SOC_RESET_CONTROL_ADDRESS,
+                              val & ~SOC_RESET_CONTROL_SI0_RST_MASK);
+       val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS);
+
+       msleep(10);
+}
+
 static int ath10k_pci_warm_reset(struct ath10k *ar)
 {
-       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        int ret = 0;
        u32 val;
 
-       ath10k_dbg(ATH10K_DBG_BOOT, "boot performing warm chip reset\n");
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot warm reset\n");
 
        ret = ath10k_do_pci_wake(ar);
        if (ret) {
@@ -1801,7 +1868,7 @@ static int ath10k_pci_warm_reset(struct ath10k *ar)
        msleep(100);
 
        /* clear fw indicator */
-       ath10k_pci_write32(ar, ar_pci->fw_indicator_address, 0);
+       ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS, 0);
 
        /* clear target LF timer interrupts */
        val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
@@ -1826,6 +1893,8 @@ static int ath10k_pci_warm_reset(struct ath10k *ar)
                                SOC_RESET_CONTROL_ADDRESS);
        msleep(10);
 
+       ath10k_pci_warm_reset_si0(ar);
+
        /* debug */
        val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
                                PCIE_INTR_CAUSE_ADDRESS);
@@ -1934,7 +2003,9 @@ static int __ath10k_pci_hif_power_up(struct ath10k *ar, bool cold_reset)
                irq_mode = "legacy";
 
        if (!test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags))
-               ath10k_info("pci irq %s\n", irq_mode);
+               ath10k_info("pci irq %s irq_mode %d reset_mode %d\n",
+                           irq_mode, ath10k_pci_irq_mode,
+                           ath10k_pci_reset_mode);
 
        return 0;
 
@@ -1952,23 +2023,52 @@ static int __ath10k_pci_hif_power_up(struct ath10k *ar, bool cold_reset)
        return ret;
 }
 
+static int ath10k_pci_hif_power_up_warm(struct ath10k *ar)
+{
+       int i, ret;
+
+       /*
+        * Sometime warm reset succeeds after retries.
+        *
+        * FIXME: It might be possible to tune ath10k_pci_warm_reset() to work
+        * at first try.
+        */
+       for (i = 0; i < ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS; i++) {
+               ret = __ath10k_pci_hif_power_up(ar, false);
+               if (ret == 0)
+                       break;
+
+               ath10k_warn("failed to warm reset (attempt %d out of %d): %d\n",
+                           i + 1, ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS, ret);
+       }
+
+       return ret;
+}
+
 static int ath10k_pci_hif_power_up(struct ath10k *ar)
 {
        int ret;
 
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot hif power up\n");
+
        /*
         * Hardware CUS232 version 2 has some issues with cold reset and the
         * preferred (and safer) way to perform a device reset is through a
         * warm reset.
         *
-        * Warm reset doesn't always work though (notably after a firmware
-        * crash) so fall back to cold reset if necessary.
+        * Warm reset doesn't always work though so fall back to cold reset may
+        * be necessary.
         */
-       ret = __ath10k_pci_hif_power_up(ar, false);
+       ret = ath10k_pci_hif_power_up_warm(ar);
        if (ret) {
-               ath10k_warn("failed to power up target using warm reset (%d), trying cold reset\n",
+               ath10k_warn("failed to power up target using warm reset: %d\n",
                            ret);
 
+               if (ath10k_pci_reset_mode == ATH10K_PCI_RESET_WARM_ONLY)
+                       return ret;
+
+               ath10k_warn("trying cold reset\n");
+
                ret = __ath10k_pci_hif_power_up(ar, true);
                if (ret) {
                        ath10k_err("failed to power up target using cold reset too (%d)\n",
@@ -1984,12 +2084,14 @@ static void ath10k_pci_hif_power_down(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
 
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot hif power down\n");
+
        ath10k_pci_free_early_irq(ar);
        ath10k_pci_kill_tasklet(ar);
        ath10k_pci_deinit_irq(ar);
+       ath10k_pci_ce_deinit(ar);
        ath10k_pci_warm_reset(ar);
 
-       ath10k_pci_ce_deinit(ar);
        if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
                ath10k_do_pci_sleep(ar);
 }
@@ -2137,7 +2239,6 @@ static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg)
 static void ath10k_pci_early_irq_tasklet(unsigned long data)
 {
        struct ath10k *ar = (struct ath10k *)data;
-       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        u32 fw_ind;
        int ret;
 
@@ -2148,14 +2249,11 @@ static void ath10k_pci_early_irq_tasklet(unsigned long data)
                return;
        }
 
-       fw_ind = ath10k_pci_read32(ar, ar_pci->fw_indicator_address);
+       fw_ind = ath10k_pci_read32(ar, FW_INDICATOR_ADDRESS);
        if (fw_ind & FW_IND_EVENT_PENDING) {
-               ath10k_pci_write32(ar, ar_pci->fw_indicator_address,
+               ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS,
                                   fw_ind & ~FW_IND_EVENT_PENDING);
-
-               /* Some structures are unavailable during early boot or at
-                * driver teardown so just print that the device has crashed. */
-               ath10k_warn("device crashed - no diagnostics available\n");
+               ath10k_pci_hif_dump_area(ar);
        }
 
        ath10k_pci_sleep(ar);
@@ -2385,33 +2483,69 @@ static int ath10k_pci_deinit_irq(struct ath10k *ar)
 static int ath10k_pci_wait_for_target_init(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       int wait_limit = 300; /* 3 sec */
+       unsigned long timeout;
        int ret;
+       u32 val;
+
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot waiting target to initialise\n");
 
        ret = ath10k_pci_wake(ar);
        if (ret) {
-               ath10k_err("failed to wake up target: %d\n", ret);
+               ath10k_err("failed to wake up target for init: %d\n", ret);
                return ret;
        }
 
-       while (wait_limit-- &&
-              !(ioread32(ar_pci->mem + FW_INDICATOR_ADDRESS) &
-                FW_IND_INITIALIZED)) {
+       timeout = jiffies + msecs_to_jiffies(ATH10K_PCI_TARGET_WAIT);
+
+       do {
+               val = ath10k_pci_read32(ar, FW_INDICATOR_ADDRESS);
+
+               ath10k_dbg(ATH10K_DBG_BOOT, "boot target indicator %x\n", val);
+
+               /* target should never return this */
+               if (val == 0xffffffff)
+                       continue;
+
+               /* the device has crashed so don't bother trying anymore */
+               if (val & FW_IND_EVENT_PENDING)
+                       break;
+
+               if (val & FW_IND_INITIALIZED)
+                       break;
+
                if (ar_pci->num_msi_intrs == 0)
                        /* Fix potential race by repeating CORE_BASE writes */
-                       iowrite32(PCIE_INTR_FIRMWARE_MASK |
-                                 PCIE_INTR_CE_MASK_ALL,
-                                 ar_pci->mem + (SOC_CORE_BASE_ADDRESS |
-                                                PCIE_INTR_ENABLE_ADDRESS));
+                       ath10k_pci_soc_write32(ar, PCIE_INTR_ENABLE_ADDRESS,
+                                              PCIE_INTR_FIRMWARE_MASK |
+                                              PCIE_INTR_CE_MASK_ALL);
+
                mdelay(10);
-       }
+       } while (time_before(jiffies, timeout));
 
-       if (wait_limit < 0) {
-               ath10k_err("target stalled\n");
+       if (val == 0xffffffff) {
+               ath10k_err("failed to read device register, device is gone\n");
                ret = -EIO;
                goto out;
        }
 
+       if (val & FW_IND_EVENT_PENDING) {
+               ath10k_warn("device has crashed during init\n");
+               ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS,
+                                  val & ~FW_IND_EVENT_PENDING);
+               ath10k_pci_hif_dump_area(ar);
+               ret = -ECOMM;
+               goto out;
+       }
+
+       if (!(val & FW_IND_INITIALIZED)) {
+               ath10k_err("failed to receive initialized event from target: %08x\n",
+                          val);
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot target initialised\n");
+
 out:
        ath10k_pci_sleep(ar);
        return ret;
@@ -2422,6 +2556,8 @@ static int ath10k_pci_cold_reset(struct ath10k *ar)
        int i, ret;
        u32 val;
 
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot cold reset\n");
+
        ret = ath10k_do_pci_wake(ar);
        if (ret) {
                ath10k_err("failed to wake up target: %d\n",
@@ -2453,6 +2589,9 @@ static int ath10k_pci_cold_reset(struct ath10k *ar)
        }
 
        ath10k_do_pci_sleep(ar);
+
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot cold reset complete\n");
+
        return 0;
 }
 
@@ -2484,7 +2623,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
        struct ath10k_pci *ar_pci;
        u32 lcr_val, chip_id;
 
-       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+       ath10k_dbg(ATH10K_DBG_PCI, "pci probe\n");
 
        ar_pci = kzalloc(sizeof(*ar_pci), GFP_KERNEL);
        if (ar_pci == NULL)
@@ -2503,7 +2642,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
                goto err_ar_pci;
        }
 
-       if (ath10k_target_ps)
+       if (ath10k_pci_target_ps)
                set_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features);
 
        ath10k_pci_dump_features(ar_pci);
@@ -2516,23 +2655,10 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
        }
 
        ar_pci->ar = ar;
-       ar_pci->fw_indicator_address = FW_INDICATOR_ADDRESS;
        atomic_set(&ar_pci->keep_awake_count, 0);
 
        pci_set_drvdata(pdev, ar);
 
-       /*
-        * Without any knowledge of the Host, the Target may have been reset or
-        * power cycled and its Config Space may no longer reflect the PCI
-        * address space that was assigned earlier by the PCI infrastructure.
-        * Refresh it now.
-        */
-       ret = pci_assign_resource(pdev, BAR_NUM);
-       if (ret) {
-               ath10k_err("failed to assign PCI space: %d\n", ret);
-               goto err_ar;
-       }
-
        ret = pci_enable_device(pdev);
        if (ret) {
                ath10k_err("failed to enable PCI device: %d\n", ret);
@@ -2594,16 +2720,24 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
 
        ath10k_do_pci_sleep(ar);
 
+       ret = ath10k_pci_alloc_ce(ar);
+       if (ret) {
+               ath10k_err("failed to allocate copy engine pipes: %d\n", ret);
+               goto err_iomap;
+       }
+
        ath10k_dbg(ATH10K_DBG_BOOT, "boot pci_mem 0x%p\n", ar_pci->mem);
 
        ret = ath10k_core_register(ar, chip_id);
        if (ret) {
                ath10k_err("failed to register driver core: %d\n", ret);
-               goto err_iomap;
+               goto err_free_ce;
        }
 
        return 0;
 
+err_free_ce:
+       ath10k_pci_free_ce(ar);
 err_iomap:
        pci_iounmap(pdev, mem);
 err_master:
@@ -2626,7 +2760,7 @@ static void ath10k_pci_remove(struct pci_dev *pdev)
        struct ath10k *ar = pci_get_drvdata(pdev);
        struct ath10k_pci *ar_pci;
 
-       ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+       ath10k_dbg(ATH10K_DBG_PCI, "pci remove\n");
 
        if (!ar)
                return;
@@ -2636,9 +2770,8 @@ static void ath10k_pci_remove(struct pci_dev *pdev)
        if (!ar_pci)
                return;
 
-       tasklet_kill(&ar_pci->msi_fw_err);
-
        ath10k_core_unregister(ar);
+       ath10k_pci_free_ce(ar);
 
        pci_iounmap(pdev, ar_pci->mem);
        pci_release_region(pdev, BAR_NUM);
@@ -2680,6 +2813,5 @@ 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_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_FW_2_FILE);
 MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_BOARD_DATA_FILE);
index b43fdb4f731973544e1a55f700c779e4191d76bb..dfdebb4157aa177acde13ea1149b5a7490c5593e 100644 (file)
@@ -189,9 +189,6 @@ struct ath10k_pci {
 
        struct ath10k_hif_cb msg_callbacks_current;
 
-       /* Target address used to signal a pending firmware event */
-       u32 fw_indicator_address;
-
        /* Copy Engine used for Diagnostic Accesses */
        struct ath10k_ce_pipe *ce_diag;
 
index 0541dd939ce9d8be7dc7e195ef952526abab0255..82669a77e553b8f6902c7dc03f99e24b42a3dea6 100644 (file)
@@ -100,189 +100,6 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
                wake_up(&htt->empty_tx_wq);
 }
 
-static const u8 rx_legacy_rate_idx[] = {
-       3,      /* 0x00  - 11Mbps  */
-       2,      /* 0x01  - 5.5Mbps */
-       1,      /* 0x02  - 2Mbps   */
-       0,      /* 0x03  - 1Mbps   */
-       3,      /* 0x04  - 11Mbps  */
-       2,      /* 0x05  - 5.5Mbps */
-       1,      /* 0x06  - 2Mbps   */
-       0,      /* 0x07  - 1Mbps   */
-       10,     /* 0x08  - 48Mbps  */
-       8,      /* 0x09  - 24Mbps  */
-       6,      /* 0x0A  - 12Mbps  */
-       4,      /* 0x0B  - 6Mbps   */
-       11,     /* 0x0C  - 54Mbps  */
-       9,      /* 0x0D  - 36Mbps  */
-       7,      /* 0x0E  - 18Mbps  */
-       5,      /* 0x0F  - 9Mbps   */
-};
-
-static void process_rx_rates(struct ath10k *ar, struct htt_rx_info *info,
-                            enum ieee80211_band band,
-                            struct ieee80211_rx_status *status)
-{
-       u8 cck, rate, rate_idx, bw, sgi, mcs, nss;
-       u8 info0 = info->rate.info0;
-       u32 info1 = info->rate.info1;
-       u32 info2 = info->rate.info2;
-       u8 preamble = 0;
-
-       /* Check if valid fields */
-       if (!(info0 & HTT_RX_INDICATION_INFO0_START_VALID))
-               return;
-
-       preamble = MS(info1, HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE);
-
-       switch (preamble) {
-       case HTT_RX_LEGACY:
-               cck = info0 & HTT_RX_INDICATION_INFO0_LEGACY_RATE_CCK;
-               rate = MS(info0, HTT_RX_INDICATION_INFO0_LEGACY_RATE);
-               rate_idx = 0;
-
-               if (rate < 0x08 || rate > 0x0F)
-                       break;
-
-               switch (band) {
-               case IEEE80211_BAND_2GHZ:
-                       if (cck)
-                               rate &= ~BIT(3);
-                       rate_idx = rx_legacy_rate_idx[rate];
-                       break;
-               case IEEE80211_BAND_5GHZ:
-                       rate_idx = rx_legacy_rate_idx[rate];
-                       /* We are using same rate table registering
-                          HW - ath10k_rates[]. In case of 5GHz skip
-                          CCK rates, so -4 here */
-                       rate_idx -= 4;
-                       break;
-               default:
-                       break;
-               }
-
-               status->rate_idx = rate_idx;
-               break;
-       case HTT_RX_HT:
-       case HTT_RX_HT_WITH_TXBF:
-               /* HT-SIG - Table 20-11 in info1 and info2 */
-               mcs = info1 & 0x1F;
-               nss = mcs >> 3;
-               bw = (info1 >> 7) & 1;
-               sgi = (info2 >> 7) & 1;
-
-               status->rate_idx = mcs;
-               status->flag |= RX_FLAG_HT;
-               if (sgi)
-                       status->flag |= RX_FLAG_SHORT_GI;
-               if (bw)
-                       status->flag |= RX_FLAG_40MHZ;
-               break;
-       case HTT_RX_VHT:
-       case HTT_RX_VHT_WITH_TXBF:
-               /* VHT-SIG-A1 in info 1, VHT-SIG-A2 in info2
-                  TODO check this */
-               mcs = (info2 >> 4) & 0x0F;
-               nss = ((info1 >> 10) & 0x07) + 1;
-               bw = info1 & 3;
-               sgi = info2 & 1;
-
-               status->rate_idx = mcs;
-               status->vht_nss = nss;
-
-               if (sgi)
-                       status->flag |= RX_FLAG_SHORT_GI;
-
-               switch (bw) {
-               /* 20MHZ */
-               case 0:
-                       break;
-               /* 40MHZ */
-               case 1:
-                       status->flag |= RX_FLAG_40MHZ;
-                       break;
-               /* 80MHZ */
-               case 2:
-                       status->vht_flag |= RX_VHT_FLAG_80MHZ;
-               }
-
-               status->flag |= RX_FLAG_VHT;
-               break;
-       default:
-               break;
-       }
-}
-
-void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info)
-{
-       struct ieee80211_rx_status *status;
-       struct ieee80211_channel *ch;
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)info->skb->data;
-
-       status = IEEE80211_SKB_RXCB(info->skb);
-       memset(status, 0, sizeof(*status));
-
-       if (info->encrypt_type != HTT_RX_MPDU_ENCRYPT_NONE) {
-               status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED |
-                               RX_FLAG_MMIC_STRIPPED;
-               hdr->frame_control = __cpu_to_le16(
-                               __le16_to_cpu(hdr->frame_control) &
-                               ~IEEE80211_FCTL_PROTECTED);
-       }
-
-       if (info->mic_err)
-               status->flag |= RX_FLAG_MMIC_ERROR;
-
-       if (info->fcs_err)
-               status->flag |= RX_FLAG_FAILED_FCS_CRC;
-
-       if (info->amsdu_more)
-               status->flag |= RX_FLAG_AMSDU_MORE;
-
-       status->signal = info->signal;
-
-       spin_lock_bh(&ar->data_lock);
-       ch = ar->scan_channel;
-       if (!ch)
-               ch = ar->rx_channel;
-       spin_unlock_bh(&ar->data_lock);
-
-       if (!ch) {
-               ath10k_warn("no channel configured; ignoring frame!\n");
-               dev_kfree_skb_any(info->skb);
-               return;
-       }
-
-       process_rx_rates(ar, info, ch->band, status);
-       status->band = ch->band;
-       status->freq = ch->center_freq;
-
-       if (info->rate.info0 & HTT_RX_INDICATION_INFO0_END_VALID) {
-               /* TSF available only in 32-bit */
-               status->mactime = info->tsf & 0xffffffff;
-               status->flag |= RX_FLAG_MACTIME_END;
-       }
-
-       ath10k_dbg(ATH10K_DBG_DATA,
-                  "rx skb %p len %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i\n",
-                  info->skb,
-                  info->skb->len,
-                  status->flag == 0 ? "legacy" : "",
-                  status->flag & RX_FLAG_HT ? "ht" : "",
-                  status->flag & RX_FLAG_VHT ? "vht" : "",
-                  status->flag & RX_FLAG_40MHZ ? "40" : "",
-                  status->vht_flag & RX_VHT_FLAG_80MHZ ? "80" : "",
-                  status->flag & RX_FLAG_SHORT_GI ? "sgi " : "",
-                  status->rate_idx,
-                  status->vht_nss,
-                  status->freq,
-                  status->band, status->flag, info->fcs_err);
-       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "rx skb: ",
-                       info->skb->data, info->skb->len);
-
-       ieee80211_rx(ar->hw, info->skb);
-}
-
 struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id,
                                     const u8 *addr)
 {
index 356dc9c04c9e3981feaeb04d8df4e8f45fc36f7f..aee3e20058f814f2e7a774005d84d77c684442f4 100644 (file)
@@ -21,7 +21,6 @@
 
 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,
                                     const u8 *addr);
index cb1f7b5bcf4cdefa774411e09fa39b80728d82e4..4b7782a529ac6b03c6a1bcd764dabc6568b70a6b 100644 (file)
@@ -639,6 +639,7 @@ int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb)
        struct sk_buff *wmi_skb;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        int len;
+       u32 buf_len = skb->len;
        u16 fc;
 
        hdr = (struct ieee80211_hdr *)skb->data;
@@ -648,6 +649,15 @@ int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb)
                return -EINVAL;
 
        len = sizeof(cmd->hdr) + skb->len;
+
+       if ((ieee80211_is_action(hdr->frame_control) ||
+            ieee80211_is_deauth(hdr->frame_control) ||
+            ieee80211_is_disassoc(hdr->frame_control)) &&
+            ieee80211_has_protected(hdr->frame_control)) {
+               len += IEEE80211_CCMP_MIC_LEN;
+               buf_len += IEEE80211_CCMP_MIC_LEN;
+       }
+
        len = round_up(len, 4);
 
        wmi_skb = ath10k_wmi_alloc_skb(len);
@@ -659,7 +669,7 @@ int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb)
        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));
+       cmd->hdr.buf_len = __cpu_to_le32(buf_len);
 
        memcpy(cmd->hdr.peer_macaddr.addr, ieee80211_get_DA(hdr), ETH_ALEN);
        memcpy(cmd->buf, skb->data, skb->len);
@@ -957,10 +967,16 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
         * frames with Protected Bit set. */
        if (ieee80211_has_protected(hdr->frame_control) &&
            !ieee80211_is_auth(hdr->frame_control)) {
-               status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED |
-                               RX_FLAG_MMIC_STRIPPED;
-               hdr->frame_control = __cpu_to_le16(fc &
+               status->flag |= RX_FLAG_DECRYPTED;
+
+               if (!ieee80211_is_action(hdr->frame_control) &&
+                   !ieee80211_is_deauth(hdr->frame_control) &&
+                   !ieee80211_is_disassoc(hdr->frame_control)) {
+                       status->flag |= RX_FLAG_IV_STRIPPED |
+                                       RX_FLAG_MMIC_STRIPPED;
+                       hdr->frame_control = __cpu_to_le16(fc &
                                        ~IEEE80211_FCTL_PROTECTED);
+               }
        }
 
        ath10k_dbg(ATH10K_DBG_MGMT,
@@ -1362,13 +1378,10 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
        struct sk_buff *bcn;
        int ret, vdev_id = 0;
 
-       ath10k_dbg(ATH10K_DBG_MGMT, "WMI_HOST_SWBA_EVENTID\n");
-
        ev = (struct wmi_host_swba_event *)skb->data;
        map = __le32_to_cpu(ev->vdev_map);
 
-       ath10k_dbg(ATH10K_DBG_MGMT, "host swba:\n"
-                  "-vdev map 0x%x\n",
+       ath10k_dbg(ATH10K_DBG_MGMT, "mgmt swba vdev_map 0x%x\n",
                   ev->vdev_map);
 
        for (; map; map >>= 1, vdev_id++) {
@@ -1385,12 +1398,7 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
                bcn_info = &ev->bcn_info[i];
 
                ath10k_dbg(ATH10K_DBG_MGMT,
-                          "-bcn_info[%d]:\n"
-                          "--tim_len %d\n"
-                          "--tim_mcast %d\n"
-                          "--tim_changed %d\n"
-                          "--tim_num_ps_pending %d\n"
-                          "--tim_bitmap 0x%08x%08x%08x%08x\n",
+                          "mgmt event bcn_info %d tim_len %d mcast %d changed %d num_ps_pending %d bitmap 0x%08x%08x%08x%08x\n",
                           i,
                           __le32_to_cpu(bcn_info->tim_info.tim_len),
                           __le32_to_cpu(bcn_info->tim_info.tim_mcast),
@@ -1439,6 +1447,7 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
                                         ATH10K_SKB_CB(arvif->beacon)->paddr,
                                         arvif->beacon->len, DMA_TO_DEVICE);
                        dev_kfree_skb_any(arvif->beacon);
+                       arvif->beacon = NULL;
                }
 
                ATH10K_SKB_CB(bcn)->paddr = dma_map_single(arvif->ar->dev,
@@ -1448,6 +1457,7 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
                                        ATH10K_SKB_CB(bcn)->paddr);
                if (ret) {
                        ath10k_warn("failed to map beacon: %d\n", ret);
+                       dev_kfree_skb_any(bcn);
                        goto skip;
                }
 
@@ -2365,7 +2375,7 @@ void ath10k_wmi_detach(struct ath10k *ar)
        ar->wmi.num_mem_chunks = 0;
 }
 
-int ath10k_wmi_connect_htc_service(struct ath10k *ar)
+int ath10k_wmi_connect(struct ath10k *ar)
 {
        int status;
        struct ath10k_htc_svc_conn_req conn_req;
@@ -2393,8 +2403,9 @@ int ath10k_wmi_connect_htc_service(struct ath10k *ar)
        return 0;
 }
 
-int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
-                                 u16 rd5g, u16 ctl2g, u16 ctl5g)
+static int ath10k_wmi_main_pdev_set_regdomain(struct ath10k *ar, u16 rd,
+                                             u16 rd2g, u16 rd5g, u16 ctl2g,
+                                             u16 ctl5g)
 {
        struct wmi_pdev_set_regdomain_cmd *cmd;
        struct sk_buff *skb;
@@ -2418,6 +2429,46 @@ int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
                                   ar->wmi.cmd->pdev_set_regdomain_cmdid);
 }
 
+static int ath10k_wmi_10x_pdev_set_regdomain(struct ath10k *ar, u16 rd,
+                                            u16 rd2g, u16 rd5g,
+                                            u16 ctl2g, u16 ctl5g,
+                                            enum wmi_dfs_region dfs_reg)
+{
+       struct wmi_pdev_set_regdomain_cmd_10x *cmd;
+       struct sk_buff *skb;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_pdev_set_regdomain_cmd_10x *)skb->data;
+       cmd->reg_domain = __cpu_to_le32(rd);
+       cmd->reg_domain_2G = __cpu_to_le32(rd2g);
+       cmd->reg_domain_5G = __cpu_to_le32(rd5g);
+       cmd->conformance_test_limit_2G = __cpu_to_le32(ctl2g);
+       cmd->conformance_test_limit_5G = __cpu_to_le32(ctl5g);
+       cmd->dfs_domain = __cpu_to_le32(dfs_reg);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x dfs_region %x\n",
+                  rd, rd2g, rd5g, ctl2g, ctl5g, dfs_reg);
+
+       return ath10k_wmi_cmd_send(ar, skb,
+                                  ar->wmi.cmd->pdev_set_regdomain_cmdid);
+}
+
+int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
+                                 u16 rd5g, u16 ctl2g, u16 ctl5g,
+                                 enum wmi_dfs_region dfs_reg)
+{
+       if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+               return ath10k_wmi_10x_pdev_set_regdomain(ar, rd, rd2g, rd5g,
+                                                       ctl2g, ctl5g, dfs_reg);
+       else
+               return ath10k_wmi_main_pdev_set_regdomain(ar, rd, rd2g, rd5g,
+                                                        ctl2g, ctl5g);
+}
+
 int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
                                const struct wmi_channel_arg *arg)
 {
@@ -3456,8 +3507,9 @@ int ath10k_wmi_peer_assoc(struct ath10k *ar,
                __cpu_to_le32(arg->peer_vht_rates.tx_mcs_set);
 
        ath10k_dbg(ATH10K_DBG_WMI,
-                  "wmi peer assoc vdev %d addr %pM\n",
-                  arg->vdev_id, arg->addr);
+                  "wmi peer assoc vdev %d addr %pM (%s)\n",
+                  arg->vdev_id, arg->addr,
+                  arg->peer_reassoc ? "reassociate" : "new");
        return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_assoc_cmdid);
 }
 
index f51d5ca0141fc61cfe2e84859c2ea2ca66160eca..e93df2c104136a10f03052908ef45ca38f514e6c 100644 (file)
@@ -198,16 +198,6 @@ struct wmi_mac_addr {
        } __packed;
 } __packed;
 
-/* macro to convert MAC address from WMI word format to char array */
-#define WMI_MAC_ADDR_TO_CHAR_ARRAY(pwmi_mac_addr, c_macaddr) do { \
-       (c_macaddr)[0] =  ((pwmi_mac_addr)->word0) & 0xff; \
-       (c_macaddr)[1] = (((pwmi_mac_addr)->word0) >> 8) & 0xff; \
-       (c_macaddr)[2] = (((pwmi_mac_addr)->word0) >> 16) & 0xff; \
-       (c_macaddr)[3] = (((pwmi_mac_addr)->word0) >> 24) & 0xff; \
-       (c_macaddr)[4] =  ((pwmi_mac_addr)->word1) & 0xff; \
-       (c_macaddr)[5] = (((pwmi_mac_addr)->word1) >> 8) & 0xff; \
-       } while (0)
-
 struct wmi_cmd_map {
        u32 init_cmdid;
        u32 start_scan_cmdid;
@@ -2185,6 +2175,31 @@ struct wmi_pdev_set_regdomain_cmd {
        __le32 conformance_test_limit_5G;
 } __packed;
 
+enum wmi_dfs_region {
+       /* Uninitialized dfs domain */
+       WMI_UNINIT_DFS_DOMAIN = 0,
+
+       /* FCC3 dfs domain */
+       WMI_FCC_DFS_DOMAIN = 1,
+
+       /* ETSI dfs domain */
+       WMI_ETSI_DFS_DOMAIN = 2,
+
+       /*Japan dfs domain */
+       WMI_MKK4_DFS_DOMAIN = 3,
+};
+
+struct wmi_pdev_set_regdomain_cmd_10x {
+       __le32 reg_domain;
+       __le32 reg_domain_2G;
+       __le32 reg_domain_5G;
+       __le32 conformance_test_limit_2G;
+       __le32 conformance_test_limit_5G;
+
+       /* dfs domain from wmi_dfs_region */
+       __le32 dfs_domain;
+} __packed;
+
 /* Command to set/unset chip in quiet mode */
 struct wmi_pdev_set_quiet_cmd {
        /* period in TUs */
@@ -2210,6 +2225,19 @@ enum ath10k_protmode {
        ATH10K_PROT_RTSCTS   = 2,    /* RTS-CTS */
 };
 
+enum wmi_rtscts_profile {
+       WMI_RTSCTS_FOR_NO_RATESERIES = 0,
+       WMI_RTSCTS_FOR_SECOND_RATESERIES,
+       WMI_RTSCTS_ACROSS_SW_RETRIES
+};
+
+#define WMI_RTSCTS_ENABLED             1
+#define WMI_RTSCTS_SET_MASK            0x0f
+#define WMI_RTSCTS_SET_LSB             0
+
+#define WMI_RTSCTS_PROFILE_MASK                0xf0
+#define WMI_RTSCTS_PROFILE_LSB         4
+
 enum wmi_beacon_gen_mode {
        WMI_BEACON_STAGGERED_MODE = 0,
        WMI_BEACON_BURST_MODE = 1
@@ -2295,9 +2323,9 @@ struct wmi_pdev_param_map {
 #define WMI_PDEV_PARAM_UNSUPPORTED 0
 
 enum wmi_pdev_param {
-       /* TX chian mask */
+       /* TX chain mask */
        WMI_PDEV_PARAM_TX_CHAIN_MASK = 0x1,
-       /* RX chian mask */
+       /* RX chain mask */
        WMI_PDEV_PARAM_RX_CHAIN_MASK,
        /* TX power limit for 2G Radio */
        WMI_PDEV_PARAM_TXPOWER_LIMIT2G,
@@ -2682,6 +2710,9 @@ struct wal_dbg_tx_stats {
        /* wal pdev resets  */
        __le32 pdev_resets;
 
+       /* frames dropped due to non-availability of stateless TIDs */
+       __le32 stateless_tid_alloc_failure;
+
        __le32 phy_underrun;
 
        /* MPDU is more than txop limit */
@@ -2738,13 +2769,21 @@ enum wmi_stats_id {
        WMI_REQUEST_AP_STAT     = 0x02
 };
 
+struct wlan_inst_rssi_args {
+       __le16 cfg_retry_count;
+       __le16 retry_count;
+};
+
 struct wmi_request_stats_cmd {
        __le32 stats_id;
 
-       /*
-        * Space to add parameters like
-        * peer mac addr
-        */
+       __le32 vdev_id;
+
+       /* peer MAC address */
+       struct wmi_mac_addr peer_macaddr;
+
+       /* Instantaneous RSSI arguments */
+       struct wlan_inst_rssi_args inst_rssi_args;
 } __packed;
 
 /* Suspend option */
@@ -2795,7 +2834,18 @@ struct wmi_stats_event {
  * PDEV statistics
  * TODO: add all PDEV stats here
  */
-struct wmi_pdev_stats {
+struct wmi_pdev_stats_old {
+       __le32 chan_nf;        /* Channel noise floor */
+       __le32 tx_frame_count; /* TX frame count */
+       __le32 rx_frame_count; /* RX frame count */
+       __le32 rx_clear_count; /* rx clear count */
+       __le32 cycle_count;    /* cycle count */
+       __le32 phy_err_count;  /* Phy error count */
+       __le32 chan_tx_pwr;    /* channel tx power */
+       struct wal_dbg_stats wal; /* WAL dbg stats */
+} __packed;
+
+struct wmi_pdev_stats_10x {
        __le32 chan_nf;        /* Channel noise floor */
        __le32 tx_frame_count; /* TX frame count */
        __le32 rx_frame_count; /* RX frame count */
@@ -2804,6 +2854,12 @@ struct wmi_pdev_stats {
        __le32 phy_err_count;  /* Phy error count */
        __le32 chan_tx_pwr;    /* channel tx power */
        struct wal_dbg_stats wal; /* WAL dbg stats */
+       __le32 ack_rx_bad;
+       __le32 rts_bad;
+       __le32 rts_good;
+       __le32 fcs_bad;
+       __le32 no_beacons;
+       __le32 mib_int_count;
 } __packed;
 
 /*
@@ -2818,10 +2874,17 @@ struct wmi_vdev_stats {
  * peer statistics.
  * TODO: add more stats
  */
-struct wmi_peer_stats {
+struct wmi_peer_stats_old {
+       struct wmi_mac_addr peer_macaddr;
+       __le32 peer_rssi;
+       __le32 peer_tx_rate;
+} __packed;
+
+struct wmi_peer_stats_10x {
        struct wmi_mac_addr peer_macaddr;
        __le32 peer_rssi;
        __le32 peer_tx_rate;
+       __le32 peer_rx_rate;
 } __packed;
 
 struct wmi_vdev_create_cmd {
@@ -4196,13 +4259,14 @@ 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);
 
-int ath10k_wmi_connect_htc_service(struct ath10k *ar);
+int ath10k_wmi_connect(struct ath10k *ar);
 int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
                                const struct wmi_channel_arg *);
 int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt);
 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);
+                                 u16 rd5g, u16 ctl2g, u16 ctl5g,
+                                 enum wmi_dfs_region dfs_reg);
 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 *);
index 1a2973b7acf2500f5c97c992315793d0b4b9a9a7..0fce1c76638e9c9ae103e01ac6752dced8b1670d 100644 (file)
@@ -3709,8 +3709,8 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
                        AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CHIRP),
                        AR5K_TPC);
        } else {
-               ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX |
-                       AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX);
+               ath5k_hw_reg_write(ah, AR5K_TUNE_MAX_TXPOWER,
+                       AR5K_PHY_TXPOWER_RATE_MAX);
        }
 
        return 0;
index e39e5860a2e9347a2d44318e69b07410c4bd2dc5..9c125ff083f73de2c766bd5ccd6f3dc16aad7ae2 100644 (file)
@@ -1,11 +1,19 @@
 config ATH6KL
        tristate "Atheros mobile chipsets support"
+       depends on CFG80211
+        ---help---
+         This module adds core support for wireless adapters based on
+         Atheros AR6003 and AR6004 chipsets. You still need separate
+         bus drivers for USB and SDIO to be able to use real devices.
+
+         If you choose to build it as a module, it will be called
+         ath6kl_core. Please note that AR6002 and AR6001 are not
+         supported by this driver.
 
 config ATH6KL_SDIO
        tristate "Atheros ath6kl SDIO support"
        depends on ATH6KL
        depends on MMC
-       depends on CFG80211
        ---help---
          This module adds support for wireless adapters based on
          Atheros AR6003 and AR6004 chipsets running over SDIO. If you
@@ -17,25 +25,31 @@ config ATH6KL_USB
        tristate "Atheros ath6kl USB support"
        depends on ATH6KL
        depends on USB
-       depends on CFG80211
        ---help---
          This module adds support for wireless adapters based on
-         Atheros AR6004 chipset running over USB. This is still under
-         implementation and it isn't functional. If you choose to
-         build it as a module, it will be called ath6kl_usb.
+         Atheros AR6004 chipset and chipsets based on it running over
+         USB. If you choose to build it as a module, it will be
+         called ath6kl_usb.
 
 config ATH6KL_DEBUG
        bool "Atheros ath6kl debugging"
        depends on ATH6KL
        ---help---
-         Enables debug support
+         Enables ath6kl debug support, including debug messages
+         enabled with debug_mask module parameter and debugfs
+         interface.
+
+         If unsure, say Y to make it easier to debug problems.
 
 config ATH6KL_TRACING
        bool "Atheros ath6kl tracing support"
        depends on ATH6KL
        depends on EVENT_TRACING
        ---help---
-         Select this to ath6kl use tracing infrastructure.
+         Select this to ath6kl use tracing infrastructure which, for
+         example, can be enabled with help of trace-cmd. All debug
+         messages and commands are delivered to using individually
+         enablable trace points.
 
          If unsure, say Y to make it easier to debug problems.
 
@@ -47,3 +61,5 @@ config ATH6KL_REGDOMAIN
          Enabling this makes it possible to change the regdomain in
          the firmware. This can be only enabled if regulatory requirements
          are taken into account.
+
+         If unsure, say N.
index c2c6f460495859ae3517c4f20daa2c15caebdde0..0e26f4a34fda329910ecc278de9fe7bea8fa6c57 100644 (file)
@@ -724,8 +724,9 @@ ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
                        ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
                                   "added bss %pM to cfg80211\n", bssid);
                kfree(ie);
-       } else
+       } else {
                ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss\n");
+       }
 
        return bss;
 }
@@ -970,7 +971,6 @@ static int ath6kl_set_probed_ssids(struct ath6kl *ar,
                                          ssid_list[i].flag,
                                          ssid_list[i].ssid.ssid_len,
                                          ssid_list[i].ssid.ssid);
-
        }
 
        /* Make sure no old entries are left behind */
@@ -1759,7 +1759,7 @@ static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi)
 }
 
 static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
-                             u8 *mac, struct station_info *sinfo)
+                             const u8 *mac, struct station_info *sinfo)
 {
        struct ath6kl *ar = ath6kl_priv(dev);
        struct ath6kl_vif *vif = netdev_priv(dev);
@@ -1897,7 +1897,6 @@ static int ath6kl_wow_usr(struct ath6kl *ar, struct ath6kl_vif *vif,
 
        /* Configure the patterns that we received from the user. */
        for (i = 0; i < wow->n_patterns; i++) {
-
                /*
                 * Convert given nl80211 specific mask value to equivalent
                 * driver specific mask value and send it to the chip along
@@ -2850,8 +2849,9 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
        if (p.prwise_crypto_type == 0) {
                p.prwise_crypto_type = NONE_CRYPT;
                ath6kl_set_cipher(vif, 0, true);
-       } else if (info->crypto.n_ciphers_pairwise == 1)
+       } else if (info->crypto.n_ciphers_pairwise == 1) {
                ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true);
+       }
 
        switch (info->crypto.cipher_group) {
        case WLAN_CIPHER_SUITE_WEP40:
@@ -2897,7 +2897,6 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
        }
 
        if (info->inactivity_timeout) {
-
                inactivity_timeout = info->inactivity_timeout;
 
                if (ar->hw.flags & ATH6KL_HW_AP_INACTIVITY_MINS)
@@ -2975,7 +2974,7 @@ static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev)
 static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
 static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
-                             u8 *mac)
+                             const u8 *mac)
 {
        struct ath6kl *ar = ath6kl_priv(dev);
        struct ath6kl_vif *vif = netdev_priv(dev);
@@ -2986,7 +2985,8 @@ static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
 }
 
 static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
-                                u8 *mac, struct station_parameters *params)
+                                const u8 *mac,
+                                struct station_parameters *params)
 {
        struct ath6kl *ar = ath6kl_priv(dev);
        struct ath6kl_vif *vif = netdev_priv(dev);
index 4b46adbe8c923b7dd410c919dc3cbc289541ea4f..b0b6520427600a05687e299318f78f873e3fd948 100644 (file)
@@ -45,9 +45,9 @@ module_param(testmode, uint, 0644);
 module_param(recovery_enable, uint, 0644);
 module_param(heart_beat_poll, uint, 0644);
 MODULE_PARM_DESC(recovery_enable, "Enable recovery from firmware error");
-MODULE_PARM_DESC(heart_beat_poll, "Enable fw error detection periodic"   \
-                "polling. This also specifies the polling interval in"  \
-                "msecs. Set reocvery_enable for this to be effective");
+MODULE_PARM_DESC(heart_beat_poll,
+                "Enable fw error detection periodic polling in msecs - Also set recovery_enable for this to be effective");
+
 
 void ath6kl_core_tx_complete(struct ath6kl *ar, struct sk_buff *skb)
 {
index dbfd17d0a5faa33fa390b150d8d39e03a42d3746..55c4064dd5067f26b8dfaf54689f85c9eb67a88e 100644 (file)
@@ -172,7 +172,6 @@ void ath6kl_dump_registers(struct ath6kl_device *dev,
                           struct ath6kl_irq_proc_registers *irq_proc_reg,
                           struct ath6kl_irq_enable_reg *irq_enable_reg)
 {
-
        ath6kl_dbg(ATH6KL_DBG_IRQ, ("<------- Register Table -------->\n"));
 
        if (irq_proc_reg != NULL) {
@@ -219,7 +218,6 @@ void ath6kl_dump_registers(struct ath6kl_device *dev,
                                   "GMBOX lookahead alias 1:   0x%x\n",
                                   irq_proc_reg->rx_gmbox_lkahd_alias[1]);
                }
-
        }
 
        if (irq_enable_reg != NULL) {
@@ -1396,7 +1394,6 @@ static ssize_t ath6kl_create_qos_write(struct file *file,
                                                const char __user *user_buf,
                                                size_t count, loff_t *ppos)
 {
-
        struct ath6kl *ar = file->private_data;
        struct ath6kl_vif *vif;
        char buf[200];
@@ -1575,7 +1572,6 @@ static ssize_t ath6kl_delete_qos_write(struct file *file,
                                const char __user *user_buf,
                                size_t count, loff_t *ppos)
 {
-
        struct ath6kl *ar = file->private_data;
        struct ath6kl_vif *vif;
        char buf[100];
index ca9ba005f2871f3e42bbc914bb5ca90f6e8b90e9..e194c10d9f0071725c1c5eba917347ae8209b0a0 100644 (file)
@@ -97,8 +97,8 @@ static inline void ath6kl_dump_registers(struct ath6kl_device *dev,
                struct ath6kl_irq_proc_registers *irq_proc_reg,
                struct ath6kl_irq_enable_reg *irq_en_reg)
 {
-
 }
+
 static inline void dump_cred_dist_stats(struct htc_target *target)
 {
 }
index fea7709b5dda59aa26fa0fbc5eb70caccea77918..18c070850a09b870900624a83ee5576551281cdb 100644 (file)
@@ -37,7 +37,6 @@ static int ath6kl_hif_cp_scat_dma_buf(struct hif_scatter_req *req,
        buf = req->virt_dma_buf;
 
        for (i = 0; i < req->scat_entries; i++) {
-
                if (from_dma)
                        memcpy(req->scat_list[i].buf, buf,
                               req->scat_list[i].len);
@@ -116,7 +115,6 @@ static void ath6kl_hif_dump_fw_crash(struct ath6kl *ar)
                            le32_to_cpu(regdump_val[i + 2]),
                            le32_to_cpu(regdump_val[i + 3]));
        }
-
 }
 
 static int ath6kl_hif_proc_dbg_intr(struct ath6kl_device *dev)
@@ -701,5 +699,4 @@ int ath6kl_hif_setup(struct ath6kl_device *dev)
 
 fail_setup:
        return status;
-
 }
index 61f6b21fb0aeeaa4e6ef20dfca3baddf6ad638cd..dc6bd8cd9b837d85155d494a0afd2b32b7775240 100644 (file)
@@ -197,9 +197,9 @@ struct hif_scatter_req {
        /* bounce buffer for upper layers to copy to/from */
        u8 *virt_dma_buf;
 
-       struct hif_scatter_item scat_list[1];
-
        u32 scat_q_depth;
+
+       struct hif_scatter_item scat_list[0];
 };
 
 struct ath6kl_irq_proc_registers {
index 65e5b719093d47943b3135e902cf68001eefd988..e481f14b98787e88354278d26e3c59615ff2851c 100644 (file)
@@ -112,9 +112,9 @@ static void ath6kl_credit_init(struct ath6kl_htc_credit_info *cred_info,
                if (cur_ep_dist->endpoint == ENDPOINT_0)
                        continue;
 
-               if (cur_ep_dist->svc_id == WMI_CONTROL_SVC)
+               if (cur_ep_dist->svc_id == WMI_CONTROL_SVC) {
                        cur_ep_dist->cred_norm = cur_ep_dist->cred_per_msg;
-               else {
+               else {
                        /*
                         * For the remaining data endpoints, we assume that
                         * each cred_per_msg are the same. We use a simple
@@ -129,7 +129,6 @@ static void ath6kl_credit_init(struct ath6kl_htc_credit_info *cred_info,
                        count = (count * 3) >> 2;
                        count = max(count, cur_ep_dist->cred_per_msg);
                        cur_ep_dist->cred_norm = count;
-
                }
 
                ath6kl_dbg(ATH6KL_DBG_CREDIT,
@@ -549,7 +548,6 @@ static int htc_check_credits(struct htc_target *target,
                             enum htc_endpoint_id eid, unsigned int len,
                             int *req_cred)
 {
-
        *req_cred = (len > target->tgt_cred_sz) ?
                     DIV_ROUND_UP(len, target->tgt_cred_sz) : 1;
 
@@ -608,7 +606,6 @@ static void ath6kl_htc_tx_pkts_get(struct htc_target *target,
        unsigned int len;
 
        while (true) {
-
                flags = 0;
 
                if (list_empty(&endpoint->txq))
@@ -889,7 +886,6 @@ static void ath6kl_htc_tx_from_queue(struct htc_target *target,
                ac = target->dev->ar->ep2ac_map[endpoint->eid];
 
        while (true) {
-
                if (list_empty(&endpoint->txq))
                        break;
 
@@ -1190,7 +1186,6 @@ static void ath6kl_htc_mbox_flush_txep(struct htc_target *target,
                list_add_tail(&packet->list, &container);
                htc_tx_complete(endpoint, &container);
        }
-
 }
 
 static void ath6kl_htc_flush_txep_all(struct htc_target *target)
@@ -1394,7 +1389,6 @@ static int ath6kl_htc_rx_setup(struct htc_target *target,
 
        ep_cb = ep->ep_cb;
        for (j = 0; j < n_msg; j++) {
-
                /*
                 * Reset flag, any packets allocated using the
                 * rx_alloc() API cannot be recycled on
@@ -1424,9 +1418,9 @@ static int ath6kl_htc_rx_setup(struct htc_target *target,
                                }
                        }
 
-                       if (list_empty(&ep->rx_bufq))
+                       if (list_empty(&ep->rx_bufq)) {
                                packet = NULL;
-                       else {
+                       else {
                                packet = list_first_entry(&ep->rx_bufq,
                                                struct htc_packet, list);
                                list_del(&packet->list);
@@ -1487,7 +1481,6 @@ static int ath6kl_htc_rx_alloc(struct htc_target *target,
        spin_lock_bh(&target->rx_lock);
 
        for (i = 0; i < msg; i++) {
-
                htc_hdr = (struct htc_frame_hdr *)&lk_ahds[i];
 
                if (htc_hdr->eid >= ENDPOINT_MAX) {
@@ -1708,7 +1701,6 @@ static int htc_parse_trailer(struct htc_target *target,
                lk_ahd = (struct htc_lookahead_report *) record_buf;
                if ((lk_ahd->pre_valid == ((~lk_ahd->post_valid) & 0xFF)) &&
                    next_lk_ahds) {
-
                        ath6kl_dbg(ATH6KL_DBG_HTC,
                                   "htc rx lk_ahd found pre_valid 0x%x post_valid 0x%x\n",
                                   lk_ahd->pre_valid, lk_ahd->post_valid);
@@ -1755,7 +1747,6 @@ static int htc_parse_trailer(struct htc_target *target,
        }
 
        return 0;
-
 }
 
 static int htc_proc_trailer(struct htc_target *target,
@@ -1776,7 +1767,6 @@ static int htc_proc_trailer(struct htc_target *target,
        status = 0;
 
        while (len > 0) {
-
                if (len < sizeof(struct htc_record_hdr)) {
                        status = -ENOMEM;
                        break;
@@ -2098,7 +2088,6 @@ static int ath6kl_htc_rx_fetch(struct htc_target *target,
                }
 
                if (!fetched_pkts) {
-
                        packet = list_first_entry(rx_pktq, struct htc_packet,
                                                   list);
 
@@ -2173,7 +2162,6 @@ int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target,
        look_aheads[0] = msg_look_ahead;
 
        while (true) {
-
                /*
                 * First lookahead sets the expected endpoint IDs for all
                 * packets in a bundle.
@@ -2825,8 +2813,9 @@ static int ath6kl_htc_reset(struct htc_target *target)
                        packet->buf = packet->buf_start;
                        packet->endpoint = ENDPOINT_0;
                        list_add_tail(&packet->list, &target->free_ctrl_rxbuf);
-               } else
+               } else {
                        list_add_tail(&packet->list, &target->free_ctrl_txbuf);
+               }
        }
 
        return 0;
index 67aa924ed8b317f9d5240ad1560a820553790c24..756fe52a12c8ad5a3496de58bbfec01d0ad9820b 100644 (file)
@@ -137,7 +137,6 @@ static void get_htc_packet_credit_based(struct htc_target *target,
                        credits_required = 0;
 
                } else {
-
                        if (ep->cred_dist.credits < credits_required)
                                break;
 
@@ -169,7 +168,6 @@ static void get_htc_packet_credit_based(struct htc_target *target,
                /* queue this packet into the caller's queue */
                list_add_tail(&packet->list, queue);
        }
-
 }
 
 static void get_htc_packet(struct htc_target *target,
@@ -279,7 +277,6 @@ static int htc_issue_packets(struct htc_target *target,
                        list_add(&packet->list, pkt_queue);
                        break;
                }
-
        }
 
        if (status != 0) {
@@ -385,7 +382,6 @@ static enum htc_send_queue_result htc_try_send(struct htc_target *target,
                         */
                        list_for_each_entry_safe(packet, tmp_pkt,
                                                 txq, list) {
-
                                ath6kl_dbg(ATH6KL_DBG_HTC,
                                           "%s: Indicat overflowed TX pkts: %p\n",
                                           __func__, packet);
@@ -403,7 +399,6 @@ static enum htc_send_queue_result htc_try_send(struct htc_target *target,
                                        list_move_tail(&packet->list,
                                                       &send_queue);
                                }
-
                        }
 
                        if (list_empty(&send_queue)) {
@@ -454,7 +449,6 @@ static enum htc_send_queue_result htc_try_send(struct htc_target *target,
         * enough transmit resources.
         */
        while (true) {
-
                if (get_queue_depth(&ep->txq) == 0)
                        break;
 
@@ -495,8 +489,8 @@ static enum htc_send_queue_result htc_try_send(struct htc_target *target,
                }
 
                spin_lock_bh(&target->tx_lock);
-
        }
+
        /* done with this endpoint, we can clear the count */
        ep->tx_proc_cnt = 0;
        spin_unlock_bh(&target->tx_lock);
@@ -1106,7 +1100,6 @@ static int ath6kl_htc_pipe_rx_complete(struct ath6kl *ar, struct sk_buff *skb,
        dev_kfree_skb(skb);
 
        return status;
-
 }
 
 static void htc_flush_rx_queue(struct htc_target *target,
@@ -1258,7 +1251,6 @@ static int ath6kl_htc_pipe_conn_service(struct htc_target *target,
                tx_alloc = 0;
 
        } else {
-
                tx_alloc = htc_get_credit_alloc(target, conn_req->svc_id);
                if (tx_alloc == 0) {
                        status = -ENOMEM;
index 4f316bdcbab58da3911c6a2365da1989ba412744..d5ef211f261c2c19e6e8deeef985dc2b83130794 100644 (file)
@@ -1192,7 +1192,6 @@ static int ath6kl_upload_board_file(struct ath6kl *ar)
 
        if (board_ext_address &&
            ar->fw_board_len == (board_data_size + board_ext_data_size)) {
-
                /* write extended board data */
                ath6kl_dbg(ATH6KL_DBG_BOOT,
                           "writing extended board data to 0x%x (%d B)\n",
index 5839fc23bdc789d5013f1c89e48f0f65eff37efe..d56554674da47924477c02423ad08c50caef2918 100644 (file)
@@ -571,7 +571,6 @@ void ath6kl_scan_complete_evt(struct ath6kl_vif *vif, int status)
 
 static int ath6kl_commit_ch_switch(struct ath6kl_vif *vif, u16 channel)
 {
-
        struct ath6kl *ar = vif->ar;
 
        vif->profile.ch = cpu_to_le16(channel);
@@ -600,7 +599,6 @@ static int ath6kl_commit_ch_switch(struct ath6kl_vif *vif, u16 channel)
 
 static void ath6kl_check_ch_switch(struct ath6kl *ar, u16 channel)
 {
-
        struct ath6kl_vif *vif;
        int res = 0;
 
@@ -692,9 +690,9 @@ void ath6kl_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid, bool ismcast)
                cfg80211_michael_mic_failure(vif->ndev, sta->mac,
                                             NL80211_KEYTYPE_PAIRWISE, keyid,
                                             tsc, GFP_KERNEL);
-       } else
+       } else {
                ath6kl_cfg80211_tkip_micerr_event(vif, keyid, ismcast);
-
+       }
 }
 
 static void ath6kl_update_target_stats(struct ath6kl_vif *vif, u8 *ptr, u32 len)
@@ -1093,8 +1091,9 @@ static int ath6kl_open(struct net_device *dev)
        if (test_bit(CONNECTED, &vif->flags)) {
                netif_carrier_on(dev);
                netif_wake_queue(dev);
-       } else
+       } else {
                netif_carrier_off(dev);
+       }
 
        return 0;
 }
@@ -1146,7 +1145,6 @@ static int ath6kl_set_features(struct net_device *dev,
                        dev->features = features | NETIF_F_RXCSUM;
                        return err;
                }
-
        }
 
        return err;
index 7126bdd4236c2b1e78dd3a9d2c600dbadbb53f0e..339d89f14d32b1991c3d3d646f460b90f41411bb 100644 (file)
@@ -348,7 +348,7 @@ static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio,
        int i, scat_req_sz, scat_list_sz, size;
        u8 *virt_buf;
 
-       scat_list_sz = (n_scat_entry - 1) * sizeof(struct hif_scatter_item);
+       scat_list_sz = n_scat_entry * sizeof(struct hif_scatter_item);
        scat_req_sz = sizeof(*s_req) + scat_list_sz;
 
        if (!virt_scat)
@@ -425,8 +425,9 @@ static int ath6kl_sdio_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf,
                        memcpy(tbuf, buf, len);
 
                bounced = true;
-       } else
+       } else {
                tbuf = buf;
+       }
 
        ret = ath6kl_sdio_io(ar_sdio->func, request, addr, tbuf, len);
        if ((request & HIF_READ) && bounced)
@@ -441,9 +442,9 @@ static int ath6kl_sdio_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf,
 static void __ath6kl_sdio_write_async(struct ath6kl_sdio *ar_sdio,
                                      struct bus_request *req)
 {
-       if (req->scat_req)
+       if (req->scat_req) {
                ath6kl_sdio_scat_rw(ar_sdio, req);
-       else {
+       else {
                void *context;
                int status;
 
@@ -656,7 +657,6 @@ static void ath6kl_sdio_scatter_req_add(struct ath6kl *ar,
        list_add_tail(&s_req->list, &ar_sdio->scat_req);
 
        spin_unlock_bh(&ar_sdio->scat_lock);
-
 }
 
 /* scatter gather read write request */
@@ -674,9 +674,9 @@ static int ath6kl_sdio_async_rw_scatter(struct ath6kl *ar,
                   "hif-scatter: total len: %d scatter entries: %d\n",
                   scat_req->len, scat_req->scat_entries);
 
-       if (request & HIF_SYNCHRONOUS)
+       if (request & HIF_SYNCHRONOUS) {
                status = ath6kl_sdio_scat_rw(ar_sdio, scat_req->busrequest);
-       else {
+       else {
                spin_lock_bh(&ar_sdio->wr_async_lock);
                list_add_tail(&scat_req->busrequest->list, &ar_sdio->wr_asyncq);
                spin_unlock_bh(&ar_sdio->wr_async_lock);
@@ -856,7 +856,6 @@ static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
 
        if (ar->suspend_mode == WLAN_POWER_STATE_WOW ||
            (!ar->suspend_mode && wow)) {
-
                ret = ath6kl_set_sdio_pm_caps(ar);
                if (ret)
                        goto cut_pwr;
@@ -878,7 +877,6 @@ static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
 
        if (ar->suspend_mode == WLAN_POWER_STATE_DEEP_SLEEP ||
            !ar->suspend_mode || try_deepsleep) {
-
                flags = sdio_get_host_pm_caps(func);
                if (!(flags & MMC_PM_KEEP_POWER))
                        goto cut_pwr;
@@ -1061,7 +1059,6 @@ static int ath6kl_sdio_bmi_credits(struct ath6kl *ar)
 
        timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT);
        while (time_before(jiffies, timeout) && !ar->bmi.cmd_credits) {
-
                /*
                 * Hit the credit counter with a 4-byte access, the first byte
                 * read will hit the counter and cause a decrement, while the
index a580a629a0da6ba24b251dab8147e7de74c34f7c..d5eeeae7711b253c7dcddbf4214613cd5aff86f7 100644 (file)
@@ -289,7 +289,7 @@ struct host_interest {
        u32 hi_hp_rx_traffic_ratio;                    /* 0xd8 */
 
        /* test applications flags */
-       u32 hi_test_apps_related    ;                  /* 0xdc */
+       u32 hi_test_apps_related;                      /* 0xdc */
        /* location of test script */
        u32 hi_ota_testscript;                         /* 0xe0 */
        /* location of CAL data */
index ebb24045a8ae6cbcac844a239d11a05ee6f86e14..40432fe7a5d2cc112a4ce6d68bb8fd43a115e01f 100644 (file)
@@ -125,8 +125,9 @@ static bool ath6kl_process_uapsdq(struct ath6kl_sta *conn,
                *flags |= WMI_DATA_HDR_FLAGS_UAPSD;
                spin_unlock_bh(&conn->psq_lock);
                return false;
-       } else if (!conn->apsd_info)
+       } else if (!conn->apsd_info) {
                return false;
+       }
 
        if (test_bit(WMM_ENABLED, &vif->flags)) {
                ether_type = be16_to_cpu(datap->h_proto);
@@ -316,8 +317,9 @@ int ath6kl_control_tx(void *devt, struct sk_buff *skb,
                cookie = NULL;
                ath6kl_err("wmi ctrl ep full, dropping pkt : 0x%p, len:%d\n",
                           skb, skb->len);
-       } else
+       } else {
                cookie = ath6kl_alloc_cookie(ar);
+       }
 
        if (cookie == NULL) {
                spin_unlock_bh(&ar->lock);
@@ -359,7 +361,7 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev)
        struct ath6kl_vif *vif = netdev_priv(dev);
        u32 map_no = 0;
        u16 htc_tag = ATH6KL_DATA_PKT_TAG;
-       u8 ac = 99 ; /* initialize to unmapped ac */
+       u8 ac = 99; /* initialize to unmapped ac */
        bool chk_adhoc_ps_mapping = false;
        int ret;
        struct wmi_tx_meta_v2 meta_v2;
@@ -449,8 +451,9 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev)
                        if (ret)
                                goto fail_tx;
                }
-       } else
+       } else {
                goto fail_tx;
+       }
 
        spin_lock_bh(&ar->lock);
 
@@ -702,7 +705,6 @@ void ath6kl_tx_complete(struct htc_target *target,
 
        /* reap completed packets */
        while (!list_empty(packet_queue)) {
-
                packet = list_first_entry(packet_queue, struct htc_packet,
                                          list);
                list_del(&packet->list);
@@ -1089,8 +1091,9 @@ static void aggr_deque_frms(struct aggr_info_conn *agg_conn, u8 tid,
                        else
                                skb_queue_tail(&rxtid->q, node->skb);
                        node->skb = NULL;
-               } else
+               } else {
                        stats->num_hole++;
+               }
 
                rxtid->seq_next = ATH6KL_NEXT_SEQ_NO(rxtid->seq_next);
                idx = AGGR_WIN_IDX(rxtid->seq_next, rxtid->hold_q_sz);
@@ -1211,7 +1214,7 @@ static bool aggr_process_recv_frm(struct aggr_info_conn *agg_conn, u8 tid,
                return is_queued;
 
        spin_lock_bh(&rxtid->lock);
-       for (idx = 0 ; idx < rxtid->hold_q_sz; idx++) {
+       for (idx = 0; idx < rxtid->hold_q_sz; idx++) {
                if (rxtid->hold_q[idx].skb) {
                        /*
                         * There is a frame in the queue and no
@@ -1265,7 +1268,6 @@ static void ath6kl_uapsd_trigger_frame_rx(struct ath6kl_vif *vif,
        is_apsdq_empty_at_start = is_apsdq_empty;
 
        while ((!is_apsdq_empty) && (num_frames_to_deliver)) {
-
                spin_lock_bh(&conn->psq_lock);
                skb = skb_dequeue(&conn->apsdq);
                is_apsdq_empty = skb_queue_empty(&conn->apsdq);
@@ -1606,16 +1608,18 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
                        if (!conn)
                                return;
                        aggr_conn = conn->aggr_conn;
-               } else
+               } else {
                        aggr_conn = vif->aggr_cntxt->aggr_conn;
+               }
 
                if (aggr_process_recv_frm(aggr_conn, tid, seq_no,
                                          is_amsdu, skb)) {
                        /* aggregation code will handle the skb */
                        return;
                }
-       } else if (!is_broadcast_ether_addr(datap->h_dest))
+       } else if (!is_broadcast_ether_addr(datap->h_dest)) {
                vif->net_stats.multicast++;
+       }
 
        ath6kl_deliver_frames_to_nw_stack(vif->ndev, skb);
 }
@@ -1710,8 +1714,9 @@ void aggr_recv_addba_req_evt(struct ath6kl_vif *vif, u8 tid_mux, u16 seq_no,
                sta = ath6kl_find_sta_by_aid(vif->ar, aid);
                if (sta)
                        aggr_conn = sta->aggr_conn;
-       } else
+       } else {
                aggr_conn = vif->aggr_cntxt->aggr_conn;
+       }
 
        if (!aggr_conn)
                return;
@@ -1766,7 +1771,6 @@ void aggr_conn_init(struct ath6kl_vif *vif, struct aggr_info *aggr_info,
                skb_queue_head_init(&rxtid->q);
                spin_lock_init(&rxtid->lock);
        }
-
 }
 
 struct aggr_info *aggr_init(struct ath6kl_vif *vif)
@@ -1806,8 +1810,9 @@ void aggr_recv_delba_req_evt(struct ath6kl_vif *vif, u8 tid_mux)
                sta = ath6kl_find_sta_by_aid(vif->ar, aid);
                if (sta)
                        aggr_conn = sta->aggr_conn;
-       } else
+       } else {
                aggr_conn = vif->aggr_cntxt->aggr_conn;
+       }
 
        if (!aggr_conn)
                return;
index 56c3fd5cef65a07e915d63d8c0dc24727a87406b..3afc5a463d06f822f339250deecfb1cefadea592 100644 (file)
@@ -236,7 +236,6 @@ static void ath6kl_usb_free_pipe_resources(struct ath6kl_usb_pipe *pipe)
                        break;
                kfree(urb_context);
        }
-
 }
 
 static void ath6kl_usb_cleanup_pipe_resources(struct ath6kl_usb *ar_usb)
@@ -245,7 +244,6 @@ static void ath6kl_usb_cleanup_pipe_resources(struct ath6kl_usb *ar_usb)
 
        for (i = 0; i < ATH6KL_USB_PIPE_MAX; i++)
                ath6kl_usb_free_pipe_resources(&ar_usb->pipes[i]);
-
 }
 
 static u8 ath6kl_usb_get_logical_pipe_num(struct ath6kl_usb *ar_usb,
index 8b4ce28e3ce8f51f7cda070364394a117d5aef66..4d7f9e4712e991deea8553f7505a7f79b9a6f6e7 100644 (file)
@@ -289,8 +289,9 @@ int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, u8 if_idx,
                           ath6kl_wmi_determine_user_priority(((u8 *) llc_hdr) +
                                        sizeof(struct ath6kl_llc_snap_hdr),
                                        layer2_priority);
-               } else
+               } else {
                        usr_pri = layer2_priority & 0x7;
+               }
 
                /*
                 * Queue the EAPOL frames in the same WMM_AC_VO queue
@@ -359,8 +360,9 @@ int ath6kl_wmi_dot11_hdr_remove(struct wmi *wmi, struct sk_buff *skb)
                hdr_size = roundup(sizeof(struct ieee80211_qos_hdr),
                                   sizeof(u32));
                skb_pull(skb, hdr_size);
-       } else if (sub_type == cpu_to_le16(IEEE80211_STYPE_DATA))
+       } else if (sub_type == cpu_to_le16(IEEE80211_STYPE_DATA)) {
                skb_pull(skb, sizeof(struct ieee80211_hdr_3addr));
+       }
 
        datap = skb->data;
        llc_hdr = (struct ath6kl_llc_snap_hdr *)(datap);
@@ -936,7 +938,6 @@ ath6kl_regd_find_country_by_rd(u16 regdmn)
 
 static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len)
 {
-
        struct ath6kl_wmi_regdomain *ev;
        struct country_code_to_enum_rd *country = NULL;
        struct reg_dmn_pair_mapping *regpair = NULL;
@@ -946,10 +947,9 @@ static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len)
        ev = (struct ath6kl_wmi_regdomain *) datap;
        reg_code = le32_to_cpu(ev->reg_code);
 
-       if ((reg_code >> ATH6KL_COUNTRY_RD_SHIFT) & COUNTRY_ERD_FLAG)
+       if ((reg_code >> ATH6KL_COUNTRY_RD_SHIFT) & COUNTRY_ERD_FLAG) {
                country = ath6kl_regd_find_country((u16) reg_code);
-       else if (!(((u16) reg_code & WORLD_SKU_MASK) == WORLD_SKU_PREFIX)) {
-
+       } else if (!(((u16) reg_code & WORLD_SKU_MASK) == WORLD_SKU_PREFIX)) {
                regpair = ath6kl_get_regpair((u16) reg_code);
                country = ath6kl_regd_find_country_by_rd((u16) reg_code);
                if (regpair)
@@ -1499,7 +1499,6 @@ static int ath6kl_wmi_cac_event_rx(struct wmi *wmi, u8 *datap, int len,
 
        if ((reply->cac_indication == CAC_INDICATION_ADMISSION_RESP) &&
            (reply->status_code != IEEE80211_TSPEC_STATUS_ADMISS_ACCEPTED)) {
-
                ts = (struct ieee80211_tspec_ie *) &(reply->tspec_suggestion);
                tsinfo = le16_to_cpu(ts->tsinfo);
                tsid = (tsinfo >> IEEE80211_WMM_IE_TSPEC_TID_SHIFT) &
@@ -1530,7 +1529,6 @@ static int ath6kl_wmi_cac_event_rx(struct wmi *wmi, u8 *datap, int len,
         * for delete qos stream from AP
         */
        else if (reply->cac_indication == CAC_INDICATION_DELETE) {
-
                ts = (struct ieee80211_tspec_ie *) &(reply->tspec_suggestion);
                tsinfo = le16_to_cpu(ts->tsinfo);
                ts_id = ((tsinfo >> IEEE80211_WMM_IE_TSPEC_TID_SHIFT) &
@@ -2322,7 +2320,7 @@ int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index,
        return ret;
 }
 
-int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, u8 *krk)
+int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, const u8 *krk)
 {
        struct sk_buff *skb;
        struct wmi_add_krk_cmd *cmd;
@@ -2479,7 +2477,6 @@ static int ath6kl_wmi_sync_point(struct wmi *wmi, u8 if_idx)
                goto free_data_skb;
 
        for (index = 0; index < num_pri_streams; index++) {
-
                if (WARN_ON(!data_sync_bufs[index].skb))
                        goto free_data_skb;
 
@@ -2704,7 +2701,6 @@ static void ath6kl_wmi_relinquish_implicit_pstream_credits(struct wmi *wmi)
 
        for (i = 0; i < WMM_NUM_AC; i++) {
                if (stream_exist & (1 << i)) {
-
                        /*
                         * FIXME: Is this lock & unlock inside
                         * for loop correct? may need rework.
@@ -2870,8 +2866,9 @@ int ath6kl_wmi_set_host_sleep_mode_cmd(struct wmi *wmi, u8 if_idx,
        if (host_mode == ATH6KL_HOST_MODE_ASLEEP) {
                ath6kl_wmi_relinquish_implicit_pstream_credits(wmi);
                cmd->asleep = cpu_to_le32(1);
-       } else
+       } else {
                cmd->awake = cpu_to_le32(1);
+       }
 
        ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb,
                                  WMI_SET_HOST_SLEEP_MODE_CMDID,
index 5c702ae4d9f8683edf91063c0acc92a32036c697..bb23fc00111dc6c3329762ee976ce93489015b90 100644 (file)
@@ -898,7 +898,6 @@ struct wmi_start_scan_cmd {
  *  flags here
  */
 enum wmi_scan_ctrl_flags_bits {
-
        /* set if can scan in the connect cmd */
        CONNECT_SCAN_CTRL_FLAGS = 0x01,
 
@@ -2617,7 +2616,7 @@ int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index,
                          u8 *key_material,
                          u8 key_op_ctrl, u8 *mac_addr,
                          enum wmi_sync_flag sync_flag);
-int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, u8 *krk);
+int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, const u8 *krk);
 int ath6kl_wmi_deletekey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index);
 int ath6kl_wmi_setpmkid_cmd(struct wmi *wmi, u8 if_idx, const u8 *bssid,
                            const u8 *pmkid, bool set);
index 8e1c7b0fe76c178567fb2c03e70bd8d4e303f337..8fcd586d1c3980c413c8499cb3aefd518f507f69 100644 (file)
@@ -53,7 +53,8 @@ obj-$(CONFIG_ATH9K_HW) += ath9k_hw.o
 obj-$(CONFIG_ATH9K_COMMON) += ath9k_common.o
 ath9k_common-y:=       common.o \
                        common-init.o \
-                       common-beacon.o
+                       common-beacon.o \
+                       common-debug.o
 
 ath9k_htc-y += htc_hst.o \
                hif_usb.o \
index 0a6163e9248c0fdefa0f0e78a695201fb8a7d0f2..c38399bc9aa96e84fce4929319599e1d4db694c7 100644 (file)
@@ -410,7 +410,7 @@ static const u32 ar9300_2p2_baseband_core[][2] = {
        {0x00009e30, 0x06336f77},
        {0x00009e34, 0x6af6532f},
        {0x00009e38, 0x0cc80c00},
-       {0x00009e40, 0x0d261820},
+       {0x00009e40, 0x0d261800},
        {0x00009e4c, 0x00001004},
        {0x00009e50, 0x00ff03f1},
        {0x00009e54, 0x00000000},
index f76139bbb74f0fcf144e22dd382be140c48cf08b..2c42ff05efa38f507cdd0f54905d8b79c0c77d32 100644 (file)
@@ -592,7 +592,7 @@ static const u32 ar9331_1p1_baseband_core[][2] = {
        {0x00009e30, 0x06336f77},
        {0x00009e34, 0x6af6532f},
        {0x00009e38, 0x0cc80c00},
-       {0x00009e40, 0x0d261820},
+       {0x00009e40, 0x0d261800},
        {0x00009e4c, 0x00001004},
        {0x00009e50, 0x00ff03f1},
        {0x00009fc0, 0x803e4788},
index 0ac8be96097f2e70f436f75aadb4be1b2d2bc44b..2154efcd3900514af944619a174db08d2514a140 100644 (file)
@@ -231,7 +231,7 @@ static const u32 ar9331_1p2_baseband_core[][2] = {
        {0x00009e30, 0x06336f77},
        {0x00009e34, 0x6af6532f},
        {0x00009e38, 0x0cc80c00},
-       {0x00009e40, 0x0d261820},
+       {0x00009e40, 0x0d261800},
        {0x00009e4c, 0x00001004},
        {0x00009e50, 0x00ff03f1},
        {0x00009fc0, 0x803e4788},
index a01f0edb65182a16b95181038786c9d8073a1cab..b995ffe88b33bb8af6ca6a7aaa47e23adee34857 100644 (file)
@@ -318,7 +318,7 @@ static const u32 ar9340_1p0_baseband_core[][2] = {
        {0x00009e30, 0x06336f77},
        {0x00009e34, 0x6af6532f},
        {0x00009e38, 0x0cc80c00},
-       {0x00009e40, 0x0d261820},
+       {0x00009e40, 0x0d261800},
        {0x00009e4c, 0x00001004},
        {0x00009e50, 0x00ff03f1},
        {0x00009e54, 0x00000000},
@@ -348,9 +348,9 @@ static const u32 ar9340_1p0_baseband_core[][2] = {
        {0x0000a370, 0x00000000},
        {0x0000a390, 0x00000001},
        {0x0000a394, 0x00000444},
-       {0x0000a398, 0x00000000},
-       {0x0000a39c, 0x210d0401},
-       {0x0000a3a0, 0xab9a7144},
+       {0x0000a398, 0x001f0e0f},
+       {0x0000a39c, 0x0075393f},
+       {0x0000a3a0, 0xb79f6427},
        {0x0000a3a4, 0x00000000},
        {0x0000a3a8, 0xaaaaaaaa},
        {0x0000a3ac, 0x3c466478},
index 3c9113d9b1bc3cfe3aa7e9e07be8efa1a2df4d14..8e5c3b9786e3ac3fab8cf42e0d80bf84f268d4d2 100644 (file)
@@ -257,9 +257,9 @@ static const u32 qca953x_1p0_baseband_core[][2] = {
        {0x0000a370, 0x00000000},
        {0x0000a390, 0x00000001},
        {0x0000a394, 0x00000444},
-       {0x0000a398, 0x1f020503},
-       {0x0000a39c, 0x29180c03},
-       {0x0000a3a0, 0x9a8b6844},
+       {0x0000a398, 0x001f0e0f},
+       {0x0000a39c, 0x0075393f},
+       {0x0000a3a0, 0xb79f6427},
        {0x0000a3a4, 0x000000ff},
        {0x0000a3a8, 0x6a6a6a6a},
        {0x0000a3ac, 0x6a6a6a6a},
index e6aec2c0207ff43754a79a236a769a33bacf401e..a5ca65240af30b8980b5732a610d73ef155063c9 100644 (file)
@@ -90,7 +90,7 @@ static const u32 ar9580_1p0_baseband_core[][2] = {
        {0x00009e30, 0x06336f77},
        {0x00009e34, 0x6af6532f},
        {0x00009e38, 0x0cc80c00},
-       {0x00009e40, 0x0d261820},
+       {0x00009e40, 0x0d261800},
        {0x00009e4c, 0x00001004},
        {0x00009e50, 0x00ff03f1},
        {0x00009e54, 0x00000000},
index 3ba03dde42150b746bca7f2571a225e34d34af8e..2ca8f7e061742420fe00a9bddf61fb366feb93dc 100644 (file)
@@ -23,8 +23,8 @@
 #include <linux/leds.h>
 #include <linux/completion.h>
 
-#include "debug.h"
 #include "common.h"
+#include "debug.h"
 #include "mci.h"
 #include "dfs.h"
 #include "spectral.h"
@@ -114,6 +114,9 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
 #define ATH_TXFIFO_DEPTH           8
 #define ATH_TX_ERROR               0x01
 
+/* Stop tx traffic 1ms before the GO goes away */
+#define ATH_P2P_PS_STOP_TIME       1000
+
 #define IEEE80211_SEQ_SEQ_SHIFT    4
 #define IEEE80211_SEQ_MAX          4096
 #define IEEE80211_WEP_IVLEN        3
@@ -271,6 +274,7 @@ struct ath_node {
 #ifdef CONFIG_ATH9K_STATION_STATISTICS
        struct ath_rx_rate_stats rx_rate_stats;
 #endif
+       u8 key_idx[4];
 };
 
 struct ath_tx_control {
@@ -366,11 +370,15 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
 /********/
 
 struct ath_vif {
+       struct ieee80211_vif *vif;
        struct ath_node mcast_node;
        int av_bslot;
        bool primary_sta_vif;
        __le64 tsf_adjust; /* TSF adjustment for staggered beacons */
        struct ath_buf *av_bcbuf;
+
+       /* P2P Client */
+       struct ieee80211_noa_data noa;
 };
 
 struct ath9k_vif_iter_data {
@@ -463,6 +471,8 @@ int ath_update_survey_stats(struct ath_softc *sc);
 void ath_update_survey_nf(struct ath_softc *sc, int channel);
 void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type);
 void ath_ps_full_sleep(unsigned long data);
+void ath9k_p2p_ps_timer(void *priv);
+void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif);
 
 /**********/
 /* BTCOEX */
@@ -713,6 +723,9 @@ struct ath_softc {
        struct completion paprd_complete;
        wait_queue_head_t tx_wait;
 
+       struct ath_gen_timer *p2p_ps_timer;
+       struct ath_vif *p2p_ps_vif;
+
        unsigned long driver_data;
 
        u8 gtt_cnt;
@@ -757,6 +770,7 @@ struct ath_softc {
        struct ath_ant_comb ant_comb;
        u8 ant_tx, ant_rx;
        struct dfs_pattern_detector *dfs_detector;
+       u64 dfs_prev_pulse_ts;
        u32 wow_enabled;
        /* relay(fs) channel for spectral scan */
        struct rchan *rfs_chan_spec_scan;
index bd9e634879e69d4b2b33d741f7c4a8c51801ba9f..e387f0b2954a0cf5500610b330f744a5b68cd69c 100644 (file)
@@ -537,8 +537,6 @@ static void ath9k_cache_beacon_config(struct ath_softc *sc,
        cur_conf->dtim_period = bss_conf->dtim_period;
        cur_conf->dtim_count = 1;
        cur_conf->ibss_creator = bss_conf->ibss_creator;
-       cur_conf->bmiss_timeout =
-               ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval;
 
        /*
         * It looks like mac80211 may end up using beacon interval of zero in
@@ -549,6 +547,9 @@ static void ath9k_cache_beacon_config(struct ath_softc *sc,
        if (cur_conf->beacon_interval == 0)
                cur_conf->beacon_interval = 100;
 
+       cur_conf->bmiss_timeout =
+               ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval;
+
        /*
         * We don't parse dtim period from mac80211 during the driver
         * initialization as it breaks association with hidden-ssid
diff --git a/drivers/net/wireless/ath/ath9k/common-debug.c b/drivers/net/wireless/ath/ath9k/common-debug.c
new file mode 100644 (file)
index 0000000..3b289f9
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2008-2011 Atheros Communications Inc.
+ *
+ * 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 "common.h"
+
+static ssize_t read_file_modal_eeprom(struct file *file, char __user *user_buf,
+                                     size_t count, loff_t *ppos)
+{
+       struct ath_hw *ah = file->private_data;
+       u32 len = 0, size = 6000;
+       char *buf;
+       size_t retval;
+
+       buf = kzalloc(size, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       len = ah->eep_ops->dump_eeprom(ah, false, buf, len, size);
+
+       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+       kfree(buf);
+
+       return retval;
+}
+
+static const struct file_operations fops_modal_eeprom = {
+       .read = read_file_modal_eeprom,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+
+void ath9k_cmn_debug_modal_eeprom(struct dentry *debugfs_phy,
+                                 struct ath_hw *ah)
+{
+       debugfs_create_file("modal_eeprom", S_IRUSR, debugfs_phy, ah,
+                           &fops_modal_eeprom);
+}
+EXPORT_SYMBOL(ath9k_cmn_debug_modal_eeprom);
+
+static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+       struct ath_hw *ah = file->private_data;
+       u32 len = 0, size = 1500;
+       ssize_t retval = 0;
+       char *buf;
+
+       buf = kzalloc(size, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       len = ah->eep_ops->dump_eeprom(ah, true, buf, len, size);
+
+       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+       kfree(buf);
+
+       return retval;
+}
+
+static const struct file_operations fops_base_eeprom = {
+       .read = read_file_base_eeprom,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+void ath9k_cmn_debug_base_eeprom(struct dentry *debugfs_phy,
+                                struct ath_hw *ah)
+{
+       debugfs_create_file("base_eeprom", S_IRUSR, debugfs_phy, ah,
+                           &fops_base_eeprom);
+}
+EXPORT_SYMBOL(ath9k_cmn_debug_base_eeprom);
+
+void ath9k_cmn_debug_stat_rx(struct ath_rx_stats *rxstats,
+                            struct ath_rx_status *rs)
+{
+#define RX_PHY_ERR_INC(c) rxstats->phy_err_stats[c]++
+#define RX_CMN_STAT_INC(c) (rxstats->c++)
+
+       RX_CMN_STAT_INC(rx_pkts_all);
+       rxstats->rx_bytes_all += rs->rs_datalen;
+
+       if (rs->rs_status & ATH9K_RXERR_CRC)
+               RX_CMN_STAT_INC(crc_err);
+       if (rs->rs_status & ATH9K_RXERR_DECRYPT)
+               RX_CMN_STAT_INC(decrypt_crc_err);
+       if (rs->rs_status & ATH9K_RXERR_MIC)
+               RX_CMN_STAT_INC(mic_err);
+       if (rs->rs_status & ATH9K_RX_DELIM_CRC_PRE)
+               RX_CMN_STAT_INC(pre_delim_crc_err);
+       if (rs->rs_status & ATH9K_RX_DELIM_CRC_POST)
+               RX_CMN_STAT_INC(post_delim_crc_err);
+       if (rs->rs_status & ATH9K_RX_DECRYPT_BUSY)
+               RX_CMN_STAT_INC(decrypt_busy_err);
+
+       if (rs->rs_status & ATH9K_RXERR_PHY) {
+               RX_CMN_STAT_INC(phy_err);
+               if (rs->rs_phyerr < ATH9K_PHYERR_MAX)
+                       RX_PHY_ERR_INC(rs->rs_phyerr);
+       }
+
+#undef RX_CMN_STAT_INC
+#undef RX_PHY_ERR_INC
+}
+EXPORT_SYMBOL(ath9k_cmn_debug_stat_rx);
+
+static ssize_t read_file_recv(struct file *file, char __user *user_buf,
+                             size_t count, loff_t *ppos)
+{
+#define RXS_ERR(s, e)                                  \
+       do {                                            \
+               len += scnprintf(buf + len, size - len, \
+                                "%18s : %10u\n", s,    \
+                                rxstats->e);           \
+       } while (0)
+
+       struct ath_rx_stats *rxstats = file->private_data;
+       char *buf;
+       unsigned int len = 0, size = 1600;
+       ssize_t retval = 0;
+
+       buf = kzalloc(size, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       RXS_ERR("PKTS-ALL", rx_pkts_all);
+       RXS_ERR("BYTES-ALL", rx_bytes_all);
+       RXS_ERR("BEACONS", rx_beacons);
+       RXS_ERR("FRAGS", rx_frags);
+       RXS_ERR("SPECTRAL", rx_spectral);
+
+       RXS_ERR("CRC ERR", crc_err);
+       RXS_ERR("DECRYPT CRC ERR", decrypt_crc_err);
+       RXS_ERR("PHY ERR", phy_err);
+       RXS_ERR("MIC ERR", mic_err);
+       RXS_ERR("PRE-DELIM CRC ERR", pre_delim_crc_err);
+       RXS_ERR("POST-DELIM CRC ERR", post_delim_crc_err);
+       RXS_ERR("DECRYPT BUSY ERR", decrypt_busy_err);
+       RXS_ERR("LENGTH-ERR", rx_len_err);
+       RXS_ERR("OOM-ERR", rx_oom_err);
+       RXS_ERR("RATE-ERR", rx_rate_err);
+       RXS_ERR("TOO-MANY-FRAGS", rx_too_many_frags_err);
+
+       if (len > size)
+               len = size;
+
+       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+       kfree(buf);
+
+       return retval;
+
+#undef RXS_ERR
+}
+
+static const struct file_operations fops_recv = {
+       .read = read_file_recv,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+void ath9k_cmn_debug_recv(struct dentry *debugfs_phy,
+                         struct ath_rx_stats *rxstats)
+{
+       debugfs_create_file("recv", S_IRUSR, debugfs_phy, rxstats,
+                           &fops_recv);
+}
+EXPORT_SYMBOL(ath9k_cmn_debug_recv);
+
+static ssize_t read_file_phy_err(struct file *file, char __user *user_buf,
+                                size_t count, loff_t *ppos)
+{
+#define PHY_ERR(s, p) \
+       len += scnprintf(buf + len, size - len, "%22s : %10u\n", s, \
+                        rxstats->phy_err_stats[p]);
+
+       struct ath_rx_stats *rxstats = file->private_data;
+       char *buf;
+       unsigned int len = 0, size = 1600;
+       ssize_t retval = 0;
+
+       buf = kzalloc(size, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       PHY_ERR("UNDERRUN ERR", ATH9K_PHYERR_UNDERRUN);
+       PHY_ERR("TIMING ERR", ATH9K_PHYERR_TIMING);
+       PHY_ERR("PARITY ERR", ATH9K_PHYERR_PARITY);
+       PHY_ERR("RATE ERR", ATH9K_PHYERR_RATE);
+       PHY_ERR("LENGTH ERR", ATH9K_PHYERR_LENGTH);
+       PHY_ERR("RADAR ERR", ATH9K_PHYERR_RADAR);
+       PHY_ERR("SERVICE ERR", ATH9K_PHYERR_SERVICE);
+       PHY_ERR("TOR ERR", ATH9K_PHYERR_TOR);
+       PHY_ERR("OFDM-TIMING ERR", ATH9K_PHYERR_OFDM_TIMING);
+       PHY_ERR("OFDM-SIGNAL-PARITY ERR", ATH9K_PHYERR_OFDM_SIGNAL_PARITY);
+       PHY_ERR("OFDM-RATE ERR", ATH9K_PHYERR_OFDM_RATE_ILLEGAL);
+       PHY_ERR("OFDM-LENGTH ERR", ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL);
+       PHY_ERR("OFDM-POWER-DROP ERR", ATH9K_PHYERR_OFDM_POWER_DROP);
+       PHY_ERR("OFDM-SERVICE ERR", ATH9K_PHYERR_OFDM_SERVICE);
+       PHY_ERR("OFDM-RESTART ERR", ATH9K_PHYERR_OFDM_RESTART);
+       PHY_ERR("FALSE-RADAR-EXT ERR", ATH9K_PHYERR_FALSE_RADAR_EXT);
+       PHY_ERR("CCK-TIMING ERR", ATH9K_PHYERR_CCK_TIMING);
+       PHY_ERR("CCK-HEADER-CRC ERR", ATH9K_PHYERR_CCK_HEADER_CRC);
+       PHY_ERR("CCK-RATE ERR", ATH9K_PHYERR_CCK_RATE_ILLEGAL);
+       PHY_ERR("CCK-SERVICE ERR", ATH9K_PHYERR_CCK_SERVICE);
+       PHY_ERR("CCK-RESTART ERR", ATH9K_PHYERR_CCK_RESTART);
+       PHY_ERR("CCK-LENGTH ERR", ATH9K_PHYERR_CCK_LENGTH_ILLEGAL);
+       PHY_ERR("CCK-POWER-DROP ERR", ATH9K_PHYERR_CCK_POWER_DROP);
+       PHY_ERR("HT-CRC ERR", ATH9K_PHYERR_HT_CRC_ERROR);
+       PHY_ERR("HT-LENGTH ERR", ATH9K_PHYERR_HT_LENGTH_ILLEGAL);
+       PHY_ERR("HT-RATE ERR", ATH9K_PHYERR_HT_RATE_ILLEGAL);
+
+       if (len > size)
+               len = size;
+
+       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+       kfree(buf);
+
+       return retval;
+
+#undef PHY_ERR
+}
+
+static const struct file_operations fops_phy_err = {
+       .read = read_file_phy_err,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+void ath9k_cmn_debug_phy_err(struct dentry *debugfs_phy,
+                            struct ath_rx_stats *rxstats)
+{
+       debugfs_create_file("phy_err", S_IRUSR, debugfs_phy, rxstats,
+                           &fops_phy_err);
+}
+EXPORT_SYMBOL(ath9k_cmn_debug_phy_err);
diff --git a/drivers/net/wireless/ath/ath9k/common-debug.h b/drivers/net/wireless/ath/ath9k/common-debug.h
new file mode 100644 (file)
index 0000000..7c97884
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2008-2011 Atheros Communications Inc.
+ *
+ * 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.
+ */
+
+
+
+/**
+ * struct ath_rx_stats - RX Statistics
+ * @rx_pkts_all:  No. of total frames received, including ones that
+       may have had errors.
+ * @rx_bytes_all:  No. of total bytes received, including ones that
+       may have had errors.
+ * @crc_err: No. of frames with incorrect CRC value
+ * @decrypt_crc_err: No. of frames whose CRC check failed after
+       decryption process completed
+ * @phy_err: No. of frames whose reception failed because the PHY
+       encountered an error
+ * @mic_err: No. of frames with incorrect TKIP MIC verification failure
+ * @pre_delim_crc_err: Pre-Frame delimiter CRC error detections
+ * @post_delim_crc_err: Post-Frame delimiter CRC error detections
+ * @decrypt_busy_err: Decryption interruptions counter
+ * @phy_err_stats: Individual PHY error statistics
+ * @rx_len_err:  No. of frames discarded due to bad length.
+ * @rx_oom_err:  No. of frames dropped due to OOM issues.
+ * @rx_rate_err:  No. of frames dropped due to rate errors.
+ * @rx_too_many_frags_err:  Frames dropped due to too-many-frags received.
+ * @rx_beacons:  No. of beacons received.
+ * @rx_frags:  No. of rx-fragements received.
+ * @rx_spectral: No of spectral packets received.
+ */
+struct ath_rx_stats {
+       u32 rx_pkts_all;
+       u32 rx_bytes_all;
+       u32 crc_err;
+       u32 decrypt_crc_err;
+       u32 phy_err;
+       u32 mic_err;
+       u32 pre_delim_crc_err;
+       u32 post_delim_crc_err;
+       u32 decrypt_busy_err;
+       u32 phy_err_stats[ATH9K_PHYERR_MAX];
+       u32 rx_len_err;
+       u32 rx_oom_err;
+       u32 rx_rate_err;
+       u32 rx_too_many_frags_err;
+       u32 rx_beacons;
+       u32 rx_frags;
+       u32 rx_spectral;
+};
+
+void ath9k_cmn_debug_modal_eeprom(struct dentry *debugfs_phy,
+                                 struct ath_hw *ah);
+void ath9k_cmn_debug_base_eeprom(struct dentry *debugfs_phy,
+                                struct ath_hw *ah);
+void ath9k_cmn_debug_stat_rx(struct ath_rx_stats *rxstats,
+                            struct ath_rx_status *rs);
+void ath9k_cmn_debug_recv(struct dentry *debugfs_phy,
+                         struct ath_rx_stats *rxstats);
+void ath9k_cmn_debug_phy_err(struct dentry *debugfs_phy,
+                            struct ath_rx_stats *rxstats);
index ca38116838f00e5206c5ec35b3589c9bc4cbba58..ffc454b18637588f37964857c5a1b4997cc2ee5c 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "common-init.h"
 #include "common-beacon.h"
+#include "common-debug.h"
 
 /* Common header for Atheros 802.11n base driver cores */
 
index 780ff1bee6f69ceac8729a9b75913c0bdad6c28b..6cc42be48d4e60e68f3e2603ebfc465f89159a0d 100644 (file)
@@ -948,151 +948,11 @@ static const struct file_operations fops_reset = {
        .llseek = default_llseek,
 };
 
-static ssize_t read_file_recv(struct file *file, char __user *user_buf,
-                             size_t count, loff_t *ppos)
-{
-#define RXS_ERR(s, e)                                      \
-       do {                                                \
-               len += scnprintf(buf + len, size - len,     \
-                                "%18s : %10u\n", s,        \
-                                sc->debug.stats.rxstats.e);\
-       } while (0)
-
-       struct ath_softc *sc = file->private_data;
-       char *buf;
-       unsigned int len = 0, size = 1600;
-       ssize_t retval = 0;
-
-       buf = kzalloc(size, GFP_KERNEL);
-       if (buf == NULL)
-               return -ENOMEM;
-
-       RXS_ERR("PKTS-ALL", rx_pkts_all);
-       RXS_ERR("BYTES-ALL", rx_bytes_all);
-       RXS_ERR("BEACONS", rx_beacons);
-       RXS_ERR("FRAGS", rx_frags);
-       RXS_ERR("SPECTRAL", rx_spectral);
-
-       RXS_ERR("CRC ERR", crc_err);
-       RXS_ERR("DECRYPT CRC ERR", decrypt_crc_err);
-       RXS_ERR("PHY ERR", phy_err);
-       RXS_ERR("MIC ERR", mic_err);
-       RXS_ERR("PRE-DELIM CRC ERR", pre_delim_crc_err);
-       RXS_ERR("POST-DELIM CRC ERR", post_delim_crc_err);
-       RXS_ERR("DECRYPT BUSY ERR", decrypt_busy_err);
-       RXS_ERR("LENGTH-ERR", rx_len_err);
-       RXS_ERR("OOM-ERR", rx_oom_err);
-       RXS_ERR("RATE-ERR", rx_rate_err);
-       RXS_ERR("TOO-MANY-FRAGS", rx_too_many_frags_err);
-
-       if (len > size)
-               len = size;
-
-       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-       kfree(buf);
-
-       return retval;
-
-#undef RXS_ERR
-}
-
 void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs)
 {
-#define RX_PHY_ERR_INC(c) sc->debug.stats.rxstats.phy_err_stats[c]++
-
-       RX_STAT_INC(rx_pkts_all);
-       sc->debug.stats.rxstats.rx_bytes_all += rs->rs_datalen;
-
-       if (rs->rs_status & ATH9K_RXERR_CRC)
-               RX_STAT_INC(crc_err);
-       if (rs->rs_status & ATH9K_RXERR_DECRYPT)
-               RX_STAT_INC(decrypt_crc_err);
-       if (rs->rs_status & ATH9K_RXERR_MIC)
-               RX_STAT_INC(mic_err);
-       if (rs->rs_status & ATH9K_RX_DELIM_CRC_PRE)
-               RX_STAT_INC(pre_delim_crc_err);
-       if (rs->rs_status & ATH9K_RX_DELIM_CRC_POST)
-               RX_STAT_INC(post_delim_crc_err);
-       if (rs->rs_status & ATH9K_RX_DECRYPT_BUSY)
-               RX_STAT_INC(decrypt_busy_err);
-
-       if (rs->rs_status & ATH9K_RXERR_PHY) {
-               RX_STAT_INC(phy_err);
-               if (rs->rs_phyerr < ATH9K_PHYERR_MAX)
-                       RX_PHY_ERR_INC(rs->rs_phyerr);
-       }
-
-#undef RX_PHY_ERR_INC
+       ath9k_cmn_debug_stat_rx(&sc->debug.stats.rxstats, rs);
 }
 
-static const struct file_operations fops_recv = {
-       .read = read_file_recv,
-       .open = simple_open,
-       .owner = THIS_MODULE,
-       .llseek = default_llseek,
-};
-
-static ssize_t read_file_phy_err(struct file *file, char __user *user_buf,
-                                size_t count, loff_t *ppos)
-{
-#define PHY_ERR(s, p) \
-       len += scnprintf(buf + len, size - len, "%22s : %10u\n", s, \
-                        sc->debug.stats.rxstats.phy_err_stats[p]);
-
-       struct ath_softc *sc = file->private_data;
-       char *buf;
-       unsigned int len = 0, size = 1600;
-       ssize_t retval = 0;
-
-       buf = kzalloc(size, GFP_KERNEL);
-       if (buf == NULL)
-               return -ENOMEM;
-
-       PHY_ERR("UNDERRUN ERR", ATH9K_PHYERR_UNDERRUN);
-       PHY_ERR("TIMING ERR", ATH9K_PHYERR_TIMING);
-       PHY_ERR("PARITY ERR", ATH9K_PHYERR_PARITY);
-       PHY_ERR("RATE ERR", ATH9K_PHYERR_RATE);
-       PHY_ERR("LENGTH ERR", ATH9K_PHYERR_LENGTH);
-       PHY_ERR("RADAR ERR", ATH9K_PHYERR_RADAR);
-       PHY_ERR("SERVICE ERR", ATH9K_PHYERR_SERVICE);
-       PHY_ERR("TOR ERR", ATH9K_PHYERR_TOR);
-       PHY_ERR("OFDM-TIMING ERR", ATH9K_PHYERR_OFDM_TIMING);
-       PHY_ERR("OFDM-SIGNAL-PARITY ERR", ATH9K_PHYERR_OFDM_SIGNAL_PARITY);
-       PHY_ERR("OFDM-RATE ERR", ATH9K_PHYERR_OFDM_RATE_ILLEGAL);
-       PHY_ERR("OFDM-LENGTH ERR", ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL);
-       PHY_ERR("OFDM-POWER-DROP ERR", ATH9K_PHYERR_OFDM_POWER_DROP);
-       PHY_ERR("OFDM-SERVICE ERR", ATH9K_PHYERR_OFDM_SERVICE);
-       PHY_ERR("OFDM-RESTART ERR", ATH9K_PHYERR_OFDM_RESTART);
-       PHY_ERR("FALSE-RADAR-EXT ERR", ATH9K_PHYERR_FALSE_RADAR_EXT);
-       PHY_ERR("CCK-TIMING ERR", ATH9K_PHYERR_CCK_TIMING);
-       PHY_ERR("CCK-HEADER-CRC ERR", ATH9K_PHYERR_CCK_HEADER_CRC);
-       PHY_ERR("CCK-RATE ERR", ATH9K_PHYERR_CCK_RATE_ILLEGAL);
-       PHY_ERR("CCK-SERVICE ERR", ATH9K_PHYERR_CCK_SERVICE);
-       PHY_ERR("CCK-RESTART ERR", ATH9K_PHYERR_CCK_RESTART);
-       PHY_ERR("CCK-LENGTH ERR", ATH9K_PHYERR_CCK_LENGTH_ILLEGAL);
-       PHY_ERR("CCK-POWER-DROP ERR", ATH9K_PHYERR_CCK_POWER_DROP);
-       PHY_ERR("HT-CRC ERR", ATH9K_PHYERR_HT_CRC_ERROR);
-       PHY_ERR("HT-LENGTH ERR", ATH9K_PHYERR_HT_LENGTH_ILLEGAL);
-       PHY_ERR("HT-RATE ERR", ATH9K_PHYERR_HT_RATE_ILLEGAL);
-
-       if (len > size)
-               len = size;
-
-       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-       kfree(buf);
-
-       return retval;
-
-#undef PHY_ERR
-}
-
-static const struct file_operations fops_phy_err = {
-       .read = read_file_phy_err,
-       .open = simple_open,
-       .owner = THIS_MODULE,
-       .llseek = default_llseek,
-};
-
 static ssize_t read_file_regidx(struct file *file, char __user *user_buf,
                                 size_t count, loff_t *ppos)
 {
@@ -1268,62 +1128,6 @@ static const struct file_operations fops_dump_nfcal = {
        .llseek = default_llseek,
 };
 
-static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf,
-                                    size_t count, loff_t *ppos)
-{
-       struct ath_softc *sc = file->private_data;
-       struct ath_hw *ah = sc->sc_ah;
-       u32 len = 0, size = 1500;
-       ssize_t retval = 0;
-       char *buf;
-
-       buf = kzalloc(size, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-
-       len = ah->eep_ops->dump_eeprom(ah, true, buf, len, size);
-
-       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-       kfree(buf);
-
-       return retval;
-}
-
-static const struct file_operations fops_base_eeprom = {
-       .read = read_file_base_eeprom,
-       .open = simple_open,
-       .owner = THIS_MODULE,
-       .llseek = default_llseek,
-};
-
-static ssize_t read_file_modal_eeprom(struct file *file, char __user *user_buf,
-                                     size_t count, loff_t *ppos)
-{
-       struct ath_softc *sc = file->private_data;
-       struct ath_hw *ah = sc->sc_ah;
-       u32 len = 0, size = 6000;
-       char *buf;
-       size_t retval;
-
-       buf = kzalloc(size, GFP_KERNEL);
-       if (buf == NULL)
-               return -ENOMEM;
-
-       len = ah->eep_ops->dump_eeprom(ah, false, buf, len, size);
-
-       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-       kfree(buf);
-
-       return retval;
-}
-
-static const struct file_operations fops_modal_eeprom = {
-       .read = read_file_modal_eeprom,
-       .open = simple_open,
-       .owner = THIS_MODULE,
-       .llseek = default_llseek,
-};
-
 #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
 static ssize_t read_file_btcoex(struct file *file, char __user *user_buf,
                                size_t count, loff_t *ppos)
@@ -1524,10 +1328,10 @@ int ath9k_init_debug(struct ath_hw *ah)
                            &fops_misc);
        debugfs_create_file("reset", S_IRUSR, sc->debug.debugfs_phy, sc,
                            &fops_reset);
-       debugfs_create_file("recv", S_IRUSR, sc->debug.debugfs_phy, sc,
-                           &fops_recv);
-       debugfs_create_file("phy_err", S_IRUSR, sc->debug.debugfs_phy, sc,
-                           &fops_phy_err);
+
+       ath9k_cmn_debug_recv(sc->debug.debugfs_phy, &sc->debug.stats.rxstats);
+       ath9k_cmn_debug_phy_err(sc->debug.debugfs_phy, &sc->debug.stats.rxstats);
+
        debugfs_create_u8("rx_chainmask", S_IRUSR, sc->debug.debugfs_phy,
                          &ah->rxchainmask);
        debugfs_create_u8("tx_chainmask", S_IRUSR, sc->debug.debugfs_phy,
@@ -1547,10 +1351,10 @@ int ath9k_init_debug(struct ath_hw *ah)
                            &fops_regdump);
        debugfs_create_file("dump_nfcal", S_IRUSR, sc->debug.debugfs_phy, sc,
                            &fops_dump_nfcal);
-       debugfs_create_file("base_eeprom", S_IRUSR, sc->debug.debugfs_phy, sc,
-                           &fops_base_eeprom);
-       debugfs_create_file("modal_eeprom", S_IRUSR, sc->debug.debugfs_phy, sc,
-                           &fops_modal_eeprom);
+
+       ath9k_cmn_debug_base_eeprom(sc->debug.debugfs_phy, sc->sc_ah);
+       ath9k_cmn_debug_modal_eeprom(sc->debug.debugfs_phy, sc->sc_ah);
+
        debugfs_create_u32("gpio_mask", S_IRUSR | S_IWUSR,
                           sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask);
        debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR,
index 559a68c2709cc882d5edf646417dd7348d8fffc8..53ae15bd0c9d4ac8b40a7a9de3689c607d56c3cd 100644 (file)
@@ -221,50 +221,6 @@ struct ath_rx_rate_stats {
        } cck_stats[4];
 };
 
-/**
- * struct ath_rx_stats - RX Statistics
- * @rx_pkts_all:  No. of total frames received, including ones that
-       may have had errors.
- * @rx_bytes_all:  No. of total bytes received, including ones that
-       may have had errors.
- * @crc_err: No. of frames with incorrect CRC value
- * @decrypt_crc_err: No. of frames whose CRC check failed after
-       decryption process completed
- * @phy_err: No. of frames whose reception failed because the PHY
-       encountered an error
- * @mic_err: No. of frames with incorrect TKIP MIC verification failure
- * @pre_delim_crc_err: Pre-Frame delimiter CRC error detections
- * @post_delim_crc_err: Post-Frame delimiter CRC error detections
- * @decrypt_busy_err: Decryption interruptions counter
- * @phy_err_stats: Individual PHY error statistics
- * @rx_len_err:  No. of frames discarded due to bad length.
- * @rx_oom_err:  No. of frames dropped due to OOM issues.
- * @rx_rate_err:  No. of frames dropped due to rate errors.
- * @rx_too_many_frags_err:  Frames dropped due to too-many-frags received.
- * @rx_beacons:  No. of beacons received.
- * @rx_frags:  No. of rx-fragements received.
- * @rx_spectral: No of spectral packets received.
- */
-struct ath_rx_stats {
-       u32 rx_pkts_all;
-       u32 rx_bytes_all;
-       u32 crc_err;
-       u32 decrypt_crc_err;
-       u32 phy_err;
-       u32 mic_err;
-       u32 pre_delim_crc_err;
-       u32 post_delim_crc_err;
-       u32 decrypt_busy_err;
-       u32 phy_err_stats[ATH9K_PHYERR_MAX];
-       u32 rx_len_err;
-       u32 rx_oom_err;
-       u32 rx_rate_err;
-       u32 rx_too_many_frags_err;
-       u32 rx_beacons;
-       u32 rx_frags;
-       u32 rx_spectral;
-};
-
 #define ANT_MAIN 0
 #define ANT_ALT  1
 
index 857bb28b389411ea2e1586abd3f5c749c0127327..726271c7c3306e8a9255fd645bcbe0d3c6d8fb3e 100644 (file)
@@ -178,12 +178,12 @@ void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data,
        pe.ts = mactime;
        if (ath9k_postprocess_radar_event(sc, &ard, &pe)) {
                struct dfs_pattern_detector *pd = sc->dfs_detector;
-               static u64 last_ts;
                ath_dbg(common, DFS,
                        "ath9k_dfs_process_phyerr: channel=%d, ts=%llu, "
                        "width=%d, rssi=%d, delta_ts=%llu\n",
-                       pe.freq, pe.ts, pe.width, pe.rssi, pe.ts-last_ts);
-               last_ts = pe.ts;
+                       pe.freq, pe.ts, pe.width, pe.rssi,
+                       pe.ts - sc->dfs_prev_pulse_ts);
+               sc->dfs_prev_pulse_ts = pe.ts;
                DFS_STAT_INC(sc, pulses_processed);
                if (pd != NULL && pd->add_pulse(pd, &pe)) {
                        DFS_STAT_INC(sc, radar_detected);
index dab1f0cab9937d17fd0df75d88c3eafb0ff8ec54..09a5d72f3ff5b4e9cf27b6c0305193e738c1aba5 100644 (file)
@@ -325,14 +325,14 @@ static inline struct ath9k_htc_tx_ctl *HTC_SKB_CB(struct sk_buff *skb)
 
 #define TX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.tx_stats.c++)
 #define TX_STAT_ADD(c, a) (hif_dev->htc_handle->drv_priv->debug.tx_stats.c += a)
-#define RX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.rx_stats.c++)
-#define RX_STAT_ADD(c, a) (hif_dev->htc_handle->drv_priv->debug.rx_stats.c += a)
+#define RX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.skbrx_stats.c++)
+#define RX_STAT_ADD(c, a) (hif_dev->htc_handle->drv_priv->debug.skbrx_stats.c += a)
 #define CAB_STAT_INC   priv->debug.tx_stats.cab_queued++
 
 #define TX_QSTAT_INC(q) (priv->debug.tx_stats.queue_stats[q]++)
 
 void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv,
-                          struct ath_htc_rx_status *rxs);
+                          struct ath_rx_status *rs);
 
 struct ath_tx_stats {
        u32 buf_queued;
@@ -345,25 +345,18 @@ struct ath_tx_stats {
        u32 queue_stats[IEEE80211_NUM_ACS];
 };
 
-struct ath_rx_stats {
+struct ath_skbrx_stats {
        u32 skb_allocated;
        u32 skb_completed;
        u32 skb_completed_bytes;
        u32 skb_dropped;
-       u32 err_crc;
-       u32 err_decrypt_crc;
-       u32 err_mic;
-       u32 err_pre_delim;
-       u32 err_post_delim;
-       u32 err_decrypt_busy;
-       u32 err_phy;
-       u32 err_phy_stats[ATH9K_PHYERR_MAX];
 };
 
 struct ath9k_debug {
        struct dentry *debugfs_phy;
        struct ath_tx_stats tx_stats;
        struct ath_rx_stats rx_stats;
+       struct ath_skbrx_stats skbrx_stats;
 };
 
 void ath9k_htc_get_et_strings(struct ieee80211_hw *hw,
@@ -385,7 +378,7 @@ void ath9k_htc_get_et_stats(struct ieee80211_hw *hw,
 #define TX_QSTAT_INC(c) do { } while (0)
 
 static inline void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv,
-                                        struct ath_htc_rx_status *rxs)
+                                        struct ath_rx_status *rs)
 {
 }
 
index fb071ee4fcfb3dd5969005a40537908ec92791c6..8b529e4b8ac4f37019426102b5d8d4b428882701 100644 (file)
@@ -243,39 +243,14 @@ static const struct file_operations fops_xmit = {
 };
 
 void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv,
-                          struct ath_htc_rx_status *rxs)
+                            struct ath_rx_status *rs)
 {
-#define RX_PHY_ERR_INC(c) priv->debug.rx_stats.err_phy_stats[c]++
-
-       if (rxs->rs_status & ATH9K_RXERR_CRC)
-               priv->debug.rx_stats.err_crc++;
-       if (rxs->rs_status & ATH9K_RXERR_DECRYPT)
-               priv->debug.rx_stats.err_decrypt_crc++;
-       if (rxs->rs_status & ATH9K_RXERR_MIC)
-               priv->debug.rx_stats.err_mic++;
-       if (rxs->rs_status & ATH9K_RX_DELIM_CRC_PRE)
-               priv->debug.rx_stats.err_pre_delim++;
-       if (rxs->rs_status & ATH9K_RX_DELIM_CRC_POST)
-               priv->debug.rx_stats.err_post_delim++;
-       if (rxs->rs_status & ATH9K_RX_DECRYPT_BUSY)
-               priv->debug.rx_stats.err_decrypt_busy++;
-
-       if (rxs->rs_status & ATH9K_RXERR_PHY) {
-               priv->debug.rx_stats.err_phy++;
-               if (rxs->rs_phyerr < ATH9K_PHYERR_MAX)
-                       RX_PHY_ERR_INC(rxs->rs_phyerr);
-       }
-
-#undef RX_PHY_ERR_INC
+       ath9k_cmn_debug_stat_rx(&priv->debug.rx_stats, rs);
 }
 
-static ssize_t read_file_recv(struct file *file, char __user *user_buf,
+static ssize_t read_file_skb_rx(struct file *file, char __user *user_buf,
                              size_t count, loff_t *ppos)
 {
-#define PHY_ERR(s, 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;
        unsigned int len = 0, size = 1500;
@@ -287,63 +262,13 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf,
 
        len += scnprintf(buf + len, size - len,
                         "%20s : %10u\n", "SKBs allocated",
-                        priv->debug.rx_stats.skb_allocated);
+                        priv->debug.skbrx_stats.skb_allocated);
        len += scnprintf(buf + len, size - len,
                         "%20s : %10u\n", "SKBs completed",
-                        priv->debug.rx_stats.skb_completed);
+                        priv->debug.skbrx_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);
-       PHY_ERR("TIMING", ATH9K_PHYERR_TIMING);
-       PHY_ERR("PARITY", ATH9K_PHYERR_PARITY);
-       PHY_ERR("RATE", ATH9K_PHYERR_RATE);
-       PHY_ERR("LENGTH", ATH9K_PHYERR_LENGTH);
-       PHY_ERR("RADAR", ATH9K_PHYERR_RADAR);
-       PHY_ERR("SERVICE", ATH9K_PHYERR_SERVICE);
-       PHY_ERR("TOR", ATH9K_PHYERR_TOR);
-       PHY_ERR("OFDM-TIMING", ATH9K_PHYERR_OFDM_TIMING);
-       PHY_ERR("OFDM-SIGNAL-PARITY", ATH9K_PHYERR_OFDM_SIGNAL_PARITY);
-       PHY_ERR("OFDM-RATE", ATH9K_PHYERR_OFDM_RATE_ILLEGAL);
-       PHY_ERR("OFDM-LENGTH", ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL);
-       PHY_ERR("OFDM-POWER-DROP", ATH9K_PHYERR_OFDM_POWER_DROP);
-       PHY_ERR("OFDM-SERVICE", ATH9K_PHYERR_OFDM_SERVICE);
-       PHY_ERR("OFDM-RESTART", ATH9K_PHYERR_OFDM_RESTART);
-       PHY_ERR("FALSE-RADAR-EXT", ATH9K_PHYERR_FALSE_RADAR_EXT);
-       PHY_ERR("CCK-TIMING", ATH9K_PHYERR_CCK_TIMING);
-       PHY_ERR("CCK-HEADER-CRC", ATH9K_PHYERR_CCK_HEADER_CRC);
-       PHY_ERR("CCK-RATE", ATH9K_PHYERR_CCK_RATE_ILLEGAL);
-       PHY_ERR("CCK-SERVICE", ATH9K_PHYERR_CCK_SERVICE);
-       PHY_ERR("CCK-RESTART", ATH9K_PHYERR_CCK_RESTART);
-       PHY_ERR("CCK-LENGTH", ATH9K_PHYERR_CCK_LENGTH_ILLEGAL);
-       PHY_ERR("CCK-POWER-DROP", ATH9K_PHYERR_CCK_POWER_DROP);
-       PHY_ERR("HT-CRC", ATH9K_PHYERR_HT_CRC_ERROR);
-       PHY_ERR("HT-LENGTH", ATH9K_PHYERR_HT_LENGTH_ILLEGAL);
-       PHY_ERR("HT-RATE", ATH9K_PHYERR_HT_RATE_ILLEGAL);
+                        priv->debug.skbrx_stats.skb_dropped);
 
        if (len > size)
                len = size;
@@ -352,12 +277,10 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf,
        kfree(buf);
 
        return retval;
-
-#undef PHY_ERR
 }
 
-static const struct file_operations fops_recv = {
-       .read = read_file_recv,
+static const struct file_operations fops_skb_rx = {
+       .read = read_file_skb_rx,
        .open = simple_open,
        .owner = THIS_MODULE,
        .llseek = default_llseek,
@@ -486,423 +409,6 @@ static const struct file_operations fops_debug = {
        .llseek = default_llseek,
 };
 
-static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf,
-                                    size_t count, loff_t *ppos)
-{
-       struct ath9k_htc_priv *priv = file->private_data;
-       struct ath_common *common = ath9k_hw_common(priv->ah);
-       struct base_eep_header *pBase = NULL;
-       unsigned int len = 0, size = 1500;
-       ssize_t retval = 0;
-       char *buf;
-
-       pBase = ath9k_htc_get_eeprom_base(priv);
-
-       if (pBase == NULL) {
-               ath_err(common, "Unknown EEPROM type\n");
-               return 0;
-       }
-
-       buf = kzalloc(size, GFP_KERNEL);
-       if (buf == NULL)
-               return -ENOMEM;
-
-       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.
-        */
-       if (AR_SREV_9271(priv->ah)) {
-               struct base_eep_header_4k *pBase4k =
-                       &priv->ah->eeprom.map4k.baseEepHeader;
-
-               len += scnprintf(buf + len, size - len,
-                                "%20s : %10d\n",
-                                "TX Gain type",
-                                pBase4k->txGainType);
-       }
-
-       /*
-        * UB95 specific data.
-        */
-       if (priv->ah->hw_version.usbdev == AR9287_USB) {
-               struct base_eep_ar9287_header *pBase9287 =
-                       &priv->ah->eeprom.map9287.baseEepHeader;
-
-               len += scnprintf(buf + len, size - len,
-                                "%20s : %10ddB\n",
-                                "Power Table Offset",
-                                pBase9287->pwrTableOffset);
-
-               len += scnprintf(buf + len, size - len,
-                                "%20s : %10d\n",
-                                "OpenLoop Power Ctrl",
-                                pBase9287->openLoopPwrCntl);
-       }
-
-       len += scnprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress",
-                        pBase->macAddr);
-       if (len > size)
-               len = size;
-
-       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-       kfree(buf);
-
-       return retval;
-}
-
-static const struct file_operations fops_base_eeprom = {
-       .read = read_file_base_eeprom,
-       .open = simple_open,
-       .owner = THIS_MODULE,
-       .llseek = default_llseek,
-};
-
-static ssize_t read_4k_modal_eeprom(struct file *file,
-                                   char __user *user_buf,
-                                   size_t count, loff_t *ppos)
-{
-#define PR_EEP(_s, _val)                                               \
-       do {                                                            \
-               len += scnprintf(buf + len, size - len, "%20s : %10d\n",\
-                                _s, (_val));                           \
-       } while (0)
-
-       struct ath9k_htc_priv *priv = file->private_data;
-       struct modal_eep_4k_header *pModal = &priv->ah->eeprom.map4k.modalHeader;
-       unsigned int len = 0, size = 2048;
-       ssize_t retval = 0;
-       char *buf;
-
-       buf = kzalloc(size, GFP_KERNEL);
-       if (buf == NULL)
-               return -ENOMEM;
-
-       PR_EEP("Chain0 Ant. Control", pModal->antCtrlChain[0]);
-       PR_EEP("Ant. Common Control", pModal->antCtrlCommon);
-       PR_EEP("Chain0 Ant. Gain", pModal->antennaGainCh[0]);
-       PR_EEP("Switch Settle", pModal->switchSettling);
-       PR_EEP("Chain0 TxRxAtten", pModal->txRxAttenCh[0]);
-       PR_EEP("Chain0 RxTxMargin", pModal->rxTxMarginCh[0]);
-       PR_EEP("ADC Desired size", pModal->adcDesiredSize);
-       PR_EEP("PGA Desired size", pModal->pgaDesiredSize);
-       PR_EEP("Chain0 xlna Gain", pModal->xlnaGainCh[0]);
-       PR_EEP("txEndToXpaOff", pModal->txEndToXpaOff);
-       PR_EEP("txEndToRxOn", pModal->txEndToRxOn);
-       PR_EEP("txFrameToXpaOn", pModal->txFrameToXpaOn);
-       PR_EEP("CCA Threshold)", pModal->thresh62);
-       PR_EEP("Chain0 NF Threshold", pModal->noiseFloorThreshCh[0]);
-       PR_EEP("xpdGain", pModal->xpdGain);
-       PR_EEP("External PD", pModal->xpd);
-       PR_EEP("Chain0 I Coefficient", pModal->iqCalICh[0]);
-       PR_EEP("Chain0 Q Coefficient", pModal->iqCalQCh[0]);
-       PR_EEP("pdGainOverlap", pModal->pdGainOverlap);
-       PR_EEP("O/D Bias Version", pModal->version);
-       PR_EEP("CCK OutputBias", pModal->ob_0);
-       PR_EEP("BPSK OutputBias", pModal->ob_1);
-       PR_EEP("QPSK OutputBias", pModal->ob_2);
-       PR_EEP("16QAM OutputBias", pModal->ob_3);
-       PR_EEP("64QAM OutputBias", pModal->ob_4);
-       PR_EEP("CCK Driver1_Bias", pModal->db1_0);
-       PR_EEP("BPSK Driver1_Bias", pModal->db1_1);
-       PR_EEP("QPSK Driver1_Bias", pModal->db1_2);
-       PR_EEP("16QAM Driver1_Bias", pModal->db1_3);
-       PR_EEP("64QAM Driver1_Bias", pModal->db1_4);
-       PR_EEP("CCK Driver2_Bias", pModal->db2_0);
-       PR_EEP("BPSK Driver2_Bias", pModal->db2_1);
-       PR_EEP("QPSK Driver2_Bias", pModal->db2_2);
-       PR_EEP("16QAM Driver2_Bias", pModal->db2_3);
-       PR_EEP("64QAM Driver2_Bias", pModal->db2_4);
-       PR_EEP("xPA Bias Level", pModal->xpaBiasLvl);
-       PR_EEP("txFrameToDataStart", pModal->txFrameToDataStart);
-       PR_EEP("txFrameToPaOn", pModal->txFrameToPaOn);
-       PR_EEP("HT40 Power Inc.", pModal->ht40PowerIncForPdadc);
-       PR_EEP("Chain0 bswAtten", pModal->bswAtten[0]);
-       PR_EEP("Chain0 bswMargin", pModal->bswMargin[0]);
-       PR_EEP("HT40 Switch Settle", pModal->swSettleHt40);
-       PR_EEP("Chain0 xatten2Db", pModal->xatten2Db[0]);
-       PR_EEP("Chain0 xatten2Margin", pModal->xatten2Margin[0]);
-       PR_EEP("Ant. Diversity ctl1", pModal->antdiv_ctl1);
-       PR_EEP("Ant. Diversity ctl2", pModal->antdiv_ctl2);
-       PR_EEP("TX Diversity", pModal->tx_diversity);
-
-       if (len > size)
-               len = size;
-
-       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-       kfree(buf);
-
-       return retval;
-
-#undef PR_EEP
-}
-
-static ssize_t read_def_modal_eeprom(struct file *file,
-                                    char __user *user_buf,
-                                    size_t count, loff_t *ppos)
-{
-#define PR_EEP(_s, _val)                                               \
-       do {                                                            \
-               if (pBase->opCapFlags & AR5416_OPFLAGS_11G) {           \
-                       pModal = &priv->ah->eeprom.def.modalHeader[1];  \
-                       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 += scnprintf(buf + len, size - len, "%9d\n",\
-                                       (_val));                        \
-               }                                                       \
-       } while (0)
-
-       struct ath9k_htc_priv *priv = file->private_data;
-       struct base_eep_header *pBase = &priv->ah->eeprom.def.baseEepHeader;
-       struct modal_eep_header *pModal = NULL;
-       unsigned int len = 0, size = 3500;
-       ssize_t retval = 0;
-       char *buf;
-
-       buf = kzalloc(size, GFP_KERNEL);
-       if (buf == NULL)
-               return -ENOMEM;
-
-       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]);
-       PR_EEP("Chain2 Ant. Control", pModal->antCtrlChain[2]);
-       PR_EEP("Ant. Common Control", pModal->antCtrlCommon);
-       PR_EEP("Chain0 Ant. Gain", pModal->antennaGainCh[0]);
-       PR_EEP("Chain1 Ant. Gain", pModal->antennaGainCh[1]);
-       PR_EEP("Chain2 Ant. Gain", pModal->antennaGainCh[2]);
-       PR_EEP("Switch Settle", pModal->switchSettling);
-       PR_EEP("Chain0 TxRxAtten", pModal->txRxAttenCh[0]);
-       PR_EEP("Chain1 TxRxAtten", pModal->txRxAttenCh[1]);
-       PR_EEP("Chain2 TxRxAtten", pModal->txRxAttenCh[2]);
-       PR_EEP("Chain0 RxTxMargin", pModal->rxTxMarginCh[0]);
-       PR_EEP("Chain1 RxTxMargin", pModal->rxTxMarginCh[1]);
-       PR_EEP("Chain2 RxTxMargin", pModal->rxTxMarginCh[2]);
-       PR_EEP("ADC Desired size", pModal->adcDesiredSize);
-       PR_EEP("PGA Desired size", pModal->pgaDesiredSize);
-       PR_EEP("Chain0 xlna Gain", pModal->xlnaGainCh[0]);
-       PR_EEP("Chain1 xlna Gain", pModal->xlnaGainCh[1]);
-       PR_EEP("Chain2 xlna Gain", pModal->xlnaGainCh[2]);
-       PR_EEP("txEndToXpaOff", pModal->txEndToXpaOff);
-       PR_EEP("txEndToRxOn", pModal->txEndToRxOn);
-       PR_EEP("txFrameToXpaOn", pModal->txFrameToXpaOn);
-       PR_EEP("CCA Threshold)", pModal->thresh62);
-       PR_EEP("Chain0 NF Threshold", pModal->noiseFloorThreshCh[0]);
-       PR_EEP("Chain1 NF Threshold", pModal->noiseFloorThreshCh[1]);
-       PR_EEP("Chain2 NF Threshold", pModal->noiseFloorThreshCh[2]);
-       PR_EEP("xpdGain", pModal->xpdGain);
-       PR_EEP("External PD", pModal->xpd);
-       PR_EEP("Chain0 I Coefficient", pModal->iqCalICh[0]);
-       PR_EEP("Chain1 I Coefficient", pModal->iqCalICh[1]);
-       PR_EEP("Chain2 I Coefficient", pModal->iqCalICh[2]);
-       PR_EEP("Chain0 Q Coefficient", pModal->iqCalQCh[0]);
-       PR_EEP("Chain1 Q Coefficient", pModal->iqCalQCh[1]);
-       PR_EEP("Chain2 Q Coefficient", pModal->iqCalQCh[2]);
-       PR_EEP("pdGainOverlap", pModal->pdGainOverlap);
-       PR_EEP("Chain0 OutputBias", pModal->ob);
-       PR_EEP("Chain0 DriverBias", pModal->db);
-       PR_EEP("xPA Bias Level", pModal->xpaBiasLvl);
-       PR_EEP("2chain pwr decrease", pModal->pwrDecreaseFor2Chain);
-       PR_EEP("3chain pwr decrease", pModal->pwrDecreaseFor3Chain);
-       PR_EEP("txFrameToDataStart", pModal->txFrameToDataStart);
-       PR_EEP("txFrameToPaOn", pModal->txFrameToPaOn);
-       PR_EEP("HT40 Power Inc.", pModal->ht40PowerIncForPdadc);
-       PR_EEP("Chain0 bswAtten", pModal->bswAtten[0]);
-       PR_EEP("Chain1 bswAtten", pModal->bswAtten[1]);
-       PR_EEP("Chain2 bswAtten", pModal->bswAtten[2]);
-       PR_EEP("Chain0 bswMargin", pModal->bswMargin[0]);
-       PR_EEP("Chain1 bswMargin", pModal->bswMargin[1]);
-       PR_EEP("Chain2 bswMargin", pModal->bswMargin[2]);
-       PR_EEP("HT40 Switch Settle", pModal->swSettleHt40);
-       PR_EEP("Chain0 xatten2Db", pModal->xatten2Db[0]);
-       PR_EEP("Chain1 xatten2Db", pModal->xatten2Db[1]);
-       PR_EEP("Chain2 xatten2Db", pModal->xatten2Db[2]);
-       PR_EEP("Chain0 xatten2Margin", pModal->xatten2Margin[0]);
-       PR_EEP("Chain1 xatten2Margin", pModal->xatten2Margin[1]);
-       PR_EEP("Chain2 xatten2Margin", pModal->xatten2Margin[2]);
-       PR_EEP("Chain1 OutputBias", pModal->ob_ch1);
-       PR_EEP("Chain1 DriverBias", pModal->db_ch1);
-       PR_EEP("LNA Control", pModal->lna_ctl);
-       PR_EEP("XPA Bias Freq0", pModal->xpaBiasLvlFreq[0]);
-       PR_EEP("XPA Bias Freq1", pModal->xpaBiasLvlFreq[1]);
-       PR_EEP("XPA Bias Freq2", pModal->xpaBiasLvlFreq[2]);
-
-       if (len > size)
-               len = size;
-
-       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-       kfree(buf);
-
-       return retval;
-
-#undef PR_EEP
-}
-
-static ssize_t read_9287_modal_eeprom(struct file *file,
-                                     char __user *user_buf,
-                                     size_t count, loff_t *ppos)
-{
-#define PR_EEP(_s, _val)                                               \
-       do {                                                            \
-               len += scnprintf(buf + len, size - len, "%20s : %10d\n",\
-                                _s, (_val));                           \
-       } while (0)
-
-       struct ath9k_htc_priv *priv = file->private_data;
-       struct modal_eep_ar9287_header *pModal = &priv->ah->eeprom.map9287.modalHeader;
-       unsigned int len = 0, size = 3000;
-       ssize_t retval = 0;
-       char *buf;
-
-       buf = kzalloc(size, GFP_KERNEL);
-       if (buf == NULL)
-               return -ENOMEM;
-
-       PR_EEP("Chain0 Ant. Control", pModal->antCtrlChain[0]);
-       PR_EEP("Chain1 Ant. Control", pModal->antCtrlChain[1]);
-       PR_EEP("Ant. Common Control", pModal->antCtrlCommon);
-       PR_EEP("Chain0 Ant. Gain", pModal->antennaGainCh[0]);
-       PR_EEP("Chain1 Ant. Gain", pModal->antennaGainCh[1]);
-       PR_EEP("Switch Settle", pModal->switchSettling);
-       PR_EEP("Chain0 TxRxAtten", pModal->txRxAttenCh[0]);
-       PR_EEP("Chain1 TxRxAtten", pModal->txRxAttenCh[1]);
-       PR_EEP("Chain0 RxTxMargin", pModal->rxTxMarginCh[0]);
-       PR_EEP("Chain1 RxTxMargin", pModal->rxTxMarginCh[1]);
-       PR_EEP("ADC Desired size", pModal->adcDesiredSize);
-       PR_EEP("txEndToXpaOff", pModal->txEndToXpaOff);
-       PR_EEP("txEndToRxOn", pModal->txEndToRxOn);
-       PR_EEP("txFrameToXpaOn", pModal->txFrameToXpaOn);
-       PR_EEP("CCA Threshold)", pModal->thresh62);
-       PR_EEP("Chain0 NF Threshold", pModal->noiseFloorThreshCh[0]);
-       PR_EEP("Chain1 NF Threshold", pModal->noiseFloorThreshCh[1]);
-       PR_EEP("xpdGain", pModal->xpdGain);
-       PR_EEP("External PD", pModal->xpd);
-       PR_EEP("Chain0 I Coefficient", pModal->iqCalICh[0]);
-       PR_EEP("Chain1 I Coefficient", pModal->iqCalICh[1]);
-       PR_EEP("Chain0 Q Coefficient", pModal->iqCalQCh[0]);
-       PR_EEP("Chain1 Q Coefficient", pModal->iqCalQCh[1]);
-       PR_EEP("pdGainOverlap", pModal->pdGainOverlap);
-       PR_EEP("xPA Bias Level", pModal->xpaBiasLvl);
-       PR_EEP("txFrameToDataStart", pModal->txFrameToDataStart);
-       PR_EEP("txFrameToPaOn", pModal->txFrameToPaOn);
-       PR_EEP("HT40 Power Inc.", pModal->ht40PowerIncForPdadc);
-       PR_EEP("Chain0 bswAtten", pModal->bswAtten[0]);
-       PR_EEP("Chain1 bswAtten", pModal->bswAtten[1]);
-       PR_EEP("Chain0 bswMargin", pModal->bswMargin[0]);
-       PR_EEP("Chain1 bswMargin", pModal->bswMargin[1]);
-       PR_EEP("HT40 Switch Settle", pModal->swSettleHt40);
-       PR_EEP("AR92x7 Version", pModal->version);
-       PR_EEP("DriverBias1", pModal->db1);
-       PR_EEP("DriverBias2", pModal->db1);
-       PR_EEP("CCK OutputBias", pModal->ob_cck);
-       PR_EEP("PSK OutputBias", pModal->ob_psk);
-       PR_EEP("QAM OutputBias", pModal->ob_qam);
-       PR_EEP("PAL_OFF OutputBias", pModal->ob_pal_off);
-
-       if (len > size)
-               len = size;
-
-       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
-       kfree(buf);
-
-       return retval;
-
-#undef PR_EEP
-}
-
-static ssize_t read_file_modal_eeprom(struct file *file, char __user *user_buf,
-                                     size_t count, loff_t *ppos)
-{
-       struct ath9k_htc_priv *priv = file->private_data;
-
-       if (AR_SREV_9271(priv->ah))
-               return read_4k_modal_eeprom(file, user_buf, count, ppos);
-       else if (priv->ah->hw_version.usbdev == AR9280_USB)
-               return read_def_modal_eeprom(file, user_buf, count, ppos);
-       else if (priv->ah->hw_version.usbdev == AR9287_USB)
-               return read_9287_modal_eeprom(file, user_buf, count, ppos);
-
-       return 0;
-}
-
-static const struct file_operations fops_modal_eeprom = {
-       .read = read_file_modal_eeprom,
-       .open = simple_open,
-       .owner = THIS_MODULE,
-       .llseek = default_llseek,
-};
-
-
 /* Ethtool support for get-stats */
 #define AMKSTR(nm) #nm "_BE", #nm "_BK", #nm "_VI", #nm "_VO"
 static const char ath9k_htc_gstrings_stats[][ETH_GSTRING_LEN] = {
@@ -947,6 +453,8 @@ int ath9k_htc_get_et_sset_count(struct ieee80211_hw *hw,
 
 #define STXBASE priv->debug.tx_stats
 #define SRXBASE priv->debug.rx_stats
+#define SKBTXBASE priv->debug.tx_stats
+#define SKBRXBASE priv->debug.skbrx_stats
 #define ASTXQ(a)                                       \
        data[i++] = STXBASE.a[IEEE80211_AC_BE];         \
        data[i++] = STXBASE.a[IEEE80211_AC_BK];         \
@@ -960,24 +468,24 @@ void ath9k_htc_get_et_stats(struct ieee80211_hw *hw,
        struct ath9k_htc_priv *priv = hw->priv;
        int i = 0;
 
-       data[i++] = STXBASE.skb_success;
-       data[i++] = STXBASE.skb_success_bytes;
-       data[i++] = SRXBASE.skb_completed;
-       data[i++] = SRXBASE.skb_completed_bytes;
+       data[i++] = SKBTXBASE.skb_success;
+       data[i++] = SKBTXBASE.skb_success_bytes;
+       data[i++] = SKBRXBASE.skb_completed;
+       data[i++] = SKBRXBASE.skb_completed_bytes;
 
        ASTXQ(queue_stats);
 
-       data[i++] = SRXBASE.err_crc;
-       data[i++] = SRXBASE.err_decrypt_crc;
-       data[i++] = SRXBASE.err_phy;
-       data[i++] = SRXBASE.err_mic;
-       data[i++] = SRXBASE.err_pre_delim;
-       data[i++] = SRXBASE.err_post_delim;
-       data[i++] = SRXBASE.err_decrypt_busy;
+       data[i++] = SRXBASE.crc_err;
+       data[i++] = SRXBASE.decrypt_crc_err;
+       data[i++] = SRXBASE.phy_err;
+       data[i++] = SRXBASE.mic_err;
+       data[i++] = SRXBASE.pre_delim_crc_err;
+       data[i++] = SRXBASE.post_delim_crc_err;
+       data[i++] = SRXBASE.decrypt_busy_err;
 
-       data[i++] = SRXBASE.err_phy_stats[ATH9K_PHYERR_RADAR];
-       data[i++] = SRXBASE.err_phy_stats[ATH9K_PHYERR_OFDM_TIMING];
-       data[i++] = SRXBASE.err_phy_stats[ATH9K_PHYERR_CCK_TIMING];
+       data[i++] = SRXBASE.phy_err_stats[ATH9K_PHYERR_RADAR];
+       data[i++] = SRXBASE.phy_err_stats[ATH9K_PHYERR_OFDM_TIMING];
+       data[i++] = SRXBASE.phy_err_stats[ATH9K_PHYERR_CCK_TIMING];
 
        WARN_ON(i != ATH9K_HTC_SSTATS_LEN);
 }
@@ -1001,18 +509,21 @@ int ath9k_htc_init_debug(struct ath_hw *ah)
                            priv, &fops_tgt_rx_stats);
        debugfs_create_file("xmit", S_IRUSR, priv->debug.debugfs_phy,
                            priv, &fops_xmit);
-       debugfs_create_file("recv", S_IRUSR, priv->debug.debugfs_phy,
-                           priv, &fops_recv);
+       debugfs_create_file("skb_rx", S_IRUSR, priv->debug.debugfs_phy,
+                           priv, &fops_skb_rx);
+
+       ath9k_cmn_debug_recv(priv->debug.debugfs_phy, &priv->debug.rx_stats);
+       ath9k_cmn_debug_phy_err(priv->debug.debugfs_phy, &priv->debug.rx_stats);
+
        debugfs_create_file("slot", S_IRUSR, priv->debug.debugfs_phy,
                            priv, &fops_slot);
        debugfs_create_file("queue", S_IRUSR, priv->debug.debugfs_phy,
                            priv, &fops_queue);
        debugfs_create_file("debug", S_IRUSR | S_IWUSR, priv->debug.debugfs_phy,
                            priv, &fops_debug);
-       debugfs_create_file("base_eeprom", S_IRUSR, priv->debug.debugfs_phy,
-                           priv, &fops_base_eeprom);
-       debugfs_create_file("modal_eeprom", S_IRUSR, priv->debug.debugfs_phy,
-                           priv, &fops_modal_eeprom);
+
+       ath9k_cmn_debug_base_eeprom(priv->debug.debugfs_phy, priv->ah);
+       ath9k_cmn_debug_modal_eeprom(priv->debug.debugfs_phy, priv->ah);
 
        return 0;
 }
index 289f3d8924b5735cb773d2b1d527b5cf106b5d64..bb86eb2ffc953545d759647d70cacc86a4227022 100644 (file)
@@ -996,8 +996,6 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv,
                goto rx_next;
        }
 
-       ath9k_htc_err_stat_rx(priv, rxstatus);
-
        /* Get the RX status information */
 
        memset(rx_status, 0, sizeof(struct ieee80211_rx_status));
@@ -1005,6 +1003,7 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv,
        /* Copy everything from ath_htc_rx_status (HTC_RX_FRAME_HEADER).
         * After this, we can drop this part of skb. */
        rx_status_htc_to_ath(&rx_stats, rxstatus);
+       ath9k_htc_err_stat_rx(priv, &rx_stats);
        rx_status->mactime = be64_to_cpu(rxstatus->rs_tstamp);
        skb_pull(skb, HTC_RX_FRAME_HEADER_SIZE);
 
index c8a9dfab1fee2ea4778046b186428ba3f014e054..2a8ed8375ec0584771f4f7dcc833d17131ec34b2 100644 (file)
@@ -26,7 +26,6 @@
 #include "ar9003_mac.h"
 #include "ar9003_mci.h"
 #include "ar9003_phy.h"
-#include "debug.h"
 #include "ath9k.h"
 
 static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type);
@@ -246,6 +245,8 @@ static void ath9k_hw_read_revisions(struct ath_hw *ah)
                return;
        case AR9300_DEVID_AR953X:
                ah->hw_version.macVersion = AR_SREV_VERSION_9531;
+               if (ah->get_mac_revision)
+                       ah->hw_version.macRev = ah->get_mac_revision();
                return;
        }
 
index 36ae6490e5543af837f046f169115e3dd5d65d6b..0246b990fe87ade49ffa26ca417a8ff999179ebc 100644 (file)
@@ -61,6 +61,10 @@ static int ath9k_ps_enable;
 module_param_named(ps_enable, ath9k_ps_enable, int, 0444);
 MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave");
 
+static int ath9k_use_chanctx;
+module_param_named(use_chanctx, ath9k_use_chanctx, int, 0444);
+MODULE_PARM_DESC(use_chanctx, "Enable channel context for concurrency");
+
 bool is_ath9k_unloaded;
 
 #ifdef CONFIG_MAC80211_LEDS
@@ -508,7 +512,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        sc->tx99_power = MAX_RATE_POWER + 1;
        init_waitqueue_head(&sc->tx_wait);
 
-       if (!pdata) {
+       if (!pdata || pdata->use_eeprom) {
                ah->ah_flags |= AH_USE_EEPROM;
                sc->sc_ah->led_pin = -1;
        } else {
@@ -589,6 +593,9 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        if (ret)
                goto err_btcoex;
 
+       sc->p2p_ps_timer = ath_gen_timer_alloc(sc->sc_ah, ath9k_p2p_ps_timer,
+               NULL, sc, AR_FIRST_NDP_TIMER);
+
        ath9k_cmn_init_crypto(sc->sc_ah);
        ath9k_init_misc(sc);
        ath_fill_led_pin(sc);
@@ -643,17 +650,20 @@ static void ath9k_init_txpower_limits(struct ath_softc *sc)
 }
 
 static const struct ieee80211_iface_limit if_limits[] = {
-       { .max = 2048,  .types = BIT(NL80211_IFTYPE_STATION) |
-                                BIT(NL80211_IFTYPE_P2P_CLIENT) |
-                                BIT(NL80211_IFTYPE_WDS) },
+       { .max = 2048,  .types = BIT(NL80211_IFTYPE_STATION) },
        { .max = 8,     .types =
 #ifdef CONFIG_MAC80211_MESH
                                 BIT(NL80211_IFTYPE_MESH_POINT) |
 #endif
-                                BIT(NL80211_IFTYPE_AP) |
+                                BIT(NL80211_IFTYPE_AP) },
+       { .max = 1,     .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
                                 BIT(NL80211_IFTYPE_P2P_GO) },
 };
 
+static const struct ieee80211_iface_limit wds_limits[] = {
+       { .max = 2048,  .types = BIT(NL80211_IFTYPE_WDS) },
+};
+
 static const struct ieee80211_iface_limit if_dfs_limits[] = {
        { .max = 1,     .types = BIT(NL80211_IFTYPE_AP) |
 #ifdef CONFIG_MAC80211_MESH
@@ -670,6 +680,13 @@ static const struct ieee80211_iface_combination if_comb[] = {
                .num_different_channels = 1,
                .beacon_int_infra_match = true,
        },
+       {
+               .limits = wds_limits,
+               .n_limits = ARRAY_SIZE(wds_limits),
+               .max_interfaces = 2048,
+               .num_different_channels = 1,
+               .beacon_int_infra_match = true,
+       },
 #ifdef CONFIG_ATH9K_DFS_CERTIFIED
        {
                .limits = if_dfs_limits,
@@ -711,19 +728,23 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
        if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || ath9k_modparam_nohwcrypt)
                hw->flags |= IEEE80211_HW_MFP_CAPABLE;
 
-       hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
+       hw->wiphy->features |= (NL80211_FEATURE_ACTIVE_MONITOR |
+                               NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE);
 
        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);
+               if (!ath9k_use_chanctx) {
+                       hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
+                       hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_WDS);
+               } else
+                       hw->wiphy->n_iface_combinations = 1;
        }
 
        hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
@@ -855,6 +876,9 @@ static void ath9k_deinit_softc(struct ath_softc *sc)
 {
        int i = 0;
 
+       if (sc->p2p_ps_timer)
+               ath_gen_timer_free(sc->sc_ah, sc->p2p_ps_timer);
+
        ath9k_deinit_btcoex(sc);
 
        for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
index 51ce36f108f9a8f0f6619e6b73e613722c2a72a8..275205ab5f15ea15a00f341942efbe6f3bdc6adc 100644 (file)
@@ -958,3 +958,25 @@ void ath9k_hw_set_interrupts(struct ath_hw *ah)
        return;
 }
 EXPORT_SYMBOL(ath9k_hw_set_interrupts);
+
+#define ATH9K_HW_MAX_DCU       10
+#define ATH9K_HW_SLICE_PER_DCU 16
+#define ATH9K_HW_BIT_IN_SLICE  16
+void ath9k_hw_set_tx_filter(struct ath_hw *ah, u8 destidx, bool set)
+{
+       int dcu_idx;
+       u32 filter;
+
+       for (dcu_idx = 0; dcu_idx < 10; dcu_idx++) {
+               filter = SM(set, AR_D_TXBLK_WRITE_COMMAND);
+               filter |= SM(dcu_idx, AR_D_TXBLK_WRITE_DCU);
+               filter |= SM((destidx / ATH9K_HW_SLICE_PER_DCU),
+                            AR_D_TXBLK_WRITE_SLICE);
+               filter |= BIT(destidx % ATH9K_HW_BIT_IN_SLICE);
+               ath_dbg(ath9k_hw_common(ah), PS,
+                       "DCU%d staid %d set %d txfilter %08x\n",
+                       dcu_idx, destidx, set, filter);
+               REG_WRITE(ah, AR_D_TXBLK_BASE, filter);
+       }
+}
+EXPORT_SYMBOL(ath9k_hw_set_tx_filter);
index 89df634e81f9a703d6dcf5eefee8881ef738570b..da768675753595c3c27ef3fd920327af3c1a547e 100644 (file)
@@ -729,6 +729,7 @@ void ath9k_hw_startpcureceive(struct ath_hw *ah, bool is_scanning);
 void ath9k_hw_abortpcurecv(struct ath_hw *ah);
 bool ath9k_hw_stopdmarecv(struct ath_hw *ah, bool *reset);
 int ath9k_hw_beaconq_setup(struct ath_hw *ah);
+void ath9k_hw_set_tx_filter(struct ath_hw *ah, u8 destidx, bool set);
 
 /* Interrupt Handling */
 bool ath9k_hw_intrpend(struct ath_hw *ah);
index d69853b848ce1f10167275c4e85d6026ee41c5f1..62ac95d6bb9d6e60b3bbc47e4f7715eecbe56bb9 100644 (file)
@@ -261,6 +261,8 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
        sc->gtt_cnt = 0;
        ieee80211_wake_queues(sc->hw);
 
+       ath9k_p2p_ps_timer(sc);
+
        return true;
 }
 
@@ -419,6 +421,7 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta,
        an->sc = sc;
        an->sta = sta;
        an->vif = vif;
+       memset(&an->key_idx, 0, sizeof(an->key_idx));
 
        ath_tx_node_init(sc, an);
 }
@@ -1119,6 +1122,8 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
        if (ath9k_uses_beacons(vif->type))
                ath9k_beacon_assign_slot(sc, vif);
 
+       avp->vif = vif;
+
        an->sc = sc;
        an->sta = NULL;
        an->vif = vif;
@@ -1163,6 +1168,29 @@ static int ath9k_change_interface(struct ieee80211_hw *hw,
        return 0;
 }
 
+static void
+ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp)
+{
+       struct ath_hw *ah = sc->sc_ah;
+       s32 tsf, target_tsf;
+
+       if (!avp || !avp->noa.has_next_tsf)
+               return;
+
+       ath9k_hw_gen_timer_stop(ah, sc->p2p_ps_timer);
+
+       tsf = ath9k_hw_gettsf32(sc->sc_ah);
+
+       target_tsf = avp->noa.next_tsf;
+       if (!avp->noa.absent)
+               target_tsf -= ATH_P2P_PS_STOP_TIME;
+
+       if (target_tsf - tsf < ATH_P2P_PS_STOP_TIME)
+               target_tsf = tsf + ATH_P2P_PS_STOP_TIME;
+
+       ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, (u32) target_tsf, 1000000);
+}
+
 static void ath9k_remove_interface(struct ieee80211_hw *hw,
                                   struct ieee80211_vif *vif)
 {
@@ -1174,6 +1202,13 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
 
        mutex_lock(&sc->mutex);
 
+       spin_lock_bh(&sc->sc_pcu_lock);
+       if (avp == sc->p2p_ps_vif) {
+               sc->p2p_ps_vif = NULL;
+               ath9k_update_p2p_ps_timer(sc, NULL);
+       }
+       spin_unlock_bh(&sc->sc_pcu_lock);
+
        sc->nvifs--;
        sc->tx99_vif = NULL;
 
@@ -1427,8 +1462,10 @@ static int ath9k_sta_add(struct ieee80211_hw *hw,
                return 0;
 
        key = ath_key_config(common, vif, sta, &ps_key);
-       if (key > 0)
+       if (key > 0) {
                an->ps_key = key;
+               an->key_idx[0] = key;
+       }
 
        return 0;
 }
@@ -1446,6 +1483,7 @@ static void ath9k_del_ps_key(struct ath_softc *sc,
 
        ath_key_delete(common, &ps_key);
        an->ps_key = 0;
+       an->key_idx[0] = 0;
 }
 
 static int ath9k_sta_remove(struct ieee80211_hw *hw,
@@ -1460,6 +1498,19 @@ static int ath9k_sta_remove(struct ieee80211_hw *hw,
        return 0;
 }
 
+static void ath9k_sta_set_tx_filter(struct ath_hw *ah,
+                                   struct ath_node *an,
+                                   bool set)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) {
+               if (!an->key_idx[i])
+                       continue;
+               ath9k_hw_set_tx_filter(ah, an->key_idx[i], set);
+       }
+}
+
 static void ath9k_sta_notify(struct ieee80211_hw *hw,
                         struct ieee80211_vif *vif,
                         enum sta_notify_cmd cmd,
@@ -1472,8 +1523,10 @@ static void ath9k_sta_notify(struct ieee80211_hw *hw,
        case STA_NOTIFY_SLEEP:
                an->sleeping = true;
                ath_tx_aggr_sleep(sta, sc, an);
+               ath9k_sta_set_tx_filter(sc->sc_ah, an, true);
                break;
        case STA_NOTIFY_AWAKE:
+               ath9k_sta_set_tx_filter(sc->sc_ah, an, false);
                an->sleeping = false;
                ath_tx_aggr_wakeup(sc, an);
                break;
@@ -1529,7 +1582,8 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
 {
        struct ath_softc *sc = hw->priv;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       int ret = 0;
+       struct ath_node *an = NULL;
+       int ret = 0, i;
 
        if (ath9k_modparam_nohwcrypt)
                return -ENOSPC;
@@ -1551,13 +1605,16 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
 
        mutex_lock(&sc->mutex);
        ath9k_ps_wakeup(sc);
-       ath_dbg(common, CONFIG, "Set HW Key\n");
+       ath_dbg(common, CONFIG, "Set HW Key %d\n", cmd);
+       if (sta)
+               an = (struct ath_node *)sta->drv_priv;
 
        switch (cmd) {
        case SET_KEY:
                if (sta)
                        ath9k_del_ps_key(sc, vif, sta);
 
+               key->hw_key_idx = 0;
                ret = ath_key_config(common, vif, sta, key);
                if (ret >= 0) {
                        key->hw_key_idx = ret;
@@ -1570,9 +1627,27 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
                                key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
                        ret = 0;
                }
+               if (an && key->hw_key_idx) {
+                       for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) {
+                               if (an->key_idx[i])
+                                       continue;
+                               an->key_idx[i] = key->hw_key_idx;
+                               break;
+                       }
+                       WARN_ON(i == ARRAY_SIZE(an->key_idx));
+               }
                break;
        case DISABLE_KEY:
                ath_key_delete(common, key);
+               if (an) {
+                       for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) {
+                               if (an->key_idx[i] != key->hw_key_idx)
+                                       continue;
+                               an->key_idx[i] = 0;
+                               break;
+                       }
+               }
+               key->hw_key_idx = 0;
                break;
        default:
                ret = -EINVAL;
@@ -1636,6 +1711,66 @@ static void ath9k_bss_assoc_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
                ath9k_set_assoc_state(sc, vif);
 }
 
+void ath9k_p2p_ps_timer(void *priv)
+{
+       struct ath_softc *sc = priv;
+       struct ath_vif *avp = sc->p2p_ps_vif;
+       struct ieee80211_vif *vif;
+       struct ieee80211_sta *sta;
+       struct ath_node *an;
+       u32 tsf;
+
+       if (!avp)
+               return;
+
+       tsf = ath9k_hw_gettsf32(sc->sc_ah);
+       if (!avp->noa.absent)
+               tsf += ATH_P2P_PS_STOP_TIME;
+
+       if (!avp->noa.has_next_tsf ||
+           avp->noa.next_tsf - tsf > BIT(31))
+               ieee80211_update_p2p_noa(&avp->noa, tsf);
+
+       ath9k_update_p2p_ps_timer(sc, avp);
+
+       rcu_read_lock();
+
+       vif = avp->vif;
+       sta = ieee80211_find_sta(vif, vif->bss_conf.bssid);
+       if (!sta)
+               goto out;
+
+       an = (void *) sta->drv_priv;
+       if (an->sleeping == !!avp->noa.absent)
+               goto out;
+
+       an->sleeping = avp->noa.absent;
+       if (an->sleeping)
+               ath_tx_aggr_sleep(sta, sc, an);
+       else
+               ath_tx_aggr_wakeup(sc, an);
+
+out:
+       rcu_read_unlock();
+}
+
+void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif)
+{
+       struct ath_vif *avp = (void *)vif->drv_priv;
+       u32 tsf;
+
+       if (!sc->p2p_ps_timer)
+               return;
+
+       if (vif->type != NL80211_IFTYPE_STATION || !vif->p2p)
+               return;
+
+       sc->p2p_ps_vif = avp;
+       tsf = ath9k_hw_gettsf32(sc->sc_ah);
+       ieee80211_parse_p2p_noa(&vif->bss_conf.p2p_noa_attr, &avp->noa, tsf);
+       ath9k_update_p2p_ps_timer(sc, avp);
+}
+
 static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
                                   struct ieee80211_vif *vif,
                                   struct ieee80211_bss_conf *bss_conf,
@@ -1650,6 +1785,7 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        struct ath_vif *avp = (void *)vif->drv_priv;
+       unsigned long flags;
        int slottime;
 
        ath9k_ps_wakeup(sc);
@@ -1710,6 +1846,15 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
                }
        }
 
+       if (changed & BSS_CHANGED_P2P_PS) {
+               spin_lock_bh(&sc->sc_pcu_lock);
+               spin_lock_irqsave(&sc->sc_pm_lock, flags);
+               if (!(sc->ps_flags & PS_BEACON_SYNC))
+                       ath9k_update_p2p_ps(sc, vif);
+               spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
+               spin_unlock_bh(&sc->sc_pcu_lock);
+       }
+
        if (changed & CHECK_ANI)
                ath_check_ani(sc);
 
@@ -1883,7 +2028,8 @@ static bool ath9k_has_tx_pending(struct ath_softc *sc)
        return !!npend;
 }
 
-static void ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                       u32 queues, bool drop)
 {
        struct ath_softc *sc = hw->priv;
        struct ath_hw *ah = sc->sc_ah;
@@ -2084,14 +2230,6 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
        clear_bit(ATH_OP_SCANNING, &common->op_flags);
 }
 
-static void ath9k_channel_switch_beacon(struct ieee80211_hw *hw,
-                                       struct ieee80211_vif *vif,
-                                       struct cfg80211_chan_def *chandef)
-{
-       /* depend on vif->csa_active only */
-       return;
-}
-
 struct ieee80211_ops ath9k_ops = {
        .tx                 = ath9k_tx,
        .start              = ath9k_start,
@@ -2139,5 +2277,4 @@ struct ieee80211_ops ath9k_ops = {
 #endif
        .sw_scan_start      = ath9k_sw_scan_start,
        .sw_scan_complete   = ath9k_sw_scan_complete,
-       .channel_switch_beacon     = ath9k_channel_switch_beacon,
 };
index 914dbc6b17208df991e92d3c3539508ea31f57ad..4dec09e565ed865470ff6bc5738e86c21d523906 100644 (file)
@@ -686,7 +686,7 @@ static bool ath_pci_eeprom_read(struct ath_common *common, u32 off, u16 *data)
        struct ath_softc *sc = (struct ath_softc *) common->priv;
        struct ath9k_platform_data *pdata = sc->dev->platform_data;
 
-       if (pdata) {
+       if (pdata && !pdata->use_eeprom) {
                if (off >= (ARRAY_SIZE(pdata->eeprom_data))) {
                        ath_err(common,
                                "%s: eeprom read failed, offset %08x is out of range\n",
@@ -914,6 +914,7 @@ static int ath_pci_suspend(struct device *device)
         */
        ath9k_stop_btcoex(sc);
        ath9k_hw_disable(sc->sc_ah);
+       del_timer_sync(&sc->sleep_timer);
        ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP);
 
        return 0;
index 19df969ec9093235eff8075d558e076edca2785a..9105a92364f78241fa565b661a017ce59f7400f5 100644 (file)
@@ -34,7 +34,8 @@ 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_rxbuf *bf)
+static void ath_rx_buf_link(struct ath_softc *sc, struct ath_rxbuf *bf,
+                           bool flush)
 {
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
@@ -59,18 +60,19 @@ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_rxbuf *bf)
                             common->rx_bufsize,
                             0);
 
-       if (sc->rx.rxlink == NULL)
-               ath9k_hw_putrxbuf(ah, bf->bf_daddr);
-       else
+       if (sc->rx.rxlink)
                *sc->rx.rxlink = bf->bf_daddr;
+       else if (!flush)
+               ath9k_hw_putrxbuf(ah, bf->bf_daddr);
 
        sc->rx.rxlink = &ds->ds_link;
 }
 
-static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_rxbuf *bf)
+static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_rxbuf *bf,
+                             bool flush)
 {
        if (sc->rx.buf_hold)
-               ath_rx_buf_link(sc, sc->rx.buf_hold);
+               ath_rx_buf_link(sc, sc->rx.buf_hold, flush);
 
        sc->rx.buf_hold = bf;
 }
@@ -442,7 +444,7 @@ int ath_startrecv(struct ath_softc *sc)
        sc->rx.buf_hold = NULL;
        sc->rx.rxlink = NULL;
        list_for_each_entry_safe(bf, tbf, &sc->rx.rxbuf, list) {
-               ath_rx_buf_link(sc, bf);
+               ath_rx_buf_link(sc, bf, false);
        }
 
        /* We could have deleted elements so the list may be empty now */
@@ -538,7 +540,10 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb)
                sc->ps_flags &= ~PS_BEACON_SYNC;
                ath_dbg(common, PS,
                        "Reconfigure beacon timers based on synchronized timestamp\n");
-               ath9k_set_beacon(sc);
+               if (!(WARN_ON_ONCE(sc->cur_beacon_conf.beacon_interval == 0)))
+                       ath9k_set_beacon(sc);
+               if (sc->p2p_ps_vif)
+                       ath9k_update_p2p_ps(sc, sc->p2p_ps_vif->vif);
        }
 
        if (ath_beacon_dtim_pending_cab(skb)) {
@@ -1115,12 +1120,12 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
 requeue:
                list_add_tail(&bf->list, &sc->rx.rxbuf);
 
-               if (edma) {
-                       ath_rx_edma_buf_link(sc, qtype);
-               } else {
-                       ath_rx_buf_relink(sc, bf);
+               if (!edma) {
+                       ath_rx_buf_relink(sc, bf, flush);
                        if (!flush)
                                ath9k_hw_rxena(ah);
+               } else if (!flush) {
+                       ath_rx_edma_buf_link(sc, qtype);
                }
 
                if (!budget--)
index b1fd3fa84983df0e0b85d86b86b02c158883fd35..f1bbce3f7774ee16a9e29b8bb62f72ade1a9d99f 100644 (file)
 #define AR_D_QCUMASK         0x000003FF
 #define AR_D_QCUMASK_RESV0   0xFFFFFC00
 
-#define AR_D_TXBLK_CMD  0x1038
-#define AR_D_TXBLK_DATA(i) (AR_D_TXBLK_CMD+(i))
-
 #define AR_D0_LCL_IFS     0x1040
 #define AR_D1_LCL_IFS     0x1044
 #define AR_D2_LCL_IFS     0x1048
index 4c8cdb097b6599c08816a4e184537d764bc9ef43..f8ded84b7be8c5e3b6038910a8829364a5e5ba1e 100644 (file)
@@ -1707,7 +1707,9 @@ static int carl9170_op_get_survey(struct ieee80211_hw *hw, int idx,
        return 0;
 }
 
-static void carl9170_op_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+static void carl9170_op_flush(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif,
+                             u32 queues, bool drop)
 {
        struct ar9170 *ar = hw->priv;
        unsigned int vid;
index ca115f33746f228bc4f9007e0397b567f644e2a8..f35c7f30f9a6f66f234a4816260d0ea473fc573e 100644 (file)
@@ -1076,8 +1076,14 @@ static int carl9170_usb_probe(struct usb_interface *intf,
 
        carl9170_set_state(ar, CARL9170_STOPPED);
 
-       return request_firmware_nowait(THIS_MODULE, 1, CARL9170FW_NAME,
+       err = request_firmware_nowait(THIS_MODULE, 1, CARL9170FW_NAME,
                &ar->udev->dev, GFP_KERNEL, ar, carl9170_usb_firmware_step2);
+       if (err) {
+               usb_put_dev(udev);
+               usb_put_dev(udev);
+               carl9170_free(ar);
+       }
+       return err;
 }
 
 static void carl9170_usb_disconnect(struct usb_interface *intf)
index a1a69c5db409262e3f63a1f1246d6590e448cbfa..650be79c7ac97070f0fccd62cea916ce56d8c097 100644 (file)
@@ -73,9 +73,52 @@ static const struct radar_types etsi_radar_types_v15 = {
        .radar_types            = etsi_radar_ref_types_v15,
 };
 
-/* for now, we support ETSI radar types, FCC and JP are TODO */
+#define FCC_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB)      \
+{                                                              \
+       ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX),               \
+       PMIN - PRI_TOLERANCE,                                   \
+       PMAX * PRF + PRI_TOLERANCE, PRF, PPB * PRF,             \
+       PPB_THRESH(PPB), PRI_TOLERANCE,                         \
+}
+
+static const struct radar_detector_specs fcc_radar_ref_types[] = {
+       FCC_PATTERN(0, 0, 1, 1428, 1428, 1, 18),
+       FCC_PATTERN(1, 0, 5, 150, 230, 1, 23),
+       FCC_PATTERN(2, 6, 10, 200, 500, 1, 16),
+       FCC_PATTERN(3, 11, 20, 200, 500, 1, 12),
+       FCC_PATTERN(4, 50, 100, 1000, 2000, 20, 1),
+       FCC_PATTERN(5, 0, 1, 333, 333, 1, 9),
+};
+
+static const struct radar_types fcc_radar_types = {
+       .region                 = NL80211_DFS_FCC,
+       .num_radar_types        = ARRAY_SIZE(fcc_radar_ref_types),
+       .radar_types            = fcc_radar_ref_types,
+};
+
+#define JP_PATTERN FCC_PATTERN
+static const struct radar_detector_specs jp_radar_ref_types[] = {
+       JP_PATTERN(0, 0, 1, 1428, 1428, 1, 18),
+       JP_PATTERN(1, 2, 3, 3846, 3846, 1, 18),
+       JP_PATTERN(2, 0, 1, 1388, 1388, 1, 18),
+       JP_PATTERN(3, 1, 2, 4000, 4000, 1, 18),
+       JP_PATTERN(4, 0, 5, 150, 230, 1, 23),
+       JP_PATTERN(5, 6, 10, 200, 500, 1, 16),
+       JP_PATTERN(6, 11, 20, 200, 500, 1, 12),
+       JP_PATTERN(7, 50, 100, 1000, 2000, 20, 1),
+       JP_PATTERN(5, 0, 1, 333, 333, 1, 9),
+};
+
+static const struct radar_types jp_radar_types = {
+       .region                 = NL80211_DFS_JP,
+       .num_radar_types        = ARRAY_SIZE(jp_radar_ref_types),
+       .radar_types            = jp_radar_ref_types,
+};
+
 static const struct radar_types *dfs_domains[] = {
        &etsi_radar_types_v15,
+       &fcc_radar_types,
+       &jp_radar_types,
 };
 
 /**
index 7bf0ef8a1f56f176e14cf0a7905410a0593362a3..63986931829eb6cd76ecf04e467cab30523a460f 100644 (file)
@@ -2068,7 +2068,7 @@ static void wcn36xx_smd_rsp_process(struct wcn36xx *wcn, void *buf, size_t len)
                if (!msg_ind)
                        goto nomem;
                msg_ind->msg_len = len;
-               msg_ind->msg = kmalloc(len, GFP_KERNEL);
+               msg_ind->msg = kmemdup(buf, len, GFP_KERNEL);
                if (!msg_ind->msg) {
                        kfree(msg_ind);
 nomem:
@@ -2080,7 +2080,6 @@ static void wcn36xx_smd_rsp_process(struct wcn36xx *wcn, void *buf, size_t len)
                                    msg_header->msg_type);
                        break;
                }
-               memcpy(msg_ind->msg, buf, len);
                mutex_lock(&wcn->hal_ind_mutex);
                list_add_tail(&msg_ind->list, &wcn->hal_ind_queue);
                queue_work(wcn->hal_ind_wq, &wcn->hal_ind_work);
index 4806a49cb61b139e0250e8152e27c91b79b50a0e..820d4ebd93222b6e3a7b28dd68d1d590e2b97a7b 100644 (file)
@@ -172,7 +172,7 @@ static int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
 
 static int wil_cfg80211_get_station(struct wiphy *wiphy,
                                    struct net_device *ndev,
-                                   u8 *mac, struct station_info *sinfo)
+                                   const u8 *mac, struct station_info *sinfo)
 {
        struct wil6210_priv *wil = wiphy_to_wil(wiphy);
        int rc;
@@ -288,6 +288,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
        }
 
        wil->scan_request = request;
+       mod_timer(&wil->scan_timer, jiffies + WIL6210_SCAN_TO);
 
        memset(&cmd, 0, sizeof(cmd));
        cmd.cmd.num_channels = 0;
@@ -671,7 +672,7 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
 }
 
 static int wil_cfg80211_del_station(struct wiphy *wiphy,
-                                   struct net_device *dev, u8 *mac)
+                                   struct net_device *dev, const u8 *mac)
 {
        struct wil6210_priv *wil = wiphy_to_wil(wiphy);
 
index ecdabe4adec3bb59b90cce9f00571edf2ca5bad6..8d4bc4bfb6643a64609b2e06c3b303fa72fc27fa 100644 (file)
@@ -35,7 +35,7 @@ static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil,
        void __iomem *x = wmi_addr(wil, vring->hwtail);
 
        seq_printf(s, "VRING %s = {\n", name);
-       seq_printf(s, "  pa     = 0x%016llx\n", (unsigned long long)vring->pa);
+       seq_printf(s, "  pa     = %pad\n", &vring->pa);
        seq_printf(s, "  va     = 0x%p\n", vring->va);
        seq_printf(s, "  size   = %d\n", vring->size);
        seq_printf(s, "  swtail = %d\n", vring->swtail);
@@ -473,7 +473,7 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
                           u[0], u[1], u[2], u[3]);
                seq_printf(s, "  DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n",
                           u[4], u[5], u[6], u[7]);
-               seq_printf(s, "  SKB = %p\n", skb);
+               seq_printf(s, "  SKB = 0x%p\n", skb);
 
                if (skb) {
                        skb_get(skb);
index 5824cd41e4bac6d387087ab84739df3225b8f799..73593aa3cd9813e2bd6849b969427f83f1a9a578 100644 (file)
@@ -338,7 +338,7 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)
        }
 
        if (isr)
-               wil_err(wil, "un-handled MISC ISR bits 0x%08x\n", isr);
+               wil_dbg_irq(wil, "un-handled MISC ISR bits 0x%08x\n", isr);
 
        wil->isr_misc = 0;
 
index 95f4efe9ef37c652722a5ca381af4529a6ea65e9..11e6d9d22eae47faa34a51c813238f2acd1a1eea 100644 (file)
@@ -81,7 +81,7 @@ static void wil_disconnect_cid(struct wil6210_priv *wil, int cid)
        memset(&sta->stats, 0, sizeof(sta->stats));
 }
 
-static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
+static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid)
 {
        int cid = -ENOENT;
        struct net_device *ndev = wil_to_ndev(wil);
@@ -150,6 +150,15 @@ static void wil_connect_timer_fn(ulong x)
        schedule_work(&wil->disconnect_worker);
 }
 
+static void wil_scan_timer_fn(ulong x)
+{
+       struct wil6210_priv *wil = (void *)x;
+
+       clear_bit(wil_status_fwready, &wil->status);
+       wil_err(wil, "Scan timeout detected, start fw error recovery\n");
+       schedule_work(&wil->fw_error_worker);
+}
+
 static void wil_fw_error_worker(struct work_struct *work)
 {
        struct wil6210_priv *wil = container_of(work,
@@ -161,12 +170,30 @@ static void wil_fw_error_worker(struct work_struct *work)
        if (no_fw_recovery)
                return;
 
+       /* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO
+        * passed since last recovery attempt
+        */
+       if (time_is_after_jiffies(wil->last_fw_recovery +
+                                 WIL6210_FW_RECOVERY_TO))
+               wil->recovery_count++;
+       else
+               wil->recovery_count = 1; /* fw was alive for a long time */
+
+       if (wil->recovery_count > WIL6210_FW_RECOVERY_RETRIES) {
+               wil_err(wil, "too many recovery attempts (%d), giving up\n",
+                       wil->recovery_count);
+               return;
+       }
+
+       wil->last_fw_recovery = jiffies;
+
        mutex_lock(&wil->mutex);
        switch (wdev->iftype) {
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_MONITOR:
-               wil_info(wil, "fw error recovery started...\n");
+               wil_info(wil, "fw error recovery started (try %d)...\n",
+                        wil->recovery_count);
                wil_reset(wil);
 
                /* need to re-allocate Rx ring after reset */
@@ -230,6 +257,7 @@ int wil_priv_init(struct wil6210_priv *wil)
 
        wil->pending_connect_cid = -1;
        setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil);
+       setup_timer(&wil->scan_timer, wil_scan_timer_fn, (ulong)wil);
 
        INIT_WORK(&wil->connect_worker, wil_connect_worker);
        INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker);
@@ -249,10 +277,12 @@ int wil_priv_init(struct wil6210_priv *wil)
                return -EAGAIN;
        }
 
+       wil->last_fw_recovery = jiffies;
+
        return 0;
 }
 
-void wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
+void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid)
 {
        del_timer_sync(&wil->connect_timer);
        _wil6210_disconnect(wil, bssid);
@@ -260,6 +290,7 @@ void wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
 
 void wil_priv_deinit(struct wil6210_priv *wil)
 {
+       del_timer_sync(&wil->scan_timer);
        cancel_work_sync(&wil->disconnect_worker);
        cancel_work_sync(&wil->fw_error_worker);
        mutex_lock(&wil->mutex);
@@ -363,8 +394,8 @@ static int wil_wait_for_fw_ready(struct wil6210_priv *wil)
                wil_err(wil, "Firmware not ready\n");
                return -ETIME;
        } else {
-               wil_dbg_misc(wil, "FW ready after %d ms\n",
-                            jiffies_to_msecs(to-left));
+               wil_info(wil, "FW ready after %d ms. HW version 0x%08x\n",
+                        jiffies_to_msecs(to-left), wil->hw_version);
        }
        return 0;
 }
@@ -391,6 +422,7 @@ int wil_reset(struct wil6210_priv *wil)
        if (wil->scan_request) {
                wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
                             wil->scan_request);
+               del_timer_sync(&wil->scan_timer);
                cfg80211_scan_done(wil->scan_request, true);
                wil->scan_request = NULL;
        }
@@ -520,6 +552,7 @@ static int __wil_down(struct wil6210_priv *wil)
        napi_disable(&wil->napi_tx);
 
        if (wil->scan_request) {
+               del_timer_sync(&wil->scan_timer);
                cfg80211_scan_done(wil->scan_request, true);
                wil->scan_request = NULL;
        }
index fdcaeb820e75857469ab4d0a6d854cdb4d6aa373..106b6dcb773a35fe3e57eba85423f4e2135ae24c 100644 (file)
@@ -32,12 +32,26 @@ static int wil_stop(struct net_device *ndev)
        return wil_down(wil);
 }
 
+static int wil_change_mtu(struct net_device *ndev, int new_mtu)
+{
+       struct wil6210_priv *wil = ndev_to_wil(ndev);
+
+       if (new_mtu < 68 || new_mtu > IEEE80211_MAX_DATA_LEN_DMG)
+               return -EINVAL;
+
+       wil_dbg_misc(wil, "change MTU %d -> %d\n", ndev->mtu, new_mtu);
+       ndev->mtu = new_mtu;
+
+       return 0;
+}
+
 static const struct net_device_ops wil_netdev_ops = {
        .ndo_open               = wil_open,
        .ndo_stop               = wil_stop,
        .ndo_start_xmit         = wil_start_xmit,
        .ndo_set_mac_address    = eth_mac_addr,
        .ndo_validate_addr      = eth_validate_addr,
+       .ndo_change_mtu         = wil_change_mtu,
 };
 
 static int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget)
index f1e1bb338d681e71c96b130363bf04387c3ffa85..1e2e07b9d13d9d6b2957849b4792679c77a6dcf5 100644 (file)
@@ -74,8 +74,6 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
        if (rc)
                goto release_irq;
 
-       wil_info(wil, "HW version: 0x%08x\n", wil->hw_version);
-
        return 0;
 
  release_irq:
@@ -140,7 +138,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                goto err_release_reg;
        }
        /* rollback to err_iounmap */
-       dev_info(&pdev->dev, "CSR at %pR -> %p\n", &pdev->resource[0], csr);
+       dev_info(&pdev->dev, "CSR at %pR -> 0x%p\n", &pdev->resource[0], csr);
 
        wil = wil_if_alloc(dev, csr);
        if (IS_ERR(wil)) {
index d04629fe053f5e2f864bc1342ebdadf144d17008..747ae127587763d0bbdd652f674749960cadb1f8 100644 (file)
@@ -49,10 +49,17 @@ static void wil_release_reorder_frames(struct wil6210_priv *wil,
 {
        int index;
 
-       while (seq_less(r->head_seq_num, hseq)) {
+       /* note: this function is never called with
+        * hseq preceding r->head_seq_num, i.e it is always true
+        * !seq_less(hseq, r->head_seq_num)
+        * and thus on loop exit it should be
+        * r->head_seq_num == hseq
+        */
+       while (seq_less(r->head_seq_num, hseq) && r->stored_mpdu_num) {
                index = reorder_index(r, r->head_seq_num);
                wil_release_reorder_frame(wil, r, index);
        }
+       r->head_seq_num = hseq;
 }
 
 static void wil_reorder_release(struct wil6210_priv *wil,
@@ -91,6 +98,22 @@ void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb)
 
        spin_lock(&r->reorder_lock);
 
+       /** Due to the race between WMI events, where BACK establishment
+        * reported, and data Rx, few packets may be pass up before reorder
+        * buffer get allocated. Catch up by pretending SSN is what we
+        * see in the 1-st Rx packet
+        */
+       if (r->first_time) {
+               r->first_time = false;
+               if (seq != r->head_seq_num) {
+                       wil_err(wil, "Error: 1-st frame with wrong sequence"
+                               " %d, should be %d. Fixing...\n", seq,
+                               r->head_seq_num);
+                       r->head_seq_num = seq;
+                       r->ssn = seq;
+               }
+       }
+
        /* frame with out of date sequence number */
        if (seq_less(seq, r->head_seq_num)) {
                dev_kfree_skb(skb);
@@ -162,6 +185,7 @@ struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil,
        r->head_seq_num = ssn;
        r->buf_size = size;
        r->stored_mpdu_num = 0;
+       r->first_time = true;
        return r;
 }
 
index c8c547457eb4f73dd5751df4324c5b41f18971e2..0784ef3d4ce2795b68091cef9353e47d7712239b 100644 (file)
@@ -64,6 +64,22 @@ static inline int wil_vring_avail_tx(struct vring *vring)
        return vring->size - used - 1;
 }
 
+/**
+ * wil_vring_wmark_low - low watermark for available descriptor space
+ */
+static inline int wil_vring_wmark_low(struct vring *vring)
+{
+       return vring->size/8;
+}
+
+/**
+ * wil_vring_wmark_high - high watermark for available descriptor space
+ */
+static inline int wil_vring_wmark_high(struct vring *vring)
+{
+       return vring->size/4;
+}
+
 static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
 {
        struct device *dev = wil_to_dev(wil);
@@ -98,8 +114,8 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
                _d->dma.status = TX_DMA_STATUS_DU;
        }
 
-       wil_dbg_misc(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size,
-                    vring->va, (unsigned long long)vring->pa, vring->ctx);
+       wil_dbg_misc(wil, "vring[%d] 0x%p:%pad 0x%p\n", vring->size,
+                    vring->va, &vring->pa, vring->ctx);
 
        return 0;
 }
@@ -880,8 +896,8 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
        pa = dma_map_single(dev, skb->data,
                        skb_headlen(skb), DMA_TO_DEVICE);
 
-       wil_dbg_txrx(wil, "Tx skb %d bytes %p -> %#08llx\n", skb_headlen(skb),
-                    skb->data, (unsigned long long)pa);
+       wil_dbg_txrx(wil, "Tx skb %d bytes 0x%p -> %pad\n", skb_headlen(skb),
+                    skb->data, &pa);
        wil_hex_dump_txrx("Tx ", DUMP_PREFIX_OFFSET, 16, 1,
                          skb->data, skb_headlen(skb), false);
 
@@ -1007,7 +1023,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
        rc = wil_tx_vring(wil, vring, skb);
 
        /* do we still have enough room in the vring? */
-       if (wil_vring_avail_tx(vring) < vring->size/8)
+       if (wil_vring_avail_tx(vring) < wil_vring_wmark_low(vring))
                netif_tx_stop_all_queues(wil_to_ndev(wil));
 
        switch (rc) {
@@ -1116,7 +1132,7 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
                        done++;
                }
        }
-       if (wil_vring_avail_tx(vring) > vring->size/4)
+       if (wil_vring_avail_tx(vring) > wil_vring_wmark_high(vring))
                netif_tx_wake_all_queues(wil_to_ndev(wil));
 
        return done;
index 2a2dec75f02606c9ea71ecd95d846287520157d5..e25edc52398fed91bce54cb0b3fb723a9d1ff443 100644 (file)
@@ -35,11 +35,14 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1)
 #define WIL6210_MEM_SIZE (2*1024*1024UL)
 
 #define WIL6210_RX_RING_SIZE   (128)
-#define WIL6210_TX_RING_SIZE   (128)
+#define WIL6210_TX_RING_SIZE   (512)
 #define WIL6210_MAX_TX_RINGS   (24) /* HW limit */
 #define WIL6210_MAX_CID                (8) /* HW limit */
 #define WIL6210_NAPI_BUDGET    (16) /* arbitrary */
 #define WIL6210_ITR_TRSH       (10000) /* arbitrary - about 15 IRQs/msec */
+#define WIL6210_FW_RECOVERY_RETRIES    (5) /* try to recover this many times */
+#define WIL6210_FW_RECOVERY_TO msecs_to_jiffies(5000)
+#define WIL6210_SCAN_TO                msecs_to_jiffies(10000)
 
 /* Hardware definitions begin */
 
@@ -301,6 +304,7 @@ struct wil_tid_ampdu_rx {
        u16 buf_size;
        u16 timeout;
        u8 dialog_token;
+       bool first_time; /* is it 1-st time this buffer used? */
 };
 
 struct wil6210_stats {
@@ -360,6 +364,8 @@ struct wil6210_priv {
        u32 fw_version;
        u32 hw_version;
        u8 n_mids; /* number of additional MIDs as reported by FW */
+       int recovery_count; /* num of FW recovery attempts in a short time */
+       unsigned long last_fw_recovery; /* jiffies of last fw recovery */
        /* profile */
        u32 monitor_flags;
        u32 secure_pcp; /* create secure PCP? */
@@ -381,6 +387,7 @@ struct wil6210_priv {
        struct work_struct disconnect_worker;
        struct work_struct fw_error_worker;     /* for FW error recovery */
        struct timer_list connect_timer;
+       struct timer_list scan_timer; /* detect scan timeout */
        int pending_connect_cid;
        struct list_head pending_wmi_ev;
        /*
@@ -507,7 +514,7 @@ void wil_wdev_free(struct wil6210_priv *wil);
 int wmi_set_mac_address(struct wil6210_priv *wil, void *addr);
 int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan);
 int wmi_pcp_stop(struct wil6210_priv *wil);
-void wil6210_disconnect(struct wil6210_priv *wil, void *bssid);
+void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid);
 
 int wil_rx_init(struct wil6210_priv *wil);
 void wil_rx_fini(struct wil6210_priv *wil);
index 2ba56eef0c457d4397c27fa84a1291b011d3884f..6cc0e182cc703c1f0c8ad243a008f8f6129590a6 100644 (file)
@@ -192,7 +192,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
        might_sleep();
 
        if (!test_bit(wil_status_fwready, &wil->status)) {
-               wil_err(wil, "FW not ready\n");
+               wil_err(wil, "WMI: cannot send command while FW not ready\n");
                return -EAGAIN;
        }
 
@@ -276,8 +276,8 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len)
        wil->fw_version = le32_to_cpu(evt->sw_version);
        wil->n_mids = evt->numof_additional_mids;
 
-       wil_dbg_wmi(wil, "FW ver. %d; MAC %pM; %d MID's\n", wil->fw_version,
-                   evt->mac, wil->n_mids);
+       wil_info(wil, "FW ver. %d; MAC %pM; %d MID's\n", wil->fw_version,
+                evt->mac, wil->n_mids);
 
        if (!is_valid_ether_addr(ndev->dev_addr)) {
                memcpy(ndev->dev_addr, evt->mac, ETH_ALEN);
@@ -290,7 +290,7 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len)
 static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d,
                             int len)
 {
-       wil_dbg_wmi(wil, "WMI: FW ready\n");
+       wil_dbg_wmi(wil, "WMI: got FW ready event\n");
 
        set_bit(wil_status_fwready, &wil->status);
        /* reuse wmi_ready for the firmware ready indication */
@@ -348,9 +348,10 @@ static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id,
 {
        if (wil->scan_request) {
                struct wmi_scan_complete_event *data = d;
-               bool aborted = (data->status != 0);
+               bool aborted = (data->status != WMI_SCAN_SUCCESS);
 
                wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", data->status);
+               del_timer_sync(&wil->scan_timer);
                cfg80211_scan_done(wil->scan_request, aborted);
                wil->scan_request = NULL;
        } else {
@@ -658,21 +659,27 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
        u8 *cmd;
        void __iomem *src;
        ulong flags;
+       unsigned n;
 
        if (!test_bit(wil_status_reset_done, &wil->status)) {
                wil_err(wil, "Reset not completed\n");
                return;
        }
 
-       for (;;) {
+       for (n = 0;; n++) {
                u16 len;
 
                r->head = ioread32(wil->csr + HOST_MBOX +
                                   offsetof(struct wil6210_mbox_ctl, rx.head));
-               if (r->tail == r->head)
+               if (r->tail == r->head) {
+                       if (n == 0)
+                               wil_dbg_wmi(wil, "No events?\n");
                        return;
+               }
 
-               /* read cmd from tail */
+               wil_dbg_wmi(wil, "Mbox head %08x tail %08x\n",
+                           r->head, r->tail);
+               /* read cmd descriptor from tail */
                wil_memcpy_fromio_32(&d_tail, wil->csr + HOSTADDR(r->tail),
                                     sizeof(struct wil6210_mbox_ring_desc));
                if (d_tail.sync == 0) {
@@ -680,13 +687,18 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
                        return;
                }
 
+               /* read cmd header from descriptor */
                if (0 != wmi_read_hdr(wil, d_tail.addr, &hdr)) {
                        wil_err(wil, "Mbox evt at 0x%08x?\n",
                                le32_to_cpu(d_tail.addr));
                        return;
                }
-
                len = le16_to_cpu(hdr.len);
+               wil_dbg_wmi(wil, "Mbox evt %04x %04x %04x %02x\n",
+                           le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type),
+                           hdr.flags);
+
+               /* read cmd buffer from descriptor */
                src = wmi_buffer(wil, d_tail.addr) +
                      sizeof(struct wil6210_mbox_hdr);
                evt = kmalloc(ALIGN(offsetof(struct pending_wmi_event,
@@ -702,9 +714,6 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
                iowrite32(0, wil->csr + HOSTADDR(r->tail) +
                          offsetof(struct wil6210_mbox_ring_desc, sync));
                /* indicate */
-               wil_dbg_wmi(wil, "Mbox evt %04x %04x %04x %02x\n",
-                           le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type),
-                           hdr.flags);
                if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) &&
                    (len >= sizeof(struct wil6210_mbox_hdr_wmi))) {
                        struct wil6210_mbox_hdr_wmi *wmi = &evt->event.wmi;
@@ -734,6 +743,8 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
                        wil_dbg_wmi(wil, "queue_work -> %d\n", q);
                }
        }
+       if (n > 1)
+               wil_dbg_wmi(wil, "%s -> %d events processed\n", __func__, n);
 }
 
 int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,
@@ -802,6 +813,7 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan)
                .network_type = wmi_nettype,
                .disable_sec_offload = 1,
                .channel = chan - 1,
+               .pcp_max_assoc_sta = WIL6210_MAX_CID,
        };
        struct {
                struct wil6210_mbox_hdr_wmi wmi;
index 50b8528394f4050b8098098e9bbdd0520ae1bf2a..17334c852866c3cc8a061eecb829052ebd9415cf 100644 (file)
@@ -28,7 +28,7 @@
 #define __WILOCITY_WMI_H__
 
 /* General */
-
+#define WILOCITY_MAX_ASSOC_STA (8)
 #define WMI_MAC_LEN            (6)
 #define WMI_PROX_RANGE_NUM     (3)
 
@@ -219,15 +219,6 @@ struct wmi_disconnect_sta_cmd {
        __le16 disconnect_reason;
 } __packed;
 
-/*
- * WMI_RECONNECT_CMDID
- */
-struct wmi_reconnect_cmd {
-       u8 channel;                     /* hint */
-       u8 reserved;
-       u8 bssid[WMI_MAC_LEN];          /* mandatory if set */
-} __packed;
-
 
 /*
  * WMI_SET_PMK_CMDID
@@ -296,11 +287,13 @@ enum wmi_scan_type {
        WMI_LONG_SCAN           = 0,
        WMI_SHORT_SCAN          = 1,
        WMI_PBC_SCAN            = 2,
+       WMI_ACTIVE_SCAN         = 3,
+       WMI_DIRECT_SCAN         = 4,
 };
 
 struct wmi_start_scan_cmd {
-       u8 reserved[8];
-
+       u8 direct_scan_mac_addr[6];
+       u8 reserved[2];
        __le32 home_dwell_time; /* Max duration in the home channel(ms) */
        __le32 force_scan_interval;     /* Time interval between scans (ms)*/
        u8 scan_type;           /* wmi_scan_type */
@@ -332,6 +325,7 @@ struct wmi_probed_ssid_cmd {
        u8 ssid[WMI_MAX_SSID_LEN];
 } __packed;
 
+
 /*
  * WMI_SET_APPIE_CMDID
  * Add Application specified IE to a management frame
@@ -427,7 +421,7 @@ struct wmi_bcon_ctrl_cmd {
        __le16 frag_num;
        __le64 ss_mask;
        u8 network_type;
-       u8 reserved;
+       u8 pcp_max_assoc_sta;
        u8 disable_sec_offload;
        u8 disable_sec;
 } __packed;
@@ -450,7 +444,7 @@ enum wmi_port_role {
 struct wmi_port_allocate_cmd {
        u8 mac[WMI_MAC_LEN];
        u8 port_role;
-       u8 midid;
+       u8 mid;
 } __packed;
 
 /*
@@ -467,6 +461,7 @@ struct wmi_delete_port_cmd {
 enum wmi_discovery_mode {
        WMI_DISCOVERY_MODE_NON_OFFLOAD  = 0,
        WMI_DISCOVERY_MODE_OFFLOAD      = 1,
+       WMI_DISCOVERY_MODE_PEER2PEER    = 2,
 };
 
 struct wmi_p2p_cfg_cmd {
@@ -493,7 +488,8 @@ struct wmi_power_mgmt_cfg_cmd {
  */
 struct wmi_pcp_start_cmd {
        __le16 bcon_interval;
-       u8 reserved0[10];
+       u8 pcp_max_assoc_sta;
+       u8 reserved0[9];
        u8 network_type;
        u8 channel;
        u8 disable_sec_offload;
@@ -857,6 +853,7 @@ enum wmi_event_id {
        WMI_RF_MGMT_STATUS_EVENTID              = 0x1853,
        WMI_BF_SM_MGMT_DONE_EVENTID             = 0x1838,
        WMI_RX_MGMT_PACKET_EVENTID              = 0x1840,
+       WMI_TX_MGMT_PACKET_EVENTID              = 0x1841,
 
        /* Performance monitoring events */
        WMI_DATA_PORT_OPEN_EVENTID              = 0x1860,
@@ -1040,16 +1037,23 @@ enum wmi_disconnect_reason {
 struct wmi_disconnect_event {
        __le16 protocol_reason_status;  /* reason code, see 802.11 spec. */
        u8 bssid[WMI_MAC_LEN];          /* set if known */
-       u8 disconnect_reason;           /* see wmi_disconnect_reason_e */
-       u8 assoc_resp_len;
-       u8 assoc_info[0];
+       u8 disconnect_reason;           /* see wmi_disconnect_reason */
+       u8 assoc_resp_len;              /* not in use */
+       u8 assoc_info[0];               /* not in use */
 } __packed;
 
 /*
  * WMI_SCAN_COMPLETE_EVENTID
  */
+enum scan_status {
+       WMI_SCAN_SUCCESS        = 0,
+       WMI_SCAN_FAILED         = 1,
+       WMI_SCAN_ABORTED        = 2,
+       WMI_SCAN_REJECTED       = 3,
+};
+
 struct wmi_scan_complete_event {
-       __le32 status;
+       __le32 status;  /* scan_status */
 } __packed;
 
 /*
@@ -1256,6 +1260,14 @@ struct wmi_rx_mgmt_info {
        u8 channel;     /* From Radio MNGR */
 } __packed;
 
+
+/*
+ * WMI_TX_MGMT_PACKET_EVENTID
+ */
+struct wmi_tx_mgmt_packet_event {
+       u8 payload[0];
+} __packed;
+
 struct wmi_rx_mgmt_packet_event {
        struct wmi_rx_mgmt_info info;
        u8 payload[0];
index 088d544ec63f940b2a7b2234f1eac9b7458a19ce..e3f67b8d3f8003d546867b51648d25fda81d0f15 100644 (file)
@@ -1,7 +1,8 @@
 config B43
        tristate "Broadcom 43xx wireless support (mac80211 stack)"
-       depends on SSB_POSSIBLE && MAC80211 && HAS_DMA
-       select SSB
+       depends on (BCMA_POSSIBLE || SSB_POSSIBLE) && MAC80211 && HAS_DMA
+       select BCMA if B43_BCMA
+       select SSB if B43_SSB
        select FW_LOADER
        ---help---
          b43 is a driver for the Broadcom 43xx series wireless devices.
@@ -27,14 +28,33 @@ config B43
          If unsure, say M.
 
 config B43_BCMA
-       bool "Support for BCMA bus"
-       depends on B43 && (BCMA = y || BCMA = B43)
-       default y
+       bool
 
 config B43_SSB
        bool
-       depends on B43 && (SSB = y || SSB = B43)
-       default y
+
+choice
+       prompt "Supported bus types"
+       depends on B43
+       default B43_BCMA_AND_SSB
+
+config B43_BUSES_BCMA_AND_SSB
+       bool "BCMA and SSB"
+       depends on BCMA_POSSIBLE && SSB_POSSIBLE
+       select B43_BCMA
+       select B43_SSB
+
+config B43_BUSES_BCMA
+       bool "BCMA only"
+       depends on BCMA_POSSIBLE
+       select B43_BCMA
+
+config B43_BUSES_SSB
+       bool "SSB only"
+       depends on SSB_POSSIBLE
+       select B43_SSB
+
+endchoice
 
 # Auto-select SSB PCI-HOST support, if possible
 config B43_PCI_AUTOSELECT
@@ -53,7 +73,7 @@ config B43_PCICORE_AUTOSELECT
 
 config B43_PCMCIA
        bool "Broadcom 43xx PCMCIA device support"
-       depends on B43 && SSB_PCMCIAHOST_POSSIBLE
+       depends on B43 && B43_SSB && SSB_PCMCIAHOST_POSSIBLE
        select SSB_PCMCIAHOST
        ---help---
          Broadcom 43xx PCMCIA device support.
@@ -73,7 +93,7 @@ config B43_PCMCIA
 
 config B43_SDIO
        bool "Broadcom 43xx SDIO device support"
-       depends on B43 && SSB_SDIOHOST_POSSIBLE
+       depends on B43 && B43_SSB && SSB_SDIOHOST_POSSIBLE
        select SSB_SDIOHOST
        ---help---
          Broadcom 43xx device support for Soft-MAC SDIO devices.
@@ -98,7 +118,7 @@ config B43_BCMA_PIO
 
 config B43_PIO
        bool
-       depends on B43
+       depends on B43 && B43_SSB
        select SSB_BLOCKIO
        default y
 
@@ -116,7 +136,7 @@ config B43_PHY_N
 
 config B43_PHY_LP
        bool "Support for low-power (LP-PHY) devices"
-       depends on B43
+       depends on B43 && B43_SSB
        default y
        ---help---
          Support for the LP-PHY.
index 54376fddfaf9f11fef6e4a7a0ae5554d5e427f4c..4113b69347640359aa22020eb0dcfd7efe0b7c14 100644 (file)
@@ -915,10 +915,6 @@ struct b43_wl {
        char rng_name[30 + 1];
 #endif /* CONFIG_B43_HWRNG */
 
-       /* List of all wireless devices on this chip */
-       struct list_head devlist;
-       u8 nr_devs;
-
        bool radiotap_enabled;
        bool radio_enabled;
 
index 184c95659279070f2a64edfa6628f109ded51f22..f3205c6988bc4354a14a5226011f762868253936 100644 (file)
@@ -5,7 +5,9 @@ enum b43_bus_type {
 #ifdef CONFIG_B43_BCMA
        B43_BUS_BCMA,
 #endif
+#ifdef CONFIG_B43_SSB
        B43_BUS_SSB,
+#endif
 };
 
 struct b43_bus_dev {
@@ -52,13 +54,21 @@ struct b43_bus_dev {
 
 static inline bool b43_bus_host_is_pcmcia(struct b43_bus_dev *dev)
 {
+#ifdef CONFIG_B43_SSB
        return (dev->bus_type == B43_BUS_SSB &&
                dev->sdev->bus->bustype == SSB_BUSTYPE_PCMCIA);
+#else
+       return false;
+#endif
 }
 static inline bool b43_bus_host_is_sdio(struct b43_bus_dev *dev)
 {
+#ifdef CONFIG_B43_SSB
        return (dev->bus_type == B43_BUS_SSB &&
                dev->sdev->bus->bustype == SSB_BUSTYPE_SDIO);
+#else
+       return false;
+#endif
 }
 
 struct b43_bus_dev *b43_bus_dev_bcma_init(struct bcma_device *core);
index 69fc3d65531a7eeb88c4901f804ab197dbbb939e..32538ac5f7e4c0e821d5669329c7d2bd692c7ddd 100644 (file)
@@ -182,7 +182,7 @@ static struct ieee80211_rate __b43_ratetable[] = {
 #define b43_g_ratetable                (__b43_ratetable + 0)
 #define b43_g_ratetable_size   12
 
-#define CHAN4G(_channel, _freq, _flags) {                      \
+#define CHAN2G(_channel, _freq, _flags) {                      \
        .band                   = IEEE80211_BAND_2GHZ,          \
        .center_freq            = (_freq),                      \
        .hw_value               = (_channel),                   \
@@ -191,23 +191,31 @@ static struct ieee80211_rate __b43_ratetable[] = {
        .max_power              = 30,                           \
 }
 static struct ieee80211_channel b43_2ghz_chantable[] = {
-       CHAN4G(1, 2412, 0),
-       CHAN4G(2, 2417, 0),
-       CHAN4G(3, 2422, 0),
-       CHAN4G(4, 2427, 0),
-       CHAN4G(5, 2432, 0),
-       CHAN4G(6, 2437, 0),
-       CHAN4G(7, 2442, 0),
-       CHAN4G(8, 2447, 0),
-       CHAN4G(9, 2452, 0),
-       CHAN4G(10, 2457, 0),
-       CHAN4G(11, 2462, 0),
-       CHAN4G(12, 2467, 0),
-       CHAN4G(13, 2472, 0),
-       CHAN4G(14, 2484, 0),
+       CHAN2G(1, 2412, 0),
+       CHAN2G(2, 2417, 0),
+       CHAN2G(3, 2422, 0),
+       CHAN2G(4, 2427, 0),
+       CHAN2G(5, 2432, 0),
+       CHAN2G(6, 2437, 0),
+       CHAN2G(7, 2442, 0),
+       CHAN2G(8, 2447, 0),
+       CHAN2G(9, 2452, 0),
+       CHAN2G(10, 2457, 0),
+       CHAN2G(11, 2462, 0),
+       CHAN2G(12, 2467, 0),
+       CHAN2G(13, 2472, 0),
+       CHAN2G(14, 2484, 0),
 };
-#undef CHAN4G
+#undef CHAN2G
 
+#define CHAN4G(_channel, _flags) {                             \
+       .band                   = IEEE80211_BAND_5GHZ,          \
+       .center_freq            = 4000 + (5 * (_channel)),      \
+       .hw_value               = (_channel),                   \
+       .flags                  = (_flags),                     \
+       .max_antenna_gain       = 0,                            \
+       .max_power              = 30,                           \
+}
 #define CHAN5G(_channel, _flags) {                             \
        .band                   = IEEE80211_BAND_5GHZ,          \
        .center_freq            = 5000 + (5 * (_channel)),      \
@@ -217,6 +225,18 @@ static struct ieee80211_channel b43_2ghz_chantable[] = {
        .max_power              = 30,                           \
 }
 static struct ieee80211_channel b43_5ghz_nphy_chantable[] = {
+       CHAN4G(184, 0),         CHAN4G(186, 0),
+       CHAN4G(188, 0),         CHAN4G(190, 0),
+       CHAN4G(192, 0),         CHAN4G(194, 0),
+       CHAN4G(196, 0),         CHAN4G(198, 0),
+       CHAN4G(200, 0),         CHAN4G(202, 0),
+       CHAN4G(204, 0),         CHAN4G(206, 0),
+       CHAN4G(208, 0),         CHAN4G(210, 0),
+       CHAN4G(212, 0),         CHAN4G(214, 0),
+       CHAN4G(216, 0),         CHAN4G(218, 0),
+       CHAN4G(220, 0),         CHAN4G(222, 0),
+       CHAN4G(224, 0),         CHAN4G(226, 0),
+       CHAN4G(228, 0),
        CHAN5G(32, 0),          CHAN5G(34, 0),
        CHAN5G(36, 0),          CHAN5G(38, 0),
        CHAN5G(40, 0),          CHAN5G(42, 0),
@@ -260,18 +280,7 @@ static struct ieee80211_channel b43_5ghz_nphy_chantable[] = {
        CHAN5G(170, 0),         CHAN5G(172, 0),
        CHAN5G(174, 0),         CHAN5G(176, 0),
        CHAN5G(178, 0),         CHAN5G(180, 0),
-       CHAN5G(182, 0),         CHAN5G(184, 0),
-       CHAN5G(186, 0),         CHAN5G(188, 0),
-       CHAN5G(190, 0),         CHAN5G(192, 0),
-       CHAN5G(194, 0),         CHAN5G(196, 0),
-       CHAN5G(198, 0),         CHAN5G(200, 0),
-       CHAN5G(202, 0),         CHAN5G(204, 0),
-       CHAN5G(206, 0),         CHAN5G(208, 0),
-       CHAN5G(210, 0),         CHAN5G(212, 0),
-       CHAN5G(214, 0),         CHAN5G(216, 0),
-       CHAN5G(218, 0),         CHAN5G(220, 0),
-       CHAN5G(222, 0),         CHAN5G(224, 0),
-       CHAN5G(226, 0),         CHAN5G(228, 0),
+       CHAN5G(182, 0),
 };
 
 static struct ieee80211_channel b43_5ghz_aphy_chantable[] = {
@@ -295,6 +304,7 @@ static struct ieee80211_channel b43_5ghz_aphy_chantable[] = {
        CHAN5G(208, 0),         CHAN5G(212, 0),
        CHAN5G(216, 0),
 };
+#undef CHAN4G
 #undef CHAN5G
 
 static struct ieee80211_supported_band b43_band_5GHz_nphy = {
@@ -1175,18 +1185,7 @@ static void b43_bcma_phy_reset(struct b43_wldev *dev)
        bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, flags);
        udelay(2);
 
-       /* Take PHY out of reset */
-       flags = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
-       flags &= ~B43_BCMA_IOCTL_PHY_RESET;
-       flags |= BCMA_IOCTL_FGC;
-       bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, flags);
-       udelay(1);
-
-       /* Do not force clock anymore */
-       flags = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
-       flags &= ~BCMA_IOCTL_FGC;
-       bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, flags);
-       udelay(1);
+       b43_phy_take_out_of_reset(dev);
 }
 
 static void b43_bcma_wireless_core_reset(struct b43_wldev *dev, bool gmode)
@@ -1195,18 +1194,22 @@ static void b43_bcma_wireless_core_reset(struct b43_wldev *dev, bool gmode)
                  B43_BCMA_CLKCTLST_PHY_PLL_REQ;
        u32 status = B43_BCMA_CLKCTLST_80211_PLL_ST |
                     B43_BCMA_CLKCTLST_PHY_PLL_ST;
+       u32 flags;
+
+       flags = B43_BCMA_IOCTL_PHY_CLKEN;
+       if (gmode)
+               flags |= B43_BCMA_IOCTL_GMODE;
+       b43_device_enable(dev, flags);
 
-       b43_device_enable(dev, B43_BCMA_IOCTL_PHY_CLKEN);
        bcma_core_set_clockmode(dev->dev->bdev, BCMA_CLKMODE_FAST);
        b43_bcma_phy_reset(dev);
        bcma_core_pll_ctl(dev->dev->bdev, req, status, true);
 }
 #endif
 
+#ifdef CONFIG_B43_SSB
 static void b43_ssb_wireless_core_reset(struct b43_wldev *dev, bool gmode)
 {
-       struct ssb_device *sdev = dev->dev->sdev;
-       u32 tmslow;
        u32 flags = 0;
 
        if (gmode)
@@ -1218,18 +1221,9 @@ static void b43_ssb_wireless_core_reset(struct b43_wldev *dev, bool gmode)
        b43_device_enable(dev, flags);
        msleep(2);              /* Wait for the PLL to turn on. */
 
-       /* Now take the PHY out of Reset again */
-       tmslow = ssb_read32(sdev, SSB_TMSLOW);
-       tmslow |= SSB_TMSLOW_FGC;
-       tmslow &= ~B43_TMSLOW_PHYRESET;
-       ssb_write32(sdev, SSB_TMSLOW, tmslow);
-       ssb_read32(sdev, SSB_TMSLOW);   /* flush */
-       msleep(1);
-       tmslow &= ~SSB_TMSLOW_FGC;
-       ssb_write32(sdev, SSB_TMSLOW, tmslow);
-       ssb_read32(sdev, SSB_TMSLOW);   /* flush */
-       msleep(1);
+       b43_phy_take_out_of_reset(dev);
 }
+#endif
 
 void b43_wireless_core_reset(struct b43_wldev *dev, bool gmode)
 {
@@ -2704,32 +2698,37 @@ static int b43_upload_initvals(struct b43_wldev *dev)
        struct b43_firmware *fw = &dev->fw;
        const struct b43_iv *ivals;
        size_t count;
-       int err;
 
        hdr = (const struct b43_fw_header *)(fw->initvals.data->data);
        ivals = (const struct b43_iv *)(fw->initvals.data->data + hdr_len);
        count = be32_to_cpu(hdr->size);
-       err = b43_write_initvals(dev, ivals, count,
+       return b43_write_initvals(dev, ivals, count,
                                 fw->initvals.data->size - hdr_len);
-       if (err)
-               goto out;
-       if (fw->initvals_band.data) {
-               hdr = (const struct b43_fw_header *)(fw->initvals_band.data->data);
-               ivals = (const struct b43_iv *)(fw->initvals_band.data->data + hdr_len);
-               count = be32_to_cpu(hdr->size);
-               err = b43_write_initvals(dev, ivals, count,
-                                        fw->initvals_band.data->size - hdr_len);
-               if (err)
-                       goto out;
-       }
-out:
+}
 
-       return err;
+static int b43_upload_initvals_band(struct b43_wldev *dev)
+{
+       const size_t hdr_len = sizeof(struct b43_fw_header);
+       const struct b43_fw_header *hdr;
+       struct b43_firmware *fw = &dev->fw;
+       const struct b43_iv *ivals;
+       size_t count;
+
+       if (!fw->initvals_band.data)
+               return 0;
+
+       hdr = (const struct b43_fw_header *)(fw->initvals_band.data->data);
+       ivals = (const struct b43_iv *)(fw->initvals_band.data->data + hdr_len);
+       count = be32_to_cpu(hdr->size);
+       return b43_write_initvals(dev, ivals, count,
+                                 fw->initvals_band.data->size - hdr_len);
 }
 
 /* Initialize the GPIOs
  * http://bcm-specs.sipsolutions.net/GPIO
  */
+
+#ifdef CONFIG_B43_SSB
 static struct ssb_device *b43_ssb_gpio_dev(struct b43_wldev *dev)
 {
        struct ssb_bus *bus = dev->dev->sdev->bus;
@@ -2740,10 +2739,13 @@ static struct ssb_device *b43_ssb_gpio_dev(struct b43_wldev *dev)
        return bus->chipco.dev;
 #endif
 }
+#endif
 
 static int b43_gpio_init(struct b43_wldev *dev)
 {
+#ifdef CONFIG_B43_SSB
        struct ssb_device *gpiodev;
+#endif
        u32 mask, set;
 
        b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_GPOUTSMSK, 0);
@@ -2802,7 +2804,9 @@ static int b43_gpio_init(struct b43_wldev *dev)
 /* Turn off all GPIO stuff. Call this on module unload, for example. */
 static void b43_gpio_cleanup(struct b43_wldev *dev)
 {
+#ifdef CONFIG_B43_SSB
        struct ssb_device *gpiodev;
+#endif
 
        switch (dev->dev->bus_type) {
 #ifdef CONFIG_B43_BCMA
@@ -3086,6 +3090,10 @@ static int b43_chip_init(struct b43_wldev *dev)
        if (err)
                goto err_gpio_clean;
 
+       err = b43_upload_initvals_band(dev);
+       if (err)
+               goto err_gpio_clean;
+
        /* Turn the Analog on and initialize the PHY. */
        phy->ops->switch_analog(dev, 1);
        err = b43_phy_init(dev);
@@ -3685,37 +3693,6 @@ static void b43_op_set_tsf(struct ieee80211_hw *hw,
        mutex_unlock(&wl->mutex);
 }
 
-static void b43_put_phy_into_reset(struct b43_wldev *dev)
-{
-       u32 tmp;
-
-       switch (dev->dev->bus_type) {
-#ifdef CONFIG_B43_BCMA
-       case B43_BUS_BCMA:
-               b43err(dev->wl,
-                      "Putting PHY into reset not supported on BCMA\n");
-               break;
-#endif
-#ifdef CONFIG_B43_SSB
-       case B43_BUS_SSB:
-               tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW);
-               tmp &= ~B43_TMSLOW_GMODE;
-               tmp |= B43_TMSLOW_PHYRESET;
-               tmp |= SSB_TMSLOW_FGC;
-               ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp);
-               msleep(1);
-
-               tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW);
-               tmp &= ~SSB_TMSLOW_FGC;
-               tmp |= B43_TMSLOW_PHYRESET;
-               ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp);
-               msleep(1);
-
-               break;
-#endif
-       }
-}
-
 static const char *band_to_string(enum ieee80211_band band)
 {
        switch (band) {
@@ -3731,94 +3708,75 @@ static const char *band_to_string(enum ieee80211_band band)
 }
 
 /* Expects wl->mutex locked */
-static int b43_switch_band(struct b43_wl *wl, struct ieee80211_channel *chan)
+static int b43_switch_band(struct b43_wldev *dev,
+                          struct ieee80211_channel *chan)
 {
-       struct b43_wldev *up_dev = NULL;
-       struct b43_wldev *down_dev;
-       struct b43_wldev *d;
-       int err;
-       bool uninitialized_var(gmode);
-       int prev_status;
+       struct b43_phy *phy = &dev->phy;
+       bool gmode;
+       u32 tmp;
 
-       /* Find a device and PHY which supports the band. */
-       list_for_each_entry(d, &wl->devlist, list) {
-               switch (chan->band) {
-               case IEEE80211_BAND_5GHZ:
-                       if (d->phy.supports_5ghz) {
-                               up_dev = d;
-                               gmode = false;
-                       }
-                       break;
-               case IEEE80211_BAND_2GHZ:
-                       if (d->phy.supports_2ghz) {
-                               up_dev = d;
-                               gmode = true;
-                       }
-                       break;
-               default:
-                       B43_WARN_ON(1);
-                       return -EINVAL;
-               }
-               if (up_dev)
-                       break;
+       switch (chan->band) {
+       case IEEE80211_BAND_5GHZ:
+               gmode = false;
+               break;
+       case IEEE80211_BAND_2GHZ:
+               gmode = true;
+               break;
+       default:
+               B43_WARN_ON(1);
+               return -EINVAL;
        }
-       if (!up_dev) {
-               b43err(wl, "Could not find a device for %s-GHz band operation\n",
+
+       if (!((gmode && phy->supports_2ghz) ||
+             (!gmode && phy->supports_5ghz))) {
+               b43err(dev->wl, "This device doesn't support %s-GHz band\n",
                       band_to_string(chan->band));
                return -ENODEV;
        }
-       if ((up_dev == wl->current_dev) &&
-           (!!wl->current_dev->phy.gmode == !!gmode)) {
+
+       if (!!phy->gmode == !!gmode) {
                /* This device is already running. */
                return 0;
        }
-       b43dbg(wl, "Switching to %s-GHz band\n",
+
+       b43dbg(dev->wl, "Switching to %s GHz band\n",
               band_to_string(chan->band));
-       down_dev = wl->current_dev;
 
-       prev_status = b43_status(down_dev);
-       /* Shutdown the currently running core. */
-       if (prev_status >= B43_STAT_STARTED)
-               down_dev = b43_wireless_core_stop(down_dev);
-       if (prev_status >= B43_STAT_INITIALIZED)
-               b43_wireless_core_exit(down_dev);
+       /* Some new devices don't need disabling radio for band switching */
+       if (!(phy->type == B43_PHYTYPE_N && phy->rev >= 3))
+               b43_software_rfkill(dev, true);
 
-       if (down_dev != up_dev) {
-               /* We switch to a different core, so we put PHY into
-                * RESET on the old core. */
-               b43_put_phy_into_reset(down_dev);
+       phy->gmode = gmode;
+       b43_phy_put_into_reset(dev);
+       switch (dev->dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+       case B43_BUS_BCMA:
+               tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
+               if (gmode)
+                       tmp |= B43_BCMA_IOCTL_GMODE;
+               else
+                       tmp &= ~B43_BCMA_IOCTL_GMODE;
+               bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp);
+               break;
+#endif
+#ifdef CONFIG_B43_SSB
+       case B43_BUS_SSB:
+               tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW);
+               if (gmode)
+                       tmp |= B43_TMSLOW_GMODE;
+               else
+                       tmp &= ~B43_TMSLOW_GMODE;
+               ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp);
+               break;
+#endif
        }
+       b43_phy_take_out_of_reset(dev);
 
-       /* Now start the new core. */
-       up_dev->phy.gmode = gmode;
-       if (prev_status >= B43_STAT_INITIALIZED) {
-               err = b43_wireless_core_init(up_dev);
-               if (err) {
-                       b43err(wl, "Fatal: Could not initialize device for "
-                              "selected %s-GHz band\n",
-                              band_to_string(chan->band));
-                       goto init_failure;
-               }
-       }
-       if (prev_status >= B43_STAT_STARTED) {
-               err = b43_wireless_core_start(up_dev);
-               if (err) {
-                       b43err(wl, "Fatal: Could not start device for "
-                              "selected %s-GHz band\n",
-                              band_to_string(chan->band));
-                       b43_wireless_core_exit(up_dev);
-                       goto init_failure;
-               }
-       }
-       B43_WARN_ON(b43_status(up_dev) != prev_status);
+       b43_upload_initvals_band(dev);
 
-       wl->current_dev = up_dev;
+       b43_phy_init(dev);
 
        return 0;
-init_failure:
-       /* Whoops, failed to init the new core. No core is operating now. */
-       wl->current_dev = NULL;
-       return err;
 }
 
 /* Write the short and long frame retry limit values. */
@@ -3851,8 +3809,10 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
 
        dev = wl->current_dev;
 
+       b43_mac_suspend(dev);
+
        /* Switch the band (if necessary). This might change the active core. */
-       err = b43_switch_band(wl, conf->chandef.chan);
+       err = b43_switch_band(dev, conf->chandef.chan);
        if (err)
                goto out_unlock_mutex;
 
@@ -3871,8 +3831,6 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
        else
                phy->is_40mhz = false;
 
-       b43_mac_suspend(dev);
-
        if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
                b43_set_retry_limits(dev, conf->short_frame_max_tx_count,
                                          conf->long_frame_max_tx_count);
@@ -4582,8 +4540,12 @@ static void b43_imcfglo_timeouts_workaround(struct b43_wldev *dev)
        struct ssb_bus *bus;
        u32 tmp;
 
+#ifdef CONFIG_B43_SSB
        if (dev->dev->bus_type != B43_BUS_SSB)
                return;
+#else
+       return;
+#endif
 
        bus = dev->dev->sdev->bus;
 
@@ -4738,7 +4700,7 @@ static int b43_wireless_core_init(struct b43_wldev *dev)
        }
        if (sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW)
                hf |= B43_HF_DSCRQ; /* Disable slowclock requests from ucode. */
-#ifdef CONFIG_SSB_DRIVER_PCICORE
+#if defined(CONFIG_B43_SSB) && defined(CONFIG_SSB_DRIVER_PCICORE)
        if (dev->dev->bus_type == B43_BUS_SSB &&
            dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI &&
            dev->dev->sdev->bus->pcicore.dev->id.revision <= 10)
@@ -5129,10 +5091,82 @@ static void b43_wireless_core_detach(struct b43_wldev *dev)
        b43_phy_free(dev);
 }
 
+static void b43_supported_bands(struct b43_wldev *dev, bool *have_2ghz_phy,
+                               bool *have_5ghz_phy)
+{
+       u16 dev_id = 0;
+
+#ifdef CONFIG_B43_BCMA
+       if (dev->dev->bus_type == B43_BUS_BCMA &&
+           dev->dev->bdev->bus->hosttype == BCMA_HOSTTYPE_PCI)
+               dev_id = dev->dev->bdev->bus->host_pci->device;
+#endif
+#ifdef CONFIG_B43_SSB
+       if (dev->dev->bus_type == B43_BUS_SSB &&
+           dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI)
+               dev_id = dev->dev->sdev->bus->host_pci->device;
+#endif
+       /* Override with SPROM value if available */
+       if (dev->dev->bus_sprom->dev_id)
+               dev_id = dev->dev->bus_sprom->dev_id;
+
+       /* Note: below IDs can be "virtual" (not maching e.g. real PCI ID) */
+       switch (dev_id) {
+       case 0x4324: /* BCM4306 */
+       case 0x4312: /* BCM4311 */
+       case 0x4319: /* BCM4318 */
+       case 0x4328: /* BCM4321 */
+       case 0x432b: /* BCM4322 */
+       case 0x4350: /* BCM43222 */
+       case 0x4353: /* BCM43224 */
+       case 0x0576: /* BCM43224 */
+       case 0x435f: /* BCM6362 */
+       case 0x4331: /* BCM4331 */
+       case 0x4359: /* BCM43228 */
+       case 0x43a0: /* BCM4360 */
+       case 0x43b1: /* BCM4352 */
+               /* Dual band devices */
+               *have_2ghz_phy = true;
+               *have_5ghz_phy = true;
+               return;
+       case 0x4321: /* BCM4306 */
+       case 0x4313: /* BCM4311 */
+       case 0x431a: /* BCM4318 */
+       case 0x432a: /* BCM4321 */
+       case 0x432d: /* BCM4322 */
+       case 0x4352: /* BCM43222 */
+       case 0x4333: /* BCM4331 */
+       case 0x43a2: /* BCM4360 */
+       case 0x43b3: /* BCM4352 */
+               /* 5 GHz only devices */
+               *have_2ghz_phy = false;
+               *have_5ghz_phy = true;
+               return;
+       }
+
+       /* As a fallback, try to guess using PHY type */
+       switch (dev->phy.type) {
+       case B43_PHYTYPE_A:
+               *have_2ghz_phy = false;
+               *have_5ghz_phy = true;
+               return;
+       case B43_PHYTYPE_G:
+       case B43_PHYTYPE_N:
+       case B43_PHYTYPE_LP:
+       case B43_PHYTYPE_HT:
+       case B43_PHYTYPE_LCN:
+               *have_2ghz_phy = true;
+               *have_5ghz_phy = false;
+               return;
+       }
+
+       B43_WARN_ON(1);
+}
+
 static int b43_wireless_core_attach(struct b43_wldev *dev)
 {
        struct b43_wl *wl = dev->wl;
-       struct pci_dev *pdev = NULL;
+       struct b43_phy *phy = &dev->phy;
        int err;
        u32 tmp;
        bool have_2ghz_phy = false, have_5ghz_phy = false;
@@ -5144,19 +5178,15 @@ static int b43_wireless_core_attach(struct b43_wldev *dev)
         * that in core_init(), too.
         */
 
-#ifdef CONFIG_B43_SSB
-       if (dev->dev->bus_type == B43_BUS_SSB &&
-           dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI)
-               pdev = dev->dev->sdev->bus->host_pci;
-#endif
-
        err = b43_bus_powerup(dev, 0);
        if (err) {
                b43err(wl, "Bus powerup failed\n");
                goto out;
        }
 
-       /* Get the PHY type. */
+       phy->do_full_init = true;
+
+       /* Try to guess supported bands for the first init needs */
        switch (dev->dev->bus_type) {
 #ifdef CONFIG_B43_BCMA
        case B43_BUS_BCMA:
@@ -5178,51 +5208,31 @@ static int b43_wireless_core_attach(struct b43_wldev *dev)
        }
 
        dev->phy.gmode = have_2ghz_phy;
-       dev->phy.radio_on = true;
        b43_wireless_core_reset(dev, dev->phy.gmode);
 
+       /* Get the PHY type. */
        err = b43_phy_versioning(dev);
        if (err)
                goto err_powerdown;
-       /* Check if this device supports multiband. */
-       if (!pdev ||
-           (pdev->device != 0x4312 &&
-            pdev->device != 0x4319 && pdev->device != 0x4324)) {
-               /* No multiband support. */
-               have_2ghz_phy = false;
+
+       /* Get real info about supported bands */
+       b43_supported_bands(dev, &have_2ghz_phy, &have_5ghz_phy);
+
+       /* We don't support 5 GHz on some PHYs yet */
+       switch (dev->phy.type) {
+       case B43_PHYTYPE_A:
+       case B43_PHYTYPE_N:
+       case B43_PHYTYPE_LP:
+       case B43_PHYTYPE_HT:
+               b43warn(wl, "5 GHz band is unsupported on this PHY\n");
                have_5ghz_phy = false;
-               switch (dev->phy.type) {
-               case B43_PHYTYPE_A:
-                       have_5ghz_phy = true;
-                       break;
-               case B43_PHYTYPE_LP: //FIXME not always!
-#if 0 //FIXME enabling 5GHz causes a NULL pointer dereference
-                       have_5ghz_phy = 1;
-#endif
-               case B43_PHYTYPE_G:
-               case B43_PHYTYPE_N:
-               case B43_PHYTYPE_HT:
-               case B43_PHYTYPE_LCN:
-                       have_2ghz_phy = true;
-                       break;
-               default:
-                       B43_WARN_ON(1);
-               }
        }
-       if (dev->phy.type == B43_PHYTYPE_A) {
-               /* FIXME */
-               b43err(wl, "IEEE 802.11a devices are unsupported\n");
+
+       if (!have_2ghz_phy && !have_5ghz_phy) {
+               b43err(wl, "b43 can't support any band on this device\n");
                err = -EOPNOTSUPP;
                goto err_powerdown;
        }
-       if (1 /* disable A-PHY */) {
-               /* FIXME: For now we disable the A-PHY on multi-PHY devices. */
-               if (dev->phy.type != B43_PHYTYPE_N &&
-                   dev->phy.type != B43_PHYTYPE_LP) {
-                       have_2ghz_phy = true;
-                       have_5ghz_phy = false;
-               }
-       }
 
        err = b43_phy_allocate(dev);
        if (err)
@@ -5270,7 +5280,6 @@ static void b43_one_core_detach(struct b43_bus_dev *dev)
        b43_debugfs_remove_device(wldev);
        b43_wireless_core_detach(wldev);
        list_del(&wldev->list);
-       wl->nr_devs--;
        b43_bus_set_wldev(dev, NULL);
        kfree(wldev);
 }
@@ -5295,8 +5304,6 @@ static int b43_one_core_attach(struct b43_bus_dev *dev, struct b43_wl *wl)
        if (err)
                goto err_kfree_wldev;
 
-       list_add(&wldev->list, &wl->devlist);
-       wl->nr_devs++;
        b43_bus_set_wldev(dev, wldev);
        b43_debugfs_add_device(wldev);
 
@@ -5314,6 +5321,7 @@ static int b43_one_core_attach(struct b43_bus_dev *dev, struct b43_wl *wl)
        (pdev->subsystem_vendor == PCI_VENDOR_ID_##_subvendor) &&       \
        (pdev->subsystem_device == _subdevice)                          )
 
+#ifdef CONFIG_B43_SSB
 static void b43_sprom_fixup(struct ssb_bus *bus)
 {
        struct pci_dev *pdev;
@@ -5345,6 +5353,7 @@ static void b43_wireless_exit(struct b43_bus_dev *dev, struct b43_wl *wl)
        ssb_set_devtypedata(dev->sdev, NULL);
        ieee80211_free_hw(hw);
 }
+#endif
 
 static struct b43_wl *b43_wireless_init(struct b43_bus_dev *dev)
 {
@@ -5386,7 +5395,6 @@ static struct b43_wl *b43_wireless_init(struct b43_bus_dev *dev)
        wl->hw = hw;
        mutex_init(&wl->mutex);
        spin_lock_init(&wl->hardirq_lock);
-       INIT_LIST_HEAD(&wl->devlist);
        INIT_WORK(&wl->beacon_update_trigger, b43_beacon_update_trigger_work);
        INIT_WORK(&wl->txpower_adjust_work, b43_phy_txpower_adjust_work);
        INIT_WORK(&wl->tx_work, b43_tx_work);
@@ -5486,39 +5494,42 @@ int b43_ssb_probe(struct ssb_device *sdev, const struct ssb_device_id *id)
        struct b43_bus_dev *dev;
        struct b43_wl *wl;
        int err;
-       int first = 0;
 
        dev = b43_bus_dev_ssb_init(sdev);
        if (!dev)
                return -ENOMEM;
 
        wl = ssb_get_devtypedata(sdev);
-       if (!wl) {
-               /* Probing the first core. Must setup common struct b43_wl */
-               first = 1;
-               b43_sprom_fixup(sdev->bus);
-               wl = b43_wireless_init(dev);
-               if (IS_ERR(wl)) {
-                       err = PTR_ERR(wl);
-                       goto out;
-               }
-               ssb_set_devtypedata(sdev, wl);
-               B43_WARN_ON(ssb_get_devtypedata(sdev) != wl);
+       if (wl) {
+               b43err(NULL, "Dual-core devices are not supported\n");
+               err = -ENOTSUPP;
+               goto err_ssb_kfree_dev;
+       }
+
+       b43_sprom_fixup(sdev->bus);
+
+       wl = b43_wireless_init(dev);
+       if (IS_ERR(wl)) {
+               err = PTR_ERR(wl);
+               goto err_ssb_kfree_dev;
        }
+       ssb_set_devtypedata(sdev, wl);
+       B43_WARN_ON(ssb_get_devtypedata(sdev) != wl);
+
        err = b43_one_core_attach(dev, wl);
        if (err)
-               goto err_wireless_exit;
+               goto err_ssb_wireless_exit;
 
        /* setup and start work to load firmware */
        INIT_WORK(&wl->firmware_load, b43_request_firmware);
        schedule_work(&wl->firmware_load);
 
-      out:
        return err;
 
-      err_wireless_exit:
-       if (first)
-               b43_wireless_exit(dev, wl);
+err_ssb_wireless_exit:
+       b43_wireless_exit(dev, wl);
+err_ssb_kfree_dev:
+       kfree(dev);
        return err;
 }
 
@@ -5546,13 +5557,8 @@ static void b43_ssb_remove(struct ssb_device *sdev)
        /* Unregister HW RNG driver */
        b43_rng_exit(wl);
 
-       if (list_empty(&wl->devlist)) {
-               b43_leds_unregister(wl);
-               /* Last core on the chip unregistered.
-                * We can destroy common struct b43_wl.
-                */
-               b43_wireless_exit(dev, wl);
-       }
+       b43_leds_unregister(wl);
+       b43_wireless_exit(dev, wl);
 }
 
 static struct ssb_driver b43_ssb_driver = {
index dbaa51890198945b7552ed506bc3c8bfe4ba2359..08244b3b327e5f98f06b5d5db8943d8e624bb3a8 100644 (file)
@@ -96,12 +96,16 @@ int b43_phy_init(struct b43_wldev *dev)
 
        phy->channel = ops->get_default_chan(dev);
 
-       ops->software_rfkill(dev, false);
+       phy->ops->switch_analog(dev, true);
+       b43_software_rfkill(dev, false);
+
        err = ops->init(dev);
        if (err) {
                b43err(dev->wl, "PHY init failed\n");
                goto err_block_rf;
        }
+       phy->do_full_init = false;
+
        /* Make sure to switch hardware and firmware (SHM) to
         * the default channel. */
        err = b43_switch_channel(dev, ops->get_default_chan(dev));
@@ -113,10 +117,11 @@ int b43_phy_init(struct b43_wldev *dev)
        return 0;
 
 err_phy_exit:
+       phy->do_full_init = true;
        if (ops->exit)
                ops->exit(dev);
 err_block_rf:
-       ops->software_rfkill(dev, true);
+       b43_software_rfkill(dev, true);
 
        return err;
 }
@@ -125,7 +130,8 @@ void b43_phy_exit(struct b43_wldev *dev)
 {
        const struct b43_phy_operations *ops = dev->phy.ops;
 
-       ops->software_rfkill(dev, true);
+       b43_software_rfkill(dev, true);
+       dev->phy.do_full_init = true;
        if (ops->exit)
                ops->exit(dev);
 }
@@ -312,6 +318,90 @@ void b43_phy_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set)
        }
 }
 
+void b43_phy_put_into_reset(struct b43_wldev *dev)
+{
+       u32 tmp;
+
+       switch (dev->dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+       case B43_BUS_BCMA:
+               tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
+               tmp &= ~B43_BCMA_IOCTL_GMODE;
+               tmp |= B43_BCMA_IOCTL_PHY_RESET;
+               tmp |= BCMA_IOCTL_FGC;
+               bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp);
+               udelay(1);
+
+               tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
+               tmp &= ~BCMA_IOCTL_FGC;
+               bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp);
+               udelay(1);
+               break;
+#endif
+#ifdef CONFIG_B43_SSB
+       case B43_BUS_SSB:
+               tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW);
+               tmp &= ~B43_TMSLOW_GMODE;
+               tmp |= B43_TMSLOW_PHYRESET;
+               tmp |= SSB_TMSLOW_FGC;
+               ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp);
+               usleep_range(1000, 2000);
+
+               tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW);
+               tmp &= ~SSB_TMSLOW_FGC;
+               ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp);
+               usleep_range(1000, 2000);
+
+               break;
+#endif
+       }
+}
+
+void b43_phy_take_out_of_reset(struct b43_wldev *dev)
+{
+       u32 tmp;
+
+       switch (dev->dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+       case B43_BUS_BCMA:
+               /* Unset reset bit (with forcing clock) */
+               tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
+               tmp &= ~B43_BCMA_IOCTL_PHY_RESET;
+               tmp &= ~B43_BCMA_IOCTL_PHY_CLKEN;
+               tmp |= BCMA_IOCTL_FGC;
+               bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp);
+               udelay(1);
+
+               /* Do not force clock anymore */
+               tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
+               tmp &= ~BCMA_IOCTL_FGC;
+               tmp |= B43_BCMA_IOCTL_PHY_CLKEN;
+               bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp);
+               udelay(1);
+               break;
+#endif
+#ifdef CONFIG_B43_SSB
+       case B43_BUS_SSB:
+               /* Unset reset bit (with forcing clock) */
+               tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW);
+               tmp &= ~B43_TMSLOW_PHYRESET;
+               tmp &= ~B43_TMSLOW_PHYCLKEN;
+               tmp |= SSB_TMSLOW_FGC;
+               ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp);
+               ssb_read32(dev->dev->sdev, SSB_TMSLOW); /* flush */
+               usleep_range(1000, 2000);
+
+               tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW);
+               tmp &= ~SSB_TMSLOW_FGC;
+               tmp |= B43_TMSLOW_PHYCLKEN;
+               ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp);
+               ssb_read32(dev->dev->sdev, SSB_TMSLOW); /* flush */
+               usleep_range(1000, 2000);
+               break;
+#endif
+       }
+}
+
 int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel)
 {
        struct b43_phy *phy = &(dev->phy);
index f1b999349876bbfc8cad799858434f5e64a14b37..4ad6240d9ff40e53557b4d29b27788b3eb9602d5 100644 (file)
@@ -231,9 +231,12 @@ struct b43_phy {
        /* HT info */
        bool is_40mhz;
 
-       /* GMODE bit enabled? */
+       /* Is GMODE (2 GHz mode) bit enabled? */
        bool gmode;
 
+       /* After power reset full init has to be performed */
+       bool do_full_init;
+
        /* Analog Type */
        u8 analog;
        /* B43_PHYTYPE_ */
@@ -390,6 +393,9 @@ void b43_phy_lock(struct b43_wldev *dev);
  */
 void b43_phy_unlock(struct b43_wldev *dev);
 
+void b43_phy_put_into_reset(struct b43_wldev *dev);
+void b43_phy_take_out_of_reset(struct b43_wldev *dev);
+
 /**
  * b43_switch_channel - Switch to another channel
  */
index 12f467b8d564f1c5cdca06acbdf65ca8f80b48c8..8f5c14bc10e6fedcab2d7b88a0009470772dad8a 100644 (file)
@@ -1587,6 +1587,7 @@ static void b43_phy_initb5(struct b43_wldev *dev)
        b43_write16(dev, 0x03E4, (b43_read16(dev, 0x03E4) & 0xFFC0) | 0x0004);
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/Init/B6 */
 static void b43_phy_initb6(struct b43_wldev *dev)
 {
        struct b43_phy *phy = &dev->phy;
@@ -1670,7 +1671,7 @@ static void b43_phy_initb6(struct b43_wldev *dev)
                b43_radio_write16(dev, 0x50, 0x20);
        }
        if (phy->radio_rev <= 2) {
-               b43_radio_write16(dev, 0x7C, 0x20);
+               b43_radio_write16(dev, 0x50, 0x20);
                b43_radio_write16(dev, 0x5A, 0x70);
                b43_radio_write16(dev, 0x5B, 0x7B);
                b43_radio_write16(dev, 0x5C, 0xB0);
@@ -1686,9 +1687,8 @@ static void b43_phy_initb6(struct b43_wldev *dev)
                b43_phy_write(dev, 0x2A, 0x8AC0);
        b43_phy_write(dev, 0x0038, 0x0668);
        b43_set_txpower_g(dev, &gphy->bbatt, &gphy->rfatt, gphy->tx_control);
-       if (phy->radio_rev <= 5) {
+       if (phy->radio_rev == 4 || phy->radio_rev == 5)
                b43_phy_maskset(dev, 0x5D, 0xFF80, 0x0003);
-       }
        if (phy->radio_rev <= 2)
                b43_radio_write16(dev, 0x005D, 0x000D);
 
index 24ccbe96e0c8a0723bb165b0f32677ab6ba7ded5..86569f6a870507c1723d95ccc6d39a2c13f449a2 100644 (file)
@@ -257,6 +257,72 @@ static void b43_nphy_rf_ctl_override(struct b43_wldev *dev, u16 field,
        }
 }
 
+static void b43_nphy_rf_ctl_intc_override_rev7(struct b43_wldev *dev,
+                                              enum n_intc_override intc_override,
+                                              u16 value, u8 core_sel)
+{
+       u16 reg, tmp, tmp2, val;
+       int core;
+
+       for (core = 0; core < 2; core++) {
+               if ((core_sel == 1 && core != 0) ||
+                   (core_sel == 2 && core != 1))
+                       continue;
+
+               reg = (core == 0) ? B43_NPHY_RFCTL_INTC1 : B43_NPHY_RFCTL_INTC2;
+
+               switch (intc_override) {
+               case N_INTC_OVERRIDE_OFF:
+                       b43_phy_write(dev, reg, 0);
+                       b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX);
+                       break;
+               case N_INTC_OVERRIDE_TRSW:
+                       b43_phy_maskset(dev, reg, ~0xC0, value << 6);
+                       b43_phy_set(dev, reg, 0x400);
+
+                       b43_phy_mask(dev, 0x2ff, ~0xC000 & 0xFFFF);
+                       b43_phy_set(dev, 0x2ff, 0x2000);
+                       b43_phy_set(dev, 0x2ff, 0x0001);
+                       break;
+               case N_INTC_OVERRIDE_PA:
+                       tmp = 0x0030;
+                       if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ)
+                               val = value << 5;
+                       else
+                               val = value << 4;
+                       b43_phy_maskset(dev, reg, ~tmp, val);
+                       b43_phy_set(dev, reg, 0x1000);
+                       break;
+               case N_INTC_OVERRIDE_EXT_LNA_PU:
+                       if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+                               tmp = 0x0001;
+                               tmp2 = 0x0004;
+                               val = value;
+                       } else {
+                               tmp = 0x0004;
+                               tmp2 = 0x0001;
+                               val = value << 2;
+                       }
+                       b43_phy_maskset(dev, reg, ~tmp, val);
+                       b43_phy_mask(dev, reg, ~tmp2);
+                       break;
+               case N_INTC_OVERRIDE_EXT_LNA_GAIN:
+                       if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+                               tmp = 0x0002;
+                               tmp2 = 0x0008;
+                               val = value << 1;
+                       } else {
+                               tmp = 0x0008;
+                               tmp2 = 0x0002;
+                               val = value << 3;
+                       }
+                       b43_phy_maskset(dev, reg, ~tmp, val);
+                       b43_phy_mask(dev, reg, ~tmp2);
+                       break;
+               }
+       }
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlIntcOverride */
 static void b43_nphy_rf_ctl_intc_override(struct b43_wldev *dev,
                                          enum n_intc_override intc_override,
@@ -265,6 +331,12 @@ static void b43_nphy_rf_ctl_intc_override(struct b43_wldev *dev,
        u8 i, j;
        u16 reg, tmp, val;
 
+       if (dev->phy.rev >= 7) {
+               b43_nphy_rf_ctl_intc_override_rev7(dev, intc_override, value,
+                                                  core);
+               return;
+       }
+
        B43_WARN_ON(dev->phy.rev < 3);
 
        for (i = 0; i < 2; i++) {
@@ -419,7 +491,8 @@ static void b43_nphy_stay_in_carrier_search(struct b43_wldev *dev, bool enable)
                static const u16 clip[] = { 0xFFFF, 0xFFFF };
                if (nphy->deaf_count++ == 0) {
                        nphy->classifier_state = b43_nphy_classifier(dev, 0, 0);
-                       b43_nphy_classifier(dev, 0x7, 0);
+                       b43_nphy_classifier(dev, 0x7,
+                                           B43_NPHY_CLASSCTL_WAITEDEN);
                        b43_nphy_read_clip_detection(dev, nphy->clip_state);
                        b43_nphy_write_clip_detection(dev, clip);
                }
@@ -627,13 +700,11 @@ static void b43_radio_2057_init_post(struct b43_wldev *dev)
        b43_radio_mask(dev, R2057_RFPLL_MISC_CAL_RESETN, ~0x78);
        b43_radio_mask(dev, R2057_XTAL_CONFIG2, ~0x80);
 
-       if (dev->phy.n->init_por) {
+       if (dev->phy.do_full_init) {
                b43_radio_2057_rcal(dev);
                b43_radio_2057_rccal(dev);
        }
        b43_radio_mask(dev, R2057_RFPLL_MASTER, ~0x8);
-
-       dev->phy.n->init_por = false;
 }
 
 /* http://bcm-v4.sipsolutions.net/802.11/Radio/2057/Init */
@@ -734,9 +805,16 @@ static void b43_radio_2056_setup(struct b43_wldev *dev,
        u16 bias, cbias;
        u16 pag_boost, padg_boost, pgag_boost, mixg_boost;
        u16 paa_boost, pada_boost, pgaa_boost, mixa_boost;
+       bool is_pkg_fab_smic;
 
        B43_WARN_ON(dev->phy.rev < 3);
 
+       is_pkg_fab_smic =
+               ((dev->dev->chip_id == BCMA_CHIP_ID_BCM43224 ||
+                 dev->dev->chip_id == BCMA_CHIP_ID_BCM43225 ||
+                 dev->dev->chip_id == BCMA_CHIP_ID_BCM43421) &&
+                dev->dev->chip_pkg == BCMA_PKG_ID_BCM43224_FAB_SMIC);
+
        b43_chantab_radio_2056_upload(dev, e);
        b2056_upload_syn_pll_cp2(dev, band == IEEE80211_BAND_5GHZ);
 
@@ -744,7 +822,8 @@ static void b43_radio_2056_setup(struct b43_wldev *dev,
            b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
                b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER1, 0x1F);
                b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER2, 0x1F);
-               if (dev->dev->chip_id == 0x4716) {
+               if (dev->dev->chip_id == BCMA_CHIP_ID_BCM4716 ||
+                   dev->dev->chip_id == BCMA_CHIP_ID_BCM47162) {
                        b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER4, 0x14);
                        b43_radio_write(dev, B2056_SYN_PLL_CP2, 0);
                } else {
@@ -752,6 +831,13 @@ static void b43_radio_2056_setup(struct b43_wldev *dev,
                        b43_radio_write(dev, B2056_SYN_PLL_CP2, 0x14);
                }
        }
+       if (sprom->boardflags2_hi & B43_BFH2_GPLL_WAR2 &&
+           b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+               b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER1, 0x1f);
+               b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER2, 0x1f);
+               b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER4, 0x0b);
+               b43_radio_write(dev, B2056_SYN_PLL_CP2, 0x20);
+       }
        if (sprom->boardflags2_lo & B43_BFL2_APLL_WAR &&
            b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
                b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER1, 0x1F);
@@ -767,7 +853,8 @@ static void b43_radio_2056_setup(struct b43_wldev *dev,
                                b43_radio_write(dev,
                                        offset | B2056_TX_PADG_IDAC, 0xcc);
 
-                               if (dev->dev->chip_id == 0x4716) {
+                               if (dev->dev->chip_id == BCMA_CHIP_ID_BCM4716 ||
+                                   dev->dev->chip_id == BCMA_CHIP_ID_BCM47162) {
                                        bias = 0x40;
                                        cbias = 0x45;
                                        pag_boost = 0x5;
@@ -776,6 +863,10 @@ static void b43_radio_2056_setup(struct b43_wldev *dev,
                                } else {
                                        bias = 0x25;
                                        cbias = 0x20;
+                                       if (is_pkg_fab_smic) {
+                                               bias = 0x2a;
+                                               cbias = 0x38;
+                                       }
                                        pag_boost = 0x4;
                                        pgag_boost = 0x03;
                                        mixg_boost = 0x65;
@@ -844,6 +935,8 @@ static void b43_radio_2056_setup(struct b43_wldev *dev,
                        mixa_boost = 0xF;
                }
 
+               cbias = is_pkg_fab_smic ? 0x35 : 0x30;
+
                for (i = 0; i < 2; i++) {
                        offset = i ? B2056_TX1 : B2056_TX0;
 
@@ -862,11 +955,11 @@ static void b43_radio_2056_setup(struct b43_wldev *dev,
                        b43_radio_write(dev,
                                offset | B2056_TX_PADA_CASCBIAS, 0x03);
                        b43_radio_write(dev,
-                               offset | B2056_TX_INTPAA_IAUX_STAT, 0x50);
+                               offset | B2056_TX_INTPAA_IAUX_STAT, 0x30);
                        b43_radio_write(dev,
-                               offset | B2056_TX_INTPAA_IMAIN_STAT, 0x50);
+                               offset | B2056_TX_INTPAA_IMAIN_STAT, 0x30);
                        b43_radio_write(dev,
-                               offset | B2056_TX_INTPAA_CASCBIAS, 0x30);
+                               offset | B2056_TX_INTPAA_CASCBIAS, cbias);
                }
        }
 
@@ -933,7 +1026,7 @@ static void b43_radio_init2056_post(struct b43_wldev *dev)
        b43_radio_mask(dev, B2056_SYN_COM_RESET, ~0x2);
        b43_radio_mask(dev, B2056_SYN_PLL_MAST2, ~0xFC);
        b43_radio_mask(dev, B2056_SYN_RCCAL_CTRL0, ~0x1);
-       if (dev->phy.n->init_por)
+       if (dev->phy.do_full_init)
                b43_radio_2056_rcal(dev);
 }
 
@@ -946,8 +1039,6 @@ static void b43_radio_init2056(struct b43_wldev *dev)
        b43_radio_init2056_pre(dev);
        b2056_upload_inittabs(dev, 0, 0);
        b43_radio_init2056_post(dev);
-
-       dev->phy.n->init_por = false;
 }
 
 /**************************************************
@@ -1164,23 +1255,20 @@ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops,
        u16 seq_mode;
        u32 tmp;
 
-       if (nphy->hang_avoid)
-               b43_nphy_stay_in_carrier_search(dev, true);
+       b43_nphy_stay_in_carrier_search(dev, true);
 
        if ((nphy->bb_mult_save & 0x80000000) == 0) {
                tmp = b43_ntab_read(dev, B43_NTAB16(15, 87));
                nphy->bb_mult_save = (tmp & 0xFFFF) | 0x80000000;
        }
 
+       /* TODO: add modify_bbmult argument */
        if (!dev->phy.is_40mhz)
                tmp = 0x6464;
        else
                tmp = 0x4747;
        b43_ntab_write(dev, B43_NTAB16(15, 87), tmp);
 
-       if (nphy->hang_avoid)
-               b43_nphy_stay_in_carrier_search(dev, false);
-
        b43_phy_write(dev, B43_NPHY_SAMP_DEPCNT, (samps - 1));
 
        if (loops != 0xFFFF)
@@ -1213,6 +1301,8 @@ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops,
                b43err(dev->wl, "run samples timeout\n");
 
        b43_phy_write(dev, B43_NPHY_RFSEQMODE, seq_mode);
+
+       b43_nphy_stay_in_carrier_search(dev, false);
 }
 
 /**************************************************
@@ -1588,8 +1678,8 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
        struct b43_phy_n *nphy = dev->phy.n;
 
        u16 saved_regs_phy_rfctl[2];
-       u16 saved_regs_phy[13];
-       u16 regs_to_store[] = {
+       u16 saved_regs_phy[22];
+       u16 regs_to_store_rev3[] = {
                B43_NPHY_AFECTL_OVER1, B43_NPHY_AFECTL_OVER,
                B43_NPHY_AFECTL_C1, B43_NPHY_AFECTL_C2,
                B43_NPHY_TXF_40CO_B1S1, B43_NPHY_RFCTL_OVER,
@@ -1598,6 +1688,20 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
                B43_NPHY_RFCTL_LUT_TRSW_UP1, B43_NPHY_RFCTL_LUT_TRSW_UP2,
                B43_NPHY_RFCTL_RSSIO1, B43_NPHY_RFCTL_RSSIO2
        };
+       u16 regs_to_store_rev7[] = {
+               B43_NPHY_AFECTL_OVER1, B43_NPHY_AFECTL_OVER,
+               B43_NPHY_AFECTL_C1, B43_NPHY_AFECTL_C2,
+               B43_NPHY_TXF_40CO_B1S1, B43_NPHY_RFCTL_OVER,
+               0x342, 0x343, 0x346, 0x347,
+               0x2ff,
+               B43_NPHY_TXF_40CO_B1S0, B43_NPHY_TXF_40CO_B32S1,
+               B43_NPHY_RFCTL_CMD,
+               B43_NPHY_RFCTL_LUT_TRSW_UP1, B43_NPHY_RFCTL_LUT_TRSW_UP2,
+               0x340, 0x341, 0x344, 0x345,
+               B43_NPHY_RFCTL_RSSIO1, B43_NPHY_RFCTL_RSSIO2
+       };
+       u16 *regs_to_store;
+       int regs_amount;
 
        u16 class;
 
@@ -1617,6 +1721,15 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
        u8 rx_core_state;
        int core, i, j, vcm;
 
+       if (dev->phy.rev >= 7) {
+               regs_to_store = regs_to_store_rev7;
+               regs_amount = ARRAY_SIZE(regs_to_store_rev7);
+       } else {
+               regs_to_store = regs_to_store_rev3;
+               regs_amount = ARRAY_SIZE(regs_to_store_rev3);
+       }
+       BUG_ON(regs_amount > ARRAY_SIZE(saved_regs_phy));
+
        class = b43_nphy_classifier(dev, 0, 0);
        b43_nphy_classifier(dev, 7, 4);
        b43_nphy_read_clip_detection(dev, clip_state);
@@ -1624,22 +1737,29 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
 
        saved_regs_phy_rfctl[0] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1);
        saved_regs_phy_rfctl[1] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2);
-       for (i = 0; i < ARRAY_SIZE(regs_to_store); i++)
+       for (i = 0; i < regs_amount; i++)
                saved_regs_phy[i] = b43_phy_read(dev, regs_to_store[i]);
 
        b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_OFF, 0, 7);
        b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, 1, 7);
-       b43_nphy_rf_ctl_override(dev, 0x1, 0, 0, false);
-       b43_nphy_rf_ctl_override(dev, 0x2, 1, 0, false);
-       b43_nphy_rf_ctl_override(dev, 0x80, 1, 0, false);
-       b43_nphy_rf_ctl_override(dev, 0x40, 1, 0, false);
-
-       if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
-               b43_nphy_rf_ctl_override(dev, 0x20, 0, 0, false);
-               b43_nphy_rf_ctl_override(dev, 0x10, 1, 0, false);
+
+       if (dev->phy.rev >= 7) {
+               /* TODO */
+               if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+               } else {
+               }
        } else {
-               b43_nphy_rf_ctl_override(dev, 0x10, 0, 0, false);
-               b43_nphy_rf_ctl_override(dev, 0x20, 1, 0, false);
+               b43_nphy_rf_ctl_override(dev, 0x1, 0, 0, false);
+               b43_nphy_rf_ctl_override(dev, 0x2, 1, 0, false);
+               b43_nphy_rf_ctl_override(dev, 0x80, 1, 0, false);
+               b43_nphy_rf_ctl_override(dev, 0x40, 1, 0, false);
+               if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+                       b43_nphy_rf_ctl_override(dev, 0x20, 0, 0, false);
+                       b43_nphy_rf_ctl_override(dev, 0x10, 1, 0, false);
+               } else {
+                       b43_nphy_rf_ctl_override(dev, 0x10, 0, 0, false);
+                       b43_nphy_rf_ctl_override(dev, 0x20, 1, 0, false);
+               }
        }
 
        rx_core_state = b43_nphy_get_rx_core_state(dev);
@@ -1654,8 +1774,11 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
 
                /* Grab RSSI results for every possible VCM */
                for (vcm = 0; vcm < 8; vcm++) {
-                       b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC, 0xE3,
-                                       vcm << 2);
+                       if (dev->phy.rev >= 7)
+                               ;
+                       else
+                               b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC,
+                                                 0xE3, vcm << 2);
                        b43_nphy_poll_rssi(dev, N_RSSI_NB, results[vcm], 8);
                }
 
@@ -1682,8 +1805,11 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
                }
 
                /* Select the best VCM */
-               b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC, 0xE3,
-                                 vcm_final << 2);
+               if (dev->phy.rev >= 7)
+                       ;
+               else
+                       b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC,
+                                         0xE3, vcm_final << 2);
 
                for (i = 0; i < 4; i++) {
                        if (core != i / 2)
@@ -1736,9 +1862,9 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
 
        b43_phy_set(dev, B43_NPHY_RFCTL_OVER, 0x1);
        b43_phy_set(dev, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_CMD_RXTX);
-       b43_phy_mask(dev, B43_NPHY_TXF_40CO_B1S1, ~0x1);
+       b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, ~0x1);
 
-       for (i = 0; i < ARRAY_SIZE(regs_to_store); i++)
+       for (i = 0; i < regs_amount; i++)
                b43_phy_write(dev, regs_to_store[i], saved_regs_phy[i]);
 
        /* Store for future configuration */
@@ -2494,8 +2620,8 @@ static void b43_nphy_workarounds_rev3plus(struct b43_wldev *dev)
        struct ssb_sprom *sprom = dev->dev->bus_sprom;
 
        /* TX to RX */
-       u8 tx2rx_events[8] = { 0x4, 0x3, 0x6, 0x5, 0x2, 0x1, 0x8, 0x1F };
-       u8 tx2rx_delays[8] = { 8, 4, 2, 2, 4, 4, 6, 1 };
+       u8 tx2rx_events[7] = { 0x4, 0x3, 0x5, 0x2, 0x1, 0x8, 0x1F };
+       u8 tx2rx_delays[7] = { 8, 4, 4, 4, 4, 6, 1 };
        /* RX to TX */
        u8 rx2tx_events_ipa[9] = { 0x0, 0x1, 0x2, 0x8, 0x5, 0x6, 0xF, 0x3,
                                        0x1F };
@@ -2503,6 +2629,23 @@ static void b43_nphy_workarounds_rev3plus(struct b43_wldev *dev)
        u8 rx2tx_events[9] = { 0x0, 0x1, 0x2, 0x8, 0x5, 0x6, 0x3, 0x4, 0x1F };
        u8 rx2tx_delays[9] = { 8, 6, 6, 4, 4, 18, 42, 1, 1 };
 
+       u16 vmids[5][4] = {
+               { 0xa2, 0xb4, 0xb4, 0x89, }, /* 0 */
+               { 0xb4, 0xb4, 0xb4, 0x24, }, /* 1 */
+               { 0xa2, 0xb4, 0xb4, 0x74, }, /* 2 */
+               { 0xa2, 0xb4, 0xb4, 0x270, }, /* 3 */
+               { 0xa2, 0xb4, 0xb4, 0x00, }, /* 4 and 5 */
+       };
+       u16 gains[5][4] = {
+               { 0x02, 0x02, 0x02, 0x00, }, /* 0 */
+               { 0x02, 0x02, 0x02, 0x02, }, /* 1 */
+               { 0x02, 0x02, 0x02, 0x04, }, /* 2 */
+               { 0x02, 0x02, 0x02, 0x00, }, /* 3 */
+               { 0x02, 0x02, 0x02, 0x00, }, /* 4 and 5 */
+       };
+       u16 *vmid, *gain;
+
+       u8 pdet_range;
        u16 tmp16;
        u32 tmp32;
 
@@ -2561,7 +2704,71 @@ static void b43_nphy_workarounds_rev3plus(struct b43_wldev *dev)
        b43_ntab_write(dev, B43_NTAB16(8, 0), 2);
        b43_ntab_write(dev, B43_NTAB16(8, 16), 2);
 
-       /* TODO */
+       if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
+               pdet_range = sprom->fem.ghz2.pdet_range;
+       else
+               pdet_range = sprom->fem.ghz5.pdet_range;
+       vmid = vmids[min_t(u16, pdet_range, 4)];
+       gain = gains[min_t(u16, pdet_range, 4)];
+       switch (pdet_range) {
+       case 3:
+               if (!(dev->phy.rev >= 4 &&
+                     b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ))
+                       break;
+               /* FALL THROUGH */
+       case 0:
+       case 1:
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x08), 4, vmid);
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x18), 4, vmid);
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x0c), 4, gain);
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x1c), 4, gain);
+               break;
+       case 2:
+               if (dev->phy.rev >= 6) {
+                       if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
+                               vmid[3] = 0x94;
+                       else
+                               vmid[3] = 0x8e;
+                       gain[3] = 3;
+               } else if (dev->phy.rev == 5) {
+                       vmid[3] = 0x84;
+                       gain[3] = 2;
+               }
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x08), 4, vmid);
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x18), 4, vmid);
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x0c), 4, gain);
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x1c), 4, gain);
+               break;
+       case 4:
+       case 5:
+               if (b43_current_band(dev->wl) != IEEE80211_BAND_2GHZ) {
+                       if (pdet_range == 4) {
+                               vmid[3] = 0x8e;
+                               tmp16 = 0x96;
+                               gain[3] = 0x2;
+                       } else {
+                               vmid[3] = 0x89;
+                               tmp16 = 0x89;
+                               gain[3] = 0;
+                       }
+               } else {
+                       if (pdet_range == 4) {
+                               vmid[3] = 0x89;
+                               tmp16 = 0x8b;
+                               gain[3] = 0x2;
+                       } else {
+                               vmid[3] = 0x74;
+                               tmp16 = 0x70;
+                               gain[3] = 0;
+                       }
+               }
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x08), 4, vmid);
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x0c), 4, gain);
+               vmid[3] = tmp16;
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x18), 4, vmid);
+               b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x1c), 4, gain);
+               break;
+       }
 
        b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_MAST_BIAS, 0x00);
        b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_MAST_BIAS, 0x00);
@@ -2600,7 +2807,7 @@ static void b43_nphy_workarounds_rev3plus(struct b43_wldev *dev)
        /* Dropped probably-always-true condition */
        b43_phy_write(dev, B43_NPHY_ED_CRS40ASSERTTHRESH0, 0x03eb);
        b43_phy_write(dev, B43_NPHY_ED_CRS40ASSERTTHRESH1, 0x03eb);
-       b43_phy_write(dev, B43_NPHY_ED_CRS40DEASSERTTHRESH1, 0x0341);
+       b43_phy_write(dev, B43_NPHY_ED_CRS40DEASSERTTHRESH0, 0x0341);
        b43_phy_write(dev, B43_NPHY_ED_CRS40DEASSERTTHRESH1, 0x0341);
        b43_phy_write(dev, B43_NPHY_ED_CRS20LASSERTTHRESH0, 0x042b);
        b43_phy_write(dev, B43_NPHY_ED_CRS20LASSERTTHRESH1, 0x042b);
@@ -3211,6 +3418,20 @@ static void b43_nphy_tx_prepare_adjusted_power_table(struct b43_wldev *dev)
        u8 idx, delta;
        u8 i, stf_mode;
 
+       /* Array adj_pwr_tbl corresponds to the hardware table. It consists of
+        * 21 groups, each containing 4 entries.
+        *
+        * First group has entries for CCK modulation.
+        * The rest of groups has 1 entry per modulation (SISO, CDD, STBC, SDM).
+        *
+        * Group 0 is for CCK
+        * Groups 1..4 use BPSK (group per coding rate)
+        * Groups 5..8 use QPSK (group per coding rate)
+        * Groups 9..12 use 16-QAM (group per coding rate)
+        * Groups 13..16 use 64-QAM (group per coding rate)
+        * Groups 17..20 are unknown
+        */
+
        for (i = 0; i < 4; i++)
                nphy->adj_pwr_tbl[i] = nphy->tx_power_offset[i];
 
@@ -3409,10 +3630,8 @@ static void b43_nphy_tx_power_ctl_setup(struct b43_wldev *dev)
        }
 
        b43_nphy_tx_prepare_adjusted_power_table(dev);
-       /*
        b43_ntab_write_bulk(dev, B43_NTAB16(26, 64), 84, nphy->adj_pwr_tbl);
        b43_ntab_write_bulk(dev, B43_NTAB16(27, 64), 84, nphy->adj_pwr_tbl);
-       */
 
        if (nphy->hang_avoid)
                b43_nphy_stay_in_carrier_search(dev, false);
@@ -5124,7 +5343,7 @@ static int b43_phy_initn(struct b43_wldev *dev)
        b43_phy_write(dev, B43_NPHY_TXMACIF_HOLDOFF, 0x0015);
        b43_phy_write(dev, B43_NPHY_TXMACDELAY, 0x0320);
        if (phy->rev >= 3 && phy->rev <= 6)
-               b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x0014);
+               b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x0032);
        b43_nphy_tx_lp_fbw(dev);
        if (phy->rev >= 3)
                b43_nphy_spur_workaround(dev);
@@ -5338,7 +5557,6 @@ static void b43_nphy_op_prepare_structs(struct b43_wldev *dev)
        nphy->hang_avoid = (phy->rev == 3 || phy->rev == 4);
        nphy->spur_avoid = (phy->rev >= 3) ?
                                B43_SPUR_AVOID_AUTO : B43_SPUR_AVOID_DISABLE;
-       nphy->init_por = true;
        nphy->gain_boost = true; /* this way we follow wl, assume it is true */
        nphy->txrx_chain = 2; /* sth different than 0 and 1 for now */
        nphy->phyrxchain = 3; /* to avoid b43_nphy_set_rx_core_state like wl */
@@ -5379,8 +5597,6 @@ static void b43_nphy_op_prepare_structs(struct b43_wldev *dev)
                nphy->ipa2g_on = sprom->fem.ghz2.extpa_gain == 2;
                nphy->ipa5g_on = sprom->fem.ghz5.extpa_gain == 2;
        }
-
-       nphy->init_por = true;
 }
 
 static void b43_nphy_op_free(struct b43_wldev *dev)
@@ -5441,8 +5657,11 @@ static u16 b43_nphy_op_radio_read(struct b43_wldev *dev, u16 reg)
 {
        /* Register 1 is a 32-bit register. */
        B43_WARN_ON(reg == 1);
-       /* N-PHY needs 0x100 for read access */
-       reg |= 0x100;
+
+       if (dev->phy.rev >= 7)
+               reg |= 0x200; /* Radio 0x2057 */
+       else
+               reg |= 0x100;
 
        b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg);
        return b43_read16(dev, B43_MMIO_RADIO_DATA_LOW);
@@ -5488,10 +5707,12 @@ static void b43_nphy_op_software_rfkill(struct b43_wldev *dev,
                }
        } else {
                if (dev->phy.rev >= 7) {
-                       b43_radio_2057_init(dev);
+                       if (!dev->phy.radio_on)
+                               b43_radio_2057_init(dev);
                        b43_switch_channel(dev, dev->phy.channel);
                } else if (dev->phy.rev >= 3) {
-                       b43_radio_init2056(dev);
+                       if (!dev->phy.radio_on)
+                               b43_radio_init2056(dev);
                        b43_switch_channel(dev, dev->phy.channel);
                } else {
                        b43_radio_init2055(dev);
index 9a5b6bc27d24e5657d1e2ccda530283a4dc14da7..ecfbf66dbc3b7fdacc8d3e07dbf8d1476355fd16 100644 (file)
@@ -931,7 +931,6 @@ struct b43_phy_n {
        u16 papd_epsilon_offset[2];
        s32 preamble_override;
        u32 bb_mult_save;
-       bool init_por;
 
        bool gain_boost;
        bool elna_gain_config;
index b4fd9345d673a3542d94e66260ebc685eed7ef8d..2ce25607c60d389505ee3c9e664ee503adcd5406 100644 (file)
@@ -48,7 +48,7 @@ struct b2056_inittabs_pts {
        unsigned int rx_length;
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev3_syn[] = {
+static const struct b2056_inittab_entry b2056_inittab_phy_rev3_syn[] = {
        [B2056_SYN_RESERVED_ADDR2]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR3]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR4]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -232,7 +232,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev3_syn[] = {
        [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev3_tx[] = {
+static const struct b2056_inittab_entry b2056_inittab_phy_rev3_tx[] = {
        [B2056_TX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -380,7 +380,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev3_tx[] = {
        [B2056_TX_STATUS_TXLPF_RC]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev3_rx[] = {
+static const struct b2056_inittab_entry b2056_inittab_phy_rev3_rx[] = {
        [B2056_RX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -530,7 +530,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev3_rx[] = {
        [B2056_RX_STATUS_HPC_RC]        = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev4_syn[] = {
+static const struct b2056_inittab_entry b2056_inittab_phy_rev4_syn[] = {
        [B2056_SYN_RESERVED_ADDR2]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR3]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR4]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -714,7 +714,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev4_syn[] = {
        [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev4_tx[] = {
+static const struct b2056_inittab_entry b2056_inittab_phy_rev4_tx[] = {
        [B2056_TX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -862,7 +862,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev4_tx[] = {
        [B2056_TX_STATUS_TXLPF_RC]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev4_rx[] = {
+static const struct b2056_inittab_entry b2056_inittab_phy_rev4_rx[] = {
        [B2056_RX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -1012,7 +1012,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev4_rx[] = {
        [B2056_RX_STATUS_HPC_RC]        = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev5_syn[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev5_syn[] = {
        [B2056_SYN_RESERVED_ADDR2]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR3]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR4]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -1196,7 +1196,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev5_syn[] = {
        [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev5_tx[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev5_tx[] = {
        [B2056_TX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -1352,7 +1352,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev5_tx[] = {
        [B2056_TX_GMBB_IDAC7]           = { .ghz5 = 0x0075, .ghz2 = 0x0075, UPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev5_rx[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev5_rx[] = {
        [B2056_RX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -1502,7 +1502,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev5_rx[] = {
        [B2056_RX_STATUS_HPC_RC]        = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev6_syn[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev6_syn[] = {
        [B2056_SYN_RESERVED_ADDR2]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR3]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR4]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -1686,7 +1686,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev6_syn[] = {
        [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev6_tx[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev6_tx[] = {
        [B2056_TX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -1842,7 +1842,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev6_tx[] = {
        [B2056_TX_GMBB_IDAC7]           = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev6_rx[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev6_rx[] = {
        [B2056_RX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -1992,7 +1992,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev6_rx[] = {
        [B2056_RX_STATUS_HPC_RC]        = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev7_syn[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev7_9_syn[] = {
        [B2056_SYN_RESERVED_ADDR2]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR3]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR4]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -2176,7 +2176,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev7_syn[] = {
        [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev7_tx[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev7_9_tx[] = {
        [B2056_TX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -2332,7 +2332,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev7_tx[] = {
        [B2056_TX_GMBB_IDAC7]           = { .ghz5 = 0x0075, .ghz2 = 0x0075, UPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev7_rx[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev7_9_rx[] = {
        [B2056_RX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -2482,7 +2482,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev7_rx[] = {
        [B2056_RX_STATUS_HPC_RC]        = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev8_syn[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev8_syn[] = {
        [B2056_SYN_RESERVED_ADDR2]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR3]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_SYN_RESERVED_ADDR4]      = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -2666,7 +2666,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev8_syn[] = {
        [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev8_tx[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev8_tx[] = {
        [B2056_TX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_TX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -2822,7 +2822,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev8_tx[] = {
        [B2056_TX_GMBB_IDAC7]           = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, },
 };
 
-static const struct b2056_inittab_entry b2056_inittab_rev8_rx[] = {
+static const struct b2056_inittab_entry b2056_inittab_radio_rev8_rx[] = {
        [B2056_RX_RESERVED_ADDR2]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR3]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
        [B2056_RX_RESERVED_ADDR4]       = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
@@ -2972,24 +2972,69 @@ static const struct b2056_inittab_entry b2056_inittab_rev8_rx[] = {
        [B2056_RX_STATUS_HPC_RC]        = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, },
 };
 
-#define INITTABSPTS(prefix) \
-       .syn            = prefix##_syn,                 \
-       .syn_length     = ARRAY_SIZE(prefix##_syn),     \
-       .tx             = prefix##_tx,                  \
-       .tx_length      = ARRAY_SIZE(prefix##_tx),      \
-       .rx             = prefix##_rx,                  \
-       .rx_length      = ARRAY_SIZE(prefix##_rx)
+static const struct b2056_inittab_entry b2056_inittab_radio_rev11_syn[] = {
+       [B2056_SYN_PLL_PFD]             = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, },
+       [B2056_SYN_PLL_CP2]             = { .ghz5 = 0x003f, .ghz2 = 0x003f, UPLOAD, },
+       [B2056_SYN_PLL_LOOPFILTER1]     = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, },
+       [B2056_SYN_PLL_LOOPFILTER2]     = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, },
+       [B2056_SYN_PLL_LOOPFILTER4]     = { .ghz5 = 0x002b, .ghz2 = 0x002b, UPLOAD, },
+       [B2056_SYN_PLL_VCO2]            = { .ghz5 = 0x00f7, .ghz2 = 0x00f7, UPLOAD, },
+       [B2056_SYN_PLL_VCOCAL12]        = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, },
+       [B2056_SYN_LOGENBUF2]           = { .ghz5 = 0x008f, .ghz2 = 0x008f, UPLOAD, },
+};
 
-static const struct b2056_inittabs_pts b2056_inittabs[] = {
-       [3] = { INITTABSPTS(b2056_inittab_rev3) },
-       [4] = { INITTABSPTS(b2056_inittab_rev4) },
-       [5] = { INITTABSPTS(b2056_inittab_rev5) },
-       [6] = { INITTABSPTS(b2056_inittab_rev6) },
-       [7] = { INITTABSPTS(b2056_inittab_rev7) },
-       [8] = { INITTABSPTS(b2056_inittab_rev8) },
-       [9] = { INITTABSPTS(b2056_inittab_rev7) },
+static const struct b2056_inittab_entry b2056_inittab_radio_rev11_tx[] = {
+       [B2056_TX_PA_SPARE2]            = { .ghz5 = 0x00ee, .ghz2 = 0x00ee, UPLOAD, },
+       [B2056_TX_INTPAA_IAUX_STAT]     = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, },
+       [B2056_TX_INTPAA_IMAIN_STAT]    = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, },
+       [B2056_TX_INTPAA_PASLOPE]       = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, },
+       [B2056_TX_INTPAG_PASLOPE]       = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, },
+       [B2056_TX_PADA_IDAC]            = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, },
+       [B2056_TX_PADA_SLOPE]           = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, },
+       [B2056_TX_PADG_SLOPE]           = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, },
+       [B2056_TX_PGAA_IDAC]            = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, },
+       [B2056_TX_PGAA_SLOPE]           = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, },
+       [B2056_TX_PGAG_SLOPE]           = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, },
+       [B2056_TX_GMBB_IDAC]            = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, },
+       [B2056_TX_TXSPARE1]             = { .ghz5 = 0x0030, .ghz2 = 0x0030, UPLOAD, },
+};
+
+static const struct b2056_inittab_entry b2056_inittab_radio_rev11_rx[] = {
+       [B2056_RX_BIASPOLE_LNAA1_IDAC]  = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, },
+       [B2056_RX_LNAA2_IDAC]           = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, },
+       [B2056_RX_BIASPOLE_LNAG1_IDAC]  = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, },
+       [B2056_RX_LNAG2_IDAC]           = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, },
+       [B2056_RX_MIXA_VCM]             = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, },
+       [B2056_RX_MIXA_LOB_BIAS]        = { .ghz5 = 0x0088, .ghz2 = 0x0088, UPLOAD, },
+       [B2056_RX_MIXA_BIAS_AUX]        = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, },
+       [B2056_RX_MIXG_VCM]             = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, },
+       [B2056_RX_TIA_IOPAMP]           = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, },
+       [B2056_RX_TIA_QOPAMP]           = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, },
+       [B2056_RX_TIA_IMISC]            = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, },
+       [B2056_RX_TIA_QMISC]            = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, },
+       [B2056_RX_RXLPF_OUTVCM]         = { .ghz5 = 0x0004, .ghz2 = 0x0004, UPLOAD, },
+       [B2056_RX_VGA_BIAS_DCCANCEL]    = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, },
+       [B2056_RX_RXSPARE3]             = { .ghz5 = 0x0005, .ghz2 = 0x0005, UPLOAD, },
 };
 
+#define INITTABSPTS(prefix) \
+       static const struct b2056_inittabs_pts prefix = {       \
+               .syn            = prefix##_syn,                 \
+               .syn_length     = ARRAY_SIZE(prefix##_syn),     \
+               .tx             = prefix##_tx,                  \
+               .tx_length      = ARRAY_SIZE(prefix##_tx),      \
+               .rx             = prefix##_rx,                  \
+               .rx_length      = ARRAY_SIZE(prefix##_rx),      \
+       }
+
+INITTABSPTS(b2056_inittab_phy_rev3);
+INITTABSPTS(b2056_inittab_phy_rev4);
+INITTABSPTS(b2056_inittab_radio_rev5);
+INITTABSPTS(b2056_inittab_radio_rev6);
+INITTABSPTS(b2056_inittab_radio_rev7_9);
+INITTABSPTS(b2056_inittab_radio_rev8);
+INITTABSPTS(b2056_inittab_radio_rev11);
+
 #define RADIOREGS3(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \
                   r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, \
                   r20, r21, r22, r23, r24, r25, r26, r27, r28, r29, \
@@ -3041,7 +3086,7 @@ static const struct b2056_inittabs_pts b2056_inittabs[] = {
        .phy_regs.phy_bw6       = r5
 
 /* http://bcm-v4.sipsolutions.net/802.11/Radio/2056/ChannelTable */
-static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev3[] = {
+static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_phy_rev3[] = {
   {    .freq                   = 4920,
        RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04,
                   0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00,
@@ -4036,7 +4081,7 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev3[] =
   },
 };
 
-static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev4[] = {
+static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_phy_rev4[] = {
   {    .freq                   = 4920,
        RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04,
                   0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00,
@@ -5031,7 +5076,7 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev4[] =
   },
 };
 
-static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev5[] = {
+static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev5[] = {
   {    .freq                   = 4920,
        RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04,
                   0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00,
@@ -6026,7 +6071,7 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev5[] =
   },
 };
 
-static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev6[] = {
+static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev6[] = {
   {    .freq                   = 4920,
        RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04,
                   0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00,
@@ -7021,7 +7066,7 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev6[] =
   },
 };
 
-static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev7_9[] = {
+static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev7_9[] = {
   {    .freq                   = 4920,
        RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04,
                   0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00,
@@ -8016,7 +8061,7 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev7_9[]
   },
 };
 
-static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev8[] = {
+static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev8[] = {
   {    .freq                   = 4920,
        RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04,
                   0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00,
@@ -9011,6 +9056,1154 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev8[] =
   },
 };
 
+static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev11[] = {
+       {
+               .freq                   = 4920,
+               RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216),
+       },
+       {
+               .freq                   = 4930,
+               RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215),
+       },
+       {
+               .freq                   = 4940,
+               RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214),
+       },
+       {
+               .freq                   = 4950,
+               RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213),
+       },
+       {
+               .freq                   = 4960,
+               RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212),
+       },
+       {
+               .freq                   = 4970,
+               RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211),
+       },
+       {
+               .freq                   = 4980,
+               RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f),
+       },
+       {
+               .freq                   = 4990,
+               RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e),
+       },
+       {
+               .freq                   = 5000,
+               RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d),
+       },
+       {
+               .freq                   = 5010,
+               RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c),
+       },
+       {
+               .freq                   = 5020,
+               RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b),
+       },
+       {
+               .freq                   = 5030,
+               RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a),
+       },
+       {
+               .freq                   = 5040,
+               RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209),
+       },
+       {
+               .freq                   = 5050,
+               RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208),
+       },
+       {
+               .freq                   = 5060,
+               RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207),
+       },
+       {
+               .freq                   = 5070,
+               RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206),
+       },
+       {
+               .freq                   = 5080,
+               RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205),
+       },
+       {
+               .freq                   = 5090,
+               RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204),
+       },
+       {
+               .freq                   = 5100,
+               RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203),
+       },
+       {
+               .freq                   = 5110,
+               RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202),
+       },
+       {
+               .freq                   = 5120,
+               RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201),
+       },
+       {
+               .freq                   = 5130,
+               RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200),
+       },
+       {
+               .freq                   = 5140,
+               RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f,
+                          0x00, 0x6f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x77,
+                          0x00, 0x0f, 0x00, 0x6f, 0x00),
+               PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff),
+       },
+       {
+               .freq                   = 5160,
+               RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e,
+                          0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77,
+                          0x00, 0x0e, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd),
+       },
+       {
+               .freq                   = 5170,
+               RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00,
+                          0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e,
+                          0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77,
+                          0x00, 0x0e, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc),
+       },
+       {
+               .freq                   = 5180,
+               RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00,
+                          0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e,
+                          0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77,
+                          0x00, 0x0e, 0x00, 0x6f, 0x00),
+               PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb),
+       },
+       {
+               .freq                   = 5190,
+               RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00,
+                          0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d,
+                          0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77,
+                          0x00, 0x0d, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa),
+       },
+       {
+               .freq                   = 5200,
+               RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00,
+                          0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d,
+                          0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77,
+                          0x00, 0x0d, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9),
+       },
+       {
+               .freq                   = 5210,
+               RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00,
+                          0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d,
+                          0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77,
+                          0x00, 0x0d, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8),
+       },
+       {
+               .freq                   = 5220,
+               RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00,
+                          0xfe, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d,
+                          0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77,
+                          0x00, 0x0d, 0x00, 0x6f, 0x00),
+               PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7),
+       },
+       {
+               .freq                   = 5230,
+               RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00,
+                          0xee, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d,
+                          0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77,
+                          0x00, 0x0d, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6),
+       },
+       {
+               .freq                   = 5240,
+               RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00,
+                          0xee, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d,
+                          0x00, 0x6f, 0x00, 0xc8, 0x00, 0x05, 0x00, 0x77,
+                          0x00, 0x0d, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5),
+       },
+       {
+               .freq                   = 5250,
+               RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00,
+                          0xed, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d,
+                          0x00, 0x6f, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x77,
+                          0x00, 0x0d, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4),
+       },
+       {
+               .freq                   = 5260,
+               RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00,
+                          0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d,
+                          0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77,
+                          0x00, 0x0d, 0x00, 0x6f, 0x00),
+               PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3),
+       },
+       {
+               .freq                   = 5270,
+               RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00,
+                          0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c,
+                          0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77,
+                          0x00, 0x0c, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2),
+       },
+       {
+               .freq                   = 5280,
+               RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00,
+                          0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c,
+                          0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0c, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1),
+       },
+       {
+               .freq                   = 5290,
+               RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00,
+                          0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c,
+                          0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0c, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0),
+       },
+       {
+               .freq                   = 5300,
+               RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00,
+                          0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c,
+                          0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0c, 0x00, 0x6f, 0x00),
+               PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0),
+       },
+       {
+               .freq                   = 5310,
+               RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00,
+                          0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c,
+                          0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0c, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef),
+       },
+       {
+               .freq                   = 5320,
+               RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00,
+                          0xdb, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c,
+                          0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0c, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee),
+       },
+       {
+               .freq                   = 5330,
+               RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00,
+                          0xcb, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b,
+                          0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0b, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed),
+       },
+       {
+               .freq                   = 5340,
+               RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00,
+                          0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b,
+                          0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0b, 0x00, 0x6f, 0x00),
+               PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec),
+       },
+       {
+               .freq                   = 5350,
+               RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00,
+                          0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b,
+                          0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0b, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb),
+       },
+       {
+               .freq                   = 5360,
+               RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00,
+                          0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a,
+                          0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0a, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea),
+       },
+       {
+               .freq                   = 5370,
+               RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00,
+                          0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a,
+                          0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0a, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9),
+       },
+       {
+               .freq                   = 5380,
+               RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00,
+                          0xb8, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a,
+                          0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0a, 0x00, 0x6f, 0x00),
+               PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8),
+       },
+       {
+               .freq                   = 5390,
+               RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00,
+                          0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a,
+                          0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0a, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7),
+       },
+       {
+               .freq                   = 5400,
+               RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00,
+                          0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a,
+                          0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77,
+                          0x00, 0x0a, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6),
+       },
+       {
+               .freq                   = 5410,
+               RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00,
+                          0xb7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a,
+                          0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77,
+                          0x00, 0x0a, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5),
+       },
+       {
+               .freq                   = 5420,
+               RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00,
+                          0xa7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a,
+                          0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77,
+                          0x00, 0x0a, 0x00, 0x6f, 0x00),
+               PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5),
+       },
+       {
+               .freq                   = 5430,
+               RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00,
+                          0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a,
+                          0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77,
+                          0x00, 0x0a, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4),
+       },
+       {
+               .freq                   = 5440,
+               RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00,
+                          0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3),
+       },
+       {
+               .freq                   = 5450,
+               RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00,
+                          0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2),
+       },
+       {
+               .freq                   = 5460,
+               RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00,
+                          0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1),
+       },
+       {
+               .freq                   = 5470,
+               RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00,
+                          0x94, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x73, 0x00, 0x01, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0),
+       },
+       {
+               .freq                   = 5480,
+               RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00,
+                          0x84, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df),
+       },
+       {
+               .freq                   = 5490,
+               RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00,
+                          0x83, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de),
+       },
+       {
+               .freq                   = 5500,
+               RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00,
+                          0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd),
+       },
+       {
+               .freq                   = 5510,
+               RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00,
+                          0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd),
+       },
+       {
+               .freq                   = 5520,
+               RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00,
+                          0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc),
+       },
+       {
+               .freq                   = 5530,
+               RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00,
+                          0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db),
+       },
+       {
+               .freq                   = 5540,
+               RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00,
+                          0x71, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da),
+       },
+       {
+               .freq                   = 5550,
+               RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00,
+                          0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9),
+       },
+       {
+               .freq                   = 5560,
+               RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00,
+                          0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8),
+       },
+       {
+               .freq                   = 5570,
+               RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00,
+                          0x61, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09,
+                          0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x09, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7),
+       },
+       {
+               .freq                   = 5580,
+               RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00,
+                          0x60, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08,
+                          0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x08, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7),
+       },
+       {
+               .freq                   = 5590,
+               RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00,
+                          0x50, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08,
+                          0x00, 0x6f, 0x00, 0x61, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x08, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6),
+       },
+       {
+               .freq                   = 5600,
+               RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00,
+                          0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08,
+                          0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x08, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5),
+       },
+       {
+               .freq                   = 5610,
+               RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00,
+                          0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08,
+                          0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x08, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4),
+       },
+       {
+               .freq                   = 5620,
+               RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00,
+                          0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07,
+                          0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x07, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3),
+       },
+       {
+               .freq                   = 5630,
+               RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00,
+                          0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07,
+                          0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x07, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2),
+       },
+       {
+               .freq                   = 5640,
+               RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00,
+                          0x40, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07,
+                          0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x07, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2),
+       },
+       {
+               .freq                   = 5650,
+               RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00,
+                          0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07,
+                          0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x07, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1),
+       },
+       {
+               .freq                   = 5660,
+               RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00,
+                          0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0),
+       },
+       {
+               .freq                   = 5670,
+               RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00,
+                          0x40, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf),
+       },
+       {
+               .freq                   = 5680,
+               RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00,
+                          0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce),
+       },
+       {
+               .freq                   = 5690,
+               RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00,
+                          0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6f, 0x00),
+               PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce),
+       },
+       {
+               .freq                   = 5700,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00,
+                          0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6e, 0x00),
+               PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd),
+       },
+       {
+               .freq                   = 5710,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00,
+                          0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6e, 0x00),
+               PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc),
+       },
+       {
+               .freq                   = 5720,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00,
+                          0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6e, 0x00),
+               PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb),
+       },
+       {
+               .freq                   = 5725,
+               RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x05, 0x05, 0x02,
+                          0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00,
+                          0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6e, 0x00),
+               PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb),
+       },
+       {
+               .freq                   = 5730,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00,
+                          0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6e, 0x00),
+               PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca),
+       },
+       {
+               .freq                   = 5735,
+               RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x05, 0x05, 0x02,
+                          0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00,
+                          0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6d, 0x00),
+               PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca),
+       },
+       {
+               .freq                   = 5740,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00,
+                          0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6d, 0x00),
+               PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9),
+       },
+       {
+               .freq                   = 5745,
+               RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x05, 0x05, 0x02,
+                          0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00,
+                          0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06,
+                          0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x06, 0x00, 0x6d, 0x00),
+               PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9),
+       },
+       {
+               .freq                   = 5750,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00,
+                          0x20, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6d, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6d, 0x00),
+               PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9),
+       },
+       {
+               .freq                   = 5755,
+               RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x05, 0x05, 0x02,
+                          0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00,
+                          0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6c, 0x00),
+               PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8),
+       },
+       {
+               .freq                   = 5760,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00,
+                          0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6c, 0x00),
+               PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8),
+       },
+       {
+               .freq                   = 5765,
+               RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x05, 0x05, 0x02,
+                          0x15, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00,
+                          0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6c, 0x00),
+               PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8),
+       },
+       {
+               .freq                   = 5770,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00,
+                          0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6b, 0x00),
+               PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7),
+       },
+       {
+               .freq                   = 5775,
+               RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x05, 0x05, 0x02,
+                          0x15, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00,
+                          0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6b, 0x00),
+               PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7),
+       },
+       {
+               .freq                   = 5780,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00,
+                          0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6b, 0x00),
+               PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6),
+       },
+       {
+               .freq                   = 5785,
+               RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x05, 0x05, 0x02,
+                          0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00,
+                          0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6b, 0x00),
+               PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6),
+       },
+       {
+               .freq                   = 5790,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00,
+                          0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6b, 0x00),
+               PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6),
+       },
+       {
+               .freq                   = 5795,
+               RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x05, 0x05, 0x02,
+                          0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6b, 0x00),
+               PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5),
+       },
+       {
+               .freq                   = 5800,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6b, 0x00),
+               PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5),
+       },
+       {
+               .freq                   = 5805,
+               RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x05, 0x05, 0x02,
+                          0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6a, 0x00),
+               PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4),
+       },
+       {
+               .freq                   = 5810,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6a, 0x00),
+               PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4),
+       },
+       {
+               .freq                   = 5815,
+               RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x05, 0x05, 0x02,
+                          0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6a, 0x00),
+               PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4),
+       },
+       {
+               .freq                   = 5820,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x6a, 0x00),
+               PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3),
+       },
+       {
+               .freq                   = 5825,
+               RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x05, 0x05, 0x02,
+                          0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x69, 0x00),
+               PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3),
+       },
+       {
+               .freq                   = 5830,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05,
+                          0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x05, 0x00, 0x69, 0x00),
+               PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2),
+       },
+       {
+               .freq                   = 5840,
+               RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04,
+                          0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x04, 0x00, 0x69, 0x00),
+               PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2),
+       },
+       {
+               .freq                   = 5850,
+               RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04,
+                          0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x04, 0x00, 0x69, 0x00),
+               PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1),
+       },
+       {
+               .freq                   = 5860,
+               RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04,
+                          0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x04, 0x00, 0x69, 0x00),
+               PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0),
+       },
+       {
+               .freq                   = 5870,
+               RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04,
+                          0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x04, 0x00, 0x68, 0x00),
+               PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf),
+       },
+       {
+               .freq                   = 5880,
+               RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04,
+                          0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x04, 0x00, 0x68, 0x00),
+               PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf),
+       },
+       {
+               .freq                   = 5890,
+               RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04,
+                          0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x04, 0x00, 0x68, 0x00),
+               PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be),
+       },
+       {
+               .freq                   = 5900,
+               RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04,
+                          0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x04, 0x00, 0x68, 0x00),
+               PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd),
+       },
+       {
+               .freq                   = 5910,
+               RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x02,
+                          0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04,
+                          0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+                          0x00, 0x04, 0x00, 0x68, 0x00),
+               PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc),
+       },
+       {
+               .freq                   = 2412,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00,
+                          0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00,
+                          0x70, 0x00, 0x0b, 0x00, 0x0a),
+               PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443),
+       },
+       {
+               .freq                   = 2417,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x71, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00,
+                          0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00,
+                          0x70, 0x00, 0x0b, 0x00, 0x0a),
+               PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441),
+       },
+       {
+               .freq                   = 2422,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x76, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x67, 0x00, 0x03, 0x00, 0x70, 0x00,
+                          0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00,
+                          0x70, 0x00, 0x0b, 0x00, 0x0a),
+               PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f),
+       },
+       {
+               .freq                   = 2427,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x7b, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x57, 0x00, 0x03, 0x00, 0x70, 0x00,
+                          0x0a, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00,
+                          0x70, 0x00, 0x0a, 0x00, 0x0a),
+               PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d),
+       },
+       {
+               .freq                   = 2432,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x80, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x56, 0x00, 0x03, 0x00, 0x70, 0x00,
+                          0x0a, 0x00, 0x0a, 0x00, 0x77, 0x00, 0x03, 0x00,
+                          0x70, 0x00, 0x0a, 0x00, 0x0a),
+               PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a),
+       },
+       {
+               .freq                   = 2437,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x85, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x46, 0x00, 0x03, 0x00, 0x70, 0x00,
+                          0x0a, 0x00, 0x0a, 0x00, 0x76, 0x00, 0x03, 0x00,
+                          0x70, 0x00, 0x0a, 0x00, 0x0a),
+               PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438),
+       },
+       {
+               .freq                   = 2442,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8a, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00,
+                          0x0a, 0x00, 0x0a, 0x00, 0x66, 0x00, 0x02, 0x00,
+                          0x70, 0x00, 0x0a, 0x00, 0x0a),
+               PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436),
+       },
+       {
+               .freq                   = 2447,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8f, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00,
+                          0x0a, 0x00, 0x09, 0x00, 0x55, 0x00, 0x02, 0x00,
+                          0x70, 0x00, 0x0a, 0x00, 0x09),
+               PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434),
+       },
+       {
+               .freq                   = 2452,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x94, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x23, 0x00, 0x02, 0x00, 0x70, 0x00,
+                          0x0a, 0x00, 0x09, 0x00, 0x45, 0x00, 0x02, 0x00,
+                          0x70, 0x00, 0x0a, 0x00, 0x09),
+               PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431),
+       },
+       {
+               .freq                   = 2457,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x99, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x12, 0x00, 0x02, 0x00, 0x70, 0x00,
+                          0x0a, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00,
+                          0x70, 0x00, 0x0a, 0x00, 0x09),
+               PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f),
+       },
+       {
+               .freq                   = 2462,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x9e, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x70, 0x00,
+                          0x09, 0x00, 0x09, 0x00, 0x33, 0x00, 0x02, 0x00,
+                          0x70, 0x00, 0x09, 0x00, 0x09),
+               PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d),
+       },
+       {
+               .freq                   = 2467,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa3, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00,
+                          0x09, 0x00, 0x09, 0x00, 0x22, 0x00, 0x02, 0x00,
+                          0x70, 0x00, 0x09, 0x00, 0x09),
+               PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b),
+       },
+       {
+               .freq                   = 2472,
+               RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa8, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00,
+                          0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00,
+                          0x09, 0x00, 0x09, 0x00, 0x11, 0x00, 0x02, 0x00,
+                          0x70, 0x00, 0x09, 0x00, 0x09),
+               PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429),
+       },
+       {
+               .freq                   = 2484,
+               RADIOREGS3(0xff, 0x01, 0x03, 0x09, 0xb4, 0x06, 0x06, 0x04,
+                          0x2b, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00,
+                          0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00,
+                          0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00,
+                          0x70, 0x00, 0x09, 0x00, 0x09),
+               PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424),
+       },
+};
+
+static const struct b2056_inittabs_pts
+*b43_nphy_get_inittabs_rev3(struct b43_wldev *dev)
+{
+       struct b43_phy *phy = &dev->phy;
+
+       switch (dev->phy.rev) {
+       case 3:
+               return &b2056_inittab_phy_rev3;
+       case 4:
+               return &b2056_inittab_phy_rev4;
+       default:
+               switch (phy->radio_rev) {
+               case 5:
+                       return &b2056_inittab_radio_rev5;
+               case 6:
+                       return &b2056_inittab_radio_rev6;
+               case 7:
+               case 9:
+                       return &b2056_inittab_radio_rev7_9;
+               case 8:
+                       return &b2056_inittab_radio_rev8;
+               case 11:
+                       return &b2056_inittab_radio_rev11;
+               }
+       }
+
+       return NULL;
+}
+
 static void b2056_upload_inittab(struct b43_wldev *dev, bool ghz5,
                                 bool ignore_uploadflag, u16 routing,
                                 const struct b2056_inittab_entry *e,
@@ -9037,11 +10230,11 @@ void b2056_upload_inittabs(struct b43_wldev *dev,
 {
        const struct b2056_inittabs_pts *pts;
 
-       if (dev->phy.rev >= ARRAY_SIZE(b2056_inittabs)) {
+       pts = b43_nphy_get_inittabs_rev3(dev);
+       if (!pts) {
                B43_WARN_ON(1);
                return;
        }
-       pts = &b2056_inittabs[dev->phy.rev];
 
        b2056_upload_inittab(dev, ghz5, ignore_uploadflag,
                                B2056_SYN, pts->syn, pts->syn_length);
@@ -9060,11 +10253,12 @@ void b2056_upload_syn_pll_cp2(struct b43_wldev *dev, bool ghz5)
        const struct b2056_inittabs_pts *pts;
        const struct b2056_inittab_entry *e;
 
-       if (dev->phy.rev >= ARRAY_SIZE(b2056_inittabs)) {
+       pts = b43_nphy_get_inittabs_rev3(dev);
+       if (!pts) {
                B43_WARN_ON(1);
                return;
        }
-       pts = &b2056_inittabs[dev->phy.rev];
+
        e = &pts->syn[B2056_SYN_PLL_CP2];
 
        b43_radio_write(dev, B2056_SYN_PLL_CP2, ghz5 ? e->ghz5 : e->ghz2);
@@ -9073,38 +10267,46 @@ void b2056_upload_syn_pll_cp2(struct b43_wldev *dev, bool ghz5)
 const struct b43_nphy_channeltab_entry_rev3 *
 b43_nphy_get_chantabent_rev3(struct b43_wldev *dev, u16 freq)
 {
+       struct b43_phy *phy = &dev->phy;
        const struct b43_nphy_channeltab_entry_rev3 *e;
        unsigned int length, i;
 
-       switch (dev->phy.rev) {
+       switch (phy->rev) {
        case 3:
-               e = b43_nphy_channeltab_rev3;
-               length = ARRAY_SIZE(b43_nphy_channeltab_rev3);
+               e = b43_nphy_channeltab_phy_rev3;
+               length = ARRAY_SIZE(b43_nphy_channeltab_phy_rev3);
                break;
        case 4:
-               e = b43_nphy_channeltab_rev4;
-               length = ARRAY_SIZE(b43_nphy_channeltab_rev4);
-               break;
-       case 5:
-               e = b43_nphy_channeltab_rev5;
-               length = ARRAY_SIZE(b43_nphy_channeltab_rev5);
-               break;
-       case 6:
-               e = b43_nphy_channeltab_rev6;
-               length = ARRAY_SIZE(b43_nphy_channeltab_rev6);
-               break;
-       case 7:
-       case 9:
-               e = b43_nphy_channeltab_rev7_9;
-               length = ARRAY_SIZE(b43_nphy_channeltab_rev7_9);
-               break;
-       case 8:
-               e = b43_nphy_channeltab_rev8;
-               length = ARRAY_SIZE(b43_nphy_channeltab_rev8);
+               e = b43_nphy_channeltab_phy_rev4;
+               length = ARRAY_SIZE(b43_nphy_channeltab_phy_rev4);
                break;
        default:
-               B43_WARN_ON(1);
-               return NULL;
+               switch (phy->radio_rev) {
+               case 5:
+                       e = b43_nphy_channeltab_radio_rev5;
+                       length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev5);
+                       break;
+               case 6:
+                       e = b43_nphy_channeltab_radio_rev6;
+                       length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev6);
+                       break;
+               case 7:
+               case 9:
+                       e = b43_nphy_channeltab_radio_rev7_9;
+                       length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev7_9);
+                       break;
+               case 8:
+                       e = b43_nphy_channeltab_radio_rev8;
+                       length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev8);
+                       break;
+               case 11:
+                       e = b43_nphy_channeltab_radio_rev11;
+                       length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev11);
+                       break;
+               default:
+                       B43_WARN_ON(1);
+                       return NULL;
+               }
        }
 
        for (i = 0; i < length; i++, e++) {
index 94c755fdda14749eaa36716032283b8cb9fea7d2..4047c05e380759f4f0b64bbdc5188f04efcce242 100644 (file)
@@ -1627,74 +1627,7 @@ static const u32 b43_ntab_tdtrn_r3[] = {
        0xfa58fc00, 0x0b64fc7e, 0x0800f7b6, 0x00f006be,
 };
 
-static const u32 b43_ntab_noisevar0_r3[] = {
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-       0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
-};
-
-static const u32 b43_ntab_noisevar1_r3[] = {
+static const u32 b43_ntab_noisevar_r3[] = {
        0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
        0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
        0x02110211, 0x0000014d, 0x02110211, 0x0000014d,
@@ -3109,31 +3042,32 @@ static void b43_nphy_tables_init_rev3(struct b43_wldev *dev)
                antswlut = sprom->fem.ghz2.antswlut;
 
        /* Static tables */
-       ntab_upload(dev, B43_NTAB_FRAMESTRUCT_R3, b43_ntab_framestruct_r3);
-       ntab_upload(dev, B43_NTAB_PILOT_R3, b43_ntab_pilot_r3);
-       ntab_upload(dev, B43_NTAB_TMAP_R3, b43_ntab_tmap_r3);
-       ntab_upload(dev, B43_NTAB_INTLEVEL_R3, b43_ntab_intlevel_r3);
-       ntab_upload(dev, B43_NTAB_TDTRN_R3, b43_ntab_tdtrn_r3);
-       ntab_upload(dev, B43_NTAB_NOISEVAR0_R3, b43_ntab_noisevar0_r3);
-       ntab_upload(dev, B43_NTAB_NOISEVAR1_R3, b43_ntab_noisevar1_r3);
-       ntab_upload(dev, B43_NTAB_MCS_R3, b43_ntab_mcs_r3);
-       ntab_upload(dev, B43_NTAB_TDI20A0_R3, b43_ntab_tdi20a0_r3);
-       ntab_upload(dev, B43_NTAB_TDI20A1_R3, b43_ntab_tdi20a1_r3);
-       ntab_upload(dev, B43_NTAB_TDI40A0_R3, b43_ntab_tdi40a0_r3);
-       ntab_upload(dev, B43_NTAB_TDI40A1_R3, b43_ntab_tdi40a1_r3);
-       ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3);
-       ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3);
-       ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3);
-       ntab_upload(dev, B43_NTAB_C0_ESTPLT_R3, b43_ntab_estimatepowerlt0_r3);
-       ntab_upload(dev, B43_NTAB_C1_ESTPLT_R3, b43_ntab_estimatepowerlt1_r3);
-       ntab_upload(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3);
-       ntab_upload(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3);
-       ntab_upload(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3);
-       ntab_upload(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3);
-       ntab_upload(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3);
-       ntab_upload(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3);
-       ntab_upload(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3);
-       ntab_upload(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3);
+       if (dev->phy.do_full_init) {
+               ntab_upload(dev, B43_NTAB_FRAMESTRUCT_R3, b43_ntab_framestruct_r3);
+               ntab_upload(dev, B43_NTAB_PILOT_R3, b43_ntab_pilot_r3);
+               ntab_upload(dev, B43_NTAB_TMAP_R3, b43_ntab_tmap_r3);
+               ntab_upload(dev, B43_NTAB_INTLEVEL_R3, b43_ntab_intlevel_r3);
+               ntab_upload(dev, B43_NTAB_TDTRN_R3, b43_ntab_tdtrn_r3);
+               ntab_upload(dev, B43_NTAB_NOISEVAR_R3, b43_ntab_noisevar_r3);
+               ntab_upload(dev, B43_NTAB_MCS_R3, b43_ntab_mcs_r3);
+               ntab_upload(dev, B43_NTAB_TDI20A0_R3, b43_ntab_tdi20a0_r3);
+               ntab_upload(dev, B43_NTAB_TDI20A1_R3, b43_ntab_tdi20a1_r3);
+               ntab_upload(dev, B43_NTAB_TDI40A0_R3, b43_ntab_tdi40a0_r3);
+               ntab_upload(dev, B43_NTAB_TDI40A1_R3, b43_ntab_tdi40a1_r3);
+               ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3);
+               ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3);
+               ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3);
+               ntab_upload(dev, B43_NTAB_C0_ESTPLT_R3, b43_ntab_estimatepowerlt0_r3);
+               ntab_upload(dev, B43_NTAB_C1_ESTPLT_R3, b43_ntab_estimatepowerlt1_r3);
+               ntab_upload(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3);
+               ntab_upload(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3);
+               ntab_upload(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3);
+               ntab_upload(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3);
+               ntab_upload(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3);
+               ntab_upload(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3);
+               ntab_upload(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3);
+               ntab_upload(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3);
+       }
 
        /* Volatile tables */
        if (antswlut < ARRAY_SIZE(b43_ntab_antswctl_r3))
@@ -3146,20 +3080,22 @@ static void b43_nphy_tables_init_rev3(struct b43_wldev *dev)
 static void b43_nphy_tables_init_rev0(struct b43_wldev *dev)
 {
        /* Static tables */
-       ntab_upload(dev, B43_NTAB_FRAMESTRUCT, b43_ntab_framestruct);
-       ntab_upload(dev, B43_NTAB_FRAMELT, b43_ntab_framelookup);
-       ntab_upload(dev, B43_NTAB_TMAP, b43_ntab_tmap);
-       ntab_upload(dev, B43_NTAB_TDTRN, b43_ntab_tdtrn);
-       ntab_upload(dev, B43_NTAB_INTLEVEL, b43_ntab_intlevel);
-       ntab_upload(dev, B43_NTAB_PILOT, b43_ntab_pilot);
-       ntab_upload(dev, B43_NTAB_TDI20A0, b43_ntab_tdi20a0);
-       ntab_upload(dev, B43_NTAB_TDI20A1, b43_ntab_tdi20a1);
-       ntab_upload(dev, B43_NTAB_TDI40A0, b43_ntab_tdi40a0);
-       ntab_upload(dev, B43_NTAB_TDI40A1, b43_ntab_tdi40a1);
-       ntab_upload(dev, B43_NTAB_CHANEST, b43_ntab_channelest);
-       ntab_upload(dev, B43_NTAB_MCS, b43_ntab_mcs);
-       ntab_upload(dev, B43_NTAB_NOISEVAR10, b43_ntab_noisevar10);
-       ntab_upload(dev, B43_NTAB_NOISEVAR11, b43_ntab_noisevar11);
+       if (dev->phy.do_full_init) {
+               ntab_upload(dev, B43_NTAB_FRAMESTRUCT, b43_ntab_framestruct);
+               ntab_upload(dev, B43_NTAB_FRAMELT, b43_ntab_framelookup);
+               ntab_upload(dev, B43_NTAB_TMAP, b43_ntab_tmap);
+               ntab_upload(dev, B43_NTAB_TDTRN, b43_ntab_tdtrn);
+               ntab_upload(dev, B43_NTAB_INTLEVEL, b43_ntab_intlevel);
+               ntab_upload(dev, B43_NTAB_PILOT, b43_ntab_pilot);
+               ntab_upload(dev, B43_NTAB_TDI20A0, b43_ntab_tdi20a0);
+               ntab_upload(dev, B43_NTAB_TDI20A1, b43_ntab_tdi20a1);
+               ntab_upload(dev, B43_NTAB_TDI40A0, b43_ntab_tdi40a0);
+               ntab_upload(dev, B43_NTAB_TDI40A1, b43_ntab_tdi40a1);
+               ntab_upload(dev, B43_NTAB_CHANEST, b43_ntab_channelest);
+               ntab_upload(dev, B43_NTAB_MCS, b43_ntab_mcs);
+               ntab_upload(dev, B43_NTAB_NOISEVAR10, b43_ntab_noisevar10);
+               ntab_upload(dev, B43_NTAB_NOISEVAR11, b43_ntab_noisevar11);
+       }
 
        /* Volatile tables */
        ntab_upload(dev, B43_NTAB_BDI, b43_ntab_bdi);
index 9ff33adcff891cad9551bafab507924a59a54afb..3a58aee4c4cf714aa72bc22a8c85670b135eb934 100644 (file)
@@ -143,8 +143,7 @@ struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent(
 #define B43_NTAB_TMAP_R3               B43_NTAB32(12,   0) /* TM AP  */
 #define B43_NTAB_INTLEVEL_R3           B43_NTAB32(13,   0) /* INT LV  */
 #define B43_NTAB_TDTRN_R3              B43_NTAB32(14,   0) /* TD TRN  */
-#define B43_NTAB_NOISEVAR0_R3          B43_NTAB32(16,   0) /* noise variance 0  */
-#define B43_NTAB_NOISEVAR1_R3          B43_NTAB32(16, 128) /* noise variance 1  */
+#define B43_NTAB_NOISEVAR_R3           B43_NTAB32(16,   0) /* noise variance */
 #define B43_NTAB_MCS_R3                        B43_NTAB16(18,   0) /* MCS  */
 #define B43_NTAB_TDI20A0_R3            B43_NTAB32(19, 128) /* TDI 20/0  */
 #define B43_NTAB_TDI20A1_R3            B43_NTAB32(19, 256) /* TDI 20/1  */
index 9b1a038be08b860da91461b82c6bd84dd5000531..c218c08fb2f5b15ad3233b475b5d275d2f4ba0df 100644 (file)
@@ -441,7 +441,7 @@ static void b43_wa_altagc(struct b43_wldev *dev)
 
 static void b43_wa_tr_ltov(struct b43_wldev *dev) /* TR Lookup Table Original Values */
 {
-       b43_gtab_write(dev, B43_GTAB_ORIGTR, 0, 0xC480);
+       b43_gtab_write(dev, B43_GTAB_ORIGTR, 0, 0x7654);
 }
 
 static void b43_wa_cpll_nonpilot(struct b43_wldev *dev)
index 31adb8cf0291fb0bbebf1a6896c8f4ff4d7288b9..4f38f19b8e3d373847778766ec8ad820e5d1d2bd 100644 (file)
@@ -408,7 +408,7 @@ int b43_generate_txhdr(struct b43_wldev *dev,
                mac_ctl |= B43_TXH_MAC_HWSEQ;
        if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
                mac_ctl |= B43_TXH_MAC_STMSDU;
-       if (phy->type == B43_PHYTYPE_A)
+       if (!phy->gmode)
                mac_ctl |= B43_TXH_MAC_5GHZ;
 
        /* Overwrite rates[0].count to make the retry calculation
index 1d2ceac3a221bd779320daf540bc49ecc9b219c5..98e67c18f276471d804e751122ae63c2f26662bf 100644 (file)
@@ -33,7 +33,7 @@ brcmfmac-objs += \
                bcdc.o \
                dhd_common.o \
                dhd_linux.o \
-               nvram.o \
+               firmware.o \
                btcoex.o
 brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \
                dhd_sdio.o \
index 939d6b13292248563f92bc4287632341c4997072..16f9ab2568a8089c1c38eff8f8998e8fc29ee330 100644 (file)
@@ -186,7 +186,7 @@ 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);
 u32 brcmf_get_chip_info(struct brcmf_if *ifp);
-void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp,
+void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx,
                      bool success);
 
 /* Sets dongle media info (drv_version, mac address). */
index c4535616064e8389125b238a50fcc74d3c6d47da..7735328fff21e6da15fd8e8a801f63638b6594b9 100644 (file)
@@ -63,7 +63,6 @@ struct brcmf_bus_dcmd {
  */
 struct brcmf_bus_ops {
        int (*preinit)(struct device *dev);
-       int (*init)(struct device *dev);
        void (*stop)(struct device *dev);
        int (*txdata)(struct device *dev, struct sk_buff *skb);
        int (*txctl)(struct device *dev, unsigned char *msg, uint len);
@@ -99,6 +98,7 @@ struct brcmf_bus {
        unsigned long tx_realloc;
        u32 chip;
        u32 chiprev;
+       bool always_use_fws_queue;
 
        struct brcmf_bus_ops *ops;
 };
@@ -113,11 +113,6 @@ static inline int brcmf_bus_preinit(struct brcmf_bus *bus)
        return bus->ops->preinit(bus->dev);
 }
 
-static inline int brcmf_bus_init(struct brcmf_bus *bus)
-{
-       return bus->ops->init(bus->dev);
-}
-
 static inline void brcmf_bus_stop(struct brcmf_bus *bus)
 {
        bus->ops->stop(bus->dev);
index 6a8983a1fb9c3451908c1cf28426d78e6600d091..ed3e32ce8c23ee8fd032ad35520c7fee1e3cb18a 100644 (file)
@@ -32,6 +32,9 @@
 #define BRCMF_DEFAULT_SCAN_UNASSOC_TIME        40
 #define BRCMF_DEFAULT_PACKET_FILTER    "100 0 0 0 0x01 0x00"
 
+/* boost value for RSSI_DELTA in preferred join selection */
+#define BRCMF_JOIN_PREF_RSSI_BOOST     8
+
 
 bool brcmf_c_prec_enq(struct device *dev, struct pktq *q,
                      struct sk_buff *pkt, int prec)
@@ -246,6 +249,7 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
 {
        s8 eventmask[BRCMF_EVENTING_MASK_LEN];
        u8 buf[BRCMF_DCMD_SMLEN];
+       struct brcmf_join_pref_params join_pref_params[2];
        char *ptr;
        s32 err;
 
@@ -298,6 +302,20 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
                goto done;
        }
 
+       /* Setup join_pref to select target by RSSI(with boost on 5GHz) */
+       join_pref_params[0].type = BRCMF_JOIN_PREF_RSSI_DELTA;
+       join_pref_params[0].len = 2;
+       join_pref_params[0].rssi_gain = BRCMF_JOIN_PREF_RSSI_BOOST;
+       join_pref_params[0].band = WLC_BAND_5G;
+       join_pref_params[1].type = BRCMF_JOIN_PREF_RSSI;
+       join_pref_params[1].len = 2;
+       join_pref_params[1].rssi_gain = 0;
+       join_pref_params[1].band = 0;
+       err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params,
+                                      sizeof(join_pref_params));
+       if (err)
+               brcmf_err("Set join_pref error (%d)\n", err);
+
        /* Setup event_msgs, enable E_IF */
        err = brcmf_fil_iovar_data_get(ifp, "event_msgs", eventmask,
                                       BRCMF_EVENTING_MASK_LEN);
index 7d28cd3850925a7af0a4b9f34c2888afa83e0d03..09dd8c13d8448392372299c6eb8954132fca9c72 100644 (file)
@@ -190,7 +190,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
        int ret;
        struct brcmf_if *ifp = netdev_priv(ndev);
        struct brcmf_pub *drvr = ifp->drvr;
-       struct ethhdr *eh;
+       struct ethhdr *eh = (struct ethhdr *)(skb->data);
 
        brcmf_dbg(DATA, "Enter, idx=%d\n", ifp->bssidx);
 
@@ -236,6 +236,9 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
                goto done;
        }
 
+       if (eh->h_proto == htons(ETH_P_PAE))
+               atomic_inc(&ifp->pend_8021x_cnt);
+
        ret = brcmf_fws_process_skb(ifp, skb);
 
 done:
@@ -538,31 +541,26 @@ void brcmf_rx_frame(struct device *dev, struct sk_buff *skb)
                brcmf_netif_rx(ifp, skb);
 }
 
-void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp,
+void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx,
                      bool success)
 {
        struct brcmf_if *ifp;
        struct ethhdr *eh;
-       u8 ifidx;
        u16 type;
-       int res;
-
-       res = brcmf_proto_hdrpull(drvr, false, &ifidx, txp);
 
        ifp = drvr->iflist[ifidx];
        if (!ifp)
                goto done;
 
-       if (res == 0) {
-               eh = (struct ethhdr *)(txp->data);
-               type = ntohs(eh->h_proto);
+       eh = (struct ethhdr *)(txp->data);
+       type = ntohs(eh->h_proto);
 
-               if (type == ETH_P_PAE) {
-                       atomic_dec(&ifp->pend_8021x_cnt);
-                       if (waitqueue_active(&ifp->pend_8021x_wait))
-                               wake_up(&ifp->pend_8021x_wait);
-               }
+       if (type == ETH_P_PAE) {
+               atomic_dec(&ifp->pend_8021x_cnt);
+               if (waitqueue_active(&ifp->pend_8021x_wait))
+                       wake_up(&ifp->pend_8021x_wait);
        }
+
        if (!success)
                ifp->stats.tx_errors++;
 done:
@@ -573,13 +571,17 @@ void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success)
 {
        struct brcmf_bus *bus_if = dev_get_drvdata(dev);
        struct brcmf_pub *drvr = bus_if->drvr;
+       u8 ifidx;
 
        /* await txstatus signal for firmware if active */
        if (brcmf_fws_fc_active(drvr->fws)) {
                if (!success)
                        brcmf_fws_bustxfail(drvr->fws, txp);
        } else {
-               brcmf_txfinalize(drvr, txp, success);
+               if (brcmf_proto_hdrpull(drvr, false, &ifidx, txp))
+                       brcmu_pkt_buf_free_skb(txp);
+               else
+                       brcmf_txfinalize(drvr, txp, ifidx, success);
        }
 }
 
@@ -914,13 +916,6 @@ int brcmf_bus_start(struct device *dev)
 
        brcmf_dbg(TRACE, "\n");
 
-       /* Bring up the bus */
-       ret = brcmf_bus_init(bus_if);
-       if (ret != 0) {
-               brcmf_err("brcmf_sdbrcm_bus_init failed %d\n", ret);
-               return ret;
-       }
-
        /* add primary networking interface */
        ifp = brcmf_add_if(drvr, 0, 0, "wlan%d", NULL);
        if (IS_ERR(ifp))
index 13c89a0c4ba7ec63f8d7a33688caafa3d407b9f7..8fa0dbbbda72b6ed1d1dc84020594287cf595a37 100644 (file)
@@ -42,7 +42,7 @@
 #include <soc.h>
 #include "sdio_host.h"
 #include "chip.h"
-#include "nvram.h"
+#include "firmware.h"
 
 #define DCMD_RESP_TIMEOUT  2000        /* In milli second */
 
@@ -632,43 +632,28 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = {
        { BCM4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) }
 };
 
-
-static const struct firmware *brcmf_sdio_get_fw(struct brcmf_sdio *bus,
-                                                 enum brcmf_firmware_type type)
+static const char *brcmf_sdio_get_fwname(struct brcmf_chip *ci,
+                                        enum brcmf_firmware_type type)
 {
-       const struct firmware *fw;
-       const char *name;
-       int err, i;
+       int 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)) {
+               if (brcmf_fwname_data[i].chipid == ci->chip &&
+                   brcmf_fwname_data[i].revmsk & BIT(ci->chiprev)) {
                        switch (type) {
                        case BRCMF_FIRMWARE_BIN:
-                               name = brcmf_fwname_data[i].bin;
-                               break;
+                               return brcmf_fwname_data[i].bin;
                        case BRCMF_FIRMWARE_NVRAM:
-                               name = brcmf_fwname_data[i].nv;
-                               break;
+                               return brcmf_fwname_data[i].nv;
                        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);
+                 ci->chip, 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)
@@ -3278,20 +3263,13 @@ static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus,
 }
 
 static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus,
-                                    const struct firmware *nv)
+                                    void *vars, u32 varsz)
 {
-       void *vars;
-       u32 varsz;
        int address;
        int err;
 
        brcmf_dbg(TRACE, "Enter\n");
 
-       vars = brcmf_nvram_strip(nv, &varsz);
-
-       if (vars == NULL)
-               return -EINVAL;
-
        address = bus->ci->ramsize - varsz + bus->ci->rambase;
        err = brcmf_sdiod_ramrw(bus->sdiodev, true, address, vars, varsz);
        if (err)
@@ -3300,15 +3278,14 @@ static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus,
        else if (!brcmf_sdio_verifymemory(bus->sdiodev, address, vars, varsz))
                err = -EIO;
 
-       brcmf_nvram_free(vars);
-
        return err;
 }
 
-static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus)
+static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus,
+                                       const struct firmware *fw,
+                                       void *nvram, u32 nvlen)
 {
        int bcmerror = -EFAULT;
-       const struct firmware *fw;
        u32 rstvec;
 
        sdio_claim_host(bus->sdiodev->func[1]);
@@ -3317,12 +3294,6 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus)
        /* Keep arm in reset */
        brcmf_chip_enter_download(bus->ci);
 
-       fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_BIN);
-       if (fw == NULL) {
-               bcmerror = -ENOENT;
-               goto err;
-       }
-
        rstvec = get_unaligned_le32(fw->data);
        brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec);
 
@@ -3330,17 +3301,12 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus)
        release_firmware(fw);
        if (bcmerror) {
                brcmf_err("dongle image file download failed\n");
+               brcmf_fw_nvram_free(nvram);
                goto err;
        }
 
-       fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_NVRAM);
-       if (fw == NULL) {
-               bcmerror = -ENOENT;
-               goto err;
-       }
-
-       bcmerror = brcmf_sdio_download_nvram(bus, fw);
-       release_firmware(fw);
+       bcmerror = brcmf_sdio_download_nvram(bus, nvram, nvlen);
+       brcmf_fw_nvram_free(nvram);
        if (bcmerror) {
                brcmf_err("dongle nvram file download failed\n");
                goto err;
@@ -3490,97 +3456,6 @@ static int brcmf_sdio_bus_preinit(struct device *dev)
        return err;
 }
 
-static int brcmf_sdio_bus_init(struct device *dev)
-{
-       struct brcmf_bus *bus_if = dev_get_drvdata(dev);
-       struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
-       struct brcmf_sdio *bus = sdiodev->bus;
-       int err, ret = 0;
-       u8 saveclk;
-
-       brcmf_dbg(TRACE, "Enter\n");
-
-       /* try to download image and nvram to the dongle */
-       if (bus_if->state == BRCMF_BUS_DOWN) {
-               bus->alp_only = true;
-               err = brcmf_sdio_download_firmware(bus);
-               if (err)
-                       return err;
-               bus->alp_only = false;
-       }
-
-       if (!bus->sdiodev->bus_if->drvr)
-               return 0;
-
-       /* Start the watchdog timer */
-       bus->sdcnt.tickcnt = 0;
-       brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
-
-       sdio_claim_host(bus->sdiodev->func[1]);
-
-       /* Make sure backplane clock is on, needed to generate F2 interrupt */
-       brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
-       if (bus->clkstate != CLK_AVAIL)
-               goto exit;
-
-       /* Force clocks on backplane to be sure F2 interrupt propagates */
-       saveclk = brcmf_sdiod_regrb(bus->sdiodev,
-                                   SBSDIO_FUNC1_CHIPCLKCSR, &err);
-       if (!err) {
-               brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
-                                 (saveclk | SBSDIO_FORCE_HT), &err);
-       }
-       if (err) {
-               brcmf_err("Failed to force clock for F2: err %d\n", err);
-               goto exit;
-       }
-
-       /* Enable function 2 (frame transfers) */
-       w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT,
-                 offsetof(struct sdpcmd_regs, tosbmailboxdata));
-       err = sdio_enable_func(bus->sdiodev->func[SDIO_FUNC_2]);
-
-
-       brcmf_dbg(INFO, "enable F2: err=%d\n", err);
-
-       /* If F2 successfully enabled, set core and enable interrupts */
-       if (!err) {
-               /* Set up the interrupt mask and enable interrupts */
-               bus->hostintmask = HOSTINTMASK;
-               w_sdreg32(bus, bus->hostintmask,
-                         offsetof(struct sdpcmd_regs, hostintmask));
-
-               brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_WATERMARK, 8, &err);
-       } else {
-               /* Disable F2 again */
-               sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]);
-               ret = -ENODEV;
-       }
-
-       if (brcmf_chip_sr_capable(bus->ci)) {
-               brcmf_sdio_sr_init(bus);
-       } else {
-               /* Restore previous clock setting */
-               brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
-                                 saveclk, &err);
-       }
-
-       if (ret == 0) {
-               ret = brcmf_sdiod_intr_register(bus->sdiodev);
-               if (ret != 0)
-                       brcmf_err("intr register failed:%d\n", ret);
-       }
-
-       /* If we didn't come up, turn off backplane clock */
-       if (ret != 0)
-               brcmf_sdio_clkctl(bus, CLK_NONE, false);
-
-exit:
-       sdio_release_host(bus->sdiodev->func[1]);
-
-       return ret;
-}
-
 void brcmf_sdio_isr(struct brcmf_sdio *bus)
 {
        brcmf_dbg(TRACE, "Enter\n");
@@ -4020,13 +3895,114 @@ brcmf_sdio_watchdog(unsigned long data)
 static struct brcmf_bus_ops brcmf_sdio_bus_ops = {
        .stop = brcmf_sdio_bus_stop,
        .preinit = brcmf_sdio_bus_preinit,
-       .init = brcmf_sdio_bus_init,
        .txdata = brcmf_sdio_bus_txdata,
        .txctl = brcmf_sdio_bus_txctl,
        .rxctl = brcmf_sdio_bus_rxctl,
        .gettxq = brcmf_sdio_bus_gettxq,
 };
 
+static void brcmf_sdio_firmware_callback(struct device *dev,
+                                        const struct firmware *code,
+                                        void *nvram, u32 nvram_len)
+{
+       struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+       struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+       struct brcmf_sdio *bus = sdiodev->bus;
+       int err = 0;
+       u8 saveclk;
+
+       brcmf_dbg(TRACE, "Enter: dev=%s\n", dev_name(dev));
+
+       /* try to download image and nvram to the dongle */
+       if (bus_if->state == BRCMF_BUS_DOWN) {
+               bus->alp_only = true;
+               err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len);
+               if (err)
+                       goto fail;
+               bus->alp_only = false;
+       }
+
+       if (!bus_if->drvr)
+               return;
+
+       /* Start the watchdog timer */
+       bus->sdcnt.tickcnt = 0;
+       brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
+
+       sdio_claim_host(sdiodev->func[1]);
+
+       /* Make sure backplane clock is on, needed to generate F2 interrupt */
+       brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
+       if (bus->clkstate != CLK_AVAIL)
+               goto release;
+
+       /* Force clocks on backplane to be sure F2 interrupt propagates */
+       saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err);
+       if (!err) {
+               brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+                                 (saveclk | SBSDIO_FORCE_HT), &err);
+       }
+       if (err) {
+               brcmf_err("Failed to force clock for F2: err %d\n", err);
+               goto release;
+       }
+
+       /* Enable function 2 (frame transfers) */
+       w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT,
+                 offsetof(struct sdpcmd_regs, tosbmailboxdata));
+       err = sdio_enable_func(sdiodev->func[SDIO_FUNC_2]);
+
+
+       brcmf_dbg(INFO, "enable F2: err=%d\n", err);
+
+       /* If F2 successfully enabled, set core and enable interrupts */
+       if (!err) {
+               /* Set up the interrupt mask and enable interrupts */
+               bus->hostintmask = HOSTINTMASK;
+               w_sdreg32(bus, bus->hostintmask,
+                         offsetof(struct sdpcmd_regs, hostintmask));
+
+               brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK, 8, &err);
+       } else {
+               /* Disable F2 again */
+               sdio_disable_func(sdiodev->func[SDIO_FUNC_2]);
+               goto release;
+       }
+
+       if (brcmf_chip_sr_capable(bus->ci)) {
+               brcmf_sdio_sr_init(bus);
+       } else {
+               /* Restore previous clock setting */
+               brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+                                 saveclk, &err);
+       }
+
+       if (err == 0) {
+               err = brcmf_sdiod_intr_register(sdiodev);
+               if (err != 0)
+                       brcmf_err("intr register failed:%d\n", err);
+       }
+
+       /* If we didn't come up, turn off backplane clock */
+       if (err != 0)
+               brcmf_sdio_clkctl(bus, CLK_NONE, false);
+
+       sdio_release_host(sdiodev->func[1]);
+
+       err = brcmf_bus_start(dev);
+       if (err != 0) {
+               brcmf_err("dongle is not responding\n");
+               goto fail;
+       }
+       return;
+
+release:
+       sdio_release_host(sdiodev->func[1]);
+fail:
+       brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), err);
+       device_release_driver(dev);
+}
+
 struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
 {
        int ret;
@@ -4110,8 +4086,13 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
                goto fail;
        }
 
+       /* Query the F2 block size, set roundup accordingly */
+       bus->blocksize = bus->sdiodev->func[2]->cur_blksize;
+       bus->roundup = min(max_roundup, bus->blocksize);
+
        /* Allocate buffers */
        if (bus->sdiodev->bus_if->maxctl) {
+               bus->sdiodev->bus_if->maxctl += bus->roundup;
                bus->rxblen =
                    roundup((bus->sdiodev->bus_if->maxctl + SDPCM_HDRLEN),
                            ALIGNMENT) + bus->head_align;
@@ -4139,10 +4120,6 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
        bus->idletime = BRCMF_IDLE_INTERVAL;
        bus->idleclock = BRCMF_IDLE_ACTIVE;
 
-       /* Query the F2 block size, set roundup accordingly */
-       bus->blocksize = bus->sdiodev->func[2]->cur_blksize;
-       bus->roundup = min(max_roundup, bus->blocksize);
-
        /* SR state */
        bus->sleeping = false;
        bus->sr_enabled = false;
@@ -4150,10 +4127,14 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
        brcmf_sdio_debugfs_create(bus);
        brcmf_dbg(INFO, "completed!!\n");
 
-       /* if firmware path present try to download and bring up bus */
-       ret = brcmf_bus_start(bus->sdiodev->dev);
+       ret = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM,
+                                    brcmf_sdio_get_fwname(bus->ci,
+                                                          BRCMF_FIRMWARE_BIN),
+                                    brcmf_sdio_get_fwname(bus->ci,
+                                                          BRCMF_FIRMWARE_NVRAM),
+                                    brcmf_sdio_firmware_callback);
        if (ret != 0) {
-               brcmf_err("dongle is not responding\n");
+               brcmf_err("async firmware request failed: %d\n", ret);
                goto fail;
        }
 
@@ -4173,9 +4154,7 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus)
                /* De-register interrupt handler */
                brcmf_sdiod_intr_unregister(bus->sdiodev);
 
-               if (bus->sdiodev->bus_if->drvr) {
-                       brcmf_detach(bus->sdiodev->dev);
-               }
+               brcmf_detach(bus->sdiodev->dev);
 
                cancel_work_sync(&bus->datawork);
                if (bus->brcmf_wq)
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
new file mode 100644 (file)
index 0000000..7b7d237
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 2013 Broadcom Corporation
+ *
+ * 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/kernel.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include "dhd_dbg.h"
+#include "firmware.h"
+
+enum nvram_parser_state {
+       IDLE,
+       KEY,
+       VALUE,
+       COMMENT,
+       END
+};
+
+/**
+ * struct nvram_parser - internal info for parser.
+ *
+ * @state: current parser state.
+ * @fwnv: input buffer being parsed.
+ * @nvram: output buffer with parse result.
+ * @nvram_len: lenght of parse result.
+ * @line: current line.
+ * @column: current column in line.
+ * @pos: byte offset in input buffer.
+ * @entry: start position of key,value entry.
+ */
+struct nvram_parser {
+       enum nvram_parser_state state;
+       const struct firmware *fwnv;
+       u8 *nvram;
+       u32 nvram_len;
+       u32 line;
+       u32 column;
+       u32 pos;
+       u32 entry;
+};
+
+static bool is_nvram_char(char c)
+{
+       /* comment marker excluded */
+       if (c == '#')
+               return false;
+
+       /* key and value may have any other readable character */
+       return (c > 0x20 && c < 0x7f);
+}
+
+static bool is_whitespace(char c)
+{
+       return (c == ' ' || c == '\r' || c == '\n' || c == '\t');
+}
+
+static enum nvram_parser_state brcmf_nvram_handle_idle(struct nvram_parser *nvp)
+{
+       char c;
+
+       c = nvp->fwnv->data[nvp->pos];
+       if (c == '\n')
+               return COMMENT;
+       if (is_whitespace(c))
+               goto proceed;
+       if (c == '#')
+               return COMMENT;
+       if (is_nvram_char(c)) {
+               nvp->entry = nvp->pos;
+               return KEY;
+       }
+       brcmf_dbg(INFO, "warning: ln=%d:col=%d: ignoring invalid character\n",
+                 nvp->line, nvp->column);
+proceed:
+       nvp->column++;
+       nvp->pos++;
+       return IDLE;
+}
+
+static enum nvram_parser_state brcmf_nvram_handle_key(struct nvram_parser *nvp)
+{
+       enum nvram_parser_state st = nvp->state;
+       char c;
+
+       c = nvp->fwnv->data[nvp->pos];
+       if (c == '=') {
+               st = VALUE;
+       } else if (!is_nvram_char(c)) {
+               brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n",
+                         nvp->line, nvp->column);
+               return COMMENT;
+       }
+
+       nvp->column++;
+       nvp->pos++;
+       return st;
+}
+
+static enum nvram_parser_state
+brcmf_nvram_handle_value(struct nvram_parser *nvp)
+{
+       char c;
+       char *skv;
+       char *ekv;
+       u32 cplen;
+
+       c = nvp->fwnv->data[nvp->pos];
+       if (!is_nvram_char(c)) {
+               /* key,value pair complete */
+               ekv = (u8 *)&nvp->fwnv->data[nvp->pos];
+               skv = (u8 *)&nvp->fwnv->data[nvp->entry];
+               cplen = ekv - skv;
+               /* copy to output buffer */
+               memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen);
+               nvp->nvram_len += cplen;
+               nvp->nvram[nvp->nvram_len] = '\0';
+               nvp->nvram_len++;
+               return IDLE;
+       }
+       nvp->pos++;
+       nvp->column++;
+       return VALUE;
+}
+
+static enum nvram_parser_state
+brcmf_nvram_handle_comment(struct nvram_parser *nvp)
+{
+       char *eol, *sol;
+
+       sol = (char *)&nvp->fwnv->data[nvp->pos];
+       eol = strchr(sol, '\n');
+       if (eol == NULL)
+               return END;
+
+       /* eat all moving to next line */
+       nvp->line++;
+       nvp->column = 1;
+       nvp->pos += (eol - sol) + 1;
+       return IDLE;
+}
+
+static enum nvram_parser_state brcmf_nvram_handle_end(struct nvram_parser *nvp)
+{
+       /* final state */
+       return END;
+}
+
+static enum nvram_parser_state
+(*nv_parser_states[])(struct nvram_parser *nvp) = {
+       brcmf_nvram_handle_idle,
+       brcmf_nvram_handle_key,
+       brcmf_nvram_handle_value,
+       brcmf_nvram_handle_comment,
+       brcmf_nvram_handle_end
+};
+
+static int brcmf_init_nvram_parser(struct nvram_parser *nvp,
+                                  const struct firmware *nv)
+{
+       memset(nvp, 0, sizeof(*nvp));
+       nvp->fwnv = nv;
+       /* Alloc for extra 0 byte + roundup by 4 + length field */
+       nvp->nvram = kzalloc(nv->size + 1 + 3 + sizeof(u32), GFP_KERNEL);
+       if (!nvp->nvram)
+               return -ENOMEM;
+
+       nvp->line = 1;
+       nvp->column = 1;
+       return 0;
+}
+
+/* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a fil
+ * and ending in a NUL. Removes carriage returns, empty lines, comment lines,
+ * and converts newlines to NULs. Shortens buffer as needed and pads with NULs.
+ * End of buffer is completed with token identifying length of buffer.
+ */
+static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length)
+{
+       struct nvram_parser nvp;
+       u32 pad;
+       u32 token;
+       __le32 token_le;
+
+       if (brcmf_init_nvram_parser(&nvp, nv) < 0)
+               return NULL;
+
+       while (nvp.pos < nv->size) {
+               nvp.state = nv_parser_states[nvp.state](&nvp);
+               if (nvp.state == END)
+                       break;
+       }
+       pad = nvp.nvram_len;
+       *new_length = roundup(nvp.nvram_len + 1, 4);
+       while (pad != *new_length) {
+               nvp.nvram[pad] = 0;
+               pad++;
+       }
+
+       token = *new_length / 4;
+       token = (~token << 16) | (token & 0x0000FFFF);
+       token_le = cpu_to_le32(token);
+
+       memcpy(&nvp.nvram[*new_length], &token_le, sizeof(token_le));
+       *new_length += sizeof(token_le);
+
+       return nvp.nvram;
+}
+
+void brcmf_fw_nvram_free(void *nvram)
+{
+       kfree(nvram);
+}
+
+struct brcmf_fw {
+       struct device *dev;
+       u16 flags;
+       const struct firmware *code;
+       const char *nvram_name;
+       void (*done)(struct device *dev, const struct firmware *fw,
+                    void *nvram_image, u32 nvram_len);
+};
+
+static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
+{
+       struct brcmf_fw *fwctx = ctx;
+       u32 nvram_length = 0;
+       void *nvram = NULL;
+
+       brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
+       if (!fw && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
+               goto fail;
+
+       if (fw) {
+               nvram = brcmf_fw_nvram_strip(fw, &nvram_length);
+               release_firmware(fw);
+               if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
+                       goto fail;
+       }
+
+       fwctx->done(fwctx->dev, fwctx->code, nvram, nvram_length);
+       kfree(fwctx);
+       return;
+
+fail:
+       brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
+       if (fwctx->code)
+               release_firmware(fwctx->code);
+       device_release_driver(fwctx->dev);
+       kfree(fwctx);
+}
+
+static void brcmf_fw_request_code_done(const struct firmware *fw, void *ctx)
+{
+       struct brcmf_fw *fwctx = ctx;
+       int ret;
+
+       brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
+       if (!fw)
+               goto fail;
+
+       /* only requested code so done here */
+       if (!(fwctx->flags & BRCMF_FW_REQUEST_NVRAM)) {
+               fwctx->done(fwctx->dev, fw, NULL, 0);
+               kfree(fwctx);
+               return;
+       }
+       fwctx->code = fw;
+       ret = request_firmware_nowait(THIS_MODULE, true, fwctx->nvram_name,
+                                     fwctx->dev, GFP_KERNEL, fwctx,
+                                     brcmf_fw_request_nvram_done);
+
+       if (!ret)
+               return;
+
+       /* when nvram is optional call .done() callback here */
+       if (fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL) {
+               fwctx->done(fwctx->dev, fw, NULL, 0);
+               kfree(fwctx);
+               return;
+       }
+
+       /* failed nvram request */
+       release_firmware(fw);
+fail:
+       brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
+       device_release_driver(fwctx->dev);
+       kfree(fwctx);
+}
+
+int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
+                          const char *code, const char *nvram,
+                          void (*fw_cb)(struct device *dev,
+                                        const struct firmware *fw,
+                                        void *nvram_image, u32 nvram_len))
+{
+       struct brcmf_fw *fwctx;
+
+       brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev));
+       if (!fw_cb || !code)
+               return -EINVAL;
+
+       if ((flags & BRCMF_FW_REQUEST_NVRAM) && !nvram)
+               return -EINVAL;
+
+       fwctx = kzalloc(sizeof(*fwctx), GFP_KERNEL);
+       if (!fwctx)
+               return -ENOMEM;
+
+       fwctx->dev = dev;
+       fwctx->flags = flags;
+       fwctx->done = fw_cb;
+       if (flags & BRCMF_FW_REQUEST_NVRAM)
+               fwctx->nvram_name = nvram;
+
+       return request_firmware_nowait(THIS_MODULE, true, code, dev,
+                                      GFP_KERNEL, fwctx,
+                                      brcmf_fw_request_code_done);
+}
similarity index 52%
rename from drivers/net/wireless/brcm80211/brcmfmac/nvram.h
rename to drivers/net/wireless/brcm80211/brcmfmac/firmware.h
index d454580928c9ab2e41b84bc35032065570e4cdf5..6431bfd7afffc6c544fa1e292b3a6a98cfd4a664 100644 (file)
  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifndef BRCMFMAC_NVRAM_H
-#define BRCMFMAC_NVRAM_H
+#ifndef BRCMFMAC_FIRMWARE_H
+#define BRCMFMAC_FIRMWARE_H
 
+#define BRCMF_FW_REQUEST               0x000F
+#define  BRCMF_FW_REQUEST_NVRAM                0x0001
+#define BRCMF_FW_REQ_FLAGS             0x00F0
+#define  BRCMF_FW_REQ_NV_OPTIONAL      0x0010
 
-void *brcmf_nvram_strip(const struct firmware *nv, u32 *new_length);
-void brcmf_nvram_free(void *nvram);
-
+void brcmf_fw_nvram_free(void *nvram);
+/*
+ * Request firmware(s) asynchronously. When the asynchronous request
+ * fails it will not use the callback, but call device_release_driver()
+ * instead which will call the driver .remove() callback.
+ */
+int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
+                          const char *code, const char *nvram,
+                          void (*fw_cb)(struct device *dev,
+                                        const struct firmware *fw,
+                                        void *nvram_image, u32 nvram_len));
 
-#endif /* BRCMFMAC_NVRAM_H */
+#endif /* BRCMFMAC_FIRMWARE_H */
index 614e4888504fae4f517389bfb49c4d08a5519924..2bc68a2137fccb535f5a34fb34e6394db85ec645 100644 (file)
 #define BRCMF_OBSS_COEX_OFF            0
 #define BRCMF_OBSS_COEX_ON             1
 
+/* join preference types for join_pref iovar */
+enum brcmf_join_pref_types {
+       BRCMF_JOIN_PREF_RSSI = 1,
+       BRCMF_JOIN_PREF_WPA,
+       BRCMF_JOIN_PREF_BAND,
+       BRCMF_JOIN_PREF_RSSI_DELTA,
+};
+
 enum brcmf_fil_p2p_if_types {
        BRCMF_FIL_P2P_IF_CLIENT,
        BRCMF_FIL_P2P_IF_GO,
@@ -282,6 +290,22 @@ struct brcmf_assoc_params_le {
        __le16 chanspec_list[1];
 };
 
+/**
+ * struct join_pref params - parameters for preferred join selection.
+ *
+ * @type: preference type (see enum brcmf_join_pref_types).
+ * @len: length of bytes following (currently always 2).
+ * @rssi_gain: signal gain for selection (only when @type is RSSI_DELTA).
+ * @band: band to which selection preference applies.
+ *     This is used if @type is BAND or RSSI_DELTA.
+ */
+struct brcmf_join_pref_params {
+       u8 type;
+       u8 len;
+       u8 rssi_gain;
+       u8 band;
+};
+
 /* used for join with or without a specific bssid and channel list */
 struct brcmf_join_params {
        struct brcmf_ssid_le ssid_le;
index c3e7d76dbf35f508e33a1e1d88164ede54b1967a..699908de314a94ff3f382f62536756b3f4a5eed2 100644 (file)
@@ -476,6 +476,7 @@ struct brcmf_fws_info {
        bool bus_flow_blocked;
        bool creditmap_received;
        u8 mode;
+       bool avoid_queueing;
 };
 
 /*
@@ -1369,13 +1370,12 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
 }
 
 static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
-                                        struct sk_buff *skb, u32 genbit,
-                                        u16 seq)
+                                        struct sk_buff *skb, u8 ifidx,
+                                        u32 genbit, u16 seq)
 {
        struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
        u32 hslot;
        int ret;
-       u8 ifidx;
 
        hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
 
@@ -1389,29 +1389,21 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
 
        entry->generation = genbit;
 
-       ret = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb);
-       if (ret == 0) {
-               brcmf_skb_htod_tag_set_field(skb, GENERATION, genbit);
-               brcmf_skbcb(skb)->htod_seq = seq;
-               if (brcmf_skb_htod_seq_get_field(skb, FROMFW)) {
-                       brcmf_skb_htod_seq_set_field(skb, FROMDRV, 1);
-                       brcmf_skb_htod_seq_set_field(skb, FROMFW, 0);
-               } else {
-                       brcmf_skb_htod_seq_set_field(skb, FROMDRV, 0);
-               }
-               ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo,
-                                   skb);
+       brcmf_skb_htod_tag_set_field(skb, GENERATION, genbit);
+       brcmf_skbcb(skb)->htod_seq = seq;
+       if (brcmf_skb_htod_seq_get_field(skb, FROMFW)) {
+               brcmf_skb_htod_seq_set_field(skb, FROMDRV, 1);
+               brcmf_skb_htod_seq_set_field(skb, FROMFW, 0);
+       } else {
+               brcmf_skb_htod_seq_set_field(skb, FROMDRV, 0);
        }
+       ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb);
 
        if (ret != 0) {
-               /* suppress q is full or hdrpull failed, drop this packet */
-               brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb,
-                                       true);
+               /* suppress q is full drop this packet */
+               brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, true);
        } else {
-               /*
-                * Mark suppressed to avoid a double free during
-                * wlfc cleanup
-                */
+               /* Mark suppressed to avoid a double free during wlfc cleanup */
                brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot);
        }
 
@@ -1428,6 +1420,7 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
        struct sk_buff *skb;
        struct brcmf_skbuff_cb *skcb;
        struct brcmf_fws_mac_descriptor *entry = NULL;
+       u8 ifidx;
 
        brcmf_dbg(DATA, "flags %d\n", flags);
 
@@ -1476,12 +1469,15 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot,
        }
        brcmf_fws_macdesc_return_req_credit(skb);
 
+       if (brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb)) {
+               brcmu_pkt_buf_free_skb(skb);
+               return -EINVAL;
+       }
        if (!remove_from_hanger)
-               ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, genbit,
-                                                   seq);
-
+               ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifidx,
+                                                   genbit, seq);
        if (remove_from_hanger || ret)
-               brcmf_txfinalize(fws->drvr, skb, true);
+               brcmf_txfinalize(fws->drvr, skb, ifidx, true);
 
        return 0;
 }
@@ -1868,7 +1864,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
        struct ethhdr *eh = (struct ethhdr *)(skb->data);
        int fifo = BRCMF_FWS_FIFO_BCMC;
        bool multicast = is_multicast_ether_addr(eh->h_dest);
-       bool pae = eh->h_proto == htons(ETH_P_PAE);
+       int rc = 0;
 
        brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto));
        /* determine the priority */
@@ -1876,8 +1872,13 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
                skb->priority = cfg80211_classify8021d(skb, NULL);
 
        drvr->tx_multicast += !!multicast;
-       if (pae)
-               atomic_inc(&ifp->pend_8021x_cnt);
+
+       if (fws->avoid_queueing) {
+               rc = brcmf_proto_txdata(drvr, ifp->ifidx, 0, skb);
+               if (rc < 0)
+                       brcmf_txfinalize(drvr, skb, ifp->ifidx, false);
+               return rc;
+       }
 
        /* set control buffer information */
        skcb->if_flags = 0;
@@ -1899,15 +1900,12 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
                brcmf_fws_schedule_deq(fws);
        } else {
                brcmf_err("drop skb: no hanger slot\n");
-               if (pae) {
-                       atomic_dec(&ifp->pend_8021x_cnt);
-                       if (waitqueue_active(&ifp->pend_8021x_wait))
-                               wake_up(&ifp->pend_8021x_wait);
-               }
-               brcmu_pkt_buf_free_skb(skb);
+               brcmf_txfinalize(drvr, skb, ifp->ifidx, false);
+               rc = -ENOMEM;
        }
        brcmf_fws_unlock(fws);
-       return 0;
+
+       return rc;
 }
 
 void brcmf_fws_reset_interface(struct brcmf_if *ifp)
@@ -1982,7 +1980,8 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
                                ret = brcmf_proto_txdata(drvr, ifidx, 0, skb);
                                brcmf_fws_lock(fws);
                                if (ret < 0)
-                                       brcmf_txfinalize(drvr, skb, false);
+                                       brcmf_txfinalize(drvr, skb, ifidx,
+                                                        false);
                                if (fws->bus_flow_blocked)
                                        break;
                        }
@@ -2039,6 +2038,13 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
        fws->drvr = drvr;
        fws->fcmode = fcmode;
 
+       if ((drvr->bus_if->always_use_fws_queue == false) &&
+           (fcmode == BRCMF_FWS_FCMODE_NONE)) {
+               fws->avoid_queueing = true;
+               brcmf_dbg(INFO, "FWS queueing will be avoided\n");
+               return 0;
+       }
+
        fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq");
        if (fws->fws_wq == NULL) {
                brcmf_err("workqueue creation failed\n");
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/nvram.c b/drivers/net/wireless/brcm80211/brcmfmac/nvram.c
deleted file mode 100644 (file)
index d5ef86d..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (c) 2013 Broadcom Corporation
- *
- * 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/kernel.h>
-#include <linux/slab.h>
-#include <linux/firmware.h>
-
-#include "nvram.h"
-
-/* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a file
- * and ending in a NUL. Removes carriage returns, empty lines, comment lines,
- * and converts newlines to NULs. Shortens buffer as needed and pads with NULs.
- * End of buffer is completed with token identifying length of buffer.
- */
-void *brcmf_nvram_strip(const struct firmware *nv, u32 *new_length)
-{
-       u8 *nvram;
-       u32 i;
-       u32 len;
-       u32 column;
-       u8 val;
-       bool comment;
-       u32 token;
-       __le32 token_le;
-
-       /* Alloc for extra 0 byte + roundup by 4 + length field */
-       nvram = kmalloc(nv->size + 1 + 3 + sizeof(token_le), GFP_KERNEL);
-       if (!nvram)
-               return NULL;
-
-       len = 0;
-       column = 0;
-       comment = false;
-       for (i = 0; i < nv->size; i++) {
-               val = nv->data[i];
-               if (val == 0)
-                       break;
-               if (val == '\r')
-                       continue;
-               if (comment && (val != '\n'))
-                       continue;
-               comment = false;
-               if (val == '#') {
-                       comment = true;
-                       continue;
-               }
-               if (val == '\n') {
-                       if (column == 0)
-                               continue;
-                       nvram[len] = 0;
-                       len++;
-                       column = 0;
-                       continue;
-               }
-               nvram[len] = val;
-               len++;
-               column++;
-       }
-       column = len;
-       *new_length = roundup(len + 1, 4);
-       while (column != *new_length) {
-               nvram[column] = 0;
-               column++;
-       }
-
-       token = *new_length / 4;
-       token = (~token << 16) | (token & 0x0000FFFF);
-       token_le = cpu_to_le32(token);
-
-       memcpy(&nvram[*new_length], &token_le, sizeof(token_le));
-       *new_length += sizeof(token_le);
-
-       return nvram;
-}
-
-void brcmf_nvram_free(void *nvram)
-{
-       kfree(nvram);
-}
-
-
index 24f65cd538595a84e6c66b62b8698bbc97456288..6db51a666f619abedaee11ac4822b917b24b4f3c 100644 (file)
@@ -25,6 +25,7 @@
 #include <dhd_bus.h>
 #include <dhd_dbg.h>
 
+#include "firmware.h"
 #include "usb_rdl.h"
 #include "usb.h"
 
@@ -61,12 +62,6 @@ struct brcmf_usb_image {
        u8 *image;
        int image_len;
 };
-static struct list_head fw_image_list;
-
-struct intr_transfer_buf {
-       u32 notification;
-       u32 reserved;
-};
 
 struct brcmf_usbdev_info {
        struct brcmf_usbdev bus_pub; /* MUST BE FIRST */
@@ -75,7 +70,7 @@ struct brcmf_usbdev_info {
        struct list_head rx_postq;
        struct list_head tx_freeq;
        struct list_head tx_postq;
-       uint rx_pipe, tx_pipe, intr_pipe, rx_pipe2;
+       uint rx_pipe, tx_pipe, rx_pipe2;
 
        int rx_low_watermark;
        int tx_low_watermark;
@@ -87,7 +82,7 @@ struct brcmf_usbdev_info {
        struct brcmf_usbreq *tx_reqs;
        struct brcmf_usbreq *rx_reqs;
 
-       u8 *image;      /* buffer for combine fw and nvram */
+       const u8 *image;        /* buffer for combine fw and nvram */
        int image_len;
 
        struct usb_device *usbdev;
@@ -104,10 +99,6 @@ struct brcmf_usbdev_info {
        ulong ctl_op;
 
        struct urb *bulk_urb; /* used for FW download */
-       struct urb *intr_urb; /* URB for interrupt endpoint */
-       int intr_size;          /* Size of interrupt message */
-       int interval;           /* Interrupt polling interval */
-       struct intr_transfer_buf intr; /* Data buffer for interrupt endpoint */
 };
 
 static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo,
@@ -531,39 +522,6 @@ brcmf_usb_state_change(struct brcmf_usbdev_info *devinfo, int state)
        }
 }
 
-static void
-brcmf_usb_intr_complete(struct urb *urb)
-{
-       struct brcmf_usbdev_info *devinfo =
-                       (struct brcmf_usbdev_info *)urb->context;
-       int err;
-
-       brcmf_dbg(USB, "Enter, urb->status=%d\n", urb->status);
-
-       if (devinfo == NULL)
-               return;
-
-       if (unlikely(urb->status)) {
-               if (urb->status == -ENOENT ||
-                   urb->status == -ESHUTDOWN ||
-                   urb->status == -ENODEV) {
-                       brcmf_usb_state_change(devinfo,
-                                              BRCMFMAC_USB_STATE_DOWN);
-               }
-       }
-
-       if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_DOWN) {
-               brcmf_err("intr cb when DBUS down, ignoring\n");
-               return;
-       }
-
-       if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) {
-               err = usb_submit_urb(devinfo->intr_urb, GFP_ATOMIC);
-               if (err)
-                       brcmf_err("usb_submit_urb, err=%d\n", err);
-       }
-}
-
 static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
 {
        struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
@@ -619,7 +577,6 @@ static int brcmf_usb_up(struct device *dev)
 {
        struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
        u16 ifnum;
-       int ret;
 
        brcmf_dbg(USB, "Enter\n");
        if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP)
@@ -628,23 +585,6 @@ static int brcmf_usb_up(struct device *dev)
        /* Success, indicate devinfo is fully up */
        brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_UP);
 
-       if (devinfo->intr_urb) {
-               usb_fill_int_urb(devinfo->intr_urb, devinfo->usbdev,
-                       devinfo->intr_pipe,
-                       &devinfo->intr,
-                       devinfo->intr_size,
-                       (usb_complete_t)brcmf_usb_intr_complete,
-                       devinfo,
-                       devinfo->interval);
-
-               ret = usb_submit_urb(devinfo->intr_urb, GFP_ATOMIC);
-               if (ret) {
-                       brcmf_err("USB_SUBMIT_URB failed with status %d\n",
-                                 ret);
-                       return -EINVAL;
-               }
-       }
-
        if (devinfo->ctl_urb) {
                devinfo->ctl_in_pipe = usb_rcvctrlpipe(devinfo->usbdev, 0);
                devinfo->ctl_out_pipe = usb_sndctrlpipe(devinfo->usbdev, 0);
@@ -681,8 +621,6 @@ static void brcmf_usb_down(struct device *dev)
                return;
 
        brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_DOWN);
-       if (devinfo->intr_urb)
-               usb_kill_urb(devinfo->intr_urb);
 
        if (devinfo->ctl_urb)
                usb_kill_urb(devinfo->ctl_urb);
@@ -1021,7 +959,7 @@ brcmf_usb_fw_download(struct brcmf_usbdev_info *devinfo)
        }
 
        err = brcmf_usb_dlstart(devinfo,
-               devinfo->image, devinfo->image_len);
+               (u8 *)devinfo->image, devinfo->image_len);
        if (err == 0)
                err = brcmf_usb_dlrun(devinfo);
        return err;
@@ -1036,7 +974,6 @@ static void brcmf_usb_detach(struct brcmf_usbdev_info *devinfo)
        brcmf_usb_free_q(&devinfo->rx_freeq, false);
        brcmf_usb_free_q(&devinfo->tx_freeq, false);
 
-       usb_free_urb(devinfo->intr_urb);
        usb_free_urb(devinfo->ctl_urb);
        usb_free_urb(devinfo->bulk_urb);
 
@@ -1080,68 +1017,20 @@ static int check_file(const u8 *headers)
        return -1;
 }
 
-static int brcmf_usb_get_fw(struct brcmf_usbdev_info *devinfo)
+static const char *brcmf_usb_get_fwname(struct brcmf_usbdev_info *devinfo)
 {
-       s8 *fwname;
-       const struct firmware *fw;
-       struct brcmf_usb_image *fw_image;
-       int err;
-
-       brcmf_dbg(USB, "Enter\n");
        switch (devinfo->bus_pub.devid) {
        case 43143:
-               fwname = BRCMF_USB_43143_FW_NAME;
-               break;
+               return BRCMF_USB_43143_FW_NAME;
        case 43235:
        case 43236:
        case 43238:
-               fwname = BRCMF_USB_43236_FW_NAME;
-               break;
+               return BRCMF_USB_43236_FW_NAME;
        case 43242:
-               fwname = BRCMF_USB_43242_FW_NAME;
-               break;
+               return BRCMF_USB_43242_FW_NAME;
        default:
-               return -EINVAL;
-               break;
-       }
-       brcmf_dbg(USB, "Loading FW %s\n", fwname);
-       list_for_each_entry(fw_image, &fw_image_list, list) {
-               if (fw_image->fwname == fwname) {
-                       devinfo->image = fw_image->image;
-                       devinfo->image_len = fw_image->image_len;
-                       return 0;
-               }
-       }
-       /* fw image not yet loaded. Load it now and add to list */
-       err = request_firmware(&fw, fwname, devinfo->dev);
-       if (!fw) {
-               brcmf_err("fail to request firmware %s\n", fwname);
-               return err;
-       }
-       if (check_file(fw->data) < 0) {
-               brcmf_err("invalid firmware %s\n", fwname);
-               return -EINVAL;
+               return NULL;
        }
-
-       fw_image = kzalloc(sizeof(*fw_image), GFP_ATOMIC);
-       if (!fw_image)
-               return -ENOMEM;
-       INIT_LIST_HEAD(&fw_image->list);
-       list_add_tail(&fw_image->list, &fw_image_list);
-       fw_image->fwname = fwname;
-       fw_image->image = vmalloc(fw->size);
-       if (!fw_image->image)
-               return -ENOMEM;
-
-       memcpy(fw_image->image, fw->data, fw->size);
-       fw_image->image_len = fw->size;
-
-       release_firmware(fw);
-
-       devinfo->image = fw_image->image;
-       devinfo->image_len = fw_image->image_len;
-
-       return 0;
 }
 
 
@@ -1186,11 +1075,6 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo,
                goto error;
        devinfo->tx_freecount = ntxq;
 
-       devinfo->intr_urb = usb_alloc_urb(0, GFP_ATOMIC);
-       if (!devinfo->intr_urb) {
-               brcmf_err("usb_alloc_urb (intr) failed\n");
-               goto error;
-       }
        devinfo->ctl_urb = usb_alloc_urb(0, GFP_ATOMIC);
        if (!devinfo->ctl_urb) {
                brcmf_err("usb_alloc_urb (ctl) failed\n");
@@ -1202,16 +1086,6 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo,
                goto error;
        }
 
-       if (!brcmf_usb_dlneeded(devinfo))
-               return &devinfo->bus_pub;
-
-       brcmf_dbg(USB, "Start fw downloading\n");
-       if (brcmf_usb_get_fw(devinfo))
-               goto error;
-
-       if (brcmf_usb_fw_download(devinfo))
-               goto error;
-
        return &devinfo->bus_pub;
 
 error:
@@ -1222,18 +1096,77 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo,
 
 static struct brcmf_bus_ops brcmf_usb_bus_ops = {
        .txdata = brcmf_usb_tx,
-       .init = brcmf_usb_up,
        .stop = brcmf_usb_down,
        .txctl = brcmf_usb_tx_ctlpkt,
        .rxctl = brcmf_usb_rx_ctlpkt,
 };
 
+static int brcmf_usb_bus_setup(struct brcmf_usbdev_info *devinfo)
+{
+       int ret;
+
+       /* Attach to the common driver interface */
+       ret = brcmf_attach(devinfo->dev);
+       if (ret) {
+               brcmf_err("brcmf_attach failed\n");
+               return ret;
+       }
+
+       ret = brcmf_usb_up(devinfo->dev);
+       if (ret)
+               goto fail;
+
+       ret = brcmf_bus_start(devinfo->dev);
+       if (ret)
+               goto fail;
+
+       return 0;
+fail:
+       brcmf_detach(devinfo->dev);
+       return ret;
+}
+
+static void brcmf_usb_probe_phase2(struct device *dev,
+                                  const struct firmware *fw,
+                                  void *nvram, u32 nvlen)
+{
+       struct brcmf_bus *bus = dev_get_drvdata(dev);
+       struct brcmf_usbdev_info *devinfo;
+       int ret;
+
+       brcmf_dbg(USB, "Start fw downloading\n");
+       ret = check_file(fw->data);
+       if (ret < 0) {
+               brcmf_err("invalid firmware\n");
+               release_firmware(fw);
+               goto error;
+       }
+
+       devinfo = bus->bus_priv.usb->devinfo;
+       devinfo->image = fw->data;
+       devinfo->image_len = fw->size;
+
+       ret = brcmf_usb_fw_download(devinfo);
+       release_firmware(fw);
+       if (ret)
+               goto error;
+
+       ret = brcmf_usb_bus_setup(devinfo);
+       if (ret)
+               goto error;
+
+       return;
+error:
+       brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), ret);
+       device_release_driver(dev);
+}
+
 static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo)
 {
        struct brcmf_bus *bus = NULL;
        struct brcmf_usbdev *bus_pub = NULL;
-       int ret;
        struct device *dev = devinfo->dev;
+       int ret;
 
        brcmf_dbg(USB, "Enter\n");
        bus_pub = brcmf_usb_attach(devinfo, BRCMF_USB_NRXQ, BRCMF_USB_NTXQ);
@@ -1254,22 +1187,18 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo)
        bus->chip = bus_pub->devid;
        bus->chiprev = bus_pub->chiprev;
        bus->proto_type = BRCMF_PROTO_BCDC;
+       bus->always_use_fws_queue = true;
 
-       /* Attach to the common driver interface */
-       ret = brcmf_attach(dev);
-       if (ret) {
-               brcmf_err("brcmf_attach failed\n");
-               goto fail;
-       }
-
-       ret = brcmf_bus_start(dev);
-       if (ret) {
-               brcmf_err("dongle is not responding\n");
-               brcmf_detach(dev);
-               goto fail;
+       if (!brcmf_usb_dlneeded(devinfo)) {
+               ret = brcmf_usb_bus_setup(devinfo);
+               if (ret)
+                       goto fail;
        }
-
+       /* request firmware here */
+       brcmf_fw_get_firmwares(dev, 0, brcmf_usb_get_fwname(devinfo), NULL,
+                              brcmf_usb_probe_phase2);
        return 0;
+
 fail:
        /* Release resources in reverse order */
        kfree(bus);
@@ -1357,9 +1286,6 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
                goto fail;
        }
 
-       endpoint_num = endpoint->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
-       devinfo->intr_pipe = usb_rcvintpipe(usb, endpoint_num);
-
        devinfo->rx_pipe = 0;
        devinfo->rx_pipe2 = 0;
        devinfo->tx_pipe = 0;
@@ -1391,16 +1317,9 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
                }
        }
 
-       /* Allocate interrupt URB and data buffer */
-       /* RNDIS says 8-byte intr, our old drivers used 4-byte */
-       if (IFEPDESC(usb, CONTROL_IF, 0).wMaxPacketSize == cpu_to_le16(16))
-               devinfo->intr_size = 8;
-       else
-               devinfo->intr_size = 4;
-
-       devinfo->interval = IFEPDESC(usb, CONTROL_IF, 0).bInterval;
-
-       if (usb->speed == USB_SPEED_HIGH)
+       if (usb->speed == USB_SPEED_SUPER)
+               brcmf_dbg(USB, "Broadcom super speed USB wireless device detected\n");
+       else if (usb->speed == USB_SPEED_HIGH)
                brcmf_dbg(USB, "Broadcom high speed USB wireless device detected\n");
        else
                brcmf_dbg(USB, "Broadcom full speed USB wireless device detected\n");
@@ -1455,23 +1374,18 @@ static int brcmf_usb_resume(struct usb_interface *intf)
        struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
 
        brcmf_dbg(USB, "Enter\n");
-       if (!brcmf_attach(devinfo->dev))
-               return brcmf_bus_start(&usb->dev);
-
-       return 0;
+       return brcmf_usb_bus_setup(devinfo);
 }
 
 static int brcmf_usb_reset_resume(struct usb_interface *intf)
 {
        struct usb_device *usb = interface_to_usbdev(intf);
        struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
-
        brcmf_dbg(USB, "Enter\n");
 
-       if (!brcmf_usb_fw_download(devinfo))
-               return brcmf_usb_resume(intf);
-
-       return -EIO;
+       return brcmf_fw_get_firmwares(&usb->dev, 0,
+                                     brcmf_usb_get_fwname(devinfo), NULL,
+                                     brcmf_usb_probe_phase2);
 }
 
 #define BRCMF_USB_VENDOR_ID_BROADCOM   0x0a5c
@@ -1506,16 +1420,6 @@ static struct usb_driver brcmf_usbdrvr = {
        .disable_hub_initiated_lpm = 1,
 };
 
-static void brcmf_release_fw(struct list_head *q)
-{
-       struct brcmf_usb_image *fw_image, *next;
-
-       list_for_each_entry_safe(fw_image, next, q, list) {
-               vfree(fw_image->image);
-               list_del_init(&fw_image->list);
-       }
-}
-
 static int brcmf_usb_reset_device(struct device *dev, void *notused)
 {
        /* device past is the usb interface so we
@@ -1534,12 +1438,10 @@ void brcmf_usb_exit(void)
        ret = driver_for_each_device(drv, NULL, NULL,
                                     brcmf_usb_reset_device);
        usb_deregister(&brcmf_usbdrvr);
-       brcmf_release_fw(&fw_image_list);
 }
 
 void brcmf_usb_register(void)
 {
        brcmf_dbg(USB, "Enter\n");
-       INIT_LIST_HEAD(&fw_image_list);
        usb_register(&brcmf_usbdrvr);
 }
index be1985296bdc75e725cdc161aa4101cea4a19476..d8fa276e368b4fc038b62a941191b3d25393b8f6 100644 (file)
@@ -221,9 +221,9 @@ static const struct ieee80211_regdomain brcmf_regdom = {
                 */
                REG_RULE(2484-10, 2484+10, 20, 6, 20, 0),
                /* IEEE 802.11a, channel 36..64 */
-               REG_RULE(5150-10, 5350+10, 40, 6, 20, 0),
+               REG_RULE(5150-10, 5350+10, 80, 6, 20, 0),
                /* IEEE 802.11a, channel 100..165 */
-               REG_RULE(5470-10, 5850+10, 40, 6, 20, 0), }
+               REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), }
 };
 
 static const u32 __wl_cipher_suites[] = {
@@ -341,6 +341,60 @@ static u8 brcmf_mw_to_qdbm(u16 mw)
        return qdbm;
 }
 
+static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
+                              struct cfg80211_chan_def *ch)
+{
+       struct brcmu_chan ch_inf;
+       s32 primary_offset;
+
+       brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n",
+                 ch->chan->center_freq, ch->center_freq1, ch->width);
+       ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1);
+       primary_offset = ch->center_freq1 - ch->chan->center_freq;
+       switch (ch->width) {
+       case NL80211_CHAN_WIDTH_20:
+               ch_inf.bw = BRCMU_CHAN_BW_20;
+               WARN_ON(primary_offset != 0);
+               break;
+       case NL80211_CHAN_WIDTH_40:
+               ch_inf.bw = BRCMU_CHAN_BW_40;
+               if (primary_offset < 0)
+                       ch_inf.sb = BRCMU_CHAN_SB_U;
+               else
+                       ch_inf.sb = BRCMU_CHAN_SB_L;
+               break;
+       case NL80211_CHAN_WIDTH_80:
+               ch_inf.bw = BRCMU_CHAN_BW_80;
+               if (primary_offset < 0) {
+                       if (primary_offset < -CH_10MHZ_APART)
+                               ch_inf.sb = BRCMU_CHAN_SB_UU;
+                       else
+                               ch_inf.sb = BRCMU_CHAN_SB_UL;
+               } else {
+                       if (primary_offset > CH_10MHZ_APART)
+                               ch_inf.sb = BRCMU_CHAN_SB_LL;
+                       else
+                               ch_inf.sb = BRCMU_CHAN_SB_LU;
+               }
+               break;
+       default:
+               WARN_ON_ONCE(1);
+       }
+       switch (ch->chan->band) {
+       case IEEE80211_BAND_2GHZ:
+               ch_inf.band = BRCMU_CHAN_BAND_2G;
+               break;
+       case IEEE80211_BAND_5GHZ:
+               ch_inf.band = BRCMU_CHAN_BAND_5G;
+               break;
+       default:
+               WARN_ON_ONCE(1);
+       }
+       d11inf->encchspec(&ch_inf);
+
+       return ch_inf.chspec;
+}
+
 u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
                        struct ieee80211_channel *ch)
 {
@@ -586,6 +640,9 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
                if (err)
                        brcmf_err("Scan abort  failed\n");
        }
+
+       brcmf_set_mpc(ifp, 1);
+
        /*
         * e-scan can be initiated by scheduled scan
         * which takes precedence.
@@ -595,12 +652,10 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
                cfg->sched_escan = false;
                if (!aborted)
                        cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
-               brcmf_set_mpc(ifp, 1);
        } else if (scan_request) {
                brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
                          aborted ? "Aborted" : "Done");
                cfg80211_scan_done(scan_request, aborted);
-               brcmf_set_mpc(ifp, 1);
        }
        if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
                brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
@@ -1236,8 +1291,8 @@ brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
                                params->chandef.chan->center_freq);
                if (params->channel_fixed) {
                        /* adding chanspec */
-                       chanspec = channel_to_chanspec(&cfg->d11inf,
-                                                      params->chandef.chan);
+                       chanspec = chandef_to_chanspec(&cfg->d11inf,
+                                                      &params->chandef);
                        join_params.params_le.chanspec_list[0] =
                                cpu_to_le16(chanspec);
                        join_params.params_le.chanspec_num = cpu_to_le32(1);
@@ -2182,7 +2237,7 @@ brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
 
 static s32
 brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
-                          u8 *mac, struct station_info *sinfo)
+                          const u8 *mac, struct station_info *sinfo)
 {
        struct brcmf_if *ifp = netdev_priv(ndev);
        struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
@@ -3124,7 +3179,7 @@ brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
        }
 
        if (!request->n_ssids || !request->n_match_sets) {
-               brcmf_err("Invalid sched scan req!! n_ssids:%d\n",
+               brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n",
                          request->n_ssids);
                return -EINVAL;
        }
@@ -3733,23 +3788,6 @@ brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
        return err;
 }
 
-static s32
-brcmf_cfg80211_set_channel(struct brcmf_cfg80211_info *cfg,
-                          struct brcmf_if *ifp,
-                          struct ieee80211_channel *channel)
-{
-       u16 chanspec;
-       s32 err;
-
-       brcmf_dbg(TRACE, "band=%d, center_freq=%d\n", channel->band,
-                 channel->center_freq);
-
-       chanspec = channel_to_chanspec(&cfg->d11inf, channel);
-       err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
-
-       return err;
-}
-
 static s32
 brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
                        struct cfg80211_ap_settings *settings)
@@ -3765,11 +3803,12 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
        struct brcmf_join_params join_params;
        enum nl80211_iftype dev_role;
        struct brcmf_fil_bss_enable_le bss_enable;
+       u16 chanspec;
 
-       brcmf_dbg(TRACE, "channel_type=%d, beacon_interval=%d, dtim_period=%d,\n",
-                 cfg80211_get_chandef_type(&settings->chandef),
-                 settings->beacon_interval,
-                 settings->dtim_period);
+       brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
+                 settings->chandef.chan->hw_value,
+                 settings->chandef.center_freq1, settings->chandef.width,
+                 settings->beacon_interval, settings->dtim_period);
        brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
                  settings->ssid, settings->ssid_len, settings->auth_type,
                  settings->inactivity_timeout);
@@ -3826,9 +3865,10 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
 
        brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
 
-       err = brcmf_cfg80211_set_channel(cfg, ifp, settings->chandef.chan);
+       chanspec = chandef_to_chanspec(&cfg->d11inf, &settings->chandef);
+       err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
        if (err < 0) {
-               brcmf_err("Set Channel failed, %d\n", err);
+               brcmf_err("Set Channel failed: chspec=%d, %d\n", chanspec, err);
                goto exit;
        }
 
@@ -3975,7 +4015,7 @@ brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
 
 static int
 brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
-                          u8 *mac)
+                          const u8 *mac)
 {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
        struct brcmf_scb_val_le scbval;
@@ -4203,7 +4243,7 @@ static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
 }
 
 static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
-                                   struct net_device *ndev, u8 *peer,
+                                   struct net_device *ndev, const u8 *peer,
                                    enum nl80211_tdls_operation oper)
 {
        struct brcmf_if *ifp;
@@ -4364,6 +4404,8 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
                        WIPHY_FLAG_OFFCHAN_TX |
                        WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
                        WIPHY_FLAG_SUPPORTS_TDLS;
+       if (!brcmf_roamoff)
+               wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
        wiphy->mgmt_stypes = brcmf_txrx_stypes;
        wiphy->max_remain_on_channel_duration = 5000;
        brcmf_wiphy_pno_params(wiphy);
@@ -4685,7 +4727,6 @@ brcmf_notify_connect_status(struct brcmf_if *ifp,
        struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
        struct ieee80211_channel *chan;
        s32 err = 0;
-       u16 reason;
 
        if (brcmf_is_apmode(ifp->vif)) {
                err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
@@ -4706,16 +4747,6 @@ brcmf_notify_connect_status(struct brcmf_if *ifp,
                brcmf_dbg(CONN, "Linkdown\n");
                if (!brcmf_is_ibssmode(ifp->vif)) {
                        brcmf_bss_connect_done(cfg, ndev, e, false);
-                       if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED,
-                                              &ifp->vif->sme_state)) {
-                               reason = 0;
-                               if (((e->event_code == BRCMF_E_DEAUTH_IND) ||
-                                    (e->event_code == BRCMF_E_DISASSOC_IND)) &&
-                                   (e->reason != WLAN_REASON_UNSPECIFIED))
-                                       reason = e->reason;
-                               cfg80211_disconnected(ndev, reason, NULL, 0,
-                                                     GFP_KERNEL);
-                       }
                }
                brcmf_link_down(ifp->vif);
                brcmf_init_prof(ndev_to_prof(ndev));
@@ -5215,6 +5246,9 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg,
                if (!(bw_cap[band] & WLC_BW_40MHZ_BIT) &&
                    ch.bw == BRCMU_CHAN_BW_40)
                        continue;
+               if (!(bw_cap[band] & WLC_BW_80MHZ_BIT) &&
+                   ch.bw == BRCMU_CHAN_BW_80)
+                       continue;
                update = false;
                for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) {
                        if (band_chan_arr[j].hw_value == ch.chnum) {
@@ -5231,10 +5265,13 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg,
                                ieee80211_channel_to_frequency(ch.chnum, band);
                        band_chan_arr[index].hw_value = ch.chnum;
 
-                       if (ch.bw == BRCMU_CHAN_BW_40) {
-                               /* assuming the order is HT20, HT40 Upper,
-                                * HT40 lower from chanspecs
-                                */
+                       /* assuming the chanspecs order is HT20,
+                        * HT40 upper, HT40 lower, and VHT80.
+                        */
+                       if (ch.bw == BRCMU_CHAN_BW_80) {
+                               band_chan_arr[index].flags &=
+                                       ~IEEE80211_CHAN_NO_80MHZ;
+                       } else if (ch.bw == BRCMU_CHAN_BW_40) {
                                ht40_flag = band_chan_arr[index].flags &
                                            IEEE80211_CHAN_NO_HT40;
                                if (ch.sb == BRCMU_CHAN_SB_U) {
@@ -5255,8 +5292,13 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg,
                                                    IEEE80211_CHAN_NO_HT40MINUS;
                                }
                        } else {
+                               /* disable other bandwidths for now as mentioned
+                                * order assure they are enabled for subsequent
+                                * chanspecs.
+                                */
                                band_chan_arr[index].flags =
-                                                       IEEE80211_CHAN_NO_HT40;
+                                               IEEE80211_CHAN_NO_HT40 |
+                                               IEEE80211_CHAN_NO_80MHZ;
                                ch.bw = BRCMU_CHAN_BW_20;
                                cfg->d11inf.encchspec(&ch);
                                channel = ch.chspec;
@@ -5323,13 +5365,63 @@ static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
        }
 }
 
+static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
+                               u32 bw_cap[2], u32 nchain)
+{
+       band->ht_cap.ht_supported = true;
+       if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
+               band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
+               band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+       }
+       band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
+       band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
+       band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+       band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
+       memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
+       band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+}
+
+static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
+{
+       u16 mcs_map;
+       int i;
+
+       for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)
+               mcs_map = (mcs_map << 2) | supp;
+
+       return cpu_to_le16(mcs_map);
+}
+
+static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
+                                u32 bw_cap[2], u32 nchain)
+{
+       __le16 mcs_map;
+
+       /* not allowed in 2.4G band */
+       if (band->band == IEEE80211_BAND_2GHZ)
+               return;
+
+       band->vht_cap.vht_supported = true;
+       /* 80MHz is mandatory */
+       band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
+       if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {
+               band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+               band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
+       }
+       /* all support 256-QAM */
+       mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
+       band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
+       band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
+}
+
 static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg)
 {
        struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
        struct wiphy *wiphy;
        s32 phy_list;
        u32 band_list[3];
-       u32 nmode;
+       u32 nmode = 0;
+       u32 vhtmode = 0;
        u32 bw_cap[2] = { 0, 0 };
        u32 rxchain;
        u32 nchain;
@@ -5360,14 +5452,16 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg)
        brcmf_dbg(INFO, "BRCMF_C_GET_BANDLIST reported: 0x%08x 0x%08x 0x%08x phy\n",
                  band_list[0], band_list[1], band_list[2]);
 
+       (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
        err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
        if (err) {
                brcmf_err("nmode error (%d)\n", err);
        } else {
                brcmf_get_bwcap(ifp, bw_cap);
        }
-       brcmf_dbg(INFO, "nmode=%d, bw_cap=(%d, %d)\n", nmode,
-                 bw_cap[IEEE80211_BAND_2GHZ], bw_cap[IEEE80211_BAND_5GHZ]);
+       brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
+                 nmode, vhtmode, bw_cap[IEEE80211_BAND_2GHZ],
+                 bw_cap[IEEE80211_BAND_5GHZ]);
 
        err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
        if (err) {
@@ -5398,17 +5492,10 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg)
                else
                        continue;
 
-               if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
-                       band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
-                       band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
-               }
-               band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
-               band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
-               band->ht_cap.ht_supported = true;
-               band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
-               band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
-               memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
-               band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+               if (nmode)
+                       brcmf_update_ht_cap(band, bw_cap, nchain);
+               if (vhtmode)
+                       brcmf_update_vht_cap(band, bw_cap, nchain);
                bands[band->band] = band;
        }
 
index 8c5fa4e581392d73d28ba29b790151be8f37f569..43c71bfaa4744fe4094069b28ff4d9084d6c679f 100644 (file)
@@ -897,7 +897,8 @@ static bool brcms_tx_flush_completed(struct brcms_info *wl)
        return result;
 }
 
-static void brcms_ops_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+static void brcms_ops_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                           u32 queues, bool drop)
 {
        struct brcms_info *wl = hw->priv;
        int ret;
index 9417cb5a2553f70fc2d2733f80c65d61477dc185..af8ba64ace39286e3214d69bfc23f776e66b6336 100644 (file)
@@ -4870,14 +4870,11 @@ static void brcms_c_detach_module(struct brcms_c_info *wlc)
 /*
  * low level detach
  */
-static int brcms_b_detach(struct brcms_c_info *wlc)
+static void brcms_b_detach(struct brcms_c_info *wlc)
 {
        uint i;
        struct brcms_hw_band *band;
        struct brcms_hardware *wlc_hw = wlc->hw;
-       int callbacks;
-
-       callbacks = 0;
 
        brcms_b_detach_dmapio(wlc_hw);
 
@@ -4900,9 +4897,6 @@ static int brcms_b_detach(struct brcms_c_info *wlc)
                ai_detach(wlc_hw->sih);
                wlc_hw->sih = NULL;
        }
-
-       return callbacks;
-
 }
 
 /*
@@ -4917,14 +4911,15 @@ static int brcms_b_detach(struct brcms_c_info *wlc)
  */
 uint brcms_c_detach(struct brcms_c_info *wlc)
 {
-       uint callbacks = 0;
+       uint callbacks;
 
        if (wlc == NULL)
                return 0;
 
-       callbacks += brcms_b_detach(wlc);
+       brcms_b_detach(wlc);
 
        /* delete software timers */
+       callbacks = 0;
        if (!brcms_c_radio_monitor_stop(wlc))
                callbacks++;
 
index 30e54e2c6c9b6f87ccfe618553c94cf1b2cedee4..2b2522bdd8eb516b422a779b4057b89bd959400e 100644 (file)
 #include <brcmu_wifi.h>
 #include <brcmu_d11.h>
 
-static void brcmu_d11n_encchspec(struct brcmu_chan *ch)
+static u16 d11n_sb(enum brcmu_chan_sb sb)
 {
-       ch->chspec = ch->chnum & BRCMU_CHSPEC_CH_MASK;
+       switch (sb) {
+       case BRCMU_CHAN_SB_NONE:
+               return BRCMU_CHSPEC_D11N_SB_N;
+       case BRCMU_CHAN_SB_L:
+               return BRCMU_CHSPEC_D11N_SB_L;
+       case BRCMU_CHAN_SB_U:
+               return BRCMU_CHSPEC_D11N_SB_U;
+       default:
+               WARN_ON(1);
+       }
+       return 0;
+}
 
-       switch (ch->bw) {
+static u16 d11n_bw(enum brcmu_chan_bw bw)
+{
+       switch (bw) {
        case BRCMU_CHAN_BW_20:
-               ch->chspec |= BRCMU_CHSPEC_D11N_BW_20 | BRCMU_CHSPEC_D11N_SB_N;
-               break;
+               return BRCMU_CHSPEC_D11N_BW_20;
        case BRCMU_CHAN_BW_40:
+               return BRCMU_CHSPEC_D11N_BW_40;
        default:
-               WARN_ON_ONCE(1);
-               break;
+               WARN_ON(1);
        }
+       return 0;
+}
+
+static void brcmu_d11n_encchspec(struct brcmu_chan *ch)
+{
+       if (ch->bw == BRCMU_CHAN_BW_20)
+               ch->sb = BRCMU_CHAN_SB_NONE;
+
+       ch->chspec = 0;
+       brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_CH_MASK,
+                       BRCMU_CHSPEC_CH_SHIFT, ch->chnum);
+       brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11N_SB_MASK,
+                       0, d11n_sb(ch->sb));
+       brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11N_BW_MASK,
+                       0, d11n_bw(ch->bw));
 
        if (ch->chnum <= CH_MAX_2G_CHANNEL)
                ch->chspec |= BRCMU_CHSPEC_D11N_BND_2G;
@@ -41,23 +68,34 @@ static void brcmu_d11n_encchspec(struct brcmu_chan *ch)
                ch->chspec |= BRCMU_CHSPEC_D11N_BND_5G;
 }
 
-static void brcmu_d11ac_encchspec(struct brcmu_chan *ch)
+static u16 d11ac_bw(enum brcmu_chan_bw bw)
 {
-       ch->chspec = ch->chnum & BRCMU_CHSPEC_CH_MASK;
-
-       switch (ch->bw) {
+       switch (bw) {
        case BRCMU_CHAN_BW_20:
-               ch->chspec |= BRCMU_CHSPEC_D11AC_BW_20;
-               break;
+               return BRCMU_CHSPEC_D11AC_BW_20;
        case BRCMU_CHAN_BW_40:
+               return BRCMU_CHSPEC_D11AC_BW_40;
        case BRCMU_CHAN_BW_80:
-       case BRCMU_CHAN_BW_80P80:
-       case BRCMU_CHAN_BW_160:
+               return BRCMU_CHSPEC_D11AC_BW_80;
        default:
-               WARN_ON_ONCE(1);
-               break;
+               WARN_ON(1);
        }
+       return 0;
+}
 
+static void brcmu_d11ac_encchspec(struct brcmu_chan *ch)
+{
+       if (ch->bw == BRCMU_CHAN_BW_20 || ch->sb == BRCMU_CHAN_SB_NONE)
+               ch->sb = BRCMU_CHAN_SB_L;
+
+       brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_CH_MASK,
+                       BRCMU_CHSPEC_CH_SHIFT, ch->chnum);
+       brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11AC_SB_MASK,
+                       BRCMU_CHSPEC_D11AC_SB_SHIFT, ch->sb);
+       brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11AC_BW_MASK,
+                       0, d11ac_bw(ch->bw));
+
+       ch->chspec &= ~BRCMU_CHSPEC_D11AC_BND_MASK;
        if (ch->chnum <= CH_MAX_2G_CHANNEL)
                ch->chspec |= BRCMU_CHSPEC_D11AC_BND_2G;
        else
@@ -73,6 +111,7 @@ static void brcmu_d11n_decchspec(struct brcmu_chan *ch)
        switch (ch->chspec & BRCMU_CHSPEC_D11N_BW_MASK) {
        case BRCMU_CHSPEC_D11N_BW_20:
                ch->bw = BRCMU_CHAN_BW_20;
+               ch->sb = BRCMU_CHAN_SB_NONE;
                break;
        case BRCMU_CHSPEC_D11N_BW_40:
                ch->bw = BRCMU_CHAN_BW_40;
@@ -112,6 +151,7 @@ static void brcmu_d11ac_decchspec(struct brcmu_chan *ch)
        switch (ch->chspec & BRCMU_CHSPEC_D11AC_BW_MASK) {
        case BRCMU_CHSPEC_D11AC_BW_20:
                ch->bw = BRCMU_CHAN_BW_20;
+               ch->sb = BRCMU_CHAN_SB_NONE;
                break;
        case BRCMU_CHSPEC_D11AC_BW_40:
                ch->bw = BRCMU_CHAN_BW_40;
@@ -128,6 +168,25 @@ static void brcmu_d11ac_decchspec(struct brcmu_chan *ch)
                break;
        case BRCMU_CHSPEC_D11AC_BW_80:
                ch->bw = BRCMU_CHAN_BW_80;
+               ch->sb = brcmu_maskget16(ch->chspec, BRCMU_CHSPEC_D11AC_SB_MASK,
+                                        BRCMU_CHSPEC_D11AC_SB_SHIFT);
+               switch (ch->sb) {
+               case BRCMU_CHAN_SB_LL:
+                       ch->chnum -= CH_30MHZ_APART;
+                       break;
+               case BRCMU_CHAN_SB_LU:
+                       ch->chnum -= CH_10MHZ_APART;
+                       break;
+               case BRCMU_CHAN_SB_UL:
+                       ch->chnum += CH_10MHZ_APART;
+                       break;
+               case BRCMU_CHAN_SB_UU:
+                       ch->chnum += CH_30MHZ_APART;
+                       break;
+               default:
+                       WARN_ON_ONCE(1);
+                       break;
+               }
                break;
        case BRCMU_CHSPEC_D11AC_BW_8080:
        case BRCMU_CHSPEC_D11AC_BW_160:
index 8660a2cba09810428f127967c996a02cfbc174a1..f9745ea8b3e042d4cec222518d7b2fb76439eedf 100644 (file)
@@ -108,13 +108,7 @@ enum brcmu_chan_bw {
 };
 
 enum brcmu_chan_sb {
-       BRCMU_CHAN_SB_NONE = 0,
-       BRCMU_CHAN_SB_L,
-       BRCMU_CHAN_SB_U,
-       BRCMU_CHAN_SB_LL,
-       BRCMU_CHAN_SB_LU,
-       BRCMU_CHAN_SB_UL,
-       BRCMU_CHAN_SB_UU,
+       BRCMU_CHAN_SB_NONE = -1,
        BRCMU_CHAN_SB_LLL,
        BRCMU_CHAN_SB_LLU,
        BRCMU_CHAN_SB_LUL,
@@ -123,6 +117,12 @@ enum brcmu_chan_sb {
        BRCMU_CHAN_SB_ULU,
        BRCMU_CHAN_SB_UUL,
        BRCMU_CHAN_SB_UUU,
+       BRCMU_CHAN_SB_L = BRCMU_CHAN_SB_LLL,
+       BRCMU_CHAN_SB_U = BRCMU_CHAN_SB_LLU,
+       BRCMU_CHAN_SB_LL = BRCMU_CHAN_SB_LLL,
+       BRCMU_CHAN_SB_LU = BRCMU_CHAN_SB_LLU,
+       BRCMU_CHAN_SB_UL = BRCMU_CHAN_SB_LUL,
+       BRCMU_CHAN_SB_UU = BRCMU_CHAN_SB_LUU,
 };
 
 struct brcmu_chan {
index 74419d4bd123772f94e222326c61ee4e93f0f9d3..76b5d3a8629481df004c333a7bcd1990bec8e40c 100644 (file)
@@ -29,6 +29,7 @@
 #define CH_UPPER_SB                    0x01
 #define CH_LOWER_SB                    0x02
 #define CH_EWA_VALID                   0x04
+#define CH_30MHZ_APART                 6
 #define CH_20MHZ_APART                 4
 #define CH_10MHZ_APART                 2
 #define CH_5MHZ_APART                  1 /* 2G band channels are 5 Mhz apart */
index 103f7bce893208c30eb692cca9aa9adf51b8d8b9..cd0cad7f775993661af9e8f4577c89976b15b811 100644 (file)
@@ -936,7 +936,8 @@ static int __cw1200_flush(struct cw1200_common *priv, bool drop)
        return ret;
 }
 
-void cw1200_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+void cw1200_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                 u32 queues, bool drop)
 {
        struct cw1200_common *priv = hw->priv;
 
index 35babb62cc6a8b5a952f0a989c15ad4edf802f17..b7e386b7662b668b8299a9ab52f3c22f2633dfe5 100644 (file)
@@ -40,7 +40,8 @@ int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
 
 int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value);
 
-void cw1200_flush(struct ieee80211_hw *hw, u32 queues, bool drop);
+void cw1200_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                 u32 queues, bool drop);
 
 u64 cw1200_prepare_multicast(struct ieee80211_hw *hw,
                             struct netdev_hw_addr_list *mc_list);
index 67db34e56d7eb0765c5e75025e02470cae01da64..52919ad4272622aeb92d8b9d3d74daf1715a3d40 100644 (file)
@@ -882,7 +882,7 @@ void hostap_setup_dev(struct net_device *dev, local_info_t *local,
        dev->mtu = local->mtu;
 
 
-       SET_ETHTOOL_OPS(dev, &prism2_ethtool_ops);
+       dev->ethtool_ops = &prism2_ethtool_ops;
 
 }
 
index d37a6fd90d400a6dfd90d20cdf02d73e7e536d42..b598e2803500ec7b109111f5118dde1414547a22 100644 (file)
@@ -573,7 +573,7 @@ il3945_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb)
                rx_status.flag |= RX_FLAG_SHORTPRE;
 
        if ((unlikely(rx_stats->phy_count > 20))) {
-               D_DROP("dsp size out of range [0,20]: %d/n",
+               D_DROP("dsp size out of range [0,20]: %d\n",
                       rx_stats->phy_count);
                return;
        }
index 888ad5c74639e351a3727c8b934a68f7969849e5..c159c05db6ef212b8b684c5726e61a8a97c62c39 100644 (file)
@@ -670,7 +670,7 @@ il4965_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb)
        }
 
        if ((unlikely(phy_res->cfg_phy_cnt > 20))) {
-               D_DROP("dsp size out of range [0,20]: %d/n",
+               D_DROP("dsp size out of range [0,20]: %d\n",
                       phy_res->cfg_phy_cnt);
                return;
        }
index 4f42174d999412102e273744fc39ff692b9a9234..ecc674627e6e10b30a5a7b11ab3150c1ad42b37e 100644 (file)
@@ -4755,7 +4755,8 @@ il_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 }
 EXPORT_SYMBOL(il_mac_change_interface);
 
-void il_mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+void il_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                 u32 queues, bool drop)
 {
        struct il_priv *il = hw->priv;
        unsigned long timeout = jiffies + msecs_to_jiffies(500);
index dfb13c70efe83ea8415f93ef2dad9c97a0cb6891..ea5c0f863c4ee35b2cf6738569a5cb3253553c12 100644 (file)
@@ -1723,7 +1723,8 @@ void il_mac_remove_interface(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif);
 int il_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                            enum nl80211_iftype newtype, bool newp2p);
-void il_mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop);
+void il_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                 u32 queues, bool drop);
 int il_alloc_txq_mem(struct il_priv *il);
 void il_free_txq_mem(struct il_priv *il);
 
index 74b3b4de7bb7de57ef42fabfe36866eef3b05287..7fd50428b93494416db2d4c46a3862c18c5bc2e1 100644 (file)
@@ -2,10 +2,6 @@ config IWLWIFI
        tristate "Intel Wireless WiFi Next Gen AGN - Wireless-N/Advanced-N/Ultimate-N (iwlwifi) "
        depends on PCI && MAC80211 && HAS_IOMEM
        select FW_LOADER
-       select NEW_LEDS
-       select LEDS_CLASS
-       select LEDS_TRIGGERS
-       select MAC80211_LEDS
        ---help---
          Select to build the driver supporting the:
 
@@ -43,6 +39,14 @@ config IWLWIFI
          say M here and read <file:Documentation/kbuild/modules.txt>.  The
          module will be called iwlwifi.
 
+config IWLWIFI_LEDS
+       bool
+       depends on IWLWIFI
+       depends on LEDS_CLASS=y || LEDS_CLASS=IWLWIFI
+       select LEDS_TRIGGERS
+       select MAC80211_LEDS
+       default y
+
 config IWLDVM
        tristate "Intel Wireless WiFi DVM Firmware support"
        depends on IWLWIFI
@@ -124,7 +128,6 @@ config IWLWIFI_DEBUG_EXPERIMENTAL_UCODE
          Enable use of experimental ucode for testing and debugging.
 
 config IWLWIFI_DEVICE_TRACING
-
        bool "iwlwifi device access tracing"
        depends on IWLWIFI
        depends on EVENT_TRACING
index dce7ab2e0c4bfdda46c7ed5f7b1429c7633e7ce8..4d19685f31c3ad97fc48ad4a88ffb6b491abdf2c 100644 (file)
@@ -4,9 +4,10 @@ iwldvm-objs            += main.o rs.o mac80211.o ucode.o tx.o
 iwldvm-objs            += lib.o calib.o tt.o sta.o rx.o
 
 iwldvm-objs            += power.o
-iwldvm-objs            += scan.o led.o
+iwldvm-objs            += scan.o
 iwldvm-objs            += rxon.o devices.o
 
+iwldvm-$(CONFIG_IWLWIFI_LEDS) += led.o
 iwldvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o
 
 ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../
index be1086c87157c158834d622546e8bfc2b25b7dff..20e6aa9107009ccc33246190534cbc7674c54d03 100644 (file)
@@ -94,7 +94,6 @@ int iwl_send_calib_results(struct iwl_priv *priv)
 {
        struct iwl_host_cmd hcmd = {
                .id = REPLY_PHY_CALIBRATION_CMD,
-               .flags = CMD_SYNC,
        };
        struct iwl_calib_result *res;
 
index d2fe2596d54ec4525a7661a9b381f3e5352bae61..0ffb6ff1a255f8ac609ba3799c02489ab853eea1 100644 (file)
@@ -1481,7 +1481,7 @@ static ssize_t iwl_dbgfs_ucode_bt_stats_read(struct file *file,
 
        /* make request to uCode to retrieve statistics information */
        mutex_lock(&priv->mutex);
-       ret = iwl_send_statistics_request(priv, CMD_SYNC, false);
+       ret = iwl_send_statistics_request(priv, 0, false);
        mutex_unlock(&priv->mutex);
 
        if (ret)
@@ -1868,7 +1868,7 @@ static ssize_t iwl_dbgfs_clear_ucode_statistics_write(struct file *file,
 
        /* make request to uCode to retrieve statistics information */
        mutex_lock(&priv->mutex);
-       iwl_send_statistics_request(priv, CMD_SYNC, true);
+       iwl_send_statistics_request(priv, 0, true);
        mutex_unlock(&priv->mutex);
 
        return count;
@@ -2188,7 +2188,6 @@ static int iwl_cmd_echo_test(struct iwl_priv *priv)
        struct iwl_host_cmd cmd = {
                .id = REPLY_ECHO,
                .len = { 0 },
-               .flags = CMD_SYNC,
        };
 
        ret = iwl_dvm_send_cmd(priv, &cmd);
@@ -2320,7 +2319,7 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct file *file,
        mutex_lock(&priv->mutex);
 
        /* take the return value to make compiler happy - it will fail anyway */
-       ret = iwl_dvm_send_cmd_pdu(priv, REPLY_ERROR, CMD_SYNC, 0, NULL);
+       ret = iwl_dvm_send_cmd_pdu(priv, REPLY_ERROR, 0, 0, NULL);
 
        mutex_unlock(&priv->mutex);
 
index 3441f70d0ff911594dfd4066e782f0ca042e02d6..a6f22c32a27994000f578c2baff08a2f62042aa0 100644 (file)
@@ -888,9 +888,11 @@ struct iwl_priv {
 
        struct iwl_event_log event_log;
 
+#ifdef CONFIG_IWLWIFI_LEDS
        struct led_classdev led;
        unsigned long blink_on, blink_off;
        bool led_registered;
+#endif
 
        /* WoWLAN GTK rekey data */
        u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN];
index 758c54eeb206718f8077aa2686304ea1a1e4c9bb..34b41e5f7cfccc34af9fdf63d405222138c18b37 100644 (file)
@@ -417,7 +417,6 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv,
        struct iwl_host_cmd hcmd = {
                .id = REPLY_CHANNEL_SWITCH,
                .len = { sizeof(cmd), },
-               .flags = CMD_SYNC,
                .data = { &cmd, },
        };
 
@@ -579,7 +578,6 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv,
        struct iwl_host_cmd hcmd = {
                .id = REPLY_CHANNEL_SWITCH,
                .len = { sizeof(*cmd), },
-               .flags = CMD_SYNC,
                .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
        };
        int err;
index 6a0817d9c4fa05f15e1ceffd4272925451994f80..1c6b2252d0f24cb8ee2b94d0f62040361304b58e 100644 (file)
@@ -36,8 +36,20 @@ struct iwl_priv;
 #define IWL_LED_ACTIVITY       (0<<1)
 #define IWL_LED_LINK           (1<<1)
 
+#ifdef CONFIG_IWLWIFI_LEDS
 void iwlagn_led_enable(struct iwl_priv *priv);
 void iwl_leds_init(struct iwl_priv *priv);
 void iwl_leds_exit(struct iwl_priv *priv);
+#else
+static inline void iwlagn_led_enable(struct iwl_priv *priv)
+{
+}
+static inline void iwl_leds_init(struct iwl_priv *priv)
+{
+}
+static inline void iwl_leds_exit(struct iwl_priv *priv)
+{
+}
+#endif
 
 #endif /* __iwl_leds_h__ */
index 576f7ee38ca5894150cba0e54eff4bcc5ff2f079..2191621d69c1618375482d3e3a1dc7d8f2b7f192 100644 (file)
@@ -81,7 +81,7 @@ int iwlagn_send_tx_power(struct iwl_priv *priv)
        else
                tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD;
 
-       return iwl_dvm_send_cmd_pdu(priv, tx_ant_cfg_cmd, CMD_SYNC,
+       return iwl_dvm_send_cmd_pdu(priv, tx_ant_cfg_cmd, 0,
                        sizeof(tx_power_cmd), &tx_power_cmd);
 }
 
@@ -141,7 +141,6 @@ int iwlagn_txfifo_flush(struct iwl_priv *priv, u32 scd_q_msk)
        struct iwl_host_cmd cmd = {
                .id = REPLY_TXFIFO_FLUSH,
                .len = { sizeof(struct iwl_txfifo_flush_cmd), },
-               .flags = CMD_SYNC,
                .data = { &flush_cmd, },
        };
 
@@ -180,7 +179,7 @@ void iwlagn_dev_txfifo_flush(struct iwl_priv *priv)
                goto done;
        }
        IWL_DEBUG_INFO(priv, "wait transmit/flush all frames\n");
-       iwl_trans_wait_tx_queue_empty(priv->trans);
+       iwl_trans_wait_tx_queue_empty(priv->trans, 0xffffffff);
 done:
        ieee80211_wake_queues(priv->hw);
        mutex_unlock(&priv->mutex);
@@ -333,12 +332,12 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv)
                memcpy(&bt_cmd_v2.basic, &basic,
                        sizeof(basic));
                ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG,
-                       CMD_SYNC, sizeof(bt_cmd_v2), &bt_cmd_v2);
+                       0, sizeof(bt_cmd_v2), &bt_cmd_v2);
        } else {
                memcpy(&bt_cmd_v1.basic, &basic,
                        sizeof(basic));
                ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG,
-                       CMD_SYNC, sizeof(bt_cmd_v1), &bt_cmd_v1);
+                       0, sizeof(bt_cmd_v1), &bt_cmd_v1);
        }
        if (ret)
                IWL_ERR(priv, "failed to send BT Coex Config\n");
@@ -1044,7 +1043,6 @@ int iwlagn_send_patterns(struct iwl_priv *priv,
        struct iwl_host_cmd cmd = {
                .id = REPLY_WOWLAN_PATTERNS,
                .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
-               .flags = CMD_SYNC,
        };
        int i, err;
 
@@ -1201,7 +1199,6 @@ int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan)
                if (key_data.use_rsc_tsc) {
                        struct iwl_host_cmd rsc_tsc_cmd = {
                                .id = REPLY_WOWLAN_TSC_RSC_PARAMS,
-                               .flags = CMD_SYNC,
                                .data[0] = key_data.rsc_tsc,
                                .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
                                .len[0] = sizeof(*key_data.rsc_tsc),
@@ -1215,7 +1212,7 @@ int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan)
                if (key_data.use_tkip) {
                        ret = iwl_dvm_send_cmd_pdu(priv,
                                                 REPLY_WOWLAN_TKIP_PARAMS,
-                                                CMD_SYNC, sizeof(tkip_cmd),
+                                                0, sizeof(tkip_cmd),
                                                 &tkip_cmd);
                        if (ret)
                                goto out;
@@ -1231,20 +1228,20 @@ int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan)
 
                        ret = iwl_dvm_send_cmd_pdu(priv,
                                                 REPLY_WOWLAN_KEK_KCK_MATERIAL,
-                                                CMD_SYNC, sizeof(kek_kck_cmd),
+                                                0, sizeof(kek_kck_cmd),
                                                 &kek_kck_cmd);
                        if (ret)
                                goto out;
                }
        }
 
-       ret = iwl_dvm_send_cmd_pdu(priv, REPLY_D3_CONFIG, CMD_SYNC,
+       ret = iwl_dvm_send_cmd_pdu(priv, REPLY_D3_CONFIG, 0,
                                     sizeof(d3_cfg_cmd), &d3_cfg_cmd);
        if (ret)
                goto out;
 
        ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_WAKEUP_FILTER,
-                                CMD_SYNC, sizeof(wakeup_filter_cmd),
+                                0, sizeof(wakeup_filter_cmd),
                                 &wakeup_filter_cmd);
        if (ret)
                goto out;
index dd55c9cf7ba80376ef3ae507b434e79d6dd1cca4..29af7b51e3708788d02f4a1651205a348a5102dd 100644 (file)
@@ -1091,7 +1091,8 @@ static void iwlagn_configure_filter(struct ieee80211_hw *hw,
                        FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL;
 }
 
-static void iwlagn_mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+static void iwlagn_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                            u32 queues, bool drop)
 {
        struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
 
@@ -1119,7 +1120,7 @@ static void iwlagn_mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
                }
        }
        IWL_DEBUG_MAC80211(priv, "wait transmit/flush all frames\n");
-       iwl_trans_wait_tx_queue_empty(priv->trans);
+       iwl_trans_wait_tx_queue_empty(priv->trans, 0xffffffff);
 done:
        mutex_unlock(&priv->mutex);
        IWL_DEBUG_MAC80211(priv, "leave\n");
index 6a6df71af1d7ba6e4b4dfec16a2042c6cc357de4..0b7f46f0b079a442b327ecf80184fd00294c4377 100644 (file)
@@ -128,7 +128,6 @@ int iwlagn_send_beacon_cmd(struct iwl_priv *priv)
        struct iwl_tx_beacon_cmd *tx_beacon_cmd;
        struct iwl_host_cmd cmd = {
                .id = REPLY_TX_BEACON,
-               .flags = CMD_SYNC,
        };
        struct ieee80211_tx_info *info;
        u32 frame_size;
@@ -311,8 +310,7 @@ int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear)
                                        sizeof(struct iwl_statistics_cmd),
                                        &statistics_cmd);
        else
-               return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD,
-                                       CMD_SYNC,
+               return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD, 0,
                                        sizeof(struct iwl_statistics_cmd),
                                        &statistics_cmd);
 }
@@ -622,7 +620,7 @@ static void iwl_rf_kill_ct_config(struct iwl_priv *priv)
 
                ret = iwl_dvm_send_cmd_pdu(priv,
                                       REPLY_CT_KILL_CONFIG_CMD,
-                                      CMD_SYNC, sizeof(adv_cmd), &adv_cmd);
+                                      0, sizeof(adv_cmd), &adv_cmd);
                if (ret)
                        IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n");
                else
@@ -637,7 +635,7 @@ static void iwl_rf_kill_ct_config(struct iwl_priv *priv)
 
                ret = iwl_dvm_send_cmd_pdu(priv,
                                       REPLY_CT_KILL_CONFIG_CMD,
-                                      CMD_SYNC, sizeof(cmd), &cmd);
+                                      0, sizeof(cmd), &cmd);
                if (ret)
                        IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n");
                else
@@ -673,9 +671,7 @@ static int iwlagn_send_tx_ant_config(struct iwl_priv *priv, u8 valid_tx_ant)
 
        if (IWL_UCODE_API(priv->fw->ucode_ver) > 1) {
                IWL_DEBUG_HC(priv, "select valid tx ant: %u\n", valid_tx_ant);
-               return iwl_dvm_send_cmd_pdu(priv,
-                                       TX_ANT_CONFIGURATION_CMD,
-                                       CMD_SYNC,
+               return iwl_dvm_send_cmd_pdu(priv, TX_ANT_CONFIGURATION_CMD, 0,
                                        sizeof(struct iwl_tx_ant_config_cmd),
                                        &tx_ant_cmd);
        } else {
@@ -703,7 +699,7 @@ static void iwl_send_bt_config(struct iwl_priv *priv)
                (bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active");
 
        if (iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG,
-                            CMD_SYNC, sizeof(struct iwl_bt_cmd), &bt_cmd))
+                            0, sizeof(struct iwl_bt_cmd), &bt_cmd))
                IWL_ERR(priv, "failed to send BT Coex Config\n");
 }
 
@@ -987,7 +983,7 @@ static void iwl_bg_restart(struct work_struct *data)
                        ieee80211_restart_hw(priv->hw);
                else
                        IWL_ERR(priv,
-                               "Cannot request restart before registrating with mac80211");
+                               "Cannot request restart before registrating with mac80211\n");
        } else {
                WARN_ON(1);
        }
@@ -1127,7 +1123,6 @@ static void iwl_option_config(struct iwl_priv *priv)
 static int iwl_eeprom_init_hw_params(struct iwl_priv *priv)
 {
        struct iwl_nvm_data *data = priv->nvm_data;
-       char *debug_msg;
 
        if (data->sku_cap_11n_enable &&
            !priv->cfg->ht_params) {
@@ -1141,8 +1136,8 @@ static int iwl_eeprom_init_hw_params(struct iwl_priv *priv)
                return -EINVAL;
        }
 
-       debug_msg = "Device SKU: 24GHz %s %s, 52GHz %s %s, 11.n %s %s\n";
-       IWL_DEBUG_INFO(priv, debug_msg,
+       IWL_DEBUG_INFO(priv,
+                      "Device SKU: 24GHz %s %s, 52GHz %s %s, 11.n %s %s\n",
                       data->sku_cap_band_24GHz_enable ? "" : "NOT", "enabled",
                       data->sku_cap_band_52GHz_enable ? "" : "NOT", "enabled",
                       data->sku_cap_11n_enable ? "" : "NOT", "enabled");
@@ -1350,7 +1345,7 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
        iwl_set_hw_params(priv);
 
        if (!(priv->nvm_data->sku_cap_ipan_enable)) {
-               IWL_DEBUG_INFO(priv, "Your EEPROM disabled PAN");
+               IWL_DEBUG_INFO(priv, "Your EEPROM disabled PAN\n");
                ucode_flags &= ~IWL_UCODE_TLV_FLAGS_PAN;
                /*
                 * if not PAN, then don't support P2P -- might be a uCode
@@ -2019,10 +2014,10 @@ void iwlagn_lift_passive_no_rx(struct iwl_priv *priv)
 
        for (mq = 0; mq < IWLAGN_FIRST_AMPDU_QUEUE; mq++) {
                if (!test_bit(mq, &priv->transport_queue_stop)) {
-                       IWL_DEBUG_TX_QUEUES(priv, "Wake queue %d", mq);
+                       IWL_DEBUG_TX_QUEUES(priv, "Wake queue %d\n", mq);
                        ieee80211_wake_queue(priv->hw, mq);
                } else {
-                       IWL_DEBUG_TX_QUEUES(priv, "Don't wake queue %d", mq);
+                       IWL_DEBUG_TX_QUEUES(priv, "Don't wake queue %d\n", mq);
                }
        }
 
@@ -2053,6 +2048,17 @@ static bool iwl_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
        return false;
 }
 
+static void iwl_napi_add(struct iwl_op_mode *op_mode,
+                        struct napi_struct *napi,
+                        struct net_device *napi_dev,
+                        int (*poll)(struct napi_struct *, int),
+                        int weight)
+{
+       struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
+
+       ieee80211_napi_add(priv->hw, napi, napi_dev, poll, weight);
+}
+
 static const struct iwl_op_mode_ops iwl_dvm_ops = {
        .start = iwl_op_mode_dvm_start,
        .stop = iwl_op_mode_dvm_stop,
@@ -2065,6 +2071,7 @@ static const struct iwl_op_mode_ops iwl_dvm_ops = {
        .cmd_queue_full = iwl_cmd_queue_full,
        .nic_config = iwl_nic_config,
        .wimax_active = iwl_wimax_active,
+       .napi_add = iwl_napi_add,
 };
 
 /*****************************************************************************
index b4e61417013aff585bf8dcfa33b8fa9e3c5c84c6..f2c1439566b5ffa7814c02c041bf34bb3b3391d6 100644 (file)
@@ -278,7 +278,7 @@ static int iwl_set_power(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd)
                        le32_to_cpu(cmd->sleep_interval[3]),
                        le32_to_cpu(cmd->sleep_interval[4]));
 
-       return iwl_dvm_send_cmd_pdu(priv, POWER_TABLE_CMD, CMD_SYNC,
+       return iwl_dvm_send_cmd_pdu(priv, POWER_TABLE_CMD, 0,
                                sizeof(struct iwl_powertable_cmd), cmd);
 }
 
@@ -361,7 +361,7 @@ int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd,
 
                memcpy(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd));
        } else
-               IWL_ERR(priv, "set power fail, ret = %d", ret);
+               IWL_ERR(priv, "set power fail, ret = %d\n", ret);
 
        return ret;
 }
index aa773a2da4ab877f6b5876a28024a6c2e23ffa9b..32b78a66536db90bbb1e67f10e210df49f6b2d03 100644 (file)
@@ -1453,7 +1453,7 @@ static int rs_move_legacy_other(struct iwl_priv *priv,
                        tbl->action = IWL_LEGACY_SWITCH_SISO;
                break;
        default:
-               IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load);
+               IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load);
                break;
        }
 
@@ -1628,7 +1628,7 @@ static int rs_move_siso_to_other(struct iwl_priv *priv,
                        tbl->action = IWL_SISO_SWITCH_ANTENNA1;
                break;
        default:
-               IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load);
+               IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load);
                break;
        }
 
@@ -1799,7 +1799,7 @@ static int rs_move_mimo2_to_other(struct iwl_priv *priv,
                        tbl->action = IWL_MIMO2_SWITCH_SISO_A;
                break;
        default:
-               IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load);
+               IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load);
                break;
        }
 
@@ -1969,7 +1969,7 @@ static int rs_move_mimo3_to_other(struct iwl_priv *priv,
                        tbl->action = IWL_MIMO3_SWITCH_SISO_A;
                break;
        default:
-               IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load);
+               IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load);
                break;
        }
 
@@ -2709,7 +2709,7 @@ static void rs_initialize_lq(struct iwl_priv *priv,
        rs_set_expected_tpt_table(lq_sta, tbl);
        rs_fill_link_cmd(NULL, lq_sta, rate);
        priv->stations[lq_sta->lq.sta_id].lq = &lq_sta->lq;
-       iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_SYNC, true);
+       iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, 0, true);
 }
 
 static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta,
index cd8377346aff0c2936a6f0ee773ae8fd33042025..debec963c610d4693fb40ca22a27b9cd3b6310ac 100644 (file)
@@ -786,7 +786,7 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv,
 
        memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
 
-       ieee80211_rx_ni(priv->hw, skb);
+       ieee80211_rx(priv->hw, skb);
 }
 
 static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in)
index 503a81e581855729fde646408a8ae33b0195539f..ed50de6362ed1d5dcd56b45243ff0b140dcbafe5 100644 (file)
@@ -104,7 +104,7 @@ static int iwlagn_disable_bss(struct iwl_priv *priv,
 
        send->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
        ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd,
-                               CMD_SYNC, sizeof(*send), send);
+                               0, sizeof(*send), send);
 
        send->filter_flags = old_filter;
 
@@ -134,7 +134,7 @@ static int iwlagn_disable_pan(struct iwl_priv *priv,
        send->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
        send->dev_type = RXON_DEV_TYPE_P2P;
        ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd,
-                               CMD_SYNC, sizeof(*send), send);
+                               0, sizeof(*send), send);
 
        send->filter_flags = old_filter;
        send->dev_type = old_dev_type;
@@ -160,7 +160,7 @@ static int iwlagn_disconn_pan(struct iwl_priv *priv,
        int ret;
 
        send->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
-       ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, CMD_SYNC,
+       ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, 0,
                                sizeof(*send), send);
 
        send->filter_flags = old_filter;
@@ -189,7 +189,7 @@ static void iwlagn_update_qos(struct iwl_priv *priv,
                      ctx->qos_data.qos_active,
                      ctx->qos_data.def_qos_parm.qos_flags);
 
-       ret = iwl_dvm_send_cmd_pdu(priv, ctx->qos_cmd, CMD_SYNC,
+       ret = iwl_dvm_send_cmd_pdu(priv, ctx->qos_cmd, 0,
                               sizeof(struct iwl_qosparam_cmd),
                               &ctx->qos_data.def_qos_parm);
        if (ret)
@@ -353,7 +353,7 @@ static int iwl_send_rxon_timing(struct iwl_priv *priv,
                        le16_to_cpu(ctx->timing.atim_window));
 
        return iwl_dvm_send_cmd_pdu(priv, ctx->rxon_timing_cmd,
-                               CMD_SYNC, sizeof(ctx->timing), &ctx->timing);
+                               0, sizeof(ctx->timing), &ctx->timing);
 }
 
 static int iwlagn_rxon_disconn(struct iwl_priv *priv,
@@ -495,7 +495,7 @@ static int iwlagn_rxon_connect(struct iwl_priv *priv,
         * Associated RXON doesn't clear the station table in uCode,
         * so we don't need to restore stations etc. after this.
         */
-       ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, CMD_SYNC,
+       ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, 0,
                      sizeof(struct iwl_rxon_cmd), &ctx->staging);
        if (ret) {
                IWL_ERR(priv, "Error setting new RXON (%d)\n", ret);
@@ -610,7 +610,7 @@ int iwlagn_set_pan_params(struct iwl_priv *priv)
        cmd.slots[0].width = cpu_to_le16(slot0);
        cmd.slots[1].width = cpu_to_le16(slot1);
 
-       ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WIPAN_PARAMS, CMD_SYNC,
+       ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WIPAN_PARAMS, 0,
                        sizeof(cmd), &cmd);
        if (ret)
                IWL_ERR(priv, "Error setting PAN parameters (%d)\n", ret);
@@ -823,7 +823,7 @@ static int iwl_check_rxon_cmd(struct iwl_priv *priv,
 
        if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK))
                        == (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) {
-               IWL_WARN(priv, "CCK and auto detect");
+               IWL_WARN(priv, "CCK and auto detect\n");
                errors |= BIT(8);
        }
 
@@ -1395,7 +1395,7 @@ static void iwlagn_chain_noise_reset(struct iwl_priv *priv)
                        priv->phy_calib_chain_noise_reset_cmd);
                ret = iwl_dvm_send_cmd_pdu(priv,
                                        REPLY_PHY_CALIBRATION_CMD,
-                                       CMD_SYNC, sizeof(cmd), &cmd);
+                                       0, sizeof(cmd), &cmd);
                if (ret)
                        IWL_ERR(priv,
                                "Could not send REPLY_PHY_CALIBRATION_CMD\n");
index be98b913ed582046d6598d0cf7454cf3bd8278cc..43bef901e8f9a80a7c3a56f63a7d2da93fda7076 100644 (file)
@@ -59,7 +59,7 @@ static int iwl_send_scan_abort(struct iwl_priv *priv)
        int ret;
        struct iwl_host_cmd cmd = {
                .id = REPLY_SCAN_ABORT_CMD,
-               .flags = CMD_SYNC | CMD_WANT_SKB,
+               .flags = CMD_WANT_SKB,
        };
        __le32 *status;
 
@@ -639,7 +639,6 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
        struct iwl_host_cmd cmd = {
                .id = REPLY_SCAN_CMD,
                .len = { sizeof(struct iwl_scan_cmd), },
-               .flags = CMD_SYNC,
        };
        struct iwl_scan_cmd *scan;
        struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
index 9cdd91cdf661825604e9ae8c89649213667b0b0e..6ec86adbe4a1fcc9df9aaf0d8b73ca4b48ac2e9b 100644 (file)
@@ -39,7 +39,7 @@ static int iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id)
        lockdep_assert_held(&priv->sta_lock);
 
        if (sta_id >= IWLAGN_STATION_COUNT) {
-               IWL_ERR(priv, "invalid sta_id %u", sta_id);
+               IWL_ERR(priv, "invalid sta_id %u\n", sta_id);
                return -EINVAL;
        }
        if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE))
@@ -165,7 +165,7 @@ int iwl_send_add_sta(struct iwl_priv *priv,
        iwl_free_resp(&cmd);
 
        if (cmd.handler_status)
-               IWL_ERR(priv, "%s - error in the CMD response %d", __func__,
+               IWL_ERR(priv, "%s - error in the CMD response %d\n", __func__,
                        cmd.handler_status);
 
        return cmd.handler_status;
@@ -261,7 +261,7 @@ int iwl_sta_update_ht(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
        cmd.station_flags = flags;
        cmd.sta.sta_id = sta_id;
 
-       return iwl_send_add_sta(priv, &cmd, CMD_SYNC);
+       return iwl_send_add_sta(priv, &cmd, 0);
 }
 
 static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index,
@@ -413,7 +413,7 @@ int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx,
        spin_unlock_bh(&priv->sta_lock);
 
        /* Add station to device's station table */
-       ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
+       ret = iwl_send_add_sta(priv, &sta_cmd, 0);
        if (ret) {
                spin_lock_bh(&priv->sta_lock);
                IWL_ERR(priv, "Adding station %pM failed.\n",
@@ -456,7 +456,6 @@ static int iwl_send_remove_station(struct iwl_priv *priv,
        struct iwl_host_cmd cmd = {
                .id = REPLY_REMOVE_STA,
                .len = { sizeof(struct iwl_rem_sta_cmd), },
-               .flags = CMD_SYNC,
                .data = { &rm_sta_cmd, },
        };
 
@@ -740,7 +739,7 @@ void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
                                        send_lq = true;
                        }
                        spin_unlock_bh(&priv->sta_lock);
-                       ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
+                       ret = iwl_send_add_sta(priv, &sta_cmd, 0);
                        if (ret) {
                                spin_lock_bh(&priv->sta_lock);
                                IWL_ERR(priv, "Adding station %pM failed.\n",
@@ -756,8 +755,7 @@ void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
                         * current LQ command
                         */
                        if (send_lq)
-                               iwl_send_lq_cmd(priv, ctx, &lq,
-                                               CMD_SYNC, true);
+                               iwl_send_lq_cmd(priv, ctx, &lq, 0, true);
                        spin_lock_bh(&priv->sta_lock);
                        priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS;
                }
@@ -968,7 +966,7 @@ int iwlagn_add_bssid_station(struct iwl_priv *priv,
                return -ENOMEM;
        }
 
-       ret = iwl_send_lq_cmd(priv, ctx, link_cmd, CMD_SYNC, true);
+       ret = iwl_send_lq_cmd(priv, ctx, link_cmd, 0, true);
        if (ret)
                IWL_ERR(priv, "Link quality command failed (%d)\n", ret);
 
@@ -999,7 +997,6 @@ static int iwl_send_static_wepkey_cmd(struct iwl_priv *priv,
        struct iwl_host_cmd cmd = {
                .id = ctx->wep_key_cmd,
                .data = { wep_cmd, },
-               .flags = CMD_SYNC,
        };
 
        might_sleep();
@@ -1248,7 +1245,7 @@ int iwl_remove_dynamic_key(struct iwl_priv *priv,
        sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK;
        sta_cmd.mode = STA_CONTROL_MODIFY_MSK;
 
-       return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
+       return iwl_send_add_sta(priv, &sta_cmd, 0);
 }
 
 int iwl_set_dynamic_key(struct iwl_priv *priv,
@@ -1284,13 +1281,13 @@ int iwl_set_dynamic_key(struct iwl_priv *priv,
                ieee80211_get_key_rx_seq(keyconf, 0, &seq);
                ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);
                ret = iwlagn_send_sta_key(priv, keyconf, sta_id,
-                                         seq.tkip.iv32, p1k, CMD_SYNC);
+                                         seq.tkip.iv32, p1k, 0);
                break;
        case WLAN_CIPHER_SUITE_CCMP:
        case WLAN_CIPHER_SUITE_WEP40:
        case WLAN_CIPHER_SUITE_WEP104:
                ret = iwlagn_send_sta_key(priv, keyconf, sta_id,
-                                         0, NULL, CMD_SYNC);
+                                         0, NULL, 0);
                break;
        default:
                IWL_ERR(priv, "Unknown cipher %x\n", keyconf->cipher);
@@ -1409,7 +1406,7 @@ int iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid)
        memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
        spin_unlock_bh(&priv->sta_lock);
 
-       return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
+       return iwl_send_add_sta(priv, &sta_cmd, 0);
 }
 
 int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta,
@@ -1433,7 +1430,7 @@ int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta,
        memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
        spin_unlock_bh(&priv->sta_lock);
 
-       return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
+       return iwl_send_add_sta(priv, &sta_cmd, 0);
 }
 
 int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta,
@@ -1458,7 +1455,7 @@ int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta,
        memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd));
        spin_unlock_bh(&priv->sta_lock);
 
-       return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC);
+       return iwl_send_add_sta(priv, &sta_cmd, 0);
 }
 
 
index 058c5892c427afdf3df48038bf8e715017d379c6..acb981a0a0aaa0bb31f99e7f5306e5c03f6cdd47 100644 (file)
@@ -236,7 +236,7 @@ static void iwl_prepare_ct_kill_task(struct iwl_priv *priv)
 {
        IWL_DEBUG_TEMP(priv, "Prepare to enter IWL_TI_CT_KILL\n");
        /* make request to retrieve statistics information */
-       iwl_send_statistics_request(priv, CMD_SYNC, false);
+       iwl_send_statistics_request(priv, 0, false);
        /* Reschedule the ct_kill wait timer */
        mod_timer(&priv->thermal_throttle.ct_kill_waiting_tm,
                 jiffies + msecs_to_jiffies(CT_KILL_WAITING_DURATION));
index 398dd096674cf17bd8112e8e7d06ad4ce57427f0..3255a1723d176f59ee5547e7981a9d3bbe7d9583 100644 (file)
@@ -402,10 +402,10 @@ int iwlagn_tx_skb(struct iwl_priv *priv,
                /* aggregation is on for this <sta,tid> */
                if (info->flags & IEEE80211_TX_CTL_AMPDU &&
                    tid_data->agg.state != IWL_AGG_ON) {
-                       IWL_ERR(priv, "TX_CTL_AMPDU while not in AGG:"
-                               " Tx flags = 0x%08x, agg.state = %d",
+                       IWL_ERR(priv,
+                               "TX_CTL_AMPDU while not in AGG: Tx flags = 0x%08x, agg.state = %d\n",
                                info->flags, tid_data->agg.state);
-                       IWL_ERR(priv, "sta_id = %d, tid = %d seq_num = %d",
+                       IWL_ERR(priv, "sta_id = %d, tid = %d seq_num = %d\n",
                                sta_id, tid,
                                IEEE80211_SEQ_TO_SN(tid_data->seq_number));
                        goto drop_unlock_sta;
@@ -416,7 +416,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv,
                 */
                if (WARN_ONCE(tid_data->agg.state != IWL_AGG_ON &&
                              tid_data->agg.state != IWL_AGG_OFF,
-                   "Tx while agg.state = %d", tid_data->agg.state))
+                             "Tx while agg.state = %d\n", tid_data->agg.state))
                        goto drop_unlock_sta;
 
                seq_number = tid_data->seq_number;
@@ -778,8 +778,8 @@ static void iwlagn_check_ratid_empty(struct iwl_priv *priv, int sta_id, u8 tid)
                /* There are no packets for this RA / TID in the HW any more */
                if (tid_data->agg.ssn == tid_data->next_reclaimed) {
                        IWL_DEBUG_TX_QUEUES(priv,
-                               "Can continue DELBA flow ssn = next_recl ="
-                               " %d", tid_data->next_reclaimed);
+                               "Can continue DELBA flow ssn = next_recl = %d\n",
+                               tid_data->next_reclaimed);
                        iwl_trans_txq_disable(priv->trans,
                                              tid_data->agg.txq_id);
                        iwlagn_dealloc_agg_txq(priv, tid_data->agg.txq_id);
@@ -791,8 +791,8 @@ static void iwlagn_check_ratid_empty(struct iwl_priv *priv, int sta_id, u8 tid)
                /* There are no packets for this RA / TID in the HW any more */
                if (tid_data->agg.ssn == tid_data->next_reclaimed) {
                        IWL_DEBUG_TX_QUEUES(priv,
-                               "Can continue ADDBA flow ssn = next_recl ="
-                               " %d", tid_data->next_reclaimed);
+                               "Can continue ADDBA flow ssn = next_recl = %d\n",
+                               tid_data->next_reclaimed);
                        tid_data->agg.state = IWL_AGG_STARTING;
                        ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid);
                }
@@ -1216,8 +1216,8 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
                            ctx->vif->type == NL80211_IFTYPE_STATION) {
                                /* block and stop all queues */
                                priv->passive_no_rx = true;
-                               IWL_DEBUG_TX_QUEUES(priv, "stop all queues: "
-                                                   "passive channel");
+                               IWL_DEBUG_TX_QUEUES(priv,
+                                       "stop all queues: passive channel\n");
                                ieee80211_stop_queues(priv->hw);
 
                                IWL_DEBUG_TX_REPLY(priv,
@@ -1271,7 +1271,7 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
 
        while (!skb_queue_empty(&skbs)) {
                skb = __skb_dequeue(&skbs);
-               ieee80211_tx_status_ni(priv->hw, skb);
+               ieee80211_tx_status(priv->hw, skb);
        }
 
        return 0;
@@ -1411,7 +1411,7 @@ int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
 
        while (!skb_queue_empty(&reclaimed_skbs)) {
                skb = __skb_dequeue(&reclaimed_skbs);
-               ieee80211_tx_status_ni(priv->hw, skb);
+               ieee80211_tx_status(priv->hw, skb);
        }
 
        return 0;
index cf03ef5619d9fea602151cf573162848ffd12366..d5cee1530597b8cf2de444e98aebffd89d636715 100644 (file)
@@ -172,7 +172,7 @@ static int iwl_send_wimax_coex(struct iwl_priv *priv)
        memset(&coex_cmd, 0, sizeof(coex_cmd));
 
        return iwl_dvm_send_cmd_pdu(priv,
-                               COEX_PRIORITY_TABLE_CMD, CMD_SYNC,
+                               COEX_PRIORITY_TABLE_CMD, 0,
                                sizeof(coex_cmd), &coex_cmd);
 }
 
@@ -205,7 +205,7 @@ void iwl_send_prio_tbl(struct iwl_priv *priv)
        memcpy(prio_tbl_cmd.prio_tbl, iwl_bt_prio_tbl,
                sizeof(iwl_bt_prio_tbl));
        if (iwl_dvm_send_cmd_pdu(priv,
-                               REPLY_BT_COEX_PRIO_TABLE, CMD_SYNC,
+                               REPLY_BT_COEX_PRIO_TABLE, 0,
                                sizeof(prio_tbl_cmd), &prio_tbl_cmd))
                IWL_ERR(priv, "failed to send BT prio tbl command\n");
 }
@@ -218,7 +218,7 @@ int iwl_send_bt_env(struct iwl_priv *priv, u8 action, u8 type)
        env_cmd.action = action;
        env_cmd.type = type;
        ret = iwl_dvm_send_cmd_pdu(priv,
-                              REPLY_BT_COEX_PROT_ENV, CMD_SYNC,
+                              REPLY_BT_COEX_PROT_ENV, 0,
                               sizeof(env_cmd), &env_cmd);
        if (ret)
                IWL_ERR(priv, "failed to send BT env command\n");
index 854ba84ccb730995f0d786d26a85a1c2fc14c0dc..c3817fae16c04207136e5d45e8cc65bd3a125429 100644 (file)
@@ -62,6 +62,7 @@ static const struct iwl_base_params iwl1000_base_params = {
        .led_compensation = 51,
        .wd_timeout = IWL_WATCHDOG_DISABLED,
        .max_event_log_size = 128,
+       .scd_chain_ext_wa = true,
 };
 
 static const struct iwl_ht_params iwl1000_ht_params = {
index 3e63323637f3f593dd895528dac4e5fb794fdb4c..21e5d0843a62a84a0f21ff337d1b674750fa3999 100644 (file)
@@ -75,6 +75,7 @@ static const struct iwl_base_params iwl2000_base_params = {
        .wd_timeout = IWL_DEF_WD_TIMEOUT,
        .max_event_log_size = 512,
        .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+       .scd_chain_ext_wa = true,
 };
 
 
@@ -88,6 +89,7 @@ static const struct iwl_base_params iwl2030_base_params = {
        .wd_timeout = IWL_LONG_WD_TIMEOUT,
        .max_event_log_size = 512,
        .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+       .scd_chain_ext_wa = true,
 };
 
 static const struct iwl_ht_params iwl2000_ht_params = {
index 6674f2c4541c183fbae6c2a1bb0861d9eb3a966f..332bbede39e5b0fc6bb25b7ab30bbbd22c929b8b 100644 (file)
@@ -61,6 +61,7 @@ static const struct iwl_base_params iwl5000_base_params = {
        .led_compensation = 51,
        .wd_timeout = IWL_WATCHDOG_DISABLED,
        .max_event_log_size = 512,
+       .scd_chain_ext_wa = true,
 };
 
 static const struct iwl_ht_params iwl5000_ht_params = {
index 8048de90233fa038545e9d752eaaffbfc3968c72..8f2c3c8c6b843f78f346225d371ee3ad3df54f23 100644 (file)
@@ -85,6 +85,7 @@ static const struct iwl_base_params iwl6000_base_params = {
        .wd_timeout = IWL_DEF_WD_TIMEOUT,
        .max_event_log_size = 512,
        .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+       .scd_chain_ext_wa = true,
 };
 
 static const struct iwl_base_params iwl6050_base_params = {
@@ -97,6 +98,7 @@ static const struct iwl_base_params iwl6050_base_params = {
        .wd_timeout = IWL_DEF_WD_TIMEOUT,
        .max_event_log_size = 1024,
        .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+       .scd_chain_ext_wa = true,
 };
 
 static const struct iwl_base_params iwl6000_g2_base_params = {
@@ -109,6 +111,7 @@ static const struct iwl_base_params iwl6000_g2_base_params = {
        .wd_timeout = IWL_LONG_WD_TIMEOUT,
        .max_event_log_size = 512,
        .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+       .scd_chain_ext_wa = true,
 };
 
 static const struct iwl_ht_params iwl6000_ht_params = {
index 4c2d4ef28b220c719ac49f9f9726b931c2d35442..48730064da73f5e1e058f756d9473a2a0a5bc376 100644 (file)
 #define IWL3160_UCODE_API_MAX  9
 
 /* Oldest version we won't warn about */
-#define IWL7260_UCODE_API_OK   8
-#define IWL3160_UCODE_API_OK   8
+#define IWL7260_UCODE_API_OK   9
+#define IWL3160_UCODE_API_OK   9
 
 /* Lowest firmware API version supported */
-#define IWL7260_UCODE_API_MIN  7
-#define IWL3160_UCODE_API_MIN  7
+#define IWL7260_UCODE_API_MIN  8
+#define IWL3160_UCODE_API_MIN  8
 
 /* NVM versions */
 #define IWL7260_NVM_VERSION            0x0a1d
@@ -98,7 +98,7 @@
 #define NVM_HW_SECTION_NUM_FAMILY_7000         0
 
 static const struct iwl_base_params iwl7000_base_params = {
-       .eeprom_size = OTP_LOW_IMAGE_SIZE,
+       .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_7000,
        .num_of_queues = IWLAGN_NUM_QUEUES,
        .pll_cfg_val = 0,
        .shadow_ram_support = true,
@@ -107,6 +107,7 @@ static const struct iwl_base_params iwl7000_base_params = {
        .max_event_log_size = 512,
        .shadow_reg_enable = true,
        .pcie_l1_allowed = true,
+       .apmg_wake_up_wa = true,
 };
 
 static const struct iwl_ht_params iwl7000_ht_params = {
index f5bd82b885929a8c1188dc44ffdd27892dc2739b..51c41531d81d7f5af8354aced5da7e6b4e647f78 100644 (file)
 #define IWL8000_MODULE_FIRMWARE(api) IWL8000_FW_PRE __stringify(api) ".ucode"
 
 #define NVM_HW_SECTION_NUM_FAMILY_8000         10
+#define DEFAULT_NVM_FILE_FAMILY_8000           "iwl_nvm_8000.bin"
 
 static const struct iwl_base_params iwl8000_base_params = {
-       .eeprom_size = OTP_LOW_IMAGE_SIZE,
+       .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_8000,
        .num_of_queues = IWLAGN_NUM_QUEUES,
        .pll_cfg_val = 0,
        .shadow_ram_support = true,
@@ -118,6 +119,7 @@ const struct iwl_cfg iwl8260_2ac_cfg = {
        .ht_params = &iwl8000_ht_params,
        .nvm_ver = IWL8000_NVM_VERSION,
        .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
+       .default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000,
 };
 
 const struct iwl_cfg iwl8260_n_cfg = {
@@ -127,6 +129,7 @@ const struct iwl_cfg iwl8260_n_cfg = {
        .ht_params = &iwl8000_ht_params,
        .nvm_ver = IWL8000_NVM_VERSION,
        .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
+       .default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000,
 };
 
 MODULE_FIRMWARE(IWL8000_MODULE_FIRMWARE(IWL8000_UCODE_API_OK));
index 7f37fb86837b7a46569ba74f90df0a7bdb8d0271..04a483d386592c4cf359d741328844b2c0f02e51 100644 (file)
 
 /* EEPROM */
 #define IWLAGN_EEPROM_IMG_SIZE         2048
-/* OTP */
-/* lower blocks contain EEPROM image and calibration data */
-#define OTP_LOW_IMAGE_SIZE             (2 * 512 * sizeof(u16)) /* 2 KB */
+
 /* high blocks contain PAPD data */
 #define OTP_HIGH_IMAGE_SIZE_6x00        (6 * 512 * sizeof(u16)) /* 6 KB */
 #define OTP_HIGH_IMAGE_SIZE_1000        (0x200 * sizeof(u16)) /* 1024 bytes */
index 3f17dc3f2c8a9fdde83bddb254efb8cc8d33502f..b7047905f41a38ce6a88f27b93ad75384306958a 100644 (file)
@@ -146,6 +146,9 @@ static inline u8 num_of_ant(u8 mask)
  * @wd_timeout: TX queues watchdog timeout
  * @max_event_log_size: size of event log buffer size for ucode event logging
  * @shadow_reg_enable: HW shadow register support
+ * @apmg_wake_up_wa: should the MAC access REQ be asserted when a command
+ *     is in flight. This is due to a HW bug in 7260, 3160 and 7265.
+ * @scd_chain_ext_wa: should the chain extension feature in SCD be disabled.
  */
 struct iwl_base_params {
        int eeprom_size;
@@ -160,6 +163,8 @@ struct iwl_base_params {
        u32 max_event_log_size;
        const bool shadow_reg_enable;
        const bool pcie_l1_allowed;
+       const bool apmg_wake_up_wa;
+       const bool scd_chain_ext_wa;
 };
 
 /*
@@ -188,6 +193,11 @@ struct iwl_ht_params {
 #define EEPROM_6000_REG_BAND_24_HT40_CHANNELS  0x80
 #define EEPROM_REGULATORY_BAND_NO_HT40         0
 
+/* lower blocks contain EEPROM image and calibration data */
+#define OTP_LOW_IMAGE_SIZE             (2 * 512 * sizeof(u16)) /* 2 KB */
+#define OTP_LOW_IMAGE_SIZE_FAMILY_7000 (16 * 512 * sizeof(u16)) /* 16 KB */
+#define OTP_LOW_IMAGE_SIZE_FAMILY_8000 (32 * 512 * sizeof(u16)) /* 32 KB */
+
 struct iwl_eeprom_params {
        const u8 regulatory_bands[7];
        bool enhanced_txpower;
@@ -264,6 +274,8 @@ struct iwl_cfg {
        u8   nvm_hw_section_num;
        bool lp_xtal_workaround;
        const struct iwl_pwr_tx_backoff *pwr_tx_backoffs;
+       bool no_power_up_nic_in_init;
+       const char *default_nvm_file;
 };
 
 /*
index 8a44f594528df6c4f503face66300c93d79f1f44..09feff4fa226e781b1b8350ade568e9001709335 100644 (file)
@@ -61,8 +61,6 @@
  *
  *****************************************************************************/
 
-#define DEBUG
-
 #include <linux/device.h>
 #include <linux/interrupt.h>
 #include <linux/export.h>
@@ -128,8 +126,8 @@ void __iwl_dbg(struct device *dev,
 #ifdef CONFIG_IWLWIFI_DEBUG
        if (iwl_have_debug_level(level) &&
            (!limit || net_ratelimit()))
-               dev_dbg(dev, "%c %s %pV", in_interrupt() ? 'I' : 'U',
-                       function, &vaf);
+               dev_printk(KERN_DEBUG, dev, "%c %s %pV",
+                          in_interrupt() ? 'I' : 'U', function, &vaf);
 #endif
        trace_iwlwifi_dbg(level, in_interrupt(), function, &vaf);
        va_end(args);
index c8cbdbe15924a61123d76828c0d1f6c7686dcfe4..295083510e729a76f13347a568c661acbdfdda52 100644 (file)
@@ -47,12 +47,32 @@ void __iwl_warn(struct device *dev, const char *fmt, ...) __printf(2, 3);
 void __iwl_info(struct device *dev, const char *fmt, ...) __printf(2, 3);
 void __iwl_crit(struct device *dev, const char *fmt, ...) __printf(2, 3);
 
+/* not all compilers can evaluate strlen() at compile time, so use sizeof() */
+#define CHECK_FOR_NEWLINE(f) BUILD_BUG_ON(f[sizeof(f) - 2] != '\n')
+
 /* No matter what is m (priv, bus, trans), this will work */
-#define IWL_ERR(m, f, a...) __iwl_err((m)->dev, false, false, f, ## a)
-#define IWL_ERR_DEV(d, f, a...) __iwl_err((d), false, false, f, ## a)
-#define IWL_WARN(m, f, a...) __iwl_warn((m)->dev, f, ## a)
-#define IWL_INFO(m, f, a...) __iwl_info((m)->dev, f, ## a)
-#define IWL_CRIT(m, f, a...) __iwl_crit((m)->dev, f, ## a)
+#define IWL_ERR_DEV(d, f, a...)                                                \
+       do {                                                            \
+               CHECK_FOR_NEWLINE(f);                                   \
+               __iwl_err((d), false, false, f, ## a);                  \
+       } while (0)
+#define IWL_ERR(m, f, a...)                                            \
+       IWL_ERR_DEV((m)->dev, f, ## a)
+#define IWL_WARN(m, f, a...)                                           \
+       do {                                                            \
+               CHECK_FOR_NEWLINE(f);                                   \
+               __iwl_warn((m)->dev, f, ## a);                          \
+       } while (0)
+#define IWL_INFO(m, f, a...)                                           \
+       do {                                                            \
+               CHECK_FOR_NEWLINE(f);                                   \
+               __iwl_info((m)->dev, f, ## a);                          \
+       } while (0)
+#define IWL_CRIT(m, f, a...)                                           \
+       do {                                                            \
+               CHECK_FOR_NEWLINE(f);                                   \
+               __iwl_crit((m)->dev, f, ## a);                          \
+       } while (0)
 
 #if defined(CONFIG_IWLWIFI_DEBUG) || defined(CONFIG_IWLWIFI_DEVICE_TRACING)
 void __iwl_dbg(struct device *dev,
@@ -72,12 +92,17 @@ do {                                                                        \
                       DUMP_PREFIX_OFFSET, 16, 1, p, len, 1);           \
 } while (0)
 
+#define __IWL_DEBUG_DEV(dev, level, limit, fmt, args...)               \
+       do {                                                            \
+               CHECK_FOR_NEWLINE(fmt);                                 \
+               __iwl_dbg(dev, level, limit, __func__, fmt, ##args);    \
+       } while (0)
 #define IWL_DEBUG(m, level, fmt, args...)                              \
-       __iwl_dbg((m)->dev, level, false, __func__, fmt, ##args)
+       __IWL_DEBUG_DEV((m)->dev, level, false, fmt, ##args)
 #define IWL_DEBUG_DEV(dev, level, fmt, args...)                                \
-       __iwl_dbg((dev), level, false, __func__, fmt, ##args)
+       __IWL_DEBUG_DEV(dev, level, false, fmt, ##args)
 #define IWL_DEBUG_LIMIT(m, level, fmt, args...)                                \
-       __iwl_dbg((m)->dev, level, true, __func__, fmt, ##args)
+       __IWL_DEBUG_DEV((m)->dev, level, true, fmt, ##args)
 
 #ifdef CONFIG_IWLWIFI_DEBUG
 #define iwl_print_hex_dump(m, level, p, len)                           \
index 0a3e841b44a9ebe81cbd40b0313e8cafcc355f3a..f2a5c12269a3ed7de811399580c9ea908bc89173 100644 (file)
@@ -1243,6 +1243,7 @@ struct iwl_mod_params iwlwifi_mod_params = {
        .bt_coex_active = true,
        .power_level = IWL_POWER_INDEX_1,
        .wd_disable = true,
+       .uapsd_disable = false,
        /* the rest are 0 by default */
 };
 IWL_EXPORT_SYMBOL(iwlwifi_mod_params);
@@ -1356,6 +1357,10 @@ MODULE_PARM_DESC(wd_disable,
 module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, S_IRUGO);
 MODULE_PARM_DESC(nvm_file, "NVM file name");
 
+module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable,
+                  bool, S_IRUGO);
+MODULE_PARM_DESC(uapsd_disable, "disable U-APSD functionality (default: N)");
+
 /*
  * set bt_coex_active to true, uCode will do kill/defer
  * every time the priority line is asserted (BT is sending signals on the
similarity index 82%
rename from drivers/net/wireless/iwlwifi/mvm/fw-error-dump.h
rename to drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
index 58c8941c0d95ef6d6e0d6bb51349663dbf3b93dd..2953ffceda3881d29be37812544a374816b2130c 100644 (file)
  * enum iwl_fw_error_dump_type - types of data in the dump file
  * @IWL_FW_ERROR_DUMP_SRAM:
  * @IWL_FW_ERROR_DUMP_REG:
+ * @IWL_FW_ERROR_DUMP_RXF:
+ * @IWL_FW_ERROR_DUMP_TXCMD: last TX command data, structured as
+ *     &struct iwl_fw_error_dump_txcmd packets
  */
 enum iwl_fw_error_dump_type {
        IWL_FW_ERROR_DUMP_SRAM = 0,
        IWL_FW_ERROR_DUMP_REG = 1,
+       IWL_FW_ERROR_DUMP_RXF = 2,
+       IWL_FW_ERROR_DUMP_TXCMD = 3,
 
        IWL_FW_ERROR_DUMP_MAX,
 };
@@ -89,7 +94,7 @@ struct iwl_fw_error_dump_data {
        __le32 type;
        __le32 len;
        __u8 data[];
-} __packed __aligned(4);
+} __packed;
 
 /**
  * struct iwl_fw_error_dump_file - the layout of the header of the file
@@ -101,6 +106,29 @@ struct iwl_fw_error_dump_file {
        __le32 barker;
        __le32 file_len;
        u8 data[0];
-} __packed __aligned(4);
+} __packed;
+
+/**
+ * struct iwl_fw_error_dump_txcmd - TX command data
+ * @cmdlen: original length of command
+ * @caplen: captured length of command (may be less)
+ * @data: captured command data, @caplen bytes
+ */
+struct iwl_fw_error_dump_txcmd {
+       __le32 cmdlen;
+       __le32 caplen;
+       u8 data[];
+} __packed;
+
+/**
+ * iwl_mvm_fw_error_next_data - advance fw error dump data pointer
+ * @data: previous data block
+ * Returns: next data block
+ */
+static inline struct iwl_fw_error_dump_data *
+iwl_mvm_fw_error_next_data(struct iwl_fw_error_dump_data *data)
+{
+       return (void *)(data->data + le32_to_cpu(data->len));
+}
 
 #endif /* __fw_error_dump_h__ */
index d14f19339d6140607c99d1b6660b039f9ac4aa66..0aa7c0085c9fd04554b1a3314a2212638f920a78 100644 (file)
  * @IWL_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w).
  * @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_UAPSD_SUPPORT: 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.
+ * @IWL_UCODE_TLV_FLAGS_P2P_PM: P2P client supports PM as a stand alone MAC
  * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_DCM: support power save on BSS station and
  *     P2P client interfaces simultaneously if they are in different bindings.
+ * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_SCM: support power save on BSS station and
+ *     P2P client interfaces simultaneously if they are in same bindings.
  * @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save
  * @IWL_UCODE_TLV_FLAGS_BCAST_FILTERING: uCode supports broadcast filtering.
  * @IWL_UCODE_TLV_FLAGS_GO_UAPSD: AP/GO interfaces support uAPSD clients
+ * @IWL_UCODE_TLV_FLAGS_EBS_SUPPORT: this uCode image supports EBS.
  */
 enum iwl_ucode_tlv_flag {
        IWL_UCODE_TLV_FLAGS_PAN                 = BIT(0),
@@ -104,22 +99,16 @@ 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_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_P2P_PM              = BIT(21),
        IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM      = BIT(22),
+       IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM      = BIT(23),
        IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT       = BIT(24),
+       IWL_UCODE_TLV_FLAGS_EBS_SUPPORT         = BIT(25),
        IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD        = BIT(26),
        IWL_UCODE_TLV_FLAGS_BCAST_FILTERING     = BIT(29),
        IWL_UCODE_TLV_FLAGS_GO_UAPSD            = BIT(30),
@@ -128,9 +117,11 @@ enum iwl_ucode_tlv_flag {
 /**
  * enum iwl_ucode_tlv_api - ucode api
  * @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field.
+ * @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA.
  */
 enum iwl_ucode_tlv_api {
        IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID     = BIT(0),
+       IWL_UCODE_TLV_API_CSA_FLOW              = BIT(4),
 };
 
 /**
@@ -183,6 +174,7 @@ enum iwl_ucode_sec {
 #define IWL_UCODE_SECTION_MAX 12
 #define IWL_API_ARRAY_SIZE     1
 #define IWL_CAPABILITIES_ARRAY_SIZE    1
+#define CPU1_CPU2_SEPARATOR_SECTION    0xFFFFCCCC
 
 struct iwl_ucode_capabilities {
        u32 max_probe_length;
@@ -205,6 +197,11 @@ struct fw_img {
        bool is_dual_cpus;
 };
 
+struct iwl_sf_region {
+       u32 addr;
+       u32 size;
+};
+
 /* uCode version contains 4 values: Major/Minor/API/Serial */
 #define IWL_UCODE_MAJOR(ver)   (((ver) & 0xFF000000) >> 24)
 #define IWL_UCODE_MINOR(ver)   (((ver) & 0x00FF0000) >> 16)
index 44cc3cf45762d1465e47627b70eea990f257e3a3..5eef4ae7333bad13527327cc851f022f23e3f991 100644 (file)
@@ -33,6 +33,7 @@
 #include "iwl-io.h"
 #include "iwl-csr.h"
 #include "iwl-debug.h"
+#include "iwl-prph.h"
 #include "iwl-fh.h"
 
 #define IWL_POLL_INTERVAL 10   /* microseconds */
@@ -183,6 +184,23 @@ void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
 }
 IWL_EXPORT_SYMBOL(iwl_clear_bits_prph);
 
+void iwl_force_nmi(struct iwl_trans *trans)
+{
+       /*
+        * In HW previous to the 8000 HW family, and in the 8000 HW family
+        * itself when the revision step==0, the DEVICE_SET_NMI_REG is used
+        * to force an NMI. Otherwise, a different register -
+        * DEVICE_SET_NMI_8000B_REG - is used.
+        */
+       if ((trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) ||
+           ((trans->hw_rev & 0xc) == 0x0))
+               iwl_write_prph(trans, DEVICE_SET_NMI_REG, DEVICE_SET_NMI_VAL);
+       else
+               iwl_write_prph(trans, DEVICE_SET_NMI_8000B_REG,
+                              DEVICE_SET_NMI_8000B_VAL);
+}
+IWL_EXPORT_SYMBOL(iwl_force_nmi);
+
 static const char *get_fh_string(int cmd)
 {
 #define IWL_CMD(x) case x: return #x
index 665ddd9dbbc48ff5dec47f246b8efb0639fb5c86..705d12c079e8b2ef2492ca82a56aa92d64cfe09b 100644 (file)
@@ -80,6 +80,7 @@ void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask);
 void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
                            u32 bits, u32 mask);
 void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask);
+void iwl_force_nmi(struct iwl_trans *trans);
 
 /* Error handling */
 int iwl_dump_fh(struct iwl_trans *trans, char **buf);
index d994317db85b72cbfc11f602e2d414efc54233b1..d051857729ab8e2f238115165c482d09033147db 100644 (file)
@@ -119,6 +119,7 @@ struct iwl_mod_params {
 #endif
        int ant_coupling;
        char *nvm_file;
+       bool uapsd_disable;
 };
 
 #endif /* #__iwl_modparams_h__ */
index 6be30c69850619f81c2468febb3634fd5cf390fd..85eee79c495c8f1080d9844a3f71ffad1b0d023d 100644 (file)
@@ -62,6 +62,7 @@
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/export.h>
+#include <linux/etherdevice.h>
 #include "iwl-drv.h"
 #include "iwl-modparams.h"
 #include "iwl-nvm-parse.h"
@@ -127,19 +128,20 @@ static const u8 iwl_nvm_channels[] = {
 
 static const u8 iwl_nvm_channels_family_8000[] = {
        /* 2.4 GHz */
-       1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+       1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
        /* 5 GHz */
        36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92,
        96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
        149, 153, 157, 161, 165, 169, 173, 177, 181
 };
 
-#define IWL_NUM_CHANNELS       ARRAY_SIZE(iwl_nvm_channels)
+#define IWL_NUM_CHANNELS               ARRAY_SIZE(iwl_nvm_channels)
 #define IWL_NUM_CHANNELS_FAMILY_8000   ARRAY_SIZE(iwl_nvm_channels_family_8000)
-#define NUM_2GHZ_CHANNELS      14
-#define FIRST_2GHZ_HT_MINUS    5
-#define LAST_2GHZ_HT_PLUS      9
-#define LAST_5GHZ_HT           161
+#define NUM_2GHZ_CHANNELS              14
+#define NUM_2GHZ_CHANNELS_FAMILY_8000  14
+#define FIRST_2GHZ_HT_MINUS            5
+#define LAST_2GHZ_HT_PLUS              9
+#define LAST_5GHZ_HT                   161
 
 #define DEFAULT_MAX_TX_POWER 16
 
@@ -202,21 +204,23 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
        struct ieee80211_channel *channel;
        u16 ch_flags;
        bool is_5ghz;
-       int num_of_ch;
+       int num_of_ch, num_2ghz_channels;
        const u8 *nvm_chan;
 
        if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
                num_of_ch = IWL_NUM_CHANNELS;
                nvm_chan = &iwl_nvm_channels[0];
+               num_2ghz_channels = NUM_2GHZ_CHANNELS;
        } else {
                num_of_ch = IWL_NUM_CHANNELS_FAMILY_8000;
                nvm_chan = &iwl_nvm_channels_family_8000[0];
+               num_2ghz_channels = NUM_2GHZ_CHANNELS_FAMILY_8000;
        }
 
        for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) {
                ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx);
 
-               if (ch_idx >= NUM_2GHZ_CHANNELS &&
+               if (ch_idx >= num_2ghz_channels &&
                    !data->sku_cap_band_52GHz_enable)
                        ch_flags &= ~NVM_CHANNEL_VALID;
 
@@ -225,7 +229,7 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
                                         "Ch. %d Flags %x [%sGHz] - No traffic\n",
                                         nvm_chan[ch_idx],
                                         ch_flags,
-                                        (ch_idx >= NUM_2GHZ_CHANNELS) ?
+                                        (ch_idx >= num_2ghz_channels) ?
                                         "5.2" : "2.4");
                        continue;
                }
@@ -234,7 +238,7 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
                n_channels++;
 
                channel->hw_value = nvm_chan[ch_idx];
-               channel->band = (ch_idx < NUM_2GHZ_CHANNELS) ?
+               channel->band = (ch_idx < num_2ghz_channels) ?
                                IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
                channel->center_freq =
                        ieee80211_channel_to_frequency(
@@ -242,7 +246,7 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
 
                /* TODO: Need to be dependent to the NVM */
                channel->flags = IEEE80211_CHAN_NO_HT40;
-               if (ch_idx < NUM_2GHZ_CHANNELS &&
+               if (ch_idx < num_2ghz_channels &&
                    (ch_flags & NVM_CHANNEL_40MHZ)) {
                        if (nvm_chan[ch_idx] <= LAST_2GHZ_HT_PLUS)
                                channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
@@ -250,7 +254,7 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
                                channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
                } else if (nvm_chan[ch_idx] <= LAST_5GHZ_HT &&
                           (ch_flags & NVM_CHANNEL_40MHZ)) {
-                       if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0)
+                       if ((ch_idx - num_2ghz_channels) % 2 == 0)
                                channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
                        else
                                channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
@@ -447,13 +451,7 @@ static void iwl_set_hw_address(const struct iwl_cfg *cfg,
                               struct iwl_nvm_data *data,
                               const __le16 *nvm_sec)
 {
-       u8 hw_addr[ETH_ALEN];
-
-       if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
-               memcpy(hw_addr, nvm_sec + HW_ADDR, ETH_ALEN);
-       else
-               memcpy(hw_addr, nvm_sec + MAC_ADDRESS_OVERRIDE_FAMILY_8000,
-                      ETH_ALEN);
+       const u8 *hw_addr = (const u8 *)(nvm_sec + HW_ADDR);
 
        /* The byte order is little endian 16 bit, meaning 214365 */
        data->hw_addr[0] = hw_addr[1];
@@ -464,6 +462,41 @@ static void iwl_set_hw_address(const struct iwl_cfg *cfg,
        data->hw_addr[5] = hw_addr[4];
 }
 
+static void iwl_set_hw_address_family_8000(const struct iwl_cfg *cfg,
+                                          struct iwl_nvm_data *data,
+                                          const __le16 *mac_override,
+                                          const __le16 *nvm_hw)
+{
+       const u8 *hw_addr;
+
+       if (mac_override) {
+               hw_addr = (const u8 *)(mac_override +
+                                MAC_ADDRESS_OVERRIDE_FAMILY_8000);
+
+               /* The byte order is little endian 16 bit, meaning 214365 */
+               data->hw_addr[0] = hw_addr[1];
+               data->hw_addr[1] = hw_addr[0];
+               data->hw_addr[2] = hw_addr[3];
+               data->hw_addr[3] = hw_addr[2];
+               data->hw_addr[4] = hw_addr[5];
+               data->hw_addr[5] = hw_addr[4];
+
+               if (is_valid_ether_addr(hw_addr))
+                       return;
+       }
+
+       /* take the MAC address from the OTP */
+       hw_addr = (const u8 *)(nvm_hw + HW_ADDR0_FAMILY_8000);
+       data->hw_addr[0] = hw_addr[3];
+       data->hw_addr[1] = hw_addr[2];
+       data->hw_addr[2] = hw_addr[1];
+       data->hw_addr[3] = hw_addr[0];
+
+       hw_addr = (const u8 *)(nvm_hw + HW_ADDR1_FAMILY_8000);
+       data->hw_addr[4] = hw_addr[1];
+       data->hw_addr[5] = hw_addr[0];
+}
+
 struct iwl_nvm_data *
 iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
                   const __le16 *nvm_hw, const __le16 *nvm_sw,
@@ -523,7 +556,7 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
                                rx_chains);
        } else {
                /* MAC address in family 8000 */
-               iwl_set_hw_address(cfg, data, mac_override);
+               iwl_set_hw_address_family_8000(cfg, data, mac_override, nvm_hw);
 
                iwl_init_sbands(dev, cfg, data, regulatory,
                                sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains,
index ea29504ac61704c39c24a117dec0a5d92aa58376..99785c892f963c7435b0048c4a097f6cae9e7808 100644 (file)
@@ -63,6 +63,7 @@
 #ifndef __iwl_op_mode_h__
 #define __iwl_op_mode_h__
 
+#include <linux/netdevice.h>
 #include <linux/debugfs.h>
 
 struct iwl_op_mode;
@@ -112,8 +113,11 @@ struct iwl_cfg;
  * @stop: stop the op_mode. Must free all the memory allocated.
  *     May sleep
  * @rx: Rx notification to the op_mode. rxb is the Rx buffer itself. Cmd is the
- *     HCMD this Rx responds to.
- *     This callback may sleep, it is called from a threaded IRQ handler.
+ *     HCMD this Rx responds to. Can't sleep.
+ * @napi_add: NAPI initialisation. The transport is fully responsible for NAPI,
+ *     but the higher layers need to know about it (in particular mac80211 to
+ *     to able to call the right NAPI RX functions); this function is needed
+ *     to eventually call netif_napi_add() with higher layer involvement.
  * @queue_full: notifies that a HW queue is full.
  *     Must be atomic and called with BH disabled.
  * @queue_not_full: notifies that a HW queue is not full any more.
@@ -143,6 +147,11 @@ struct iwl_op_mode_ops {
        void (*stop)(struct iwl_op_mode *op_mode);
        int (*rx)(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb,
                  struct iwl_device_cmd *cmd);
+       void (*napi_add)(struct iwl_op_mode *op_mode,
+                        struct napi_struct *napi,
+                        struct net_device *napi_dev,
+                        int (*poll)(struct napi_struct *, int),
+                        int weight);
        void (*queue_full)(struct iwl_op_mode *op_mode, int queue);
        void (*queue_not_full)(struct iwl_op_mode *op_mode, int queue);
        bool (*hw_rf_kill)(struct iwl_op_mode *op_mode, bool state);
@@ -180,7 +189,6 @@ static inline int iwl_op_mode_rx(struct iwl_op_mode *op_mode,
                                  struct iwl_rx_cmd_buffer *rxb,
                                  struct iwl_device_cmd *cmd)
 {
-       might_sleep();
        return op_mode->ops->rx(op_mode, rxb, cmd);
 }
 
@@ -249,4 +257,15 @@ static inline int iwl_op_mode_exit_d0i3(struct iwl_op_mode *op_mode)
        return op_mode->ops->exit_d0i3(op_mode);
 }
 
+static inline void iwl_op_mode_napi_add(struct iwl_op_mode *op_mode,
+                                       struct napi_struct *napi,
+                                       struct net_device *napi_dev,
+                                       int (*poll)(struct napi_struct *, int),
+                                       int weight)
+{
+       if (!op_mode->ops->napi_add)
+               return;
+       op_mode->ops->napi_add(op_mode, napi, napi_dev, poll, weight);
+}
+
 #endif /* __iwl_op_mode_h__ */
index b761ac4822a35b1e6a8b59f952655d39f1f0cb82..d4fb5cad07ea1d36c508c25ec83450a2e80e3807 100644 (file)
@@ -345,7 +345,6 @@ static int iwl_send_phy_db_cmd(struct iwl_phy_db *phy_db, u16 type,
        struct iwl_phy_db_cmd phy_db_cmd;
        struct iwl_host_cmd cmd = {
                .id = PHY_DB_CMD,
-               .flags = CMD_SYNC,
        };
 
        IWL_DEBUG_INFO(phy_db->trans,
@@ -393,13 +392,13 @@ static int iwl_phy_db_send_all_channel_groups(
                                          entry->data);
                if (err) {
                        IWL_ERR(phy_db->trans,
-                               "Can't SEND phy_db section %d (%d), err %d",
+                               "Can't SEND phy_db section %d (%d), err %d\n",
                                type, i, err);
                        return err;
                }
 
                IWL_DEBUG_INFO(phy_db->trans,
-                              "Sent PHY_DB HCMD, type = %d num = %d",
+                              "Sent PHY_DB HCMD, type = %d num = %d\n",
                               type, i);
        }
 
@@ -451,7 +450,7 @@ int iwl_send_phy_db_data(struct iwl_phy_db *phy_db)
                                                 IWL_NUM_PAPD_CH_GROUPS);
        if (err) {
                IWL_ERR(phy_db->trans,
-                       "Cannot send channel specific PAPD groups");
+                       "Cannot send channel specific PAPD groups\n");
                return err;
        }
 
@@ -461,7 +460,7 @@ int iwl_send_phy_db_data(struct iwl_phy_db *phy_db)
                                                 IWL_NUM_TXP_CH_GROUPS);
        if (err) {
                IWL_ERR(phy_db->trans,
-                       "Cannot send channel specific TX power groups");
+                       "Cannot send channel specific TX power groups\n");
                return err;
        }
 
index 5f657c501406cc995f7f8c065f26d9983ba43ffe..4997e27672b3ae22b4176df391a5bbb3ae9e1f4d 100644 (file)
 
 /* Device NMI register */
 #define DEVICE_SET_NMI_REG 0x00a01c30
+#define DEVICE_SET_NMI_VAL 0x1
+#define DEVICE_SET_NMI_8000B_REG 0x00a01c24
+#define DEVICE_SET_NMI_8000B_VAL 0x1000000
 
 /* Shared registers (0x0..0x3ff, via target indirect or periphery */
 #define SHR_BASE       0x00a10000
@@ -348,4 +351,12 @@ enum secure_load_status_reg {
 
 #define LMPM_SECURE_TIME_OUT   (100)
 
+/* Rx FIFO */
+#define RXF_SIZE_ADDR                  (0xa00c88)
+#define RXF_SIZE_BYTE_CND_POS          (7)
+#define RXF_SIZE_BYTE_CNT_MSK          (0x3ff << RXF_SIZE_BYTE_CND_POS)
+
+#define RXF_LD_FENCE_OFFSET_ADDR       (0xa00c10)
+#define RXF_FIFO_RD_FENCE_ADDR         (0xa00c0c)
+
 #endif                         /* __iwl_prph_h__ */
index 8cdb0dd618a6fdfcc8d57095e41974e6e22984ab..34d49e171fb4dae1f10928c931c26dc93a414e67 100644 (file)
@@ -189,10 +189,9 @@ static inline u32 iwl_rx_packet_payload_len(const struct iwl_rx_packet *pkt)
 /**
  * enum CMD_MODE - how to send the host commands ?
  *
- * @CMD_SYNC: The caller will be stalled until the fw responds to the command
  * @CMD_ASYNC: Return right away and don't wait for the response
- * @CMD_WANT_SKB: valid only with CMD_SYNC. The caller needs the buffer of the
- *     response. The caller needs to call iwl_free_resp when done.
+ * @CMD_WANT_SKB: Not valid with CMD_ASYNC. The caller needs the buffer of
+ *     the response. The caller needs to call iwl_free_resp when done.
  * @CMD_HIGH_PRIO: The command is high priority - it goes to the front of the
  *     command queue, but after other high priority commands. valid only
  *     with CMD_ASYNC.
@@ -202,7 +201,6 @@ static inline u32 iwl_rx_packet_payload_len(const struct iwl_rx_packet *pkt)
  *     (i.e. mark it as non-idle).
  */
 enum CMD_MODE {
-       CMD_SYNC                = 0,
        CMD_ASYNC               = BIT(0),
        CMD_WANT_SKB            = BIT(1),
        CMD_SEND_IN_RFKILL      = BIT(2),
@@ -427,7 +425,7 @@ struct iwl_trans;
  * @send_cmd:send a host command. Must return -ERFKILL if RFkill is asserted.
  *     If RFkill is asserted in the middle of a SYNC host command, it must
  *     return -ERFKILL straight away.
- *     May sleep only if CMD_SYNC is set
+ *     May sleep only if CMD_ASYNC is not set
  * @tx: send an skb
  *     Must be atomic
  * @reclaim: free packet until ssn. Returns a list of freed packets.
@@ -437,8 +435,7 @@ struct iwl_trans;
  *     this one. The op_mode must not configure the HCMD queue. May sleep.
  * @txq_disable: de-configure a Tx queue to send AMPDUs
  *     Must be atomic
- * @wait_tx_queue_empty: wait until all tx queues are empty
- *     May sleep
+ * @wait_tx_queue_empty: wait until tx queues are empty. May sleep.
  * @dbgfs_register: add the dbgfs files under this directory. Files will be
  *     automatically deleted.
  * @write8: write a u8 to a register at offset ofs from the BAR
@@ -464,6 +461,11 @@ struct iwl_trans;
  * @unref: release a reference previously taken with @ref. Note that
  *     initially the reference count is 1, making an initial @unref
  *     necessary to allow low power states.
+ * @dump_data: fill a data dump with debug data, maybe containing last
+ *     TX'ed commands and similar. When called with a NULL buffer and
+ *     zero buffer length, provide only the (estimated) required buffer
+ *     length. Return the used buffer length.
+ *     Note that the transport must fill in the proper file headers.
  */
 struct iwl_trans_ops {
 
@@ -471,6 +473,8 @@ struct iwl_trans_ops {
        void (*op_mode_leave)(struct iwl_trans *iwl_trans);
        int (*start_fw)(struct iwl_trans *trans, const struct fw_img *fw,
                        bool run_in_rfkill);
+       int (*update_sf)(struct iwl_trans *trans,
+                        struct iwl_sf_region *st_fwrd_space);
        void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr);
        void (*stop_device)(struct iwl_trans *trans);
 
@@ -490,7 +494,7 @@ struct iwl_trans_ops {
        void (*txq_disable)(struct iwl_trans *trans, int queue);
 
        int (*dbgfs_register)(struct iwl_trans *trans, struct dentry* dir);
-       int (*wait_tx_queue_empty)(struct iwl_trans *trans);
+       int (*wait_tx_queue_empty)(struct iwl_trans *trans, u32 txq_bm);
 
        void (*write8)(struct iwl_trans *trans, u32 ofs, u8 val);
        void (*write32)(struct iwl_trans *trans, u32 ofs, u32 val);
@@ -512,6 +516,10 @@ struct iwl_trans_ops {
                              u32 value);
        void (*ref)(struct iwl_trans *trans);
        void (*unref)(struct iwl_trans *trans);
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       u32 (*dump_data)(struct iwl_trans *trans, void *buf, u32 buflen);
+#endif
 };
 
 /**
@@ -630,6 +638,17 @@ static inline int iwl_trans_start_fw(struct iwl_trans *trans,
        return trans->ops->start_fw(trans, fw, run_in_rfkill);
 }
 
+static inline int iwl_trans_update_sf(struct iwl_trans *trans,
+                                     struct iwl_sf_region *st_fwrd_space)
+{
+       might_sleep();
+
+       if (trans->ops->update_sf)
+               return trans->ops->update_sf(trans, st_fwrd_space);
+
+       return 0;
+}
+
 static inline void iwl_trans_stop_device(struct iwl_trans *trans)
 {
        might_sleep();
@@ -665,6 +684,16 @@ static inline void iwl_trans_unref(struct iwl_trans *trans)
                trans->ops->unref(trans);
 }
 
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+static inline u32 iwl_trans_dump_data(struct iwl_trans *trans,
+                                     void *buf, u32 buflen)
+{
+       if (!trans->ops->dump_data)
+               return 0;
+       return trans->ops->dump_data(trans, buf, buflen);
+}
+#endif
+
 static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
                                     struct iwl_host_cmd *cmd)
 {
@@ -678,7 +707,7 @@ static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
                return -EIO;
 
        if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) {
-               IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
+               IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
                return -EIO;
        }
 
@@ -720,7 +749,7 @@ static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb,
                return -EIO;
 
        if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
-               IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
+               IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
 
        return trans->ops->tx(trans, skb, dev_cmd, queue);
 }
@@ -729,7 +758,7 @@ static inline void iwl_trans_reclaim(struct iwl_trans *trans, int queue,
                                     int ssn, struct sk_buff_head *skbs)
 {
        if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
-               IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
+               IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
 
        trans->ops->reclaim(trans, queue, ssn, skbs);
 }
@@ -746,7 +775,7 @@ static inline void iwl_trans_txq_enable(struct iwl_trans *trans, int queue,
        might_sleep();
 
        if (unlikely((trans->state != IWL_TRANS_FW_ALIVE)))
-               IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
+               IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
 
        trans->ops->txq_enable(trans, queue, fifo, sta_id, tid,
                                 frame_limit, ssn);
@@ -759,12 +788,13 @@ static inline void iwl_trans_ac_txq_enable(struct iwl_trans *trans, int queue,
                             IWL_MAX_TID_COUNT, IWL_FRAME_LIMIT, 0);
 }
 
-static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans)
+static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans,
+                                               u32 txq_bm)
 {
        if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
-               IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
+               IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
 
-       return trans->ops->wait_tx_queue_empty(trans);
+       return trans->ops->wait_tx_queue_empty(trans, txq_bm);
 }
 
 static inline int iwl_trans_dbgfs_register(struct iwl_trans *trans,
index ccdd3b7c4cce38fb10caf66326e7c439f0c079ee..c30d7f64ec1e4e1c47a635e091c4e503c2ceaa83 100644 (file)
@@ -3,8 +3,9 @@ iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
 iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o
 iwlmvm-y += scan.o time-event.o rs.o
 iwlmvm-y += power.o coex.o
-iwlmvm-y += led.o tt.o offloading.o
+iwlmvm-y += tt.o offloading.o
 iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o
+iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o
 iwlmvm-$(CONFIG_PM_SLEEP) += d3.o
 
 ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../
index 0489314425cbdf4a4b782867644ee9a0a0ca4b63..c8c3b38228f02f9768b780a7bdd31273f49e9541 100644 (file)
@@ -104,12 +104,9 @@ static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
 #define BT_DISABLE_REDUCED_TXPOWER_THRESHOLD   (-65)
 #define BT_ANTENNA_COUPLING_THRESHOLD          (30)
 
-int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm)
+static 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,
+       return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, 0,
                                    sizeof(struct iwl_bt_coex_prio_tbl_cmd),
                                    &iwl_bt_prio_tbl);
 }
@@ -127,10 +124,10 @@ const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX] = {
 };
 
 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),
+       cpu_to_le32(0xf0f0f0f0), /* 50% */
+       cpu_to_le32(0xc0c0c0c0), /* 25% */
+       cpu_to_le32(0xfcfcfcfc), /* 75% */
+       cpu_to_le32(0xfefefefe), /* 87.5% */
 };
 
 static const __le32 iwl_single_shared_ant[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = {
@@ -303,8 +300,8 @@ static const __le64 iwl_ci_mask[][3] = {
 };
 
 static const __le32 iwl_bt_mprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE] = {
-       cpu_to_le32(0x22002200),
-       cpu_to_le32(0x33113311),
+       cpu_to_le32(0x28412201),
+       cpu_to_le32(0x11118451),
 };
 
 struct corunning_block_luts {
@@ -568,13 +565,13 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
                .id = BT_CONFIG,
                .len = { sizeof(*bt_cmd), },
                .dataflags = { IWL_HCMD_DFL_NOCOPY, },
-               .flags = CMD_SYNC,
        };
        int ret;
        u32 flags;
 
-       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NEWBT_COEX))
-               return 0;
+       ret = iwl_send_bt_prio_tbl(mvm);
+       if (ret)
+               return ret;
 
        bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
        if (!bt_cmd)
@@ -582,10 +579,12 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
        cmd.data[0] = bt_cmd;
 
        bt_cmd->max_kill = 5;
-       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->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->override_primary_lut = BT_COEX_INVALID_LUT;
+       bt_cmd->override_secondary_lut = BT_COEX_INVALID_LUT;
 
        flags = iwlwifi_mod_params.bt_coex_active ?
                        BT_COEX_NW : BT_COEX_DISABLE;
@@ -663,7 +662,6 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
                .data[0] = &bt_cmd,
                .len = { sizeof(*bt_cmd), },
                .dataflags = { IWL_HCMD_DFL_NOCOPY, },
-               .flags = CMD_SYNC,
        };
        int ret = 0;
 
@@ -717,7 +715,8 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
        return ret;
 }
 
-int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, bool enable)
+static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
+                                      bool enable)
 {
        struct iwl_bt_coex_cmd *bt_cmd;
        /* Send ASYNC since this can be sent from an atomic context */
@@ -735,8 +734,7 @@ int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, bool enable)
                return 0;
 
        /* nothing to do */
-       if (mvmsta->bt_reduced_txpower_dbg ||
-           mvmsta->bt_reduced_txpower == enable)
+       if (mvmsta->bt_reduced_txpower == enable)
                return 0;
 
        bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC);
@@ -803,23 +801,10 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
 
        switch (vif->type) {
        case NL80211_IFTYPE_STATION:
+               /* Count BSSes vifs */
+               data->num_bss_ifaces++;
                /* default smps_mode for BSS / P2P client is AUTOMATIC */
                smps_mode = IEEE80211_SMPS_AUTOMATIC;
-               data->num_bss_ifaces++;
-
-               /*
-                * Count unassoc BSSes, relax SMSP constraints
-                * and disable reduced Tx Power
-                */
-               if (!vif->bss_conf.assoc) {
-                       iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
-                                           smps_mode);
-                       if (iwl_mvm_bt_coex_reduced_txp(mvm,
-                                                       mvmvif->ap_sta_id,
-                                                       false))
-                               IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
-                       return;
-               }
                break;
        case NL80211_IFTYPE_AP:
                /* default smps_mode for AP / GO is OFF */
@@ -845,8 +830,12 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
                /* ... relax constraints and disable rssi events */
                iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
                                    smps_mode);
-               if (vif->type == NL80211_IFTYPE_STATION)
+               data->reduced_tx_power = false;
+               if (vif->type == NL80211_IFTYPE_STATION) {
+                       iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
+                                                   false);
                        iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
+               }
                return;
        }
 
@@ -857,6 +846,11 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
                smps_mode = vif->type == NL80211_IFTYPE_AP ?
                                IEEE80211_SMPS_OFF :
                                IEEE80211_SMPS_DYNAMIC;
+
+       /* relax SMPS contraints for next association */
+       if (!vif->bss_conf.assoc)
+               smps_mode = IEEE80211_SMPS_AUTOMATIC;
+
        IWL_DEBUG_COEX(data->mvm,
                       "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n",
                       mvmvif->id, data->notif->bt_status, bt_activity_grading,
@@ -903,22 +897,18 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
                /* if secondary is not NULL, it might be a GO */
                data->secondary = chanctx_conf;
 
-       /* don't reduce the Tx power if in loose scheme */
+       /*
+        * don't reduce the Tx power if one of these is true:
+        *  we are in LOOSE
+        *  single share antenna product
+        *  BT is active
+        *  we are associated
+        */
        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;
-       }
-
-       /* 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");
+           mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc ||
+           !data->notif->bt_status) {
                data->reduced_tx_power = false;
-
-               /* ... and there is no need to get reports on RSSI any more. */
+               iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false);
                iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
                return;
        }
@@ -1022,9 +1012,9 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
 
        /* 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,
+               if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0,
                                         sizeof(cmd), &cmd))
-                       IWL_ERR(mvm, "Failed to send BT_CI cmd");
+                       IWL_ERR(mvm, "Failed to send BT_CI cmd\n");
                memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd));
        }
 
@@ -1039,7 +1029,6 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
                IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
 }
 
-/* upon association, the fw will send in BT Coex notification */
 int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
                             struct iwl_rx_cmd_buffer *rxb,
                             struct iwl_device_cmd *dev_cmd)
@@ -1215,6 +1204,17 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
        return iwl_get_coex_type(mvm, mvmsta->vif) == BT_COEX_TIGHT_LUT;
 }
 
+bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
+                                   enum ieee80211_band band)
+{
+       u32 bt_activity = le32_to_cpu(mvm->last_bt_notif.bt_activity_grading);
+
+       if (band != IEEE80211_BAND_2GHZ)
+               return false;
+
+       return bt_activity >= BT_LOW_TRAFFIC;
+}
+
 u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
                           struct ieee80211_tx_info *info, u8 ac)
 {
@@ -1249,9 +1249,6 @@ u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
 
 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);
 }
 
@@ -1270,7 +1267,6 @@ int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm,
                .id = BT_CONFIG,
                .len = { sizeof(*bt_cmd), },
                .dataflags = { IWL_HCMD_DFL_NOCOPY, },
-               .flags = CMD_SYNC,
        };
 
        if (!IWL_MVM_BT_COEX_CORUNNING)
index e56f5a0edf855331a1411e76406a143176b5e9d5..645b3cfc29a5e5c0bcb10f6c9eaded6a1827e5ed 100644 (file)
@@ -193,8 +193,7 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw,
                        wkc.wep_key.key_offset = data->wep_key_idx;
                }
 
-               ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, CMD_SYNC,
-                                          sizeof(wkc), &wkc);
+               ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, 0, sizeof(wkc), &wkc);
                data->error = ret != 0;
 
                mvm->ptk_ivlen = key->iv_len;
@@ -341,7 +340,6 @@ static int iwl_mvm_send_patterns(struct iwl_mvm *mvm,
        struct iwl_host_cmd cmd = {
                .id = WOWLAN_PATTERNS,
                .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
-               .flags = CMD_SYNC,
        };
        int i, err;
 
@@ -518,7 +516,6 @@ static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm,
                .id = REMOTE_WAKE_CONFIG_CMD,
                .len = { sizeof(*cfg), },
                .dataflags = { IWL_HCMD_DFL_NOCOPY, },
-               .flags = CMD_SYNC,
        };
        int ret;
 
@@ -666,10 +663,8 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
        if (WARN_ON(!vif->bss_conf.assoc))
                return -EINVAL;
-       /* hack */
-       vif->bss_conf.assoc = false;
+
        ret = iwl_mvm_mac_ctxt_add(mvm, vif);
-       vif->bss_conf.assoc = true;
        if (ret)
                return ret;
 
@@ -705,7 +700,7 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                return ret;
        rcu_assign_pointer(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id], ap_sta);
 
-       ret = iwl_mvm_mac_ctxt_changed(mvm, vif);
+       ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false);
        if (ret)
                return ret;
 
@@ -719,7 +714,7 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        for (i = 1; i < MAX_BINDINGS; i++)
                quota_cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID);
 
-       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, CMD_SYNC,
+       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0,
                                   sizeof(quota_cmd), &quota_cmd);
        if (ret)
                IWL_ERR(mvm, "Failed to send quota: %d\n", ret);
@@ -739,15 +734,13 @@ static int iwl_mvm_get_last_nonqos_seq(struct iwl_mvm *mvm,
        };
        struct iwl_host_cmd cmd = {
                .id = NON_QOS_TX_COUNTER_CMD,
-               .flags = CMD_SYNC | CMD_WANT_SKB,
+               .flags = 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);
-       }
+       cmd.data[0] = &query_cmd;
+       cmd.len[0] = sizeof(query_cmd);
 
        err = iwl_mvm_send_cmd(mvm, &cmd);
        if (err)
@@ -758,10 +751,8 @@ static int iwl_mvm_get_last_nonqos_seq(struct iwl_mvm *mvm,
                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 = (u16) (err - 0x10);
+               /* firmware returns next, not last-used seqno */
+               err = (u16) (err - 0x10);
        }
 
        iwl_free_resp(&cmd);
@@ -785,11 +776,7 @@ void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 
        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,
+       if (iwl_mvm_send_cmd_pdu(mvm, NON_QOS_TX_COUNTER_CMD, 0,
                                 sizeof(query_cmd), &query_cmd))
                IWL_ERR(mvm, "failed to set non-QoS seqno\n");
 }
@@ -804,7 +791,7 @@ iwl_mvm_send_wowlan_config_cmd(struct iwl_mvm *mvm,
        if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID)
                cmd_len = sizeof(*cmd);
 
-       return iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, CMD_SYNC,
+       return iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0,
                                    cmd_len, cmd);
 }
 
@@ -833,7 +820,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
        };
        struct iwl_host_cmd d3_cfg_cmd = {
                .id = D3_CONFIG_CMD,
-               .flags = CMD_SYNC | CMD_WANT_SKB,
+               .flags = CMD_WANT_SKB,
                .data[0] = &d3_cfg_cmd_data,
                .len[0] = sizeof(d3_cfg_cmd_data),
        };
@@ -983,7 +970,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
                if (key_data.use_rsc_tsc) {
                        struct iwl_host_cmd rsc_tsc_cmd = {
                                .id = WOWLAN_TSC_RSC_PARAM,
-                               .flags = CMD_SYNC,
                                .data[0] = key_data.rsc_tsc,
                                .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
                                .len[0] = sizeof(*key_data.rsc_tsc),
@@ -997,7 +983,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
                if (key_data.use_tkip) {
                        ret = iwl_mvm_send_cmd_pdu(mvm,
                                                   WOWLAN_TKIP_PARAM,
-                                                  CMD_SYNC, sizeof(tkip_cmd),
+                                                  0, sizeof(tkip_cmd),
                                                   &tkip_cmd);
                        if (ret)
                                goto out;
@@ -1014,8 +1000,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
                        kek_kck_cmd.replay_ctr = mvmvif->rekey_data.replay_ctr;
 
                        ret = iwl_mvm_send_cmd_pdu(mvm,
-                                                  WOWLAN_KEK_KCK_MATERIAL,
-                                                  CMD_SYNC,
+                                                  WOWLAN_KEK_KCK_MATERIAL, 0,
                                                   sizeof(kek_kck_cmd),
                                                   &kek_kck_cmd);
                        if (ret)
@@ -1031,7 +1016,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
        if (ret)
                goto out;
 
-       ret = iwl_mvm_send_proto_offload(mvm, vif, false, CMD_SYNC);
+       ret = iwl_mvm_send_proto_offload(mvm, vif, false, 0);
        if (ret)
                goto out;
 
@@ -1043,7 +1028,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
        if (ret)
                goto out;
 
-       ret = iwl_mvm_power_update_mac(mvm, vif);
+       ret = iwl_mvm_power_update_mac(mvm);
        if (ret)
                goto out;
 
@@ -1082,6 +1067,15 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
 
 int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
 {
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       if (iwl_mvm_is_d0i3_supported(mvm)) {
+               mutex_lock(&mvm->d0i3_suspend_mutex);
+               __set_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags);
+               mutex_unlock(&mvm->d0i3_suspend_mutex);
+               return 0;
+       }
+
        return __iwl_mvm_suspend(hw, wowlan, false);
 }
 
@@ -1277,7 +1271,7 @@ static void iwl_mvm_set_tkip_rx_seq(struct tkip_sc *scs,
 }
 
 static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key,
-                                  struct iwl_wowlan_status_v6 *status)
+                                  struct iwl_wowlan_status *status)
 {
        union iwl_all_tsc_rsc *rsc = &status->gtk.rsc.all_tsc_rsc;
 
@@ -1294,7 +1288,7 @@ static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key,
 }
 
 struct iwl_mvm_d3_gtk_iter_data {
-       struct iwl_wowlan_status_v6 *status;
+       struct iwl_wowlan_status *status;
        void *last_gtk;
        u32 cipher;
        bool find_phase, unhandled_cipher;
@@ -1370,7 +1364,7 @@ static void iwl_mvm_d3_update_gtks(struct ieee80211_hw *hw,
 
 static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
                                          struct ieee80211_vif *vif,
-                                         struct iwl_wowlan_status_v6 *status)
+                                         struct iwl_wowlan_status *status)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mvm_d3_gtk_iter_data gtkdata = {
@@ -1465,10 +1459,10 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
        } err_info;
        struct iwl_host_cmd cmd = {
                .id = WOWLAN_GET_STATUSES,
-               .flags = CMD_SYNC | CMD_WANT_SKB,
+               .flags = CMD_WANT_SKB,
        };
        struct iwl_wowlan_status_data status;
-       struct iwl_wowlan_status_v6 *status_v6;
+       struct iwl_wowlan_status *fw_status;
        int ret, len, status_size, i;
        bool keep;
        struct ieee80211_sta *ap_sta;
@@ -1491,7 +1485,7 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
        }
 
        /* only for tracing for now */
-       ret = iwl_mvm_send_cmd_pdu(mvm, OFFLOADS_QUERY_CMD, CMD_SYNC, 0, NULL);
+       ret = iwl_mvm_send_cmd_pdu(mvm, OFFLOADS_QUERY_CMD, 0, 0, NULL);
        if (ret)
                IWL_ERR(mvm, "failed to query offload statistics (%d)\n", ret);
 
@@ -1505,10 +1499,7 @@ static bool 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);
+       status_size = sizeof(*fw_status);
 
        len = iwl_rx_packet_payload_len(cmd.resp_pkt);
        if (len < status_size) {
@@ -1516,35 +1507,18 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
                goto out_free_resp;
        }
 
-       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;
-       }
+       fw_status = (void *)cmd.resp_pkt->data;
+
+       status.pattern_number = le16_to_cpu(fw_status->pattern_number);
+       for (i = 0; i < 8; i++)
+               status.qos_seq_ctr[i] =
+                       le16_to_cpu(fw_status->qos_seq_ctr[i]);
+       status.wakeup_reasons = le32_to_cpu(fw_status->wakeup_reasons);
+       status.wake_packet_length =
+               le32_to_cpu(fw_status->wake_packet_length);
+       status.wake_packet_bufsize =
+               le32_to_cpu(fw_status->wake_packet_bufsize);
+       status.wake_packet = fw_status->wake_packet;
 
        if (len != status_size + ALIGN(status.wake_packet_bufsize, 4)) {
                IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
@@ -1571,7 +1545,7 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
 
        iwl_mvm_report_wakeup_reasons(mvm, vif, &status);
 
-       keep = iwl_mvm_setup_connection_keep(mvm, vif, status_v6);
+       keep = iwl_mvm_setup_connection_keep(mvm, vif, fw_status);
 
        iwl_free_resp(&cmd);
        return keep;
@@ -1674,6 +1648,19 @@ int iwl_mvm_resume(struct ieee80211_hw *hw)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 
+       if (iwl_mvm_is_d0i3_supported(mvm)) {
+               bool exit_now;
+
+               mutex_lock(&mvm->d0i3_suspend_mutex);
+               __clear_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags);
+               exit_now = __test_and_clear_bit(D0I3_PENDING_WAKEUP,
+                                               &mvm->d0i3_suspend_flags);
+               mutex_unlock(&mvm->d0i3_suspend_mutex);
+               if (exit_now)
+                       _iwl_mvm_exit_d0i3(mvm);
+               return 0;
+       }
+
        return __iwl_mvm_resume(mvm, false);
 }
 
index 9b59e1d7ae71ea888973992cb88363ba2371fdad..2e90ff795c13212d6d8ca7be35df935a907d6b3c 100644 (file)
@@ -103,10 +103,6 @@ static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm,
                IWL_DEBUG_POWER(mvm, "tx_data_timeout=%d\n", val);
                dbgfs_pm->tx_data_timeout = val;
                break;
-       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;
@@ -154,12 +150,6 @@ static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf,
                if (sscanf(buf + 16, "%d", &val) != 1)
                        return -EINVAL;
                param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT;
-       } 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;
        } else if (!strncmp("lprx=", buf, 5)) {
                if (sscanf(buf + 5, "%d", &val) != 1)
                        return -EINVAL;
@@ -185,7 +175,7 @@ static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf,
 
        mutex_lock(&mvm->mutex);
        iwl_dbgfs_update_pm(mvm, vif, param, val);
-       ret = iwl_mvm_power_update_mac(mvm, vif);
+       ret = iwl_mvm_power_update_mac(mvm);
        mutex_unlock(&mvm->mutex);
 
        return ret ?: count;
@@ -272,10 +262,9 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
                        struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
 
                        pos += scnprintf(buf+pos, bufsz-pos,
-                                        "ap_sta_id %d - reduced Tx power %d force %d\n",
+                                        "ap_sta_id %d - reduced Tx power %d\n",
                                         ap_sta_id,
-                                        mvm_sta->bt_reduced_txpower,
-                                        mvm_sta->bt_reduced_txpower_dbg);
+                                        mvm_sta->bt_reduced_txpower);
                }
        }
 
@@ -293,41 +282,6 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
 
-static ssize_t iwl_dbgfs_reduced_txp_write(struct ieee80211_vif *vif,
-                                          char *buf, size_t count,
-                                          loff_t *ppos)
-{
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       struct iwl_mvm *mvm = mvmvif->mvm;
-       struct iwl_mvm_sta *mvmsta;
-       bool reduced_tx_power;
-       int ret;
-
-       if (mvmvif->ap_sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id))
-               return -ENOTCONN;
-
-       if (strtobool(buf, &reduced_tx_power) != 0)
-               return -EINVAL;
-
-       mutex_lock(&mvm->mutex);
-
-       mvmsta = iwl_mvm_sta_from_staid_protected(mvm, mvmvif->ap_sta_id);
-       if (IS_ERR_OR_NULL(mvmsta)) {
-               mutex_unlock(&mvm->mutex);
-               return -ENOTCONN;
-       }
-
-       mvmsta->bt_reduced_txpower_dbg = false;
-       ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
-                                         reduced_tx_power);
-       if (!ret)
-               mvmsta->bt_reduced_txpower_dbg = true;
-
-       mutex_unlock(&mvm->mutex);
-
-       return ret ? : count;
-}
-
 static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif,
                                enum iwl_dbgfs_bf_mask param, int value)
 {
@@ -462,9 +416,9 @@ static ssize_t iwl_dbgfs_bf_params_write(struct ieee80211_vif *vif, char *buf,
        mutex_lock(&mvm->mutex);
        iwl_dbgfs_update_bf(vif, param, value);
        if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value)
-               ret = iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC);
+               ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
        else
-               ret = iwl_mvm_enable_beacon_filter(mvm, vif, CMD_SYNC);
+               ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
        mutex_unlock(&mvm->mutex);
 
        return ret ?: count;
@@ -568,7 +522,6 @@ MVM_DEBUGFS_READ_FILE_OPS(mac_params);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params, 32);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params, 256);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 10);
-MVM_DEBUGFS_WRITE_FILE_OPS(reduced_txp, 10);
 
 void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 {
@@ -592,8 +545,7 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
                return;
        }
 
-       if ((mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT) &&
-           iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM &&
+       if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM &&
            ((vif->type == NL80211_IFTYPE_STATION && !vif->p2p) ||
             (vif->type == NL80211_IFTYPE_STATION && vif->p2p &&
              mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM)))
@@ -601,7 +553,6 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
                                         S_IRUSR);
 
        MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, S_IRUSR);
-       MVM_DEBUGFS_ADD_FILE_VIF(reduced_txp, mvmvif->dbgfs_dir, S_IWUSR);
        MVM_DEBUGFS_ADD_FILE_VIF(low_latency, mvmvif->dbgfs_dir,
                                 S_IRUSR | S_IWUSR);
 
index 1b52deea60812e4f6ac42c94412b1ecf518f44f7..29ca72695eaa60e0f53121dd45f1d080cdefba1d 100644 (file)
@@ -65,9 +65,8 @@
 #include "mvm.h"
 #include "sta.h"
 #include "iwl-io.h"
-#include "iwl-prph.h"
 #include "debugfs.h"
-#include "fw-error-dump.h"
+#include "iwl-fw-error-dump.h"
 
 static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf,
                                        size_t count, loff_t *ppos)
@@ -136,9 +135,6 @@ static int iwl_dbgfs_fw_error_dump_open(struct inode *inode, struct file *file)
 
        file->private_data = mvm->fw_error_dump;
        mvm->fw_error_dump = NULL;
-       kfree(mvm->fw_error_sram);
-       mvm->fw_error_sram = NULL;
-       mvm->fw_error_sram_len = 0;
        ret = 0;
 
 out:
@@ -684,7 +680,7 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mvm *mvm, char *buf,
                mvm->restart_fw++;
 
        /* take the return value to make compiler happy - it will fail anyway */
-       ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_ERROR, CMD_SYNC, 0, NULL);
+       ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_ERROR, 0, 0, NULL);
 
        mutex_unlock(&mvm->mutex);
 
@@ -694,7 +690,7 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mvm *mvm, char *buf,
 static ssize_t iwl_dbgfs_fw_nmi_write(struct iwl_mvm *mvm, char *buf,
                                      size_t count, loff_t *ppos)
 {
-       iwl_write_prph(mvm->trans, DEVICE_SET_NMI_REG, 1);
+       iwl_force_nmi(mvm->trans);
 
        return count;
 }
@@ -841,7 +837,7 @@ static ssize_t iwl_dbgfs_bcast_filters_write(struct iwl_mvm *mvm, char *buf,
        /* send updated bcast filtering configuration */
        if (mvm->dbgfs_bcast_filtering.override &&
            iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
-               err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, CMD_SYNC,
+               err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0,
                                           sizeof(cmd), &cmd);
        mutex_unlock(&mvm->mutex);
 
@@ -913,7 +909,7 @@ static ssize_t iwl_dbgfs_bcast_filters_macs_write(struct iwl_mvm *mvm,
        /* send updated bcast filtering configuration */
        if (mvm->dbgfs_bcast_filtering.override &&
            iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
-               err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, CMD_SYNC,
+               err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0,
                                           sizeof(cmd), &cmd);
        mutex_unlock(&mvm->mutex);
 
@@ -1004,6 +1000,7 @@ static ssize_t iwl_dbgfs_d0i3_refs_read(struct file *file,
        PRINT_MVM_REF(IWL_MVM_REF_P2P_CLIENT);
        PRINT_MVM_REF(IWL_MVM_REF_AP_IBSS);
        PRINT_MVM_REF(IWL_MVM_REF_USER);
+       PRINT_MVM_REF(IWL_MVM_REF_EXIT_WORK);
 
        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
@@ -1108,9 +1105,9 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8);
 
 static const struct file_operations iwl_dbgfs_fw_error_dump_ops = {
-        .open = iwl_dbgfs_fw_error_dump_open,
-        .read = iwl_dbgfs_fw_error_dump_read,
-        .release = iwl_dbgfs_fw_error_dump_release,
+       .open = iwl_dbgfs_fw_error_dump_open,
+       .read = iwl_dbgfs_fw_error_dump_read,
+       .release = iwl_dbgfs_fw_error_dump_release,
 };
 
 #ifdef CONFIG_IWLWIFI_BCAST_FILTERING
@@ -1138,9 +1135,8 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
        MVM_DEBUGFS_ADD_FILE(fw_error_dump, dbgfs_dir, S_IRUSR);
        MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR);
        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(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(drv_rx_stats, mvm->debugfs_dir, S_IRUSR);
        MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR);
index 21877e5966a8093d6780d3297944c1a214dc55e3..5fe82c29c8ad07bcb7bab43726a4e09b136d8b53 100644 (file)
@@ -141,7 +141,8 @@ enum iwl_bt_coex_lut_type {
        BT_COEX_TX_DIS_LUT,
 
        BT_COEX_MAX_LUT,
-};
+       BT_COEX_INVALID_LUT = 0xff,
+}; /* BT_COEX_DECISION_LUT_INDEX_API_E_VER_1 */
 
 #define BT_COEX_LUT_SIZE (12)
 #define BT_COEX_CORUN_LUT_SIZE (32)
@@ -154,19 +155,23 @@ enum iwl_bt_coex_lut_type {
  * @flags:&enum iwl_bt_coex_flags
  * @max_kill:
  * @bt_reduced_tx_power: enum %iwl_bt_reduced_tx_power
- * @bt4_antenna_isolation:
- * @bt4_antenna_isolation_thr:
- * @bt4_tx_tx_delta_freq_thr:
- * @bt4_tx_rx_max_freq0:
- * @bt_prio_boost:
+ * @override_primary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT
+ *     should be set by default
+ * @override_secondary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT
+ *     should be set by default
+ * @bt4_antenna_isolation: antenna isolation
+ * @bt4_antenna_isolation_thr: antenna threshold value
+ * @bt4_tx_tx_delta_freq_thr: TxTx delta frequency
+ * @bt4_tx_rx_max_freq0: TxRx max frequency
+ * @bt_prio_boost: BT priority boost registers
  * @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:
+ * @kill_ack_msk: kill ACK mask. 1 - Tx ACK, 0 - kill Tx of ACK.
+ * @kill_cts_msk: kill CTS mask. 1 - Tx CTS, 0 - kill Tx of CTS.
+ * @decision_lut: PTA decision LUT, per Prio-Ch
+ * @bt4_multiprio_lut: multi priority LUT configuration
+ * @bt4_corun_lut20: co-running 20 MHz LUT configuration
+ * @bt4_corun_lut40: co-running 40 MHz LUT configuration
  * @valid_bit_msk: enum %iwl_bt_coex_valid_bit_msk
  *
  * The structure is used for the BT_COEX command.
@@ -175,7 +180,8 @@ struct iwl_bt_coex_cmd {
        __le32 flags;
        u8 max_kill;
        u8 bt_reduced_tx_power;
-       u8 reserved[2];
+       u8 override_primary_lut;
+       u8 override_secondary_lut;
 
        u8 bt4_antenna_isolation;
        u8 bt4_antenna_isolation_thr;
@@ -194,7 +200,7 @@ struct iwl_bt_coex_cmd {
        __le32 bt4_corun_lut40[BT_COEX_CORUN_LUT_SIZE];
 
        __le32 valid_bit_msk;
-} __packed; /* BT_COEX_CMD_API_S_VER_3 */
+} __packed; /* BT_COEX_CMD_API_S_VER_5 */
 
 /**
  * struct iwl_bt_coex_ci_cmd - bt coex channel inhibition command
@@ -282,7 +288,7 @@ enum iwl_bt_activity_grading {
        BT_ON_NO_CONNECTION     = 1,
        BT_LOW_TRAFFIC          = 2,
        BT_HIGH_TRAFFIC         = 3,
-};
+}; /* BT_COEX_BT_ACTIVITY_GRADING_API_E_VER_1 */
 
 /**
  * struct iwl_bt_coex_profile_notif - notification about BT coex
@@ -310,7 +316,7 @@ struct iwl_bt_coex_profile_notif {
        __le32 primary_ch_lut;
        __le32 secondary_ch_lut;
        __le32 bt_activity_grading;
-} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_2 */
+} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_3 */
 
 enum iwl_bt_coex_prio_table_event {
        BT_COEX_PRIO_TBL_EVT_INIT_CALIB1                = 0,
index 10fcc1a79ebddf3087d7de7c2c29389849a425fa..13696fe419b778c68c9d72d7a289a3dd3c453b39 100644 (file)
@@ -345,21 +345,6 @@ 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_v4 {
-       __le64 replay_ctr;
-       __le16 pattern_number;
-       __le16 non_qos_seq_ctr;
-       __le16 qos_seq_ctr[8];
-       __le32 wakeup_reasons;
-       __le32 rekey_status;
-       __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_4 */
-
 struct iwl_wowlan_gtk_status {
        u8 key_index;
        u8 reserved[3];
@@ -368,7 +353,7 @@ struct iwl_wowlan_gtk_status {
        struct iwl_wowlan_rsc_tsc_params_cmd rsc;
 } __packed;
 
-struct iwl_wowlan_status_v6 {
+struct iwl_wowlan_status {
        struct iwl_wowlan_gtk_status gtk;
        __le64 replay_ctr;
        __le16 pattern_number;
index 39148b5bb33262596e1dea348c1ae32c9a2c9166..8bb5b94bf9639689fa6445cd046f97eccbcec834 100644 (file)
@@ -334,7 +334,7 @@ enum {
  */
 struct iwl_lq_cmd {
        u8 sta_id;
-       u8 reserved1;
+       u8 reduced_tpc;
        u16 control;
        /* LINK_QUAL_GENERAL_PARAMS_API_S_VER_1 */
        u8 flags;
index d73a89ecd78aa0963c40a268fc7dd8e3d7fbe29d..6959fda3fe09d09e34d5fe19c7ded403fba79c37 100644 (file)
@@ -169,8 +169,12 @@ enum iwl_scan_type {
        SCAN_TYPE_DISCOVERY_FORCED      = 6,
 }; /* SCAN_ACTIVITY_TYPE_E_VER_1 */
 
-/* Maximal number of channels to scan */
-#define MAX_NUM_SCAN_CHANNELS 0x24
+/**
+ * Maximal number of channels to scan
+ * it should be equal to:
+ * max(IWL_NUM_CHANNELS, IWL_NUM_CHANNELS_FAMILY_8000)
+ */
+#define MAX_NUM_SCAN_CHANNELS 50
 
 /**
  * struct iwl_scan_cmd - scan request command
@@ -534,13 +538,16 @@ struct iwl_scan_offload_schedule {
  *
  * IWL_SCAN_OFFLOAD_FLAG_PASS_ALL: pass all results - no filtering.
  * IWL_SCAN_OFFLOAD_FLAG_CACHED_CHANNEL: add cached channels to partial scan.
- * IWL_SCAN_OFFLOAD_FLAG_ENERGY_SCAN: use energy based scan before partial scan
- *     on A band.
+ * IWL_SCAN_OFFLOAD_FLAG_EBS_QUICK_MODE: EBS duration is 100mSec - typical
+ *     beacon period. Finding channel activity in this mode is not guaranteed.
+ * IWL_SCAN_OFFLOAD_FLAG_EBS_ACCURATE_MODE: EBS duration is 200mSec.
+ *     Assuming beacon period is 100ms finding channel activity is guaranteed.
  */
 enum iwl_scan_offload_flags {
        IWL_SCAN_OFFLOAD_FLAG_PASS_ALL          = BIT(0),
        IWL_SCAN_OFFLOAD_FLAG_CACHED_CHANNEL    = BIT(2),
-       IWL_SCAN_OFFLOAD_FLAG_ENERGY_SCAN       = BIT(3),
+       IWL_SCAN_OFFLOAD_FLAG_EBS_QUICK_MODE    = BIT(5),
+       IWL_SCAN_OFFLOAD_FLAG_EBS_ACCURATE_MODE = BIT(6),
 };
 
 /**
@@ -563,17 +570,24 @@ enum iwl_scan_offload_compleate_status {
        IWL_SCAN_OFFLOAD_ABORTED        = 2,
 };
 
+enum iwl_scan_ebs_status {
+       IWL_SCAN_EBS_SUCCESS,
+       IWL_SCAN_EBS_FAILED,
+       IWL_SCAN_EBS_CHAN_NOT_FOUND,
+};
+
 /**
  * iwl_scan_offload_complete - SCAN_OFFLOAD_COMPLETE_NTF_API_S_VER_1
  * @last_schedule_line:                last schedule line executed (fast or regular)
  * @last_schedule_iteration:   last scan iteration executed before scan abort
  * @status:                    enum iwl_scan_offload_compleate_status
+ * @ebs_status: last EBS status, see IWL_SCAN_EBS_*
  */
 struct iwl_scan_offload_complete {
        u8 last_schedule_line;
        u8 last_schedule_iteration;
        u8 status;
-       u8 reserved;
+       u8 ebs_status;
 } __packed;
 
 /**
index d636478672626e9436c12dc2313d3288d94303ed..39cebee8016feaab62f005e5e843447784594429 100644 (file)
@@ -255,22 +255,19 @@ struct iwl_mvm_keyinfo {
 } __packed;
 
 /**
- * struct iwl_mvm_add_sta_cmd_v5 - Add/modify a station in the fw's sta table.
+ * struct iwl_mvm_add_sta_cmd - 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
- * @multicast_tx_key_id: multicast tx key id. Relevant only when multicast key
- *     sent
+ * @awake_acs:
+ * @tid_disable_tx: is tid BIT(tid) enabled for Tx. Clear BIT(x) to enable
+ *     AMPDU for tid x. Set %STA_MODIFY_TID_DISABLE_TX to change this field.
  * @mac_id_n_color: the Mac context this station belongs to
  * @addr[ETH_ALEN]: station's MAC address
  * @sta_id: index of station in uCode's station table
  * @modify_mask: STA_MODIFY_*, selects which parameters to modify vs. leave
  *     alone. 1 - modify, 0 - don't change.
- * @key: look at %iwl_mvm_keyinfo
  * @station_flags: look at %iwl_sta_flags
  * @station_flags_msk: what of %station_flags have changed
- * @tid_disable_tx: is tid BIT(tid) enabled for Tx. Clear BIT(x) to enable
- *     AMPDU for tid x. Set %STA_MODIFY_TID_DISABLE_TX to change this field.
  * @add_immediate_ba_tid: tid for which to add block-ack support (Rx)
  *     Set %STA_MODIFY_ADD_BA_TID to use this field, and also set
  *     add_immediate_ba_ssn.
@@ -294,40 +291,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_v5 {
-       u8 add_modify;
-       u8 unicast_tx_key_id;
-       u8 multicast_tx_key_id;
-       u8 reserved1;
-       __le32 mac_id_n_color;
-       u8 addr[ETH_ALEN];
-       __le16 reserved2;
-       u8 sta_id;
-       u8 modify_mask;
-       __le16 reserved3;
-       struct iwl_mvm_keyinfo key;
-       __le32 station_flags;
-       __le32 station_flags_msk;
-       __le16 tid_disable_tx;
-       __le16 reserved4;
-       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_5 */
-
-/**
- * struct iwl_mvm_add_sta_cmd_v7 - Add / modify a station
- * VER_7 of this command is quite similar to VER_5 except
- * exclusion of all fields related to the security key installation.
- * It only differs from VER_6 by the "awake_acs" field that is
- * reserved and ignored in VER_6.
- */
-struct iwl_mvm_add_sta_cmd_v7 {
+struct iwl_mvm_add_sta_cmd {
        u8 add_modify;
        u8 awake_acs;
        __le16 tid_disable_tx;
index 8e122f3a7a74e8a97914a13d9821a7229bc7c2bc..6cc5f52b807f1bc343ea632674e215423c3abb1d 100644 (file)
@@ -482,7 +482,8 @@ struct iwl_mvm_tx_resp {
        u8 pa_integ_res_b[3];
        u8 pa_integ_res_c[3];
        __le16 measurement_req_id;
-       __le16 reserved;
+       u8 reduced_tpc;
+       u8 reserved;
 
        __le32 tfd_info;
        __le16 seq_ctl;
index 6e75b52588de3ca68a44c41ca339df1e57eae37f..309a9b9a94fecc26918f967e7b9e7a01374d43b3 100644 (file)
@@ -71,6 +71,7 @@
 #include "fw-api-power.h"
 #include "fw-api-d3.h"
 #include "fw-api-coex.h"
+#include "fw-api-scan.h"
 
 /* maximal number of Tx queues in any platform */
 #define IWL_MVM_MAX_QUEUES     20
@@ -604,52 +605,7 @@ enum {
        TE_V1_NOTIF_INTERNAL_FRAG_END = BIT(7),
 }; /* MAC_EVENT_ACTION_API_E_VER_2 */
 
-
-/**
- * struct iwl_time_event_cmd_api_v1 - configuring Time Events
- * with struct MAC_TIME_EVENT_DATA_API_S_VER_1 (see also
- * with version 2. determined by IWL_UCODE_TLV_FLAGS)
- * ( TIME_EVENT_CMD = 0x29 )
- * @id_and_color: ID and color of the relevant MAC
- * @action: action to perform, one of FW_CTXT_ACTION_*
- * @id: this field has two meanings, depending on the action:
- *     If the action is ADD, then it means the type of event to add.
- *     For all other actions it is the unique event ID assigned when the
- *     event was added by the FW.
- * @apply_time: When to start the Time Event (in GP2)
- * @max_delay: maximum delay to event's start (apply time), in TU
- * @depends_on: the unique ID of the event we depend on (if any)
- * @interval: interval between repetitions, in TU
- * @interval_reciprocal: 2^32 / interval
- * @duration: duration of event in TU
- * @repeat: how many repetitions to do, can be TE_REPEAT_ENDLESS
- * @dep_policy: one of TE_V1_INDEPENDENT, TE_V1_DEP_OTHER, TE_V1_DEP_TSF
- *     and TE_V1_EVENT_SOCIOPATHIC
- * @is_present: 0 or 1, are we present or absent during the Time Event
- * @max_frags: maximal number of fragments the Time Event can be divided to
- * @notify: notifications using TE_V1_NOTIF_* (whom to notify when)
- */
-struct iwl_time_event_cmd_v1 {
-       /* COMMON_INDEX_HDR_API_S_VER_1 */
-       __le32 id_and_color;
-       __le32 action;
-       __le32 id;
-       /* MAC_TIME_EVENT_DATA_API_S_VER_1 */
-       __le32 apply_time;
-       __le32 max_delay;
-       __le32 dep_policy;
-       __le32 depends_on;
-       __le32 is_present;
-       __le32 max_frags;
-       __le32 interval;
-       __le32 interval_reciprocal;
-       __le32 duration;
-       __le32 repeat;
-       __le32 notify;
-} __packed; /* MAC_TIME_EVENT_CMD_API_S_VER_1 */
-
-
-/* Time event - defines for command API v2 */
+/* Time event - defines for command API */
 
 /*
  * @TE_V2_FRAG_NONE: fragmentation of the time event is NOT allowed.
@@ -680,7 +636,7 @@ enum {
 #define TE_V2_PLACEMENT_POS    12
 #define TE_V2_ABSENCE_POS      15
 
-/* Time event policy values (for time event cmd api v2)
+/* Time event policy values
  * A notification (both event and fragment) includes a status indicating weather
  * the FW was able to schedule the event or not. For fragment start/end
  * notification the status is always success. There is no start/end fragment
@@ -727,7 +683,7 @@ enum {
 };
 
 /**
- * struct iwl_time_event_cmd_api_v2 - configuring Time Events
+ * struct iwl_time_event_cmd_api - configuring Time Events
  * with struct MAC_TIME_EVENT_DATA_API_S_VER_2 (see also
  * with version 1. determined by IWL_UCODE_TLV_FLAGS)
  * ( TIME_EVENT_CMD = 0x29 )
@@ -750,7 +706,7 @@ enum {
  *     TE_EVENT_SOCIOPATHIC
  *     using TE_ABSENCE and using TE_NOTIF_*
  */
-struct iwl_time_event_cmd_v2 {
+struct iwl_time_event_cmd {
        /* COMMON_INDEX_HDR_API_S_VER_1 */
        __le32 id_and_color;
        __le32 action;
index 7ce20062f32d443be34fe87865d91afd71a0a014..883e702152d5289163f48f7464cf630f6795ec2f 100644 (file)
@@ -99,7 +99,7 @@ static int iwl_send_tx_ant_cfg(struct iwl_mvm *mvm, u8 valid_tx_ant)
        };
 
        IWL_DEBUG_FW(mvm, "select valid tx ant: %u\n", valid_tx_ant);
-       return iwl_mvm_send_cmd_pdu(mvm, TX_ANT_CONFIGURATION_CMD, CMD_SYNC,
+       return iwl_mvm_send_cmd_pdu(mvm, TX_ANT_CONFIGURATION_CMD, 0,
                                    sizeof(tx_ant_cmd), &tx_ant_cmd);
 }
 
@@ -137,6 +137,8 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
                alive_data->scd_base_addr = le32_to_cpu(palive2->scd_base_ptr);
                mvm->umac_error_event_table =
                        le32_to_cpu(palive2->error_info_addr);
+               mvm->sf_space.addr = le32_to_cpu(palive2->st_fwrd_addr);
+               mvm->sf_space.size = le32_to_cpu(palive2->st_fwrd_size);
 
                alive_data->valid = le16_to_cpu(palive2->status) ==
                                    IWL_ALIVE_STATUS_OK;
@@ -180,6 +182,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
        int ret, i;
        enum iwl_ucode_type old_type = mvm->cur_ucode;
        static const u8 alive_cmd[] = { MVM_ALIVE };
+       struct iwl_sf_region st_fwrd_space;
 
        fw = iwl_get_ucode_image(mvm, ucode_type);
        if (WARN_ON(!fw))
@@ -215,6 +218,14 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
                return -EIO;
        }
 
+       /*
+        * update the sdio allocation according to the pointer we get in the
+        * alive notification.
+        */
+       st_fwrd_space.addr = mvm->sf_space.addr;
+       st_fwrd_space.size = mvm->sf_space.size;
+       ret = iwl_trans_update_sf(mvm->trans, &st_fwrd_space);
+
        iwl_trans_fw_alive(mvm->trans, alive_data.scd_base_addr);
 
        /*
@@ -256,7 +267,7 @@ static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm)
        IWL_DEBUG_INFO(mvm, "Sending Phy CFG command: 0x%x\n",
                       phy_cfg_cmd.phy_cfg);
 
-       return iwl_mvm_send_cmd_pdu(mvm, PHY_CONFIGURATION_CMD, CMD_SYNC,
+       return iwl_mvm_send_cmd_pdu(mvm, PHY_CONFIGURATION_CMD, 0,
                                    sizeof(phy_cfg_cmd), &phy_cfg_cmd);
 }
 
@@ -288,14 +299,14 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
                goto error;
        }
 
-       ret = iwl_send_bt_prio_tbl(mvm);
+       ret = iwl_send_bt_init_conf(mvm);
        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);
+               ret = iwl_nvm_init(mvm, true);
                if (ret) {
                        IWL_ERR(mvm, "Failed to read NVM: %d\n", ret);
                        goto error;
@@ -303,7 +314,7 @@ 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)
+       if (mvm->nvm_file_name)
                iwl_mvm_load_nvm_to_nic(mvm);
 
        ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans);
@@ -424,10 +435,6 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
        if (ret)
                goto error;
 
-       ret = iwl_send_bt_prio_tbl(mvm);
-       if (ret)
-               goto error;
-
        ret = iwl_send_bt_init_conf(mvm);
        if (ret)
                goto error;
@@ -468,12 +475,6 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
        /* Initialize tx backoffs to the minimal possible */
        iwl_mvm_tt_tx_backoff(mvm, 0);
 
-       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)) {
-               ret = iwl_power_legacy_set_cam_mode(mvm);
-               if (ret)
-                       goto error;
-       }
-
        ret = iwl_mvm_power_update_device(mvm);
        if (ret)
                goto error;
index 9ccec10bba166299cc91cf1706992937127d277a..8b530277763258551cf09292298f8f14be074174 100644 (file)
@@ -667,12 +667,9 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
        if (vif->bss_conf.qos)
                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) {
                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);
+               cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_SELF_CTS_EN);
        }
        IWL_DEBUG_RATE(mvm, "use_cts_prot %d, ht_operation_mode %d\n",
                       vif->bss_conf.use_cts_prot,
@@ -688,7 +685,7 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
 static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm,
                                     struct iwl_mac_ctx_cmd *cmd)
 {
-       int ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, CMD_SYNC,
+       int ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, 0,
                                       sizeof(*cmd), cmd);
        if (ret)
                IWL_ERR(mvm, "Failed to send MAC context (action:%d): %d\n",
@@ -696,19 +693,39 @@ static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm,
        return ret;
 }
 
-/*
- * Fill the specific data for mac context of type station or p2p client
- */
-static void iwl_mvm_mac_ctxt_cmd_fill_sta(struct iwl_mvm *mvm,
-                                         struct ieee80211_vif *vif,
-                                         struct iwl_mac_data_sta *ctxt_sta,
-                                         bool force_assoc_off)
+static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm,
+                                   struct ieee80211_vif *vif,
+                                   u32 action, bool force_assoc_off)
 {
+       struct iwl_mac_ctx_cmd cmd = {};
+       struct iwl_mac_data_sta *ctxt_sta;
+
+       WARN_ON(vif->type != NL80211_IFTYPE_STATION);
+
+       /* Fill the common data for all mac context types */
+       iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
+
+       if (vif->p2p) {
+               struct ieee80211_p2p_noa_attr *noa =
+                       &vif->bss_conf.p2p_noa_attr;
+
+               cmd.p2p_sta.ctwin = cpu_to_le32(noa->oppps_ctwindow &
+                                       IEEE80211_P2P_OPPPS_CTWINDOW_MASK);
+               ctxt_sta = &cmd.p2p_sta.sta;
+       } else {
+               ctxt_sta = &cmd.sta;
+       }
+
        /* We need the dtim_period to set the MAC as associated */
        if (vif->bss_conf.assoc && vif->bss_conf.dtim_period &&
            !force_assoc_off) {
                u32 dtim_offs;
 
+               /* Allow beacons to pass through as long as we are not
+                * associated, or we do not have dtim period information.
+                */
+               cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON);
+
                /*
                 * The DTIM count counts down, so when it is N that means N
                 * more beacon intervals happen until the DTIM TBTT. Therefore
@@ -755,51 +772,6 @@ static void iwl_mvm_mac_ctxt_cmd_fill_sta(struct iwl_mvm *mvm,
 
        ctxt_sta->listen_interval = cpu_to_le32(mvm->hw->conf.listen_interval);
        ctxt_sta->assoc_id = cpu_to_le32(vif->bss_conf.aid);
-}
-
-static int iwl_mvm_mac_ctxt_cmd_station(struct iwl_mvm *mvm,
-                                       struct ieee80211_vif *vif,
-                                       u32 action)
-{
-       struct iwl_mac_ctx_cmd cmd = {};
-
-       WARN_ON(vif->type != NL80211_IFTYPE_STATION || vif->p2p);
-
-       /* Fill the common data for all mac context types */
-       iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
-
-       /* Allow beacons to pass through as long as we are not associated,or we
-        * do not have dtim period information */
-       if (!vif->bss_conf.assoc || !vif->bss_conf.dtim_period)
-               cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON);
-       else
-               cmd.filter_flags &= ~cpu_to_le32(MAC_FILTER_IN_BEACON);
-
-       /* Fill the data specific for station mode */
-       iwl_mvm_mac_ctxt_cmd_fill_sta(mvm, vif, &cmd.sta,
-                                     action == FW_CTXT_ACTION_ADD);
-
-       return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
-}
-
-static int iwl_mvm_mac_ctxt_cmd_p2p_client(struct iwl_mvm *mvm,
-                                          struct ieee80211_vif *vif,
-                                          u32 action)
-{
-       struct iwl_mac_ctx_cmd cmd = {};
-       struct ieee80211_p2p_noa_attr *noa = &vif->bss_conf.p2p_noa_attr;
-
-       WARN_ON(vif->type != NL80211_IFTYPE_STATION || !vif->p2p);
-
-       /* Fill the common data for all mac context types */
-       iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
-
-       /* Fill the data specific for station mode */
-       iwl_mvm_mac_ctxt_cmd_fill_sta(mvm, vif, &cmd.p2p_sta.sta,
-                                     action == FW_CTXT_ACTION_ADD);
-
-       cmd.p2p_sta.ctwin = cpu_to_le32(noa->oppps_ctwindow &
-                                       IEEE80211_P2P_OPPPS_CTWINDOW_MASK);
 
        return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
 }
@@ -1137,16 +1109,12 @@ static int iwl_mvm_mac_ctxt_cmd_go(struct iwl_mvm *mvm,
 }
 
 static int iwl_mvm_mac_ctx_send(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
-                               u32 action)
+                               u32 action, bool force_assoc_off)
 {
        switch (vif->type) {
        case NL80211_IFTYPE_STATION:
-               if (!vif->p2p)
-                       return iwl_mvm_mac_ctxt_cmd_station(mvm, vif,
-                                                           action);
-               else
-                       return iwl_mvm_mac_ctxt_cmd_p2p_client(mvm, vif,
-                                                              action);
+               return iwl_mvm_mac_ctxt_cmd_sta(mvm, vif, action,
+                                               force_assoc_off);
                break;
        case NL80211_IFTYPE_AP:
                if (!vif->p2p)
@@ -1176,7 +1144,8 @@ int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
                      vif->addr, ieee80211_vif_type_p2p(vif)))
                return -EIO;
 
-       ret = iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD);
+       ret = iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD,
+                                  true);
        if (ret)
                return ret;
 
@@ -1187,7 +1156,8 @@ int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
        return 0;
 }
 
-int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                            bool force_assoc_off)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 
@@ -1195,7 +1165,8 @@ int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
                      vif->addr, ieee80211_vif_type_p2p(vif)))
                return -EIO;
 
-       return iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY);
+       return iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY,
+                                   force_assoc_off);
 }
 
 int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
@@ -1214,7 +1185,7 @@ int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
                                                           mvmvif->color));
        cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
 
-       ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, CMD_SYNC,
+       ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, 0,
                                   sizeof(cmd), &cmd);
        if (ret) {
                IWL_ERR(mvm, "Failed to remove MAC context: %d\n", ret);
@@ -1240,11 +1211,23 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
        u32 rate __maybe_unused =
                le32_to_cpu(beacon->beacon_notify_hdr.initial_rate);
 
+       lockdep_assert_held(&mvm->mutex);
+
        IWL_DEBUG_RX(mvm, "beacon status %#x retries:%d tsf:0x%16llX rate:%d\n",
                     status & TX_STATUS_MSK,
                     beacon->beacon_notify_hdr.failure_frame,
                     le64_to_cpu(beacon->tsf),
                     rate);
+
+       if (unlikely(mvm->csa_vif && mvm->csa_vif->csa_active)) {
+               if (!ieee80211_csa_is_complete(mvm->csa_vif)) {
+                       iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm->csa_vif);
+               } else {
+                       ieee80211_csa_finish(mvm->csa_vif);
+                       mvm->csa_vif = NULL;
+               }
+       }
+
        return 0;
 }
 
index 8735ef1f44ae8fadd5cae19b0f5b404ffe6ef901..7215f59801863d3b7d72398de8c96c7b73c3902b 100644 (file)
@@ -295,7 +295,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
            !iwlwifi_mod_params.sw_crypto)
                hw->flags |= IEEE80211_HW_MFP_CAPABLE;
 
-       if (0 && mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT) {
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT &&
+           IWL_UCODE_API(mvm->fw->ucode_ver) >= 9 &&
+           !iwlwifi_mod_params.uapsd_disable) {
                hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD;
                hw->uapsd_queues = IWL_UAPSD_AC_INFO;
                hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
@@ -309,11 +311,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                BIT(NL80211_IFTYPE_P2P_CLIENT) |
                BIT(NL80211_IFTYPE_AP) |
                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);
+               BIT(NL80211_IFTYPE_P2P_DEVICE) |
+               BIT(NL80211_IFTYPE_ADHOC);
 
        hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
        hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
@@ -322,6 +321,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD)
                hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
 
+       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_CSA_FLOW)
+               hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
+
        hw->wiphy->iface_combinations = iwl_mvm_iface_combinations;
        hw->wiphy->n_iface_combinations =
                ARRAY_SIZE(iwl_mvm_iface_combinations);
@@ -365,14 +367,11 @@ 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->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;
@@ -386,7 +385,11 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        }
 
 #ifdef CONFIG_PM_SLEEP
-       if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
+       if (iwl_mvm_is_d0i3_supported(mvm) &&
+           device_can_wakeup(mvm->trans->dev)) {
+               mvm->wowlan.flags = WIPHY_WOWLAN_ANY;
+               hw->wiphy->wowlan = &mvm->wowlan;
+       } else if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
            mvm->trans->ops->d3_suspend &&
            mvm->trans->ops->d3_resume &&
            device_can_wakeup(mvm->trans->dev)) {
@@ -540,13 +543,22 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
                return -EACCES;
 
        /* return from D0i3 before starting a new Tx aggregation */
-       if (action == IEEE80211_AMPDU_TX_START) {
+       switch (action) {
+       case IEEE80211_AMPDU_TX_START:
+       case IEEE80211_AMPDU_TX_STOP_CONT:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+       case IEEE80211_AMPDU_TX_OPERATIONAL:
                iwl_mvm_ref(mvm, IWL_MVM_REF_TX_AGG);
                tx_agg_ref = true;
 
                /*
-                * wait synchronously until D0i3 exit to get the correct
-                * sequence number for the tid
+                * for tx start, wait synchronously until D0i3 exit to
+                * get the correct sequence number for the tid.
+                * additionally, some other ampdu actions use direct
+                * target access, which is not handled automatically
+                * by the trans layer (unlike commands), so wait for
+                * d0i3 exit in these cases as well.
                 */
                if (!wait_event_timeout(mvm->d0i3_exit_waitq,
                          !test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status), HZ)) {
@@ -554,6 +566,9 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
                        iwl_mvm_unref(mvm, IWL_MVM_REF_TX_AGG);
                        return -EIO;
                }
+               break;
+       default:
+               break;
        }
 
        mutex_lock(&mvm->mutex);
@@ -758,7 +773,7 @@ static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                .pwr_restriction = cpu_to_le16(tx_power),
        };
 
-       return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, CMD_SYNC,
+       return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0,
                                    sizeof(reduce_txpwr_cmd),
                                    &reduce_txpwr_cmd);
 }
@@ -817,18 +832,17 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
        if (ret)
                goto out_release;
 
-       ret = iwl_mvm_power_update_mac(mvm, vif);
+       ret = iwl_mvm_power_update_mac(mvm);
        if (ret)
                goto out_release;
 
        /* beacon filtering */
-       ret = iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC);
+       ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
        if (ret)
                goto out_remove_mac;
 
-       if (!mvm->bf_allowed_vif && false &&
-           vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
-           mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BF_UPDATED){
+       if (!mvm->bf_allowed_vif &&
+           vif->type == NL80211_IFTYPE_STATION && !vif->p2p) {
                mvm->bf_allowed_vif = mvmvif;
                vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
                                     IEEE80211_VIF_SUPPORTS_CQM_RSSI;
@@ -969,7 +983,7 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
        if (mvm->vif_count && vif->type != NL80211_IFTYPE_P2P_DEVICE)
                mvm->vif_count--;
 
-       iwl_mvm_power_update_mac(mvm, vif);
+       iwl_mvm_power_update_mac(mvm);
        iwl_mvm_mac_ctxt_remove(mvm, vif);
 
 out_release:
@@ -1223,10 +1237,14 @@ static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm,
        if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING))
                return 0;
 
+       /* bcast filtering isn't supported for P2P client */
+       if (vif->p2p)
+               return 0;
+
        if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
                return 0;
 
-       return iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, CMD_SYNC,
+       return iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0,
                                    sizeof(cmd), &cmd);
 }
 #else
@@ -1253,7 +1271,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
        if (changes & BSS_CHANGED_ASSOC && bss_conf->assoc)
                iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif);
 
-       ret = iwl_mvm_mac_ctxt_changed(mvm, vif);
+       ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false);
        if (ret)
                IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
 
@@ -1333,10 +1351,10 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                iwl_mvm_remove_time_event(mvm, mvmvif,
                                          &mvmvif->time_event_data);
                iwl_mvm_sf_update(mvm, vif, false);
-               WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, CMD_SYNC));
+               WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0));
        } else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS |
                              BSS_CHANGED_QOS)) {
-               ret = iwl_mvm_power_update_mac(mvm, vif);
+               ret = iwl_mvm_power_update_mac(mvm);
                if (ret)
                        IWL_ERR(mvm, "failed to update power mode\n");
        }
@@ -1347,16 +1365,19 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
        }
 
        if (changes & BSS_CHANGED_CQM) {
-               IWL_DEBUG_MAC80211(mvm, "cqm info_changed");
+               IWL_DEBUG_MAC80211(mvm, "cqm info_changed\n");
                /* reset cqm events tracking */
                mvmvif->bf_data.last_cqm_event = 0;
-               ret = iwl_mvm_update_beacon_filter(mvm, vif, false, CMD_SYNC);
-               if (ret)
-                       IWL_ERR(mvm, "failed to update CQM thresholds\n");
+               if (mvmvif->bf_data.bf_enabled) {
+                       ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
+                       if (ret)
+                               IWL_ERR(mvm,
+                                       "failed to update CQM thresholds\n");
+               }
        }
 
        if (changes & BSS_CHANGED_ARP_FILTER) {
-               IWL_DEBUG_MAC80211(mvm, "arp filter changed");
+               IWL_DEBUG_MAC80211(mvm, "arp filter changed\n");
                iwl_mvm_configure_bcast_filter(mvm, vif);
        }
 }
@@ -1402,7 +1423,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
        mvmvif->ap_ibss_active = true;
 
        /* power updated needs to be done before quotas */
-       iwl_mvm_power_update_mac(mvm, vif);
+       iwl_mvm_power_update_mac(mvm);
 
        ret = iwl_mvm_update_quotas(mvm, vif);
        if (ret)
@@ -1410,7 +1431,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
 
        /* 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_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false);
 
        iwl_mvm_ref(mvm, IWL_MVM_REF_AP_IBSS);
 
@@ -1420,7 +1441,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
        return 0;
 
 out_quota_failed:
-       iwl_mvm_power_update_mac(mvm, vif);
+       iwl_mvm_power_update_mac(mvm);
        mvmvif->ap_ibss_active = false;
        iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta);
 out_unbind:
@@ -1450,13 +1471,13 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
 
        /* 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_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false);
 
        iwl_mvm_update_quotas(mvm, NULL);
        iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta);
        iwl_mvm_binding_remove_vif(mvm, vif);
 
-       iwl_mvm_power_update_mac(mvm, vif);
+       iwl_mvm_power_update_mac(mvm);
 
        iwl_mvm_mac_ctxt_remove(mvm, vif);
 
@@ -1477,7 +1498,7 @@ iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm,
 
        if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT |
                       BSS_CHANGED_BANDWIDTH) &&
-           iwl_mvm_mac_ctxt_changed(mvm, vif))
+           iwl_mvm_mac_ctxt_changed(mvm, vif, false))
                IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
 
        /* Need to send a new beacon template to the FW */
@@ -1495,6 +1516,9 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
 
        mutex_lock(&mvm->mutex);
 
+       if (changes & BSS_CHANGED_IDLE && !bss_conf->idle)
+               iwl_mvm_sched_scan_stop(mvm, true);
+
        switch (vif->type) {
        case NL80211_IFTYPE_STATION:
                iwl_mvm_bss_info_changed_station(mvm, vif, bss_conf, changes);
@@ -1525,7 +1549,7 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
 
        switch (mvm->scan_status) {
        case IWL_MVM_SCAN_SCHED:
-               ret = iwl_mvm_sched_scan_stop(mvm);
+               ret = iwl_mvm_sched_scan_stop(mvm, true);
                if (ret) {
                        ret = -EBUSY;
                        goto out;
@@ -1697,6 +1721,11 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
                ret = iwl_mvm_add_sta(mvm, vif, sta);
        } else if (old_state == IEEE80211_STA_NONE &&
                   new_state == IEEE80211_STA_AUTH) {
+               /*
+                * EBS may be disabled due to previous failures reported by FW.
+                * Reset EBS status here assuming environment has been changed.
+                */
+               mvm->last_ebs_successful = true;
                ret = 0;
        } else if (old_state == IEEE80211_STA_AUTH &&
                   new_state == IEEE80211_STA_ASSOC) {
@@ -1708,14 +1737,12 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
        } else if (old_state == IEEE80211_STA_ASSOC &&
                   new_state == IEEE80211_STA_AUTHORIZED) {
                /* enable beacon filtering */
-               if (vif->bss_conf.dtim_period)
-                       WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif,
-                                                            CMD_SYNC));
+               WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0));
                ret = 0;
        } else if (old_state == IEEE80211_STA_AUTHORIZED &&
                   new_state == IEEE80211_STA_ASSOC) {
                /* disable beacon filtering */
-               WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC));
+               WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif, 0));
                ret = 0;
        } else if (old_state == IEEE80211_STA_ASSOC &&
                   new_state == IEEE80211_STA_AUTH) {
@@ -1772,7 +1799,7 @@ static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw,
                int ret;
 
                mutex_lock(&mvm->mutex);
-               ret = iwl_mvm_mac_ctxt_changed(mvm, vif);
+               ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false);
                mutex_unlock(&mvm->mutex);
                return ret;
        }
@@ -1865,7 +1892,7 @@ static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw,
        int ret;
 
        mutex_lock(&mvm->mutex);
-       ret = iwl_mvm_sched_scan_stop(mvm);
+       ret = iwl_mvm_sched_scan_stop(mvm, false);
        mutex_unlock(&mvm->mutex);
        iwl_mvm_wait_for_async_handlers(mvm);
 
@@ -2161,10 +2188,10 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
                return;
 
        mutex_lock(&mvm->mutex);
+       iwl_mvm_bt_coex_vif_change(mvm);
        iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def,
                                 ctx->rx_chains_static,
                                 ctx->rx_chains_dynamic);
-       iwl_mvm_bt_coex_vif_change(mvm);
        mutex_unlock(&mvm->mutex);
 }
 
@@ -2184,6 +2211,11 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
 
        switch (vif->type) {
        case NL80211_IFTYPE_AP:
+               /* Unless it's a CSA flow we have nothing to do here */
+               if (vif->csa_active) {
+                       mvmvif->ap_ibss_active = true;
+                       break;
+               }
        case NL80211_IFTYPE_ADHOC:
                /*
                 * The AP binding flow is handled as part of the start_ap flow
@@ -2207,7 +2239,7 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
         * Power state must be updated before quotas,
         * otherwise fw will complain.
         */
-       iwl_mvm_power_update_mac(mvm, vif);
+       iwl_mvm_power_update_mac(mvm);
 
        /* Setting the quota at this stage is only required for monitor
         * interfaces. For the other types, the bss_info changed flow
@@ -2220,11 +2252,17 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
                        goto out_remove_binding;
        }
 
+       /* Handle binding during CSA */
+       if (vif->type == NL80211_IFTYPE_AP) {
+               iwl_mvm_update_quotas(mvm, vif);
+               iwl_mvm_mac_ctxt_changed(mvm, vif, false);
+       }
+
        goto out_unlock;
 
  out_remove_binding:
        iwl_mvm_binding_remove_vif(mvm, vif);
-       iwl_mvm_power_update_mac(mvm, vif);
+       iwl_mvm_power_update_mac(mvm);
  out_unlock:
        mutex_unlock(&mvm->mutex);
        if (ret)
@@ -2244,22 +2282,29 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
        iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data);
 
        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);
                break;
+       case NL80211_IFTYPE_AP:
+               /* This part is triggered only during CSA */
+               if (!vif->csa_active || !mvmvif->ap_ibss_active)
+                       goto out_unlock;
+
+               mvmvif->ap_ibss_active = false;
+               iwl_mvm_update_quotas(mvm, NULL);
+               /*TODO: bt_coex notification here? */
        default:
                break;
        }
 
        iwl_mvm_binding_remove_vif(mvm, vif);
-       iwl_mvm_power_update_mac(mvm, vif);
 
 out_unlock:
        mvmvif->phy_ctxt = NULL;
+       iwl_mvm_power_update_mac(mvm);
        mutex_unlock(&mvm->mutex);
 }
 
@@ -2323,9 +2368,8 @@ static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm,
                        return -EINVAL;
 
                if (nla_get_u32(tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE]))
-                       return iwl_mvm_enable_beacon_filter(mvm, vif,
-                                                           CMD_SYNC);
-               return iwl_mvm_disable_beacon_filter(mvm, vif, CMD_SYNC);
+                       return iwl_mvm_enable_beacon_filter(mvm, vif, 0);
+               return iwl_mvm_disable_beacon_filter(mvm, vif, 0);
        }
 
        return -EOPNOTSUPP;
@@ -2346,6 +2390,53 @@ static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw,
 }
 #endif
 
+static void iwl_mvm_channel_switch_beacon(struct ieee80211_hw *hw,
+                                         struct ieee80211_vif *vif,
+                                         struct cfg80211_chan_def *chandef)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       mutex_lock(&mvm->mutex);
+       if (WARN(mvm->csa_vif && mvm->csa_vif->csa_active,
+                "Another CSA is already in progress"))
+               goto out_unlock;
+
+       IWL_DEBUG_MAC80211(mvm, "CSA started to freq %d\n",
+                          chandef->center_freq1);
+       mvm->csa_vif = vif;
+
+out_unlock:
+       mutex_unlock(&mvm->mutex);
+}
+
+static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif, u32 queues, bool drop)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif;
+       struct iwl_mvm_sta *mvmsta;
+
+       if (!vif || vif->type != NL80211_IFTYPE_STATION)
+               return;
+
+       mutex_lock(&mvm->mutex);
+       mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       mvmsta = iwl_mvm_sta_from_staid_protected(mvm, mvmvif->ap_sta_id);
+
+       if (WARN_ON_ONCE(!mvmsta))
+               goto done;
+
+       if (drop) {
+               if (iwl_mvm_flush_tx_path(mvm, mvmsta->tfd_queue_msk, true))
+                       IWL_ERR(mvm, "flush request fail\n");
+       } else {
+               iwl_trans_wait_tx_queue_empty(mvm->trans,
+                                             mvmsta->tfd_queue_msk);
+       }
+done:
+       mutex_unlock(&mvm->mutex);
+}
+
 const struct ieee80211_ops iwl_mvm_hw_ops = {
        .tx = iwl_mvm_mac_tx,
        .ampdu_action = iwl_mvm_mac_ampdu_action,
@@ -2369,6 +2460,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
        .sta_rc_update = iwl_mvm_sta_rc_update,
        .conf_tx = iwl_mvm_mac_conf_tx,
        .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,
+       .flush = iwl_mvm_mac_flush,
        .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,
@@ -2388,6 +2480,8 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
 
        .set_tim = iwl_mvm_set_tim,
 
+       .channel_switch_beacon = iwl_mvm_channel_switch_beacon,
+
        CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)
 
 #ifdef CONFIG_PM_SLEEP
index f1ec0986c3c912865f0d51e1056ec03c786d8cc2..fcc6c29482d0ef516bba48459b09230b9ead4007 100644 (file)
@@ -164,7 +164,6 @@ enum iwl_dbgfs_pm_mask {
        MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS = BIT(2),
        MVM_DEBUGFS_PM_RX_DATA_TIMEOUT = BIT(3),
        MVM_DEBUGFS_PM_TX_DATA_TIMEOUT = BIT(4),
-       MVM_DEBUGFS_PM_DISABLE_POWER_OFF = BIT(5),
        MVM_DEBUGFS_PM_LPRX_ENA = BIT(6),
        MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD = BIT(7),
        MVM_DEBUGFS_PM_SNOOZE_ENABLE = BIT(8),
@@ -177,7 +176,6 @@ struct iwl_dbgfs_pm {
        u32 tx_data_timeout;
        bool skip_over_dtim;
        u8 skip_dtim_periods;
-       bool disable_power_off;
        bool lprx_ena;
        u32 lprx_rssi_threshold;
        bool snooze_ena;
@@ -232,6 +230,7 @@ enum iwl_mvm_ref_type {
        IWL_MVM_REF_USER,
        IWL_MVM_REF_TX,
        IWL_MVM_REF_TX_AGG,
+       IWL_MVM_REF_EXIT_WORK,
 
        IWL_MVM_REF_COUNT,
 };
@@ -265,6 +264,7 @@ struct iwl_mvm_vif_bf_data {
  * @uploaded: indicates the MAC context has been added to the device
  * @ap_ibss_active: indicates that AP/IBSS is configured and that the interface
  *     should get quota etc.
+ * @pm_enabled - Indicate if MAC power management is allowed
  * @monitor_active: indicates that monitor context is configured, and that the
  *     interface should get quota etc.
  * @low_latency: indicates that this interface is in low-latency mode
@@ -283,6 +283,7 @@ struct iwl_mvm_vif {
 
        bool uploaded;
        bool ap_ibss_active;
+       bool pm_enabled;
        bool monitor_active;
        bool low_latency;
        struct iwl_mvm_vif_bf_data bf_data;
@@ -451,6 +452,11 @@ struct iwl_mvm_frame_stats {
        int last_frame_idx;
 };
 
+enum {
+       D0I3_DEFER_WAKEUP,
+       D0I3_PENDING_WAKEUP,
+};
+
 struct iwl_mvm {
        /* for logger access */
        struct device *dev;
@@ -484,6 +490,7 @@ struct iwl_mvm {
        u32 log_event_table;
        u32 umac_error_event_table;
        bool support_umac_log;
+       struct iwl_sf_region sf_space;
 
        u32 ampdu_ref;
 
@@ -495,6 +502,7 @@ struct iwl_mvm {
        u8 queue_to_mac80211[IWL_MAX_HW_QUEUES];
        atomic_t queue_stop_count[IWL_MAX_HW_QUEUES];
 
+       const char *nvm_file_name;
        struct iwl_nvm_data *nvm_data;
        /* NVM sections */
        struct iwl_nvm_section nvm_sections[NVM_MAX_NUM_SECTIONS];
@@ -535,6 +543,8 @@ struct iwl_mvm {
        /* Internal station */
        struct iwl_mvm_int_sta aux_sta;
 
+       bool last_ebs_successful;
+
        u8 scan_last_antenna_idx; /* to toggle TX between antennas */
        u8 mgmt_last_antenna_idx;
 
@@ -578,8 +588,12 @@ struct iwl_mvm {
        void *fw_error_dump;
        void *fw_error_sram;
        u32 fw_error_sram_len;
+       u32 *fw_error_rxf;
+       u32 fw_error_rxf_len;
 
+#ifdef CONFIG_IWLWIFI_LEDS
        struct led_classdev led;
+#endif
 
        struct ieee80211_vif *p2p_device_vif;
 
@@ -601,6 +615,9 @@ struct iwl_mvm {
        bool d0i3_offloading;
        struct work_struct d0i3_exit_work;
        struct sk_buff_head d0i3_tx;
+       /* protect d0i3_suspend_flags */
+       struct mutex d0i3_suspend_mutex;
+       unsigned long d0i3_suspend_flags;
        /* sync d0i3_tx queue and IWL_MVM_STATUS_IN_D0I3 status flag */
        spinlock_t d0i3_tx_lock;
        wait_queue_head_t d0i3_exit_waitq;
@@ -629,8 +646,8 @@ struct iwl_mvm {
 
        /* Indicate if device power save is allowed */
        bool ps_disabled;
-       /* Indicate if device power management is allowed */
-       bool pm_disabled;
+
+       struct ieee80211_vif *csa_vif;
 };
 
 /* Extract MVM priv from op_mode and _hw */
@@ -705,6 +722,7 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm);
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm);
 void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm);
+void iwl_mvm_fw_error_rxf_dump(struct iwl_mvm *mvm);
 #endif
 u8 first_antenna(u8 mask);
 u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx);
@@ -745,7 +763,7 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
                          struct iwl_device_cmd *cmd);
 
 /* NVM */
-int iwl_nvm_init(struct iwl_mvm *mvm);
+int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic);
 int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm);
 
 int iwl_mvm_up(struct iwl_mvm *mvm);
@@ -796,7 +814,8 @@ void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm,
 int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                            bool force_assoc_off);
 int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm,
                                struct ieee80211_vif *vif);
@@ -840,7 +859,7 @@ 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);
-int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm);
+int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify);
 int iwl_mvm_rx_sched_scan_results(struct iwl_mvm *mvm,
                                  struct iwl_rx_cmd_buffer *rxb,
                                  struct iwl_device_cmd *cmd);
@@ -874,10 +893,8 @@ void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm,
 int rs_pretty_print_rate(char *buf, const u32 rate);
 
 /* power management */
-int iwl_power_legacy_set_cam_mode(struct iwl_mvm *mvm);
-
 int iwl_mvm_power_update_device(struct iwl_mvm *mvm);
-int iwl_mvm_power_update_mac(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_power_update_mac(struct iwl_mvm *mvm);
 int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                                 char *buf, int bufsz);
 
@@ -886,8 +903,18 @@ int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
                                             struct iwl_rx_cmd_buffer *rxb,
                                             struct iwl_device_cmd *cmd);
 
+#ifdef CONFIG_IWLWIFI_LEDS
 int iwl_mvm_leds_init(struct iwl_mvm *mvm);
 void iwl_mvm_leds_exit(struct iwl_mvm *mvm);
+#else
+static inline int iwl_mvm_leds_init(struct iwl_mvm *mvm)
+{
+       return 0;
+}
+static inline void iwl_mvm_leds_exit(struct iwl_mvm *mvm)
+{
+}
+#endif
 
 /* D3 (WoWLAN, NetDetect) */
 int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan);
@@ -922,9 +949,9 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
 void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
 void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
 void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq);
+int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm);
 
 /* BT Coex */
-int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm);
 int iwl_send_bt_init_conf(struct iwl_mvm *mvm);
 int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
                             struct iwl_rx_cmd_buffer *rxb,
@@ -936,9 +963,10 @@ u16 iwl_mvm_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);
+bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
+                                   enum ieee80211_band band);
 u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
                           struct ieee80211_tx_info *info, u8 ac);
-int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, bool enable);
 
 enum iwl_bt_kill_msk {
        BT_KILL_MSK_DEFAULT,
@@ -969,17 +997,11 @@ int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
 int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
                                  struct ieee80211_vif *vif,
                                  u32 flags);
-int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
-                               struct ieee80211_vif *vif, bool enable);
-int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm,
-                                struct ieee80211_vif *vif,
-                                bool force,
-                                u32 flags);
-
 /* SMPS */
 void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                                enum iwl_mvm_smps_type_request req_type,
                                enum ieee80211_smps_mode smps_request);
+bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm);
 
 /* Low latency */
 int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
index cf2d09f53782b0227f9a47d9a8761af8b5ea7ffe..808f78f6fbf9fe478553bd54264f784d98c01597 100644 (file)
 #define NVM_WRITE_OPCODE 1
 #define NVM_READ_OPCODE 0
 
+/* load nvm chunk response */
+enum {
+       READ_NVM_CHUNK_SUCCEED = 0,
+       READ_NVM_CHUNK_NOT_VALID_ADDRESS = 1
+};
+
 /*
  * prepare the NVM host command w/ the pointers to the nvm buffer
  * and send it to fw
@@ -90,7 +96,7 @@ static int iwl_nvm_write_chunk(struct iwl_mvm *mvm, u16 section,
        struct iwl_host_cmd cmd = {
                .id = NVM_ACCESS_CMD,
                .len = { sizeof(struct iwl_nvm_access_cmd), length },
-               .flags = CMD_SYNC | CMD_SEND_IN_RFKILL,
+               .flags = CMD_SEND_IN_RFKILL,
                .data = { &nvm_access_cmd, data },
                /* data may come from vmalloc, so use _DUP */
                .dataflags = { 0, IWL_HCMD_DFL_DUP },
@@ -112,7 +118,7 @@ static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,
        struct iwl_rx_packet *pkt;
        struct iwl_host_cmd cmd = {
                .id = NVM_ACCESS_CMD,
-               .flags = CMD_SYNC | CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
+               .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
                .data = { &nvm_access_cmd, },
        };
        int ret, bytes_read, offset_read;
@@ -139,10 +145,26 @@ static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,
        offset_read = le16_to_cpu(nvm_resp->offset);
        resp_data = nvm_resp->data;
        if (ret) {
-               IWL_ERR(mvm,
-                       "NVM access command failed with status %d (device: %s)\n",
-                       ret, mvm->cfg->name);
-               ret = -EINVAL;
+               if ((offset != 0) &&
+                   (ret == READ_NVM_CHUNK_NOT_VALID_ADDRESS)) {
+                       /*
+                        * meaning of NOT_VALID_ADDRESS:
+                        * driver try to read chunk from address that is
+                        * multiple of 2K and got an error since addr is empty.
+                        * meaning of (offset != 0): driver already
+                        * read valid data from another chunk so this case
+                        * is not an error.
+                        */
+                       IWL_DEBUG_EEPROM(mvm->trans->dev,
+                                        "NVM access command failed on offset 0x%x since that section size is multiple 2K\n",
+                                        offset);
+                       ret = 0;
+               } else {
+                       IWL_DEBUG_EEPROM(mvm->trans->dev,
+                                        "NVM access command failed with status %d (device: %s)\n",
+                                        ret, mvm->cfg->name);
+                       ret = -EIO;
+               }
                goto exit;
        }
 
@@ -211,9 +233,9 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
        while (ret == length) {
                ret = iwl_nvm_read_chunk(mvm, section, offset, length, data);
                if (ret < 0) {
-                       IWL_ERR(mvm,
-                               "Cannot read NVM from section %d offset %d, length %d\n",
-                               section, offset, length);
+                       IWL_DEBUG_EEPROM(mvm->trans->dev,
+                                        "Cannot read NVM from section %d offset %d, length %d\n",
+                                        section, offset, length);
                        return ret;
                }
                offset += ret;
@@ -238,13 +260,20 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
                        return NULL;
                }
        } else {
+               /* SW and REGULATORY sections are mandatory */
                if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
-                   !mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data ||
                    !mvm->nvm_sections[NVM_SECTION_TYPE_REGULATORY].data) {
                        IWL_ERR(mvm,
                                "Can't parse empty family 8000 NVM sections\n");
                        return NULL;
                }
+               /* MAC_OVERRIDE or at least HW section must exist */
+               if (!mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data &&
+                   !mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data) {
+                       IWL_ERR(mvm,
+                               "Can't parse mac_address, empty sections\n");
+                       return NULL;
+               }
        }
 
        if (WARN_ON(!mvm->cfg))
@@ -311,16 +340,16 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
         * get here after that we assume the NVM request can be satisfied
         * synchronously.
         */
-       ret = request_firmware(&fw_entry, iwlwifi_mod_params.nvm_file,
+       ret = request_firmware(&fw_entry, mvm->nvm_file_name,
                               mvm->trans->dev);
        if (ret) {
                IWL_ERR(mvm, "ERROR: %s isn't available %d\n",
-                       iwlwifi_mod_params.nvm_file, ret);
+                       mvm->nvm_file_name, ret);
                return ret;
        }
 
        IWL_INFO(mvm, "Loaded NVM file %s (%zu bytes)\n",
-                iwlwifi_mod_params.nvm_file, fw_entry->size);
+                mvm->nvm_file_name, fw_entry->size);
 
        if (fw_entry->size < sizeof(*file_sec)) {
                IWL_ERR(mvm, "NVM file too small\n");
@@ -427,53 +456,28 @@ int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm)
        return ret;
 }
 
-int iwl_nvm_init(struct iwl_mvm *mvm)
+int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic)
 {
-       int ret, i, section;
+       int ret, section;
        u8 *nvm_buffer, *temp;
-       int nvm_to_read[NVM_MAX_NUM_SECTIONS];
-       int num_of_sections_to_read;
 
        if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS))
                return -EINVAL;
 
-       /* load external NVM if configured */
-       if (iwlwifi_mod_params.nvm_file) {
-               /* move to External NVM flow */
-               ret = iwl_mvm_read_external_nvm(mvm);
-               if (ret)
-                       return ret;
-       } else {
-               /* list of NVM sections we are allowed/need to read */
-               if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
-                       nvm_to_read[0] = mvm->cfg->nvm_hw_section_num;
-                       nvm_to_read[1] = NVM_SECTION_TYPE_SW;
-                       nvm_to_read[2] = NVM_SECTION_TYPE_CALIBRATION;
-                       nvm_to_read[3] = NVM_SECTION_TYPE_PRODUCTION;
-                       num_of_sections_to_read = 4;
-               } else {
-                       nvm_to_read[0] = NVM_SECTION_TYPE_SW;
-                       nvm_to_read[1] = NVM_SECTION_TYPE_CALIBRATION;
-                       nvm_to_read[2] = NVM_SECTION_TYPE_PRODUCTION;
-                       nvm_to_read[3] = NVM_SECTION_TYPE_REGULATORY;
-                       nvm_to_read[4] = NVM_SECTION_TYPE_MAC_OVERRIDE;
-                       num_of_sections_to_read = 5;
-               }
-
+       /* load NVM values from nic */
+       if (read_nvm_from_nic) {
                /* 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 < num_of_sections_to_read; i++) {
-                       section = nvm_to_read[i];
+               for (section = 0; section < NVM_MAX_NUM_SECTIONS; section++) {
                        /* we override the constness for initial read */
                        ret = iwl_nvm_read_section(mvm, section, nvm_buffer);
                        if (ret < 0)
-                               break;
+                               continue;
                        temp = kmemdup(nvm_buffer, ret, GFP_KERNEL);
                        if (!temp) {
                                ret = -ENOMEM;
@@ -502,15 +506,21 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
                                        mvm->nvm_hw_blob.size = ret;
                                        break;
                                }
-                               WARN(1, "section: %d", section);
                        }
 #endif
                }
                kfree(nvm_buffer);
-               if (ret < 0)
+       }
+
+       /* load external NVM if configured */
+       if (mvm->nvm_file_name) {
+               /* move to External NVM flow */
+               ret = iwl_mvm_read_external_nvm(mvm);
+               if (ret)
                        return ret;
        }
 
+       /* parse the relevant nvm sections */
        mvm->nvm_data = iwl_parse_nvm_sections(mvm);
        if (!mvm->nvm_data)
                return -ENODATA;
index 9545d7fdd4bfc69dfb1fb8c4e07de097d58b6ea7..cc2f7de396deb396d2b20e261b4c2877137dfa3e 100644 (file)
@@ -79,8 +79,8 @@
 #include "iwl-prph.h"
 #include "rs.h"
 #include "fw-api-scan.h"
-#include "fw-error-dump.h"
 #include "time-event.h"
+#include "iwl-fw-error-dump.h"
 
 /*
  * module name, copyright, version, etc.
@@ -220,7 +220,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
        RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, false),
 
        RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true),
-       RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, false),
+       RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, true),
        RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, true),
        RX_HANDLER(ANTENNA_COUPLING_NOTIFICATION,
                   iwl_mvm_rx_ant_coupling_notif, true),
@@ -402,6 +402,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        mvm->sf_state = SF_UNINIT;
 
        mutex_init(&mvm->mutex);
+       mutex_init(&mvm->d0i3_suspend_mutex);
        spin_lock_init(&mvm->async_handlers_lock);
        INIT_LIST_HEAD(&mvm->time_event_list);
        INIT_LIST_HEAD(&mvm->async_handlers_list);
@@ -465,13 +466,24 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
 
        min_backoff = calc_min_backoff(trans, cfg);
        iwl_mvm_tt_initialize(mvm, min_backoff);
+       /* set the nvm_file_name according to priority */
+       if (iwlwifi_mod_params.nvm_file)
+               mvm->nvm_file_name = iwlwifi_mod_params.nvm_file;
+       else
+               mvm->nvm_file_name = mvm->cfg->default_nvm_file;
+
+       if (WARN(cfg->no_power_up_nic_in_init && !mvm->nvm_file_name,
+                "not allowing power-up and not having nvm_file\n"))
+               goto out_free;
 
        /*
-        * If the NVM exists in an external file,
-        * there is no need to unnecessarily power up the NIC at driver load
+        * Even if nvm exists in the nvm_file driver should read agin the nvm
+        * from the nic because there might be entries that exist in the OTP
+        * and not in the file.
+        * for nics with no_power_up_nic_in_init: rely completley on nvm_file
         */
-       if (iwlwifi_mod_params.nvm_file) {
-               err = iwl_nvm_init(mvm);
+       if (cfg->no_power_up_nic_in_init && mvm->nvm_file_name) {
+               err = iwl_nvm_init(mvm, false);
                if (err)
                        goto out_free;
        } else {
@@ -518,7 +530,7 @@ 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);
-       if (!iwlwifi_mod_params.nvm_file)
+       if (!cfg->no_power_up_nic_in_init || !mvm->nvm_file_name)
                iwl_trans_op_mode_leave(trans);
        ieee80211_free_hw(mvm->hw);
        return NULL;
@@ -538,6 +550,7 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
        kfree(mvm->scan_cmd);
        vfree(mvm->fw_error_dump);
        kfree(mvm->fw_error_sram);
+       kfree(mvm->fw_error_rxf);
        kfree(mvm->mcast_filter_cmd);
        mvm->mcast_filter_cmd = NULL;
 
@@ -814,6 +827,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
        struct iwl_fw_error_dump_file *dump_file;
        struct iwl_fw_error_dump_data *dump_data;
        u32 file_len;
+       u32 trans_len;
 
        lockdep_assert_held(&mvm->mutex);
 
@@ -821,8 +835,13 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
                return;
 
        file_len = mvm->fw_error_sram_len +
+                  mvm->fw_error_rxf_len +
                   sizeof(*dump_file) +
-                  sizeof(*dump_data);
+                  sizeof(*dump_data) * 2;
+
+       trans_len = iwl_trans_dump_data(mvm->trans, NULL, 0);
+       if (trans_len)
+               file_len += trans_len;
 
        dump_file = vmalloc(file_len);
        if (!dump_file)
@@ -833,7 +852,12 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
        dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER);
        dump_file->file_len = cpu_to_le32(file_len);
        dump_data = (void *)dump_file->data;
-       dump_data->type = IWL_FW_ERROR_DUMP_SRAM;
+       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF);
+       dump_data->len = cpu_to_le32(mvm->fw_error_rxf_len);
+       memcpy(dump_data->data, mvm->fw_error_rxf, mvm->fw_error_rxf_len);
+
+       dump_data = iwl_mvm_fw_error_next_data(dump_data);
+       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM);
        dump_data->len = cpu_to_le32(mvm->fw_error_sram_len);
 
        /*
@@ -842,6 +866,23 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
         * mvm->fw_error_sram right now.
         */
        memcpy(dump_data->data, mvm->fw_error_sram, mvm->fw_error_sram_len);
+
+       kfree(mvm->fw_error_rxf);
+       mvm->fw_error_rxf = NULL;
+       mvm->fw_error_rxf_len = 0;
+
+       kfree(mvm->fw_error_sram);
+       mvm->fw_error_sram = NULL;
+       mvm->fw_error_sram_len = 0;
+
+       if (trans_len) {
+               void *buf = iwl_mvm_fw_error_next_data(dump_data);
+               u32 real_trans_len = iwl_trans_dump_data(mvm->trans, buf,
+                                                        trans_len);
+               dump_data = (void *)((u8 *)buf + real_trans_len);
+               dump_file->file_len =
+                       cpu_to_le32(file_len - trans_len + real_trans_len);
+       }
 }
 #endif
 
@@ -853,6 +894,7 @@ static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode)
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        iwl_mvm_fw_error_sram_dump(mvm);
+       iwl_mvm_fw_error_rxf_dump(mvm);
 #endif
 
        iwl_mvm_nic_restart(mvm);
@@ -1126,9 +1168,9 @@ static void iwl_mvm_d0i3_exit_work(struct work_struct *wk)
        struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, d0i3_exit_work);
        struct iwl_host_cmd get_status_cmd = {
                .id = WOWLAN_GET_STATUSES,
-               .flags = CMD_SYNC | CMD_HIGH_PRIO | CMD_WANT_SKB,
+               .flags = CMD_HIGH_PRIO | CMD_WANT_SKB,
        };
-       struct iwl_wowlan_status_v6 *status;
+       struct iwl_wowlan_status *status;
        int ret;
        u32 disconnection_reasons, wakeup_reasons;
        __le16 *qos_seq = NULL;
@@ -1158,18 +1200,27 @@ static void iwl_mvm_d0i3_exit_work(struct work_struct *wk)
        iwl_free_resp(&get_status_cmd);
 out:
        iwl_mvm_d0i3_enable_tx(mvm, qos_seq);
+       iwl_mvm_unref(mvm, IWL_MVM_REF_EXIT_WORK);
        mutex_unlock(&mvm->mutex);
 }
 
-static int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode)
+int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm)
 {
-       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
        u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE |
                    CMD_WAKE_UP_TRANS;
        int ret;
 
        IWL_DEBUG_RPM(mvm, "MVM exiting D0i3\n");
 
+       mutex_lock(&mvm->d0i3_suspend_mutex);
+       if (test_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags)) {
+               IWL_DEBUG_RPM(mvm, "Deferring d0i3 exit until resume\n");
+               __set_bit(D0I3_PENDING_WAKEUP, &mvm->d0i3_suspend_flags);
+               mutex_unlock(&mvm->d0i3_suspend_mutex);
+               return 0;
+       }
+       mutex_unlock(&mvm->d0i3_suspend_mutex);
+
        ret = iwl_mvm_send_cmd_pdu(mvm, D0I3_END_CMD, flags, 0, NULL);
        if (ret)
                goto out;
@@ -1183,6 +1234,25 @@ static int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode)
        return ret;
 }
 
+static int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode)
+{
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+
+       iwl_mvm_ref(mvm, IWL_MVM_REF_EXIT_WORK);
+       return _iwl_mvm_exit_d0i3(mvm);
+}
+
+static void iwl_mvm_napi_add(struct iwl_op_mode *op_mode,
+                            struct napi_struct *napi,
+                            struct net_device *napi_dev,
+                            int (*poll)(struct napi_struct *, int),
+                            int weight)
+{
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+
+       ieee80211_napi_add(mvm->hw, napi, napi_dev, poll, weight);
+}
+
 static const struct iwl_op_mode_ops iwl_mvm_ops = {
        .start = iwl_op_mode_mvm_start,
        .stop = iwl_op_mode_mvm_stop,
@@ -1196,4 +1266,5 @@ static const struct iwl_op_mode_ops iwl_mvm_ops = {
        .nic_config = iwl_mvm_nic_config,
        .enter_d0i3 = iwl_mvm_enter_d0i3,
        .exit_d0i3 = iwl_mvm_exit_d0i3,
+       .napi_add = iwl_mvm_napi_add,
 };
index 237efe0ac1c44dab52d375ced3cb84912aabe082..539f3a942d437565ab6ba9accd06f71874985af4 100644 (file)
@@ -156,6 +156,18 @@ static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm,
        idle_cnt = chains_static;
        active_cnt = chains_dynamic;
 
+       /* In scenarios where we only ever use a single-stream rates,
+        * i.e. legacy 11b/g/a associations, single-stream APs or even
+        * static SMPS, enable both chains to get diversity, improving
+        * the case where we're far enough from the AP that attenuation
+        * between the two antennas is sufficiently different to impact
+        * performance.
+        */
+       if (active_cnt == 1 && iwl_mvm_rx_diversity_allowed(mvm)) {
+               idle_cnt = 2;
+               active_cnt = 2;
+       }
+
        cmd->rxchain_info = cpu_to_le32(mvm->fw->valid_rx_ant <<
                                        PHY_RX_CHAIN_VALID_POS);
        cmd->rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS);
@@ -187,7 +199,7 @@ static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm,
        iwl_mvm_phy_ctxt_cmd_data(mvm, &cmd, chandef,
                                  chains_static, chains_dynamic);
 
-       ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, CMD_SYNC,
+       ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, 0,
                                   sizeof(struct iwl_phy_context_cmd),
                                   &cmd);
        if (ret)
@@ -202,18 +214,15 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
                         struct cfg80211_chan_def *chandef,
                         u8 chains_static, u8 chains_dynamic)
 {
-       int ret;
-
        WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
                ctxt->ref);
        lockdep_assert_held(&mvm->mutex);
 
        ctxt->channel = chandef->chan;
-       ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
-                                    chains_static, chains_dynamic,
-                                    FW_CTXT_ACTION_ADD, 0);
 
-       return ret;
+       return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
+                                     chains_static, chains_dynamic,
+                                     FW_CTXT_ACTION_ADD, 0);
 }
 
 /*
index 6b636eab33391cbec4957180efe2e74d2ad07388..c182a8baf685857d3c2857443d53ae978e8646a8 100644 (file)
@@ -123,28 +123,6 @@ void iwl_mvm_beacon_filter_set_cqm_params(struct iwl_mvm *mvm,
        cmd->ba_enable_beacon_abort = cpu_to_le32(mvmvif->bf_data.ba_enabled);
 }
 
-int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
-                               struct ieee80211_vif *vif, bool enable)
-{
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       struct iwl_beacon_filter_cmd cmd = {
-               IWL_BF_CMD_CONFIG_DEFAULTS,
-               .bf_enable_beacon_filter = cpu_to_le32(1),
-               .ba_enable_beacon_abort = cpu_to_le32(enable),
-       };
-
-       if (!mvmvif->bf_data.bf_enabled)
-               return 0;
-
-       if (mvm->cur_ucode == IWL_UCODE_WOWLAN)
-               cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3);
-
-       mvmvif->bf_data.ba_enabled = enable;
-       iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, &cmd);
-       iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
-       return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, CMD_SYNC);
-}
-
 static void iwl_mvm_power_log(struct iwl_mvm *mvm,
                              struct iwl_mac_power_cmd *cmd)
 {
@@ -268,6 +246,57 @@ static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm,
                IWL_MVM_PS_HEAVY_RX_THLD_PERCENT;
 }
 
+static void iwl_mvm_binding_iterator(void *_data, u8 *mac,
+                                     struct ieee80211_vif *vif)
+{
+       unsigned long *data = _data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (!mvmvif->phy_ctxt)
+               return;
+
+       if (vif->type == NL80211_IFTYPE_STATION ||
+           vif->type == NL80211_IFTYPE_AP)
+               __set_bit(mvmvif->phy_ctxt->id, data);
+}
+
+static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm,
+                                      struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       unsigned long phy_ctxt_counter = 0;
+
+       ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+                                                  IEEE80211_IFACE_ITER_NORMAL,
+                                                  iwl_mvm_binding_iterator,
+                                                  &phy_ctxt_counter);
+
+       if (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid,
+                   ETH_ALEN))
+               return false;
+
+       if (vif->p2p &&
+           !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD))
+               return false;
+       /*
+        * Avoid using uAPSD if P2P client is associated to GO that uses
+        * opportunistic power save. This is due to current FW limitation.
+        */
+       if (vif->p2p &&
+           (vif->bss_conf.p2p_noa_attr.oppps_ctwindow &
+           IEEE80211_P2P_OPPPS_ENABLE_BIT))
+               return false;
+
+       /*
+        * Avoid using uAPSD if client is in DCM -
+        * low latency issue in Miracast
+        */
+       if (hweight8(phy_ctxt_counter) >= 2)
+               return false;
+
+       return true;
+}
+
 static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
                                    struct ieee80211_vif *vif,
                                    struct iwl_mac_power_cmd *cmd)
@@ -280,7 +309,6 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
        bool radar_detect = false;
        struct iwl_mvm_vif *mvmvif __maybe_unused =
                iwl_mvm_vif_from_mac80211(vif);
-       bool allow_uapsd = true;
 
        cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
                                                            mvmvif->color));
@@ -303,13 +331,8 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
 
        cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
 
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
-           mvmvif->dbgfs_pm.disable_power_off)
-               cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
-#endif
        if (!vif->bss_conf.ps || iwl_mvm_vif_low_latency(mvmvif) ||
-           mvm->pm_disabled)
+           !mvmvif->pm_enabled)
                return;
 
        cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
@@ -351,23 +374,7 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
                        cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
        }
 
-       if (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid,
-                   ETH_ALEN))
-               allow_uapsd = false;
-
-       if (vif->p2p &&
-           !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD))
-               allow_uapsd = false;
-       /*
-        * Avoid using uAPSD if P2P client is associated to GO that uses
-        * opportunistic power save. This is due to current FW limitation.
-        */
-       if (vif->p2p &&
-           vif->bss_conf.p2p_noa_attr.oppps_ctwindow &
-           IEEE80211_P2P_OPPPS_ENABLE_BIT)
-               allow_uapsd = false;
-
-       if (allow_uapsd)
+       if (iwl_mvm_power_allow_uapsd(mvm, vif))
                iwl_mvm_power_configure_uapsd(mvm, vif, cmd);
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -421,20 +428,13 @@ static int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm,
 {
        struct iwl_mac_power_cmd cmd = {};
 
-       if (vif->type != NL80211_IFTYPE_STATION)
-               return 0;
-
-       if (vif->p2p &&
-           !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM))
-               return 0;
-
        iwl_mvm_power_build_cmd(mvm, vif, &cmd);
        iwl_mvm_power_log(mvm, &cmd);
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        memcpy(&iwl_mvm_vif_from_mac80211(vif)->mac_pwr_cmd, &cmd, sizeof(cmd));
 #endif
 
-       return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_SYNC,
+       return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, 0,
                                    sizeof(cmd), &cmd);
 }
 
@@ -444,12 +444,6 @@ int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
                .flags = cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK),
        };
 
-       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT))
-               return 0;
-
-       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)
                mvm->ps_disabled = true;
 
@@ -466,7 +460,7 @@ int iwl_mvm_power_update_device(struct iwl_mvm *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),
+       return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, 0, sizeof(cmd),
                                    &cmd);
 }
 
@@ -508,86 +502,69 @@ int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
        return 0;
 }
 
-struct iwl_power_constraint {
+struct iwl_power_vifs {
        struct ieee80211_vif *bf_vif;
        struct ieee80211_vif *bss_vif;
        struct ieee80211_vif *p2p_vif;
-       u16 bss_phyctx_id;
-       u16 p2p_phyctx_id;
-       bool pm_disabled;
-       bool ps_disabled;
-       struct iwl_mvm *mvm;
+       struct ieee80211_vif *ap_vif;
+       struct ieee80211_vif *monitor_vif;
+       bool p2p_active;
+       bool bss_active;
+       bool ap_active;
+       bool monitor_active;
 };
 
 static void iwl_mvm_power_iterator(void *_data, u8 *mac,
                                   struct ieee80211_vif *vif)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       struct iwl_power_constraint *power_iterator = _data;
-       struct iwl_mvm *mvm = power_iterator->mvm;
+       struct iwl_power_vifs *power_iterator = _data;
 
+       mvmvif->pm_enabled = false;
        switch (ieee80211_vif_type_p2p(vif)) {
        case NL80211_IFTYPE_P2P_DEVICE:
                break;
 
        case NL80211_IFTYPE_P2P_GO:
        case NL80211_IFTYPE_AP:
-               /* no BSS power mgmt if we have an active AP */
-               if (mvmvif->ap_ibss_active)
-                       power_iterator->pm_disabled = true;
+               /* only a single MAC of the same type */
+               WARN_ON(power_iterator->ap_vif);
+               power_iterator->ap_vif = vif;
+               if (mvmvif->phy_ctxt)
+                       if (mvmvif->phy_ctxt->id < MAX_PHYS)
+                               power_iterator->ap_active = true;
                break;
 
        case NL80211_IFTYPE_MONITOR:
-               /* no BSS power mgmt and no device power save */
-               power_iterator->pm_disabled = true;
-               power_iterator->ps_disabled = true;
+               /* only a single MAC of the same type */
+               WARN_ON(power_iterator->monitor_vif);
+               power_iterator->monitor_vif = vif;
+               if (mvmvif->phy_ctxt)
+                       if (mvmvif->phy_ctxt->id < MAX_PHYS)
+                               power_iterator->monitor_active = true;
                break;
 
        case NL80211_IFTYPE_P2P_CLIENT:
-               if (mvmvif->phy_ctxt)
-                       power_iterator->p2p_phyctx_id = mvmvif->phy_ctxt->id;
-
-               /* we should have only one P2P vif */
+               /* only a single MAC of the same type */
                WARN_ON(power_iterator->p2p_vif);
                power_iterator->p2p_vif = vif;
-
-               IWL_DEBUG_POWER(mvm, "p2p: p2p_id=%d, bss_id=%d\n",
-                               power_iterator->p2p_phyctx_id,
-                               power_iterator->bss_phyctx_id);
-               if (!(mvm->fw->ucode_capa.flags &
-                     IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM)) {
-                       /* no BSS power mgmt if we have a P2P client*/
-                       power_iterator->pm_disabled = true;
-               } else if (power_iterator->p2p_phyctx_id < MAX_PHYS &&
-                          power_iterator->bss_phyctx_id < MAX_PHYS &&
-                          power_iterator->p2p_phyctx_id ==
-                          power_iterator->bss_phyctx_id) {
-                       power_iterator->pm_disabled = true;
-               }
+               if (mvmvif->phy_ctxt)
+                       if (mvmvif->phy_ctxt->id < MAX_PHYS)
+                               power_iterator->p2p_active = true;
                break;
 
        case NL80211_IFTYPE_STATION:
-               if (mvmvif->phy_ctxt)
-                       power_iterator->bss_phyctx_id = mvmvif->phy_ctxt->id;
-
-               /* we should have only one BSS vif */
+               /* only a single MAC of the same type */
                WARN_ON(power_iterator->bss_vif);
                power_iterator->bss_vif = vif;
+               if (mvmvif->phy_ctxt)
+                       if (mvmvif->phy_ctxt->id < MAX_PHYS)
+                               power_iterator->bss_active = true;
 
                if (mvmvif->bf_data.bf_enabled &&
                    !WARN_ON(power_iterator->bf_vif))
                        power_iterator->bf_vif = vif;
 
-               IWL_DEBUG_POWER(mvm, "bss: p2p_id=%d, bss_id=%d\n",
-                               power_iterator->p2p_phyctx_id,
-                               power_iterator->bss_phyctx_id);
-               if (mvm->fw->ucode_capa.flags &
-                   IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM &&
-                       (power_iterator->p2p_phyctx_id < MAX_PHYS &&
-                        power_iterator->bss_phyctx_id < MAX_PHYS &&
-                        power_iterator->p2p_phyctx_id ==
-                        power_iterator->bss_phyctx_id))
-                       power_iterator->pm_disabled = true;
                break;
 
        default:
@@ -596,70 +573,73 @@ static void iwl_mvm_power_iterator(void *_data, u8 *mac,
 }
 
 static void
-iwl_mvm_power_get_global_constraint(struct iwl_mvm *mvm,
-                                   struct iwl_power_constraint *constraint)
+iwl_mvm_power_set_pm(struct iwl_mvm *mvm,
+                                   struct iwl_power_vifs *vifs)
 {
-       lockdep_assert_held(&mvm->mutex);
+       struct iwl_mvm_vif *bss_mvmvif = NULL;
+       struct iwl_mvm_vif *p2p_mvmvif = NULL;
+       struct iwl_mvm_vif *ap_mvmvif = NULL;
+       bool client_same_channel = false;
+       bool ap_same_channel = false;
 
-       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) {
-               constraint->pm_disabled = true;
-               constraint->ps_disabled = true;
-       }
+       lockdep_assert_held(&mvm->mutex);
 
+       /* get vifs info + set pm_enable to false */
        ieee80211_iterate_active_interfaces_atomic(mvm->hw,
                                            IEEE80211_IFACE_ITER_NORMAL,
-                                           iwl_mvm_power_iterator, constraint);
-}
-
-int iwl_mvm_power_update_mac(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
-{
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       struct iwl_power_constraint constraint = {
-                   .p2p_phyctx_id = MAX_PHYS,
-                   .bss_phyctx_id = MAX_PHYS,
-                   .mvm = mvm,
-       };
-       bool ba_enable;
-       int ret;
+                                           iwl_mvm_power_iterator, vifs);
 
-       lockdep_assert_held(&mvm->mutex);
+       if (vifs->bss_vif)
+               bss_mvmvif = iwl_mvm_vif_from_mac80211(vifs->bss_vif);
 
-       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT))
-               return 0;
+       if (vifs->p2p_vif)
+               p2p_mvmvif = iwl_mvm_vif_from_mac80211(vifs->p2p_vif);
 
-       iwl_mvm_power_get_global_constraint(mvm, &constraint);
-       mvm->ps_disabled = constraint.ps_disabled;
-       mvm->pm_disabled = constraint.pm_disabled;
+       if (vifs->ap_vif)
+               ap_mvmvif = iwl_mvm_vif_from_mac80211(vifs->ap_vif);
 
-       /* don't update device power state unless we add / remove monitor */
-       if (vif->type == NL80211_IFTYPE_MONITOR) {
-               ret = iwl_mvm_power_update_device(mvm);
-               if (ret)
-                       return ret;
+       /* enable PM on bss if bss stand alone */
+       if (vifs->bss_active && !vifs->p2p_active && !vifs->ap_active) {
+               bss_mvmvif->pm_enabled = true;
+               return;
        }
 
-       if (constraint.bss_vif) {
-               ret = iwl_mvm_power_send_cmd(mvm, constraint.bss_vif);
-               if (ret)
-                       return ret;
+       /* enable PM on p2p if p2p stand alone */
+       if (vifs->p2p_active && !vifs->bss_active && !vifs->ap_active) {
+               if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM)
+                       p2p_mvmvif->pm_enabled = true;
+               return;
        }
 
-       if (constraint.p2p_vif) {
-               ret = iwl_mvm_power_send_cmd(mvm, constraint.p2p_vif);
-               if (ret)
-                       return ret;
+       if (vifs->bss_active && vifs->p2p_active)
+               client_same_channel = (bss_mvmvif->phy_ctxt->id ==
+                                      p2p_mvmvif->phy_ctxt->id);
+       if (vifs->bss_active && vifs->ap_active)
+               ap_same_channel = (bss_mvmvif->phy_ctxt->id ==
+                                  ap_mvmvif->phy_ctxt->id);
+
+       /* clients are not stand alone: enable PM if DCM */
+       if (!(client_same_channel || ap_same_channel) &&
+           (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM)) {
+               if (vifs->bss_active)
+                       bss_mvmvif->pm_enabled = true;
+               if (vifs->p2p_active &&
+                   (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM))
+                       p2p_mvmvif->pm_enabled = true;
+               return;
        }
 
-       if (!constraint.bf_vif)
-               return 0;
-
-       vif = constraint.bf_vif;
-       mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
-       ba_enable = !(constraint.pm_disabled || constraint.ps_disabled ||
-                     !vif->bss_conf.ps || iwl_mvm_vif_low_latency(mvmvif));
-
-       return iwl_mvm_update_beacon_abort(mvm, constraint.bf_vif, ba_enable);
+       /*
+        * There is only one channel in the system and there are only
+        * bss and p2p clients that share it
+        */
+       if (client_same_channel && !vifs->ap_active &&
+           (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM)) {
+               /* share same channel*/
+               bss_mvmvif->pm_enabled = true;
+               if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM)
+                       p2p_mvmvif->pm_enabled = true;
+       }
 }
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -671,19 +651,10 @@ int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
        struct iwl_mac_power_cmd cmd = {};
        int pos = 0;
 
-       if (WARN_ON(!(mvm->fw->ucode_capa.flags &
-                     IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)))
-               return 0;
-
        mutex_lock(&mvm->mutex);
        memcpy(&cmd, &mvmvif->mac_pwr_cmd, sizeof(cmd));
        mutex_unlock(&mvm->mutex);
 
-       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",
@@ -790,7 +761,7 @@ static int _iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        int ret;
 
-       if (mvmvif != mvm->bf_allowed_vif ||
+       if (mvmvif != mvm->bf_allowed_vif || !vif->bss_conf.dtim_period ||
            vif->type != NL80211_IFTYPE_STATION || vif->p2p)
                return 0;
 
@@ -818,6 +789,26 @@ int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
        return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, flags, false);
 }
 
+static int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
+                                      struct ieee80211_vif *vif,
+                                      bool enable)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_beacon_filter_cmd cmd = {
+               IWL_BF_CMD_CONFIG_DEFAULTS,
+               .bf_enable_beacon_filter = cpu_to_le32(1),
+       };
+
+       if (!mvmvif->bf_data.bf_enabled)
+               return 0;
+
+       if (mvm->cur_ucode == IWL_UCODE_WOWLAN)
+               cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3);
+
+       mvmvif->bf_data.ba_enabled = enable;
+       return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, 0, false);
+}
+
 int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
                                  struct ieee80211_vif *vif,
                                  u32 flags)
@@ -826,8 +817,7 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        int ret;
 
-       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BF_UPDATED) ||
-           vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
                return 0;
 
        ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, flags);
@@ -838,6 +828,55 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
        return ret;
 }
 
+int iwl_mvm_power_update_mac(struct iwl_mvm *mvm)
+{
+       struct iwl_mvm_vif *mvmvif;
+       struct iwl_power_vifs vifs = {};
+       bool ba_enable;
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       iwl_mvm_power_set_pm(mvm, &vifs);
+
+       /* disable PS if CAM */
+       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) {
+               mvm->ps_disabled = true;
+       } else {
+       /* don't update device power state unless we add / remove monitor */
+               if (vifs.monitor_vif) {
+                       if (vifs.monitor_active)
+                               mvm->ps_disabled = true;
+                       ret = iwl_mvm_power_update_device(mvm);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       if (vifs.bss_vif) {
+               ret = iwl_mvm_power_send_cmd(mvm, vifs.bss_vif);
+               if (ret)
+                       return ret;
+       }
+
+       if (vifs.p2p_vif) {
+               ret = iwl_mvm_power_send_cmd(mvm, vifs.p2p_vif);
+               if (ret)
+                       return ret;
+       }
+
+       if (!vifs.bf_vif)
+               return 0;
+
+       mvmvif = iwl_mvm_vif_from_mac80211(vifs.bf_vif);
+
+       ba_enable = !(!mvmvif->pm_enabled || mvm->ps_disabled ||
+                     !vifs.bf_vif->bss_conf.ps ||
+                     iwl_mvm_vif_low_latency(mvmvif));
+
+       return iwl_mvm_update_beacon_abort(mvm, vifs.bf_vif, ba_enable);
+}
+
 int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm,
                                   struct ieee80211_vif *vif,
                                   bool enable, u32 flags)
@@ -861,9 +900,10 @@ int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm,
                if (WARN_ON(!dtimper_msec))
                        return 0;
 
-               cmd.flags |=
-                       cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
                cmd.skip_dtim_periods = 300 / dtimper_msec;
+               if (cmd.skip_dtim_periods)
+                       cmd.flags |=
+                               cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
        }
        iwl_mvm_power_log(mvm, &cmd);
 #ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -894,33 +934,3 @@ int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm,
 
        return ret;
 }
-
-int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm,
-                                struct ieee80211_vif *vif,
-                                bool force,
-                                u32 flags)
-{
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
-       if (mvmvif != mvm->bf_allowed_vif)
-               return 0;
-
-       if (!mvmvif->bf_data.bf_enabled) {
-               /* disable beacon filtering explicitly if force is true */
-               if (force)
-                       return iwl_mvm_disable_beacon_filter(mvm, vif, flags);
-               return 0;
-       }
-
-       return iwl_mvm_enable_beacon_filter(mvm, vif, flags);
-}
-
-int iwl_power_legacy_set_cam_mode(struct iwl_mvm *mvm)
-{
-       struct iwl_powertable_cmd cmd = {
-               .keep_alive_seconds = POWER_KEEP_ALIVE_PERIOD_SEC,
-       };
-
-       return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
-                                   sizeof(cmd), &cmd);
-}
index 35e86e06dffda924f9f4ba75d36717ac311d3602..ba68d7b8450508d9c7b123500b654d2195b5613a 100644 (file)
@@ -285,7 +285,7 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
 
        iwl_mvm_adjust_quota_for_noa(mvm, &cmd);
 
-       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, CMD_SYNC,
+       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0,
                                   sizeof(cmd), &cmd);
        if (ret)
                IWL_ERR(mvm, "Failed to send quota: %d\n", ret);
index e1c838899363b373d176a71bc32d02282d56c018..306a6caa486889b4dc27ea53fc350f246a7433f8 100644 (file)
@@ -211,7 +211,7 @@ static const struct rs_tx_column rs_tx_columns[] = {
                .next_columns = {
                        RS_COLUMN_LEGACY_ANT_B,
                        RS_COLUMN_SISO_ANT_A,
-                       RS_COLUMN_SISO_ANT_B,
+                       RS_COLUMN_MIMO2,
                        RS_COLUMN_INVALID,
                        RS_COLUMN_INVALID,
                        RS_COLUMN_INVALID,
@@ -223,8 +223,8 @@ static const struct rs_tx_column rs_tx_columns[] = {
                .ant = ANT_B,
                .next_columns = {
                        RS_COLUMN_LEGACY_ANT_A,
-                       RS_COLUMN_SISO_ANT_A,
                        RS_COLUMN_SISO_ANT_B,
+                       RS_COLUMN_MIMO2,
                        RS_COLUMN_INVALID,
                        RS_COLUMN_INVALID,
                        RS_COLUMN_INVALID,
@@ -238,10 +238,10 @@ static const struct rs_tx_column rs_tx_columns[] = {
                        RS_COLUMN_SISO_ANT_B,
                        RS_COLUMN_MIMO2,
                        RS_COLUMN_SISO_ANT_A_SGI,
-                       RS_COLUMN_SISO_ANT_B_SGI,
                        RS_COLUMN_LEGACY_ANT_A,
                        RS_COLUMN_LEGACY_ANT_B,
                        RS_COLUMN_INVALID,
+                       RS_COLUMN_INVALID,
                },
                .checks = {
                        rs_siso_allow,
@@ -254,10 +254,10 @@ static const struct rs_tx_column rs_tx_columns[] = {
                        RS_COLUMN_SISO_ANT_A,
                        RS_COLUMN_MIMO2,
                        RS_COLUMN_SISO_ANT_B_SGI,
-                       RS_COLUMN_SISO_ANT_A_SGI,
                        RS_COLUMN_LEGACY_ANT_A,
                        RS_COLUMN_LEGACY_ANT_B,
                        RS_COLUMN_INVALID,
+                       RS_COLUMN_INVALID,
                },
                .checks = {
                        rs_siso_allow,
@@ -271,10 +271,10 @@ static const struct rs_tx_column rs_tx_columns[] = {
                        RS_COLUMN_SISO_ANT_B_SGI,
                        RS_COLUMN_MIMO2_SGI,
                        RS_COLUMN_SISO_ANT_A,
-                       RS_COLUMN_SISO_ANT_B,
-                       RS_COLUMN_MIMO2,
                        RS_COLUMN_LEGACY_ANT_A,
                        RS_COLUMN_LEGACY_ANT_B,
+                       RS_COLUMN_INVALID,
+                       RS_COLUMN_INVALID,
                },
                .checks = {
                        rs_siso_allow,
@@ -289,10 +289,10 @@ static const struct rs_tx_column rs_tx_columns[] = {
                        RS_COLUMN_SISO_ANT_A_SGI,
                        RS_COLUMN_MIMO2_SGI,
                        RS_COLUMN_SISO_ANT_B,
-                       RS_COLUMN_SISO_ANT_A,
-                       RS_COLUMN_MIMO2,
                        RS_COLUMN_LEGACY_ANT_A,
                        RS_COLUMN_LEGACY_ANT_B,
+                       RS_COLUMN_INVALID,
+                       RS_COLUMN_INVALID,
                },
                .checks = {
                        rs_siso_allow,
@@ -304,12 +304,12 @@ static const struct rs_tx_column rs_tx_columns[] = {
                .ant = ANT_AB,
                .next_columns = {
                        RS_COLUMN_SISO_ANT_A,
-                       RS_COLUMN_SISO_ANT_B,
-                       RS_COLUMN_SISO_ANT_A_SGI,
-                       RS_COLUMN_SISO_ANT_B_SGI,
                        RS_COLUMN_MIMO2_SGI,
                        RS_COLUMN_LEGACY_ANT_A,
                        RS_COLUMN_LEGACY_ANT_B,
+                       RS_COLUMN_INVALID,
+                       RS_COLUMN_INVALID,
+                       RS_COLUMN_INVALID,
                },
                .checks = {
                        rs_mimo_allow,
@@ -321,12 +321,12 @@ static const struct rs_tx_column rs_tx_columns[] = {
                .sgi = true,
                .next_columns = {
                        RS_COLUMN_SISO_ANT_A_SGI,
-                       RS_COLUMN_SISO_ANT_B_SGI,
-                       RS_COLUMN_SISO_ANT_A,
-                       RS_COLUMN_SISO_ANT_B,
                        RS_COLUMN_MIMO2,
                        RS_COLUMN_LEGACY_ANT_A,
                        RS_COLUMN_LEGACY_ANT_B,
+                       RS_COLUMN_INVALID,
+                       RS_COLUMN_INVALID,
+                       RS_COLUMN_INVALID,
                },
                .checks = {
                        rs_mimo_allow,
@@ -527,6 +527,9 @@ static void rs_rate_scale_clear_tbl_windows(struct iwl_mvm *mvm,
        IWL_DEBUG_RATE(mvm, "Clearing up window stats\n");
        for (i = 0; i < IWL_RATE_COUNT; i++)
                rs_rate_scale_clear_window(&tbl->win[i]);
+
+       for (i = 0; i < ARRAY_SIZE(tbl->tpc_win); i++)
+               rs_rate_scale_clear_window(&tbl->tpc_win[i]);
 }
 
 static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type)
@@ -656,17 +659,34 @@ static int _rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
        return 0;
 }
 
-static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
-                             int scale_index, int attempts, int successes)
+static int rs_collect_tx_data(struct iwl_lq_sta *lq_sta,
+                             struct iwl_scale_tbl_info *tbl,
+                             int scale_index, int attempts, int successes,
+                             u8 reduced_txp)
 {
        struct iwl_rate_scale_data *window = NULL;
+       int ret;
 
        if (scale_index < 0 || scale_index >= IWL_RATE_COUNT)
                return -EINVAL;
 
+       if (tbl->column != RS_COLUMN_INVALID) {
+               lq_sta->tx_stats[tbl->column][scale_index].total += attempts;
+               lq_sta->tx_stats[tbl->column][scale_index].success += successes;
+       }
+
        /* Select window for current tx bit rate */
        window = &(tbl->win[scale_index]);
 
+       ret = _rs_collect_tx_data(tbl, scale_index, attempts, successes,
+                                 window);
+       if (ret)
+               return ret;
+
+       if (WARN_ON_ONCE(reduced_txp > TPC_MAX_REDUCTION))
+               return -EINVAL;
+
+       window = &tbl->tpc_win[reduced_txp];
        return _rs_collect_tx_data(tbl, scale_index, attempts, successes,
                                   window);
 }
@@ -1000,6 +1020,7 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
        u32 ucode_rate;
        struct rs_rate rate;
        struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl;
+       u8 reduced_txp = (uintptr_t)info->status.status_driver_data[0];
 
        /* Treat uninitialized rate scaling data same as non-existing. */
        if (!lq_sta) {
@@ -1141,9 +1162,10 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
        if (info->flags & IEEE80211_TX_STAT_AMPDU) {
                ucode_rate = le32_to_cpu(table->rs_table[0]);
                rs_rate_from_ucode_rate(ucode_rate, info->band, &rate);
-               rs_collect_tx_data(curr_tbl, rate.index,
+               rs_collect_tx_data(lq_sta, curr_tbl, rate.index,
                                   info->status.ampdu_len,
-                                  info->status.ampdu_ack_len);
+                                  info->status.ampdu_ack_len,
+                                  reduced_txp);
 
                /* Update success/fail counts if not searching for new mode */
                if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) {
@@ -1176,8 +1198,9 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
                        else
                                continue;
 
-                       rs_collect_tx_data(tmp_tbl, rate.index, 1,
-                                          i < retries ? 0 : legacy_success);
+                       rs_collect_tx_data(lq_sta, tmp_tbl, rate.index, 1,
+                                          i < retries ? 0 : legacy_success,
+                                          reduced_txp);
                }
 
                /* Update success/fail counts if not searching for new mode */
@@ -1188,6 +1211,7 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
        }
        /* The last TX rate is cached in lq_sta; it's set in if/else above */
        lq_sta->last_rate_n_flags = ucode_rate;
+       IWL_DEBUG_RATE(mvm, "reduced txpower: %d\n", reduced_txp);
 done:
        /* See if there's a better rate or modulation mode to try. */
        if (sta && sta->supp_rates[sband->band])
@@ -1311,105 +1335,50 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta,
        tbl->expected_tpt = rs_get_expected_tpt_table(lq_sta, column, rate->bw);
 }
 
-/*
- * Find starting rate for new "search" high-throughput mode of modulation.
- * Goal is to find lowest expected rate (under perfect conditions) that is
- * above the current measured throughput of "active" mode, to give new mode
- * a fair chance to prove itself without too many challenges.
- *
- * This gets called when transitioning to more aggressive modulation
- * (i.e. legacy to SISO or MIMO, or SISO to MIMO), as well as less aggressive
- * (i.e. MIMO to SISO).  When moving to MIMO, bit rate will typically need
- * to decrease to match "active" throughput.  When moving from MIMO to SISO,
- * bit rate will typically need to increase, but not if performance was bad.
- */
 static s32 rs_get_best_rate(struct iwl_mvm *mvm,
                            struct iwl_lq_sta *lq_sta,
                            struct iwl_scale_tbl_info *tbl,     /* "search" */
-                           u16 rate_mask, s8 index)
+                           unsigned long rate_mask, s8 index)
 {
-       /* "active" values */
        struct iwl_scale_tbl_info *active_tbl =
            &(lq_sta->lq_info[lq_sta->active_tbl]);
-       s32 active_sr = active_tbl->win[index].success_ratio;
-       s32 active_tpt = active_tbl->expected_tpt[index];
-       /* expected "search" throughput */
+       s32 success_ratio = active_tbl->win[index].success_ratio;
+       u16 expected_current_tpt = active_tbl->expected_tpt[index];
        const u16 *tpt_tbl = tbl->expected_tpt;
-
-       s32 new_rate, high, low, start_hi;
        u16 high_low;
-       s8 rate = index;
-
-       new_rate = high = low = start_hi = IWL_RATE_INVALID;
-
-       while (1) {
-               high_low = rs_get_adjacent_rate(mvm, rate, rate_mask,
-                                               tbl->rate.type);
-
-               low = high_low & 0xff;
-               high = (high_low >> 8) & 0xff;
+       u32 target_tpt;
+       int rate_idx;
 
-               /*
-                * Lower the "search" bit rate, to give new "search" mode
-                * approximately the same throughput as "active" if:
-                *
-                * 1) "Active" mode has been working modestly well (but not
-                *    great), and expected "search" throughput (under perfect
-                *    conditions) at candidate rate is above the actual
-                *    measured "active" throughput (but less than expected
-                *    "active" throughput under perfect conditions).
-                * OR
-                * 2) "Active" mode has been working perfectly or very well
-                *    and expected "search" throughput (under perfect
-                *    conditions) at candidate rate is above expected
-                *    "active" throughput (under perfect conditions).
-                */
-               if ((((100 * tpt_tbl[rate]) > lq_sta->last_tpt) &&
-                    ((active_sr > RS_SR_FORCE_DECREASE) &&
-                     (active_sr <= IWL_RATE_HIGH_TH) &&
-                     (tpt_tbl[rate] <= active_tpt))) ||
-                   ((active_sr >= IWL_RATE_SCALE_SWITCH) &&
-                    (tpt_tbl[rate] > active_tpt))) {
-                       /* (2nd or later pass)
-                        * If we've already tried to raise the rate, and are
-                        * now trying to lower it, use the higher rate. */
-                       if (start_hi != IWL_RATE_INVALID) {
-                               new_rate = start_hi;
-                               break;
-                       }
-
-                       new_rate = rate;
+       if (success_ratio > RS_SR_NO_DECREASE) {
+               target_tpt = 100 * expected_current_tpt;
+               IWL_DEBUG_RATE(mvm,
+                              "SR %d high. Find rate exceeding EXPECTED_CURRENT %d\n",
+                              success_ratio, target_tpt);
+       } else {
+               target_tpt = lq_sta->last_tpt;
+               IWL_DEBUG_RATE(mvm,
+                              "SR %d not thag good. Find rate exceeding ACTUAL_TPT %d\n",
+                              success_ratio, target_tpt);
+       }
 
-                       /* Loop again with lower rate */
-                       if (low != IWL_RATE_INVALID)
-                               rate = low;
+       rate_idx = find_first_bit(&rate_mask, BITS_PER_LONG);
 
-                       /* Lower rate not available, use the original */
-                       else
-                               break;
-
-               /* Else try to raise the "search" rate to match "active" */
-               } else {
-                       /* (2nd or later pass)
-                        * If we've already tried to lower the rate, and are
-                        * now trying to raise it, use the lower rate. */
-                       if (new_rate != IWL_RATE_INVALID)
-                               break;
+       while (rate_idx != IWL_RATE_INVALID) {
+               if (target_tpt < (100 * tpt_tbl[rate_idx]))
+                       break;
 
-                       /* Loop again with higher rate */
-                       else if (high != IWL_RATE_INVALID) {
-                               start_hi = high;
-                               rate = high;
+               high_low = rs_get_adjacent_rate(mvm, rate_idx, rate_mask,
+                                               tbl->rate.type);
 
-                       /* Higher rate not available, use the original */
-                       } else {
-                               new_rate = rate;
-                               break;
-                       }
-               }
+               rate_idx = (high_low >> 8) & 0xff;
        }
 
-       return new_rate;
+       IWL_DEBUG_RATE(mvm, "Best rate found %d target_tp %d expected_new %d\n",
+                      rate_idx, target_tpt,
+                      rate_idx != IWL_RATE_INVALID ?
+                      100 * tpt_tbl[rate_idx] : IWL_INVALID_VALUE);
+
+       return rate_idx;
 }
 
 static u32 rs_bw_from_sta_bw(struct ieee80211_sta *sta)
@@ -1584,7 +1553,7 @@ static enum rs_column rs_get_next_column(struct iwl_mvm *mvm,
 
                tpt = lq_sta->last_tpt / 100;
                expected_tpt_tbl = rs_get_expected_tpt_table(lq_sta, next_col,
-                                                            tbl->rate.bw);
+                                                    rs_bw_from_sta_bw(sta));
                if (WARN_ON_ONCE(!expected_tpt_tbl))
                        continue;
 
@@ -1625,7 +1594,7 @@ static int rs_switch_to_column(struct iwl_mvm *mvm,
        const struct rs_tx_column *curr_column = &rs_tx_columns[tbl->column];
        u32 sz = (sizeof(struct iwl_scale_tbl_info) -
                  (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
-       u16 rate_mask = 0;
+       unsigned long rate_mask = 0;
        u32 rate_idx = 0;
 
        memcpy(search_tbl, tbl, sz);
@@ -1667,7 +1636,7 @@ static int rs_switch_to_column(struct iwl_mvm *mvm,
                    !(BIT(rate_idx) & rate_mask)) {
                        IWL_DEBUG_RATE(mvm,
                                       "can not switch with index %d"
-                                      " rate mask %x\n",
+                                      " rate mask %lx\n",
                                       rate_idx, rate_mask);
 
                        goto err;
@@ -1769,6 +1738,203 @@ static enum rs_action rs_get_rate_action(struct iwl_mvm *mvm,
        return action;
 }
 
+static void rs_get_adjacent_txp(struct iwl_mvm *mvm, int index,
+                               int *weaker, int *stronger)
+{
+       *weaker = index + TPC_TX_POWER_STEP;
+       if (*weaker > TPC_MAX_REDUCTION)
+               *weaker = TPC_INVALID;
+
+       *stronger = index - TPC_TX_POWER_STEP;
+       if (*stronger < 0)
+               *stronger = TPC_INVALID;
+}
+
+static bool rs_tpc_allowed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                          struct rs_rate *rate, enum ieee80211_band band)
+{
+       int index = rate->index;
+       bool cam = (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM);
+       bool sta_ps_disabled = (vif->type == NL80211_IFTYPE_STATION &&
+                               !vif->bss_conf.ps);
+
+       IWL_DEBUG_RATE(mvm, "cam: %d sta_ps_disabled %d\n",
+                      cam, sta_ps_disabled);
+       /*
+        * allow tpc only if power management is enabled, or bt coex
+        * activity grade allows it and we are on 2.4Ghz.
+        */
+       if ((cam || sta_ps_disabled) &&
+           !iwl_mvm_bt_coex_is_tpc_allowed(mvm, band))
+               return false;
+
+       IWL_DEBUG_RATE(mvm, "check rate, table type: %d\n", rate->type);
+       if (is_legacy(rate))
+               return index == IWL_RATE_54M_INDEX;
+       if (is_ht(rate))
+               return index == IWL_RATE_MCS_7_INDEX;
+       if (is_vht(rate))
+               return index == IWL_RATE_MCS_7_INDEX ||
+                      index == IWL_RATE_MCS_8_INDEX ||
+                      index == IWL_RATE_MCS_9_INDEX;
+
+       WARN_ON_ONCE(1);
+       return false;
+}
+
+enum tpc_action {
+       TPC_ACTION_STAY,
+       TPC_ACTION_DECREASE,
+       TPC_ACTION_INCREASE,
+       TPC_ACTION_NO_RESTIRCTION,
+};
+
+static enum tpc_action rs_get_tpc_action(struct iwl_mvm *mvm,
+                                        s32 sr, int weak, int strong,
+                                        int current_tpt,
+                                        int weak_tpt, int strong_tpt)
+{
+       /* stay until we have valid tpt */
+       if (current_tpt == IWL_INVALID_VALUE) {
+               IWL_DEBUG_RATE(mvm, "no current tpt. stay.\n");
+               return TPC_ACTION_STAY;
+       }
+
+       /* Too many failures, increase txp */
+       if (sr <= TPC_SR_FORCE_INCREASE || current_tpt == 0) {
+               IWL_DEBUG_RATE(mvm, "increase txp because of weak SR\n");
+               return TPC_ACTION_NO_RESTIRCTION;
+       }
+
+       /* try decreasing first if applicable */
+       if (weak != TPC_INVALID) {
+               if (weak_tpt == IWL_INVALID_VALUE &&
+                   (strong_tpt == IWL_INVALID_VALUE ||
+                    current_tpt >= strong_tpt)) {
+                       IWL_DEBUG_RATE(mvm,
+                                      "no weak txp measurement. decrease txp\n");
+                       return TPC_ACTION_DECREASE;
+               }
+
+               if (weak_tpt > current_tpt) {
+                       IWL_DEBUG_RATE(mvm,
+                                      "lower txp has better tpt. decrease txp\n");
+                       return TPC_ACTION_DECREASE;
+               }
+       }
+
+       /* next, increase if needed */
+       if (sr < TPC_SR_NO_INCREASE && strong != TPC_INVALID) {
+               if (weak_tpt == IWL_INVALID_VALUE &&
+                   strong_tpt != IWL_INVALID_VALUE &&
+                   current_tpt < strong_tpt) {
+                       IWL_DEBUG_RATE(mvm,
+                                      "higher txp has better tpt. increase txp\n");
+                       return TPC_ACTION_INCREASE;
+               }
+
+               if (weak_tpt < current_tpt &&
+                   (strong_tpt == IWL_INVALID_VALUE ||
+                    strong_tpt > current_tpt)) {
+                       IWL_DEBUG_RATE(mvm,
+                                      "lower txp has worse tpt. increase txp\n");
+                       return TPC_ACTION_INCREASE;
+               }
+       }
+
+       IWL_DEBUG_RATE(mvm, "no need to increase or decrease txp - stay\n");
+       return TPC_ACTION_STAY;
+}
+
+static bool rs_tpc_perform(struct iwl_mvm *mvm,
+                          struct ieee80211_sta *sta,
+                          struct iwl_lq_sta *lq_sta,
+                          struct iwl_scale_tbl_info *tbl)
+{
+       struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+       struct ieee80211_vif *vif = mvm_sta->vif;
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       enum ieee80211_band band;
+       struct iwl_rate_scale_data *window;
+       struct rs_rate *rate = &tbl->rate;
+       enum tpc_action action;
+       s32 sr;
+       u8 cur = lq_sta->lq.reduced_tpc;
+       int current_tpt;
+       int weak, strong;
+       int weak_tpt = IWL_INVALID_VALUE, strong_tpt = IWL_INVALID_VALUE;
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+       if (lq_sta->dbg_fixed_txp_reduction <= TPC_MAX_REDUCTION) {
+               IWL_DEBUG_RATE(mvm, "fixed tpc: %d\n",
+                              lq_sta->dbg_fixed_txp_reduction);
+               lq_sta->lq.reduced_tpc = lq_sta->dbg_fixed_txp_reduction;
+               return cur != lq_sta->dbg_fixed_txp_reduction;
+       }
+#endif
+
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(vif->chanctx_conf);
+       if (WARN_ON(!chanctx_conf))
+               band = IEEE80211_NUM_BANDS;
+       else
+               band = chanctx_conf->def.chan->band;
+       rcu_read_unlock();
+
+       if (!rs_tpc_allowed(mvm, vif, rate, band)) {
+               IWL_DEBUG_RATE(mvm,
+                              "tpc is not allowed. remove txp restrictions\n");
+               lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION;
+               return cur != TPC_NO_REDUCTION;
+       }
+
+       rs_get_adjacent_txp(mvm, cur, &weak, &strong);
+
+       /* Collect measured throughputs for current and adjacent rates */
+       window = tbl->tpc_win;
+       sr = window[cur].success_ratio;
+       current_tpt = window[cur].average_tpt;
+       if (weak != TPC_INVALID)
+               weak_tpt = window[weak].average_tpt;
+       if (strong != TPC_INVALID)
+               strong_tpt = window[strong].average_tpt;
+
+       IWL_DEBUG_RATE(mvm,
+                      "(TPC: %d): cur_tpt %d SR %d weak %d strong %d weak_tpt %d strong_tpt %d\n",
+                      cur, current_tpt, sr, weak, strong,
+                      weak_tpt, strong_tpt);
+
+       action = rs_get_tpc_action(mvm, sr, weak, strong,
+                                  current_tpt, weak_tpt, strong_tpt);
+
+       /* override actions if we are on the edge */
+       if (weak == TPC_INVALID && action == TPC_ACTION_DECREASE) {
+               IWL_DEBUG_RATE(mvm, "already in lowest txp, stay\n");
+               action = TPC_ACTION_STAY;
+       } else if (strong == TPC_INVALID &&
+                  (action == TPC_ACTION_INCREASE ||
+                   action == TPC_ACTION_NO_RESTIRCTION)) {
+               IWL_DEBUG_RATE(mvm, "already in highest txp, stay\n");
+               action = TPC_ACTION_STAY;
+       }
+
+       switch (action) {
+       case TPC_ACTION_DECREASE:
+               lq_sta->lq.reduced_tpc = weak;
+               return true;
+       case TPC_ACTION_INCREASE:
+               lq_sta->lq.reduced_tpc = strong;
+               return true;
+       case TPC_ACTION_NO_RESTIRCTION:
+               lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION;
+               return true;
+       case TPC_ACTION_STAY:
+               /* do nothing */
+               break;
+       }
+       return false;
+}
+
 /*
  * Do rate scaling and search for new modulation mode.
  */
@@ -2019,6 +2185,9 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
                break;
        case RS_ACTION_STAY:
                /* No change */
+               if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN)
+                       update_lq = rs_tpc_perform(mvm, sta, lq_sta, tbl);
+               break;
        default:
                break;
        }
@@ -2271,10 +2440,6 @@ static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta,
                        if (i == IWL_RATE_9M_INDEX)
                                continue;
 
-                       /* Disable MCS9 as a workaround */
-                       if (i == IWL_RATE_MCS_9_INDEX)
-                               continue;
-
                        /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */
                        if (i == IWL_RATE_MCS_9_INDEX &&
                            sta->bandwidth == IEEE80211_STA_RX_BW_20)
@@ -2293,10 +2458,6 @@ static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta,
                        if (i == IWL_RATE_9M_INDEX)
                                continue;
 
-                       /* Disable MCS9 as a workaround */
-                       if (i == IWL_RATE_MCS_9_INDEX)
-                               continue;
-
                        /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */
                        if (i == IWL_RATE_MCS_9_INDEX &&
                            sta->bandwidth == IEEE80211_STA_RX_BW_20)
@@ -2478,6 +2639,7 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
        lq_sta->is_agg = 0;
 #ifdef CONFIG_MAC80211_DEBUGFS
        lq_sta->dbg_fixed_rate = 0;
+       lq_sta->dbg_fixed_txp_reduction = TPC_INVALID;
 #endif
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        iwl_mvm_reset_frame_stats(mvm, &mvm->drv_rx_stats);
@@ -2653,6 +2815,7 @@ static void rs_fill_lq_cmd(struct iwl_mvm *mvm,
                rs_build_rates_table_from_fixed(mvm, lq_cmd,
                                                lq_sta->band,
                                                lq_sta->dbg_fixed_rate);
+               lq_cmd->reduced_tpc = 0;
                ant = (lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) >>
                        RATE_MCS_ANT_POS;
        } else
@@ -2783,7 +2946,6 @@ static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file,
        size_t buf_size;
        u32 parsed_rate;
 
-
        mvm = lq_sta->drv;
        memset(buf, 0, sizeof(buf));
        buf_size = min(count, sizeof(buf) -  1);
@@ -2856,6 +3018,7 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
                        lq_sta->lq.agg_disable_start_th,
                        lq_sta->lq.agg_frame_cnt_limit);
 
+       desc += sprintf(buff+desc, "reduced tpc=%d\n", lq_sta->lq.reduced_tpc);
        desc += sprintf(buff+desc,
                        "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n",
                        lq_sta->lq.initial_rate_index[0],
@@ -2928,6 +3091,94 @@ static const struct file_operations rs_sta_dbgfs_stats_table_ops = {
        .llseek = default_llseek,
 };
 
+static ssize_t rs_sta_dbgfs_drv_tx_stats_read(struct file *file,
+                                             char __user *user_buf,
+                                             size_t count, loff_t *ppos)
+{
+       static const char * const column_name[] = {
+               [RS_COLUMN_LEGACY_ANT_A] = "LEGACY_ANT_A",
+               [RS_COLUMN_LEGACY_ANT_B] = "LEGACY_ANT_B",
+               [RS_COLUMN_SISO_ANT_A] = "SISO_ANT_A",
+               [RS_COLUMN_SISO_ANT_B] = "SISO_ANT_B",
+               [RS_COLUMN_SISO_ANT_A_SGI] = "SISO_ANT_A_SGI",
+               [RS_COLUMN_SISO_ANT_B_SGI] = "SISO_ANT_B_SGI",
+               [RS_COLUMN_MIMO2] = "MIMO2",
+               [RS_COLUMN_MIMO2_SGI] = "MIMO2_SGI",
+       };
+
+       static const char * const rate_name[] = {
+               [IWL_RATE_1M_INDEX] = "1M",
+               [IWL_RATE_2M_INDEX] = "2M",
+               [IWL_RATE_5M_INDEX] = "5.5M",
+               [IWL_RATE_11M_INDEX] = "11M",
+               [IWL_RATE_6M_INDEX] = "6M|MCS0",
+               [IWL_RATE_9M_INDEX] = "9M",
+               [IWL_RATE_12M_INDEX] = "12M|MCS1",
+               [IWL_RATE_18M_INDEX] = "18M|MCS2",
+               [IWL_RATE_24M_INDEX] = "24M|MCS3",
+               [IWL_RATE_36M_INDEX] = "36M|MCS4",
+               [IWL_RATE_48M_INDEX] = "48M|MCS5",
+               [IWL_RATE_54M_INDEX] = "54M|MCS6",
+               [IWL_RATE_MCS_7_INDEX] = "MCS7",
+               [IWL_RATE_MCS_8_INDEX] = "MCS8",
+               [IWL_RATE_MCS_9_INDEX] = "MCS9",
+       };
+
+       char *buff, *pos, *endpos;
+       int col, rate;
+       ssize_t ret;
+       struct iwl_lq_sta *lq_sta = file->private_data;
+       struct rs_rate_stats *stats;
+       static const size_t bufsz = 1024;
+
+       buff = kmalloc(bufsz, GFP_KERNEL);
+       if (!buff)
+               return -ENOMEM;
+
+       pos = buff;
+       endpos = pos + bufsz;
+
+       pos += scnprintf(pos, endpos - pos, "COLUMN,");
+       for (rate = 0; rate < IWL_RATE_COUNT; rate++)
+               pos += scnprintf(pos, endpos - pos, "%s,", rate_name[rate]);
+       pos += scnprintf(pos, endpos - pos, "\n");
+
+       for (col = 0; col < RS_COLUMN_COUNT; col++) {
+               pos += scnprintf(pos, endpos - pos,
+                                "%s,", column_name[col]);
+
+               for (rate = 0; rate < IWL_RATE_COUNT; rate++) {
+                       stats = &(lq_sta->tx_stats[col][rate]);
+                       pos += scnprintf(pos, endpos - pos,
+                                        "%llu/%llu,",
+                                        stats->success,
+                                        stats->total);
+               }
+               pos += scnprintf(pos, endpos - pos, "\n");
+       }
+
+       ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff);
+       kfree(buff);
+       return ret;
+}
+
+static ssize_t rs_sta_dbgfs_drv_tx_stats_write(struct file *file,
+                                              const char __user *user_buf,
+                                              size_t count, loff_t *ppos)
+{
+       struct iwl_lq_sta *lq_sta = file->private_data;
+       memset(lq_sta->tx_stats, 0, sizeof(lq_sta->tx_stats));
+
+       return count;
+}
+
+static const struct file_operations rs_sta_dbgfs_drv_tx_stats_ops = {
+       .read = rs_sta_dbgfs_drv_tx_stats_read,
+       .write = rs_sta_dbgfs_drv_tx_stats_write,
+       .open = simple_open,
+       .llseek = default_llseek,
+};
+
 static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir)
 {
        struct iwl_lq_sta *lq_sta = mvm_sta;
@@ -2937,9 +3188,15 @@ static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir)
        lq_sta->rs_sta_dbgfs_stats_table_file =
                debugfs_create_file("rate_stats_table", S_IRUSR, dir,
                                    lq_sta, &rs_sta_dbgfs_stats_table_ops);
+       lq_sta->rs_sta_dbgfs_drv_tx_stats_file =
+               debugfs_create_file("drv_tx_stats", S_IRUSR | S_IWUSR, dir,
+                                   lq_sta, &rs_sta_dbgfs_drv_tx_stats_ops);
        lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file =
                debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir,
                                  &lq_sta->tx_agg_tid_en);
+       lq_sta->rs_sta_dbgfs_reduced_txp_file =
+               debugfs_create_u8("reduced_tpc", S_IRUSR | S_IWUSR, dir,
+                                 &lq_sta->dbg_fixed_txp_reduction);
 }
 
 static void rs_remove_debugfs(void *mvm, void *mvm_sta)
@@ -2947,7 +3204,9 @@ static void rs_remove_debugfs(void *mvm, void *mvm_sta)
        struct iwl_lq_sta *lq_sta = mvm_sta;
        debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file);
        debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file);
+       debugfs_remove(lq_sta->rs_sta_dbgfs_drv_tx_stats_file);
        debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file);
+       debugfs_remove(lq_sta->rs_sta_dbgfs_reduced_txp_file);
 }
 #endif
 
index 0acfac96a56c6dca2d2799812231404921abd15b..374a83d7db25a98dd76da34d3fdff9557e48664f 100644 (file)
@@ -158,6 +158,13 @@ enum {
 #define RS_SR_FORCE_DECREASE           1920    /*  15% */
 #define RS_SR_NO_DECREASE              10880   /*  85% */
 
+#define TPC_SR_FORCE_INCREASE          9600    /* 75% */
+#define TPC_SR_NO_INCREASE             10880   /* 85% */
+#define TPC_TX_POWER_STEP              3
+#define TPC_MAX_REDUCTION              15
+#define TPC_NO_REDUCTION               0
+#define TPC_INVALID                    0xff
+
 #define LINK_QUAL_AGG_TIME_LIMIT_DEF   (4000) /* 4 milliseconds */
 #define LINK_QUAL_AGG_TIME_LIMIT_MAX   (8000)
 #define LINK_QUAL_AGG_TIME_LIMIT_MIN   (100)
@@ -266,9 +273,16 @@ enum rs_column {
        RS_COLUMN_MIMO2_SGI,
 
        RS_COLUMN_LAST = RS_COLUMN_MIMO2_SGI,
+       RS_COLUMN_COUNT = RS_COLUMN_LAST + 1,
        RS_COLUMN_INVALID,
 };
 
+/* Packet stats per rate */
+struct rs_rate_stats {
+       u64 success;
+       u64 total;
+};
+
 /**
  * struct iwl_scale_tbl_info -- tx params and success history for all rates
  *
@@ -280,6 +294,8 @@ struct iwl_scale_tbl_info {
        enum rs_column column;
        const u16 *expected_tpt;        /* throughput metrics; expected_tpt_G, etc. */
        struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */
+       /* per txpower-reduction history */
+       struct iwl_rate_scale_data tpc_win[TPC_MAX_REDUCTION + 1];
 };
 
 enum {
@@ -315,6 +331,8 @@ struct iwl_lq_sta {
        bool is_vht;
        enum ieee80211_band band;
 
+       struct rs_rate_stats tx_stats[RS_COLUMN_COUNT][IWL_RATE_COUNT];
+
        /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */
        unsigned long active_legacy_rate;
        unsigned long active_siso_rate;
@@ -334,8 +352,11 @@ struct iwl_lq_sta {
 #ifdef CONFIG_MAC80211_DEBUGFS
        struct dentry *rs_sta_dbgfs_scale_table_file;
        struct dentry *rs_sta_dbgfs_stats_table_file;
+       struct dentry *rs_sta_dbgfs_drv_tx_stats_file;
        struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file;
+       struct dentry *rs_sta_dbgfs_reduced_txp_file;
        u32 dbg_fixed_rate;
+       u8 dbg_fixed_txp_reduction;
 #endif
        struct iwl_mvm *drv;
 
@@ -345,6 +366,9 @@ struct iwl_lq_sta {
        u32 last_rate_n_flags;
        /* packets destined for this STA are aggregated */
        u8 is_agg;
+
+       /* tx power reduce for this sta */
+       int tpc_reduce;
 };
 
 /* Initialize station's rate scaling information after adding station */
index 6061553a5e444956c7b5d626695a2950fb1f3fd1..cf7276967acdec6439392c82e44e94de52d453c8 100644 (file)
@@ -60,7 +60,6 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 #include "iwl-trans.h"
-
 #include "mvm.h"
 #include "fw-api.h"
 
@@ -130,42 +129,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
 
        memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
 
-       ieee80211_rx_ni(mvm->hw, skb);
-}
-
-static void iwl_mvm_calc_rssi(struct iwl_mvm *mvm,
-                             struct iwl_rx_phy_info *phy_info,
-                             struct ieee80211_rx_status *rx_status)
-{
-       int rssi_a, rssi_b, rssi_a_dbm, rssi_b_dbm, max_rssi_dbm;
-       u32 agc_a, agc_b;
-       u32 val;
-
-       val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_AGC_IDX]);
-       agc_a = (val & IWL_OFDM_AGC_A_MSK) >> IWL_OFDM_AGC_A_POS;
-       agc_b = (val & IWL_OFDM_AGC_B_MSK) >> IWL_OFDM_AGC_B_POS;
-
-       val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_RSSI_AB_IDX]);
-       rssi_a = (val & IWL_OFDM_RSSI_INBAND_A_MSK) >> IWL_OFDM_RSSI_A_POS;
-       rssi_b = (val & IWL_OFDM_RSSI_INBAND_B_MSK) >> IWL_OFDM_RSSI_B_POS;
-
-       /*
-        * dBm = rssi dB - agc dB - constant.
-        * Higher AGC (higher radio gain) means lower signal.
-        */
-       rssi_a_dbm = rssi_a - IWL_RSSI_OFFSET - agc_a;
-       rssi_b_dbm = rssi_b - IWL_RSSI_OFFSET - agc_b;
-       max_rssi_dbm = max_t(int, rssi_a_dbm, rssi_b_dbm);
-
-       IWL_DEBUG_STATS(mvm, "Rssi In A %d B %d Max %d AGCA %d AGCB %d\n",
-                       rssi_a_dbm, rssi_b_dbm, max_rssi_dbm, agc_a, agc_b);
-
-       rx_status->signal = max_rssi_dbm;
-       rx_status->chains = (le16_to_cpu(phy_info->phy_flags) &
-                               RX_RES_PHY_FLAGS_ANTENNA)
-                                       >> RX_RES_PHY_FLAGS_ANTENNA_POS;
-       rx_status->chain_signal[0] = rssi_a_dbm;
-       rx_status->chain_signal[1] = rssi_b_dbm;
+       ieee80211_rx(mvm->hw, skb);
 }
 
 /*
@@ -337,10 +301,7 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
         */
        /*rx_status.flag |= RX_FLAG_MACTIME_MPDU;*/
 
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_RX_ENERGY_API)
-               iwl_mvm_get_signal_strength(mvm, phy_info, &rx_status);
-       else
-               iwl_mvm_calc_rssi(mvm, phy_info, &rx_status);
+       iwl_mvm_get_signal_strength(mvm, phy_info, &rx_status);
 
        IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status.signal,
                              (unsigned long long)rx_status.mactime);
@@ -394,6 +355,8 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
                rx_status.rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;
                rx_status.flag |= RX_FLAG_VHT;
                rx_status.flag |= stbc << RX_FLAG_STBC_SHIFT;
+               if (rate_n_flags & RATE_MCS_BF_MSK)
+                       rx_status.vht_flag |= RX_VHT_FLAG_BF;
        } else {
                rx_status.rate_idx =
                        iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
index c28de54c75d400d551a0be87d4501cc98fb5967a..4b6c7d4bd199ef4dbc20defd15f966e59545d6e3 100644 (file)
@@ -306,7 +306,6 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
                .id = SCAN_REQUEST_CMD,
                .len = { 0, },
                .data = { mvm->scan_cmd, },
-               .flags = CMD_SYNC,
                .dataflags = { IWL_HCMD_DFL_NOCOPY, },
        };
        struct iwl_scan_cmd *cmd = mvm->scan_cmd;
@@ -319,7 +318,10 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
        struct iwl_mvm_scan_params params = {};
 
        lockdep_assert_held(&mvm->mutex);
-       BUG_ON(mvm->scan_cmd == NULL);
+
+       /* we should have failed registration if scan_cmd was NULL */
+       if (WARN_ON(mvm->scan_cmd == NULL))
+               return -ENOMEM;
 
        IWL_DEBUG_SCAN(mvm, "Handling mac80211 scan request\n");
        mvm->scan_status = IWL_MVM_SCAN_OS;
@@ -514,7 +516,7 @@ int iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
                                   ARRAY_SIZE(scan_abort_notif),
                                   iwl_mvm_scan_abort_notif, NULL);
 
-       ret = iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_CMD, CMD_SYNC, 0, NULL);
+       ret = iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_CMD, 0, 0, NULL);
        if (ret) {
                IWL_ERR(mvm, "Couldn't send SCAN_ABORT_CMD: %d\n", ret);
                /* mac80211's state will be cleaned in the nic_restart flow */
@@ -538,9 +540,13 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
        /* scan status must be locked for proper checking */
        lockdep_assert_held(&mvm->mutex);
 
-       IWL_DEBUG_SCAN(mvm, "Scheduled scan completed, status %s\n",
+       IWL_DEBUG_SCAN(mvm,
+                      "Scheduled scan completed, status %s EBS status %s:%d\n",
                       scan_notif->status == IWL_SCAN_OFFLOAD_COMPLETED ?
-                      "completed" : "aborted");
+                      "completed" : "aborted", scan_notif->ebs_status ==
+                      IWL_SCAN_EBS_SUCCESS ? "success" : "failed",
+                      scan_notif->ebs_status);
+
 
        /* only call mac80211 completion if the stop was initiated by FW */
        if (mvm->scan_status == IWL_MVM_SCAN_SCHED) {
@@ -548,6 +554,8 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
                ieee80211_sched_scan_stopped(mvm->hw);
        }
 
+       mvm->last_ebs_successful = !scan_notif->ebs_status;
+
        return 0;
 }
 
@@ -740,7 +748,6 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
        struct iwl_scan_offload_cfg *scan_cfg;
        struct iwl_host_cmd cmd = {
                .id = SCAN_OFFLOAD_CONFIG_CMD,
-               .flags = CMD_SYNC,
        };
        struct iwl_mvm_scan_params params = {};
 
@@ -798,7 +805,6 @@ int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm,
        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,
@@ -884,7 +890,12 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
                scan_req.flags |= cpu_to_le16(IWL_SCAN_OFFLOAD_FLAG_PASS_ALL);
        }
 
-       return iwl_mvm_send_cmd_pdu(mvm, SCAN_OFFLOAD_REQUEST_CMD, CMD_SYNC,
+       if (mvm->last_ebs_successful &&
+           mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT)
+               scan_req.flags |=
+                       cpu_to_le16(IWL_SCAN_OFFLOAD_FLAG_EBS_ACCURATE_MODE);
+
+       return iwl_mvm_send_cmd_pdu(mvm, SCAN_OFFLOAD_REQUEST_CMD, 0,
                                    sizeof(scan_req), &scan_req);
 }
 
@@ -893,7 +904,6 @@ 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;
 
@@ -922,7 +932,7 @@ static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm)
        return ret;
 }
 
-int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm)
+int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify)
 {
        int ret;
        struct iwl_notification_wait wait_scan_done;
@@ -960,5 +970,8 @@ int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm)
         */
        mvm->scan_status = IWL_MVM_SCAN_NONE;
 
+       if (notify)
+               ieee80211_sched_scan_stopped(mvm->hw);
+
        return 0;
 }
index 88809b2d165445fcf9188c8f91bcf755a9e6704f..7edfd15efc9d001f227ea2c35b046c0f47cb55af 100644 (file)
@@ -237,9 +237,6 @@ int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif,
                .sta_vif_ap_sta_id = IWL_MVM_STATION_COUNT,
        };
 
-       if (IWL_UCODE_API(mvm->fw->ucode_ver) < 8)
-               return 0;
-
        /*
         * Ignore the call if we are in HW Restart flow, or if the handled
         * vif is a p2p device.
index f339ef8842508774e2ff7d51c9bdce069e014a1b..1fb01ea2e7047201324faefdc1f181408a77d070 100644 (file)
 #include "sta.h"
 #include "rs.h"
 
-static void iwl_mvm_add_sta_cmd_v7_to_v5(struct iwl_mvm_add_sta_cmd_v7 *cmd_v7,
-                                        struct iwl_mvm_add_sta_cmd_v5 *cmd_v5)
-{
-       memset(cmd_v5, 0, sizeof(*cmd_v5));
-
-       cmd_v5->add_modify = cmd_v7->add_modify;
-       cmd_v5->tid_disable_tx = cmd_v7->tid_disable_tx;
-       cmd_v5->mac_id_n_color = cmd_v7->mac_id_n_color;
-       memcpy(cmd_v5->addr, cmd_v7->addr, ETH_ALEN);
-       cmd_v5->sta_id = cmd_v7->sta_id;
-       cmd_v5->modify_mask = cmd_v7->modify_mask;
-       cmd_v5->station_flags = cmd_v7->station_flags;
-       cmd_v5->station_flags_msk = cmd_v7->station_flags_msk;
-       cmd_v5->add_immediate_ba_tid = cmd_v7->add_immediate_ba_tid;
-       cmd_v5->remove_immediate_ba_tid = cmd_v7->remove_immediate_ba_tid;
-       cmd_v5->add_immediate_ba_ssn = cmd_v7->add_immediate_ba_ssn;
-       cmd_v5->sleep_tx_count = cmd_v7->sleep_tx_count;
-       cmd_v5->sleep_state_flags = cmd_v7->sleep_state_flags;
-       cmd_v5->assoc_id = cmd_v7->assoc_id;
-       cmd_v5->beamform_flags = cmd_v7->beamform_flags;
-       cmd_v5->tfd_queue_msk = cmd_v7->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_v7 *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_v7_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_v7 *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_v7_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,
                                    enum nl80211_iftype iftype)
 {
@@ -207,7 +98,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_v7 add_sta_cmd;
+       struct iwl_mvm_add_sta_cmd add_sta_cmd;
        int ret;
        u32 status;
        u32 agg_size = 0, mpdu_dens = 0;
@@ -295,7 +186,8 @@ 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_add_sta_cmd_status(mvm, &add_sta_cmd, &status);
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(add_sta_cmd),
+                                         &add_sta_cmd, &status);
        if (ret)
                return ret;
 
@@ -380,7 +272,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_v7 cmd = {};
+       struct iwl_mvm_add_sta_cmd cmd = {};
        int ret;
        u32 status;
 
@@ -393,7 +285,8 @@ 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_add_sta_cmd_status(mvm, &cmd, &status);
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+                                         &cmd, &status);
        if (ret)
                return ret;
 
@@ -434,7 +327,7 @@ static int iwl_mvm_rm_sta_common(struct iwl_mvm *mvm, u8 sta_id)
                return -EINVAL;
        }
 
-       ret = iwl_mvm_send_cmd_pdu(mvm, REMOVE_STA, CMD_SYNC,
+       ret = iwl_mvm_send_cmd_pdu(mvm, REMOVE_STA, 0,
                                   sizeof(rm_sta_cmd), &rm_sta_cmd);
        if (ret) {
                IWL_ERR(mvm, "Failed to remove station. Id=%d\n", sta_id);
@@ -498,7 +391,7 @@ void iwl_mvm_sta_drained_wk(struct work_struct *wk)
                                sta_id);
                        continue;
                }
-               rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], NULL);
+               RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL);
                clear_bit(sta_id, mvm->sta_drained);
        }
 
@@ -520,14 +413,6 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
                /* flush its queues here since we are freeing mvm_sta */
                ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, true);
 
-               /*
-                * Put a non-NULL since the fw station isn't removed.
-                * It will be removed after the MAC will be set as
-                * unassoc.
-                */
-               rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id],
-                                  ERR_PTR(-EINVAL));
-
                /* if we are associated - we can't remove the AP STA now */
                if (vif->bss_conf.assoc)
                        return ret;
@@ -557,7 +442,7 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
        } else {
                spin_unlock_bh(&mvm_sta->lock);
                ret = iwl_mvm_rm_sta_common(mvm, mvm_sta->sta_id);
-               rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL);
+               RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL);
        }
 
        return ret;
@@ -571,7 +456,7 @@ int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm,
 
        lockdep_assert_held(&mvm->mutex);
 
-       rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], NULL);
+       RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL);
        return ret;
 }
 
@@ -593,7 +478,7 @@ int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta,
 
 void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta)
 {
-       rcu_assign_pointer(mvm->fw_id_to_mac_id[sta->sta_id], NULL);
+       RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta->sta_id], NULL);
        memset(sta, 0, sizeof(struct iwl_mvm_int_sta));
        sta->sta_id = IWL_MVM_STATION_COUNT;
 }
@@ -603,13 +488,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_v7 cmd;
+       struct iwl_mvm_add_sta_cmd cmd;
        int ret;
        u32 status;
 
        lockdep_assert_held(&mvm->mutex);
 
-       memset(&cmd, 0, sizeof(struct iwl_mvm_add_sta_cmd_v7));
+       memset(&cmd, 0, sizeof(cmd));
        cmd.sta_id = sta->sta_id;
        cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id,
                                                             color));
@@ -619,7 +504,8 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm,
        if (addr)
                memcpy(cmd.addr, addr, ETH_ALEN);
 
-       ret = iwl_mvm_send_add_sta_cmd_status(mvm, &cmd, &status);
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+                                         &cmd, &status);
        if (ret)
                return ret;
 
@@ -753,7 +639,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_v7 cmd = {};
+       struct iwl_mvm_add_sta_cmd cmd = {};
        int ret;
        u32 status;
 
@@ -777,7 +663,8 @@ 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_add_sta_cmd_status(mvm, &cmd, &status);
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+                                         &cmd, &status);
        if (ret)
                return ret;
 
@@ -812,7 +699,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_v7 cmd = {};
+       struct iwl_mvm_add_sta_cmd cmd = {};
        int ret;
        u32 status;
 
@@ -834,7 +721,8 @@ 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_add_sta_cmd_status(mvm, &cmd, &status);
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+                                         &cmd, &status);
        if (ret)
                return ret;
 
@@ -1129,12 +1017,11 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
                                u8 sta_id, u32 tkip_iv32, u16 *tkip_p1k,
                                u32 cmd_flags)
 {
-       __le16 key_flags;
        struct iwl_mvm_add_sta_key_cmd cmd = {};
+       __le16 key_flags;
        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;
@@ -1166,13 +1053,12 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
        cmd.sta_id = sta_id;
 
        status = ADD_STA_SUCCESS;
-       if (cmd_flags == CMD_SYNC)
-               ret = iwl_mvm_send_add_sta_key_cmd_status(mvm, &cmd,
-                                                         mac_id_n_color,
-                                                         &status);
+       if (cmd_flags & CMD_ASYNC)
+               ret =  iwl_mvm_send_cmd_pdu(mvm, ADD_STA_KEY, CMD_ASYNC,
+                                           sizeof(cmd), &cmd);
        else
-               ret = iwl_mvm_send_add_sta_key_cmd(mvm, CMD_ASYNC, &cmd,
-                                                  mac_id_n_color);
+               ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd),
+                                                 &cmd, &status);
 
        switch (status) {
        case ADD_STA_SUCCESS:
@@ -1225,7 +1111,7 @@ static int iwl_mvm_send_sta_igtk(struct iwl_mvm *mvm,
                       remove_key ? "removing" : "installing",
                       igtk_cmd.sta_id);
 
-       return iwl_mvm_send_cmd_pdu(mvm, MGMT_MCAST_KEY, CMD_SYNC,
+       return iwl_mvm_send_cmd_pdu(mvm, MGMT_MCAST_KEY, 0,
                                    sizeof(igtk_cmd), &igtk_cmd);
 }
 
@@ -1312,15 +1198,15 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
                ieee80211_get_key_rx_seq(keyconf, 0, &seq);
                ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);
                ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id,
-                                          seq.tkip.iv32, p1k, CMD_SYNC);
+                                          seq.tkip.iv32, p1k, 0);
                break;
        case WLAN_CIPHER_SUITE_CCMP:
                ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id,
-                                          0, NULL, CMD_SYNC);
+                                          0, NULL, 0);
                break;
        default:
                ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf,
-                                          sta_id, 0, NULL, CMD_SYNC);
+                                          sta_id, 0, NULL, 0);
        }
 
        if (ret)
@@ -1399,9 +1285,8 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
        cmd.sta_id = sta_id;
 
        status = ADD_STA_SUCCESS;
-       ret = iwl_mvm_send_add_sta_key_cmd_status(mvm, &cmd,
-                                                 mvm_sta->mac_id_n_color,
-                                                 &status);
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd),
+                                         &cmd, &status);
 
        switch (status) {
        case ADD_STA_SUCCESS:
@@ -1448,7 +1333,7 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
                                struct ieee80211_sta *sta)
 {
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
-       struct iwl_mvm_add_sta_cmd_v7 cmd = {
+       struct iwl_mvm_add_sta_cmd cmd = {
                .add_modify = STA_MODE_MODIFY,
                .sta_id = mvmsta->sta_id,
                .station_flags_msk = cpu_to_le32(STA_FLG_PS),
@@ -1456,7 +1341,7 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
        };
        int ret;
 
-       ret = iwl_mvm_send_add_sta_cmd(mvm, CMD_ASYNC, &cmd);
+       ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd);
        if (ret)
                IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
 }
@@ -1468,7 +1353,7 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
                                       bool agg)
 {
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
-       struct iwl_mvm_add_sta_cmd_v7 cmd = {
+       struct iwl_mvm_add_sta_cmd cmd = {
                .add_modify = STA_MODE_MODIFY,
                .sta_id = mvmsta->sta_id,
                .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT,
@@ -1538,7 +1423,7 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
                cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_UAPSD);
        }
 
-       ret = iwl_mvm_send_add_sta_cmd(mvm, CMD_ASYNC, &cmd);
+       ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd);
        if (ret)
                IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
 }
index 2ed84c421481c79dfa1e847b1940ac78da8a9d9c..d98e8a2142b8c6b1e3b9e0568956e0bcaa7a357c 100644 (file)
@@ -253,6 +253,8 @@ enum iwl_mvm_agg_state {
  *     This is basically (last acked packet++).
  * @rate_n_flags: Rate at which Tx was attempted. Holds the data between the
  *     Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA).
+ * @reduced_tpc: Reduced tx power. Holds the data between the
+ *     Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA).
  * @state: state of the BA agreement establishment / tear down.
  * @txq_id: Tx queue used by the BA session
  * @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or
@@ -265,6 +267,7 @@ struct iwl_mvm_tid_data {
        u16 next_reclaimed;
        /* The rest is Tx AGG related */
        u32 rate_n_flags;
+       u8 reduced_tpc;
        enum iwl_mvm_agg_state state;
        u16 txq_id;
        u16 ssn;
@@ -284,8 +287,6 @@ static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data)
  * @tid_disable_agg: bitmap: if bit(tid) is set, the fw won't send ampdus for
  *     tid.
  * @max_agg_bufsize: the maximal size of the AGG buffer for this station
- * @bt_reduced_txpower_dbg: debug mode in which %bt_reduced_txpower is forced
- *     by debugfs.
  * @bt_reduced_txpower: is reduced tx power enabled for this station
  * @next_status_eosp: the next reclaimed packet is a PS-Poll response and
  *     we need to signal the EOSP
@@ -306,7 +307,6 @@ struct iwl_mvm_sta {
        u32 mac_id_n_color;
        u16 tid_disable_agg;
        u8 max_agg_bufsize;
-       bool bt_reduced_txpower_dbg;
        bool bt_reduced_txpower;
        bool next_status_eosp;
        spinlock_t lock;
index 61331245ad9324f29ec5a86f12a3239725619673..80100f6cc12a85a79fc71bd83cfe2f6962f8f1c0 100644 (file)
@@ -273,67 +273,10 @@ static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait,
        return true;
 }
 
-/* used to convert from time event API v2 to v1 */
-#define TE_V2_DEP_POLICY_MSK (TE_V2_DEP_OTHER | TE_V2_DEP_TSF |\
-                            TE_V2_EVENT_SOCIOPATHIC)
-static inline u16 te_v2_get_notify(__le16 policy)
-{
-       return le16_to_cpu(policy) & TE_V2_NOTIF_MSK;
-}
-
-static inline u16 te_v2_get_dep_policy(__le16 policy)
-{
-       return (le16_to_cpu(policy) & TE_V2_DEP_POLICY_MSK) >>
-               TE_V2_PLACEMENT_POS;
-}
-
-static inline u16 te_v2_get_absence(__le16 policy)
-{
-       return (le16_to_cpu(policy) & TE_V2_ABSENCE) >> TE_V2_ABSENCE_POS;
-}
-
-static void iwl_mvm_te_v2_to_v1(const struct iwl_time_event_cmd_v2 *cmd_v2,
-                               struct iwl_time_event_cmd_v1 *cmd_v1)
-{
-       cmd_v1->id_and_color = cmd_v2->id_and_color;
-       cmd_v1->action = cmd_v2->action;
-       cmd_v1->id = cmd_v2->id;
-       cmd_v1->apply_time = cmd_v2->apply_time;
-       cmd_v1->max_delay = cmd_v2->max_delay;
-       cmd_v1->depends_on = cmd_v2->depends_on;
-       cmd_v1->interval = cmd_v2->interval;
-       cmd_v1->duration = cmd_v2->duration;
-       if (cmd_v2->repeat == TE_V2_REPEAT_ENDLESS)
-               cmd_v1->repeat = cpu_to_le32(TE_V1_REPEAT_ENDLESS);
-       else
-               cmd_v1->repeat = cpu_to_le32(cmd_v2->repeat);
-       cmd_v1->max_frags = cpu_to_le32(cmd_v2->max_frags);
-       cmd_v1->interval_reciprocal = 0; /* unused */
-
-       cmd_v1->dep_policy = cpu_to_le32(te_v2_get_dep_policy(cmd_v2->policy));
-       cmd_v1->is_present = cpu_to_le32(!te_v2_get_absence(cmd_v2->policy));
-       cmd_v1->notify = cpu_to_le32(te_v2_get_notify(cmd_v2->policy));
-}
-
-static int iwl_mvm_send_time_event_cmd(struct iwl_mvm *mvm,
-                                      const struct iwl_time_event_cmd_v2 *cmd)
-{
-       struct iwl_time_event_cmd_v1 cmd_v1;
-
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_TIME_EVENT_API_V2)
-               return iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC,
-                                           sizeof(*cmd), cmd);
-
-       iwl_mvm_te_v2_to_v1(cmd, &cmd_v1);
-       return iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC,
-                                   sizeof(cmd_v1), &cmd_v1);
-}
-
-
 static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm,
                                       struct ieee80211_vif *vif,
                                       struct iwl_mvm_time_event_data *te_data,
-                                      struct iwl_time_event_cmd_v2 *te_cmd)
+                                      struct iwl_time_event_cmd *te_cmd)
 {
        static const u8 time_event_response[] = { TIME_EVENT_CMD };
        struct iwl_notification_wait wait_time_event;
@@ -369,7 +312,8 @@ static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm,
                                   ARRAY_SIZE(time_event_response),
                                   iwl_mvm_time_event_response, te_data);
 
-       ret = iwl_mvm_send_time_event_cmd(mvm, te_cmd);
+       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0,
+                                           sizeof(*te_cmd), te_cmd);
        if (ret) {
                IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret);
                iwl_remove_notification(&mvm->notif_wait, &wait_time_event);
@@ -397,7 +341,7 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm,
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
-       struct iwl_time_event_cmd_v2 time_cmd = {};
+       struct iwl_time_event_cmd time_cmd = {};
 
        lockdep_assert_held(&mvm->mutex);
 
@@ -453,7 +397,7 @@ void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
                               struct iwl_mvm_vif *mvmvif,
                               struct iwl_mvm_time_event_data *te_data)
 {
-       struct iwl_time_event_cmd_v2 time_cmd = {};
+       struct iwl_time_event_cmd time_cmd = {};
        u32 id, uid;
        int ret;
 
@@ -490,7 +434,8 @@ void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
                cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
 
        IWL_DEBUG_TE(mvm, "Removing TE 0x%x\n", le32_to_cpu(time_cmd.id));
-       ret = iwl_mvm_send_time_event_cmd(mvm, &time_cmd);
+       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0,
+                                  sizeof(time_cmd), &time_cmd);
        if (WARN_ON(ret))
                return;
 }
@@ -510,7 +455,7 @@ int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
-       struct iwl_time_event_cmd_v2 time_cmd = {};
+       struct iwl_time_event_cmd time_cmd = {};
 
        lockdep_assert_held(&mvm->mutex);
        if (te_data->running) {
index 7a99fa361954e0bc1d5e9e82bf94130b0692ac6f..868561512783956617f5cae55d294f7c5207918d 100644 (file)
@@ -409,7 +409,6 @@ void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff)
                .id = REPLY_THERMAL_MNG_BACKOFF,
                .len = { sizeof(u32), },
                .data = { &backoff, },
-               .flags = CMD_SYNC,
        };
 
        backoff = max(backoff, mvm->thermal_throttle.min_backoff);
@@ -468,13 +467,14 @@ void iwl_mvm_tt_handler(struct iwl_mvm *mvm)
        }
 
        if (params->support_tx_backoff) {
-               tx_backoff = 0;
+               tx_backoff = tt->min_backoff;
                for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) {
                        if (temperature < params->tx_backoff[i].temperature)
                                break;
-                       tx_backoff = params->tx_backoff[i].backoff;
+                       tx_backoff = max(tt->min_backoff,
+                                        params->tx_backoff[i].backoff);
                }
-               if (tx_backoff != 0)
+               if (tx_backoff != tt->min_backoff)
                        throttle_enable = true;
                if (tt->tx_backoff != tx_backoff)
                        iwl_mvm_tt_tx_backoff(mvm, tx_backoff);
@@ -484,7 +484,8 @@ void iwl_mvm_tt_handler(struct iwl_mvm *mvm)
                IWL_WARN(mvm,
                         "Due to high temperature thermal throttling initiated\n");
                tt->throttle = true;
-       } else if (tt->throttle && !tt->dynamic_smps && tt->tx_backoff == 0 &&
+       } else if (tt->throttle && !tt->dynamic_smps &&
+                  tt->tx_backoff == tt->min_backoff &&
                   temperature <= params->tx_protection_exit) {
                IWL_WARN(mvm,
                         "Temperature is back to normal thermal throttling stopped\n");
index 879aeac46cc103112fef914bcc2b38df9f028b06..3846a6c41eb165ffbb8ede0ff102547f36911e65 100644 (file)
@@ -636,7 +636,11 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                        seq_ctl = le16_to_cpu(hdr->seq_ctrl);
                }
 
-               ieee80211_tx_status_ni(mvm->hw, skb);
+               BUILD_BUG_ON(ARRAY_SIZE(info->status.status_driver_data) < 1);
+               info->status.status_driver_data[0] =
+                               (void *)(uintptr_t)tx_resp->reduced_tpc;
+
+               ieee80211_tx_status(mvm->hw, skb);
        }
 
        if (txq_id >= mvm->first_agg_queue) {
@@ -815,6 +819,7 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm,
                struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
                mvmsta->tid_data[tid].rate_n_flags =
                        le32_to_cpu(tx_resp->initial_rate);
+               mvmsta->tid_data[tid].reduced_tpc = tx_resp->reduced_tpc;
        }
 
        rcu_read_unlock();
@@ -928,6 +933,8 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
                        info->status.ampdu_len = ba_notif->txed;
                        iwl_mvm_hwrate_to_tx_status(tid_data->rate_n_flags,
                                                    info);
+                       info->status.status_driver_data[0] =
+                               (void *)(uintptr_t)tid_data->reduced_tpc;
                }
        }
 
@@ -937,7 +944,7 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
 
        while (!skb_queue_empty(&reclaimed_skbs)) {
                skb = __skb_dequeue(&reclaimed_skbs);
-               ieee80211_tx_status_ni(mvm->hw, skb);
+               ieee80211_tx_status(mvm->hw, skb);
        }
 
        return 0;
@@ -951,7 +958,7 @@ int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync)
                .flush_ctl = cpu_to_le16(DUMP_TX_FIFO_FLUSH),
        };
 
-       u32 flags = sync ? CMD_SYNC : CMD_ASYNC;
+       u32 flags = sync ? 0 : CMD_ASYNC;
 
        ret = iwl_mvm_send_cmd_pdu(mvm, TXPATH_FLUSH, flags,
                                   sizeof(flush_cmd), &flush_cmd);
index 2180902266ae6636513346df888c9d68abf0a57e..aa9fc77e8413b607861e370169e0b55ba4b697d1 100644 (file)
@@ -64,6 +64,7 @@
 
 #include "iwl-debug.h"
 #include "iwl-io.h"
+#include "iwl-prph.h"
 
 #include "mvm.h"
 #include "fw-api-rs.h"
@@ -143,7 +144,7 @@ int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd,
                      "cmd flags %x", cmd->flags))
                return -EINVAL;
 
-       cmd->flags |= CMD_SYNC | CMD_WANT_SKB;
+       cmd->flags |= CMD_WANT_SKB;
 
        ret = iwl_trans_send_cmd(mvm->trans, cmd);
        if (ret == -ERFKILL) {
@@ -469,6 +470,8 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
                        mvm->status, table.valid);
        }
 
+       /* Do not change this output - scripts rely on it */
+
        IWL_ERR(mvm, "Loaded firmware version: %s\n", mvm->fw->fw_version);
 
        trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low,
@@ -516,13 +519,14 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
                iwl_mvm_dump_umac_error_log(mvm);
 }
 
+#ifdef CONFIG_IWLWIFI_DEBUGFS
 void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm)
 {
        const struct fw_img *img;
        u32 ofs, sram_len;
        void *sram;
 
-       if (!mvm->ucode_loaded || mvm->fw_error_sram)
+       if (!mvm->ucode_loaded || mvm->fw_error_sram || mvm->fw_error_dump)
                return;
 
        img = &mvm->fw->img[mvm->cur_ucode];
@@ -538,6 +542,48 @@ void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm)
        mvm->fw_error_sram_len = sram_len;
 }
 
+void iwl_mvm_fw_error_rxf_dump(struct iwl_mvm *mvm)
+{
+       int i, reg_val;
+       unsigned long flags;
+
+       if (!mvm->ucode_loaded || mvm->fw_error_rxf || mvm->fw_error_dump)
+               return;
+
+       /* reading buffer size */
+       reg_val = iwl_trans_read_prph(mvm->trans, RXF_SIZE_ADDR);
+       mvm->fw_error_rxf_len =
+               (reg_val & RXF_SIZE_BYTE_CNT_MSK) >> RXF_SIZE_BYTE_CND_POS;
+
+       /* the register holds the value divided by 128 */
+       mvm->fw_error_rxf_len = mvm->fw_error_rxf_len << 7;
+
+       if (!mvm->fw_error_rxf_len)
+               return;
+
+       mvm->fw_error_rxf =  kzalloc(mvm->fw_error_rxf_len, GFP_ATOMIC);
+       if (!mvm->fw_error_rxf) {
+               mvm->fw_error_rxf_len = 0;
+               return;
+       }
+
+       if (!iwl_trans_grab_nic_access(mvm->trans, false, &flags)) {
+               kfree(mvm->fw_error_rxf);
+               mvm->fw_error_rxf = NULL;
+               mvm->fw_error_rxf_len = 0;
+               return;
+       }
+
+       for (i = 0; i < (mvm->fw_error_rxf_len / sizeof(u32)); i++) {
+               iwl_trans_write_prph(mvm->trans, RXF_LD_FENCE_OFFSET_ADDR,
+                                    i * sizeof(u32));
+               mvm->fw_error_rxf[i] =
+                       iwl_trans_read_prph(mvm->trans, RXF_FIFO_RD_FENCE_ADDR);
+       }
+       iwl_trans_release_nic_access(mvm->trans, &flags);
+}
+#endif
+
 /**
  * iwl_mvm_send_lq_cmd() - Send link quality command
  * @init: This command is sent as part of station initialization right
@@ -553,7 +599,7 @@ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init)
        struct iwl_host_cmd cmd = {
                .id = LQ_CMD,
                .len = { sizeof(struct iwl_lq_cmd), },
-               .flags = init ? CMD_SYNC : CMD_ASYNC,
+               .flags = init ? 0 : CMD_ASYNC,
                .data = { lq, },
        };
 
@@ -604,6 +650,39 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        ieee80211_request_smps(vif, smps_mode);
 }
 
+static void iwl_mvm_diversity_iter(void *_data, u8 *mac,
+                                  struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       bool *result = _data;
+       int i;
+
+       for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) {
+               if (mvmvif->smps_requests[i] == IEEE80211_SMPS_STATIC ||
+                   mvmvif->smps_requests[i] == IEEE80211_SMPS_DYNAMIC)
+                       *result = false;
+       }
+}
+
+bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm)
+{
+       bool result = true;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (num_of_ant(mvm->fw->valid_rx_ant) == 1)
+               return false;
+
+       if (!mvm->cfg->rx_with_siso_diversity)
+               return false;
+
+       ieee80211_iterate_active_interfaces_atomic(
+                       mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+                       iwl_mvm_diversity_iter, &result);
+
+       return result;
+}
+
 int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                               bool value)
 {
@@ -623,7 +702,7 @@ int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 
        iwl_mvm_bt_coex_vif_change(mvm);
 
-       return iwl_mvm_power_update_mac(mvm, vif);
+       return iwl_mvm_power_update_mac(mvm);
 }
 
 static void iwl_mvm_ll_iter(void *_data, u8 *mac, struct ieee80211_vif *vif)
index 3d1d57f9f5bc539d7350f518f5c13566917b6860..7091a18d5a72f9880f7a47f7a949fefd9de3b9f1 100644 (file)
@@ -417,7 +417,7 @@ static u64 splx_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splx)
            splx->package.count != 2 ||
            splx->package.elements[0].type != ACPI_TYPE_INTEGER ||
            splx->package.elements[0].integer.value != 0) {
-               IWL_ERR(trans, "Unsupported splx structure");
+               IWL_ERR(trans, "Unsupported splx structure\n");
                return 0;
        }
 
@@ -426,14 +426,14 @@ static u64 splx_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splx)
            limits->package.count < 2 ||
            limits->package.elements[0].type != ACPI_TYPE_INTEGER ||
            limits->package.elements[1].type != ACPI_TYPE_INTEGER) {
-               IWL_ERR(trans, "Invalid limits element");
+               IWL_ERR(trans, "Invalid limits element\n");
                return 0;
        }
 
        domain_type = &limits->package.elements[0];
        power_limit = &limits->package.elements[1];
        if (!(domain_type->integer.value & SPL_DOMAINTYPE_WIFI)) {
-               IWL_DEBUG_INFO(trans, "WiFi power is not limited");
+               IWL_DEBUG_INFO(trans, "WiFi power is not limited\n");
                return 0;
        }
 
@@ -450,26 +450,26 @@ static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev)
        pxsx_handle = ACPI_HANDLE(&pdev->dev);
        if (!pxsx_handle) {
                IWL_DEBUG_INFO(trans,
-                              "Could not retrieve root port ACPI handle");
+                              "Could not retrieve root port ACPI handle\n");
                return;
        }
 
        /* Get the method's handle */
        status = acpi_get_handle(pxsx_handle, (acpi_string)SPL_METHOD, &handle);
        if (ACPI_FAILURE(status)) {
-               IWL_DEBUG_INFO(trans, "SPL method not found");
+               IWL_DEBUG_INFO(trans, "SPL method not found\n");
                return;
        }
 
        /* Call SPLC with no arguments */
        status = acpi_evaluate_object(handle, NULL, NULL, &splx);
        if (ACPI_FAILURE(status)) {
-               IWL_ERR(trans, "SPLC invocation failed (0x%x)", status);
+               IWL_ERR(trans, "SPLC invocation failed (0x%x)\n", status);
                return;
        }
 
        trans->dflt_pwr_limit = splx_get_pwr_limit(trans, splx.pointer);
-       IWL_DEBUG_INFO(trans, "Default power limit set to %lld",
+       IWL_DEBUG_INFO(trans, "Default power limit set to %lld\n",
                       trans->dflt_pwr_limit);
        kfree(splx.pointer);
 }
index 9091513ea7388ce11f2294fbb609b3581073e2a0..6c22b23a2845723c33df6757a1ced2c545747e94 100644 (file)
@@ -102,7 +102,7 @@ struct iwl_rxq {
        u32 write_actual;
        struct list_head rx_free;
        struct list_head rx_used;
-       int need_update;
+       bool need_update;
        struct iwl_rb_status *rb_stts;
        dma_addr_t rb_stts_dma;
        spinlock_t lock;
@@ -117,21 +117,19 @@ struct iwl_dma_ptr {
 /**
  * iwl_queue_inc_wrap - increment queue index, wrap back to beginning
  * @index -- current index
- * @n_bd -- total number of entries in queue (must be power of 2)
  */
-static inline int iwl_queue_inc_wrap(int index, int n_bd)
+static inline int iwl_queue_inc_wrap(int index)
 {
-       return ++index & (n_bd - 1);
+       return ++index & (TFD_QUEUE_SIZE_MAX - 1);
 }
 
 /**
  * iwl_queue_dec_wrap - decrement queue index, wrap back to end
  * @index -- current index
- * @n_bd -- total number of entries in queue (must be power of 2)
  */
-static inline int iwl_queue_dec_wrap(int index, int n_bd)
+static inline int iwl_queue_dec_wrap(int index)
 {
-       return --index & (n_bd - 1);
+       return --index & (TFD_QUEUE_SIZE_MAX - 1);
 }
 
 struct iwl_cmd_meta {
@@ -145,13 +143,13 @@ struct iwl_cmd_meta {
  *
  * Contains common data for Rx and Tx queues.
  *
- * Note the difference between n_bd and n_window: the hardware
- * always assumes 256 descriptors, so n_bd is always 256 (unless
+ * Note the difference between TFD_QUEUE_SIZE_MAX and n_window: the hardware
+ * always assumes 256 descriptors, so TFD_QUEUE_SIZE_MAX is always 256 (unless
  * there might be HW changes in the future). For the normal TX
  * queues, n_window, which is the size of the software queue data
  * is also 256; however, for the command queue, n_window is only
  * 32 since we don't need so many commands pending. Since the HW
- * still uses 256 BDs for DMA though, n_bd stays 256. As a result,
+ * still uses 256 BDs for DMA though, TFD_QUEUE_SIZE_MAX stays 256. As a result,
  * the software buffers (in the variables @meta, @txb in struct
  * iwl_txq) only have 32 entries, while the HW buffers (@tfds in
  * the same struct) have 256.
@@ -162,7 +160,6 @@ struct iwl_cmd_meta {
  * data is a window overlayed over the HW queue.
  */
 struct iwl_queue {
-       int n_bd;              /* number of BDs in this queue */
        int write_ptr;       /* 1-st empty entry (index) host_w*/
        int read_ptr;         /* last used entry (index) host_r*/
        /* use for monitoring and recovering the stuck queue */
@@ -231,7 +228,7 @@ struct iwl_txq {
        spinlock_t lock;
        struct timer_list stuck_timer;
        struct iwl_trans_pcie *trans_pcie;
-       u8 need_update;
+       bool need_update;
        u8 active;
        bool ampdu;
 };
@@ -270,6 +267,9 @@ struct iwl_trans_pcie {
        struct iwl_trans *trans;
        struct iwl_drv *drv;
 
+       struct net_device napi_dev;
+       struct napi_struct napi;
+
        /* INT ICT Table */
        __le32 *ict_tbl;
        dma_addr_t ict_tbl_dma;
@@ -362,7 +362,7 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo,
 void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue);
 int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
                      struct iwl_device_cmd *dev_cmd, int txq_id);
-void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq);
+void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans);
 int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd);
 void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
                            struct iwl_rx_cmd_buffer *rxb, int handler_status);
@@ -370,6 +370,13 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
                            struct sk_buff_head *skbs);
 void iwl_trans_pcie_tx_reset(struct iwl_trans *trans);
 
+static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx)
+{
+       struct iwl_tfd_tb *tb = &tfd->tbs[idx];
+
+       return le16_to_cpu(tb->hi_n_len) >> 4;
+}
+
 /*****************************************************
 * Error handling
 ******************************************************/
index fdfa3969cac986c1824bd65c41512a9ac4ba7b39..a2698e5e062c990524d12e1d112818977aeccc2b 100644 (file)
@@ -145,15 +145,13 @@ int iwl_pcie_rx_stop(struct iwl_trans *trans)
 /*
  * iwl_pcie_rxq_inc_wr_ptr - Update the write pointer for the RX queue
  */
-static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans,
-                                   struct iwl_rxq *rxq)
+static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans)
 {
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       struct iwl_rxq *rxq = &trans_pcie->rxq;
        u32 reg;
 
-       spin_lock(&rxq->lock);
-
-       if (rxq->need_update == 0)
-               goto exit_unlock;
+       lockdep_assert_held(&rxq->lock);
 
        /*
         * explicitly wake up the NIC if:
@@ -169,13 +167,27 @@ static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans,
                                       reg);
                        iwl_set_bit(trans, CSR_GP_CNTRL,
                                    CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
-                       goto exit_unlock;
+                       rxq->need_update = true;
+                       return;
                }
        }
 
        rxq->write_actual = round_down(rxq->write, 8);
        iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual);
-       rxq->need_update = 0;
+}
+
+static void iwl_pcie_rxq_check_wrptr(struct iwl_trans *trans)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       struct iwl_rxq *rxq = &trans_pcie->rxq;
+
+       spin_lock(&rxq->lock);
+
+       if (!rxq->need_update)
+               goto exit_unlock;
+
+       iwl_pcie_rxq_inc_wr_ptr(trans);
+       rxq->need_update = false;
 
  exit_unlock:
        spin_unlock(&rxq->lock);
@@ -236,9 +248,8 @@ static void iwl_pcie_rxq_restock(struct iwl_trans *trans)
         * Increment device's write pointer in multiples of 8. */
        if (rxq->write_actual != (rxq->write & ~0x7)) {
                spin_lock(&rxq->lock);
-               rxq->need_update = 1;
+               iwl_pcie_rxq_inc_wr_ptr(trans);
                spin_unlock(&rxq->lock);
-               iwl_pcie_rxq_inc_wr_ptr(trans, rxq);
        }
 }
 
@@ -362,20 +373,9 @@ static void iwl_pcie_rxq_free_rbs(struct iwl_trans *trans)
  * Also restock the Rx queue via iwl_pcie_rxq_restock.
  * This is called as a scheduled work item (except for during initialization)
  */
-static void iwl_pcie_rx_replenish(struct iwl_trans *trans)
-{
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
-       iwl_pcie_rxq_alloc_rbs(trans, GFP_KERNEL);
-
-       spin_lock(&trans_pcie->irq_lock);
-       iwl_pcie_rxq_restock(trans);
-       spin_unlock(&trans_pcie->irq_lock);
-}
-
-static void iwl_pcie_rx_replenish_now(struct iwl_trans *trans)
+static void iwl_pcie_rx_replenish(struct iwl_trans *trans, gfp_t gfp)
 {
-       iwl_pcie_rxq_alloc_rbs(trans, GFP_ATOMIC);
+       iwl_pcie_rxq_alloc_rbs(trans, gfp);
 
        iwl_pcie_rxq_restock(trans);
 }
@@ -385,7 +385,7 @@ static void iwl_pcie_rx_replenish_work(struct work_struct *data)
        struct iwl_trans_pcie *trans_pcie =
            container_of(data, struct iwl_trans_pcie, rx_replenish);
 
-       iwl_pcie_rx_replenish(trans_pcie->trans);
+       iwl_pcie_rx_replenish(trans_pcie->trans, GFP_KERNEL);
 }
 
 static int iwl_pcie_rx_alloc(struct iwl_trans *trans)
@@ -521,14 +521,13 @@ int iwl_pcie_rx_init(struct iwl_trans *trans)
        memset(rxq->rb_stts, 0, sizeof(*rxq->rb_stts));
        spin_unlock(&rxq->lock);
 
-       iwl_pcie_rx_replenish(trans);
+       iwl_pcie_rx_replenish(trans, GFP_KERNEL);
 
        iwl_pcie_rx_hw_init(trans, rxq);
 
-       spin_lock(&trans_pcie->irq_lock);
-       rxq->need_update = 1;
-       iwl_pcie_rxq_inc_wr_ptr(trans, rxq);
-       spin_unlock(&trans_pcie->irq_lock);
+       spin_lock(&rxq->lock);
+       iwl_pcie_rxq_inc_wr_ptr(trans);
+       spin_unlock(&rxq->lock);
 
        return 0;
 }
@@ -673,7 +672,6 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans,
        /* Reuse the page if possible. For notification packets and
         * SKBs that fail to Rx correctly, add them back into the
         * rx_free list for reuse later. */
-       spin_lock(&rxq->lock);
        if (rxb->page != NULL) {
                rxb->page_dma =
                        dma_map_page(trans->dev, rxb->page, 0,
@@ -694,7 +692,6 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans,
                }
        } else
                list_add_tail(&rxb->list, &rxq->rx_used);
-       spin_unlock(&rxq->lock);
 }
 
 /*
@@ -709,6 +706,8 @@ static void iwl_pcie_rx_handle(struct iwl_trans *trans)
        u32 count = 8;
        int total_empty;
 
+restart:
+       spin_lock(&rxq->lock);
        /* uCode's read index (stored in shared DRAM) indicates the last Rx
         * buffer that the driver may process (last buffer filled by ucode). */
        r = le16_to_cpu(ACCESS_ONCE(rxq->rb_stts->closed_rb_num)) & 0x0FFF;
@@ -743,18 +742,25 @@ static void iwl_pcie_rx_handle(struct iwl_trans *trans)
                        count++;
                        if (count >= 8) {
                                rxq->read = i;
-                               iwl_pcie_rx_replenish_now(trans);
+                               spin_unlock(&rxq->lock);
+                               iwl_pcie_rx_replenish(trans, GFP_ATOMIC);
                                count = 0;
+                               goto restart;
                        }
                }
        }
 
        /* Backtrack one entry */
        rxq->read = i;
+       spin_unlock(&rxq->lock);
+
        if (fill_rx)
-               iwl_pcie_rx_replenish_now(trans);
+               iwl_pcie_rx_replenish(trans, GFP_ATOMIC);
        else
                iwl_pcie_rxq_restock(trans);
+
+       if (trans_pcie->napi.poll)
+               napi_gro_flush(&trans_pcie->napi, false);
 }
 
 /*
@@ -844,7 +850,7 @@ static u32 iwl_pcie_int_cause_ict(struct iwl_trans *trans)
                                trans_pcie->ict_index, read);
                trans_pcie->ict_tbl[trans_pcie->ict_index] = 0;
                trans_pcie->ict_index =
-                       iwl_queue_inc_wrap(trans_pcie->ict_index, ICT_COUNT);
+                       ((trans_pcie->ict_index + 1) & (ICT_COUNT - 1));
 
                read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]);
                trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index,
@@ -876,7 +882,6 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
        struct isr_statistics *isr_stats = &trans_pcie->isr_stats;
        u32 inta = 0;
        u32 handled = 0;
-       u32 i;
 
        lock_map_acquire(&trans->sync_cmd_lockdep_map);
 
@@ -1028,9 +1033,8 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
        /* uCode wakes up after power-down sleep */
        if (inta & CSR_INT_BIT_WAKEUP) {
                IWL_DEBUG_ISR(trans, "Wakeup interrupt\n");
-               iwl_pcie_rxq_inc_wr_ptr(trans, &trans_pcie->rxq);
-               for (i = 0; i < trans->cfg->base_params->num_of_queues; i++)
-                       iwl_pcie_txq_inc_wr_ptr(trans, &trans_pcie->txq[i]);
+               iwl_pcie_rxq_check_wrptr(trans);
+               iwl_pcie_txq_check_wrptrs(trans);
 
                isr_stats->wakeup++;
 
@@ -1068,8 +1072,6 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
                iwl_write8(trans, CSR_INT_PERIODIC_REG,
                            CSR_INT_PERIODIC_DIS);
 
-               iwl_pcie_rx_handle(trans);
-
                /*
                 * Enable periodic interrupt in 8 msec only if we received
                 * real RX interrupt (instead of just periodic int), to catch
@@ -1082,6 +1084,10 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
                                   CSR_INT_PERIODIC_ENA);
 
                isr_stats->rx++;
+
+               local_bh_disable();
+               iwl_pcie_rx_handle(trans);
+               local_bh_enable();
        }
 
        /* This "Tx" DMA channel is used only for loading uCode */
index 2365553f1ef79d59c3e8598335e48eb43e5cfdcb..788085bc65d78e3382c7fa76ca74de30abd4cd84 100644 (file)
@@ -73,6 +73,7 @@
 #include "iwl-csr.h"
 #include "iwl-prph.h"
 #include "iwl-agn-hw.h"
+#include "iwl-fw-error-dump.h"
 #include "internal.h"
 
 static u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg)
@@ -103,7 +104,6 @@ static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux)
 
 /* PCI registers */
 #define PCI_CFG_RETRY_TIMEOUT  0x041
-#define CPU1_CPU2_SEPARATOR_SECTION    0xFFFFCCCC
 
 static void iwl_pcie_apm_config(struct iwl_trans *trans)
 {
@@ -454,6 +454,7 @@ static int iwl_pcie_prepare_card_hw(struct iwl_trans *trans)
 {
        int ret;
        int t = 0;
+       int iter;
 
        IWL_DEBUG_INFO(trans, "iwl_trans_prepare_card_hw enter\n");
 
@@ -462,18 +463,23 @@ static int iwl_pcie_prepare_card_hw(struct iwl_trans *trans)
        if (ret >= 0)
                return 0;
 
-       /* If HW is not ready, prepare the conditions to check again */
-       iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
-                   CSR_HW_IF_CONFIG_REG_PREPARE);
+       for (iter = 0; iter < 10; iter++) {
+               /* If HW is not ready, prepare the conditions to check again */
+               iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
+                           CSR_HW_IF_CONFIG_REG_PREPARE);
+
+               do {
+                       ret = iwl_pcie_set_hw_ready(trans);
+                       if (ret >= 0)
+                               return 0;
 
-       do {
-               ret = iwl_pcie_set_hw_ready(trans);
-               if (ret >= 0)
-                       return 0;
+                       usleep_range(200, 1000);
+                       t += 200;
+               } while (t < 150000);
+               msleep(25);
+       }
 
-               usleep_range(200, 1000);
-               t += 200;
-       } while (t < 150000);
+       IWL_DEBUG_INFO(trans, "got NIC after %d iterations\n", iter);
 
        return ret;
 }
@@ -1053,6 +1059,12 @@ static void iwl_trans_pcie_write_prph(struct iwl_trans *trans, u32 addr,
        iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WDAT, val);
 }
 
+static int iwl_pcie_dummy_napi_poll(struct napi_struct *napi, int budget)
+{
+       WARN_ON(1);
+       return 0;
+}
+
 static void iwl_trans_pcie_configure(struct iwl_trans *trans,
                                     const struct iwl_trans_config *trans_cfg)
 {
@@ -1079,6 +1091,18 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans,
 
        trans_pcie->command_names = trans_cfg->command_names;
        trans_pcie->bc_table_dword = trans_cfg->bc_table_dword;
+
+       /* Initialize NAPI here - it should be before registering to mac80211
+        * in the opmode but after the HW struct is allocated.
+        * As this function may be called again in some corner cases don't
+        * do anything if NAPI was already initialized.
+        */
+       if (!trans_pcie->napi.poll && trans->op_mode->ops->napi_add) {
+               init_dummy_netdev(&trans_pcie->napi_dev);
+               iwl_op_mode_napi_add(trans->op_mode, &trans_pcie->napi,
+                                    &trans_pcie->napi_dev,
+                                    iwl_pcie_dummy_napi_poll, 64);
+       }
 }
 
 void iwl_trans_pcie_free(struct iwl_trans *trans)
@@ -1099,6 +1123,9 @@ void iwl_trans_pcie_free(struct iwl_trans *trans)
        pci_disable_device(trans_pcie->pci_dev);
        kmem_cache_destroy(trans->dev_cmd_pool);
 
+       if (trans_pcie->napi.poll)
+               netif_napi_del(&trans_pcie->napi);
+
        kfree(trans);
 }
 
@@ -1237,7 +1264,7 @@ static int iwl_trans_pcie_write_mem(struct iwl_trans *trans, u32 addr,
 
 #define IWL_FLUSH_WAIT_MS      2000
 
-static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans)
+static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        struct iwl_txq *txq;
@@ -1250,13 +1277,31 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans)
 
        /* waiting for all the tx frames complete might take a while */
        for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) {
+               u8 wr_ptr;
+
                if (cnt == trans_pcie->cmd_queue)
                        continue;
+               if (!test_bit(cnt, trans_pcie->queue_used))
+                       continue;
+               if (!(BIT(cnt) & txq_bm))
+                       continue;
+
+               IWL_DEBUG_TX_QUEUES(trans, "Emptying queue %d...\n", cnt);
                txq = &trans_pcie->txq[cnt];
                q = &txq->q;
-               while (q->read_ptr != q->write_ptr && !time_after(jiffies,
-                      now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS)))
+               wr_ptr = ACCESS_ONCE(q->write_ptr);
+
+               while (q->read_ptr != ACCESS_ONCE(q->write_ptr) &&
+                      !time_after(jiffies,
+                                  now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS))) {
+                       u8 write_ptr = ACCESS_ONCE(q->write_ptr);
+
+                       if (WARN_ONCE(wr_ptr != write_ptr,
+                                     "WR pointer moved while flushing %d -> %d\n",
+                                     wr_ptr, write_ptr))
+                               return -ETIMEDOUT;
                        msleep(1);
+               }
 
                if (q->read_ptr != q->write_ptr) {
                        IWL_ERR(trans,
@@ -1264,6 +1309,7 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans)
                        ret = -ETIMEDOUT;
                        break;
                }
+               IWL_DEBUG_TX_QUEUES(trans, "Queue %d is now empty.\n", cnt);
        }
 
        if (!ret)
@@ -1298,8 +1344,8 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans)
                IWL_ERR(trans,
                        "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n",
                        cnt, active ? "" : "in", fifo, tbl_dw,
-                       iwl_read_prph(trans,
-                                     SCD_QUEUE_RDPTR(cnt)) & (txq->q.n_bd - 1),
+                       iwl_read_prph(trans, SCD_QUEUE_RDPTR(cnt)) &
+                               (TFD_QUEUE_SIZE_MAX - 1),
                        iwl_read_prph(trans, SCD_QUEUE_WRPTR(cnt)));
        }
 
@@ -1630,6 +1676,61 @@ static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
        IWL_ERR(trans, "failed to create the trans debugfs entry\n");
        return -ENOMEM;
 }
+
+static u32 iwl_trans_pcie_get_cmdlen(struct iwl_tfd *tfd)
+{
+       u32 cmdlen = 0;
+       int i;
+
+       for (i = 0; i < IWL_NUM_OF_TBS; i++)
+               cmdlen += iwl_pcie_tfd_tb_get_len(tfd, i);
+
+       return cmdlen;
+}
+
+static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans,
+                                   void *buf, u32 buflen)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       struct iwl_fw_error_dump_data *data;
+       struct iwl_txq *cmdq = &trans_pcie->txq[trans_pcie->cmd_queue];
+       struct iwl_fw_error_dump_txcmd *txcmd;
+       u32 len;
+       int i, ptr;
+
+       if (!buf)
+               return sizeof(*data) +
+                      cmdq->q.n_window * (sizeof(*txcmd) +
+                                          TFD_MAX_PAYLOAD_SIZE);
+
+       len = 0;
+       data = buf;
+       data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXCMD);
+       txcmd = (void *)data->data;
+       spin_lock_bh(&cmdq->lock);
+       ptr = cmdq->q.write_ptr;
+       for (i = 0; i < cmdq->q.n_window; i++) {
+               u8 idx = get_cmd_index(&cmdq->q, ptr);
+               u32 caplen, cmdlen;
+
+               cmdlen = iwl_trans_pcie_get_cmdlen(&cmdq->tfds[ptr]);
+               caplen = min_t(u32, TFD_MAX_PAYLOAD_SIZE, cmdlen);
+
+               if (cmdlen) {
+                       len += sizeof(*txcmd) + caplen;
+                       txcmd->cmdlen = cpu_to_le32(cmdlen);
+                       txcmd->caplen = cpu_to_le32(caplen);
+                       memcpy(txcmd->data, cmdq->entries[idx].cmd, caplen);
+                       txcmd = (void *)((u8 *)txcmd->data + caplen);
+               }
+
+               ptr = iwl_queue_dec_wrap(ptr);
+       }
+       spin_unlock_bh(&cmdq->lock);
+
+       data->len = cpu_to_le32(len);
+       return sizeof(*data) + len;
+}
 #else
 static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
                                         struct dentry *dir)
@@ -1672,6 +1773,10 @@ static const struct iwl_trans_ops trans_ops_pcie = {
        .grab_nic_access = iwl_trans_pcie_grab_nic_access,
        .release_nic_access = iwl_trans_pcie_release_nic_access,
        .set_bits_mask = iwl_trans_pcie_set_bits_mask,
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       .dump_data = iwl_trans_pcie_dump_data,
+#endif
 };
 
 struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
index 3b0c72c1005446d2d93f7eb15ed6d3207e1c3ea1..038940afbdc57d8d176908bb4869fc8791f250eb 100644 (file)
@@ -70,20 +70,20 @@ static int iwl_queue_space(const struct iwl_queue *q)
 
        /*
         * To avoid ambiguity between empty and completely full queues, there
-        * should always be less than q->n_bd elements in the queue.
-        * If q->n_window is smaller than q->n_bd, there is no need to reserve
-        * any queue entries for this purpose.
+        * should always be less than TFD_QUEUE_SIZE_MAX elements in the queue.
+        * If q->n_window is smaller than TFD_QUEUE_SIZE_MAX, there is no need
+        * to reserve any queue entries for this purpose.
         */
-       if (q->n_window < q->n_bd)
+       if (q->n_window < TFD_QUEUE_SIZE_MAX)
                max = q->n_window;
        else
-               max = q->n_bd - 1;
+               max = TFD_QUEUE_SIZE_MAX - 1;
 
        /*
-        * q->n_bd is a power of 2, so the following is equivalent to modulo by
-        * q->n_bd and is well defined for negative dividends.
+        * TFD_QUEUE_SIZE_MAX is a power of 2, so the following is equivalent to
+        * modulo by TFD_QUEUE_SIZE_MAX and is well defined.
         */
-       used = (q->write_ptr - q->read_ptr) & (q->n_bd - 1);
+       used = (q->write_ptr - q->read_ptr) & (TFD_QUEUE_SIZE_MAX - 1);
 
        if (WARN_ON(used > max))
                return 0;
@@ -94,17 +94,11 @@ static int iwl_queue_space(const struct iwl_queue *q)
 /*
  * iwl_queue_init - Initialize queue's high/low-water and read/write indexes
  */
-static int iwl_queue_init(struct iwl_queue *q, int count, int slots_num, u32 id)
+static int iwl_queue_init(struct iwl_queue *q, int slots_num, u32 id)
 {
-       q->n_bd = count;
        q->n_window = slots_num;
        q->id = id;
 
-       /* count must be power-of-two size, otherwise iwl_queue_inc_wrap
-        * and iwl_queue_dec_wrap are broken. */
-       if (WARN_ON(!is_power_of_2(count)))
-               return -EINVAL;
-
        /* slots_num must be power-of-two size, otherwise
         * get_cmd_index is broken. */
        if (WARN_ON(!is_power_of_2(slots_num)))
@@ -197,17 +191,17 @@ static void iwl_pcie_txq_stuck_timer(unsigned long data)
                IWL_ERR(trans,
                        "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n",
                        i, active ? "" : "in", fifo, tbl_dw,
-                       iwl_read_prph(trans,
-                                     SCD_QUEUE_RDPTR(i)) & (txq->q.n_bd - 1),
+                       iwl_read_prph(trans, SCD_QUEUE_RDPTR(i)) &
+                               (TFD_QUEUE_SIZE_MAX - 1),
                        iwl_read_prph(trans, SCD_QUEUE_WRPTR(i)));
        }
 
        for (i = q->read_ptr; i != q->write_ptr;
-            i = iwl_queue_inc_wrap(i, q->n_bd))
+            i = iwl_queue_inc_wrap(i))
                IWL_ERR(trans, "scratch %d = 0x%08x\n", i,
                        le32_to_cpu(txq->scratchbufs[i].scratch));
 
-       iwl_write_prph(trans, DEVICE_SET_NMI_REG, 1);
+       iwl_force_nmi(trans);
 }
 
 /*
@@ -287,14 +281,14 @@ static void iwl_pcie_txq_inval_byte_cnt_tbl(struct iwl_trans *trans,
 /*
  * iwl_pcie_txq_inc_wr_ptr - Send new write index to hardware
  */
-void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq)
+static void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans,
+                                   struct iwl_txq *txq)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        u32 reg = 0;
        int txq_id = txq->q.id;
 
-       if (txq->need_update == 0)
-               return;
+       lockdep_assert_held(&txq->lock);
 
        /*
         * explicitly wake up the NIC if:
@@ -317,6 +311,7 @@ void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq)
                                       txq_id, reg);
                        iwl_set_bit(trans, CSR_GP_CNTRL,
                                    CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+                       txq->need_update = true;
                        return;
                }
        }
@@ -327,8 +322,23 @@ void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq)
         */
        IWL_DEBUG_TX(trans, "Q:%d WR: 0x%x\n", txq_id, txq->q.write_ptr);
        iwl_write32(trans, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8));
+}
+
+void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       int i;
+
+       for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) {
+               struct iwl_txq *txq = &trans_pcie->txq[i];
 
-       txq->need_update = 0;
+               spin_lock_bh(&txq->lock);
+               if (trans_pcie->txq[i].need_update) {
+                       iwl_pcie_txq_inc_wr_ptr(trans, txq);
+                       trans_pcie->txq[i].need_update = false;
+               }
+               spin_unlock_bh(&txq->lock);
+       }
 }
 
 static inline dma_addr_t iwl_pcie_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx)
@@ -343,13 +353,6 @@ static inline dma_addr_t iwl_pcie_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx)
        return addr;
 }
 
-static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx)
-{
-       struct iwl_tfd_tb *tb = &tfd->tbs[idx];
-
-       return le16_to_cpu(tb->hi_n_len) >> 4;
-}
-
 static inline void iwl_pcie_tfd_set_tb(struct iwl_tfd *tfd, u8 idx,
                                       dma_addr_t addr, u16 len)
 {
@@ -409,13 +412,17 @@ static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq)
 {
        struct iwl_tfd *tfd_tmp = txq->tfds;
 
-       /* rd_ptr is bounded by n_bd and idx is bounded by n_window */
+       /* rd_ptr is bounded by TFD_QUEUE_SIZE_MAX and
+        * idx is bounded by n_window
+        */
        int rd_ptr = txq->q.read_ptr;
        int idx = get_cmd_index(&txq->q, rd_ptr);
 
        lockdep_assert_held(&txq->lock);
 
-       /* We have only q->n_window txq->entries, but we use q->n_bd tfds */
+       /* We have only q->n_window txq->entries, but we use
+        * TFD_QUEUE_SIZE_MAX tfds
+        */
        iwl_pcie_tfd_unmap(trans, &txq->entries[idx].meta, &tfd_tmp[rd_ptr]);
 
        /* free SKB */
@@ -436,7 +443,7 @@ static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq)
 }
 
 static int iwl_pcie_txq_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq,
-                                 dma_addr_t addr, u16 len, u8 reset)
+                                 dma_addr_t addr, u16 len, bool reset)
 {
        struct iwl_queue *q;
        struct iwl_tfd *tfd, *tfd_tmp;
@@ -542,15 +549,14 @@ static int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq,
 {
        int ret;
 
-       txq->need_update = 0;
+       txq->need_update = false;
 
        /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise
         * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */
        BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1));
 
        /* Initialize queue's high/low-water marks, and head/tail indexes */
-       ret = iwl_queue_init(&txq->q, TFD_QUEUE_SIZE_MAX, slots_num,
-                       txq_id);
+       ret = iwl_queue_init(&txq->q, slots_num, txq_id);
        if (ret)
                return ret;
 
@@ -575,15 +581,12 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id)
        struct iwl_txq *txq = &trans_pcie->txq[txq_id];
        struct iwl_queue *q = &txq->q;
 
-       if (!q->n_bd)
-               return;
-
        spin_lock_bh(&txq->lock);
        while (q->write_ptr != q->read_ptr) {
                IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n",
                                   txq_id, q->read_ptr);
                iwl_pcie_txq_free_tfd(trans, txq);
-               q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd);
+               q->read_ptr = iwl_queue_inc_wrap(q->read_ptr);
        }
        txq->active = false;
        spin_unlock_bh(&txq->lock);
@@ -620,10 +623,12 @@ static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id)
                }
 
        /* De-alloc circular buffer of TFDs */
-       if (txq->q.n_bd) {
-               dma_free_coherent(dev, sizeof(struct iwl_tfd) *
-                                 txq->q.n_bd, txq->tfds, txq->q.dma_addr);
+       if (txq->tfds) {
+               dma_free_coherent(dev,
+                                 sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX,
+                                 txq->tfds, txq->q.dma_addr);
                txq->q.dma_addr = 0;
+               txq->tfds = NULL;
 
                dma_free_coherent(dev,
                                  sizeof(*txq->scratchbufs) * txq->q.n_window,
@@ -680,7 +685,8 @@ void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr)
        /* The chain extension of the SCD doesn't work well. This feature is
         * enabled by default by the HW, so we need to disable it manually.
         */
-       iwl_write_prph(trans, SCD_CHAINEXT_EN, 0);
+       if (trans->cfg->base_params->scd_chain_ext_wa)
+               iwl_write_prph(trans, SCD_CHAINEXT_EN, 0);
 
        iwl_trans_ac_txq_enable(trans, trans_pcie->cmd_queue,
                                trans_pcie->cmd_fifo);
@@ -931,8 +937,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        struct iwl_txq *txq = &trans_pcie->txq[txq_id];
-       /* n_bd is usually 256 => n_bd - 1 = 0xff */
-       int tfd_num = ssn & (txq->q.n_bd - 1);
+       int tfd_num = ssn & (TFD_QUEUE_SIZE_MAX - 1);
        struct iwl_queue *q = &txq->q;
        int last_to_free;
 
@@ -956,12 +961,12 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
 
        /*Since we free until index _not_ inclusive, the one before index is
         * the last we will free. This one must be used */
-       last_to_free = iwl_queue_dec_wrap(tfd_num, q->n_bd);
+       last_to_free = iwl_queue_dec_wrap(tfd_num);
 
        if (!iwl_queue_used(q, last_to_free)) {
                IWL_ERR(trans,
                        "%s: Read index for DMA queue txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n",
-                       __func__, txq_id, last_to_free, q->n_bd,
+                       __func__, txq_id, last_to_free, TFD_QUEUE_SIZE_MAX,
                        q->write_ptr, q->read_ptr);
                goto out;
        }
@@ -971,7 +976,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
 
        for (;
             q->read_ptr != tfd_num;
-            q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
+            q->read_ptr = iwl_queue_inc_wrap(q->read_ptr)) {
 
                if (WARN_ON_ONCE(txq->entries[txq->q.read_ptr].skb == NULL))
                        continue;
@@ -1010,25 +1015,26 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx)
 
        lockdep_assert_held(&txq->lock);
 
-       if ((idx >= q->n_bd) || (!iwl_queue_used(q, idx))) {
+       if ((idx >= TFD_QUEUE_SIZE_MAX) || (!iwl_queue_used(q, idx))) {
                IWL_ERR(trans,
                        "%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n",
-                       __func__, txq_id, idx, q->n_bd,
+                       __func__, txq_id, idx, TFD_QUEUE_SIZE_MAX,
                        q->write_ptr, q->read_ptr);
                return;
        }
 
-       for (idx = iwl_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx;
-            q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
+       for (idx = iwl_queue_inc_wrap(idx); q->read_ptr != idx;
+            q->read_ptr = iwl_queue_inc_wrap(q->read_ptr)) {
 
                if (nfreed++ > 0) {
                        IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n",
                                idx, q->write_ptr, q->read_ptr);
-                       iwl_write_prph(trans, DEVICE_SET_NMI_REG, 1);
+                       iwl_force_nmi(trans);
                }
        }
 
-       if (q->read_ptr == q->write_ptr) {
+       if (trans->cfg->base_params->apmg_wake_up_wa &&
+           q->read_ptr == q->write_ptr) {
                spin_lock_irqsave(&trans_pcie->reg_lock, flags);
                WARN_ON(!trans_pcie->cmd_in_flight);
                trans_pcie->cmd_in_flight = false;
@@ -1309,28 +1315,39 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
        cmd_pos = offsetof(struct iwl_device_cmd, payload);
        copy_size = sizeof(out_cmd->hdr);
        for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) {
-               int copy = 0;
+               int copy;
 
                if (!cmd->len[i])
                        continue;
 
-               /* need at least IWL_HCMD_SCRATCHBUF_SIZE copied */
-               if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) {
-                       copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size;
-
-                       if (copy > cmd->len[i])
-                               copy = cmd->len[i];
-               }
-
                /* copy everything if not nocopy/dup */
                if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY |
-                                          IWL_HCMD_DFL_DUP)))
+                                          IWL_HCMD_DFL_DUP))) {
                        copy = cmd->len[i];
 
-               if (copy) {
                        memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy);
                        cmd_pos += copy;
                        copy_size += copy;
+                       continue;
+               }
+
+               /*
+                * Otherwise we need at least IWL_HCMD_SCRATCHBUF_SIZE copied
+                * in total (for the scratchbuf handling), but copy up to what
+                * we can fit into the payload for debug dump purposes.
+                */
+               copy = min_t(int, TFD_MAX_PAYLOAD_SIZE - cmd_pos, cmd->len[i]);
+
+               memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy);
+               cmd_pos += copy;
+
+               /* However, treat copy_size the proper way, we need it below */
+               if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) {
+                       copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size;
+
+                       if (copy > cmd->len[i])
+                               copy = cmd->len[i];
+                       copy_size += copy;
                }
        }
 
@@ -1345,7 +1362,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
        memcpy(&txq->scratchbufs[q->write_ptr], &out_cmd->hdr, scratch_size);
        iwl_pcie_txq_build_tfd(trans, txq,
                               iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr),
-                              scratch_size, 1);
+                              scratch_size, true);
 
        /* map first command fragment, if any remains */
        if (copy_size > scratch_size) {
@@ -1361,7 +1378,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
                }
 
                iwl_pcie_txq_build_tfd(trans, txq, phys_addr,
-                                      copy_size - scratch_size, 0);
+                                      copy_size - scratch_size, false);
        }
 
        /* map the remaining (adjusted) nocopy/dup fragments */
@@ -1384,7 +1401,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
                        goto out;
                }
 
-               iwl_pcie_txq_build_tfd(trans, txq, phys_addr, cmdlen[i], 0);
+               iwl_pcie_txq_build_tfd(trans, txq, phys_addr, cmdlen[i], false);
        }
 
        out_meta->flags = cmd->flags;
@@ -1392,8 +1409,6 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
                kfree(txq->entries[idx].free_buf);
        txq->entries[idx].free_buf = dup_buf;
 
-       txq->need_update = 1;
-
        trace_iwlwifi_dev_hcmd(trans->dev, cmd, cmd_size, &out_cmd->hdr);
 
        /* start timer if queue currently empty */
@@ -1405,9 +1420,11 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
        /*
         * wake up the NIC to make sure that the firmware will see the host
         * command - we will let the NIC sleep once all the host commands
-        * returned.
+        * returned. This needs to be done only on NICs that have
+        * apmg_wake_up_wa set.
         */
-       if (!trans_pcie->cmd_in_flight) {
+       if (trans->cfg->base_params->apmg_wake_up_wa &&
+           !trans_pcie->cmd_in_flight) {
                trans_pcie->cmd_in_flight = true;
                __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
                                         CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
@@ -1427,7 +1444,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
        }
 
        /* Increment and update queue's write index */
-       q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
+       q->write_ptr = iwl_queue_inc_wrap(q->write_ptr);
        iwl_pcie_txq_inc_wr_ptr(trans, txq);
 
        spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
@@ -1583,7 +1600,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
                               get_cmd_string(trans_pcie, cmd->id));
                ret = -ETIMEDOUT;
 
-               iwl_write_prph(trans, DEVICE_SET_NMI_REG, 1);
+               iwl_force_nmi(trans);
                iwl_trans_fw_error(trans);
 
                goto cancel;
@@ -1661,7 +1678,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
        dma_addr_t tb0_phys, tb1_phys, scratch_phys;
        void *tb1_addr;
        u16 len, tb1_len, tb2_len;
-       u8 wait_write_ptr = 0;
+       bool wait_write_ptr;
        __le16 fc = hdr->frame_control;
        u8 hdr_len = ieee80211_hdrlen(fc);
        u16 wifi_seq;
@@ -1722,7 +1739,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
        memcpy(&txq->scratchbufs[q->write_ptr], &dev_cmd->hdr,
               IWL_HCMD_SCRATCHBUF_SIZE);
        iwl_pcie_txq_build_tfd(trans, txq, tb0_phys,
-                              IWL_HCMD_SCRATCHBUF_SIZE, 1);
+                              IWL_HCMD_SCRATCHBUF_SIZE, true);
 
        /* there must be data left over for TB1 or this code must be changed */
        BUILD_BUG_ON(sizeof(struct iwl_tx_cmd) < IWL_HCMD_SCRATCHBUF_SIZE);
@@ -1732,7 +1749,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
        tb1_phys = dma_map_single(trans->dev, tb1_addr, tb1_len, DMA_TO_DEVICE);
        if (unlikely(dma_mapping_error(trans->dev, tb1_phys)))
                goto out_err;
-       iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, 0);
+       iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, false);
 
        /*
         * Set up TFD's third entry to point directly to remainder
@@ -1748,7 +1765,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
                                           &txq->tfds[q->write_ptr]);
                        goto out_err;
                }
-               iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, 0);
+               iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, false);
        }
 
        /* Set up entry for this TFD in Tx byte-count array */
@@ -1762,12 +1779,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
        trace_iwlwifi_dev_tx_data(trans->dev, skb,
                                  skb->data + hdr_len, tb2_len);
 
-       if (!ieee80211_has_morefrags(fc)) {
-               txq->need_update = 1;
-       } else {
-               wait_write_ptr = 1;
-               txq->need_update = 0;
-       }
+       wait_write_ptr = ieee80211_has_morefrags(fc);
 
        /* start timer if queue currently empty */
        if (txq->need_update && q->read_ptr == q->write_ptr &&
@@ -1775,22 +1787,19 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
                mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout);
 
        /* Tell device the write index *just past* this latest filled TFD */
-       q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
-       iwl_pcie_txq_inc_wr_ptr(trans, txq);
+       q->write_ptr = iwl_queue_inc_wrap(q->write_ptr);
+       if (!wait_write_ptr)
+               iwl_pcie_txq_inc_wr_ptr(trans, txq);
 
        /*
         * At this point the frame is "transmitted" successfully
-        * and we will get a TX status notification eventually,
-        * regardless of the value of ret. "ret" only indicates
-        * whether or not we should update the write pointer.
+        * and we will get a TX status notification eventually.
         */
        if (iwl_queue_space(q) < q->high_mark) {
-               if (wait_write_ptr) {
-                       txq->need_update = 1;
+               if (wait_write_ptr)
                        iwl_pcie_txq_inc_wr_ptr(trans, txq);
-               } else {
+               else
                        iwl_stop_queue(trans, txq);
-               }
        }
        spin_unlock(&txq->lock);
        return 0;
index 54e344aed6e05d097c3eb60462d74064757b81fc..47a998d8f99e75bd5f59521a6593ca183cebb77a 100644 (file)
@@ -1006,9 +1006,8 @@ struct cmd_key_material {
 } __packed;
 
 static int lbs_set_key_material(struct lbs_private *priv,
-                               int key_type,
-                               int key_info,
-                               u8 *key, u16 key_len)
+                               int key_type, int key_info,
+                               const u8 *key, u16 key_len)
 {
        struct cmd_key_material cmd;
        int ret;
@@ -1610,7 +1609,7 @@ static int lbs_cfg_del_key(struct wiphy *wiphy, struct net_device *netdev,
  */
 
 static int lbs_cfg_get_station(struct wiphy *wiphy, struct net_device *dev,
-                             u8 *mac, struct station_info *sinfo)
+                              const u8 *mac, struct station_info *sinfo)
 {
        struct lbs_private *priv = wiphy_priv(wiphy);
        s8 signal, noise;
index ab966f08024a0013194c8eaf82e472d866f3492e..407784aca627bcd69b8a8632b45be53390a17481 100644 (file)
@@ -90,7 +90,8 @@ do { if ((lbs_debug & (grp)) == (grp)) \
 #define lbs_deb_cfg80211(fmt, args...)  LBS_DEB_LL(LBS_DEB_CFG80211, " cfg80211", fmt, ##args)
 
 #ifdef DEBUG
-static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, int len)
+static inline void lbs_deb_hex(unsigned int grp, const char *prompt,
+                              const u8 *buf, int len)
 {
        int i = 0;
 
index c7366b07b568ab303985c651679b3bcdb409e051..e446fed7b3459b854b05ab7728c0c15c3ac51e7e 100644 (file)
@@ -71,8 +71,10 @@ int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb)
 
        skb->ip_summed = CHECKSUM_NONE;
 
-       if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR)
-               return process_rxed_802_11_packet(priv, skb);
+       if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) {
+               ret = process_rxed_802_11_packet(priv, skb);
+               goto done;
+       }
 
        p_rx_pd = (struct rxpd *) skb->data;
        p_rx_pkt = (struct rxpackethdr *) ((u8 *)p_rx_pd +
@@ -86,7 +88,7 @@ int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb)
        if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) {
                lbs_deb_rx("rx err: frame received with bad length\n");
                dev->stats.rx_length_errors++;
-               ret = 0;
+               ret = -EINVAL;
                dev_kfree_skb(skb);
                goto done;
        }
index 9d7a52f5a4102abedd2dbebc03c26c3866da2a64..a312c653d1163fcc5c4ff394a54b0c7a96370d8f 100644 (file)
@@ -1676,7 +1676,9 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw,
        return 0;
 }
 
-static void mac80211_hwsim_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+static void mac80211_hwsim_flush(struct ieee80211_hw *hw,
+                                struct ieee80211_vif *vif,
+                                u32 queues, bool drop)
 {
        /* Not implemented, queues only on kernel side */
 }
@@ -2056,6 +2058,7 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
                            WIPHY_FLAG_AP_UAPSD |
                            WIPHY_FLAG_HAS_CHANNEL_SWITCH;
        hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
+       hw->wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE;
 
        /* ask mac80211 to reserve space for magic */
        hw->vif_data_size = sizeof(struct hwsim_vif_priv);
index c92f27aa71ede1f049c101a0ba185f31d982aaa3..706831df1fa2a4183cb3c5ad849f1aa8df8dbb14 100644 (file)
@@ -212,8 +212,7 @@ int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv,
                                      sizeof(struct mwifiex_ie_types_header));
                        memcpy((u8 *)vht_op +
                                sizeof(struct mwifiex_ie_types_header),
-                              (u8 *)bss_desc->bcn_vht_oper +
-                              sizeof(struct ieee_types_header),
+                              (u8 *)bss_desc->bcn_vht_oper,
                               le16_to_cpu(vht_op->header.len));
 
                        /* negotiate the channel width and central freq
index d14ead8beca860dba6c984d26df095b104bb1375..e1c2f67ae85e694d52b1f9e4ad69f2d50ab6ba91 100644 (file)
@@ -345,8 +345,7 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
 
                        memcpy((u8 *) ht_info +
                               sizeof(struct mwifiex_ie_types_header),
-                              (u8 *) bss_desc->bcn_ht_oper +
-                              sizeof(struct ieee_types_header),
+                              (u8 *)bss_desc->bcn_ht_oper,
                               le16_to_cpu(ht_info->header.len));
 
                        if (!(sband->ht_cap.cap &
@@ -750,3 +749,45 @@ void mwifiex_set_ba_params(struct mwifiex_private *priv)
 
        return;
 }
+
+u8 mwifiex_get_sec_chan_offset(int chan)
+{
+       u8 sec_offset;
+
+       switch (chan) {
+       case 36:
+       case 44:
+       case 52:
+       case 60:
+       case 100:
+       case 108:
+       case 116:
+       case 124:
+       case 132:
+       case 140:
+       case 149:
+       case 157:
+               sec_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+               break;
+       case 40:
+       case 48:
+       case 56:
+       case 64:
+       case 104:
+       case 112:
+       case 120:
+       case 128:
+       case 136:
+       case 144:
+       case 153:
+       case 161:
+               sec_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+               break;
+       case 165:
+       default:
+               sec_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
+               break;
+       }
+
+       return sec_offset;
+}
index 40b007a00f4bd9e786c24f8f53059c38e29e79a4..0b73fa08f5d466b98d5292d0a5b16e1011c30be3 100644 (file)
@@ -63,6 +63,7 @@ int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd,
                                int cmd_action,
                                struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl);
 void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra);
+u8 mwifiex_get_sec_chan_offset(int chan);
 
 static inline u8
 mwifiex_is_station_ampdu_allowed(struct mwifiex_private *priv,
@@ -199,7 +200,7 @@ static inline int mwifiex_is_sta_11n_enabled(struct mwifiex_private *priv,
 }
 
 static inline u8
-mwifiex_tdls_peer_11n_enabled(struct mwifiex_private *priv, u8 *ra)
+mwifiex_tdls_peer_11n_enabled(struct mwifiex_private *priv, const u8 *ra)
 {
        struct mwifiex_sta_node *node = mwifiex_get_sta_entry(priv, ra);
        if (node)
index 63211707f93955c851bfb96d71f12d5ef1f4a313..5b32106182f81c11fbc2bd985166dad198f341b1 100644 (file)
@@ -100,6 +100,7 @@ mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv,
                            struct sk_buff *skb)
 {
        struct txpd *local_tx_pd;
+       struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb);
 
        skb_push(skb, sizeof(*local_tx_pd));
 
@@ -118,6 +119,9 @@ mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv,
        local_tx_pd->tx_pkt_length = cpu_to_le16(skb->len -
                                                 sizeof(*local_tx_pd));
 
+       if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT)
+               local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET;
+
        if (local_tx_pd->tx_control == 0)
                /* TxCtrl set by user or default */
                local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl);
@@ -160,6 +164,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
        int pad = 0, ret;
        struct mwifiex_tx_param tx_param;
        struct txpd *ptx_pd = NULL;
+       struct timeval tv;
        int headroom = adapter->iface_type == MWIFIEX_USB ? 0 : INTF_HEADER_LEN;
 
        skb_src = skb_peek(&pra_list->skb_head);
@@ -182,8 +187,14 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
 
        tx_info_aggr->bss_type = tx_info_src->bss_type;
        tx_info_aggr->bss_num = tx_info_src->bss_num;
+
+       if (tx_info_src->flags & MWIFIEX_BUF_FLAG_TDLS_PKT)
+               tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT;
        skb_aggr->priority = skb_src->priority;
 
+       do_gettimeofday(&tv);
+       skb_aggr->tstamp = timeval_to_ktime(tv);
+
        do {
                /* Check if AMSDU can accommodate this MSDU */
                if (skb_tailroom(skb_aggr) < (skb_src->len + LLC_SNAP_LEN))
@@ -236,18 +247,11 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
                ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA,
                                                   skb_aggr, NULL);
        } else {
-               /*
-                * Padding per MSDU will affect the length of next
-                * packet and hence the exact length of next packet
-                * is uncertain here.
-                *
-                * Also, aggregation of transmission buffer, while
-                * downloading the data to the card, wont gain much
-                * on the AMSDU packets as the AMSDU packets utilizes
-                * the transmission buffer space to the maximum
-                * (adapter->tx_buf_size).
-                */
-               tx_param.next_pkt_len = 0;
+               if (skb_src)
+                       tx_param.next_pkt_len =
+                                       skb_src->len + sizeof(struct txpd);
+               else
+                       tx_param.next_pkt_len = 0;
 
                ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA,
                                                   skb_aggr, &tx_param);
index b9242c3dca435ee9a4d5123fd57ad0733a96a24d..3b55ce5690a54e226c5482f523a3c80d1e95d7bf 100644 (file)
@@ -200,4 +200,11 @@ getlog
 
        cat getlog
 
+fw_dump
+       This command is used to dump firmware memory into files.
+       Separate file will be created for each memory segment.
+       Usage:
+
+       cat fw_dump
+
 ===============================================================================
index 21ee27ab7b745261f9a398bcaacc92b24ce7c8f7..e95dec91a561e1172289dca0d6bbfb35b38add56 100644 (file)
@@ -994,7 +994,7 @@ mwifiex_dump_station_info(struct mwifiex_private *priv,
  */
 static int
 mwifiex_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
-                            u8 *mac, struct station_info *sinfo)
+                            const u8 *mac, struct station_info *sinfo)
 {
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
 
@@ -1270,7 +1270,7 @@ static int mwifiex_cfg80211_change_beacon(struct wiphy *wiphy,
  */
 static int
 mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
-                            u8 *mac)
+                            const u8 *mac)
 {
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
        struct mwifiex_sta_node *sta_node;
@@ -2629,7 +2629,7 @@ static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy,
  */
 static int
 mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
-                          u8 *peer, u8 action_code, u8 dialog_token,
+                          const u8 *peer, u8 action_code, u8 dialog_token,
                           u16 status_code, u32 peer_capability,
                           const u8 *extra_ies, size_t extra_ies_len)
 {
@@ -2701,7 +2701,7 @@ mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
 
 static int
 mwifiex_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
-                          u8 *peer, enum nl80211_tdls_operation action)
+                          const u8 *peer, enum nl80211_tdls_operation action)
 {
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
 
@@ -2748,9 +2748,8 @@ mwifiex_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
 }
 
 static int
-mwifiex_cfg80211_add_station(struct wiphy *wiphy,
-                            struct net_device *dev,
-                            u8 *mac, struct station_parameters *params)
+mwifiex_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev,
+                            const u8 *mac, struct station_parameters *params)
 {
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
 
@@ -2765,9 +2764,9 @@ mwifiex_cfg80211_add_station(struct wiphy *wiphy,
 }
 
 static int
-mwifiex_cfg80211_change_station(struct wiphy *wiphy,
-                               struct net_device *dev,
-                               u8 *mac, struct station_parameters *params)
+mwifiex_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev,
+                               const u8 *mac,
+                               struct station_parameters *params)
 {
        int ret;
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
index 1062c918a7bffb19cf93c1aba0daa4490856ba65..8dee6c86f4f1dc91e65978b6f7443ac9f00c2118 100644 (file)
@@ -955,8 +955,6 @@ mwifiex_cmd_timeout_func(unsigned long function_context)
                        adapter->cmd_wait_q.status = -ETIMEDOUT;
                        wake_up_interruptible(&adapter->cmd_wait_q.wait);
                        mwifiex_cancel_pending_ioctl(adapter);
-                       /* reset cmd_sent flag to unblock new commands */
-                       adapter->cmd_sent = false;
                }
        }
        if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING)
index b8a49aad12fd662434ce2a29aaa8edecfb52ba0b..7b419bbcd5444f5c5abdf40ffb2368087b77e89a 100644 (file)
@@ -256,6 +256,29 @@ mwifiex_info_read(struct file *file, char __user *ubuf,
        return ret;
 }
 
+/*
+ * Proc firmware dump read handler.
+ *
+ * This function is called when the 'fw_dump' file is opened for
+ * reading.
+ * This function dumps firmware memory in different files
+ * (ex. DTCM, ITCM, SQRAM etc.) based on the the segments for
+ * debugging.
+ */
+static ssize_t
+mwifiex_fw_dump_read(struct file *file, char __user *ubuf,
+                    size_t count, loff_t *ppos)
+{
+       struct mwifiex_private *priv = file->private_data;
+
+       if (!priv->adapter->if_ops.fw_dump)
+               return -EIO;
+
+       priv->adapter->if_ops.fw_dump(priv->adapter);
+
+       return 0;
+}
+
 /*
  * Proc getlog file read handler.
  *
@@ -699,6 +722,7 @@ static const struct file_operations mwifiex_dfs_##name##_fops = {       \
 MWIFIEX_DFS_FILE_READ_OPS(info);
 MWIFIEX_DFS_FILE_READ_OPS(debug);
 MWIFIEX_DFS_FILE_READ_OPS(getlog);
+MWIFIEX_DFS_FILE_READ_OPS(fw_dump);
 MWIFIEX_DFS_FILE_OPS(regrdwr);
 MWIFIEX_DFS_FILE_OPS(rdeeprom);
 
@@ -722,6 +746,7 @@ mwifiex_dev_debugfs_init(struct mwifiex_private *priv)
        MWIFIEX_DFS_ADD_FILE(getlog);
        MWIFIEX_DFS_ADD_FILE(regrdwr);
        MWIFIEX_DFS_ADD_FILE(rdeeprom);
+       MWIFIEX_DFS_ADD_FILE(fw_dump);
 }
 
 /*
index e7b3e16e5d34f1f8703ec2f6d4e21e9388c49edd..38da6ff6f41623618efa22add335ffef1fa46828 100644 (file)
 #define MWIFIEX_MAX_TX_BASTREAM_SUPPORTED      2
 #define MWIFIEX_MAX_RX_BASTREAM_SUPPORTED      16
 
-#define MWIFIEX_STA_AMPDU_DEF_TXWINSIZE        16
-#define MWIFIEX_STA_AMPDU_DEF_RXWINSIZE        32
+#define MWIFIEX_STA_AMPDU_DEF_TXWINSIZE        64
+#define MWIFIEX_STA_AMPDU_DEF_RXWINSIZE        64
 #define MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE        32
 #define MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE        16
-#define MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE   32
-#define MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE   48
+#define MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE   64
+#define MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE   64
 #define MWIFIEX_11AC_UAP_AMPDU_DEF_TXWINSIZE   48
 #define MWIFIEX_11AC_UAP_AMPDU_DEF_RXWINSIZE   32
 
index b485dc1ae5ebc42c5606e37723189d20e7d98b16..3175dd04834b9960698c67e750fc3d8928075088 100644 (file)
@@ -169,6 +169,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define TLV_TYPE_GWK_CIPHER         (PROPRIETARY_TLV_BASE_ID + 146)
 #define TLV_TYPE_COALESCE_RULE      (PROPRIETARY_TLV_BASE_ID + 154)
 #define TLV_TYPE_KEY_PARAM_V2       (PROPRIETARY_TLV_BASE_ID + 156)
+#define TLV_TYPE_TDLS_IDLE_TIMEOUT  (PROPRIETARY_TLV_BASE_ID + 194)
 #define TLV_TYPE_FW_API_REV         (PROPRIETARY_TLV_BASE_ID + 199)
 
 #define MWIFIEX_TX_DATA_BUF_SIZE_2K        2048
@@ -229,6 +230,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define ISENABLED_40MHZ_INTOLERANT(Dot11nDevCap) (Dot11nDevCap & BIT(8))
 #define ISSUPP_RXLDPC(Dot11nDevCap) (Dot11nDevCap & BIT(22))
 #define ISSUPP_BEAMFORMING(Dot11nDevCap) (Dot11nDevCap & BIT(30))
+#define ISALLOWED_CHANWIDTH40(ht_param) (ht_param & BIT(2))
 
 /* httxcfg bitmap
  * 0           reserved
@@ -403,7 +405,7 @@ enum P2P_MODES {
 #define HS_CFG_CANCEL                  0xffffffff
 #define HS_CFG_COND_DEF                        0x00000000
 #define HS_CFG_GPIO_DEF                        0xff
-#define HS_CFG_GAP_DEF                 0
+#define HS_CFG_GAP_DEF                 0xff
 #define HS_CFG_COND_BROADCAST_DATA     0x00000001
 #define HS_CFG_COND_UNICAST_DATA       0x00000002
 #define HS_CFG_COND_MAC_EVENT          0x00000004
@@ -487,6 +489,7 @@ enum P2P_MODES {
 #define EVENT_UAP_MIC_COUNTERMEASURES   0x0000004c
 #define EVENT_HOSTWAKE_STAIE           0x0000004d
 #define EVENT_CHANNEL_SWITCH_ANN        0x00000050
+#define EVENT_TDLS_GENERIC_EVENT        0x00000052
 #define EVENT_EXT_SCAN_REPORT           0x00000058
 #define EVENT_REMAIN_ON_CHAN_EXPIRED    0x0000005f
 
@@ -519,6 +522,7 @@ enum P2P_MODES {
 #define ACT_TDLS_DELETE            0x00
 #define ACT_TDLS_CREATE            0x01
 #define ACT_TDLS_CONFIG            0x02
+#define TDLS_EVENT_LINK_TEAR_DOWN  3
 
 #define MWIFIEX_FW_V15            15
 
@@ -535,6 +539,7 @@ struct mwifiex_ie_types_data {
 #define MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET 0x01
 #define MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET 0x08
 #define MWIFIEX_TXPD_FLAGS_TDLS_PACKET      0x10
+#define MWIFIEX_RXPD_FLAGS_TDLS_PACKET      0x01
 
 struct txpd {
        u8 bss_type;
@@ -577,7 +582,7 @@ struct rxpd {
         * [Bit 7] Reserved
         */
        u8 ht_info;
-       u8 reserved;
+       u8 flags;
 } __packed;
 
 struct uap_txpd {
@@ -708,6 +713,13 @@ struct mwifiex_ie_types_vendor_param_set {
        u8 ie[MWIFIEX_MAX_VSIE_LEN];
 };
 
+#define MWIFIEX_TDLS_IDLE_TIMEOUT      60
+
+struct mwifiex_ie_types_tdls_idle_timeout {
+       struct mwifiex_ie_types_header header;
+       __le16 value;
+} __packed;
+
 struct mwifiex_ie_types_rsn_param_set {
        struct mwifiex_ie_types_header header;
        u8 rsn_ie[1];
@@ -1745,6 +1757,15 @@ struct host_cmd_ds_802_11_subsc_evt {
        __le16 events;
 } __packed;
 
+struct mwifiex_tdls_generic_event {
+       __le16 type;
+       u8 peer_mac[ETH_ALEN];
+       union {
+               __le16 reason_code;
+               __le16 reserved;
+       } u;
+} __packed;
+
 struct mwifiex_ie {
        __le16 ie_index;
        __le16 mgmt_subtype_mask;
index ee494db5406097c35a0f22b365e2ad2e1ac674f8..1b576722671d5e6f228363c36c94c4ef47867980 100644 (file)
@@ -303,7 +303,7 @@ struct mwifiex_ds_ant_cfg {
        u32 rx_ant;
 };
 
-#define MWIFIEX_NUM_OF_CMD_BUFFER      20
+#define MWIFIEX_NUM_OF_CMD_BUFFER      50
 #define MWIFIEX_SIZE_OF_CMD_BUFFER     2048
 
 enum {
index 9c771b3e99186ffe838f771b217216ebc5a43454..cbabc12fbda390d063218375eb2b4cadc3911b8f 100644 (file)
@@ -521,7 +521,6 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
                release_firmware(adapter->firmware);
                adapter->firmware = NULL;
        }
-       complete(&adapter->fw_load);
        if (init_failed)
                mwifiex_free_adapter(adapter);
        up(sem);
@@ -535,7 +534,6 @@ static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter)
 {
        int ret;
 
-       init_completion(&adapter->fw_load);
        ret = request_firmware_nowait(THIS_MODULE, 1, adapter->fw_name,
                                      adapter->dev, GFP_KERNEL, adapter,
                                      mwifiex_fw_dpc);
index d53e1e8c9467a62663c4d28df86e623237cdc45f..1398afa8406401c9fd3898716b0849a22946cd42 100644 (file)
@@ -672,6 +672,7 @@ struct mwifiex_if_ops {
        int (*init_fw_port) (struct mwifiex_adapter *);
        int (*dnld_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *);
        void (*card_reset) (struct mwifiex_adapter *);
+       void (*fw_dump)(struct mwifiex_adapter *);
        int (*clean_pcie_ring) (struct mwifiex_adapter *adapter);
 };
 
@@ -787,7 +788,6 @@ struct mwifiex_adapter {
        struct mwifiex_wait_queue cmd_wait_q;
        u8 scan_wait_q_woken;
        spinlock_t queue_lock;          /* lock for tx queues */
-       struct completion fw_load;
        u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
        u16 max_mgmt_ie_index;
        u8 scan_delay_cnt;
@@ -910,8 +910,6 @@ int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv,
                                  struct sk_buff *skb);
 int mwifiex_process_sta_event(struct mwifiex_private *);
 int mwifiex_process_uap_event(struct mwifiex_private *);
-struct mwifiex_sta_node *
-mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac);
 void mwifiex_delete_all_station_list(struct mwifiex_private *priv);
 void *mwifiex_process_sta_txpd(struct mwifiex_private *, struct sk_buff *skb);
 void *mwifiex_process_uap_txpd(struct mwifiex_private *, struct sk_buff *skb);
@@ -1101,7 +1099,7 @@ mwifiex_11h_get_csa_closed_channel(struct mwifiex_private *priv)
                return 0;
 
        /* Clear csa channel, if DFS channel move time has passed */
-       if (jiffies > priv->csa_expire_time) {
+       if (time_after(jiffies, priv->csa_expire_time)) {
                priv->csa_chan = 0;
                priv->csa_expire_time = 0;
        }
@@ -1220,26 +1218,26 @@ void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv);
 extern const struct ethtool_ops mwifiex_ethtool_ops;
 
 void mwifiex_del_all_sta_list(struct mwifiex_private *priv);
-void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac);
+void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac);
 void
 mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies,
                       int ies_len, struct mwifiex_sta_node *node);
 struct mwifiex_sta_node *
-mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac);
+mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac);
 struct mwifiex_sta_node *
-mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac);
-int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, u8 *peer,
+mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac);
+int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer,
                                 u8 action_code, u8 dialog_token,
                                 u16 status_code, const u8 *extra_ies,
                                 size_t extra_ies_len);
-int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv,
-                                u8 *peer, u8 action_code, u8 dialog_token,
-                                u16 status_code, const u8 *extra_ies,
-                                size_t extra_ies_len);
+int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer,
+                                  u8 action_code, u8 dialog_token,
+                                  u16 status_code, const u8 *extra_ies,
+                                  size_t extra_ies_len);
 void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv,
                                       u8 *buf, int len);
-int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action);
-int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, u8 *mac);
+int mwifiex_tdls_oper(struct mwifiex_private *priv, const u8 *peer, u8 action);
+int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac);
 void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv);
 bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv);
 u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band,
index a7e8b96b2d9024de8c34e5e04b317c66d2e22820..574d4b59746801cc34ac78e6e4550c7d92aa6e9d 100644 (file)
@@ -221,9 +221,6 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev)
        if (!adapter || !adapter->priv_num)
                return;
 
-       /* In case driver is removed when asynchronous FW load is in progress */
-       wait_for_completion(&adapter->fw_load);
-
        if (user_rmmod) {
 #ifdef CONFIG_PM_SLEEP
                if (adapter->is_suspended)
@@ -1074,6 +1071,7 @@ static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter)
  * is mapped to PCI device memory. Tx ring pointers are advanced accordingly.
  * Download ready interrupt to FW is deffered if Tx ring is not full and
  * additional payload can be accomodated.
+ * Caller must ensure tx_param parameter to this function is not NULL.
  */
 static int
 mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb,
index 7b3af3d29ded478ad658eed5a3836403d6dd7542..45c5b3450cf5c719886483c9e0853c327fd62811 100644 (file)
@@ -29,9 +29,6 @@
 #define MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN   14
 
 #define MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD      4
-#define MWIFIEX_LIMIT_1_CHANNEL_PER_SCAN_CMD   15
-#define MWIFIEX_LIMIT_2_CHANNELS_PER_SCAN_CMD  27
-#define MWIFIEX_LIMIT_3_CHANNELS_PER_SCAN_CMD  35
 
 /* Memory needed to store a max sized Channel List TLV for a firmware scan */
 #define CHAN_TLV_MAX_SIZE  (sizeof(struct mwifiex_ie_types_header)         \
@@ -1055,20 +1052,10 @@ mwifiex_config_scan(struct mwifiex_private *priv,
 
        /*
         * In associated state we will reduce the number of channels scanned per
-        * scan command to avoid any traffic delay/loss. This number is decided
-        * based on total number of channels to be scanned due to constraints
-        * of command buffers.
+        * scan command to 1 to avoid any traffic delay/loss.
         */
-       if (priv->media_connected) {
-               if (chan_num < MWIFIEX_LIMIT_1_CHANNEL_PER_SCAN_CMD)
+       if (priv->media_connected)
                        *max_chan_per_scan = 1;
-               else if (chan_num < MWIFIEX_LIMIT_2_CHANNELS_PER_SCAN_CMD)
-                       *max_chan_per_scan = 2;
-               else if (chan_num < MWIFIEX_LIMIT_3_CHANNELS_PER_SCAN_CMD)
-                       *max_chan_per_scan = 3;
-               else
-                       *max_chan_per_scan = 4;
-       }
 }
 
 /*
@@ -1353,23 +1340,17 @@ int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter,
                                              bss_entry->beacon_buf);
                        break;
                case WLAN_EID_BSS_COEX_2040:
-                       bss_entry->bcn_bss_co_2040 = current_ptr +
-                               sizeof(struct ieee_types_header);
-                       bss_entry->bss_co_2040_offset = (u16) (current_ptr +
-                                       sizeof(struct ieee_types_header) -
-                                               bss_entry->beacon_buf);
+                       bss_entry->bcn_bss_co_2040 = current_ptr;
+                       bss_entry->bss_co_2040_offset =
+                               (u16) (current_ptr - bss_entry->beacon_buf);
                        break;
                case WLAN_EID_EXT_CAPABILITY:
-                       bss_entry->bcn_ext_cap = current_ptr +
-                               sizeof(struct ieee_types_header);
-                       bss_entry->ext_cap_offset = (u16) (current_ptr +
-                                       sizeof(struct ieee_types_header) -
-                                       bss_entry->beacon_buf);
+                       bss_entry->bcn_ext_cap = current_ptr;
+                       bss_entry->ext_cap_offset =
+                               (u16) (current_ptr - bss_entry->beacon_buf);
                        break;
                case WLAN_EID_OPMODE_NOTIF:
-                       bss_entry->oper_mode =
-                               (void *)(current_ptr +
-                                        sizeof(struct ieee_types_header));
+                       bss_entry->oper_mode = (void *)current_ptr;
                        bss_entry->oper_mode_offset =
                                        (u16)((u8 *)bss_entry->oper_mode -
                                              bss_entry->beacon_buf);
@@ -1757,6 +1738,19 @@ mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info,
        return 0;
 }
 
+static void mwifiex_complete_scan(struct mwifiex_private *priv)
+{
+       struct mwifiex_adapter *adapter = priv->adapter;
+
+       if (adapter->curr_cmd->wait_q_enabled) {
+               adapter->cmd_wait_q.status = 0;
+               if (!priv->scan_request) {
+                       dev_dbg(adapter->dev, "complete internal scan\n");
+                       mwifiex_complete_cmd(adapter, adapter->curr_cmd);
+               }
+       }
+}
+
 static void mwifiex_check_next_scan_command(struct mwifiex_private *priv)
 {
        struct mwifiex_adapter *adapter = priv->adapter;
@@ -1770,16 +1764,9 @@ static void mwifiex_check_next_scan_command(struct mwifiex_private *priv)
                adapter->scan_processing = false;
                spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
 
-               /* Need to indicate IOCTL complete */
-               if (adapter->curr_cmd->wait_q_enabled) {
-                       adapter->cmd_wait_q.status = 0;
-                       if (!priv->scan_request) {
-                               dev_dbg(adapter->dev,
-                                       "complete internal scan\n");
-                               mwifiex_complete_cmd(adapter,
-                                                    adapter->curr_cmd);
-                       }
-               }
+               if (!adapter->ext_scan)
+                       mwifiex_complete_scan(priv);
+
                if (priv->report_scan_result)
                        priv->report_scan_result = false;
 
@@ -1984,6 +1971,9 @@ int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv,
 int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv)
 {
        dev_dbg(priv->adapter->dev, "info: EXT scan returns successfully\n");
+
+       mwifiex_complete_scan(priv);
+
        return 0;
 }
 
index d206f04d499498d6d9c7ba92a80685588ae7bc96..4ce3d7b33991ace2cdd3ba0bcfe50c728e59220d 100644 (file)
@@ -85,6 +85,8 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
                card->supports_sdio_new_mode = data->supports_sdio_new_mode;
                card->has_control_mask = data->has_control_mask;
                card->tx_buf_size = data->tx_buf_size;
+               card->mp_tx_agg_buf_size = data->mp_tx_agg_buf_size;
+               card->mp_rx_agg_buf_size = data->mp_rx_agg_buf_size;
        }
 
        sdio_claim_host(func);
@@ -177,9 +179,6 @@ mwifiex_sdio_remove(struct sdio_func *func)
        if (!adapter || !adapter->priv_num)
                return;
 
-       /* In case driver is removed when asynchronous FW load is in progress */
-       wait_for_completion(&adapter->fw_load);
-
        if (user_rmmod) {
                if (adapter->is_suspended)
                        mwifiex_sdio_resume(adapter->dev);
@@ -1679,8 +1678,12 @@ static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter,
        if (ret) {
                if (type == MWIFIEX_TYPE_CMD)
                        adapter->cmd_sent = false;
-               if (type == MWIFIEX_TYPE_DATA)
+               if (type == MWIFIEX_TYPE_DATA) {
                        adapter->data_sent = false;
+                       /* restore curr_wr_port in error cases */
+                       card->curr_wr_port = port;
+                       card->mp_wr_bitmap |= (u32)(1 << card->curr_wr_port);
+               }
        } else {
                if (type == MWIFIEX_TYPE_DATA) {
                        if (!(card->mp_wr_bitmap & (1 << card->curr_wr_port)))
@@ -1842,8 +1845,8 @@ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter)
        card->mpa_rx.len_arr = kzalloc(sizeof(*card->mpa_rx.len_arr) *
                                       card->mp_agg_pkt_limit, GFP_KERNEL);
        ret = mwifiex_alloc_sdio_mpa_buffers(adapter,
-                                            SDIO_MP_TX_AGGR_DEF_BUF_SIZE,
-                                            SDIO_MP_RX_AGGR_DEF_BUF_SIZE);
+                                            card->mp_tx_agg_buf_size,
+                                            card->mp_rx_agg_buf_size);
        if (ret) {
                dev_err(adapter->dev, "failed to alloc sdio mp-a buffers\n");
                kfree(card->mp_regs);
index c71201b2e2a333c20f926bacb4842395230c6926..6eea30b43ed714f3bd81f9453a5008001a11b33d 100644 (file)
 #define UP_LD_CMD_PORT_HOST_INT_STATUS (0x40U)
 #define DN_LD_CMD_PORT_HOST_INT_STATUS (0x80U)
 
-#define SDIO_MP_TX_AGGR_DEF_BUF_SIZE        (8192)     /* 8K */
-
-/* Multi port RX aggregation buffer size */
-#define SDIO_MP_RX_AGGR_DEF_BUF_SIZE        (16384)    /* 16K */
+#define MWIFIEX_MP_AGGR_BUF_SIZE_16K   (16384)
+#define MWIFIEX_MP_AGGR_BUF_SIZE_32K   (32768)
 
 /* Misc. Config Register : Auto Re-enable interrupts */
 #define AUTO_RE_ENABLE_INT              BIT(4)
@@ -234,6 +232,8 @@ struct sdio_mmc_card {
        bool supports_sdio_new_mode;
        bool has_control_mask;
        u16 tx_buf_size;
+       u32 mp_tx_agg_buf_size;
+       u32 mp_rx_agg_buf_size;
 
        u32 mp_rd_bitmap;
        u32 mp_wr_bitmap;
@@ -258,6 +258,8 @@ struct mwifiex_sdio_device {
        bool supports_sdio_new_mode;
        bool has_control_mask;
        u16 tx_buf_size;
+       u32 mp_tx_agg_buf_size;
+       u32 mp_rx_agg_buf_size;
 };
 
 static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = {
@@ -315,6 +317,8 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = {
        .supports_sdio_new_mode = false,
        .has_control_mask = true,
        .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
+       .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
+       .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
 };
 
 static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = {
@@ -325,6 +329,8 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = {
        .supports_sdio_new_mode = false,
        .has_control_mask = true,
        .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
+       .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
+       .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
 };
 
 static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = {
@@ -335,6 +341,8 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = {
        .supports_sdio_new_mode = false,
        .has_control_mask = true,
        .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
+       .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
+       .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
 };
 
 static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
@@ -345,6 +353,8 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
        .supports_sdio_new_mode = true,
        .has_control_mask = false,
        .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K,
+       .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K,
+       .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K,
 };
 
 /*
index e3cac1495cc705bcf3c05b479a16981578deff98..88202ce0c13965fdff679c506a3772ef287e6d54 100644 (file)
@@ -1546,6 +1546,7 @@ mwifiex_cmd_tdls_oper(struct mwifiex_private *priv,
        struct mwifiex_ie_types_extcap *extcap;
        struct mwifiex_ie_types_vhtcap *vht_capab;
        struct mwifiex_ie_types_aid *aid;
+       struct mwifiex_ie_types_tdls_idle_timeout *timeout;
        u8 *pos, qos_info;
        u16 config_len = 0;
        struct station_parameters *params = priv->sta_params;
@@ -1643,6 +1644,12 @@ mwifiex_cmd_tdls_oper(struct mwifiex_private *priv,
                        config_len += sizeof(struct mwifiex_ie_types_aid);
                }
 
+               timeout = (void *)(pos + config_len);
+               timeout->header.type = cpu_to_le16(TLV_TYPE_TDLS_IDLE_TIMEOUT);
+               timeout->header.len = cpu_to_le16(sizeof(timeout->value));
+               timeout->value = cpu_to_le16(MWIFIEX_TDLS_IDLE_TIMEOUT);
+               config_len += sizeof(struct mwifiex_ie_types_tdls_idle_timeout);
+
                break;
        default:
                dev_err(priv->adapter->dev, "Unknown TDLS operation\n");
index bfebb0144df5ac5ae88bd78dd909888d4bb0a0ea..577f2979ed8f2bcacbacc6af6ce9af03d3f86137 100644 (file)
@@ -865,14 +865,20 @@ static int mwifiex_ret_tdls_oper(struct mwifiex_private *priv,
 
        switch (action) {
        case ACT_TDLS_DELETE:
-               if (reason)
-                       dev_err(priv->adapter->dev,
-                               "TDLS link delete for %pM failed: reason %d\n",
-                               cmd_tdls_oper->peer_mac, reason);
-               else
+               if (reason) {
+                       if (!node || reason == TDLS_ERR_LINK_NONEXISTENT)
+                               dev_dbg(priv->adapter->dev,
+                                       "TDLS link delete for %pM failed: reason %d\n",
+                                       cmd_tdls_oper->peer_mac, reason);
+                       else
+                               dev_err(priv->adapter->dev,
+                                       "TDLS link delete for %pM failed: reason %d\n",
+                                       cmd_tdls_oper->peer_mac, reason);
+               } else {
                        dev_dbg(priv->adapter->dev,
-                               "TDLS link config for %pM successful\n",
+                               "TDLS link delete for %pM successful\n",
                                cmd_tdls_oper->peer_mac);
+               }
                break;
        case ACT_TDLS_CREATE:
                if (reason) {
index 368450cc56c7d9e19b8c2a74c47d43a9c5c91b1f..f6395ef11a721b8fc6d8ee797fb34a72b7c2f43d 100644 (file)
@@ -134,6 +134,46 @@ mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code)
                netif_carrier_off(priv->netdev);
 }
 
+static int mwifiex_parse_tdls_event(struct mwifiex_private *priv,
+                                   struct sk_buff *event_skb)
+{
+       int ret = 0;
+       struct mwifiex_adapter *adapter = priv->adapter;
+       struct mwifiex_sta_node *sta_ptr;
+       struct mwifiex_tdls_generic_event *tdls_evt =
+                       (void *)event_skb->data + sizeof(adapter->event_cause);
+
+       /* reserved 2 bytes are not mandatory in tdls event */
+       if (event_skb->len < (sizeof(struct mwifiex_tdls_generic_event) -
+                             sizeof(u16) - sizeof(adapter->event_cause))) {
+               dev_err(adapter->dev, "Invalid event length!\n");
+               return -1;
+       }
+
+       sta_ptr = mwifiex_get_sta_entry(priv, tdls_evt->peer_mac);
+       if (!sta_ptr) {
+               dev_err(adapter->dev, "cannot get sta entry!\n");
+               return -1;
+       }
+
+       switch (le16_to_cpu(tdls_evt->type)) {
+       case TDLS_EVENT_LINK_TEAR_DOWN:
+               cfg80211_tdls_oper_request(priv->netdev,
+                                          tdls_evt->peer_mac,
+                                          NL80211_TDLS_TEARDOWN,
+                                          le16_to_cpu(tdls_evt->u.reason_code),
+                                          GFP_KERNEL);
+               ret = mwifiex_tdls_oper(priv, tdls_evt->peer_mac,
+                                       MWIFIEX_TDLS_DISABLE_LINK);
+               queue_work(adapter->workqueue, &adapter->main_work);
+               break;
+       default:
+               break;
+       }
+
+       return ret;
+}
+
 /*
  * This function handles events generated by firmware.
  *
@@ -459,6 +499,10 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
                        false);
                break;
 
+       case EVENT_TDLS_GENERIC_EVENT:
+               ret = mwifiex_parse_tdls_event(priv, adapter->event_skb);
+               break;
+
        default:
                dev_dbg(adapter->dev, "event: unknown event id: %#x\n",
                        eventcause);
index ed26387eccf56db59bca98f7ed6fd7e6a4065aae..8b639d7fe6df263814901554a2c43363be6791e9 100644 (file)
@@ -183,6 +183,7 @@ int mwifiex_process_sta_rx_packet(struct mwifiex_private *priv,
        struct rx_packet_hdr *rx_pkt_hdr;
        u8 ta[ETH_ALEN];
        u16 rx_pkt_type, rx_pkt_offset, rx_pkt_length, seq_num;
+       struct mwifiex_sta_node *sta_ptr;
 
        local_rx_pd = (struct rxpd *) (skb->data);
        rx_pkt_type = le16_to_cpu(local_rx_pd->rx_pkt_type);
@@ -213,14 +214,25 @@ int mwifiex_process_sta_rx_packet(struct mwifiex_private *priv,
         * If the packet is not an unicast packet then send the packet
         * directly to os. Don't pass thru rx reordering
         */
-       if (!IS_11N_ENABLED(priv) ||
+       if ((!IS_11N_ENABLED(priv) &&
+            !(ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+              !(local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET))) ||
            !ether_addr_equal_unaligned(priv->curr_addr, rx_pkt_hdr->eth803_hdr.h_dest)) {
                mwifiex_process_rx_packet(priv, skb);
                return ret;
        }
 
-       if (mwifiex_queuing_ra_based(priv)) {
+       if (mwifiex_queuing_ra_based(priv) ||
+           (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+            local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET)) {
                memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN);
+               if (local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET &&
+                   local_rx_pd->priority < MAX_NUM_TID) {
+                       sta_ptr = mwifiex_get_sta_entry(priv, ta);
+                       if (sta_ptr)
+                               sta_ptr->rx_seq[local_rx_pd->priority] =
+                                             le16_to_cpu(local_rx_pd->seq_num);
+               }
        } else {
                if (rx_pkt_type != PKT_TYPE_BAR)
                        priv->rx_seq[local_rx_pd->priority] = seq_num;
index 1236a5de7bca833adfd0eab1ed6cce865047479b..5fce7e78a36e773c28875a7636a666b50ced36d5 100644 (file)
@@ -128,6 +128,7 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags)
 {
        struct mwifiex_adapter *adapter = priv->adapter;
        struct txpd *local_tx_pd;
+       struct mwifiex_tx_param tx_param;
 /* sizeof(struct txpd) + Interface specific header */
 #define NULL_PACKET_HDR 64
        u32 data_len = NULL_PACKET_HDR;
@@ -168,8 +169,9 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags)
                                                   skb, NULL);
        } else {
                skb_push(skb, INTF_HEADER_LEN);
+               tx_param.next_pkt_len = 0;
                ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA,
-                                                  skb, NULL);
+                                                  skb, &tx_param);
        }
        switch (ret) {
        case -EBUSY:
index 97662a1ba58cf06a08ff3372f4808e5a0c7cbcd9..e73034fbbde9263b8e234ee7cd7747a1404c8a40 100644 (file)
@@ -25,8 +25,8 @@
 #define TDLS_RESP_FIX_LEN     8
 #define TDLS_CONFIRM_FIX_LEN  6
 
-static void
-mwifiex_restore_tdls_packets(struct mwifiex_private *priv, u8 *mac, u8 status)
+static void mwifiex_restore_tdls_packets(struct mwifiex_private *priv,
+                                        const u8 *mac, u8 status)
 {
        struct mwifiex_ra_list_tbl *ra_list;
        struct list_head *tid_list;
@@ -84,7 +84,8 @@ mwifiex_restore_tdls_packets(struct mwifiex_private *priv, u8 *mac, u8 status)
        return;
 }
 
-static void mwifiex_hold_tdls_packets(struct mwifiex_private *priv, u8 *mac)
+static void mwifiex_hold_tdls_packets(struct mwifiex_private *priv,
+                                     const u8 *mac)
 {
        struct mwifiex_ra_list_tbl *ra_list;
        struct list_head *ra_list_head;
@@ -185,8 +186,50 @@ static int mwifiex_tdls_add_vht_capab(struct mwifiex_private *priv,
        return 0;
 }
 
+static int
+mwifiex_tdls_add_ht_oper(struct mwifiex_private *priv, const u8 *mac,
+                        u8 vht_enabled, struct sk_buff *skb)
+{
+       struct ieee80211_ht_operation *ht_oper;
+       struct mwifiex_sta_node *sta_ptr;
+       struct mwifiex_bssdescriptor *bss_desc =
+                                       &priv->curr_bss_params.bss_descriptor;
+       u8 *pos;
+
+       sta_ptr = mwifiex_get_sta_entry(priv, mac);
+       if (unlikely(!sta_ptr)) {
+               dev_warn(priv->adapter->dev,
+                        "TDLS peer station not found in list\n");
+               return -1;
+       }
+
+       pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_operation) + 2);
+       *pos++ = WLAN_EID_HT_OPERATION;
+       *pos++ = sizeof(struct ieee80211_ht_operation);
+       ht_oper = (void *)pos;
+
+       ht_oper->primary_chan = bss_desc->channel;
+
+       /* follow AP's channel bandwidth */
+       if (ISSUPP_CHANWIDTH40(priv->adapter->hw_dot_11n_dev_cap) &&
+           bss_desc->bcn_ht_cap &&
+           ISALLOWED_CHANWIDTH40(bss_desc->bcn_ht_oper->ht_param))
+               ht_oper->ht_param = bss_desc->bcn_ht_oper->ht_param;
+
+       if (vht_enabled) {
+               ht_oper->ht_param =
+                         mwifiex_get_sec_chan_offset(bss_desc->channel);
+               ht_oper->ht_param |= BIT(2);
+       }
+
+       memcpy(&sta_ptr->tdls_cap.ht_oper, ht_oper,
+              sizeof(struct ieee80211_ht_operation));
+
+       return 0;
+}
+
 static int mwifiex_tdls_add_vht_oper(struct mwifiex_private *priv,
-                                    u8 *mac, struct sk_buff *skb)
+                                    const u8 *mac, struct sk_buff *skb)
 {
        struct mwifiex_bssdescriptor *bss_desc;
        struct ieee80211_vht_operation *vht_oper;
@@ -325,8 +368,9 @@ static void mwifiex_tdls_add_qos_capab(struct sk_buff *skb)
 }
 
 static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv,
-                            u8 *peer, u8 action_code, u8 dialog_token,
-                            u16 status_code, struct sk_buff *skb)
+                                       const u8 *peer, u8 action_code,
+                                       u8 dialog_token,
+                                       u16 status_code, struct sk_buff *skb)
 {
        struct ieee80211_tdls_data *tf;
        int ret;
@@ -428,6 +472,17 @@ static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv,
                                dev_kfree_skb_any(skb);
                                return ret;
                        }
+                       ret = mwifiex_tdls_add_ht_oper(priv, peer, 1, skb);
+                       if (ret) {
+                               dev_kfree_skb_any(skb);
+                               return ret;
+                       }
+               } else {
+                       ret = mwifiex_tdls_add_ht_oper(priv, peer, 0, skb);
+                       if (ret) {
+                               dev_kfree_skb_any(skb);
+                               return ret;
+                       }
                }
                break;
 
@@ -453,7 +508,8 @@ static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv,
 }
 
 static void
-mwifiex_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, u8 *peer, u8 *bssid)
+mwifiex_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr,
+                        const u8 *peer, const u8 *bssid)
 {
        struct ieee80211_tdls_lnkie *lnkid;
 
@@ -467,8 +523,8 @@ mwifiex_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, u8 *peer, u8 *bssid)
        memcpy(lnkid->resp_sta, peer, ETH_ALEN);
 }
 
-int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv,
-                                u8 *peer, u8 action_code, u8 dialog_token,
+int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer,
+                                u8 action_code, u8 dialog_token,
                                 u16 status_code, const u8 *extra_ies,
                                 size_t extra_ies_len)
 {
@@ -560,7 +616,8 @@ int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv,
 }
 
 static int
-mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, u8 *peer,
+mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv,
+                                   const u8 *peer,
                                    u8 action_code, u8 dialog_token,
                                    u16 status_code, struct sk_buff *skb)
 {
@@ -638,10 +695,10 @@ mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, u8 *peer,
        return 0;
 }
 
-int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv,
-                                u8 *peer, u8 action_code, u8 dialog_token,
-                                u16 status_code, const u8 *extra_ies,
-                                size_t extra_ies_len)
+int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer,
+                                  u8 action_code, u8 dialog_token,
+                                  u16 status_code, const u8 *extra_ies,
+                                  size_t extra_ies_len)
 {
        struct sk_buff *skb;
        struct mwifiex_txinfo *tx_info;
@@ -848,7 +905,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv,
 }
 
 static int
-mwifiex_tdls_process_config_link(struct mwifiex_private *priv, u8 *peer)
+mwifiex_tdls_process_config_link(struct mwifiex_private *priv, const u8 *peer)
 {
        struct mwifiex_sta_node *sta_ptr;
        struct mwifiex_ds_tdls_oper tdls_oper;
@@ -869,7 +926,7 @@ mwifiex_tdls_process_config_link(struct mwifiex_private *priv, u8 *peer)
 }
 
 static int
-mwifiex_tdls_process_create_link(struct mwifiex_private *priv, u8 *peer)
+mwifiex_tdls_process_create_link(struct mwifiex_private *priv, const u8 *peer)
 {
        struct mwifiex_sta_node *sta_ptr;
        struct mwifiex_ds_tdls_oper tdls_oper;
@@ -896,7 +953,7 @@ mwifiex_tdls_process_create_link(struct mwifiex_private *priv, u8 *peer)
 }
 
 static int
-mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, u8 *peer)
+mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, const u8 *peer)
 {
        struct mwifiex_sta_node *sta_ptr;
        struct mwifiex_ds_tdls_oper tdls_oper;
@@ -925,7 +982,7 @@ mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, u8 *peer)
 }
 
 static int
-mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, u8 *peer)
+mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, const u8 *peer)
 {
        struct mwifiex_sta_node *sta_ptr;
        struct ieee80211_mcs_info mcs;
@@ -982,7 +1039,7 @@ mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, u8 *peer)
        return 0;
 }
 
-int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action)
+int mwifiex_tdls_oper(struct mwifiex_private *priv, const u8 *peer, u8 action)
 {
        switch (action) {
        case MWIFIEX_TDLS_ENABLE_LINK:
@@ -997,7 +1054,7 @@ int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action)
        return 0;
 }
 
-int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, u8 *mac)
+int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac)
 {
        struct mwifiex_sta_node *sta_ptr;
 
index 9be6544bddedf9371e79fd468ebb0aa2dbcabe54..32643555dd2a32a302d1301427e463d877c8260a 100644 (file)
@@ -175,17 +175,19 @@ mwifiex_set_ht_params(struct mwifiex_private *priv,
                switch (GET_RXSTBC(cap_info)) {
                case MWIFIEX_RX_STBC1:
                        /* HT_CAP 1X1 mode */
-                       memset(&bss_cfg->ht_cap.mcs, 0xff, 1);
+                       bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff;
                        break;
                case MWIFIEX_RX_STBC12: /* fall through */
                case MWIFIEX_RX_STBC123:
                        /* HT_CAP 2X2 mode */
-                       memset(&bss_cfg->ht_cap.mcs, 0xff, 2);
+                       bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff;
+                       bss_cfg->ht_cap.mcs.rx_mask[1] = 0xff;
                        break;
                default:
                        dev_warn(priv->adapter->dev,
                                 "Unsupported RX-STBC, default to 2x2\n");
-                       memset(&bss_cfg->ht_cap.mcs, 0xff, 2);
+                       bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff;
+                       bss_cfg->ht_cap.mcs.rx_mask[1] = 0xff;
                        break;
                }
                priv->ap_11n_enabled = 1;
index edbe4aff00d85b569534372ea34e7e017552b234..a8ce8130cfaeeda08a2a08f7b693540fe79d9f85 100644 (file)
@@ -22,9 +22,9 @@
 
 #define USB_VERSION    "1.0"
 
+static u8 user_rmmod;
 static struct mwifiex_if_ops usb_ops;
 static struct semaphore add_remove_card_sem;
-static struct usb_card_rec *usb_card;
 
 static struct usb_device_id mwifiex_usb_table[] = {
        /* 8797 */
@@ -532,28 +532,38 @@ static int mwifiex_usb_resume(struct usb_interface *intf)
 static void mwifiex_usb_disconnect(struct usb_interface *intf)
 {
        struct usb_card_rec *card = usb_get_intfdata(intf);
+       struct mwifiex_adapter *adapter;
 
-       if (!card) {
-               pr_err("%s: card is NULL\n", __func__);
+       if (!card || !card->adapter) {
+               pr_err("%s: card or card->adapter is NULL\n", __func__);
                return;
        }
 
-       mwifiex_usb_free(card);
+       adapter = card->adapter;
+       if (!adapter->priv_num)
+               return;
 
-       if (card->adapter) {
-               struct mwifiex_adapter *adapter = card->adapter;
+       if (user_rmmod) {
+#ifdef CONFIG_PM
+               if (adapter->is_suspended)
+                       mwifiex_usb_resume(intf);
+#endif
 
-               if (!adapter->priv_num)
-                       return;
+               mwifiex_deauthenticate_all(adapter);
 
-               dev_dbg(adapter->dev, "%s: removing card\n", __func__);
-               mwifiex_remove_card(adapter, &add_remove_card_sem);
+               mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter,
+                                                         MWIFIEX_BSS_ROLE_ANY),
+                                        MWIFIEX_FUNC_SHUTDOWN);
        }
 
+       mwifiex_usb_free(card);
+
+       dev_dbg(adapter->dev, "%s: removing card\n", __func__);
+       mwifiex_remove_card(adapter, &add_remove_card_sem);
+
        usb_set_intfdata(intf, NULL);
        usb_put_dev(interface_to_usbdev(intf));
        kfree(card);
-       usb_card = NULL;
 
        return;
 }
@@ -565,6 +575,7 @@ static struct usb_driver mwifiex_usb_driver = {
        .id_table = mwifiex_usb_table,
        .suspend = mwifiex_usb_suspend,
        .resume = mwifiex_usb_resume,
+       .soft_unbind = 1,
 };
 
 static int mwifiex_usb_tx_init(struct mwifiex_adapter *adapter)
@@ -762,7 +773,6 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
 
        card->adapter = adapter;
        adapter->dev = &card->udev->dev;
-       usb_card = card;
 
        switch (le16_to_cpu(card->udev->descriptor.idProduct)) {
        case USB8897_PID_1:
@@ -1025,25 +1035,8 @@ static void mwifiex_usb_cleanup_module(void)
        if (!down_interruptible(&add_remove_card_sem))
                up(&add_remove_card_sem);
 
-       if (usb_card && usb_card->adapter) {
-               struct mwifiex_adapter *adapter = usb_card->adapter;
-
-               /* In case driver is removed when asynchronous FW downloading is
-                * in progress
-                */
-               wait_for_completion(&adapter->fw_load);
-
-#ifdef CONFIG_PM
-               if (adapter->is_suspended)
-                       mwifiex_usb_resume(usb_card->intf);
-#endif
-
-               mwifiex_deauthenticate_all(adapter);
-
-               mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter,
-                                                         MWIFIEX_BSS_ROLE_ANY),
-                                        MWIFIEX_FUNC_SHUTDOWN);
-       }
+       /* set the flag as user is removing this module */
+       user_rmmod = 1;
 
        usb_deregister(&mwifiex_usb_driver);
 }
index c3824e37f3f24a30d951510a75cd4640346620d8..6da5abf52e61a4360b236411b09310205e8316ea 100644 (file)
@@ -259,7 +259,7 @@ int mwifiex_complete_cmd(struct mwifiex_adapter *adapter,
  * NULL is returned if station entry is not found in associated STA list.
  */
 struct mwifiex_sta_node *
-mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac)
+mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac)
 {
        struct mwifiex_sta_node *node;
 
@@ -280,7 +280,7 @@ mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac)
  * If received mac address is NULL, NULL is returned.
  */
 struct mwifiex_sta_node *
-mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac)
+mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac)
 {
        struct mwifiex_sta_node *node;
        unsigned long flags;
@@ -332,7 +332,7 @@ mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies,
 }
 
 /* This function will delete a station entry from station list */
-void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac)
+void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac)
 {
        struct mwifiex_sta_node *node;
        unsigned long flags;
index 0a7cc742aed71e0fd31267305be7a26ec71b8b52..d3671d009f6c3c89c4f6e91e06220edd2c481da9 100644 (file)
@@ -92,7 +92,7 @@ mwifiex_wmm_ac_debug_print(const struct ieee_types_wmm_ac_parameters *ac_param)
  * The function also initializes the list with the provided RA.
  */
 static struct mwifiex_ra_list_tbl *
-mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, u8 *ra)
+mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, const u8 *ra)
 {
        struct mwifiex_ra_list_tbl *ra_list;
 
@@ -139,8 +139,7 @@ static u8 mwifiex_get_random_ba_threshold(void)
  * This function allocates and adds a RA list for all TIDs
  * with the given RA.
  */
-void
-mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra)
+void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra)
 {
        int i;
        struct mwifiex_ra_list_tbl *ra_list;
@@ -164,6 +163,7 @@ mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra)
                if (!mwifiex_queuing_ra_based(priv)) {
                        if (mwifiex_get_tdls_link_status(priv, ra) ==
                            TDLS_SETUP_COMPLETE) {
+                               ra_list->tdls_link = true;
                                ra_list->is_11n_enabled =
                                        mwifiex_tdls_peer_11n_enabled(priv, ra);
                        } else {
@@ -426,15 +426,6 @@ mwifiex_wmm_init(struct mwifiex_adapter *adapter)
                                                        priv->tos_to_tid_inv[i];
                }
 
-               priv->aggr_prio_tbl[6].amsdu
-                                       = priv->aggr_prio_tbl[6].ampdu_ap
-                                       = priv->aggr_prio_tbl[6].ampdu_user
-                                       = BA_STREAM_NOT_ALLOWED;
-
-               priv->aggr_prio_tbl[7].amsdu = priv->aggr_prio_tbl[7].ampdu_ap
-                                       = priv->aggr_prio_tbl[7].ampdu_user
-                                       = BA_STREAM_NOT_ALLOWED;
-
                mwifiex_set_ba_params(priv);
                mwifiex_reset_11n_rx_seq_num(priv);
 
@@ -575,7 +566,7 @@ mwifiex_clean_txrx(struct mwifiex_private *priv)
  */
 static struct mwifiex_ra_list_tbl *
 mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid,
-                           u8 *ra_addr)
+                           const u8 *ra_addr)
 {
        struct mwifiex_ra_list_tbl *ra_list;
 
@@ -596,7 +587,8 @@ mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid,
  * retrieved.
  */
 struct mwifiex_ra_list_tbl *
-mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, u8 *ra_addr)
+mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid,
+                           const u8 *ra_addr)
 {
        struct mwifiex_ra_list_tbl *ra_list;
 
@@ -657,7 +649,7 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv,
                if (ntohs(eth_hdr->h_proto) == ETH_P_TDLS)
                        dev_dbg(adapter->dev,
                                "TDLS setup packet for %pM. Don't block\n", ra);
-               else
+               else if (memcmp(priv->cfg_bssid, ra, ETH_ALEN))
                        tdls_status = mwifiex_get_tdls_link_status(priv, ra);
        }
 
index 83e42083ebff8cbc3b248f333111691e8a21fc1d..eca56e371a57bb5afb2df6165c24fbbc4d34e561 100644 (file)
@@ -99,7 +99,7 @@ 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);
-void mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra);
+void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra);
 void mwifiex_rotate_priolists(struct mwifiex_private *priv,
                              struct mwifiex_ra_list_tbl *ra, int tid);
 
@@ -123,7 +123,8 @@ void mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv);
 int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv,
                               const struct host_cmd_ds_command *resp);
 struct mwifiex_ra_list_tbl *
-mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, u8 *ra_addr);
+mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid,
+                           const u8 *ra_addr);
 u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid);
 
 #endif /* !_MWIFIEX_WMM_H_ */
index 49300d04efdf0e7221a2b0f64f0475ad6f73ee59..e27e32851f1e50f8116c14c55f3bd2f405aee17f 100644 (file)
@@ -988,8 +988,8 @@ int __orinoco_hw_setup_enc(struct orinoco_private *priv)
  * tsc must be NULL or up to 8 bytes
  */
 int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx,
-                             int set_tx, u8 *key, u8 *rsc, size_t rsc_len,
-                             u8 *tsc, size_t tsc_len)
+                             int set_tx, const u8 *key, const u8 *rsc,
+                             size_t rsc_len, const u8 *tsc, size_t tsc_len)
 {
        struct {
                __le16 idx;
index 8f6831f4e328a8b5761611a6c79965ccdfa372ac..466d1ede76f16ee5436b680631e77047af2798af 100644 (file)
@@ -38,8 +38,8 @@ int __orinoco_hw_set_wap(struct orinoco_private *priv);
 int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv);
 int __orinoco_hw_setup_enc(struct orinoco_private *priv);
 int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx,
-                             int set_tx, u8 *key, u8 *rsc, size_t rsc_len,
-                             u8 *tsc, size_t tsc_len);
+                             int set_tx, const u8 *key, const u8 *rsc,
+                             size_t rsc_len, const u8 *tsc, size_t tsc_len);
 int orinoco_clear_tkip_key(struct orinoco_private *priv, int key_idx);
 int __orinoco_hw_set_multicast_list(struct orinoco_private *priv,
                                    struct net_device *dev,
index 3ac71339d040a418465e0fd0ecaeb0a2066ddb3a..c90939ced0e483ae04c490c60a5f18eabbea1b9f 100644 (file)
@@ -1673,7 +1673,7 @@ static int ezusb_probe(struct usb_interface *interface,
                firmware.code = fw_entry->data;
        }
        if (firmware.size && firmware.code) {
-               if (ezusb_firmware_download(upriv, &firmware))
+               if (ezusb_firmware_download(upriv, &firmware) < 0)
                        goto error;
        } else {
                err("No firmware to download");
index b7a867b50b9476fb2c54ad9ccbb28815e6fa70e6..6abdaf0aa052253800697eb1631d100683ba2ec7 100644 (file)
@@ -52,9 +52,9 @@ static int orinoco_set_key(struct orinoco_private *priv, int index,
        priv->keys[index].seq_len = seq_len;
 
        if (key_len)
-               memcpy(priv->keys[index].key, key, key_len);
+               memcpy((void *)priv->keys[index].key, key, key_len);
        if (seq_len)
-               memcpy(priv->keys[index].seq, seq, seq_len);
+               memcpy((void *)priv->keys[index].seq, seq, seq_len);
 
        switch (alg) {
        case ORINOCO_ALG_TKIP:
index eede90b63f847934a8a0bc45e63695696d88a82d..7be3a4839640c6eda6b8254fd7b81bbe5d01fd33 100644 (file)
@@ -669,7 +669,8 @@ static unsigned int p54_flush_count(struct p54_common *priv)
        return total;
 }
 
-static void p54_flush(struct ieee80211_hw *dev, u32 queues, bool drop)
+static void p54_flush(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+                     u32 queues, bool drop)
 {
        struct p54_common *priv = dev->priv;
        unsigned int total, i;
index cbf0a589d32af08c392271c7bbc3afa50f8e9c84..8330fa33e50b1e2f933f813ee187c407184780ae 100644 (file)
@@ -343,7 +343,7 @@ static void ray_detach(struct pcmcia_device *link)
        ray_release(link);
 
        local = netdev_priv(dev);
-       del_timer(&local->timer);
+       del_timer_sync(&local->timer);
 
        if (link->priv) {
                unregister_netdev(dev);
index 39d22a154341019fe8038bdfa1f7e1b4cb560cf2..d2a9a08210be1379b4e56ad266c1695ca6486d17 100644 (file)
@@ -517,7 +517,7 @@ static int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev,
                                 u8 key_index, bool unicast, bool multicast);
 
 static int rndis_get_station(struct wiphy *wiphy, struct net_device *dev,
-                                       u8 *mac, struct station_info *sinfo);
+                            const u8 *mac, struct station_info *sinfo);
 
 static int rndis_dump_station(struct wiphy *wiphy, struct net_device *dev,
                               int idx, u8 *mac, struct station_info *sinfo);
@@ -2490,7 +2490,7 @@ static void rndis_fill_station_info(struct usbnet *usbdev,
 }
 
 static int rndis_get_station(struct wiphy *wiphy, struct net_device *dev,
-                                       u8 *mac, struct station_info *sinfo)
+                            const u8 *mac, struct station_info *sinfo)
 {
        struct rndis_wlan_private *priv = wiphy_priv(wiphy);
        struct usbnet *usbdev = priv->usbdev;
index 84164747ace057ee11be9bc25bf8b63bb30407b2..54aaeb09debf568c9dcad47ae3892153c2d827d6 100644 (file)
@@ -656,6 +656,7 @@ static int rsi_mac80211_ampdu_action(struct ieee80211_hw *hw,
        case IEEE80211_AMPDU_TX_START:
                common->vif_info[ii].seq_start = seq_no;
                ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+               status = 0;
                break;
 
        case IEEE80211_AMPDU_TX_STOP_CONT:
index 1b28cda6ca88124deff6c112a060f5af6692cefa..2eefbf159bc0d0abdcead9ff1caf6b867d95793f 100644 (file)
@@ -1083,7 +1083,7 @@ void rsi_inform_bss_status(struct rsi_common *common,
 {
        if (status) {
                rsi_hal_send_sta_notify_frame(common,
-                                             NL80211_IFTYPE_STATION,
+                                             RSI_IFTYPE_STATION,
                                              STA_CONNECTED,
                                              bssid,
                                              qos_enable,
@@ -1092,7 +1092,7 @@ void rsi_inform_bss_status(struct rsi_common *common,
                        rsi_send_auto_rate_request(common);
        } else {
                rsi_hal_send_sta_notify_frame(common,
-                                             NL80211_IFTYPE_STATION,
+                                             RSI_IFTYPE_STATION,
                                              STA_DISCONNECTED,
                                              bssid,
                                              qos_enable,
index f2f70784d4ade1e3dcccc0d55230385515c4600c..d3fbe33d23244bb37cf287736303365301e2e52e 100644 (file)
@@ -63,7 +63,7 @@ static inline int rsi_create_kthread(struct rsi_common *common,
                                     u8 *name)
 {
        init_completion(&thread->completion);
-       thread->task = kthread_run(func_ptr, common, name);
+       thread->task = kthread_run(func_ptr, common, "%s", name);
        if (IS_ERR(thread->task))
                return (int)PTR_ERR(thread->task);
 
index ac67c4ad63c2d3177e3e70386ff27538b69b536f..225215a3b8bb484d76b47ed853afb3aeb6eb2130 100644 (file)
@@ -73,6 +73,7 @@
 #define RX_BA_INDICATION                1
 #define RSI_TBL_SZ                      40
 #define MAX_RETRIES                     8
+#define RSI_IFTYPE_STATION              0
 
 #define STD_RATE_MCS7                   0x07
 #define STD_RATE_MCS6                   0x06
index 41d4a8167dc32f368a8fdf061bea4fe9944fd0f1..c17fcf272728cb06ae25e95787003f6f59f52dba 100644 (file)
@@ -1005,10 +1005,9 @@ void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
                                   entry->skb->len + padding_len);
 
        /*
-        * Enable beaconing again.
+        * Restore beaconing state.
         */
-       rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 1);
-       rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+       rt2800_register_write(rt2x00dev, BCN_TIME_CFG, orig_reg);
 
        /*
         * Clean up beacon skb.
@@ -1039,13 +1038,14 @@ static inline void rt2800_clear_beacon_register(struct rt2x00_dev *rt2x00dev,
 void rt2800_clear_beacon(struct queue_entry *entry)
 {
        struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
-       u32 reg;
+       u32 orig_reg, reg;
 
        /*
         * Disable beaconing while we are reloading the beacon data,
         * otherwise we might be sending out invalid data.
         */
-       rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
+       rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &orig_reg);
+       reg = orig_reg;
        rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 0);
        rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
 
@@ -1055,10 +1055,9 @@ void rt2800_clear_beacon(struct queue_entry *entry)
        rt2800_clear_beacon_register(rt2x00dev, entry->entry_idx);
 
        /*
-        * Enabled beaconing again.
+        * Restore beaconing state.
         */
-       rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 1);
-       rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+       rt2800_register_write(rt2x00dev, BCN_TIME_CFG, orig_reg);
 }
 EXPORT_SYMBOL_GPL(rt2800_clear_beacon);
 
index e3b885d8f7dbfddda2f4ae71161b24edeefdc02c..010b76505243ed1cf15d1f176033cabd5ac23f3d 100644 (file)
@@ -1448,7 +1448,8 @@ int rt2x00mac_conf_tx(struct ieee80211_hw *hw,
                      struct ieee80211_vif *vif, u16 queue,
                      const struct ieee80211_tx_queue_params *params);
 void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw);
-void rt2x00mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop);
+void rt2x00mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                    u32 queues, bool drop);
 int rt2x00mac_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant);
 int rt2x00mac_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant);
 void rt2x00mac_get_ringparam(struct ieee80211_hw *hw,
index a87ee9b6585a72cdad0394d773b17a69a8b80298..212ac4842c1628a0d141104188626d55c616c487 100644 (file)
@@ -749,7 +749,8 @@ void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw)
 }
 EXPORT_SYMBOL_GPL(rt2x00mac_rfkill_poll);
 
-void rt2x00mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+void rt2x00mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                    u32 queues, bool drop)
 {
        struct rt2x00_dev *rt2x00dev = hw->priv;
        struct data_queue *queue;
index 10572452cc21b76cc8fe4457298ea06bc3c875be..86c43d112a4b7d4f25bfbf123b7cbbfb98bd75b9 100644 (file)
@@ -68,6 +68,12 @@ int rt2x00usb_vendor_request(struct rt2x00_dev *rt2x00dev,
                }
        }
 
+       /* If the port is powered down, we get a -EPROTO error, and this
+        * leads to a endless loop. So just say that the device is gone.
+        */
+       if (status == -EPROTO)
+               clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags);
+
        rt2x00_err(rt2x00dev,
                   "Vendor Request 0x%02x failed for offset 0x%04x with error %d\n",
                   request, offset, status);
index 24402984ee5749f272609d82907cda4a68f750f6..9048a9cbe52cb929cfbd60797a00baa40c5d6583 100644 (file)
@@ -2031,13 +2031,14 @@ static void rt61pci_write_beacon(struct queue_entry *entry,
 static void rt61pci_clear_beacon(struct queue_entry *entry)
 {
        struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
-       u32 reg;
+       u32 orig_reg, reg;
 
        /*
         * Disable beaconing while we are reloading the beacon data,
         * otherwise we might be sending out invalid data.
         */
-       rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, &reg);
+       rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, &orig_reg);
+       reg = orig_reg;
        rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_GEN, 0);
        rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg);
 
@@ -2048,10 +2049,9 @@ static void rt61pci_clear_beacon(struct queue_entry *entry)
                                  HW_BEACON_OFFSET(entry->entry_idx), 0);
 
        /*
-        * Enable beaconing again.
+        * Restore global beaconing state.
         */
-       rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_GEN, 1);
-       rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg);
+       rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, orig_reg);
 }
 
 /*
index a140170b1eb3e63625ecde7b4cc43ec6bf1b87b1..95724ff9c7268700628866f433b66e82a8a9f7c4 100644 (file)
@@ -1597,13 +1597,14 @@ static void rt73usb_clear_beacon(struct queue_entry *entry)
 {
        struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
        unsigned int beacon_base;
-       u32 reg;
+       u32 orig_reg, reg;
 
        /*
         * Disable beaconing while we are reloading the beacon data,
         * otherwise we might be sending out invalid data.
         */
-       rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, &reg);
+       rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, &orig_reg);
+       reg = orig_reg;
        rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_GEN, 0);
        rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg);
 
@@ -1614,10 +1615,9 @@ static void rt73usb_clear_beacon(struct queue_entry *entry)
        rt2x00usb_register_write(rt2x00dev, beacon_base, 0);
 
        /*
-        * Enable beaconing again.
+        * Restore beaconing state.
         */
-       rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_GEN, 1);
-       rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg);
+       rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, orig_reg);
 }
 
 static int rt73usb_get_tx_data_len(struct queue_entry *entry)
index 08b056db4a3b795282d1d9e878ab400d7090bcf2..21005bd8b43c973da6ebd24556da848032727bcf 100644 (file)
@@ -1,5 +1,5 @@
-rtl8180-objs           := dev.o rtl8225.o sa2400.o max2820.o grf5101.o rtl8225se.o
+rtl818x_pci-objs       := dev.o rtl8225.o sa2400.o max2820.o grf5101.o rtl8225se.o
 
-obj-$(CONFIG_RTL8180)  += rtl8180.o
+obj-$(CONFIG_RTL8180)  += rtl818x_pci.o
 
 ccflags-y += -Idrivers/net/wireless/rtl818x
index 98d8256f037788a4d9af76c02a4e939758a08e0e..2c1c02bafa10bbfe1f198279400a84ad08256bb4 100644 (file)
@@ -284,6 +284,8 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev)
                        rx_status.band = dev->conf.chandef.chan->band;
                        rx_status.mactime = tsft;
                        rx_status.flag |= RX_FLAG_MACTIME_START;
+                       if (flags & RTL818X_RX_DESC_FLAG_SPLCP)
+                               rx_status.flag |= RX_FLAG_SHORTPRE;
                        if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR)
                                rx_status.flag |= RX_FLAG_FAILED_FCS_CRC;
 
@@ -461,18 +463,23 @@ static void rtl8180_tx(struct ieee80211_hw *dev,
                            RTL818X_TX_DESC_FLAG_NO_ENC;
 
        rc_flags = info->control.rates[0].flags;
+
+       /* HW will perform RTS-CTS when only RTS flags is set.
+        * HW will perform CTS-to-self when both RTS and CTS flags are set.
+        * RTS rate and RTS duration will be used also for CTS-to-self.
+        */
        if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) {
                tx_flags |= RTL818X_TX_DESC_FLAG_RTS;
                tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19;
+               rts_duration = ieee80211_rts_duration(dev, priv->vif,
+                                               skb->len, info);
        } else if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
-               tx_flags |= RTL818X_TX_DESC_FLAG_CTS;
+               tx_flags |= RTL818X_TX_DESC_FLAG_RTS | RTL818X_TX_DESC_FLAG_CTS;
                tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19;
+               rts_duration = ieee80211_ctstoself_duration(dev, priv->vif,
+                                               skb->len, info);
        }
 
-       if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS)
-               rts_duration = ieee80211_rts_duration(dev, priv->vif, skb->len,
-                                                     info);
-
        if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8180) {
                unsigned int remainder;
 
@@ -683,9 +690,8 @@ static void rtl8180_int_enable(struct ieee80211_hw *dev)
        struct rtl8180_priv *priv = dev->priv;
 
        if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) {
-               rtl818x_iowrite32(priv, &priv->map->IMR, IMR_TMGDOK |
-                         IMR_TBDER | IMR_THPDER |
-                         IMR_THPDER | IMR_THPDOK |
+               rtl818x_iowrite32(priv, &priv->map->IMR,
+                         IMR_TBDER | IMR_TBDOK |
                          IMR_TVODER | IMR_TVODOK |
                          IMR_TVIDER | IMR_TVIDOK |
                          IMR_TBEDER | IMR_TBEDOK |
@@ -911,7 +917,10 @@ static int rtl8180_init_hw(struct ieee80211_hw *dev)
                reg32 &= 0x00ffff00;
                reg32 |= 0xb8000054;
                rtl818x_iowrite32(priv, &priv->map->RF_PARA, reg32);
-       }
+       } else
+               /* stop unused queus (no dma alloc) */
+               rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING,
+                           (1<<1) | (1<<2));
 
        priv->rf->init(dev);
 
index 0ca17cda48fa1c01b3a8dd2ed98c5fe0c5646640..629ad8cfa17b59bf4b5f8b11202602e34c9cce33 100644 (file)
@@ -253,14 +253,21 @@ static void rtl8187_tx(struct ieee80211_hw *dev,
        flags |= ieee80211_get_tx_rate(dev, info)->hw_value << 24;
        if (ieee80211_has_morefrags(tx_hdr->frame_control))
                flags |= RTL818X_TX_DESC_FLAG_MOREFRAG;
+
+       /* HW will perform RTS-CTS when only RTS flags is set.
+        * HW will perform CTS-to-self when both RTS and CTS flags are set.
+        * RTS rate and RTS duration will be used also for CTS-to-self.
+        */
        if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) {
                flags |= RTL818X_TX_DESC_FLAG_RTS;
                flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19;
                rts_dur = ieee80211_rts_duration(dev, priv->vif,
                                                 skb->len, info);
        } else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
-               flags |= RTL818X_TX_DESC_FLAG_CTS;
+               flags |= RTL818X_TX_DESC_FLAG_RTS | RTL818X_TX_DESC_FLAG_CTS;
                flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19;
+               rts_dur = ieee80211_ctstoself_duration(dev, priv->vif,
+                                                skb->len, info);
        }
 
        if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
@@ -381,6 +388,8 @@ static void rtl8187_rx_cb(struct urb *urb)
        rx_status.freq = dev->conf.chandef.chan->center_freq;
        rx_status.band = dev->conf.chandef.chan->band;
        rx_status.flag |= RX_FLAG_MACTIME_START;
+       if (flags & RTL818X_RX_DESC_FLAG_SPLCP)
+               rx_status.flag |= RX_FLAG_SHORTPRE;
        if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR)
                rx_status.flag |= RX_FLAG_FAILED_FCS_CRC;
        memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
index 45ea4e1c4abe157ad952be2d8a5022efc1e1715a..7abef95d278bc61336d2829f40520afc1af81a1a 100644 (file)
@@ -334,9 +334,9 @@ struct rtl818x_csr {
  * I don't like to introduce a ton of "reserved"..
  * They are for RTL8187SE
  */
-#define REG_ADDR1(addr)        ((u8 __iomem *)priv->map + addr)
-#define REG_ADDR2(addr)        ((__le16 __iomem *)priv->map + (addr >> 1))
-#define REG_ADDR4(addr)        ((__le32 __iomem *)priv->map + (addr >> 2))
+#define REG_ADDR1(addr)        ((u8 __iomem *)priv->map + (addr))
+#define REG_ADDR2(addr)        ((__le16 __iomem *)priv->map + ((addr) >> 1))
+#define REG_ADDR4(addr)        ((__le32 __iomem *)priv->map + ((addr) >> 2))
 
 #define FEMR_SE                REG_ADDR2(0x1D4)
 #define ARFR           REG_ADDR2(0x1E0)
index 4ec424f26672028550ab8b19b944d451766ee08c..b1ed6d0796f67e187fb928423edda6977c91f863 100644 (file)
@@ -1387,7 +1387,8 @@ static void rtl_op_rfkill_poll(struct ieee80211_hw *hw)
  * before switch channel or power save, or tx buffer packet
  * maybe send after offchannel or rf sleep, this may cause
  * dis-association by AP */
-static void rtl_op_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+static void rtl_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                        u32 queues, bool drop)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
 
index 94cd9df98381008e53f6ecb60cbcb7f56a6aef1b..b14cf5a10f4421127e8f6ce414eee52054129163 100644 (file)
@@ -2515,23 +2515,3 @@ void rtl88ee_suspend(struct ieee80211_hw *hw)
 void rtl88ee_resume(struct ieee80211_hw *hw)
 {
 }
-
-/* Turn on AAP (RCR:bit 0) for promicuous mode. */
-void rtl88ee_allow_all_destaddr(struct ieee80211_hw *hw,
-                               bool allow_all_da, bool write_into_reg)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
-
-       if (allow_all_da) /* Set BIT0 */
-               rtlpci->receive_config |= RCR_AAP;
-        else /* Clear BIT0 */
-               rtlpci->receive_config &= ~RCR_AAP;
-
-       if (write_into_reg)
-               rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config);
-
-       RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD,
-                "receive_config = 0x%08X, write_into_reg =%d\n",
-                rtlpci->receive_config, write_into_reg);
-}
index b4460a41bd0159cac18b1161e489803c98afe244..1850fde881b587c6bbb15a630a58dfaaa16ece07 100644 (file)
@@ -61,8 +61,6 @@ void rtl8188ee_bt_reg_init(struct ieee80211_hw *hw);
 void rtl8188ee_bt_hw_init(struct ieee80211_hw *hw);
 void rtl88ee_suspend(struct ieee80211_hw *hw);
 void rtl88ee_resume(struct ieee80211_hw *hw);
-void rtl88ee_allow_all_destaddr(struct ieee80211_hw *hw,
-                               bool allow_all_da, bool write_into_reg);
 void rtl88ee_fw_clk_off_timer_callback(unsigned long data);
 
 #endif
index 1b4101bf9974e8243124f70f40e295e841a3f0a1..842d69349a37ca0812731beb9d4b6effe2173e39 100644 (file)
@@ -93,7 +93,7 @@ int rtl88e_init_sw_vars(struct ieee80211_hw *hw)
        u8 tid;
 
        rtl8188ee_bt_reg_init(hw);
-       rtlpci->msi_support = true;
+       rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support;
 
        rtlpriv->dm.dm_initialgain_enable = 1;
        rtlpriv->dm.dm_flag = 0;
@@ -255,7 +255,6 @@ static struct rtl_hal_ops rtl8188ee_hal_ops = {
        .enable_hw_sec = rtl88ee_enable_hw_security_config,
        .set_key = rtl88ee_set_key,
        .init_sw_leds = rtl88ee_init_sw_leds,
-       .allow_all_destaddr = rtl88ee_allow_all_destaddr,
        .get_bbreg = rtl88e_phy_query_bb_reg,
        .set_bbreg = rtl88e_phy_set_bb_reg,
        .get_rfreg = rtl88e_phy_query_rf_reg,
@@ -267,6 +266,7 @@ static struct rtl_mod_params rtl88ee_mod_params = {
        .inactiveps = true,
        .swctrl_lps = false,
        .fwctrl_lps = true,
+       .msi_support = false,
        .debug = DBG_EMERG,
 };
 
@@ -383,10 +383,12 @@ module_param_named(debug, rtl88ee_mod_params.debug, int, 0444);
 module_param_named(ips, rtl88ee_mod_params.inactiveps, bool, 0444);
 module_param_named(swlps, rtl88ee_mod_params.swctrl_lps, bool, 0444);
 module_param_named(fwlps, rtl88ee_mod_params.fwctrl_lps, bool, 0444);
+module_param_named(msi, rtl88ee_mod_params.msi_support, bool, 0444);
 MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n");
 MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n");
 MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n");
 MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n");
+MODULE_PARM_DESC(msi, "Set to 1 to use MSI interrupts mode (default 0)\n");
 MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)");
 
 static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume);
index 55adf043aef7e250759d9c993b224217ad974d37..cdecb0fd4d8edb531c34cb929b8a13f568f40eed 100644 (file)
@@ -2423,24 +2423,3 @@ void rtl92ce_suspend(struct ieee80211_hw *hw)
 void rtl92ce_resume(struct ieee80211_hw *hw)
 {
 }
-
-/* Turn on AAP (RCR:bit 0) for promicuous mode. */
-void rtl92ce_allow_all_destaddr(struct ieee80211_hw *hw,
-       bool allow_all_da, bool write_into_reg)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
-
-       if (allow_all_da) {/* Set BIT0 */
-               rtlpci->receive_config |= RCR_AAP;
-       } else {/* Clear BIT0 */
-               rtlpci->receive_config &= ~RCR_AAP;
-       }
-
-       if (write_into_reg)
-               rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config);
-
-       RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD,
-                "receive_config=0x%08X, write_into_reg=%d\n",
-                rtlpci->receive_config, write_into_reg);
-}
index 2d063b0c77609a608adc8d831bde0d946263a7a1..5533070f266c4c0706b4d83c2477d4c8421f17eb 100644 (file)
@@ -76,7 +76,5 @@ void rtl8192ce_bt_reg_init(struct ieee80211_hw *hw);
 void rtl8192ce_bt_hw_init(struct ieee80211_hw *hw);
 void rtl92ce_suspend(struct ieee80211_hw *hw);
 void rtl92ce_resume(struct ieee80211_hw *hw);
-void rtl92ce_allow_all_destaddr(struct ieee80211_hw *hw,
-                               bool allow_all_da, bool write_into_reg);
 
 #endif
index b790320d20305427c5ed4fa5fd9f37d944957e31..12f21f4073e887c497d4e7efda968d9217515936 100644 (file)
@@ -229,7 +229,6 @@ static struct rtl_hal_ops rtl8192ce_hal_ops = {
        .enable_hw_sec = rtl92ce_enable_hw_security_config,
        .set_key = rtl92ce_set_key,
        .init_sw_leds = rtl92ce_init_sw_leds,
-       .allow_all_destaddr = rtl92ce_allow_all_destaddr,
        .get_bbreg = rtl92c_phy_query_bb_reg,
        .set_bbreg = rtl92c_phy_set_bb_reg,
        .set_rfreg = rtl92ce_phy_set_rf_reg,
index 07cb06da67297244131394e60fa19c9ddf86044e..a903c2671b4d1701c0c71416748b5d4fa8ec6bf9 100644 (file)
@@ -511,7 +511,7 @@ static int _rtl92cu_init_power_on(struct ieee80211_hw *hw)
                        pr_info("MAC auto ON okay!\n");
                        break;
                }
-               if (pollingCount++ > 100) {
+               if (pollingCount++ > 1000) {
                        RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG,
                                 "Failed to polling REG_APS_FSMCO[APFM_ONMAC] done!\n");
                        return -ENODEV;
index c61311084d7e46615af164674337049bade62c20..361435f8608a125ca4cfdfd38b1bfa6212215ec9 100644 (file)
@@ -395,9 +395,6 @@ static struct usb_driver rtl8192cu_driver = {
        /* .resume = rtl_usb_resume, */
        /* .reset_resume = rtl8192c_resume, */
 #endif /* CONFIG_PM */
-#ifdef CONFIG_AUTOSUSPEND
-       .supports_autosuspend = 1,
-#endif
        .disable_hub_initiated_lpm = 1,
 };
 
index 9098558d916dee6ae6daf3567eb91291ad234b72..1c7101bcd79034c486be9713d99c19c335478cb6 100644 (file)
@@ -2544,23 +2544,3 @@ void rtl92se_resume(struct ieee80211_hw *hw)
                pci_write_config_dword(rtlpci->pdev, 0x40,
                        val & 0xffff00ff);
 }
-
-/* Turn on AAP (RCR:bit 0) for promicuous mode. */
-void rtl92se_allow_all_destaddr(struct ieee80211_hw *hw,
-                               bool allow_all_da, bool write_into_reg)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
-
-       if (allow_all_da) /* Set BIT0 */
-               rtlpci->receive_config |= RCR_AAP;
-       else /* Clear BIT0 */
-               rtlpci->receive_config &= ~RCR_AAP;
-
-       if (write_into_reg)
-               rtl_write_dword(rtlpriv, RCR, rtlpci->receive_config);
-
-       RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD,
-                "receive_config=0x%08X, write_into_reg=%d\n",
-                rtlpci->receive_config, write_into_reg);
-}
index da48aa8cbe6f8cb8e97ca2943e643217bfaea170..4cacee10f31eb81dd3a34bf8c2cbfd4cab22d60b 100644 (file)
@@ -74,7 +74,5 @@ void rtl92se_set_key(struct ieee80211_hw *hw,
                     u8 enc_algo, bool is_wepkey, bool clear_all);
 void rtl92se_suspend(struct ieee80211_hw *hw);
 void rtl92se_resume(struct ieee80211_hw *hw);
-void rtl92se_allow_all_destaddr(struct ieee80211_hw *hw,
-                               bool allow_all_da, bool write_into_reg);
 
 #endif
index 2e8e6f8d2d513e18a77a3710542656c2e1953ec5..1bff2a0f760087d0a510e247f01d1c6a33eee891 100644 (file)
@@ -290,7 +290,6 @@ static struct rtl_hal_ops rtl8192se_hal_ops = {
        .enable_hw_sec = rtl92se_enable_hw_security_config,
        .set_key = rtl92se_set_key,
        .init_sw_leds = rtl92se_init_sw_leds,
-       .allow_all_destaddr = rtl92se_allow_all_destaddr,
        .get_bbreg = rtl92s_phy_query_bb_reg,
        .set_bbreg = rtl92s_phy_set_bb_reg,
        .get_rfreg = rtl92s_phy_query_rf_reg,
index 48fee1be78c2f54e1e839cf1cec8e05be65b9094..5b4a714f3c8ce9905d762e08068a568827af1458 100644 (file)
@@ -32,7 +32,6 @@
 #include "dm.h"
 #include "fw.h"
 #include "../rtl8723com/fw_common.h"
-#include "../rtl8723com/fw_common.h"
 #include "phy.h"
 #include "reg.h"
 #include "hal_btc.h"
index 65c9e80e1f78ad23988bb962732396153bf1f137..87f69166a7eda86b2517feb6237da073c0ebe9da 100644 (file)
@@ -2383,24 +2383,3 @@ void rtl8723ae_suspend(struct ieee80211_hw *hw)
 void rtl8723ae_resume(struct ieee80211_hw *hw)
 {
 }
-
-/* Turn on AAP (RCR:bit 0) for promicuous mode. */
-void rtl8723ae_allow_all_destaddr(struct ieee80211_hw *hw,
-       bool allow_all_da, bool write_into_reg)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
-
-       if (allow_all_da) /* Set BIT0 */
-               rtlpci->receive_config |= RCR_AAP;
-       else /* Clear BIT0 */
-               rtlpci->receive_config &= ~RCR_AAP;
-
-       if (write_into_reg)
-               rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config);
-
-
-       RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD,
-                "receive_config=0x%08X, write_into_reg=%d\n",
-                rtlpci->receive_config, write_into_reg);
-}
index 6fa24f79b1d73551d9b40f4b355d5d1590636b68..d3bc39fb27a559dd0b527ebd5a377bb29a394dfe 100644 (file)
@@ -67,7 +67,5 @@ void rtl8723ae_bt_reg_init(struct ieee80211_hw *hw);
 void rtl8723ae_bt_hw_init(struct ieee80211_hw *hw);
 void rtl8723ae_suspend(struct ieee80211_hw *hw);
 void rtl8723ae_resume(struct ieee80211_hw *hw);
-void rtl8723ae_allow_all_destaddr(struct ieee80211_hw *hw,
-                                 bool allow_all_da, bool write_into_reg);
 
 #endif
index 1087a3bd07fa2dbd51f9983540ac44351374c58d..73cba1eec8cf9ce331afde11c69e6fdc7f75e59d 100644 (file)
@@ -238,7 +238,6 @@ static struct rtl_hal_ops rtl8723ae_hal_ops = {
        .enable_hw_sec = rtl8723ae_enable_hw_security_config,
        .set_key = rtl8723ae_set_key,
        .init_sw_leds = rtl8723ae_init_sw_leds,
-       .allow_all_destaddr = rtl8723ae_allow_all_destaddr,
        .get_bbreg = rtl8723_phy_query_bb_reg,
        .set_bbreg = rtl8723_phy_set_bb_reg,
        .get_rfreg = rtl8723ae_phy_query_rf_reg,
index 0fdf0909321f234c827107a13fd0a839c7c86be8..3d555495b45319b8d287d9edd5e1bc1c6162e625 100644 (file)
@@ -2501,23 +2501,3 @@ void rtl8723be_suspend(struct ieee80211_hw *hw)
 void rtl8723be_resume(struct ieee80211_hw *hw)
 {
 }
-
-/* Turn on AAP (RCR:bit 0) for promicuous mode. */
-void rtl8723be_allow_all_destaddr(struct ieee80211_hw *hw, bool allow_all_da,
-                                 bool write_into_reg)
-{
-       struct rtl_priv *rtlpriv = rtl_priv(hw);
-       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
-
-       if (allow_all_da) /* Set BIT0 */
-               rtlpci->receive_config |= RCR_AAP;
-       else /* Clear BIT0 */
-               rtlpci->receive_config &= ~RCR_AAP;
-
-       if (write_into_reg)
-               rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config);
-
-       RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD,
-                "receive_config = 0x%08X, write_into_reg =%d\n",
-                rtlpci->receive_config, write_into_reg);
-}
index b7449a9b57e47652489b18df83ab5a4502e40d0e..64c7551af6b797969e98086e600d2ece0240c9f4 100644 (file)
@@ -59,6 +59,4 @@ void rtl8723be_bt_reg_init(struct ieee80211_hw *hw);
 void rtl8723be_bt_hw_init(struct ieee80211_hw *hw);
 void rtl8723be_suspend(struct ieee80211_hw *hw);
 void rtl8723be_resume(struct ieee80211_hw *hw);
-void rtl8723be_allow_all_destaddr(struct ieee80211_hw *hw, bool allow_all_da,
-                                 bool write_into_reg);
 #endif
index b4577ebc4bb0bb5c16d068039cddc7f3fe41b744..ff12bf41644bbfd3909bb6506f825f1d0d7e81fa 100644 (file)
@@ -92,7 +92,7 @@ int rtl8723be_init_sw_vars(struct ieee80211_hw *hw)
        struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
 
        rtl8723be_bt_reg_init(hw);
-       rtlpci->msi_support = true;
+       rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support;
        rtlpriv->btcoexist.btc_ops = rtl_btc_get_ops_pointer();
 
        rtlpriv->dm.dm_initialgain_enable = 1;
@@ -253,6 +253,7 @@ static struct rtl_mod_params rtl8723be_mod_params = {
        .inactiveps = true,
        .swctrl_lps = false,
        .fwctrl_lps = true,
+       .msi_support = false,
        .debug = DBG_EMERG,
 };
 
@@ -365,9 +366,11 @@ module_param_named(debug, rtl8723be_mod_params.debug, int, 0444);
 module_param_named(ips, rtl8723be_mod_params.inactiveps, bool, 0444);
 module_param_named(swlps, rtl8723be_mod_params.swctrl_lps, bool, 0444);
 module_param_named(fwlps, rtl8723be_mod_params.fwctrl_lps, bool, 0444);
+module_param_named(msi, rtl8723be_mod_params.msi_support, bool, 0444);
 MODULE_PARM_DESC(swenc, "using hardware crypto (default 0 [hardware])\n");
 MODULE_PARM_DESC(ips, "using no link power save (default 1 is open)\n");
 MODULE_PARM_DESC(fwlps, "using linked fw control power save (default 1 is open)\n");
+MODULE_PARM_DESC(msi, "Set to 1 to use MSI interrupts mode (default 0)\n");
 MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)");
 
 static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume);
index e0a0d8c8fed556f66b0c766e490b16a59aee74f7..969eaea5eddd8d23862b272edfc6edbf6d9ac5df 100644 (file)
@@ -33,7 +33,6 @@
 #include "trx.h"
 #include "led.h"
 #include "dm.h"
-#include "phy.h"
 
 static u8 _rtl8723be_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue)
 {
index 6965afdf572a9d57c06fabd8b070fbc8cc58b9ce..407a7936d3642f39fb4fb0364ab0568e20f75153 100644 (file)
@@ -1960,8 +1960,6 @@ struct rtl_hal_ops {
                          u32 regaddr, u32 bitmask);
        void (*set_rfreg) (struct ieee80211_hw *hw, enum radio_path rfpath,
                           u32 regaddr, u32 bitmask, u32 data);
-       void (*allow_all_destaddr)(struct ieee80211_hw *hw,
-               bool allow_all_da, bool write_into_reg);
        void (*linked_set_reg) (struct ieee80211_hw *hw);
        void (*chk_switch_dmdp) (struct ieee80211_hw *hw);
        void (*dualmac_easy_concurrent) (struct ieee80211_hw *hw);
@@ -2030,6 +2028,10 @@ struct rtl_mod_params {
 
        /* default: 1 = using linked fw power save */
        bool fwctrl_lps;
+
+       /* default: 0 = not using MSI interrupts mode */
+       /* submodules should set their own defalut value */
+       bool msi_support;
 };
 
 struct rtl_hal_usbint_cfg {
index 5a4ec56c83d0aa15e7e00226a6cfe69c9146696b..5695628757ee17db6c4da9ba40d81000f9fa57b5 100644 (file)
@@ -2,7 +2,6 @@
 
 #include <linux/module.h>
 #include <linux/slab.h>
-#include <linux/crc7.h>
 
 #include "wl1251.h"
 #include "reg.h"
index bf1fa18b9786253159d634ec1f846f70d8551bc8..ede31f048ef98d28b96756f912edbdd2b4df8eb5 100644 (file)
@@ -2,7 +2,6 @@
 
 #include <linux/module.h>
 #include <linux/slab.h>
-#include <linux/crc7.h>
 #include <linux/etherdevice.h>
 
 #include "wl1251.h"
index db0105313745f08a02c9d408564c5d8029aa05ca..c98630394a1a299b77b907ce53851b62b7d84030 100644 (file)
@@ -124,11 +124,12 @@ static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox)
                        return ret;
        }
 
-       if (wl->vif && vector & SYNCHRONIZATION_TIMEOUT_EVENT_ID) {
+       if (vector & SYNCHRONIZATION_TIMEOUT_EVENT_ID) {
                wl1251_debug(DEBUG_EVENT, "SYNCHRONIZATION_TIMEOUT_EVENT");
 
                /* indicate to the stack, that beacons have been lost */
-               ieee80211_beacon_loss(wl->vif);
+               if (wl->vif && wl->vif->type == NL80211_IFTYPE_STATION)
+                       ieee80211_beacon_loss(wl->vif);
        }
 
        if (vector & REGAINED_BSS_EVENT_ID) {
index 757e25784a8a22f503b3483c0b55cedca5a05d3f..4e782f18ae3431600a66923216faa536d42c46b6 100644 (file)
@@ -550,6 +550,34 @@ static void wl1251_op_remove_interface(struct ieee80211_hw *hw,
        mutex_unlock(&wl->mutex);
 }
 
+static int wl1251_build_null_data(struct wl1251 *wl)
+{
+       struct sk_buff *skb = NULL;
+       int size;
+       void *ptr;
+       int ret = -ENOMEM;
+
+       if (wl->bss_type == BSS_TYPE_IBSS) {
+               size = sizeof(struct wl12xx_null_data_template);
+               ptr = NULL;
+       } else {
+               skb = ieee80211_nullfunc_get(wl->hw, wl->vif);
+               if (!skb)
+                       goto out;
+               size = skb->len;
+               ptr = skb->data;
+       }
+
+       ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA, ptr, size);
+
+out:
+       dev_kfree_skb(skb);
+       if (ret)
+               wl1251_warning("cmd buld null data failed: %d", ret);
+
+       return ret;
+}
+
 static int wl1251_build_qos_null_data(struct wl1251 *wl)
 {
        struct ieee80211_qos_hdr template;
@@ -687,16 +715,6 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed)
                wl->power_level = conf->power_level;
        }
 
-       /*
-        * Tell stack that connection is lost because hw encryption isn't
-        * supported in monitor mode.
-        * This requires temporary enabling of the hw connection monitor flag
-        */
-       if ((changed & IEEE80211_CONF_CHANGE_MONITOR) && wl->vif) {
-               wl->hw->flags |= IEEE80211_HW_CONNECTION_MONITOR;
-               ieee80211_connection_loss(wl->vif);
-       }
-
 out_sleep:
        wl1251_ps_elp_sleep(wl);
 
@@ -1103,24 +1121,19 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw,
                wl->rssi_thold = bss_conf->cqm_rssi_thold;
        }
 
-       if (changed & BSS_CHANGED_BSSID) {
+       if ((changed & BSS_CHANGED_BSSID) &&
+           memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) {
                memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
 
-               skb = ieee80211_nullfunc_get(wl->hw, wl->vif);
-               if (!skb)
-                       goto out_sleep;
-
-               ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA,
-                                             skb->data, skb->len);
-               dev_kfree_skb(skb);
-               if (ret < 0)
-                       goto out_sleep;
+               if (!is_zero_ether_addr(wl->bssid)) {
+                       ret = wl1251_build_null_data(wl);
+                       if (ret < 0)
+                               goto out_sleep;
 
-               ret = wl1251_build_qos_null_data(wl);
-               if (ret < 0)
-                       goto out;
+                       ret = wl1251_build_qos_null_data(wl);
+                       if (ret < 0)
+                               goto out_sleep;
 
-               if (wl->bss_type != BSS_TYPE_IBSS) {
                        ret = wl1251_join(wl, wl->bss_type, wl->channel,
                                          wl->beacon_int, wl->dtim_period);
                        if (ret < 0)
@@ -1129,9 +1142,6 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw,
        }
 
        if (changed & BSS_CHANGED_ASSOC) {
-               /* Disable temporary enabled hw connection monitor flag */
-               wl->hw->flags &= ~IEEE80211_HW_CONNECTION_MONITOR;
-
                if (bss_conf->assoc) {
                        wl->beacon_int = bss_conf->beacon_int;
 
@@ -1216,8 +1226,8 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw,
                if (ret < 0)
                        goto out_sleep;
 
-               ret = wl1251_join(wl, wl->bss_type, wl->beacon_int,
-                                 wl->channel, wl->dtim_period);
+               ret = wl1251_join(wl, wl->bss_type, wl->channel,
+                                 wl->beacon_int, wl->dtim_period);
 
                if (ret < 0)
                        goto out_sleep;
index b06d36d99362703c1b7aa35e6aadd5cab29e0d8f..a0aa8fa72392830f65f8c8b52303be5ad44019b9 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/irq.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/swab.h>
 #include <linux/crc7.h>
 #include <linux/spi/spi.h>
 #include <linux/wl12xx.h>
@@ -83,47 +84,44 @@ static void wl1251_spi_reset(struct wl1251 *wl)
 
 static void wl1251_spi_wake(struct wl1251 *wl)
 {
-       u8 crc[WSPI_INIT_CMD_CRC_LEN], *cmd;
        struct spi_transfer t;
        struct spi_message m;
+       u8 *cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
 
-       cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
        if (!cmd) {
                wl1251_error("could not allocate cmd for spi init");
                return;
        }
 
-       memset(crc, 0, sizeof(crc));
        memset(&t, 0, sizeof(t));
        spi_message_init(&m);
 
        /* Set WSPI_INIT_COMMAND
         * the data is being send from the MSB to LSB
         */
-       cmd[2] = 0xff;
-       cmd[3] = 0xff;
-       cmd[1] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX;
-       cmd[0] = 0;
-       cmd[7] = 0;
-       cmd[6] |= HW_ACCESS_WSPI_INIT_CMD_MASK << 3;
-       cmd[6] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN;
+       cmd[0] = 0xff;
+       cmd[1] = 0xff;
+       cmd[2] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX;
+       cmd[3] = 0;
+       cmd[4] = 0;
+       cmd[5] = HW_ACCESS_WSPI_INIT_CMD_MASK << 3;
+       cmd[5] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN;
+
+       cmd[6] = WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS
+               | WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS;
 
        if (HW_ACCESS_WSPI_FIXED_BUSY_LEN == 0)
-               cmd[5] |=  WSPI_INIT_CMD_DIS_FIXEDBUSY;
+               cmd[6] |= WSPI_INIT_CMD_DIS_FIXEDBUSY;
        else
-               cmd[5] |= WSPI_INIT_CMD_EN_FIXEDBUSY;
-
-       cmd[5] |= WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS
-               | WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS;
-
-       crc[0] = cmd[1];
-       crc[1] = cmd[0];
-       crc[2] = cmd[7];
-       crc[3] = cmd[6];
-       crc[4] = cmd[5];
+               cmd[6] |= WSPI_INIT_CMD_EN_FIXEDBUSY;
 
-       cmd[4] |= crc7(0, crc, WSPI_INIT_CMD_CRC_LEN) << 1;
-       cmd[4] |= WSPI_INIT_CMD_END;
+       cmd[7] = crc7_be(0, cmd+2, WSPI_INIT_CMD_CRC_LEN) | WSPI_INIT_CMD_END;
+       /*
+        * The above is the logical order; it must actually be stored
+        * in the buffer byte-swapped.
+        */
+       __swab32s((u32 *)cmd);
+       __swab32s((u32 *)cmd+1);
 
        t.tx_buf = cmd;
        t.len = WSPI_INIT_CMD_LEN;
index f7381dd69009a150e1901a876494d225e0267f5e..0f2cfb0d2a9ec38fe013872e6d4339c2db1345e3 100644 (file)
@@ -57,7 +57,7 @@ static const struct file_operations name## _ops = {                   \
                                            wl, &name## _ops);          \
                if (!entry || IS_ERR(entry))                            \
                        goto err;                                       \
-       } while (0);
+       } while (0)
 
 
 #define DEBUGFS_ADD_PREFIX(prefix, name, parent)                       \
@@ -66,7 +66,7 @@ static const struct file_operations name## _ops = {                   \
                                    wl, &prefix## _## name## _ops);     \
                if (!entry || IS_ERR(entry))                            \
                        goto err;                                       \
-       } while (0);
+       } while (0)
 
 #define DEBUGFS_FWSTATS_FILE(sub, name, fmt, struct_type)              \
 static ssize_t sub## _ ##name## _read(struct file *file,               \
index e71eae35336874cacca99c48d80713704142b6f1..3d6028e62750279299431ce56315bdf67fa1f8cc 100644 (file)
@@ -1416,7 +1416,7 @@ void wl1271_rx_filter_free(struct wl12xx_rx_filter *filter)
 
 int wl1271_rx_filter_alloc_field(struct wl12xx_rx_filter *filter,
                                 u16 offset, u8 flags,
-                                u8 *pattern, u8 len)
+                                const u8 *pattern, u8 len)
 {
        struct wl12xx_rx_filter_field *field;
 
@@ -5184,7 +5184,8 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
        mutex_unlock(&wl->mutex);
 }
 
-static void wlcore_op_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+static void wlcore_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                           u32 queues, bool drop)
 {
        struct wl1271 *wl = hw->priv;
 
index 29ef2492951fcdaa372611449fbc4c3cde4322f1..d3dd7bfdf3f1f33efbfbd9e0be762f664c892926 100644 (file)
@@ -217,7 +217,7 @@ static struct wl1271_if_operations sdio_ops = {
 static int wl1271_probe(struct sdio_func *func,
                                  const struct sdio_device_id *id)
 {
-       struct wlcore_platdev_data *pdev_data;
+       struct wlcore_platdev_data pdev_data;
        struct wl12xx_sdio_glue *glue;
        struct resource res[1];
        mmc_pm_flag_t mmcflags;
@@ -228,16 +228,13 @@ static int wl1271_probe(struct sdio_func *func,
        if (func->num != 0x02)
                return -ENODEV;
 
-       pdev_data = kzalloc(sizeof(*pdev_data), GFP_KERNEL);
-       if (!pdev_data)
-               goto out;
-
-       pdev_data->if_ops = &sdio_ops;
+       memset(&pdev_data, 0x00, sizeof(pdev_data));
+       pdev_data.if_ops = &sdio_ops;
 
        glue = kzalloc(sizeof(*glue), GFP_KERNEL);
        if (!glue) {
                dev_err(&func->dev, "can't allocate glue\n");
-               goto out_free_pdev_data;
+               goto out;
        }
 
        glue->dev = &func->dev;
@@ -248,9 +245,9 @@ static int wl1271_probe(struct sdio_func *func,
        /* Use block mode for transferring over one block size of data */
        func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
 
-       pdev_data->pdata = wl12xx_get_platform_data();
-       if (IS_ERR(pdev_data->pdata)) {
-               ret = PTR_ERR(pdev_data->pdata);
+       pdev_data.pdata = wl12xx_get_platform_data();
+       if (IS_ERR(pdev_data.pdata)) {
+               ret = PTR_ERR(pdev_data.pdata);
                dev_err(glue->dev, "missing wlan platform data: %d\n", ret);
                goto out_free_glue;
        }
@@ -260,7 +257,7 @@ static int wl1271_probe(struct sdio_func *func,
        dev_dbg(glue->dev, "sdio PM caps = 0x%x\n", mmcflags);
 
        if (mmcflags & MMC_PM_KEEP_POWER)
-               pdev_data->pdata->pwr_in_suspend = true;
+               pdev_data.pdata->pwr_in_suspend = true;
 
        sdio_set_drvdata(func, glue);
 
@@ -289,7 +286,7 @@ static int wl1271_probe(struct sdio_func *func,
 
        memset(res, 0x00, sizeof(res));
 
-       res[0].start = pdev_data->pdata->irq;
+       res[0].start = pdev_data.pdata->irq;
        res[0].flags = IORESOURCE_IRQ;
        res[0].name = "irq";
 
@@ -299,8 +296,8 @@ static int wl1271_probe(struct sdio_func *func,
                goto out_dev_put;
        }
 
-       ret = platform_device_add_data(glue->core, pdev_data,
-                                      sizeof(*pdev_data));
+       ret = platform_device_add_data(glue->core, &pdev_data,
+                                      sizeof(pdev_data));
        if (ret) {
                dev_err(glue->dev, "can't add platform data\n");
                goto out_dev_put;
@@ -319,9 +316,6 @@ static int wl1271_probe(struct sdio_func *func,
 out_free_glue:
        kfree(glue);
 
-out_free_pdev_data:
-       kfree(pdev_data);
-
 out:
        return ret;
 }
index dbe826dd7c23c49a38a08988cb24c50764d3efaa..392c882b28f03d9da2be51c6487db2d423b58976 100644 (file)
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/swab.h>
 #include <linux/crc7.h>
 #include <linux/spi/spi.h>
 #include <linux/wl12xx.h>
 #include <linux/platform_device.h>
-#include <linux/slab.h>
 
 #include "wlcore.h"
 #include "wl12xx_80211.h"
@@ -110,18 +111,16 @@ static void wl12xx_spi_reset(struct device *child)
 static void wl12xx_spi_init(struct device *child)
 {
        struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
-       u8 crc[WSPI_INIT_CMD_CRC_LEN], *cmd;
        struct spi_transfer t;
        struct spi_message m;
+       u8 *cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
 
-       cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
        if (!cmd) {
                dev_err(child->parent,
                        "could not allocate cmd for spi init\n");
                return;
        }
 
-       memset(crc, 0, sizeof(crc));
        memset(&t, 0, sizeof(t));
        spi_message_init(&m);
 
@@ -129,30 +128,29 @@ static void wl12xx_spi_init(struct device *child)
         * Set WSPI_INIT_COMMAND
         * the data is being send from the MSB to LSB
         */
-       cmd[2] = 0xff;
-       cmd[3] = 0xff;
-       cmd[1] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX;
-       cmd[0] = 0;
-       cmd[7] = 0;
-       cmd[6] |= HW_ACCESS_WSPI_INIT_CMD_MASK << 3;
-       cmd[6] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN;
+       cmd[0] = 0xff;
+       cmd[1] = 0xff;
+       cmd[2] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX;
+       cmd[3] = 0;
+       cmd[4] = 0;
+       cmd[5] = HW_ACCESS_WSPI_INIT_CMD_MASK << 3;
+       cmd[5] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN;
+
+       cmd[6] = WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS
+               | WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS;
 
        if (HW_ACCESS_WSPI_FIXED_BUSY_LEN == 0)
-               cmd[5] |=  WSPI_INIT_CMD_DIS_FIXEDBUSY;
+               cmd[6] |= WSPI_INIT_CMD_DIS_FIXEDBUSY;
        else
-               cmd[5] |= WSPI_INIT_CMD_EN_FIXEDBUSY;
-
-       cmd[5] |= WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS
-               | WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS;
-
-       crc[0] = cmd[1];
-       crc[1] = cmd[0];
-       crc[2] = cmd[7];
-       crc[3] = cmd[6];
-       crc[4] = cmd[5];
+               cmd[6] |= WSPI_INIT_CMD_EN_FIXEDBUSY;
 
-       cmd[4] |= crc7(0, crc, WSPI_INIT_CMD_CRC_LEN) << 1;
-       cmd[4] |= WSPI_INIT_CMD_END;
+       cmd[7] = crc7_be(0, cmd+2, WSPI_INIT_CMD_CRC_LEN) | WSPI_INIT_CMD_END;
+       /*
+        * The above is the logical order; it must actually be stored
+        * in the buffer byte-swapped.
+        */
+       __swab32s((u32 *)cmd);
+       __swab32s((u32 *)cmd+1);
 
        t.tx_buf = cmd;
        t.len = WSPI_INIT_CMD_LEN;
@@ -327,27 +325,25 @@ static struct wl1271_if_operations spi_ops = {
 static int wl1271_probe(struct spi_device *spi)
 {
        struct wl12xx_spi_glue *glue;
-       struct wlcore_platdev_data *pdev_data;
+       struct wlcore_platdev_data pdev_data;
        struct resource res[1];
        int ret = -ENOMEM;
 
-       pdev_data = kzalloc(sizeof(*pdev_data), GFP_KERNEL);
-       if (!pdev_data)
-               goto out;
+       memset(&pdev_data, 0x00, sizeof(pdev_data));
 
-       pdev_data->pdata = dev_get_platdata(&spi->dev);
-       if (!pdev_data->pdata) {
+       pdev_data.pdata = dev_get_platdata(&spi->dev);
+       if (!pdev_data.pdata) {
                dev_err(&spi->dev, "no platform data\n");
                ret = -ENODEV;
-               goto out_free_pdev_data;
+               goto out;
        }
 
-       pdev_data->if_ops = &spi_ops;
+       pdev_data.if_ops = &spi_ops;
 
        glue = kzalloc(sizeof(*glue), GFP_KERNEL);
        if (!glue) {
                dev_err(&spi->dev, "can't allocate glue\n");
-               goto out_free_pdev_data;
+               goto out;
        }
 
        glue->dev = &spi->dev;
@@ -385,8 +381,8 @@ static int wl1271_probe(struct spi_device *spi)
                goto out_dev_put;
        }
 
-       ret = platform_device_add_data(glue->core, pdev_data,
-                                      sizeof(*pdev_data));
+       ret = platform_device_add_data(glue->core, &pdev_data,
+                                      sizeof(pdev_data));
        if (ret) {
                dev_err(glue->dev, "can't add platform data\n");
                goto out_dev_put;
@@ -406,9 +402,6 @@ static int wl1271_probe(struct spi_device *spi)
 out_free_glue:
        kfree(glue);
 
-out_free_pdev_data:
-       kfree(pdev_data);
-
 out:
        return ret;
 }
index 756e890bc5ee2be0a5f68ef3e37de397ccc8dd52..c2c34a84ff3d4bf7231f84933564b0e78dcc415b 100644 (file)
@@ -512,8 +512,8 @@ int wl1271_recalc_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 void wl12xx_queue_recovery_work(struct wl1271 *wl);
 size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen);
 int wl1271_rx_filter_alloc_field(struct wl12xx_rx_filter *filter,
-                                       u16 offset, u8 flags,
-                                       u8 *pattern, u8 len);
+                                u16 offset, u8 flags,
+                                const u8 *pattern, u8 len);
 void wl1271_rx_filter_free(struct wl12xx_rx_filter *filter);
 struct wl12xx_rx_filter *wl1271_rx_filter_alloc(void);
 int wl1271_rx_filter_get_fields_size(struct wl12xx_rx_filter *filter);
index 0d4a285cbd7edb45408360a83952ab288debf62d..4dd7c4a1923ba4222d6ed0c238e3dbe97509a06e 100644 (file)
@@ -99,22 +99,43 @@ struct xenvif_rx_meta {
  */
 #define XEN_NETBK_LEGACY_SLOTS_MAX XEN_NETIF_NR_SLOTS_MIN
 
-struct xenvif {
-       /* Unique identifier for this interface. */
-       domid_t          domid;
-       unsigned int     handle;
+/* Queue name is interface name with "-qNNN" appended */
+#define QUEUE_NAME_SIZE (IFNAMSIZ + 5)
 
-       /* Is this interface disabled? True when backend discovers
-        * frontend is rogue.
+/* IRQ name is queue name with "-tx" or "-rx" appended */
+#define IRQ_NAME_SIZE (QUEUE_NAME_SIZE + 3)
+
+struct xenvif;
+
+struct xenvif_stats {
+       /* Stats fields to be updated per-queue.
+        * A subset of struct net_device_stats that contains only the
+        * fields that are updated in netback.c for each queue.
         */
-       bool disabled;
+       unsigned int rx_bytes;
+       unsigned int rx_packets;
+       unsigned int tx_bytes;
+       unsigned int tx_packets;
+
+       /* Additional stats used by xenvif */
+       unsigned long rx_gso_checksum_fixup;
+       unsigned long tx_zerocopy_sent;
+       unsigned long tx_zerocopy_success;
+       unsigned long tx_zerocopy_fail;
+       unsigned long tx_frag_overflow;
+};
+
+struct xenvif_queue { /* Per-queue data for xenvif */
+       unsigned int id; /* Queue ID, 0-based */
+       char name[QUEUE_NAME_SIZE]; /* DEVNAME-qN */
+       struct xenvif *vif; /* Parent VIF */
 
        /* Use NAPI for guest TX */
        struct napi_struct napi;
        /* When feature-split-event-channels = 0, tx_irq = rx_irq. */
        unsigned int tx_irq;
        /* Only used when feature-split-event-channels = 1 */
-       char tx_irq_name[IFNAMSIZ+4]; /* DEVNAME-tx */
+       char tx_irq_name[IRQ_NAME_SIZE]; /* DEVNAME-qN-tx */
        struct xen_netif_tx_back_ring tx;
        struct sk_buff_head tx_queue;
        struct page *mmap_pages[MAX_PENDING_REQS];
@@ -150,7 +171,7 @@ struct xenvif {
        /* When feature-split-event-channels = 0, tx_irq = rx_irq. */
        unsigned int rx_irq;
        /* Only used when feature-split-event-channels = 1 */
-       char rx_irq_name[IFNAMSIZ+4]; /* DEVNAME-rx */
+       char rx_irq_name[IRQ_NAME_SIZE]; /* DEVNAME-qN-rx */
        struct xen_netif_rx_back_ring rx;
        struct sk_buff_head rx_queue;
        RING_IDX rx_last_skb_slots;
@@ -158,14 +179,29 @@ struct xenvif {
 
        struct timer_list wake_queue;
 
-       /* This array is allocated seperately as it is large */
-       struct gnttab_copy *grant_copy_op;
+       struct gnttab_copy grant_copy_op[MAX_GRANT_COPY_OPS];
 
        /* We create one meta structure per ring request we consume, so
         * the maximum number is the same as the ring size.
         */
        struct xenvif_rx_meta meta[XEN_NETIF_RX_RING_SIZE];
 
+       /* Transmit shaping: allow 'credit_bytes' every 'credit_usec'. */
+       unsigned long   credit_bytes;
+       unsigned long   credit_usec;
+       unsigned long   remaining_credit;
+       struct timer_list credit_timeout;
+       u64 credit_window_start;
+
+       /* Statistics */
+       struct xenvif_stats stats;
+};
+
+struct xenvif {
+       /* Unique identifier for this interface. */
+       domid_t          domid;
+       unsigned int     handle;
+
        u8               fe_dev_addr[6];
 
        /* Frontend feature information. */
@@ -179,19 +215,13 @@ struct xenvif {
        /* Internal feature information. */
        u8 can_queue:1;     /* can queue packets for receiver? */
 
-       /* Transmit shaping: allow 'credit_bytes' every 'credit_usec'. */
-       unsigned long   credit_bytes;
-       unsigned long   credit_usec;
-       unsigned long   remaining_credit;
-       struct timer_list credit_timeout;
-       u64 credit_window_start;
+       /* Is this interface disabled? True when backend discovers
+        * frontend is rogue.
+        */
+       bool disabled;
 
-       /* Statistics */
-       unsigned long rx_gso_checksum_fixup;
-       unsigned long tx_zerocopy_sent;
-       unsigned long tx_zerocopy_success;
-       unsigned long tx_zerocopy_fail;
-       unsigned long tx_frag_overflow;
+       /* Queues */
+       struct xenvif_queue *queues;
 
        /* Miscellaneous private stuff. */
        struct net_device *dev;
@@ -206,7 +236,10 @@ struct xenvif *xenvif_alloc(struct device *parent,
                            domid_t domid,
                            unsigned int handle);
 
-int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref,
+int xenvif_init_queue(struct xenvif_queue *queue);
+void xenvif_deinit_queue(struct xenvif_queue *queue);
+
+int xenvif_connect(struct xenvif_queue *queue, unsigned long tx_ring_ref,
                   unsigned long rx_ring_ref, unsigned int tx_evtchn,
                   unsigned int rx_evtchn);
 void xenvif_disconnect(struct xenvif *vif);
@@ -217,44 +250,47 @@ void xenvif_xenbus_fini(void);
 
 int xenvif_schedulable(struct xenvif *vif);
 
-int xenvif_must_stop_queue(struct xenvif *vif);
+int xenvif_must_stop_queue(struct xenvif_queue *queue);
+
+int xenvif_queue_stopped(struct xenvif_queue *queue);
+void xenvif_wake_queue(struct xenvif_queue *queue);
 
 /* (Un)Map communication rings. */
-void xenvif_unmap_frontend_rings(struct xenvif *vif);
-int xenvif_map_frontend_rings(struct xenvif *vif,
+void xenvif_unmap_frontend_rings(struct xenvif_queue *queue);
+int xenvif_map_frontend_rings(struct xenvif_queue *queue,
                              grant_ref_t tx_ring_ref,
                              grant_ref_t rx_ring_ref);
 
 /* Check for SKBs from frontend and schedule backend processing */
-void xenvif_napi_schedule_or_enable_events(struct xenvif *vif);
+void xenvif_napi_schedule_or_enable_events(struct xenvif_queue *queue);
 
 /* Prevent the device from generating any further traffic. */
 void xenvif_carrier_off(struct xenvif *vif);
 
-int xenvif_tx_action(struct xenvif *vif, int budget);
+int xenvif_tx_action(struct xenvif_queue *queue, int budget);
 
 int xenvif_kthread_guest_rx(void *data);
-void xenvif_kick_thread(struct xenvif *vif);
+void xenvif_kick_thread(struct xenvif_queue *queue);
 
 int xenvif_dealloc_kthread(void *data);
 
 /* Determine whether the needed number of slots (req) are available,
  * and set req_event if not.
  */
-bool xenvif_rx_ring_slots_available(struct xenvif *vif, int needed);
+bool xenvif_rx_ring_slots_available(struct xenvif_queue *queue, int needed);
 
-void xenvif_stop_queue(struct xenvif *vif);
+void xenvif_carrier_on(struct xenvif *vif);
 
 /* Callback from stack when TX packet can be released */
 void xenvif_zerocopy_callback(struct ubuf_info *ubuf, bool zerocopy_success);
 
 /* Unmap a pending page and release it back to the guest */
-void xenvif_idx_unmap(struct xenvif *vif, u16 pending_idx);
+void xenvif_idx_unmap(struct xenvif_queue *queue, u16 pending_idx);
 
-static inline pending_ring_idx_t nr_pending_reqs(struct xenvif *vif)
+static inline pending_ring_idx_t nr_pending_reqs(struct xenvif_queue *queue)
 {
        return MAX_PENDING_REQS -
-               vif->pending_prod + vif->pending_cons;
+               queue->pending_prod + queue->pending_cons;
 }
 
 /* Callback from stack when TX packet can be released */
@@ -264,5 +300,6 @@ extern bool separate_tx_rx_irq;
 
 extern unsigned int rx_drain_timeout_msecs;
 extern unsigned int rx_drain_timeout_jiffies;
+extern unsigned int xenvif_max_queues;
 
 #endif /* __XEN_NETBACK__COMMON_H__ */
index 20e9defa10606d7d54a0a5412eb93365ce748f5c..852da34b89615ae7c663f18ea210d1a5ea046808 100644 (file)
 #define XENVIF_QUEUE_LENGTH 32
 #define XENVIF_NAPI_WEIGHT  64
 
+static inline void xenvif_stop_queue(struct xenvif_queue *queue)
+{
+       struct net_device *dev = queue->vif->dev;
+
+       if (!queue->vif->can_queue)
+               return;
+
+       netif_tx_stop_queue(netdev_get_tx_queue(dev, queue->id));
+}
+
 int xenvif_schedulable(struct xenvif *vif)
 {
        return netif_running(vif->dev) && netif_carrier_ok(vif->dev);
@@ -50,33 +60,34 @@ int xenvif_schedulable(struct xenvif *vif)
 
 static irqreturn_t xenvif_tx_interrupt(int irq, void *dev_id)
 {
-       struct xenvif *vif = dev_id;
+       struct xenvif_queue *queue = dev_id;
 
-       if (RING_HAS_UNCONSUMED_REQUESTS(&vif->tx))
-               napi_schedule(&vif->napi);
+       if (RING_HAS_UNCONSUMED_REQUESTS(&queue->tx))
+               napi_schedule(&queue->napi);
 
        return IRQ_HANDLED;
 }
 
-static int xenvif_poll(struct napi_struct *napi, int budget)
+int xenvif_poll(struct napi_struct *napi, int budget)
 {
-       struct xenvif *vif = container_of(napi, struct xenvif, napi);
+       struct xenvif_queue *queue =
+               container_of(napi, struct xenvif_queue, napi);
        int work_done;
 
        /* This vif is rogue, we pretend we've there is nothing to do
         * for this vif to deschedule it from NAPI. But this interface
         * will be turned off in thread context later.
         */
-       if (unlikely(vif->disabled)) {
+       if (unlikely(queue->vif->disabled)) {
                napi_complete(napi);
                return 0;
        }
 
-       work_done = xenvif_tx_action(vif, budget);
+       work_done = xenvif_tx_action(queue, budget);
 
        if (work_done < budget) {
                napi_complete(napi);
-               xenvif_napi_schedule_or_enable_events(vif);
+               xenvif_napi_schedule_or_enable_events(queue);
        }
 
        return work_done;
@@ -84,9 +95,9 @@ static int xenvif_poll(struct napi_struct *napi, int budget)
 
 static irqreturn_t xenvif_rx_interrupt(int irq, void *dev_id)
 {
-       struct xenvif *vif = dev_id;
+       struct xenvif_queue *queue = dev_id;
 
-       xenvif_kick_thread(vif);
+       xenvif_kick_thread(queue);
 
        return IRQ_HANDLED;
 }
@@ -99,28 +110,80 @@ static irqreturn_t xenvif_interrupt(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-static void xenvif_wake_queue(unsigned long data)
+int xenvif_queue_stopped(struct xenvif_queue *queue)
 {
-       struct xenvif *vif = (struct xenvif *)data;
+       struct net_device *dev = queue->vif->dev;
+       unsigned int id = queue->id;
+       return netif_tx_queue_stopped(netdev_get_tx_queue(dev, id));
+}
 
-       if (netif_queue_stopped(vif->dev)) {
-               netdev_err(vif->dev, "draining TX queue\n");
-               vif->rx_queue_purge = true;
-               xenvif_kick_thread(vif);
-               netif_wake_queue(vif->dev);
+void xenvif_wake_queue(struct xenvif_queue *queue)
+{
+       struct net_device *dev = queue->vif->dev;
+       unsigned int id = queue->id;
+       netif_tx_wake_queue(netdev_get_tx_queue(dev, id));
+}
+
+/* Callback to wake the queue and drain it on timeout */
+static void xenvif_wake_queue_callback(unsigned long data)
+{
+       struct xenvif_queue *queue = (struct xenvif_queue *)data;
+
+       if (xenvif_queue_stopped(queue)) {
+               netdev_err(queue->vif->dev, "draining TX queue\n");
+               queue->rx_queue_purge = true;
+               xenvif_kick_thread(queue);
+               xenvif_wake_queue(queue);
        }
 }
 
+static u16 xenvif_select_queue(struct net_device *dev, struct sk_buff *skb,
+                              void *accel_priv, select_queue_fallback_t fallback)
+{
+       unsigned int num_queues = dev->real_num_tx_queues;
+       u32 hash;
+       u16 queue_index;
+
+       /* First, check if there is only one queue to optimise the
+        * single-queue or old frontend scenario.
+        */
+       if (num_queues == 1) {
+               queue_index = 0;
+       } else {
+               /* Use skb_get_hash to obtain an L4 hash if available */
+               hash = skb_get_hash(skb);
+               queue_index = hash % num_queues;
+       }
+
+       return queue_index;
+}
+
 static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct xenvif *vif = netdev_priv(dev);
+       struct xenvif_queue *queue = NULL;
+       unsigned int num_queues = dev->real_num_tx_queues;
+       u16 index;
        int min_slots_needed;
 
        BUG_ON(skb->dev != dev);
 
-       /* Drop the packet if vif is not ready */
-       if (vif->task == NULL ||
-           vif->dealloc_task == NULL ||
+       /* Drop the packet if queues are not set up */
+       if (num_queues < 1)
+               goto drop;
+
+       /* Obtain the queue to be used to transmit this packet */
+       index = skb_get_queue_mapping(skb);
+       if (index >= num_queues) {
+               pr_warn_ratelimited("Invalid queue %hu for packet on interface %s\n.",
+                                   index, vif->dev->name);
+               index %= num_queues;
+       }
+       queue = &vif->queues[index];
+
+       /* Drop the packet if queue is not ready */
+       if (queue->task == NULL ||
+           queue->dealloc_task == NULL ||
            !xenvif_schedulable(vif))
                goto drop;
 
@@ -139,16 +202,16 @@ static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
         * then turn off the queue to give the ring a chance to
         * drain.
         */
-       if (!xenvif_rx_ring_slots_available(vif, min_slots_needed)) {
-               vif->wake_queue.function = xenvif_wake_queue;
-               vif->wake_queue.data = (unsigned long)vif;
-               xenvif_stop_queue(vif);
-               mod_timer(&vif->wake_queue,
+       if (!xenvif_rx_ring_slots_available(queue, min_slots_needed)) {
+               queue->wake_queue.function = xenvif_wake_queue_callback;
+               queue->wake_queue.data = (unsigned long)queue;
+               xenvif_stop_queue(queue);
+               mod_timer(&queue->wake_queue,
                        jiffies + rx_drain_timeout_jiffies);
        }
 
-       skb_queue_tail(&vif->rx_queue, skb);
-       xenvif_kick_thread(vif);
+       skb_queue_tail(&queue->rx_queue, skb);
+       xenvif_kick_thread(queue);
 
        return NETDEV_TX_OK;
 
@@ -161,25 +224,65 @@ static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev)
 static struct net_device_stats *xenvif_get_stats(struct net_device *dev)
 {
        struct xenvif *vif = netdev_priv(dev);
+       struct xenvif_queue *queue = NULL;
+       unsigned int num_queues = dev->real_num_tx_queues;
+       unsigned long rx_bytes = 0;
+       unsigned long rx_packets = 0;
+       unsigned long tx_bytes = 0;
+       unsigned long tx_packets = 0;
+       unsigned int index;
+
+       if (vif->queues == NULL)
+               goto out;
+
+       /* Aggregate tx and rx stats from each queue */
+       for (index = 0; index < num_queues; ++index) {
+               queue = &vif->queues[index];
+               rx_bytes += queue->stats.rx_bytes;
+               rx_packets += queue->stats.rx_packets;
+               tx_bytes += queue->stats.tx_bytes;
+               tx_packets += queue->stats.tx_packets;
+       }
+
+out:
+       vif->dev->stats.rx_bytes = rx_bytes;
+       vif->dev->stats.rx_packets = rx_packets;
+       vif->dev->stats.tx_bytes = tx_bytes;
+       vif->dev->stats.tx_packets = tx_packets;
+
        return &vif->dev->stats;
 }
 
 static void xenvif_up(struct xenvif *vif)
 {
-       napi_enable(&vif->napi);
-       enable_irq(vif->tx_irq);
-       if (vif->tx_irq != vif->rx_irq)
-               enable_irq(vif->rx_irq);
-       xenvif_napi_schedule_or_enable_events(vif);
+       struct xenvif_queue *queue = NULL;
+       unsigned int num_queues = vif->dev->real_num_tx_queues;
+       unsigned int queue_index;
+
+       for (queue_index = 0; queue_index < num_queues; ++queue_index) {
+               queue = &vif->queues[queue_index];
+               napi_enable(&queue->napi);
+               enable_irq(queue->tx_irq);
+               if (queue->tx_irq != queue->rx_irq)
+                       enable_irq(queue->rx_irq);
+               xenvif_napi_schedule_or_enable_events(queue);
+       }
 }
 
 static void xenvif_down(struct xenvif *vif)
 {
-       napi_disable(&vif->napi);
-       disable_irq(vif->tx_irq);
-       if (vif->tx_irq != vif->rx_irq)
-               disable_irq(vif->rx_irq);
-       del_timer_sync(&vif->credit_timeout);
+       struct xenvif_queue *queue = NULL;
+       unsigned int num_queues = vif->dev->real_num_tx_queues;
+       unsigned int queue_index;
+
+       for (queue_index = 0; queue_index < num_queues; ++queue_index) {
+               queue = &vif->queues[queue_index];
+               napi_disable(&queue->napi);
+               disable_irq(queue->tx_irq);
+               if (queue->tx_irq != queue->rx_irq)
+                       disable_irq(queue->rx_irq);
+               del_timer_sync(&queue->credit_timeout);
+       }
 }
 
 static int xenvif_open(struct net_device *dev)
@@ -187,7 +290,7 @@ static int xenvif_open(struct net_device *dev)
        struct xenvif *vif = netdev_priv(dev);
        if (netif_carrier_ok(dev))
                xenvif_up(vif);
-       netif_start_queue(dev);
+       netif_tx_start_all_queues(dev);
        return 0;
 }
 
@@ -196,7 +299,7 @@ static int xenvif_close(struct net_device *dev)
        struct xenvif *vif = netdev_priv(dev);
        if (netif_carrier_ok(dev))
                xenvif_down(vif);
-       netif_stop_queue(dev);
+       netif_tx_stop_all_queues(dev);
        return 0;
 }
 
@@ -236,29 +339,29 @@ static const struct xenvif_stat {
 } xenvif_stats[] = {
        {
                "rx_gso_checksum_fixup",
-               offsetof(struct xenvif, rx_gso_checksum_fixup)
+               offsetof(struct xenvif_stats, rx_gso_checksum_fixup)
        },
        /* If (sent != success + fail), there are probably packets never
         * freed up properly!
         */
        {
                "tx_zerocopy_sent",
-               offsetof(struct xenvif, tx_zerocopy_sent),
+               offsetof(struct xenvif_stats, tx_zerocopy_sent),
        },
        {
                "tx_zerocopy_success",
-               offsetof(struct xenvif, tx_zerocopy_success),
+               offsetof(struct xenvif_stats, tx_zerocopy_success),
        },
        {
                "tx_zerocopy_fail",
-               offsetof(struct xenvif, tx_zerocopy_fail)
+               offsetof(struct xenvif_stats, tx_zerocopy_fail)
        },
        /* Number of packets exceeding MAX_SKB_FRAG slots. You should use
         * a guest with the same MAX_SKB_FRAG
         */
        {
                "tx_frag_overflow",
-               offsetof(struct xenvif, tx_frag_overflow)
+               offsetof(struct xenvif_stats, tx_frag_overflow)
        },
 };
 
@@ -275,11 +378,20 @@ static int xenvif_get_sset_count(struct net_device *dev, int string_set)
 static void xenvif_get_ethtool_stats(struct net_device *dev,
                                     struct ethtool_stats *stats, u64 * data)
 {
-       void *vif = netdev_priv(dev);
+       struct xenvif *vif = netdev_priv(dev);
+       unsigned int num_queues = dev->real_num_tx_queues;
        int i;
-
-       for (i = 0; i < ARRAY_SIZE(xenvif_stats); i++)
-               data[i] = *(unsigned long *)(vif + xenvif_stats[i].offset);
+       unsigned int queue_index;
+       struct xenvif_stats *vif_stats;
+
+       for (i = 0; i < ARRAY_SIZE(xenvif_stats); i++) {
+               unsigned long accum = 0;
+               for (queue_index = 0; queue_index < num_queues; ++queue_index) {
+                       vif_stats = &vif->queues[queue_index].stats;
+                       accum += *(unsigned long *)(vif_stats + xenvif_stats[i].offset);
+               }
+               data[i] = accum;
+       }
 }
 
 static void xenvif_get_strings(struct net_device *dev, u32 stringset, u8 * data)
@@ -312,6 +424,7 @@ static const struct net_device_ops xenvif_netdev_ops = {
        .ndo_fix_features = xenvif_fix_features,
        .ndo_set_mac_address = eth_mac_addr,
        .ndo_validate_addr   = eth_validate_addr,
+       .ndo_select_queue = xenvif_select_queue,
 };
 
 struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
@@ -321,10 +434,14 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
        struct net_device *dev;
        struct xenvif *vif;
        char name[IFNAMSIZ] = {};
-       int i;
 
        snprintf(name, IFNAMSIZ - 1, "vif%u.%u", domid, handle);
-       dev = alloc_netdev(sizeof(struct xenvif), name, ether_setup);
+       /* Allocate a netdev with the max. supported number of queues.
+        * When the guest selects the desired number, it will be updated
+        * via netif_set_real_num_tx_queues().
+        */
+       dev = alloc_netdev_mq(sizeof(struct xenvif), name, ether_setup,
+                             xenvif_max_queues);
        if (dev == NULL) {
                pr_warn("Could not allocate netdev for %s\n", name);
                return ERR_PTR(-ENOMEM);
@@ -334,66 +451,28 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
 
        vif = netdev_priv(dev);
 
-       vif->grant_copy_op = vmalloc(sizeof(struct gnttab_copy) *
-                                    MAX_GRANT_COPY_OPS);
-       if (vif->grant_copy_op == NULL) {
-               pr_warn("Could not allocate grant copy space for %s\n", name);
-               free_netdev(dev);
-               return ERR_PTR(-ENOMEM);
-       }
-
        vif->domid  = domid;
        vif->handle = handle;
        vif->can_sg = 1;
        vif->ip_csum = 1;
        vif->dev = dev;
-
        vif->disabled = false;
 
-       vif->credit_bytes = vif->remaining_credit = ~0UL;
-       vif->credit_usec  = 0UL;
-       init_timer(&vif->credit_timeout);
-       vif->credit_window_start = get_jiffies_64();
-
-       init_timer(&vif->wake_queue);
+       /* Start out with no queues. The call below does not require
+        * rtnl_lock() as it happens before register_netdev().
+        */
+       vif->queues = NULL;
+       netif_set_real_num_tx_queues(dev, 0);
 
        dev->netdev_ops = &xenvif_netdev_ops;
        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->ethtool_ops = &xenvif_ethtool_ops;
 
        dev->tx_queue_len = XENVIF_QUEUE_LENGTH;
 
-       skb_queue_head_init(&vif->rx_queue);
-       skb_queue_head_init(&vif->tx_queue);
-
-       vif->pending_cons = 0;
-       vif->pending_prod = MAX_PENDING_REQS;
-       for (i = 0; i < MAX_PENDING_REQS; i++)
-               vif->pending_ring[i] = i;
-       spin_lock_init(&vif->callback_lock);
-       spin_lock_init(&vif->response_lock);
-       /* If ballooning is disabled, this will consume real memory, so you
-        * better enable it. The long term solution would be to use just a
-        * bunch of valid page descriptors, without dependency on ballooning
-        */
-       err = alloc_xenballooned_pages(MAX_PENDING_REQS,
-                                      vif->mmap_pages,
-                                      false);
-       if (err) {
-               netdev_err(dev, "Could not reserve mmap_pages\n");
-               return ERR_PTR(-ENOMEM);
-       }
-       for (i = 0; i < MAX_PENDING_REQS; i++) {
-               vif->pending_tx_info[i].callback_struct = (struct ubuf_info)
-                       { .callback = xenvif_zerocopy_callback,
-                         .ctx = NULL,
-                         .desc = i };
-               vif->grant_tx_handle[i] = NETBACK_INVALID_HANDLE;
-       }
-
        /*
         * Initialise a dummy MAC address. We choose the numerically
         * largest non-broadcast address to prevent the address getting
@@ -403,8 +482,6 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
        memset(dev->dev_addr, 0xFF, ETH_ALEN);
        dev->dev_addr[0] &= ~0x01;
 
-       netif_napi_add(dev, &vif->napi, xenvif_poll, XENVIF_NAPI_WEIGHT);
-
        netif_carrier_off(dev);
 
        err = register_netdev(dev);
@@ -421,98 +498,147 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid,
        return vif;
 }
 
-int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref,
+int xenvif_init_queue(struct xenvif_queue *queue)
+{
+       int err, i;
+
+       queue->credit_bytes = queue->remaining_credit = ~0UL;
+       queue->credit_usec  = 0UL;
+       init_timer(&queue->credit_timeout);
+       queue->credit_window_start = get_jiffies_64();
+
+       skb_queue_head_init(&queue->rx_queue);
+       skb_queue_head_init(&queue->tx_queue);
+
+       queue->pending_cons = 0;
+       queue->pending_prod = MAX_PENDING_REQS;
+       for (i = 0; i < MAX_PENDING_REQS; ++i)
+               queue->pending_ring[i] = i;
+
+       spin_lock_init(&queue->callback_lock);
+       spin_lock_init(&queue->response_lock);
+
+       /* If ballooning is disabled, this will consume real memory, so you
+        * better enable it. The long term solution would be to use just a
+        * bunch of valid page descriptors, without dependency on ballooning
+        */
+       err = alloc_xenballooned_pages(MAX_PENDING_REQS,
+                                      queue->mmap_pages,
+                                      false);
+       if (err) {
+               netdev_err(queue->vif->dev, "Could not reserve mmap_pages\n");
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < MAX_PENDING_REQS; i++) {
+               queue->pending_tx_info[i].callback_struct = (struct ubuf_info)
+                       { .callback = xenvif_zerocopy_callback,
+                         .ctx = NULL,
+                         .desc = i };
+               queue->grant_tx_handle[i] = NETBACK_INVALID_HANDLE;
+       }
+
+       init_timer(&queue->wake_queue);
+
+       netif_napi_add(queue->vif->dev, &queue->napi, xenvif_poll,
+                       XENVIF_NAPI_WEIGHT);
+
+       return 0;
+}
+
+void xenvif_carrier_on(struct xenvif *vif)
+{
+       rtnl_lock();
+       if (!vif->can_sg && vif->dev->mtu > ETH_DATA_LEN)
+               dev_set_mtu(vif->dev, ETH_DATA_LEN);
+       netdev_update_features(vif->dev);
+       netif_carrier_on(vif->dev);
+       if (netif_running(vif->dev))
+               xenvif_up(vif);
+       rtnl_unlock();
+}
+
+int xenvif_connect(struct xenvif_queue *queue, unsigned long tx_ring_ref,
                   unsigned long rx_ring_ref, unsigned int tx_evtchn,
                   unsigned int rx_evtchn)
 {
        struct task_struct *task;
        int err = -ENOMEM;
 
-       BUG_ON(vif->tx_irq);
-       BUG_ON(vif->task);
-       BUG_ON(vif->dealloc_task);
+       BUG_ON(queue->tx_irq);
+       BUG_ON(queue->task);
+       BUG_ON(queue->dealloc_task);
 
-       err = xenvif_map_frontend_rings(vif, tx_ring_ref, rx_ring_ref);
+       err = xenvif_map_frontend_rings(queue, tx_ring_ref, rx_ring_ref);
        if (err < 0)
                goto err;
 
-       init_waitqueue_head(&vif->wq);
-       init_waitqueue_head(&vif->dealloc_wq);
+       init_waitqueue_head(&queue->wq);
+       init_waitqueue_head(&queue->dealloc_wq);
 
        if (tx_evtchn == rx_evtchn) {
                /* feature-split-event-channels == 0 */
                err = bind_interdomain_evtchn_to_irqhandler(
-                       vif->domid, tx_evtchn, xenvif_interrupt, 0,
-                       vif->dev->name, vif);
+                       queue->vif->domid, tx_evtchn, xenvif_interrupt, 0,
+                       queue->name, queue);
                if (err < 0)
                        goto err_unmap;
-               vif->tx_irq = vif->rx_irq = err;
-               disable_irq(vif->tx_irq);
+               queue->tx_irq = queue->rx_irq = err;
+               disable_irq(queue->tx_irq);
        } else {
                /* feature-split-event-channels == 1 */
-               snprintf(vif->tx_irq_name, sizeof(vif->tx_irq_name),
-                        "%s-tx", vif->dev->name);
+               snprintf(queue->tx_irq_name, sizeof(queue->tx_irq_name),
+                        "%s-tx", queue->name);
                err = bind_interdomain_evtchn_to_irqhandler(
-                       vif->domid, tx_evtchn, xenvif_tx_interrupt, 0,
-                       vif->tx_irq_name, vif);
+                       queue->vif->domid, tx_evtchn, xenvif_tx_interrupt, 0,
+                       queue->tx_irq_name, queue);
                if (err < 0)
                        goto err_unmap;
-               vif->tx_irq = err;
-               disable_irq(vif->tx_irq);
+               queue->tx_irq = err;
+               disable_irq(queue->tx_irq);
 
-               snprintf(vif->rx_irq_name, sizeof(vif->rx_irq_name),
-                        "%s-rx", vif->dev->name);
+               snprintf(queue->rx_irq_name, sizeof(queue->rx_irq_name),
+                        "%s-rx", queue->name);
                err = bind_interdomain_evtchn_to_irqhandler(
-                       vif->domid, rx_evtchn, xenvif_rx_interrupt, 0,
-                       vif->rx_irq_name, vif);
+                       queue->vif->domid, rx_evtchn, xenvif_rx_interrupt, 0,
+                       queue->rx_irq_name, queue);
                if (err < 0)
                        goto err_tx_unbind;
-               vif->rx_irq = err;
-               disable_irq(vif->rx_irq);
+               queue->rx_irq = err;
+               disable_irq(queue->rx_irq);
        }
 
        task = kthread_create(xenvif_kthread_guest_rx,
-                             (void *)vif, "%s-guest-rx", vif->dev->name);
+                             (void *)queue, "%s-guest-rx", queue->name);
        if (IS_ERR(task)) {
-               pr_warn("Could not allocate kthread for %s\n", vif->dev->name);
+               pr_warn("Could not allocate kthread for %s\n", queue->name);
                err = PTR_ERR(task);
                goto err_rx_unbind;
        }
-
-       vif->task = task;
+       queue->task = task;
 
        task = kthread_create(xenvif_dealloc_kthread,
-                             (void *)vif, "%s-dealloc", vif->dev->name);
+                             (void *)queue, "%s-dealloc", queue->name);
        if (IS_ERR(task)) {
-               pr_warn("Could not allocate kthread for %s\n", vif->dev->name);
+               pr_warn("Could not allocate kthread for %s\n", queue->name);
                err = PTR_ERR(task);
                goto err_rx_unbind;
        }
+       queue->dealloc_task = task;
 
-       vif->dealloc_task = task;
-
-       rtnl_lock();
-       if (!vif->can_sg && vif->dev->mtu > ETH_DATA_LEN)
-               dev_set_mtu(vif->dev, ETH_DATA_LEN);
-       netdev_update_features(vif->dev);
-       netif_carrier_on(vif->dev);
-       if (netif_running(vif->dev))
-               xenvif_up(vif);
-       rtnl_unlock();
-
-       wake_up_process(vif->task);
-       wake_up_process(vif->dealloc_task);
+       wake_up_process(queue->task);
+       wake_up_process(queue->dealloc_task);
 
        return 0;
 
 err_rx_unbind:
-       unbind_from_irqhandler(vif->rx_irq, vif);
-       vif->rx_irq = 0;
+       unbind_from_irqhandler(queue->rx_irq, queue);
+       queue->rx_irq = 0;
 err_tx_unbind:
-       unbind_from_irqhandler(vif->tx_irq, vif);
-       vif->tx_irq = 0;
+       unbind_from_irqhandler(queue->tx_irq, queue);
+       queue->tx_irq = 0;
 err_unmap:
-       xenvif_unmap_frontend_rings(vif);
+       xenvif_unmap_frontend_rings(queue);
 err:
        module_put(THIS_MODULE);
        return err;
@@ -529,38 +655,77 @@ void xenvif_carrier_off(struct xenvif *vif)
        rtnl_unlock();
 }
 
+static void xenvif_wait_unmap_timeout(struct xenvif_queue *queue,
+                                     unsigned int worst_case_skb_lifetime)
+{
+       int i, unmap_timeout = 0;
+
+       for (i = 0; i < MAX_PENDING_REQS; ++i) {
+               if (queue->grant_tx_handle[i] != NETBACK_INVALID_HANDLE) {
+                       unmap_timeout++;
+                       schedule_timeout(msecs_to_jiffies(1000));
+                       if (unmap_timeout > worst_case_skb_lifetime &&
+                           net_ratelimit())
+                               netdev_err(queue->vif->dev,
+                                          "Page still granted! Index: %x\n",
+                                          i);
+                       i = -1;
+               }
+       }
+}
+
 void xenvif_disconnect(struct xenvif *vif)
 {
+       struct xenvif_queue *queue = NULL;
+       unsigned int num_queues = vif->dev->real_num_tx_queues;
+       unsigned int queue_index;
+
        if (netif_carrier_ok(vif->dev))
                xenvif_carrier_off(vif);
 
-       if (vif->task) {
-               del_timer_sync(&vif->wake_queue);
-               kthread_stop(vif->task);
-               vif->task = NULL;
-       }
+       for (queue_index = 0; queue_index < num_queues; ++queue_index) {
+               queue = &vif->queues[queue_index];
 
-       if (vif->dealloc_task) {
-               kthread_stop(vif->dealloc_task);
-               vif->dealloc_task = NULL;
-       }
+               if (queue->task) {
+                       del_timer_sync(&queue->wake_queue);
+                       kthread_stop(queue->task);
+                       queue->task = NULL;
+               }
 
-       if (vif->tx_irq) {
-               if (vif->tx_irq == vif->rx_irq)
-                       unbind_from_irqhandler(vif->tx_irq, vif);
-               else {
-                       unbind_from_irqhandler(vif->tx_irq, vif);
-                       unbind_from_irqhandler(vif->rx_irq, vif);
+               if (queue->dealloc_task) {
+                       kthread_stop(queue->dealloc_task);
+                       queue->dealloc_task = NULL;
                }
-               vif->tx_irq = 0;
+
+               if (queue->tx_irq) {
+                       if (queue->tx_irq == queue->rx_irq)
+                               unbind_from_irqhandler(queue->tx_irq, queue);
+                       else {
+                               unbind_from_irqhandler(queue->tx_irq, queue);
+                               unbind_from_irqhandler(queue->rx_irq, queue);
+                       }
+                       queue->tx_irq = 0;
+               }
+
+               xenvif_unmap_frontend_rings(queue);
        }
+}
 
-       xenvif_unmap_frontend_rings(vif);
+/* Reverse the relevant parts of xenvif_init_queue().
+ * Used for queue teardown from xenvif_free(), and on the
+ * error handling paths in xenbus.c:connect().
+ */
+void xenvif_deinit_queue(struct xenvif_queue *queue)
+{
+       free_xenballooned_pages(MAX_PENDING_REQS, queue->mmap_pages);
+       netif_napi_del(&queue->napi);
 }
 
 void xenvif_free(struct xenvif *vif)
 {
-       int i, unmap_timeout = 0;
+       struct xenvif_queue *queue = NULL;
+       unsigned int num_queues = vif->dev->real_num_tx_queues;
+       unsigned int queue_index;
        /* Here we want to avoid timeout messages if an skb can be legitimately
         * stuck somewhere else. Realistically this could be an another vif's
         * internal or QDisc queue. That another vif also has this
@@ -575,33 +740,21 @@ void xenvif_free(struct xenvif *vif)
        unsigned int worst_case_skb_lifetime = (rx_drain_timeout_msecs/1000) *
                DIV_ROUND_UP(XENVIF_QUEUE_LENGTH, (XEN_NETIF_RX_RING_SIZE / MAX_SKB_FRAGS));
 
-       for (i = 0; i < MAX_PENDING_REQS; ++i) {
-               if (vif->grant_tx_handle[i] != NETBACK_INVALID_HANDLE) {
-                       unmap_timeout++;
-                       schedule_timeout(msecs_to_jiffies(1000));
-                       if (unmap_timeout > worst_case_skb_lifetime &&
-                           net_ratelimit())
-                               netdev_err(vif->dev,
-                                          "Page still granted! Index: %x\n",
-                                          i);
-                       /* If there are still unmapped pages, reset the loop to
-                        * start checking again. We shouldn't exit here until
-                        * dealloc thread and NAPI instance release all the
-                        * pages. If a kernel bug causes the skbs to stall
-                        * somewhere, the interface cannot be brought down
-                        * properly.
-                        */
-                       i = -1;
-               }
-       }
-
-       free_xenballooned_pages(MAX_PENDING_REQS, vif->mmap_pages);
+       unregister_netdev(vif->dev);
 
-       netif_napi_del(&vif->napi);
+       for (queue_index = 0; queue_index < num_queues; ++queue_index) {
+               queue = &vif->queues[queue_index];
+               xenvif_wait_unmap_timeout(queue, worst_case_skb_lifetime);
+               xenvif_deinit_queue(queue);
+       }
 
-       unregister_netdev(vif->dev);
+       /* Free the array of queues. The call below does not require
+        * rtnl_lock() because it happens after unregister_netdev().
+        */
+       netif_set_real_num_tx_queues(vif->dev, 0);
+       vfree(vif->queues);
+       vif->queues = NULL;
 
-       vfree(vif->grant_copy_op);
        free_netdev(vif->dev);
 
        module_put(THIS_MODULE);
index 7367208ee8cdd8b324ce661b48aa69c1d884855b..1844a47636b67c821f03fc8a565092ab59749426 100644 (file)
@@ -62,6 +62,11 @@ unsigned int rx_drain_timeout_msecs = 10000;
 module_param(rx_drain_timeout_msecs, uint, 0444);
 unsigned int rx_drain_timeout_jiffies;
 
+unsigned int xenvif_max_queues;
+module_param_named(max_queues, xenvif_max_queues, uint, 0644);
+MODULE_PARM_DESC(max_queues,
+                "Maximum number of queues per virtual interface");
+
 /*
  * This is the maximum slots a skb can have. If a guest sends a skb
  * which exceeds this limit it is considered malicious.
@@ -70,33 +75,33 @@ unsigned int rx_drain_timeout_jiffies;
 static unsigned int fatal_skb_slots = FATAL_SKB_SLOTS_DEFAULT;
 module_param(fatal_skb_slots, uint, 0444);
 
-static void xenvif_idx_release(struct xenvif *vif, u16 pending_idx,
+static void xenvif_idx_release(struct xenvif_queue *queue, u16 pending_idx,
                               u8 status);
 
-static void make_tx_response(struct xenvif *vif,
+static void make_tx_response(struct xenvif_queue *queue,
                             struct xen_netif_tx_request *txp,
                             s8       st);
 
-static inline int tx_work_todo(struct xenvif *vif);
-static inline int rx_work_todo(struct xenvif *vif);
+static inline int tx_work_todo(struct xenvif_queue *queue);
+static inline int rx_work_todo(struct xenvif_queue *queue);
 
-static struct xen_netif_rx_response *make_rx_response(struct xenvif *vif,
+static struct xen_netif_rx_response *make_rx_response(struct xenvif_queue *queue,
                                             u16      id,
                                             s8       st,
                                             u16      offset,
                                             u16      size,
                                             u16      flags);
 
-static inline unsigned long idx_to_pfn(struct xenvif *vif,
+static inline unsigned long idx_to_pfn(struct xenvif_queue *queue,
                                       u16 idx)
 {
-       return page_to_pfn(vif->mmap_pages[idx]);
+       return page_to_pfn(queue->mmap_pages[idx]);
 }
 
-static inline unsigned long idx_to_kaddr(struct xenvif *vif,
+static inline unsigned long idx_to_kaddr(struct xenvif_queue *queue,
                                         u16 idx)
 {
-       return (unsigned long)pfn_to_kaddr(idx_to_pfn(vif, idx));
+       return (unsigned long)pfn_to_kaddr(idx_to_pfn(queue, idx));
 }
 
 #define callback_param(vif, pending_idx) \
@@ -104,13 +109,13 @@ static inline unsigned long idx_to_kaddr(struct xenvif *vif,
 
 /* Find the containing VIF's structure from a pointer in pending_tx_info array
  */
-static inline struct xenvif *ubuf_to_vif(const struct ubuf_info *ubuf)
+static inline struct xenvif_queue *ubuf_to_queue(const struct ubuf_info *ubuf)
 {
        u16 pending_idx = ubuf->desc;
        struct pending_tx_info *temp =
                container_of(ubuf, struct pending_tx_info, callback_struct);
        return container_of(temp - pending_idx,
-                           struct xenvif,
+                           struct xenvif_queue,
                            pending_tx_info[0]);
 }
 
@@ -136,24 +141,24 @@ static inline pending_ring_idx_t pending_index(unsigned i)
        return i & (MAX_PENDING_REQS-1);
 }
 
-bool xenvif_rx_ring_slots_available(struct xenvif *vif, int needed)
+bool xenvif_rx_ring_slots_available(struct xenvif_queue *queue, int needed)
 {
        RING_IDX prod, cons;
 
        do {
-               prod = vif->rx.sring->req_prod;
-               cons = vif->rx.req_cons;
+               prod = queue->rx.sring->req_prod;
+               cons = queue->rx.req_cons;
 
                if (prod - cons >= needed)
                        return true;
 
-               vif->rx.sring->req_event = prod + 1;
+               queue->rx.sring->req_event = prod + 1;
 
                /* Make sure event is visible before we check prod
                 * again.
                 */
                mb();
-       } while (vif->rx.sring->req_prod != prod);
+       } while (queue->rx.sring->req_prod != prod);
 
        return false;
 }
@@ -163,7 +168,8 @@ bool xenvif_rx_ring_slots_available(struct xenvif *vif, int needed)
  * adding 'size' bytes to a buffer which currently contains 'offset'
  * bytes.
  */
-static bool start_new_rx_buffer(int offset, unsigned long size, int head)
+static bool start_new_rx_buffer(int offset, unsigned long size, int head,
+                               bool full_coalesce)
 {
        /* simple case: we have completely filled the current buffer. */
        if (offset == MAX_BUFFER_OFFSET)
@@ -175,6 +181,7 @@ static bool start_new_rx_buffer(int offset, unsigned long size, int head)
         *     (i)   this frag would fit completely in the next buffer
         * and (ii)  there is already some data in the current buffer
         * and (iii) this is not the head buffer.
+        * and (iv)  there is no need to fully utilize the buffers
         *
         * Where:
         * - (i) stops us splitting a frag into two copies
@@ -185,6 +192,8 @@ static bool start_new_rx_buffer(int offset, unsigned long size, int head)
         *   by (ii) but is explicitly checked because
         *   netfront relies on the first buffer being
         *   non-empty and can crash otherwise.
+        * - (iv) is needed for skbs which can use up more than MAX_SKB_FRAGS
+        *   slot
         *
         * This means we will effectively linearise small
         * frags but do not needlessly split large buffers
@@ -192,7 +201,8 @@ static bool start_new_rx_buffer(int offset, unsigned long size, int head)
         * own buffers as before.
         */
        BUG_ON(size > MAX_BUFFER_OFFSET);
-       if ((offset + size > MAX_BUFFER_OFFSET) && offset && !head)
+       if ((offset + size > MAX_BUFFER_OFFSET) && offset && !head &&
+           !full_coalesce)
                return true;
 
        return false;
@@ -207,13 +217,13 @@ struct netrx_pending_operations {
        grant_ref_t copy_gref;
 };
 
-static struct xenvif_rx_meta *get_next_rx_buffer(struct xenvif *vif,
+static struct xenvif_rx_meta *get_next_rx_buffer(struct xenvif_queue *queue,
                                                 struct netrx_pending_operations *npo)
 {
        struct xenvif_rx_meta *meta;
        struct xen_netif_rx_request *req;
 
-       req = RING_GET_REQUEST(&vif->rx, vif->rx.req_cons++);
+       req = RING_GET_REQUEST(&queue->rx, queue->rx.req_cons++);
 
        meta = npo->meta + npo->meta_prod++;
        meta->gso_type = XEN_NETIF_GSO_TYPE_NONE;
@@ -227,15 +237,22 @@ static struct xenvif_rx_meta *get_next_rx_buffer(struct xenvif *vif,
        return meta;
 }
 
+struct xenvif_rx_cb {
+       int meta_slots_used;
+       bool full_coalesce;
+};
+
+#define XENVIF_RX_CB(skb) ((struct xenvif_rx_cb *)(skb)->cb)
+
 /*
  * Set up the grant operations for this fragment. If it's a flipping
  * interface, we also set up the unmap request from here.
  */
-static void xenvif_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb,
+static void xenvif_gop_frag_copy(struct xenvif_queue *queue, struct sk_buff *skb,
                                 struct netrx_pending_operations *npo,
                                 struct page *page, unsigned long size,
                                 unsigned long offset, int *head,
-                                struct xenvif *foreign_vif,
+                                struct xenvif_queue *foreign_queue,
                                 grant_ref_t foreign_gref)
 {
        struct gnttab_copy *copy_gop;
@@ -261,14 +278,17 @@ static void xenvif_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb,
                if (bytes > size)
                        bytes = size;
 
-               if (start_new_rx_buffer(npo->copy_off, bytes, *head)) {
+               if (start_new_rx_buffer(npo->copy_off,
+                                       bytes,
+                                       *head,
+                                       XENVIF_RX_CB(skb)->full_coalesce)) {
                        /*
                         * Netfront requires there to be some data in the head
                         * buffer.
                         */
                        BUG_ON(*head);
 
-                       meta = get_next_rx_buffer(vif, npo);
+                       meta = get_next_rx_buffer(queue, npo);
                }
 
                if (npo->copy_off + bytes > MAX_BUFFER_OFFSET)
@@ -278,8 +298,8 @@ static void xenvif_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb,
                copy_gop->flags = GNTCOPY_dest_gref;
                copy_gop->len = bytes;
 
-               if (foreign_vif) {
-                       copy_gop->source.domid = foreign_vif->domid;
+               if (foreign_queue) {
+                       copy_gop->source.domid = foreign_queue->vif->domid;
                        copy_gop->source.u.ref = foreign_gref;
                        copy_gop->flags |= GNTCOPY_source_gref;
                } else {
@@ -289,7 +309,7 @@ static void xenvif_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb,
                }
                copy_gop->source.offset = offset;
 
-               copy_gop->dest.domid = vif->domid;
+               copy_gop->dest.domid = queue->vif->domid;
                copy_gop->dest.offset = npo->copy_off;
                copy_gop->dest.u.ref = npo->copy_gref;
 
@@ -314,8 +334,8 @@ static void xenvif_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb,
                                gso_type = XEN_NETIF_GSO_TYPE_TCPV6;
                }
 
-               if (*head && ((1 << gso_type) & vif->gso_mask))
-                       vif->rx.req_cons++;
+               if (*head && ((1 << gso_type) & queue->vif->gso_mask))
+                       queue->rx.req_cons++;
 
                *head = 0; /* There must be something in this buffer now. */
 
@@ -337,13 +357,13 @@ static const struct ubuf_info *xenvif_find_gref(const struct sk_buff *const skb,
                                                const int i,
                                                const struct ubuf_info *ubuf)
 {
-       struct xenvif *foreign_vif = ubuf_to_vif(ubuf);
+       struct xenvif_queue *foreign_queue = ubuf_to_queue(ubuf);
 
        do {
                u16 pending_idx = ubuf->desc;
 
                if (skb_shinfo(skb)->frags[i].page.p ==
-                   foreign_vif->mmap_pages[pending_idx])
+                   foreign_queue->mmap_pages[pending_idx])
                        break;
                ubuf = (struct ubuf_info *) ubuf->ctx;
        } while (ubuf);
@@ -364,7 +384,8 @@ static const struct ubuf_info *xenvif_find_gref(const struct sk_buff *const skb,
  * frontend-side LRO).
  */
 static int xenvif_gop_skb(struct sk_buff *skb,
-                         struct netrx_pending_operations *npo)
+                         struct netrx_pending_operations *npo,
+                         struct xenvif_queue *queue)
 {
        struct xenvif *vif = netdev_priv(skb->dev);
        int nr_frags = skb_shinfo(skb)->nr_frags;
@@ -390,7 +411,7 @@ static int xenvif_gop_skb(struct sk_buff *skb,
 
        /* Set up a GSO prefix descriptor, if necessary */
        if ((1 << gso_type) & vif->gso_prefix_mask) {
-               req = RING_GET_REQUEST(&vif->rx, vif->rx.req_cons++);
+               req = RING_GET_REQUEST(&queue->rx, queue->rx.req_cons++);
                meta = npo->meta + npo->meta_prod++;
                meta->gso_type = gso_type;
                meta->gso_size = skb_shinfo(skb)->gso_size;
@@ -398,7 +419,7 @@ static int xenvif_gop_skb(struct sk_buff *skb,
                meta->id = req->id;
        }
 
-       req = RING_GET_REQUEST(&vif->rx, vif->rx.req_cons++);
+       req = RING_GET_REQUEST(&queue->rx, queue->rx.req_cons++);
        meta = npo->meta + npo->meta_prod++;
 
        if ((1 << gso_type) & vif->gso_mask) {
@@ -422,7 +443,7 @@ static int xenvif_gop_skb(struct sk_buff *skb,
                if (data + len > skb_tail_pointer(skb))
                        len = skb_tail_pointer(skb) - data;
 
-               xenvif_gop_frag_copy(vif, skb, npo,
+               xenvif_gop_frag_copy(queue, skb, npo,
                                     virt_to_page(data), len, offset, &head,
                                     NULL,
                                     0);
@@ -433,7 +454,7 @@ static int xenvif_gop_skb(struct sk_buff *skb,
                /* This variable also signals whether foreign_gref has a real
                 * value or not.
                 */
-               struct xenvif *foreign_vif = NULL;
+               struct xenvif_queue *foreign_queue = NULL;
                grant_ref_t foreign_gref;
 
                if ((skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) &&
@@ -458,8 +479,9 @@ static int xenvif_gop_skb(struct sk_buff *skb,
                        if (likely(ubuf)) {
                                u16 pending_idx = ubuf->desc;
 
-                               foreign_vif = ubuf_to_vif(ubuf);
-                               foreign_gref = foreign_vif->pending_tx_info[pending_idx].req.gref;
+                               foreign_queue = ubuf_to_queue(ubuf);
+                               foreign_gref =
+                                       foreign_queue->pending_tx_info[pending_idx].req.gref;
                                /* Just a safety measure. If this was the last
                                 * element on the list, the for loop will
                                 * iterate again if a local page were added to
@@ -477,13 +499,13 @@ static int xenvif_gop_skb(struct sk_buff *skb,
                                 */
                                ubuf = head_ubuf;
                }
-               xenvif_gop_frag_copy(vif, skb, npo,
+               xenvif_gop_frag_copy(queue, skb, npo,
                                     skb_frag_page(&skb_shinfo(skb)->frags[i]),
                                     skb_frag_size(&skb_shinfo(skb)->frags[i]),
                                     skb_shinfo(skb)->frags[i].page_offset,
                                     &head,
-                                    foreign_vif,
-                                    foreign_vif ? foreign_gref : UINT_MAX);
+                                    foreign_queue,
+                                    foreign_queue ? foreign_gref : UINT_MAX);
        }
 
        return npo->meta_prod - old_meta_prod;
@@ -515,7 +537,7 @@ static int xenvif_check_gop(struct xenvif *vif, int nr_meta_slots,
        return status;
 }
 
-static void xenvif_add_frag_responses(struct xenvif *vif, int status,
+static void xenvif_add_frag_responses(struct xenvif_queue *queue, int status,
                                      struct xenvif_rx_meta *meta,
                                      int nr_meta_slots)
 {
@@ -536,23 +558,17 @@ static void xenvif_add_frag_responses(struct xenvif *vif, int status,
                        flags = XEN_NETRXF_more_data;
 
                offset = 0;
-               make_rx_response(vif, meta[i].id, status, offset,
+               make_rx_response(queue, meta[i].id, status, offset,
                                 meta[i].size, flags);
        }
 }
 
-struct xenvif_rx_cb {
-       int meta_slots_used;
-};
-
-#define XENVIF_RX_CB(skb) ((struct xenvif_rx_cb *)(skb)->cb)
-
-void xenvif_kick_thread(struct xenvif *vif)
+void xenvif_kick_thread(struct xenvif_queue *queue)
 {
-       wake_up(&vif->wq);
+       wake_up(&queue->wq);
 }
 
-static void xenvif_rx_action(struct xenvif *vif)
+static void xenvif_rx_action(struct xenvif_queue *queue)
 {
        s8 status;
        u16 flags;
@@ -565,13 +581,13 @@ static void xenvif_rx_action(struct xenvif *vif)
        bool need_to_notify = false;
 
        struct netrx_pending_operations npo = {
-               .copy  = vif->grant_copy_op,
-               .meta  = vif->meta,
+               .copy  = queue->grant_copy_op,
+               .meta  = queue->meta,
        };
 
        skb_queue_head_init(&rxq);
 
-       while ((skb = skb_dequeue(&vif->rx_queue)) != NULL) {
+       while ((skb = skb_dequeue(&queue->rx_queue)) != NULL) {
                RING_IDX max_slots_needed;
                RING_IDX old_req_cons;
                RING_IDX ring_slots_used;
@@ -602,10 +618,15 @@ static void xenvif_rx_action(struct xenvif *vif)
 
                /* To avoid the estimate becoming too pessimal for some
                 * frontends that limit posted rx requests, cap the estimate
-                * at MAX_SKB_FRAGS.
+                * at MAX_SKB_FRAGS. In this case netback will fully coalesce
+                * the skb into the provided slots.
                 */
-               if (max_slots_needed > MAX_SKB_FRAGS)
+               if (max_slots_needed > MAX_SKB_FRAGS) {
                        max_slots_needed = MAX_SKB_FRAGS;
+                       XENVIF_RX_CB(skb)->full_coalesce = true;
+               } else {
+                       XENVIF_RX_CB(skb)->full_coalesce = false;
+               }
 
                /* We may need one more slot for GSO metadata */
                if (skb_is_gso(skb) &&
@@ -614,42 +635,42 @@ static void xenvif_rx_action(struct xenvif *vif)
                        max_slots_needed++;
 
                /* If the skb may not fit then bail out now */
-               if (!xenvif_rx_ring_slots_available(vif, max_slots_needed)) {
-                       skb_queue_head(&vif->rx_queue, skb);
+               if (!xenvif_rx_ring_slots_available(queue, max_slots_needed)) {
+                       skb_queue_head(&queue->rx_queue, skb);
                        need_to_notify = true;
-                       vif->rx_last_skb_slots = max_slots_needed;
+                       queue->rx_last_skb_slots = max_slots_needed;
                        break;
                } else
-                       vif->rx_last_skb_slots = 0;
+                       queue->rx_last_skb_slots = 0;
 
-               old_req_cons = vif->rx.req_cons;
-               XENVIF_RX_CB(skb)->meta_slots_used = xenvif_gop_skb(skb, &npo);
-               ring_slots_used = vif->rx.req_cons - old_req_cons;
+               old_req_cons = queue->rx.req_cons;
+               XENVIF_RX_CB(skb)->meta_slots_used = xenvif_gop_skb(skb, &npo, queue);
+               ring_slots_used = queue->rx.req_cons - old_req_cons;
 
                BUG_ON(ring_slots_used > max_slots_needed);
 
                __skb_queue_tail(&rxq, skb);
        }
 
-       BUG_ON(npo.meta_prod > ARRAY_SIZE(vif->meta));
+       BUG_ON(npo.meta_prod > ARRAY_SIZE(queue->meta));
 
        if (!npo.copy_prod)
                goto done;
 
        BUG_ON(npo.copy_prod > MAX_GRANT_COPY_OPS);
-       gnttab_batch_copy(vif->grant_copy_op, npo.copy_prod);
+       gnttab_batch_copy(queue->grant_copy_op, npo.copy_prod);
 
        while ((skb = __skb_dequeue(&rxq)) != NULL) {
 
-               if ((1 << vif->meta[npo.meta_cons].gso_type) &
-                   vif->gso_prefix_mask) {
-                       resp = RING_GET_RESPONSE(&vif->rx,
-                                                vif->rx.rsp_prod_pvt++);
+               if ((1 << queue->meta[npo.meta_cons].gso_type) &
+                   queue->vif->gso_prefix_mask) {
+                       resp = RING_GET_RESPONSE(&queue->rx,
+                                                queue->rx.rsp_prod_pvt++);
 
                        resp->flags = XEN_NETRXF_gso_prefix | XEN_NETRXF_more_data;
 
-                       resp->offset = vif->meta[npo.meta_cons].gso_size;
-                       resp->id = vif->meta[npo.meta_cons].id;
+                       resp->offset = queue->meta[npo.meta_cons].gso_size;
+                       resp->id = queue->meta[npo.meta_cons].id;
                        resp->status = XENVIF_RX_CB(skb)->meta_slots_used;
 
                        npo.meta_cons++;
@@ -657,10 +678,10 @@ static void xenvif_rx_action(struct xenvif *vif)
                }
 
 
-               vif->dev->stats.tx_bytes += skb->len;
-               vif->dev->stats.tx_packets++;
+               queue->stats.tx_bytes += skb->len;
+               queue->stats.tx_packets++;
 
-               status = xenvif_check_gop(vif,
+               status = xenvif_check_gop(queue->vif,
                                          XENVIF_RX_CB(skb)->meta_slots_used,
                                          &npo);
 
@@ -676,22 +697,22 @@ static void xenvif_rx_action(struct xenvif *vif)
                        flags |= XEN_NETRXF_data_validated;
 
                offset = 0;
-               resp = make_rx_response(vif, vif->meta[npo.meta_cons].id,
+               resp = make_rx_response(queue, queue->meta[npo.meta_cons].id,
                                        status, offset,
-                                       vif->meta[npo.meta_cons].size,
+                                       queue->meta[npo.meta_cons].size,
                                        flags);
 
-               if ((1 << vif->meta[npo.meta_cons].gso_type) &
-                   vif->gso_mask) {
+               if ((1 << queue->meta[npo.meta_cons].gso_type) &
+                   queue->vif->gso_mask) {
                        struct xen_netif_extra_info *gso =
                                (struct xen_netif_extra_info *)
-                               RING_GET_RESPONSE(&vif->rx,
-                                                 vif->rx.rsp_prod_pvt++);
+                               RING_GET_RESPONSE(&queue->rx,
+                                                 queue->rx.rsp_prod_pvt++);
 
                        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 = queue->meta[npo.meta_cons].gso_type;
+                       gso->u.gso.size = queue->meta[npo.meta_cons].gso_size;
                        gso->u.gso.pad = 0;
                        gso->u.gso.features = 0;
 
@@ -699,11 +720,11 @@ static void xenvif_rx_action(struct xenvif *vif)
                        gso->flags = 0;
                }
 
-               xenvif_add_frag_responses(vif, status,
-                                         vif->meta + npo.meta_cons + 1,
+               xenvif_add_frag_responses(queue, status,
+                                         queue->meta + npo.meta_cons + 1,
                                          XENVIF_RX_CB(skb)->meta_slots_used);
 
-               RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&vif->rx, ret);
+               RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&queue->rx, ret);
 
                need_to_notify |= !!ret;
 
@@ -713,20 +734,20 @@ static void xenvif_rx_action(struct xenvif *vif)
 
 done:
        if (need_to_notify)
-               notify_remote_via_irq(vif->rx_irq);
+               notify_remote_via_irq(queue->rx_irq);
 }
 
-void xenvif_napi_schedule_or_enable_events(struct xenvif *vif)
+void xenvif_napi_schedule_or_enable_events(struct xenvif_queue *queue)
 {
        int more_to_do;
 
-       RING_FINAL_CHECK_FOR_REQUESTS(&vif->tx, more_to_do);
+       RING_FINAL_CHECK_FOR_REQUESTS(&queue->tx, more_to_do);
 
        if (more_to_do)
-               napi_schedule(&vif->napi);
+               napi_schedule(&queue->napi);
 }
 
-static void tx_add_credit(struct xenvif *vif)
+static void tx_add_credit(struct xenvif_queue *queue)
 {
        unsigned long max_burst, max_credit;
 
@@ -734,55 +755,57 @@ static void tx_add_credit(struct xenvif *vif)
         * Allow a burst big enough to transmit a jumbo packet of up to 128kB.
         * Otherwise the interface can seize up due to insufficient credit.
         */
-       max_burst = RING_GET_REQUEST(&vif->tx, vif->tx.req_cons)->size;
+       max_burst = RING_GET_REQUEST(&queue->tx, queue->tx.req_cons)->size;
        max_burst = min(max_burst, 131072UL);
-       max_burst = max(max_burst, vif->credit_bytes);
+       max_burst = max(max_burst, queue->credit_bytes);
 
        /* Take care that adding a new chunk of credit doesn't wrap to zero. */
-       max_credit = vif->remaining_credit + vif->credit_bytes;
-       if (max_credit < vif->remaining_credit)
+       max_credit = queue->remaining_credit + queue->credit_bytes;
+       if (max_credit < queue->remaining_credit)
                max_credit = ULONG_MAX; /* wrapped: clamp to ULONG_MAX */
 
-       vif->remaining_credit = min(max_credit, max_burst);
+       queue->remaining_credit = min(max_credit, max_burst);
 }
 
 static void tx_credit_callback(unsigned long data)
 {
-       struct xenvif *vif = (struct xenvif *)data;
-       tx_add_credit(vif);
-       xenvif_napi_schedule_or_enable_events(vif);
+       struct xenvif_queue *queue = (struct xenvif_queue *)data;
+       tx_add_credit(queue);
+       xenvif_napi_schedule_or_enable_events(queue);
 }
 
-static void xenvif_tx_err(struct xenvif *vif,
+static void xenvif_tx_err(struct xenvif_queue *queue,
                          struct xen_netif_tx_request *txp, RING_IDX end)
 {
-       RING_IDX cons = vif->tx.req_cons;
+       RING_IDX cons = queue->tx.req_cons;
        unsigned long flags;
 
        do {
-               spin_lock_irqsave(&vif->response_lock, flags);
-               make_tx_response(vif, txp, XEN_NETIF_RSP_ERROR);
-               spin_unlock_irqrestore(&vif->response_lock, flags);
+               spin_lock_irqsave(&queue->response_lock, flags);
+               make_tx_response(queue, txp, XEN_NETIF_RSP_ERROR);
+               spin_unlock_irqrestore(&queue->response_lock, flags);
                if (cons == end)
                        break;
-               txp = RING_GET_REQUEST(&vif->tx, cons++);
+               txp = RING_GET_REQUEST(&queue->tx, cons++);
        } while (1);
-       vif->tx.req_cons = cons;
+       queue->tx.req_cons = cons;
 }
 
 static void xenvif_fatal_tx_err(struct xenvif *vif)
 {
        netdev_err(vif->dev, "fatal error; disabling device\n");
        vif->disabled = true;
-       xenvif_kick_thread(vif);
+       /* Disable the vif from queue 0's kthread */
+       if (vif->queues)
+               xenvif_kick_thread(&vif->queues[0]);
 }
 
-static int xenvif_count_requests(struct xenvif *vif,
+static int xenvif_count_requests(struct xenvif_queue *queue,
                                 struct xen_netif_tx_request *first,
                                 struct xen_netif_tx_request *txp,
                                 int work_to_do)
 {
-       RING_IDX cons = vif->tx.req_cons;
+       RING_IDX cons = queue->tx.req_cons;
        int slots = 0;
        int drop_err = 0;
        int more_data;
@@ -794,10 +817,10 @@ static int xenvif_count_requests(struct xenvif *vif,
                struct xen_netif_tx_request dropped_tx = { 0 };
 
                if (slots >= work_to_do) {
-                       netdev_err(vif->dev,
+                       netdev_err(queue->vif->dev,
                                   "Asked for %d slots but exceeds this limit\n",
                                   work_to_do);
-                       xenvif_fatal_tx_err(vif);
+                       xenvif_fatal_tx_err(queue->vif);
                        return -ENODATA;
                }
 
@@ -805,10 +828,10 @@ static int xenvif_count_requests(struct xenvif *vif,
                 * considered malicious.
                 */
                if (unlikely(slots >= fatal_skb_slots)) {
-                       netdev_err(vif->dev,
+                       netdev_err(queue->vif->dev,
                                   "Malicious frontend using %d slots, threshold %u\n",
                                   slots, fatal_skb_slots);
-                       xenvif_fatal_tx_err(vif);
+                       xenvif_fatal_tx_err(queue->vif);
                        return -E2BIG;
                }
 
@@ -821,7 +844,7 @@ static int xenvif_count_requests(struct xenvif *vif,
                 */
                if (!drop_err && slots >= XEN_NETBK_LEGACY_SLOTS_MAX) {
                        if (net_ratelimit())
-                               netdev_dbg(vif->dev,
+                               netdev_dbg(queue->vif->dev,
                                           "Too many slots (%d) exceeding limit (%d), dropping packet\n",
                                           slots, XEN_NETBK_LEGACY_SLOTS_MAX);
                        drop_err = -E2BIG;
@@ -830,7 +853,7 @@ static int xenvif_count_requests(struct xenvif *vif,
                if (drop_err)
                        txp = &dropped_tx;
 
-               memcpy(txp, RING_GET_REQUEST(&vif->tx, cons + slots),
+               memcpy(txp, RING_GET_REQUEST(&queue->tx, cons + slots),
                       sizeof(*txp));
 
                /* If the guest submitted a frame >= 64 KiB then
@@ -844,7 +867,7 @@ static int xenvif_count_requests(struct xenvif *vif,
                 */
                if (!drop_err && txp->size > first->size) {
                        if (net_ratelimit())
-                               netdev_dbg(vif->dev,
+                               netdev_dbg(queue->vif->dev,
                                           "Invalid tx request, slot size %u > remaining size %u\n",
                                           txp->size, first->size);
                        drop_err = -EIO;
@@ -854,9 +877,9 @@ static int xenvif_count_requests(struct xenvif *vif,
                slots++;
 
                if (unlikely((txp->offset + txp->size) > PAGE_SIZE)) {
-                       netdev_err(vif->dev, "Cross page boundary, txp->offset: %x, size: %u\n",
+                       netdev_err(queue->vif->dev, "Cross page boundary, txp->offset: %x, size: %u\n",
                                 txp->offset, txp->size);
-                       xenvif_fatal_tx_err(vif);
+                       xenvif_fatal_tx_err(queue->vif);
                        return -EINVAL;
                }
 
@@ -868,7 +891,7 @@ static int xenvif_count_requests(struct xenvif *vif,
        } while (more_data);
 
        if (drop_err) {
-               xenvif_tx_err(vif, first, cons + slots);
+               xenvif_tx_err(queue, first, cons + slots);
                return drop_err;
        }
 
@@ -882,17 +905,17 @@ struct xenvif_tx_cb {
 
 #define XENVIF_TX_CB(skb) ((struct xenvif_tx_cb *)(skb)->cb)
 
-static inline void xenvif_tx_create_map_op(struct xenvif *vif,
+static inline void xenvif_tx_create_map_op(struct xenvif_queue *queue,
                                          u16 pending_idx,
                                          struct xen_netif_tx_request *txp,
                                          struct gnttab_map_grant_ref *mop)
 {
-       vif->pages_to_map[mop-vif->tx_map_ops] = vif->mmap_pages[pending_idx];
-       gnttab_set_map_op(mop, idx_to_kaddr(vif, pending_idx),
+       queue->pages_to_map[mop-queue->tx_map_ops] = queue->mmap_pages[pending_idx];
+       gnttab_set_map_op(mop, idx_to_kaddr(queue, pending_idx),
                          GNTMAP_host_map | GNTMAP_readonly,
-                         txp->gref, vif->domid);
+                         txp->gref, queue->vif->domid);
 
-       memcpy(&vif->pending_tx_info[pending_idx].req, txp,
+       memcpy(&queue->pending_tx_info[pending_idx].req, txp,
               sizeof(*txp));
 }
 
@@ -913,7 +936,7 @@ static inline struct sk_buff *xenvif_alloc_skb(unsigned int size)
        return skb;
 }
 
-static struct gnttab_map_grant_ref *xenvif_get_requests(struct xenvif *vif,
+static struct gnttab_map_grant_ref *xenvif_get_requests(struct xenvif_queue *queue,
                                                        struct sk_buff *skb,
                                                        struct xen_netif_tx_request *txp,
                                                        struct gnttab_map_grant_ref *gop)
@@ -940,9 +963,9 @@ static struct gnttab_map_grant_ref *xenvif_get_requests(struct xenvif *vif,
 
        for (shinfo->nr_frags = start; shinfo->nr_frags < nr_slots;
             shinfo->nr_frags++, txp++, gop++) {
-               index = pending_index(vif->pending_cons++);
-               pending_idx = vif->pending_ring[index];
-               xenvif_tx_create_map_op(vif, pending_idx, txp, gop);
+               index = pending_index(queue->pending_cons++);
+               pending_idx = queue->pending_ring[index];
+               xenvif_tx_create_map_op(queue, pending_idx, txp, gop);
                frag_set_pending_idx(&frags[shinfo->nr_frags], pending_idx);
        }
 
@@ -950,7 +973,7 @@ static struct gnttab_map_grant_ref *xenvif_get_requests(struct xenvif *vif,
                struct sk_buff *nskb = xenvif_alloc_skb(0);
                if (unlikely(nskb == NULL)) {
                        if (net_ratelimit())
-                               netdev_err(vif->dev,
+                               netdev_err(queue->vif->dev,
                                           "Can't allocate the frag_list skb.\n");
                        return NULL;
                }
@@ -960,9 +983,9 @@ static struct gnttab_map_grant_ref *xenvif_get_requests(struct xenvif *vif,
 
                for (shinfo->nr_frags = 0; shinfo->nr_frags < frag_overflow;
                     shinfo->nr_frags++, txp++, gop++) {
-                       index = pending_index(vif->pending_cons++);
-                       pending_idx = vif->pending_ring[index];
-                       xenvif_tx_create_map_op(vif, pending_idx, txp, gop);
+                       index = pending_index(queue->pending_cons++);
+                       pending_idx = queue->pending_ring[index];
+                       xenvif_tx_create_map_op(queue, pending_idx, txp, gop);
                        frag_set_pending_idx(&frags[shinfo->nr_frags],
                                             pending_idx);
                }
@@ -973,34 +996,34 @@ static struct gnttab_map_grant_ref *xenvif_get_requests(struct xenvif *vif,
        return gop;
 }
 
-static inline void xenvif_grant_handle_set(struct xenvif *vif,
+static inline void xenvif_grant_handle_set(struct xenvif_queue *queue,
                                           u16 pending_idx,
                                           grant_handle_t handle)
 {
-       if (unlikely(vif->grant_tx_handle[pending_idx] !=
+       if (unlikely(queue->grant_tx_handle[pending_idx] !=
                     NETBACK_INVALID_HANDLE)) {
-               netdev_err(vif->dev,
+               netdev_err(queue->vif->dev,
                           "Trying to overwrite active handle! pending_idx: %x\n",
                           pending_idx);
                BUG();
        }
-       vif->grant_tx_handle[pending_idx] = handle;
+       queue->grant_tx_handle[pending_idx] = handle;
 }
 
-static inline void xenvif_grant_handle_reset(struct xenvif *vif,
+static inline void xenvif_grant_handle_reset(struct xenvif_queue *queue,
                                             u16 pending_idx)
 {
-       if (unlikely(vif->grant_tx_handle[pending_idx] ==
+       if (unlikely(queue->grant_tx_handle[pending_idx] ==
                     NETBACK_INVALID_HANDLE)) {
-               netdev_err(vif->dev,
+               netdev_err(queue->vif->dev,
                           "Trying to unmap invalid handle! pending_idx: %x\n",
                           pending_idx);
                BUG();
        }
-       vif->grant_tx_handle[pending_idx] = NETBACK_INVALID_HANDLE;
+       queue->grant_tx_handle[pending_idx] = NETBACK_INVALID_HANDLE;
 }
 
-static int xenvif_tx_check_gop(struct xenvif *vif,
+static int xenvif_tx_check_gop(struct xenvif_queue *queue,
                               struct sk_buff *skb,
                               struct gnttab_map_grant_ref **gopp_map,
                               struct gnttab_copy **gopp_copy)
@@ -1017,12 +1040,12 @@ static int xenvif_tx_check_gop(struct xenvif *vif,
        (*gopp_copy)++;
        if (unlikely(err)) {
                if (net_ratelimit())
-                       netdev_dbg(vif->dev,
+                       netdev_dbg(queue->vif->dev,
                                   "Grant copy of header failed! status: %d pending_idx: %u ref: %u\n",
                                   (*gopp_copy)->status,
                                   pending_idx,
                                   (*gopp_copy)->source.u.ref);
-               xenvif_idx_release(vif, pending_idx, XEN_NETIF_RSP_ERROR);
+               xenvif_idx_release(queue, pending_idx, XEN_NETIF_RSP_ERROR);
        }
 
 check_frags:
@@ -1035,24 +1058,24 @@ static int xenvif_tx_check_gop(struct xenvif *vif,
                newerr = gop_map->status;
 
                if (likely(!newerr)) {
-                       xenvif_grant_handle_set(vif,
+                       xenvif_grant_handle_set(queue,
                                                pending_idx,
                                                gop_map->handle);
                        /* Had a previous error? Invalidate this fragment. */
                        if (unlikely(err))
-                               xenvif_idx_unmap(vif, pending_idx);
+                               xenvif_idx_unmap(queue, pending_idx);
                        continue;
                }
 
                /* Error on this fragment: respond to client with an error. */
                if (net_ratelimit())
-                       netdev_dbg(vif->dev,
+                       netdev_dbg(queue->vif->dev,
                                   "Grant map of %d. frag failed! status: %d pending_idx: %u ref: %u\n",
                                   i,
                                   gop_map->status,
                                   pending_idx,
                                   gop_map->ref);
-               xenvif_idx_release(vif, pending_idx, XEN_NETIF_RSP_ERROR);
+               xenvif_idx_release(queue, pending_idx, XEN_NETIF_RSP_ERROR);
 
                /* Not the first error? Preceding frags already invalidated. */
                if (err)
@@ -1060,7 +1083,7 @@ static int xenvif_tx_check_gop(struct xenvif *vif,
                /* First error: invalidate preceding fragments. */
                for (j = 0; j < i; j++) {
                        pending_idx = frag_get_pending_idx(&shinfo->frags[j]);
-                       xenvif_idx_unmap(vif, pending_idx);
+                       xenvif_idx_unmap(queue, pending_idx);
                }
 
                /* Remember the error: invalidate all subsequent fragments. */
@@ -1084,7 +1107,7 @@ static int xenvif_tx_check_gop(struct xenvif *vif,
                shinfo = skb_shinfo(first_skb);
                for (j = 0; j < shinfo->nr_frags; j++) {
                        pending_idx = frag_get_pending_idx(&shinfo->frags[j]);
-                       xenvif_idx_unmap(vif, pending_idx);
+                       xenvif_idx_unmap(queue, pending_idx);
                }
        }
 
@@ -1092,7 +1115,7 @@ static int xenvif_tx_check_gop(struct xenvif *vif,
        return err;
 }
 
-static void xenvif_fill_frags(struct xenvif *vif, struct sk_buff *skb)
+static void xenvif_fill_frags(struct xenvif_queue *queue, struct sk_buff *skb)
 {
        struct skb_shared_info *shinfo = skb_shinfo(skb);
        int nr_frags = shinfo->nr_frags;
@@ -1110,23 +1133,23 @@ static void xenvif_fill_frags(struct xenvif *vif, struct sk_buff *skb)
                /* If this is not the first frag, chain it to the previous*/
                if (prev_pending_idx == INVALID_PENDING_IDX)
                        skb_shinfo(skb)->destructor_arg =
-                               &callback_param(vif, pending_idx);
+                               &callback_param(queue, pending_idx);
                else
-                       callback_param(vif, prev_pending_idx).ctx =
-                               &callback_param(vif, pending_idx);
+                       callback_param(queue, prev_pending_idx).ctx =
+                               &callback_param(queue, pending_idx);
 
-               callback_param(vif, pending_idx).ctx = NULL;
+               callback_param(queue, pending_idx).ctx = NULL;
                prev_pending_idx = pending_idx;
 
-               txp = &vif->pending_tx_info[pending_idx].req;
-               page = virt_to_page(idx_to_kaddr(vif, pending_idx));
+               txp = &queue->pending_tx_info[pending_idx].req;
+               page = virt_to_page(idx_to_kaddr(queue, pending_idx));
                __skb_fill_page_desc(skb, i, page, txp->offset, txp->size);
                skb->len += txp->size;
                skb->data_len += txp->size;
                skb->truesize += txp->size;
 
                /* Take an extra reference to offset network stack's put_page */
-               get_page(vif->mmap_pages[pending_idx]);
+               get_page(queue->mmap_pages[pending_idx]);
        }
        /* FIXME: __skb_fill_page_desc set this to true because page->pfmemalloc
         * overlaps with "index", and "mapping" is not set. I think mapping
@@ -1136,33 +1159,33 @@ static void xenvif_fill_frags(struct xenvif *vif, struct sk_buff *skb)
        skb->pfmemalloc = false;
 }
 
-static int xenvif_get_extras(struct xenvif *vif,
+static int xenvif_get_extras(struct xenvif_queue *queue,
                                struct xen_netif_extra_info *extras,
                                int work_to_do)
 {
        struct xen_netif_extra_info extra;
-       RING_IDX cons = vif->tx.req_cons;
+       RING_IDX cons = queue->tx.req_cons;
 
        do {
                if (unlikely(work_to_do-- <= 0)) {
-                       netdev_err(vif->dev, "Missing extra info\n");
-                       xenvif_fatal_tx_err(vif);
+                       netdev_err(queue->vif->dev, "Missing extra info\n");
+                       xenvif_fatal_tx_err(queue->vif);
                        return -EBADR;
                }
 
-               memcpy(&extra, RING_GET_REQUEST(&vif->tx, cons),
+               memcpy(&extra, RING_GET_REQUEST(&queue->tx, cons),
                       sizeof(extra));
                if (unlikely(!extra.type ||
                             extra.type >= XEN_NETIF_EXTRA_TYPE_MAX)) {
-                       vif->tx.req_cons = ++cons;
-                       netdev_err(vif->dev,
+                       queue->tx.req_cons = ++cons;
+                       netdev_err(queue->vif->dev,
                                   "Invalid extra type: %d\n", extra.type);
-                       xenvif_fatal_tx_err(vif);
+                       xenvif_fatal_tx_err(queue->vif);
                        return -EINVAL;
                }
 
                memcpy(&extras[extra.type - 1], &extra, sizeof(extra));
-               vif->tx.req_cons = ++cons;
+               queue->tx.req_cons = ++cons;
        } while (extra.flags & XEN_NETIF_EXTRA_FLAG_MORE);
 
        return work_to_do;
@@ -1197,7 +1220,7 @@ static int xenvif_set_skb_gso(struct xenvif *vif,
        return 0;
 }
 
-static int checksum_setup(struct xenvif *vif, struct sk_buff *skb)
+static int checksum_setup(struct xenvif_queue *queue, struct sk_buff *skb)
 {
        bool recalculate_partial_csum = false;
 
@@ -1207,7 +1230,7 @@ static int checksum_setup(struct xenvif *vif, struct sk_buff *skb)
         * recalculate the partial checksum.
         */
        if (skb->ip_summed != CHECKSUM_PARTIAL && skb_is_gso(skb)) {
-               vif->rx_gso_checksum_fixup++;
+               queue->stats.rx_gso_checksum_fixup++;
                skb->ip_summed = CHECKSUM_PARTIAL;
                recalculate_partial_csum = true;
        }
@@ -1219,31 +1242,31 @@ static int checksum_setup(struct xenvif *vif, struct sk_buff *skb)
        return skb_checksum_setup(skb, recalculate_partial_csum);
 }
 
-static bool tx_credit_exceeded(struct xenvif *vif, unsigned size)
+static bool tx_credit_exceeded(struct xenvif_queue *queue, unsigned size)
 {
        u64 now = get_jiffies_64();
-       u64 next_credit = vif->credit_window_start +
-               msecs_to_jiffies(vif->credit_usec / 1000);
+       u64 next_credit = queue->credit_window_start +
+               msecs_to_jiffies(queue->credit_usec / 1000);
 
        /* Timer could already be pending in rare cases. */
-       if (timer_pending(&vif->credit_timeout))
+       if (timer_pending(&queue->credit_timeout))
                return true;
 
        /* Passed the point where we can replenish credit? */
        if (time_after_eq64(now, next_credit)) {
-               vif->credit_window_start = now;
-               tx_add_credit(vif);
+               queue->credit_window_start = now;
+               tx_add_credit(queue);
        }
 
        /* Still too big to send right now? Set a callback. */
-       if (size > vif->remaining_credit) {
-               vif->credit_timeout.data     =
-                       (unsigned long)vif;
-               vif->credit_timeout.function =
+       if (size > queue->remaining_credit) {
+               queue->credit_timeout.data     =
+                       (unsigned long)queue;
+               queue->credit_timeout.function =
                        tx_credit_callback;
-               mod_timer(&vif->credit_timeout,
+               mod_timer(&queue->credit_timeout,
                          next_credit);
-               vif->credit_window_start = next_credit;
+               queue->credit_window_start = next_credit;
 
                return true;
        }
@@ -1251,16 +1274,16 @@ static bool tx_credit_exceeded(struct xenvif *vif, unsigned size)
        return false;
 }
 
-static void xenvif_tx_build_gops(struct xenvif *vif,
+static void xenvif_tx_build_gops(struct xenvif_queue *queue,
                                     int budget,
                                     unsigned *copy_ops,
                                     unsigned *map_ops)
 {
-       struct gnttab_map_grant_ref *gop = vif->tx_map_ops, *request_gop;
+       struct gnttab_map_grant_ref *gop = queue->tx_map_ops, *request_gop;
        struct sk_buff *skb;
        int ret;
 
-       while (skb_queue_len(&vif->tx_queue) < budget) {
+       while (skb_queue_len(&queue->tx_queue) < budget) {
                struct xen_netif_tx_request txreq;
                struct xen_netif_tx_request txfrags[XEN_NETBK_LEGACY_SLOTS_MAX];
                struct xen_netif_extra_info extras[XEN_NETIF_EXTRA_TYPE_MAX-1];
@@ -1270,69 +1293,69 @@ static void xenvif_tx_build_gops(struct xenvif *vif,
                unsigned int data_len;
                pending_ring_idx_t index;
 
-               if (vif->tx.sring->req_prod - vif->tx.req_cons >
+               if (queue->tx.sring->req_prod - queue->tx.req_cons >
                    XEN_NETIF_TX_RING_SIZE) {
-                       netdev_err(vif->dev,
+                       netdev_err(queue->vif->dev,
                                   "Impossible number of requests. "
                                   "req_prod %d, req_cons %d, size %ld\n",
-                                  vif->tx.sring->req_prod, vif->tx.req_cons,
+                                  queue->tx.sring->req_prod, queue->tx.req_cons,
                                   XEN_NETIF_TX_RING_SIZE);
-                       xenvif_fatal_tx_err(vif);
+                       xenvif_fatal_tx_err(queue->vif);
                        break;
                }
 
-               work_to_do = RING_HAS_UNCONSUMED_REQUESTS(&vif->tx);
+               work_to_do = RING_HAS_UNCONSUMED_REQUESTS(&queue->tx);
                if (!work_to_do)
                        break;
 
-               idx = vif->tx.req_cons;
+               idx = queue->tx.req_cons;
                rmb(); /* Ensure that we see the request before we copy it. */
-               memcpy(&txreq, RING_GET_REQUEST(&vif->tx, idx), sizeof(txreq));
+               memcpy(&txreq, RING_GET_REQUEST(&queue->tx, idx), sizeof(txreq));
 
                /* Credit-based scheduling. */
-               if (txreq.size > vif->remaining_credit &&
-                   tx_credit_exceeded(vif, txreq.size))
+               if (txreq.size > queue->remaining_credit &&
+                   tx_credit_exceeded(queue, txreq.size))
                        break;
 
-               vif->remaining_credit -= txreq.size;
+               queue->remaining_credit -= txreq.size;
 
                work_to_do--;
-               vif->tx.req_cons = ++idx;
+               queue->tx.req_cons = ++idx;
 
                memset(extras, 0, sizeof(extras));
                if (txreq.flags & XEN_NETTXF_extra_info) {
-                       work_to_do = xenvif_get_extras(vif, extras,
+                       work_to_do = xenvif_get_extras(queue, extras,
                                                       work_to_do);
-                       idx = vif->tx.req_cons;
+                       idx = queue->tx.req_cons;
                        if (unlikely(work_to_do < 0))
                                break;
                }
 
-               ret = xenvif_count_requests(vif, &txreq, txfrags, work_to_do);
+               ret = xenvif_count_requests(queue, &txreq, txfrags, work_to_do);
                if (unlikely(ret < 0))
                        break;
 
                idx += ret;
 
                if (unlikely(txreq.size < ETH_HLEN)) {
-                       netdev_dbg(vif->dev,
+                       netdev_dbg(queue->vif->dev,
                                   "Bad packet size: %d\n", txreq.size);
-                       xenvif_tx_err(vif, &txreq, idx);
+                       xenvif_tx_err(queue, &txreq, idx);
                        break;
                }
 
                /* No crossing a page as the payload mustn't fragment. */
                if (unlikely((txreq.offset + txreq.size) > PAGE_SIZE)) {
-                       netdev_err(vif->dev,
+                       netdev_err(queue->vif->dev,
                                   "txreq.offset: %x, size: %u, end: %lu\n",
                                   txreq.offset, txreq.size,
                                   (txreq.offset&~PAGE_MASK) + txreq.size);
-                       xenvif_fatal_tx_err(vif);
+                       xenvif_fatal_tx_err(queue->vif);
                        break;
                }
 
-               index = pending_index(vif->pending_cons);
-               pending_idx = vif->pending_ring[index];
+               index = pending_index(queue->pending_cons);
+               pending_idx = queue->pending_ring[index];
 
                data_len = (txreq.size > PKT_PROT_LEN &&
                            ret < XEN_NETBK_LEGACY_SLOTS_MAX) ?
@@ -1340,9 +1363,9 @@ static void xenvif_tx_build_gops(struct xenvif *vif,
 
                skb = xenvif_alloc_skb(data_len);
                if (unlikely(skb == NULL)) {
-                       netdev_dbg(vif->dev,
+                       netdev_dbg(queue->vif->dev,
                                   "Can't allocate a skb in start_xmit.\n");
-                       xenvif_tx_err(vif, &txreq, idx);
+                       xenvif_tx_err(queue, &txreq, idx);
                        break;
                }
 
@@ -1350,7 +1373,7 @@ static void xenvif_tx_build_gops(struct xenvif *vif,
                        struct xen_netif_extra_info *gso;
                        gso = &extras[XEN_NETIF_EXTRA_TYPE_GSO - 1];
 
-                       if (xenvif_set_skb_gso(vif, skb, gso)) {
+                       if (xenvif_set_skb_gso(queue->vif, skb, gso)) {
                                /* Failure in xenvif_set_skb_gso is fatal. */
                                kfree_skb(skb);
                                break;
@@ -1360,18 +1383,18 @@ static void xenvif_tx_build_gops(struct xenvif *vif,
                XENVIF_TX_CB(skb)->pending_idx = pending_idx;
 
                __skb_put(skb, data_len);
-               vif->tx_copy_ops[*copy_ops].source.u.ref = txreq.gref;
-               vif->tx_copy_ops[*copy_ops].source.domid = vif->domid;
-               vif->tx_copy_ops[*copy_ops].source.offset = txreq.offset;
+               queue->tx_copy_ops[*copy_ops].source.u.ref = txreq.gref;
+               queue->tx_copy_ops[*copy_ops].source.domid = queue->vif->domid;
+               queue->tx_copy_ops[*copy_ops].source.offset = txreq.offset;
 
-               vif->tx_copy_ops[*copy_ops].dest.u.gmfn =
+               queue->tx_copy_ops[*copy_ops].dest.u.gmfn =
                        virt_to_mfn(skb->data);
-               vif->tx_copy_ops[*copy_ops].dest.domid = DOMID_SELF;
-               vif->tx_copy_ops[*copy_ops].dest.offset =
+               queue->tx_copy_ops[*copy_ops].dest.domid = DOMID_SELF;
+               queue->tx_copy_ops[*copy_ops].dest.offset =
                        offset_in_page(skb->data);
 
-               vif->tx_copy_ops[*copy_ops].len = data_len;
-               vif->tx_copy_ops[*copy_ops].flags = GNTCOPY_source_gref;
+               queue->tx_copy_ops[*copy_ops].len = data_len;
+               queue->tx_copy_ops[*copy_ops].flags = GNTCOPY_source_gref;
 
                (*copy_ops)++;
 
@@ -1380,42 +1403,42 @@ static void xenvif_tx_build_gops(struct xenvif *vif,
                        skb_shinfo(skb)->nr_frags++;
                        frag_set_pending_idx(&skb_shinfo(skb)->frags[0],
                                             pending_idx);
-                       xenvif_tx_create_map_op(vif, pending_idx, &txreq, gop);
+                       xenvif_tx_create_map_op(queue, pending_idx, &txreq, gop);
                        gop++;
                } else {
                        frag_set_pending_idx(&skb_shinfo(skb)->frags[0],
                                             INVALID_PENDING_IDX);
-                       memcpy(&vif->pending_tx_info[pending_idx].req, &txreq,
+                       memcpy(&queue->pending_tx_info[pending_idx].req, &txreq,
                               sizeof(txreq));
                }
 
-               vif->pending_cons++;
+               queue->pending_cons++;
 
-               request_gop = xenvif_get_requests(vif, skb, txfrags, gop);
+               request_gop = xenvif_get_requests(queue, skb, txfrags, gop);
                if (request_gop == NULL) {
                        kfree_skb(skb);
-                       xenvif_tx_err(vif, &txreq, idx);
+                       xenvif_tx_err(queue, &txreq, idx);
                        break;
                }
                gop = request_gop;
 
-               __skb_queue_tail(&vif->tx_queue, skb);
+               __skb_queue_tail(&queue->tx_queue, skb);
 
-               vif->tx.req_cons = idx;
+               queue->tx.req_cons = idx;
 
-               if (((gop-vif->tx_map_ops) >= ARRAY_SIZE(vif->tx_map_ops)) ||
-                   (*copy_ops >= ARRAY_SIZE(vif->tx_copy_ops)))
+               if (((gop-queue->tx_map_ops) >= ARRAY_SIZE(queue->tx_map_ops)) ||
+                   (*copy_ops >= ARRAY_SIZE(queue->tx_copy_ops)))
                        break;
        }
 
-       (*map_ops) = gop - vif->tx_map_ops;
+       (*map_ops) = gop - queue->tx_map_ops;
        return;
 }
 
 /* Consolidate skb with a frag_list into a brand new one with local pages on
  * frags. Returns 0 or -ENOMEM if can't allocate new pages.
  */
-static int xenvif_handle_frag_list(struct xenvif *vif, struct sk_buff *skb)
+static int xenvif_handle_frag_list(struct xenvif_queue *queue, struct sk_buff *skb)
 {
        unsigned int offset = skb_headlen(skb);
        skb_frag_t frags[MAX_SKB_FRAGS];
@@ -1423,10 +1446,10 @@ static int xenvif_handle_frag_list(struct xenvif *vif, struct sk_buff *skb)
        struct ubuf_info *uarg;
        struct sk_buff *nskb = skb_shinfo(skb)->frag_list;
 
-       vif->tx_zerocopy_sent += 2;
-       vif->tx_frag_overflow++;
+       queue->stats.tx_zerocopy_sent += 2;
+       queue->stats.tx_frag_overflow++;
 
-       xenvif_fill_frags(vif, nskb);
+       xenvif_fill_frags(queue, nskb);
        /* Subtract frags size, we will correct it later */
        skb->truesize -= skb->data_len;
        skb->len += nskb->len;
@@ -1478,37 +1501,37 @@ static int xenvif_handle_frag_list(struct xenvif *vif, struct sk_buff *skb)
        return 0;
 }
 
-static int xenvif_tx_submit(struct xenvif *vif)
+static int xenvif_tx_submit(struct xenvif_queue *queue)
 {
-       struct gnttab_map_grant_ref *gop_map = vif->tx_map_ops;
-       struct gnttab_copy *gop_copy = vif->tx_copy_ops;
+       struct gnttab_map_grant_ref *gop_map = queue->tx_map_ops;
+       struct gnttab_copy *gop_copy = queue->tx_copy_ops;
        struct sk_buff *skb;
        int work_done = 0;
 
-       while ((skb = __skb_dequeue(&vif->tx_queue)) != NULL) {
+       while ((skb = __skb_dequeue(&queue->tx_queue)) != NULL) {
                struct xen_netif_tx_request *txp;
                u16 pending_idx;
                unsigned data_len;
 
                pending_idx = XENVIF_TX_CB(skb)->pending_idx;
-               txp = &vif->pending_tx_info[pending_idx].req;
+               txp = &queue->pending_tx_info[pending_idx].req;
 
                /* Check the remap error code. */
-               if (unlikely(xenvif_tx_check_gop(vif, skb, &gop_map, &gop_copy))) {
+               if (unlikely(xenvif_tx_check_gop(queue, skb, &gop_map, &gop_copy))) {
                        skb_shinfo(skb)->nr_frags = 0;
                        kfree_skb(skb);
                        continue;
                }
 
                data_len = skb->len;
-               callback_param(vif, pending_idx).ctx = NULL;
+               callback_param(queue, pending_idx).ctx = NULL;
                if (data_len < txp->size) {
                        /* Append the packet payload as a fragment. */
                        txp->offset += data_len;
                        txp->size -= data_len;
                } else {
                        /* Schedule a response immediately. */
-                       xenvif_idx_release(vif, pending_idx,
+                       xenvif_idx_release(queue, pending_idx,
                                           XEN_NETIF_RSP_OKAY);
                }
 
@@ -1517,12 +1540,12 @@ static int xenvif_tx_submit(struct xenvif *vif)
                else if (txp->flags & XEN_NETTXF_data_validated)
                        skb->ip_summed = CHECKSUM_UNNECESSARY;
 
-               xenvif_fill_frags(vif, skb);
+               xenvif_fill_frags(queue, skb);
 
                if (unlikely(skb_has_frag_list(skb))) {
-                       if (xenvif_handle_frag_list(vif, skb)) {
+                       if (xenvif_handle_frag_list(queue, skb)) {
                                if (net_ratelimit())
-                                       netdev_err(vif->dev,
+                                       netdev_err(queue->vif->dev,
                                                   "Not enough memory to consolidate frag_list!\n");
                                skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY;
                                kfree_skb(skb);
@@ -1535,12 +1558,12 @@ static int xenvif_tx_submit(struct xenvif *vif)
                        __pskb_pull_tail(skb, target - skb_headlen(skb));
                }
 
-               skb->dev      = vif->dev;
+               skb->dev      = queue->vif->dev;
                skb->protocol = eth_type_trans(skb, skb->dev);
                skb_reset_network_header(skb);
 
-               if (checksum_setup(vif, skb)) {
-                       netdev_dbg(vif->dev,
+               if (checksum_setup(queue, skb)) {
+                       netdev_dbg(queue->vif->dev,
                                   "Can't setup checksum in net_tx_action\n");
                        /* We have to set this flag to trigger the callback */
                        if (skb_shinfo(skb)->destructor_arg)
@@ -1565,8 +1588,8 @@ static int xenvif_tx_submit(struct xenvif *vif)
                                DIV_ROUND_UP(skb->len - hdrlen, mss);
                }
 
-               vif->dev->stats.rx_bytes += skb->len;
-               vif->dev->stats.rx_packets++;
+               queue->stats.rx_bytes += skb->len;
+               queue->stats.rx_packets++;
 
                work_done++;
 
@@ -1577,7 +1600,7 @@ static int xenvif_tx_submit(struct xenvif *vif)
                 */
                if (skb_shinfo(skb)->destructor_arg) {
                        skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY;
-                       vif->tx_zerocopy_sent++;
+                       queue->stats.tx_zerocopy_sent++;
                }
 
                netif_receive_skb(skb);
@@ -1590,47 +1613,47 @@ void xenvif_zerocopy_callback(struct ubuf_info *ubuf, bool zerocopy_success)
 {
        unsigned long flags;
        pending_ring_idx_t index;
-       struct xenvif *vif = ubuf_to_vif(ubuf);
+       struct xenvif_queue *queue = ubuf_to_queue(ubuf);
 
        /* This is the only place where we grab this lock, to protect callbacks
         * from each other.
         */
-       spin_lock_irqsave(&vif->callback_lock, flags);
+       spin_lock_irqsave(&queue->callback_lock, flags);
        do {
                u16 pending_idx = ubuf->desc;
                ubuf = (struct ubuf_info *) ubuf->ctx;
-               BUG_ON(vif->dealloc_prod - vif->dealloc_cons >=
+               BUG_ON(queue->dealloc_prod - queue->dealloc_cons >=
                        MAX_PENDING_REQS);
-               index = pending_index(vif->dealloc_prod);
-               vif->dealloc_ring[index] = pending_idx;
+               index = pending_index(queue->dealloc_prod);
+               queue->dealloc_ring[index] = pending_idx;
                /* Sync with xenvif_tx_dealloc_action:
                 * insert idx then incr producer.
                 */
                smp_wmb();
-               vif->dealloc_prod++;
+               queue->dealloc_prod++;
        } while (ubuf);
-       wake_up(&vif->dealloc_wq);
-       spin_unlock_irqrestore(&vif->callback_lock, flags);
+       wake_up(&queue->dealloc_wq);
+       spin_unlock_irqrestore(&queue->callback_lock, flags);
 
        if (likely(zerocopy_success))
-               vif->tx_zerocopy_success++;
+               queue->stats.tx_zerocopy_success++;
        else
-               vif->tx_zerocopy_fail++;
+               queue->stats.tx_zerocopy_fail++;
 }
 
-static inline void xenvif_tx_dealloc_action(struct xenvif *vif)
+static inline void xenvif_tx_dealloc_action(struct xenvif_queue *queue)
 {
        struct gnttab_unmap_grant_ref *gop;
        pending_ring_idx_t dc, dp;
        u16 pending_idx, pending_idx_release[MAX_PENDING_REQS];
        unsigned int i = 0;
 
-       dc = vif->dealloc_cons;
-       gop = vif->tx_unmap_ops;
+       dc = queue->dealloc_cons;
+       gop = queue->tx_unmap_ops;
 
        /* Free up any grants we have finished using */
        do {
-               dp = vif->dealloc_prod;
+               dp = queue->dealloc_prod;
 
                /* Ensure we see all indices enqueued by all
                 * xenvif_zerocopy_callback().
@@ -1638,38 +1661,38 @@ static inline void xenvif_tx_dealloc_action(struct xenvif *vif)
                smp_rmb();
 
                while (dc != dp) {
-                       BUG_ON(gop - vif->tx_unmap_ops > MAX_PENDING_REQS);
+                       BUG_ON(gop - queue->tx_unmap_ops > MAX_PENDING_REQS);
                        pending_idx =
-                               vif->dealloc_ring[pending_index(dc++)];
+                               queue->dealloc_ring[pending_index(dc++)];
 
-                       pending_idx_release[gop-vif->tx_unmap_ops] =
+                       pending_idx_release[gop-queue->tx_unmap_ops] =
                                pending_idx;
-                       vif->pages_to_unmap[gop-vif->tx_unmap_ops] =
-                               vif->mmap_pages[pending_idx];
+                       queue->pages_to_unmap[gop-queue->tx_unmap_ops] =
+                               queue->mmap_pages[pending_idx];
                        gnttab_set_unmap_op(gop,
-                                           idx_to_kaddr(vif, pending_idx),
+                                           idx_to_kaddr(queue, pending_idx),
                                            GNTMAP_host_map,
-                                           vif->grant_tx_handle[pending_idx]);
-                       xenvif_grant_handle_reset(vif, pending_idx);
+                                           queue->grant_tx_handle[pending_idx]);
+                       xenvif_grant_handle_reset(queue, pending_idx);
                        ++gop;
                }
 
-       } while (dp != vif->dealloc_prod);
+       } while (dp != queue->dealloc_prod);
 
-       vif->dealloc_cons = dc;
+       queue->dealloc_cons = dc;
 
-       if (gop - vif->tx_unmap_ops > 0) {
+       if (gop - queue->tx_unmap_ops > 0) {
                int ret;
-               ret = gnttab_unmap_refs(vif->tx_unmap_ops,
+               ret = gnttab_unmap_refs(queue->tx_unmap_ops,
                                        NULL,
-                                       vif->pages_to_unmap,
-                                       gop - vif->tx_unmap_ops);
+                                       queue->pages_to_unmap,
+                                       gop - queue->tx_unmap_ops);
                if (ret) {
-                       netdev_err(vif->dev, "Unmap fail: nr_ops %tx ret %d\n",
-                                  gop - vif->tx_unmap_ops, ret);
-                       for (i = 0; i < gop - vif->tx_unmap_ops; ++i) {
+                       netdev_err(queue->vif->dev, "Unmap fail: nr_ops %tx ret %d\n",
+                                  gop - queue->tx_unmap_ops, ret);
+                       for (i = 0; i < gop - queue->tx_unmap_ops; ++i) {
                                if (gop[i].status != GNTST_okay)
-                                       netdev_err(vif->dev,
+                                       netdev_err(queue->vif->dev,
                                                   " host_addr: %llx handle: %x status: %d\n",
                                                   gop[i].host_addr,
                                                   gop[i].handle,
@@ -1679,91 +1702,91 @@ static inline void xenvif_tx_dealloc_action(struct xenvif *vif)
                }
        }
 
-       for (i = 0; i < gop - vif->tx_unmap_ops; ++i)
-               xenvif_idx_release(vif, pending_idx_release[i],
+       for (i = 0; i < gop - queue->tx_unmap_ops; ++i)
+               xenvif_idx_release(queue, pending_idx_release[i],
                                   XEN_NETIF_RSP_OKAY);
 }
 
 
 /* Called after netfront has transmitted */
-int xenvif_tx_action(struct xenvif *vif, int budget)
+int xenvif_tx_action(struct xenvif_queue *queue, int budget)
 {
        unsigned nr_mops, nr_cops = 0;
        int work_done, ret;
 
-       if (unlikely(!tx_work_todo(vif)))
+       if (unlikely(!tx_work_todo(queue)))
                return 0;
 
-       xenvif_tx_build_gops(vif, budget, &nr_cops, &nr_mops);
+       xenvif_tx_build_gops(queue, budget, &nr_cops, &nr_mops);
 
        if (nr_cops == 0)
                return 0;
 
-       gnttab_batch_copy(vif->tx_copy_ops, nr_cops);
+       gnttab_batch_copy(queue->tx_copy_ops, nr_cops);
        if (nr_mops != 0) {
-               ret = gnttab_map_refs(vif->tx_map_ops,
+               ret = gnttab_map_refs(queue->tx_map_ops,
                                      NULL,
-                                     vif->pages_to_map,
+                                     queue->pages_to_map,
                                      nr_mops);
                BUG_ON(ret);
        }
 
-       work_done = xenvif_tx_submit(vif);
+       work_done = xenvif_tx_submit(queue);
 
        return work_done;
 }
 
-static void xenvif_idx_release(struct xenvif *vif, u16 pending_idx,
+static void xenvif_idx_release(struct xenvif_queue *queue, u16 pending_idx,
                               u8 status)
 {
        struct pending_tx_info *pending_tx_info;
        pending_ring_idx_t index;
        unsigned long flags;
 
-       pending_tx_info = &vif->pending_tx_info[pending_idx];
-       spin_lock_irqsave(&vif->response_lock, flags);
-       make_tx_response(vif, &pending_tx_info->req, status);
-       index = pending_index(vif->pending_prod);
-       vif->pending_ring[index] = pending_idx;
+       pending_tx_info = &queue->pending_tx_info[pending_idx];
+       spin_lock_irqsave(&queue->response_lock, flags);
+       make_tx_response(queue, &pending_tx_info->req, status);
+       index = pending_index(queue->pending_prod);
+       queue->pending_ring[index] = pending_idx;
        /* TX shouldn't use the index before we give it back here */
        mb();
-       vif->pending_prod++;
-       spin_unlock_irqrestore(&vif->response_lock, flags);
+       queue->pending_prod++;
+       spin_unlock_irqrestore(&queue->response_lock, flags);
 }
 
 
-static void make_tx_response(struct xenvif *vif,
+static void make_tx_response(struct xenvif_queue *queue,
                             struct xen_netif_tx_request *txp,
                             s8       st)
 {
-       RING_IDX i = vif->tx.rsp_prod_pvt;
+       RING_IDX i = queue->tx.rsp_prod_pvt;
        struct xen_netif_tx_response *resp;
        int notify;
 
-       resp = RING_GET_RESPONSE(&vif->tx, i);
+       resp = RING_GET_RESPONSE(&queue->tx, i);
        resp->id     = txp->id;
        resp->status = st;
 
        if (txp->flags & XEN_NETTXF_extra_info)
-               RING_GET_RESPONSE(&vif->tx, ++i)->status = XEN_NETIF_RSP_NULL;
+               RING_GET_RESPONSE(&queue->tx, ++i)->status = XEN_NETIF_RSP_NULL;
 
-       vif->tx.rsp_prod_pvt = ++i;
-       RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&vif->tx, notify);
+       queue->tx.rsp_prod_pvt = ++i;
+       RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&queue->tx, notify);
        if (notify)
-               notify_remote_via_irq(vif->tx_irq);
+               notify_remote_via_irq(queue->tx_irq);
 }
 
-static struct xen_netif_rx_response *make_rx_response(struct xenvif *vif,
+static struct xen_netif_rx_response *make_rx_response(struct xenvif_queue *queue,
                                             u16      id,
                                             s8       st,
                                             u16      offset,
                                             u16      size,
                                             u16      flags)
 {
-       RING_IDX i = vif->rx.rsp_prod_pvt;
+       RING_IDX i = queue->rx.rsp_prod_pvt;
        struct xen_netif_rx_response *resp;
 
-       resp = RING_GET_RESPONSE(&vif->rx, i);
+       resp = RING_GET_RESPONSE(&queue->rx, i);
        resp->offset     = offset;
        resp->flags      = flags;
        resp->id         = id;
@@ -1771,26 +1794,26 @@ static struct xen_netif_rx_response *make_rx_response(struct xenvif *vif,
        if (st < 0)
                resp->status = (s16)st;
 
-       vif->rx.rsp_prod_pvt = ++i;
+       queue->rx.rsp_prod_pvt = ++i;
 
        return resp;
 }
 
-void xenvif_idx_unmap(struct xenvif *vif, u16 pending_idx)
+void xenvif_idx_unmap(struct xenvif_queue *queue, u16 pending_idx)
 {
        int ret;
        struct gnttab_unmap_grant_ref tx_unmap_op;
 
        gnttab_set_unmap_op(&tx_unmap_op,
-                           idx_to_kaddr(vif, pending_idx),
+                           idx_to_kaddr(queue, pending_idx),
                            GNTMAP_host_map,
-                           vif->grant_tx_handle[pending_idx]);
-       xenvif_grant_handle_reset(vif, pending_idx);
+                           queue->grant_tx_handle[pending_idx]);
+       xenvif_grant_handle_reset(queue, pending_idx);
 
        ret = gnttab_unmap_refs(&tx_unmap_op, NULL,
-                               &vif->mmap_pages[pending_idx], 1);
+                               &queue->mmap_pages[pending_idx], 1);
        if (ret) {
-               netdev_err(vif->dev,
+               netdev_err(queue->vif->dev,
                           "Unmap fail: ret: %d pending_idx: %d host_addr: %llx handle: %x status: %d\n",
                           ret,
                           pending_idx,
@@ -1800,41 +1823,40 @@ void xenvif_idx_unmap(struct xenvif *vif, u16 pending_idx)
                BUG();
        }
 
-       xenvif_idx_release(vif, pending_idx, XEN_NETIF_RSP_OKAY);
+       xenvif_idx_release(queue, pending_idx, XEN_NETIF_RSP_OKAY);
 }
 
-static inline int rx_work_todo(struct xenvif *vif)
+static inline int rx_work_todo(struct xenvif_queue *queue)
 {
-       return (!skb_queue_empty(&vif->rx_queue) &&
-              xenvif_rx_ring_slots_available(vif, vif->rx_last_skb_slots)) ||
-              vif->rx_queue_purge;
+       return (!skb_queue_empty(&queue->rx_queue) &&
+              xenvif_rx_ring_slots_available(queue, queue->rx_last_skb_slots)) ||
+              queue->rx_queue_purge;
 }
 
-static inline int tx_work_todo(struct xenvif *vif)
+static inline int tx_work_todo(struct xenvif_queue *queue)
 {
-
-       if (likely(RING_HAS_UNCONSUMED_REQUESTS(&vif->tx)))
+       if (likely(RING_HAS_UNCONSUMED_REQUESTS(&queue->tx)))
                return 1;
 
        return 0;
 }
 
-static inline bool tx_dealloc_work_todo(struct xenvif *vif)
+static inline bool tx_dealloc_work_todo(struct xenvif_queue *queue)
 {
-       return vif->dealloc_cons != vif->dealloc_prod;
+       return queue->dealloc_cons != queue->dealloc_prod;
 }
 
-void xenvif_unmap_frontend_rings(struct xenvif *vif)
+void xenvif_unmap_frontend_rings(struct xenvif_queue *queue)
 {
-       if (vif->tx.sring)
-               xenbus_unmap_ring_vfree(xenvif_to_xenbus_device(vif),
-                                       vif->tx.sring);
-       if (vif->rx.sring)
-               xenbus_unmap_ring_vfree(xenvif_to_xenbus_device(vif),
-                                       vif->rx.sring);
+       if (queue->tx.sring)
+               xenbus_unmap_ring_vfree(xenvif_to_xenbus_device(queue->vif),
+                                       queue->tx.sring);
+       if (queue->rx.sring)
+               xenbus_unmap_ring_vfree(xenvif_to_xenbus_device(queue->vif),
+                                       queue->rx.sring);
 }
 
-int xenvif_map_frontend_rings(struct xenvif *vif,
+int xenvif_map_frontend_rings(struct xenvif_queue *queue,
                              grant_ref_t tx_ring_ref,
                              grant_ref_t rx_ring_ref)
 {
@@ -1844,85 +1866,78 @@ int xenvif_map_frontend_rings(struct xenvif *vif,
 
        int err = -ENOMEM;
 
-       err = xenbus_map_ring_valloc(xenvif_to_xenbus_device(vif),
+       err = xenbus_map_ring_valloc(xenvif_to_xenbus_device(queue->vif),
                                     tx_ring_ref, &addr);
        if (err)
                goto err;
 
        txs = (struct xen_netif_tx_sring *)addr;
-       BACK_RING_INIT(&vif->tx, txs, PAGE_SIZE);
+       BACK_RING_INIT(&queue->tx, txs, PAGE_SIZE);
 
-       err = xenbus_map_ring_valloc(xenvif_to_xenbus_device(vif),
+       err = xenbus_map_ring_valloc(xenvif_to_xenbus_device(queue->vif),
                                     rx_ring_ref, &addr);
        if (err)
                goto err;
 
        rxs = (struct xen_netif_rx_sring *)addr;
-       BACK_RING_INIT(&vif->rx, rxs, PAGE_SIZE);
+       BACK_RING_INIT(&queue->rx, rxs, PAGE_SIZE);
 
        return 0;
 
 err:
-       xenvif_unmap_frontend_rings(vif);
+       xenvif_unmap_frontend_rings(queue);
        return err;
 }
 
-void xenvif_stop_queue(struct xenvif *vif)
+static void xenvif_start_queue(struct xenvif_queue *queue)
 {
-       if (!vif->can_queue)
-               return;
-
-       netif_stop_queue(vif->dev);
-}
-
-static void xenvif_start_queue(struct xenvif *vif)
-{
-       if (xenvif_schedulable(vif))
-               netif_wake_queue(vif->dev);
+       if (xenvif_schedulable(queue->vif))
+               xenvif_wake_queue(queue);
 }
 
 int xenvif_kthread_guest_rx(void *data)
 {
-       struct xenvif *vif = data;
+       struct xenvif_queue *queue = data;
        struct sk_buff *skb;
 
        while (!kthread_should_stop()) {
-               wait_event_interruptible(vif->wq,
-                                        rx_work_todo(vif) ||
-                                        vif->disabled ||
+               wait_event_interruptible(queue->wq,
+                                        rx_work_todo(queue) ||
+                                        queue->vif->disabled ||
                                         kthread_should_stop());
 
                /* This frontend is found to be rogue, disable it in
                 * kthread context. Currently this is only set when
                 * netback finds out frontend sends malformed packet,
                 * but we cannot disable the interface in softirq
-                * context so we defer it here.
+                * context so we defer it here, if this thread is
+                * associated with queue 0.
                 */
-               if (unlikely(vif->disabled && netif_carrier_ok(vif->dev)))
-                       xenvif_carrier_off(vif);
+               if (unlikely(queue->vif->disabled && netif_carrier_ok(queue->vif->dev) && queue->id == 0))
+                       xenvif_carrier_off(queue->vif);
 
                if (kthread_should_stop())
                        break;
 
-               if (vif->rx_queue_purge) {
-                       skb_queue_purge(&vif->rx_queue);
-                       vif->rx_queue_purge = false;
+               if (queue->rx_queue_purge) {
+                       skb_queue_purge(&queue->rx_queue);
+                       queue->rx_queue_purge = false;
                }
 
-               if (!skb_queue_empty(&vif->rx_queue))
-                       xenvif_rx_action(vif);
+               if (!skb_queue_empty(&queue->rx_queue))
+                       xenvif_rx_action(queue);
 
-               if (skb_queue_empty(&vif->rx_queue) &&
-                   netif_queue_stopped(vif->dev)) {
-                       del_timer_sync(&vif->wake_queue);
-                       xenvif_start_queue(vif);
+               if (skb_queue_empty(&queue->rx_queue) &&
+                   xenvif_queue_stopped(queue)) {
+                       del_timer_sync(&queue->wake_queue);
+                       xenvif_start_queue(queue);
                }
 
                cond_resched();
        }
 
        /* Bin any remaining skbs */
-       while ((skb = skb_dequeue(&vif->rx_queue)) != NULL)
+       while ((skb = skb_dequeue(&queue->rx_queue)) != NULL)
                dev_kfree_skb(skb);
 
        return 0;
@@ -1930,22 +1945,22 @@ int xenvif_kthread_guest_rx(void *data)
 
 int xenvif_dealloc_kthread(void *data)
 {
-       struct xenvif *vif = data;
+       struct xenvif_queue *queue = data;
 
        while (!kthread_should_stop()) {
-               wait_event_interruptible(vif->dealloc_wq,
-                                        tx_dealloc_work_todo(vif) ||
+               wait_event_interruptible(queue->dealloc_wq,
+                                        tx_dealloc_work_todo(queue) ||
                                         kthread_should_stop());
                if (kthread_should_stop())
                        break;
 
-               xenvif_tx_dealloc_action(vif);
+               xenvif_tx_dealloc_action(queue);
                cond_resched();
        }
 
        /* Unmap anything remaining*/
-       if (tx_dealloc_work_todo(vif))
-               xenvif_tx_dealloc_action(vif);
+       if (tx_dealloc_work_todo(queue))
+               xenvif_tx_dealloc_action(queue);
 
        return 0;
 }
@@ -1957,6 +1972,9 @@ static int __init netback_init(void)
        if (!xen_domain())
                return -ENODEV;
 
+       /* Allow as many queues as there are CPUs, by default */
+       xenvif_max_queues = num_online_cpus();
+
        if (fatal_skb_slots < XEN_NETBK_LEGACY_SLOTS_MAX) {
                pr_info("fatal_skb_slots too small (%d), bump it to XEN_NETBK_LEGACY_SLOTS_MAX (%d)\n",
                        fatal_skb_slots, XEN_NETBK_LEGACY_SLOTS_MAX);
index 7a206cffb062c0b3a2521ca7a6ee22d4cd3bcefd..96c63dc2509e325c18ce0c63a243971a33d4d925 100644 (file)
@@ -19,6 +19,8 @@
 */
 
 #include "common.h"
+#include <linux/vmalloc.h>
+#include <linux/rtnetlink.h>
 
 struct backend_info {
        struct xenbus_device *dev;
@@ -34,8 +36,9 @@ struct backend_info {
        u8 have_hotplug_status_watch:1;
 };
 
-static int connect_rings(struct backend_info *);
-static void connect(struct backend_info *);
+static int connect_rings(struct backend_info *be, struct xenvif_queue *queue);
+static void connect(struct backend_info *be);
+static int read_xenbus_vif_flags(struct backend_info *be);
 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,
@@ -157,6 +160,12 @@ static int netback_probe(struct xenbus_device *dev,
        if (err)
                pr_debug("Error writing feature-split-event-channels\n");
 
+       /* Multi-queue support: This is an optional feature. */
+       err = xenbus_printf(XBT_NIL, dev->nodename,
+                           "multi-queue-max-queues", "%u", xenvif_max_queues);
+       if (err)
+               pr_debug("Error writing multi-queue-max-queues\n");
+
        err = xenbus_switch_state(dev, XenbusStateInitWait);
        if (err)
                goto fail;
@@ -485,10 +494,26 @@ static void connect(struct backend_info *be)
 {
        int err;
        struct xenbus_device *dev = be->dev;
+       unsigned long credit_bytes, credit_usec;
+       unsigned int queue_index;
+       unsigned int requested_num_queues;
+       struct xenvif_queue *queue;
 
-       err = connect_rings(be);
-       if (err)
+       /* Check whether the frontend requested multiple queues
+        * and read the number requested.
+        */
+       err = xenbus_scanf(XBT_NIL, dev->otherend,
+                          "multi-queue-num-queues",
+                          "%u", &requested_num_queues);
+       if (err < 0) {
+               requested_num_queues = 1; /* Fall back to single queue */
+       } else if (requested_num_queues > xenvif_max_queues) {
+               /* buggy or malicious guest */
+               xenbus_dev_fatal(dev, err,
+                                "guest requested %u queues, exceeding the maximum of %u.",
+                                requested_num_queues, xenvif_max_queues);
                return;
+       }
 
        err = xen_net_read_mac(dev, be->vif->fe_dev_addr);
        if (err) {
@@ -496,9 +521,54 @@ static void connect(struct backend_info *be)
                return;
        }
 
-       xen_net_read_rate(dev, &be->vif->credit_bytes,
-                         &be->vif->credit_usec);
-       be->vif->remaining_credit = be->vif->credit_bytes;
+       xen_net_read_rate(dev, &credit_bytes, &credit_usec);
+       read_xenbus_vif_flags(be);
+
+       /* Use the number of queues requested by the frontend */
+       be->vif->queues = vzalloc(requested_num_queues *
+                                 sizeof(struct xenvif_queue));
+       rtnl_lock();
+       netif_set_real_num_tx_queues(be->vif->dev, requested_num_queues);
+       rtnl_unlock();
+
+       for (queue_index = 0; queue_index < requested_num_queues; ++queue_index) {
+               queue = &be->vif->queues[queue_index];
+               queue->vif = be->vif;
+               queue->id = queue_index;
+               snprintf(queue->name, sizeof(queue->name), "%s-q%u",
+                               be->vif->dev->name, queue->id);
+
+               err = xenvif_init_queue(queue);
+               if (err) {
+                       /* xenvif_init_queue() cleans up after itself on
+                        * failure, but we need to clean up any previously
+                        * initialised queues. Set num_queues to i so that
+                        * earlier queues can be destroyed using the regular
+                        * disconnect logic.
+                        */
+                       rtnl_lock();
+                       netif_set_real_num_tx_queues(be->vif->dev, queue_index);
+                       rtnl_unlock();
+                       goto err;
+               }
+
+               queue->remaining_credit = credit_bytes;
+
+               err = connect_rings(be, queue);
+               if (err) {
+                       /* connect_rings() cleans up after itself on failure,
+                        * but we need to clean up after xenvif_init_queue() here,
+                        * and also clean up any previously initialised queues.
+                        */
+                       xenvif_deinit_queue(queue);
+                       rtnl_lock();
+                       netif_set_real_num_tx_queues(be->vif->dev, queue_index);
+                       rtnl_unlock();
+                       goto err;
+               }
+       }
+
+       xenvif_carrier_on(be->vif);
 
        unregister_hotplug_status_watch(be);
        err = xenbus_watch_pathfmt(dev, &be->hotplug_status_watch,
@@ -507,45 +577,109 @@ static void connect(struct backend_info *be)
        if (!err)
                be->have_hotplug_status_watch = 1;
 
-       netif_wake_queue(be->vif->dev);
+       netif_tx_wake_all_queues(be->vif->dev);
+
+       return;
+
+err:
+       if (be->vif->dev->real_num_tx_queues > 0)
+               xenvif_disconnect(be->vif); /* Clean up existing queues */
+       vfree(be->vif->queues);
+       be->vif->queues = NULL;
+       rtnl_lock();
+       netif_set_real_num_tx_queues(be->vif->dev, 0);
+       rtnl_unlock();
+       return;
 }
 
 
-static int connect_rings(struct backend_info *be)
+static int connect_rings(struct backend_info *be, struct xenvif_queue *queue)
 {
-       struct xenvif *vif = be->vif;
        struct xenbus_device *dev = be->dev;
+       unsigned int num_queues = queue->vif->dev->real_num_tx_queues;
        unsigned long tx_ring_ref, rx_ring_ref;
-       unsigned int tx_evtchn, rx_evtchn, rx_copy;
+       unsigned int tx_evtchn, rx_evtchn;
        int err;
-       int val;
+       char *xspath;
+       size_t xspathsize;
+       const size_t xenstore_path_ext_size = 11; /* sufficient for "/queue-NNN" */
+
+       /* If the frontend requested 1 queue, or we have fallen back
+        * to single queue due to lack of frontend support for multi-
+        * queue, expect the remaining XenStore keys in the toplevel
+        * directory. Otherwise, expect them in a subdirectory called
+        * queue-N.
+        */
+       if (num_queues == 1) {
+               xspath = kzalloc(strlen(dev->otherend) + 1, GFP_KERNEL);
+               if (!xspath) {
+                       xenbus_dev_fatal(dev, -ENOMEM,
+                                        "reading ring references");
+                       return -ENOMEM;
+               }
+               strcpy(xspath, dev->otherend);
+       } else {
+               xspathsize = strlen(dev->otherend) + xenstore_path_ext_size;
+               xspath = kzalloc(xspathsize, GFP_KERNEL);
+               if (!xspath) {
+                       xenbus_dev_fatal(dev, -ENOMEM,
+                                        "reading ring references");
+                       return -ENOMEM;
+               }
+               snprintf(xspath, xspathsize, "%s/queue-%u", dev->otherend,
+                        queue->id);
+       }
 
-       err = xenbus_gather(XBT_NIL, dev->otherend,
+       err = xenbus_gather(XBT_NIL, xspath,
                            "tx-ring-ref", "%lu", &tx_ring_ref,
                            "rx-ring-ref", "%lu", &rx_ring_ref, NULL);
        if (err) {
                xenbus_dev_fatal(dev, err,
                                 "reading %s/ring-ref",
-                                dev->otherend);
-               return err;
+                                xspath);
+               goto err;
        }
 
        /* Try split event channels first, then single event channel. */
-       err = xenbus_gather(XBT_NIL, dev->otherend,
+       err = xenbus_gather(XBT_NIL, xspath,
                            "event-channel-tx", "%u", &tx_evtchn,
                            "event-channel-rx", "%u", &rx_evtchn, NULL);
        if (err < 0) {
-               err = xenbus_scanf(XBT_NIL, dev->otherend,
+               err = xenbus_scanf(XBT_NIL, xspath,
                                   "event-channel", "%u", &tx_evtchn);
                if (err < 0) {
                        xenbus_dev_fatal(dev, err,
                                         "reading %s/event-channel(-tx/rx)",
-                                        dev->otherend);
-                       return err;
+                                        xspath);
+                       goto err;
                }
                rx_evtchn = tx_evtchn;
        }
 
+       /* Map the shared frame, irq etc. */
+       err = xenvif_connect(queue, tx_ring_ref, rx_ring_ref,
+                            tx_evtchn, rx_evtchn);
+       if (err) {
+               xenbus_dev_fatal(dev, err,
+                                "mapping shared-frames %lu/%lu port tx %u rx %u",
+                                tx_ring_ref, rx_ring_ref,
+                                tx_evtchn, rx_evtchn);
+               goto err;
+       }
+
+       err = 0;
+err: /* Regular return falls through with err == 0 */
+       kfree(xspath);
+       return err;
+}
+
+static int read_xenbus_vif_flags(struct backend_info *be)
+{
+       struct xenvif *vif = be->vif;
+       struct xenbus_device *dev = be->dev;
+       unsigned int rx_copy;
+       int err, val;
+
        err = xenbus_scanf(XBT_NIL, dev->otherend, "request-rx-copy", "%u",
                           &rx_copy);
        if (err == -ENOENT) {
@@ -621,16 +755,6 @@ static int connect_rings(struct backend_info *be)
                val = 0;
        vif->ipv6_csum = !!val;
 
-       /* Map the shared frame, irq etc. */
-       err = xenvif_connect(vif, tx_ring_ref, rx_ring_ref,
-                            tx_evtchn, rx_evtchn);
-       if (err) {
-               xenbus_dev_fatal(dev, err,
-                                "mapping shared-frames %lu/%lu port tx %u rx %u",
-                                tx_ring_ref, rx_ring_ref,
-                                tx_evtchn, rx_evtchn);
-               return err;
-       }
        return 0;
 }
 
index 158b5e639fc7307d5a98580cfd65ca23b9d3db8a..5a7872ac35661bd039633c4cb77102f40fa58dbb 100644 (file)
 #include <xen/interface/memory.h>
 #include <xen/interface/grant_table.h>
 
+/* Module parameters */
+static unsigned int xennet_max_queues;
+module_param_named(max_queues, xennet_max_queues, uint, 0644);
+MODULE_PARM_DESC(max_queues,
+                "Maximum number of queues per virtual interface");
+
 static const struct ethtool_ops xennet_ethtool_ops;
 
 struct netfront_cb {
@@ -73,6 +79,12 @@ struct netfront_cb {
 #define NET_RX_RING_SIZE __CONST_RING_SIZE(xen_netif_rx, PAGE_SIZE)
 #define TX_MAX_TARGET min_t(int, NET_TX_RING_SIZE, 256)
 
+/* Queue name is interface name with "-qNNN" appended */
+#define QUEUE_NAME_SIZE (IFNAMSIZ + 6)
+
+/* IRQ name is queue name with "-tx" or "-rx" appended */
+#define IRQ_NAME_SIZE (QUEUE_NAME_SIZE + 3)
+
 struct netfront_stats {
        u64                     rx_packets;
        u64                     tx_packets;
@@ -81,9 +93,12 @@ struct netfront_stats {
        struct u64_stats_sync   syncp;
 };
 
-struct netfront_info {
-       struct list_head list;
-       struct net_device *netdev;
+struct netfront_info;
+
+struct netfront_queue {
+       unsigned int id; /* Queue ID, 0-based */
+       char name[QUEUE_NAME_SIZE]; /* DEVNAME-qN */
+       struct netfront_info *info;
 
        struct napi_struct napi;
 
@@ -93,10 +108,8 @@ struct netfront_info {
        unsigned int tx_evtchn, rx_evtchn;
        unsigned int tx_irq, rx_irq;
        /* Only used when split event channels support is enabled */
-       char tx_irq_name[IFNAMSIZ+4]; /* DEVNAME-tx */
-       char rx_irq_name[IFNAMSIZ+4]; /* DEVNAME-rx */
-
-       struct xenbus_device *xbdev;
+       char tx_irq_name[IRQ_NAME_SIZE]; /* DEVNAME-qN-tx */
+       char rx_irq_name[IRQ_NAME_SIZE]; /* DEVNAME-qN-rx */
 
        spinlock_t   tx_lock;
        struct xen_netif_tx_front_ring tx;
@@ -140,11 +153,21 @@ struct netfront_info {
        unsigned long rx_pfn_array[NET_RX_RING_SIZE];
        struct multicall_entry rx_mcl[NET_RX_RING_SIZE+1];
        struct mmu_update rx_mmu[NET_RX_RING_SIZE];
+};
+
+struct netfront_info {
+       struct list_head list;
+       struct net_device *netdev;
+
+       struct xenbus_device *xbdev;
+
+       /* Multi-queue support */
+       struct netfront_queue *queues;
 
        /* Statistics */
        struct netfront_stats __percpu *stats;
 
-       unsigned long rx_gso_checksum_fixup;
+       atomic_t rx_gso_checksum_fixup;
 };
 
 struct netfront_rx_info {
@@ -187,21 +210,21 @@ static int xennet_rxidx(RING_IDX idx)
        return idx & (NET_RX_RING_SIZE - 1);
 }
 
-static struct sk_buff *xennet_get_rx_skb(struct netfront_info *np,
+static struct sk_buff *xennet_get_rx_skb(struct netfront_queue *queue,
                                         RING_IDX ri)
 {
        int i = xennet_rxidx(ri);
-       struct sk_buff *skb = np->rx_skbs[i];
-       np->rx_skbs[i] = NULL;
+       struct sk_buff *skb = queue->rx_skbs[i];
+       queue->rx_skbs[i] = NULL;
        return skb;
 }
 
-static grant_ref_t xennet_get_rx_ref(struct netfront_info *np,
+static grant_ref_t xennet_get_rx_ref(struct netfront_queue *queue,
                                            RING_IDX ri)
 {
        int i = xennet_rxidx(ri);
-       grant_ref_t ref = np->grant_rx_ref[i];
-       np->grant_rx_ref[i] = GRANT_INVALID_REF;
+       grant_ref_t ref = queue->grant_rx_ref[i];
+       queue->grant_rx_ref[i] = GRANT_INVALID_REF;
        return ref;
 }
 
@@ -221,41 +244,40 @@ static bool xennet_can_sg(struct net_device *dev)
 
 static void rx_refill_timeout(unsigned long data)
 {
-       struct net_device *dev = (struct net_device *)data;
-       struct netfront_info *np = netdev_priv(dev);
-       napi_schedule(&np->napi);
+       struct netfront_queue *queue = (struct netfront_queue *)data;
+       napi_schedule(&queue->napi);
 }
 
-static int netfront_tx_slot_available(struct netfront_info *np)
+static int netfront_tx_slot_available(struct netfront_queue *queue)
 {
-       return (np->tx.req_prod_pvt - np->tx.rsp_cons) <
+       return (queue->tx.req_prod_pvt - queue->tx.rsp_cons) <
                (TX_MAX_TARGET - MAX_SKB_FRAGS - 2);
 }
 
-static void xennet_maybe_wake_tx(struct net_device *dev)
+static void xennet_maybe_wake_tx(struct netfront_queue *queue)
 {
-       struct netfront_info *np = netdev_priv(dev);
+       struct net_device *dev = queue->info->netdev;
+       struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, queue->id);
 
-       if (unlikely(netif_queue_stopped(dev)) &&
-           netfront_tx_slot_available(np) &&
+       if (unlikely(netif_tx_queue_stopped(dev_queue)) &&
+           netfront_tx_slot_available(queue) &&
            likely(netif_running(dev)))
-               netif_wake_queue(dev);
+               netif_tx_wake_queue(netdev_get_tx_queue(dev, queue->id));
 }
 
-static void xennet_alloc_rx_buffers(struct net_device *dev)
+static void xennet_alloc_rx_buffers(struct netfront_queue *queue)
 {
        unsigned short id;
-       struct netfront_info *np = netdev_priv(dev);
        struct sk_buff *skb;
        struct page *page;
        int i, batch_target, notify;
-       RING_IDX req_prod = np->rx.req_prod_pvt;
+       RING_IDX req_prod = queue->rx.req_prod_pvt;
        grant_ref_t ref;
        unsigned long pfn;
        void *vaddr;
        struct xen_netif_rx_request *req;
 
-       if (unlikely(!netif_carrier_ok(dev)))
+       if (unlikely(!netif_carrier_ok(queue->info->netdev)))
                return;
 
        /*
@@ -264,9 +286,10 @@ static void xennet_alloc_rx_buffers(struct net_device *dev)
         * allocator, so should reduce the chance of failed allocation requests
         * both for ourself and for other kernel subsystems.
         */
-       batch_target = np->rx_target - (req_prod - np->rx.rsp_cons);
-       for (i = skb_queue_len(&np->rx_batch); i < batch_target; i++) {
-               skb = __netdev_alloc_skb(dev, RX_COPY_THRESHOLD + NET_IP_ALIGN,
+       batch_target = queue->rx_target - (req_prod - queue->rx.rsp_cons);
+       for (i = skb_queue_len(&queue->rx_batch); i < batch_target; i++) {
+               skb = __netdev_alloc_skb(queue->info->netdev,
+                                        RX_COPY_THRESHOLD + NET_IP_ALIGN,
                                         GFP_ATOMIC | __GFP_NOWARN);
                if (unlikely(!skb))
                        goto no_skb;
@@ -279,7 +302,7 @@ static void xennet_alloc_rx_buffers(struct net_device *dev)
                        kfree_skb(skb);
 no_skb:
                        /* Could not allocate any skbuffs. Try again later. */
-                       mod_timer(&np->rx_refill_timer,
+                       mod_timer(&queue->rx_refill_timer,
                                  jiffies + (HZ/10));
 
                        /* Any skbuffs queued for refill? Force them out. */
@@ -289,44 +312,44 @@ static void xennet_alloc_rx_buffers(struct net_device *dev)
                }
 
                skb_add_rx_frag(skb, 0, page, 0, 0, PAGE_SIZE);
-               __skb_queue_tail(&np->rx_batch, skb);
+               __skb_queue_tail(&queue->rx_batch, skb);
        }
 
        /* Is the batch large enough to be worthwhile? */
-       if (i < (np->rx_target/2)) {
-               if (req_prod > np->rx.sring->req_prod)
+       if (i < (queue->rx_target/2)) {
+               if (req_prod > queue->rx.sring->req_prod)
                        goto push;
                return;
        }
 
        /* Adjust our fill target if we risked running out of buffers. */
-       if (((req_prod - np->rx.sring->rsp_prod) < (np->rx_target / 4)) &&
-           ((np->rx_target *= 2) > np->rx_max_target))
-               np->rx_target = np->rx_max_target;
+       if (((req_prod - queue->rx.sring->rsp_prod) < (queue->rx_target / 4)) &&
+           ((queue->rx_target *= 2) > queue->rx_max_target))
+               queue->rx_target = queue->rx_max_target;
 
  refill:
        for (i = 0; ; i++) {
-               skb = __skb_dequeue(&np->rx_batch);
+               skb = __skb_dequeue(&queue->rx_batch);
                if (skb == NULL)
                        break;
 
-               skb->dev = dev;
+               skb->dev = queue->info->netdev;
 
                id = xennet_rxidx(req_prod + i);
 
-               BUG_ON(np->rx_skbs[id]);
-               np->rx_skbs[id] = skb;
+               BUG_ON(queue->rx_skbs[id]);
+               queue->rx_skbs[id] = skb;
 
-               ref = gnttab_claim_grant_reference(&np->gref_rx_head);
+               ref = gnttab_claim_grant_reference(&queue->gref_rx_head);
                BUG_ON((signed short)ref < 0);
-               np->grant_rx_ref[id] = ref;
+               queue->grant_rx_ref[id] = ref;
 
                pfn = page_to_pfn(skb_frag_page(&skb_shinfo(skb)->frags[0]));
                vaddr = page_address(skb_frag_page(&skb_shinfo(skb)->frags[0]));
 
-               req = RING_GET_REQUEST(&np->rx, req_prod + i);
+               req = RING_GET_REQUEST(&queue->rx, req_prod + i);
                gnttab_grant_foreign_access_ref(ref,
-                                               np->xbdev->otherend_id,
+                                               queue->info->xbdev->otherend_id,
                                                pfn_to_mfn(pfn),
                                                0);
 
@@ -337,72 +360,77 @@ static void xennet_alloc_rx_buffers(struct net_device *dev)
        wmb();          /* barrier so backend seens requests */
 
        /* Above is a suitable barrier to ensure backend will see requests. */
-       np->rx.req_prod_pvt = req_prod + i;
+       queue->rx.req_prod_pvt = req_prod + i;
  push:
-       RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&np->rx, notify);
+       RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&queue->rx, notify);
        if (notify)
-               notify_remote_via_irq(np->rx_irq);
+               notify_remote_via_irq(queue->rx_irq);
 }
 
 static int xennet_open(struct net_device *dev)
 {
        struct netfront_info *np = netdev_priv(dev);
-
-       napi_enable(&np->napi);
-
-       spin_lock_bh(&np->rx_lock);
-       if (netif_carrier_ok(dev)) {
-               xennet_alloc_rx_buffers(dev);
-               np->rx.sring->rsp_event = np->rx.rsp_cons + 1;
-               if (RING_HAS_UNCONSUMED_RESPONSES(&np->rx))
-                       napi_schedule(&np->napi);
+       unsigned int num_queues = dev->real_num_tx_queues;
+       unsigned int i = 0;
+       struct netfront_queue *queue = NULL;
+
+       for (i = 0; i < num_queues; ++i) {
+               queue = &np->queues[i];
+               napi_enable(&queue->napi);
+
+               spin_lock_bh(&queue->rx_lock);
+               if (netif_carrier_ok(dev)) {
+                       xennet_alloc_rx_buffers(queue);
+                       queue->rx.sring->rsp_event = queue->rx.rsp_cons + 1;
+                       if (RING_HAS_UNCONSUMED_RESPONSES(&queue->rx))
+                               napi_schedule(&queue->napi);
+               }
+               spin_unlock_bh(&queue->rx_lock);
        }
-       spin_unlock_bh(&np->rx_lock);
 
-       netif_start_queue(dev);
+       netif_tx_start_all_queues(dev);
 
        return 0;
 }
 
-static void xennet_tx_buf_gc(struct net_device *dev)
+static void xennet_tx_buf_gc(struct netfront_queue *queue)
 {
        RING_IDX cons, prod;
        unsigned short id;
-       struct netfront_info *np = netdev_priv(dev);
        struct sk_buff *skb;
 
-       BUG_ON(!netif_carrier_ok(dev));
+       BUG_ON(!netif_carrier_ok(queue->info->netdev));
 
        do {
-               prod = np->tx.sring->rsp_prod;
+               prod = queue->tx.sring->rsp_prod;
                rmb(); /* Ensure we see responses up to 'rp'. */
 
-               for (cons = np->tx.rsp_cons; cons != prod; cons++) {
+               for (cons = queue->tx.rsp_cons; cons != prod; cons++) {
                        struct xen_netif_tx_response *txrsp;
 
-                       txrsp = RING_GET_RESPONSE(&np->tx, cons);
+                       txrsp = RING_GET_RESPONSE(&queue->tx, cons);
                        if (txrsp->status == XEN_NETIF_RSP_NULL)
                                continue;
 
                        id  = txrsp->id;
-                       skb = np->tx_skbs[id].skb;
+                       skb = queue->tx_skbs[id].skb;
                        if (unlikely(gnttab_query_foreign_access(
-                               np->grant_tx_ref[id]) != 0)) {
+                               queue->grant_tx_ref[id]) != 0)) {
                                pr_alert("%s: warning -- grant still in use by backend domain\n",
                                         __func__);
                                BUG();
                        }
                        gnttab_end_foreign_access_ref(
-                               np->grant_tx_ref[id], GNTMAP_readonly);
+                               queue->grant_tx_ref[id], GNTMAP_readonly);
                        gnttab_release_grant_reference(
-                               &np->gref_tx_head, np->grant_tx_ref[id]);
-                       np->grant_tx_ref[id] = GRANT_INVALID_REF;
-                       np->grant_tx_page[id] = NULL;
-                       add_id_to_freelist(&np->tx_skb_freelist, np->tx_skbs, id);
+                               &queue->gref_tx_head, queue->grant_tx_ref[id]);
+                       queue->grant_tx_ref[id] = GRANT_INVALID_REF;
+                       queue->grant_tx_page[id] = NULL;
+                       add_id_to_freelist(&queue->tx_skb_freelist, queue->tx_skbs, id);
                        dev_kfree_skb_irq(skb);
                }
 
-               np->tx.rsp_cons = prod;
+               queue->tx.rsp_cons = prod;
 
                /*
                 * Set a new event, then check for race with update of tx_cons.
@@ -412,21 +440,20 @@ static void xennet_tx_buf_gc(struct net_device *dev)
                 * data is outstanding: in such cases notification from Xen is
                 * likely to be the only kick that we'll get.
                 */
-               np->tx.sring->rsp_event =
-                       prod + ((np->tx.sring->req_prod - prod) >> 1) + 1;
+               queue->tx.sring->rsp_event =
+                       prod + ((queue->tx.sring->req_prod - prod) >> 1) + 1;
                mb();           /* update shared area */
-       } while ((cons == prod) && (prod != np->tx.sring->rsp_prod));
+       } while ((cons == prod) && (prod != queue->tx.sring->rsp_prod));
 
-       xennet_maybe_wake_tx(dev);
+       xennet_maybe_wake_tx(queue);
 }
 
-static void xennet_make_frags(struct sk_buff *skb, struct net_device *dev,
+static void xennet_make_frags(struct sk_buff *skb, struct netfront_queue *queue,
                              struct xen_netif_tx_request *tx)
 {
-       struct netfront_info *np = netdev_priv(dev);
        char *data = skb->data;
        unsigned long mfn;
-       RING_IDX prod = np->tx.req_prod_pvt;
+       RING_IDX prod = queue->tx.req_prod_pvt;
        int frags = skb_shinfo(skb)->nr_frags;
        unsigned int offset = offset_in_page(data);
        unsigned int len = skb_headlen(skb);
@@ -443,19 +470,19 @@ static void xennet_make_frags(struct sk_buff *skb, struct net_device *dev,
                data += tx->size;
                offset = 0;
 
-               id = get_id_from_freelist(&np->tx_skb_freelist, np->tx_skbs);
-               np->tx_skbs[id].skb = skb_get(skb);
-               tx = RING_GET_REQUEST(&np->tx, prod++);
+               id = get_id_from_freelist(&queue->tx_skb_freelist, queue->tx_skbs);
+               queue->tx_skbs[id].skb = skb_get(skb);
+               tx = RING_GET_REQUEST(&queue->tx, prod++);
                tx->id = id;
-               ref = gnttab_claim_grant_reference(&np->gref_tx_head);
+               ref = gnttab_claim_grant_reference(&queue->gref_tx_head);
                BUG_ON((signed short)ref < 0);
 
                mfn = virt_to_mfn(data);
-               gnttab_grant_foreign_access_ref(ref, np->xbdev->otherend_id,
+               gnttab_grant_foreign_access_ref(ref, queue->info->xbdev->otherend_id,
                                                mfn, GNTMAP_readonly);
 
-               np->grant_tx_page[id] = virt_to_page(data);
-               tx->gref = np->grant_tx_ref[id] = ref;
+               queue->grant_tx_page[id] = virt_to_page(data);
+               tx->gref = queue->grant_tx_ref[id] = ref;
                tx->offset = offset;
                tx->size = len;
                tx->flags = 0;
@@ -487,21 +514,21 @@ static void xennet_make_frags(struct sk_buff *skb, struct net_device *dev,
 
                        tx->flags |= XEN_NETTXF_more_data;
 
-                       id = get_id_from_freelist(&np->tx_skb_freelist,
-                                                 np->tx_skbs);
-                       np->tx_skbs[id].skb = skb_get(skb);
-                       tx = RING_GET_REQUEST(&np->tx, prod++);
+                       id = get_id_from_freelist(&queue->tx_skb_freelist,
+                                                 queue->tx_skbs);
+                       queue->tx_skbs[id].skb = skb_get(skb);
+                       tx = RING_GET_REQUEST(&queue->tx, prod++);
                        tx->id = id;
-                       ref = gnttab_claim_grant_reference(&np->gref_tx_head);
+                       ref = gnttab_claim_grant_reference(&queue->gref_tx_head);
                        BUG_ON((signed short)ref < 0);
 
                        mfn = pfn_to_mfn(page_to_pfn(page));
                        gnttab_grant_foreign_access_ref(ref,
-                                                       np->xbdev->otherend_id,
+                                                       queue->info->xbdev->otherend_id,
                                                        mfn, GNTMAP_readonly);
 
-                       np->grant_tx_page[id] = page;
-                       tx->gref = np->grant_tx_ref[id] = ref;
+                       queue->grant_tx_page[id] = page;
+                       tx->gref = queue->grant_tx_ref[id] = ref;
                        tx->offset = offset;
                        tx->size = bytes;
                        tx->flags = 0;
@@ -518,7 +545,7 @@ static void xennet_make_frags(struct sk_buff *skb, struct net_device *dev,
                }
        }
 
-       np->tx.req_prod_pvt = prod;
+       queue->tx.req_prod_pvt = prod;
 }
 
 /*
@@ -544,6 +571,24 @@ static int xennet_count_skb_frag_slots(struct sk_buff *skb)
        return pages;
 }
 
+static u16 xennet_select_queue(struct net_device *dev, struct sk_buff *skb,
+                              void *accel_priv, select_queue_fallback_t fallback)
+{
+       unsigned int num_queues = dev->real_num_tx_queues;
+       u32 hash;
+       u16 queue_idx;
+
+       /* First, check if there is only one queue */
+       if (num_queues == 1) {
+               queue_idx = 0;
+       } else {
+               hash = skb_get_hash(skb);
+               queue_idx = hash % num_queues;
+       }
+
+       return queue_idx;
+}
+
 static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        unsigned short id;
@@ -559,6 +604,16 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
        unsigned int offset = offset_in_page(data);
        unsigned int len = skb_headlen(skb);
        unsigned long flags;
+       struct netfront_queue *queue = NULL;
+       unsigned int num_queues = dev->real_num_tx_queues;
+       u16 queue_index;
+
+       /* Drop the packet if no queues are set up */
+       if (num_queues < 1)
+               goto drop;
+       /* Determine which queue to transmit this SKB on */
+       queue_index = skb_get_queue_mapping(skb);
+       queue = &np->queues[queue_index];
 
        /* If skb->len is too big for wire format, drop skb and alert
         * user about misconfiguration.
@@ -578,30 +633,30 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
                goto drop;
        }
 
-       spin_lock_irqsave(&np->tx_lock, flags);
+       spin_lock_irqsave(&queue->tx_lock, flags);
 
        if (unlikely(!netif_carrier_ok(dev) ||
                     (slots > 1 && !xennet_can_sg(dev)) ||
                     netif_needs_gso(skb, netif_skb_features(skb)))) {
-               spin_unlock_irqrestore(&np->tx_lock, flags);
+               spin_unlock_irqrestore(&queue->tx_lock, flags);
                goto drop;
        }
 
-       i = np->tx.req_prod_pvt;
+       i = queue->tx.req_prod_pvt;
 
-       id = get_id_from_freelist(&np->tx_skb_freelist, np->tx_skbs);
-       np->tx_skbs[id].skb = skb;
+       id = get_id_from_freelist(&queue->tx_skb_freelist, queue->tx_skbs);
+       queue->tx_skbs[id].skb = skb;
 
-       tx = RING_GET_REQUEST(&np->tx, i);
+       tx = RING_GET_REQUEST(&queue->tx, i);
 
        tx->id   = id;
-       ref = gnttab_claim_grant_reference(&np->gref_tx_head);
+       ref = gnttab_claim_grant_reference(&queue->gref_tx_head);
        BUG_ON((signed short)ref < 0);
        mfn = virt_to_mfn(data);
        gnttab_grant_foreign_access_ref(
-               ref, np->xbdev->otherend_id, mfn, GNTMAP_readonly);
-       np->grant_tx_page[id] = virt_to_page(data);
-       tx->gref = np->grant_tx_ref[id] = ref;
+               ref, queue->info->xbdev->otherend_id, mfn, GNTMAP_readonly);
+       queue->grant_tx_page[id] = virt_to_page(data);
+       tx->gref = queue->grant_tx_ref[id] = ref;
        tx->offset = offset;
        tx->size = len;
 
@@ -617,7 +672,7 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
                struct xen_netif_extra_info *gso;
 
                gso = (struct xen_netif_extra_info *)
-                       RING_GET_REQUEST(&np->tx, ++i);
+                       RING_GET_REQUEST(&queue->tx, ++i);
 
                tx->flags |= XEN_NETTXF_extra_info;
 
@@ -632,14 +687,14 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
                gso->flags = 0;
        }
 
-       np->tx.req_prod_pvt = i + 1;
+       queue->tx.req_prod_pvt = i + 1;
 
-       xennet_make_frags(skb, dev, tx);
+       xennet_make_frags(skb, queue, tx);
        tx->size = skb->len;
 
-       RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&np->tx, notify);
+       RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&queue->tx, notify);
        if (notify)
-               notify_remote_via_irq(np->tx_irq);
+               notify_remote_via_irq(queue->tx_irq);
 
        u64_stats_update_begin(&stats->syncp);
        stats->tx_bytes += skb->len;
@@ -647,12 +702,12 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
        u64_stats_update_end(&stats->syncp);
 
        /* Note: It is not safe to access skb after xennet_tx_buf_gc()! */
-       xennet_tx_buf_gc(dev);
+       xennet_tx_buf_gc(queue);
 
-       if (!netfront_tx_slot_available(np))
-               netif_stop_queue(dev);
+       if (!netfront_tx_slot_available(queue))
+               netif_tx_stop_queue(netdev_get_tx_queue(dev, queue->id));
 
-       spin_unlock_irqrestore(&np->tx_lock, flags);
+       spin_unlock_irqrestore(&queue->tx_lock, flags);
 
        return NETDEV_TX_OK;
 
@@ -665,32 +720,38 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)
 static int xennet_close(struct net_device *dev)
 {
        struct netfront_info *np = netdev_priv(dev);
-       netif_stop_queue(np->netdev);
-       napi_disable(&np->napi);
+       unsigned int num_queues = dev->real_num_tx_queues;
+       unsigned int i;
+       struct netfront_queue *queue;
+       netif_tx_stop_all_queues(np->netdev);
+       for (i = 0; i < num_queues; ++i) {
+               queue = &np->queues[i];
+               napi_disable(&queue->napi);
+       }
        return 0;
 }
 
-static void xennet_move_rx_slot(struct netfront_info *np, struct sk_buff *skb,
+static void xennet_move_rx_slot(struct netfront_queue *queue, struct sk_buff *skb,
                                grant_ref_t ref)
 {
-       int new = xennet_rxidx(np->rx.req_prod_pvt);
-
-       BUG_ON(np->rx_skbs[new]);
-       np->rx_skbs[new] = skb;
-       np->grant_rx_ref[new] = ref;
-       RING_GET_REQUEST(&np->rx, np->rx.req_prod_pvt)->id = new;
-       RING_GET_REQUEST(&np->rx, np->rx.req_prod_pvt)->gref = ref;
-       np->rx.req_prod_pvt++;
+       int new = xennet_rxidx(queue->rx.req_prod_pvt);
+
+       BUG_ON(queue->rx_skbs[new]);
+       queue->rx_skbs[new] = skb;
+       queue->grant_rx_ref[new] = ref;
+       RING_GET_REQUEST(&queue->rx, queue->rx.req_prod_pvt)->id = new;
+       RING_GET_REQUEST(&queue->rx, queue->rx.req_prod_pvt)->gref = ref;
+       queue->rx.req_prod_pvt++;
 }
 
-static int xennet_get_extras(struct netfront_info *np,
+static int xennet_get_extras(struct netfront_queue *queue,
                             struct xen_netif_extra_info *extras,
                             RING_IDX rp)
 
 {
        struct xen_netif_extra_info *extra;
-       struct device *dev = &np->netdev->dev;
-       RING_IDX cons = np->rx.rsp_cons;
+       struct device *dev = &queue->info->netdev->dev;
+       RING_IDX cons = queue->rx.rsp_cons;
        int err = 0;
 
        do {
@@ -705,7 +766,7 @@ static int xennet_get_extras(struct netfront_info *np,
                }
 
                extra = (struct xen_netif_extra_info *)
-                       RING_GET_RESPONSE(&np->rx, ++cons);
+                       RING_GET_RESPONSE(&queue->rx, ++cons);
 
                if (unlikely(!extra->type ||
                             extra->type >= XEN_NETIF_EXTRA_TYPE_MAX)) {
@@ -718,33 +779,33 @@ static int xennet_get_extras(struct netfront_info *np,
                               sizeof(*extra));
                }
 
-               skb = xennet_get_rx_skb(np, cons);
-               ref = xennet_get_rx_ref(np, cons);
-               xennet_move_rx_slot(np, skb, ref);
+               skb = xennet_get_rx_skb(queue, cons);
+               ref = xennet_get_rx_ref(queue, cons);
+               xennet_move_rx_slot(queue, skb, ref);
        } while (extra->flags & XEN_NETIF_EXTRA_FLAG_MORE);
 
-       np->rx.rsp_cons = cons;
+       queue->rx.rsp_cons = cons;
        return err;
 }
 
-static int xennet_get_responses(struct netfront_info *np,
+static int xennet_get_responses(struct netfront_queue *queue,
                                struct netfront_rx_info *rinfo, RING_IDX rp,
                                struct sk_buff_head *list)
 {
        struct xen_netif_rx_response *rx = &rinfo->rx;
        struct xen_netif_extra_info *extras = rinfo->extras;
-       struct device *dev = &np->netdev->dev;
-       RING_IDX cons = np->rx.rsp_cons;
-       struct sk_buff *skb = xennet_get_rx_skb(np, cons);
-       grant_ref_t ref = xennet_get_rx_ref(np, cons);
+       struct device *dev = &queue->info->netdev->dev;
+       RING_IDX cons = queue->rx.rsp_cons;
+       struct sk_buff *skb = xennet_get_rx_skb(queue, cons);
+       grant_ref_t ref = xennet_get_rx_ref(queue, cons);
        int max = MAX_SKB_FRAGS + (rx->status <= RX_COPY_THRESHOLD);
        int slots = 1;
        int err = 0;
        unsigned long ret;
 
        if (rx->flags & XEN_NETRXF_extra_info) {
-               err = xennet_get_extras(np, extras, rp);
-               cons = np->rx.rsp_cons;
+               err = xennet_get_extras(queue, extras, rp);
+               cons = queue->rx.rsp_cons;
        }
 
        for (;;) {
@@ -753,7 +814,7 @@ static int xennet_get_responses(struct netfront_info *np,
                        if (net_ratelimit())
                                dev_warn(dev, "rx->offset: %x, size: %u\n",
                                         rx->offset, rx->status);
-                       xennet_move_rx_slot(np, skb, ref);
+                       xennet_move_rx_slot(queue, skb, ref);
                        err = -EINVAL;
                        goto next;
                }
@@ -774,7 +835,7 @@ static int xennet_get_responses(struct netfront_info *np,
                ret = gnttab_end_foreign_access_ref(ref, 0);
                BUG_ON(!ret);
 
-               gnttab_release_grant_reference(&np->gref_rx_head, ref);
+               gnttab_release_grant_reference(&queue->gref_rx_head, ref);
 
                __skb_queue_tail(list, skb);
 
@@ -789,9 +850,9 @@ static int xennet_get_responses(struct netfront_info *np,
                        break;
                }
 
-               rx = RING_GET_RESPONSE(&np->rx, cons + slots);
-               skb = xennet_get_rx_skb(np, cons + slots);
-               ref = xennet_get_rx_ref(np, cons + slots);
+               rx = RING_GET_RESPONSE(&queue->rx, cons + slots);
+               skb = xennet_get_rx_skb(queue, cons + slots);
+               ref = xennet_get_rx_ref(queue, cons + slots);
                slots++;
        }
 
@@ -802,7 +863,7 @@ static int xennet_get_responses(struct netfront_info *np,
        }
 
        if (unlikely(err))
-               np->rx.rsp_cons = cons + slots;
+               queue->rx.rsp_cons = cons + slots;
 
        return err;
 }
@@ -836,17 +897,17 @@ static int xennet_set_skb_gso(struct sk_buff *skb,
        return 0;
 }
 
-static RING_IDX xennet_fill_frags(struct netfront_info *np,
+static RING_IDX xennet_fill_frags(struct netfront_queue *queue,
                                  struct sk_buff *skb,
                                  struct sk_buff_head *list)
 {
        struct skb_shared_info *shinfo = skb_shinfo(skb);
-       RING_IDX cons = np->rx.rsp_cons;
+       RING_IDX cons = queue->rx.rsp_cons;
        struct sk_buff *nskb;
 
        while ((nskb = __skb_dequeue(list))) {
                struct xen_netif_rx_response *rx =
-                       RING_GET_RESPONSE(&np->rx, ++cons);
+                       RING_GET_RESPONSE(&queue->rx, ++cons);
                skb_frag_t *nfrag = &skb_shinfo(nskb)->frags[0];
 
                if (shinfo->nr_frags == MAX_SKB_FRAGS) {
@@ -879,7 +940,7 @@ static int checksum_setup(struct net_device *dev, struct sk_buff *skb)
         */
        if (skb->ip_summed != CHECKSUM_PARTIAL && skb_is_gso(skb)) {
                struct netfront_info *np = netdev_priv(dev);
-               np->rx_gso_checksum_fixup++;
+               atomic_inc(&np->rx_gso_checksum_fixup);
                skb->ip_summed = CHECKSUM_PARTIAL;
                recalculate_partial_csum = true;
        }
@@ -891,11 +952,10 @@ static int checksum_setup(struct net_device *dev, struct sk_buff *skb)
        return skb_checksum_setup(skb, recalculate_partial_csum);
 }
 
-static int handle_incoming_queue(struct net_device *dev,
+static int handle_incoming_queue(struct netfront_queue *queue,
                                 struct sk_buff_head *rxq)
 {
-       struct netfront_info *np = netdev_priv(dev);
-       struct netfront_stats *stats = this_cpu_ptr(np->stats);
+       struct netfront_stats *stats = this_cpu_ptr(queue->info->stats);
        int packets_dropped = 0;
        struct sk_buff *skb;
 
@@ -906,13 +966,13 @@ static int handle_incoming_queue(struct net_device *dev,
                        __pskb_pull_tail(skb, pull_to - skb_headlen(skb));
 
                /* Ethernet work: Delayed to here as it peeks the header. */
-               skb->protocol = eth_type_trans(skb, dev);
+               skb->protocol = eth_type_trans(skb, queue->info->netdev);
                skb_reset_network_header(skb);
 
-               if (checksum_setup(dev, skb)) {
+               if (checksum_setup(queue->info->netdev, skb)) {
                        kfree_skb(skb);
                        packets_dropped++;
-                       dev->stats.rx_errors++;
+                       queue->info->netdev->stats.rx_errors++;
                        continue;
                }
 
@@ -922,7 +982,7 @@ static int handle_incoming_queue(struct net_device *dev,
                u64_stats_update_end(&stats->syncp);
 
                /* Pass it up. */
-               napi_gro_receive(&np->napi, skb);
+               napi_gro_receive(&queue->napi, skb);
        }
 
        return packets_dropped;
@@ -930,8 +990,8 @@ static int handle_incoming_queue(struct net_device *dev,
 
 static int xennet_poll(struct napi_struct *napi, int budget)
 {
-       struct netfront_info *np = container_of(napi, struct netfront_info, napi);
-       struct net_device *dev = np->netdev;
+       struct netfront_queue *queue = container_of(napi, struct netfront_queue, napi);
+       struct net_device *dev = queue->info->netdev;
        struct sk_buff *skb;
        struct netfront_rx_info rinfo;
        struct xen_netif_rx_response *rx = &rinfo.rx;
@@ -944,29 +1004,29 @@ static int xennet_poll(struct napi_struct *napi, int budget)
        unsigned long flags;
        int err;
 
-       spin_lock(&np->rx_lock);
+       spin_lock(&queue->rx_lock);
 
        skb_queue_head_init(&rxq);
        skb_queue_head_init(&errq);
        skb_queue_head_init(&tmpq);
 
-       rp = np->rx.sring->rsp_prod;
+       rp = queue->rx.sring->rsp_prod;
        rmb(); /* Ensure we see queued responses up to 'rp'. */
 
-       i = np->rx.rsp_cons;
+       i = queue->rx.rsp_cons;
        work_done = 0;
        while ((i != rp) && (work_done < budget)) {
-               memcpy(rx, RING_GET_RESPONSE(&np->rx, i), sizeof(*rx));
+               memcpy(rx, RING_GET_RESPONSE(&queue->rx, i), sizeof(*rx));
                memset(extras, 0, sizeof(rinfo.extras));
 
-               err = xennet_get_responses(np, &rinfo, rp, &tmpq);
+               err = xennet_get_responses(queue, &rinfo, rp, &tmpq);
 
                if (unlikely(err)) {
 err:
                        while ((skb = __skb_dequeue(&tmpq)))
                                __skb_queue_tail(&errq, skb);
                        dev->stats.rx_errors++;
-                       i = np->rx.rsp_cons;
+                       i = queue->rx.rsp_cons;
                        continue;
                }
 
@@ -978,7 +1038,7 @@ static int xennet_poll(struct napi_struct *napi, int budget)
 
                        if (unlikely(xennet_set_skb_gso(skb, gso))) {
                                __skb_queue_head(&tmpq, skb);
-                               np->rx.rsp_cons += skb_queue_len(&tmpq);
+                               queue->rx.rsp_cons += skb_queue_len(&tmpq);
                                goto err;
                        }
                }
@@ -992,7 +1052,7 @@ static int xennet_poll(struct napi_struct *napi, int budget)
                skb->data_len = rx->status;
                skb->len += rx->status;
 
-               i = xennet_fill_frags(np, skb, &tmpq);
+               i = xennet_fill_frags(queue, skb, &tmpq);
 
                if (rx->flags & XEN_NETRXF_csum_blank)
                        skb->ip_summed = CHECKSUM_PARTIAL;
@@ -1001,22 +1061,22 @@ static int xennet_poll(struct napi_struct *napi, int budget)
 
                __skb_queue_tail(&rxq, skb);
 
-               np->rx.rsp_cons = ++i;
+               queue->rx.rsp_cons = ++i;
                work_done++;
        }
 
        __skb_queue_purge(&errq);
 
-       work_done -= handle_incoming_queue(dev, &rxq);
+       work_done -= handle_incoming_queue(queue, &rxq);
 
        /* If we get a callback with very few responses, reduce fill target. */
        /* NB. Note exponential increase, linear decrease. */
-       if (((np->rx.req_prod_pvt - np->rx.sring->rsp_prod) >
-            ((3*np->rx_target) / 4)) &&
-           (--np->rx_target < np->rx_min_target))
-               np->rx_target = np->rx_min_target;
+       if (((queue->rx.req_prod_pvt - queue->rx.sring->rsp_prod) >
+            ((3*queue->rx_target) / 4)) &&
+           (--queue->rx_target < queue->rx_min_target))
+               queue->rx_target = queue->rx_min_target;
 
-       xennet_alloc_rx_buffers(dev);
+       xennet_alloc_rx_buffers(queue);
 
        if (work_done < budget) {
                int more_to_do = 0;
@@ -1025,14 +1085,14 @@ static int xennet_poll(struct napi_struct *napi, int budget)
 
                local_irq_save(flags);
 
-               RING_FINAL_CHECK_FOR_RESPONSES(&np->rx, more_to_do);
+               RING_FINAL_CHECK_FOR_RESPONSES(&queue->rx, more_to_do);
                if (!more_to_do)
                        __napi_complete(napi);
 
                local_irq_restore(flags);
        }
 
-       spin_unlock(&np->rx_lock);
+       spin_unlock(&queue->rx_lock);
 
        return work_done;
 }
@@ -1080,43 +1140,43 @@ static struct rtnl_link_stats64 *xennet_get_stats64(struct net_device *dev,
        return tot;
 }
 
-static void xennet_release_tx_bufs(struct netfront_info *np)
+static void xennet_release_tx_bufs(struct netfront_queue *queue)
 {
        struct sk_buff *skb;
        int i;
 
        for (i = 0; i < NET_TX_RING_SIZE; i++) {
                /* Skip over entries which are actually freelist references */
-               if (skb_entry_is_link(&np->tx_skbs[i]))
+               if (skb_entry_is_link(&queue->tx_skbs[i]))
                        continue;
 
-               skb = np->tx_skbs[i].skb;
-               get_page(np->grant_tx_page[i]);
-               gnttab_end_foreign_access(np->grant_tx_ref[i],
+               skb = queue->tx_skbs[i].skb;
+               get_page(queue->grant_tx_page[i]);
+               gnttab_end_foreign_access(queue->grant_tx_ref[i],
                                          GNTMAP_readonly,
-                                         (unsigned long)page_address(np->grant_tx_page[i]));
-               np->grant_tx_page[i] = NULL;
-               np->grant_tx_ref[i] = GRANT_INVALID_REF;
-               add_id_to_freelist(&np->tx_skb_freelist, np->tx_skbs, i);
+                                         (unsigned long)page_address(queue->grant_tx_page[i]));
+               queue->grant_tx_page[i] = NULL;
+               queue->grant_tx_ref[i] = GRANT_INVALID_REF;
+               add_id_to_freelist(&queue->tx_skb_freelist, queue->tx_skbs, i);
                dev_kfree_skb_irq(skb);
        }
 }
 
-static void xennet_release_rx_bufs(struct netfront_info *np)
+static void xennet_release_rx_bufs(struct netfront_queue *queue)
 {
        int id, ref;
 
-       spin_lock_bh(&np->rx_lock);
+       spin_lock_bh(&queue->rx_lock);
 
        for (id = 0; id < NET_RX_RING_SIZE; id++) {
                struct sk_buff *skb;
                struct page *page;
 
-               skb = np->rx_skbs[id];
+               skb = queue->rx_skbs[id];
                if (!skb)
                        continue;
 
-               ref = np->grant_rx_ref[id];
+               ref = queue->grant_rx_ref[id];
                if (ref == GRANT_INVALID_REF)
                        continue;
 
@@ -1128,21 +1188,28 @@ static void xennet_release_rx_bufs(struct netfront_info *np)
                get_page(page);
                gnttab_end_foreign_access(ref, 0,
                                          (unsigned long)page_address(page));
-               np->grant_rx_ref[id] = GRANT_INVALID_REF;
+               queue->grant_rx_ref[id] = GRANT_INVALID_REF;
 
                kfree_skb(skb);
        }
 
-       spin_unlock_bh(&np->rx_lock);
+       spin_unlock_bh(&queue->rx_lock);
 }
 
 static void xennet_uninit(struct net_device *dev)
 {
        struct netfront_info *np = netdev_priv(dev);
-       xennet_release_tx_bufs(np);
-       xennet_release_rx_bufs(np);
-       gnttab_free_grant_references(np->gref_tx_head);
-       gnttab_free_grant_references(np->gref_rx_head);
+       unsigned int num_queues = dev->real_num_tx_queues;
+       struct netfront_queue *queue;
+       unsigned int i;
+
+       for (i = 0; i < num_queues; ++i) {
+               queue = &np->queues[i];
+               xennet_release_tx_bufs(queue);
+               xennet_release_rx_bufs(queue);
+               gnttab_free_grant_references(queue->gref_tx_head);
+               gnttab_free_grant_references(queue->gref_rx_head);
+       }
 }
 
 static netdev_features_t xennet_fix_features(struct net_device *dev,
@@ -1203,25 +1270,24 @@ static int xennet_set_features(struct net_device *dev,
 
 static irqreturn_t xennet_tx_interrupt(int irq, void *dev_id)
 {
-       struct netfront_info *np = dev_id;
-       struct net_device *dev = np->netdev;
+       struct netfront_queue *queue = dev_id;
        unsigned long flags;
 
-       spin_lock_irqsave(&np->tx_lock, flags);
-       xennet_tx_buf_gc(dev);
-       spin_unlock_irqrestore(&np->tx_lock, flags);
+       spin_lock_irqsave(&queue->tx_lock, flags);
+       xennet_tx_buf_gc(queue);
+       spin_unlock_irqrestore(&queue->tx_lock, flags);
 
        return IRQ_HANDLED;
 }
 
 static irqreturn_t xennet_rx_interrupt(int irq, void *dev_id)
 {
-       struct netfront_info *np = dev_id;
-       struct net_device *dev = np->netdev;
+       struct netfront_queue *queue = dev_id;
+       struct net_device *dev = queue->info->netdev;
 
        if (likely(netif_carrier_ok(dev) &&
-                  RING_HAS_UNCONSUMED_RESPONSES(&np->rx)))
-                       napi_schedule(&np->napi);
+                  RING_HAS_UNCONSUMED_RESPONSES(&queue->rx)))
+                       napi_schedule(&queue->napi);
 
        return IRQ_HANDLED;
 }
@@ -1236,7 +1302,12 @@ static irqreturn_t xennet_interrupt(int irq, void *dev_id)
 #ifdef CONFIG_NET_POLL_CONTROLLER
 static void xennet_poll_controller(struct net_device *dev)
 {
-       xennet_interrupt(0, dev);
+       /* Poll each queue */
+       struct netfront_info *info = netdev_priv(dev);
+       unsigned int num_queues = dev->real_num_tx_queues;
+       unsigned int i;
+       for (i = 0; i < num_queues; ++i)
+               xennet_interrupt(0, &info->queues[i]);
 }
 #endif
 
@@ -1251,6 +1322,7 @@ static const struct net_device_ops xennet_netdev_ops = {
        .ndo_validate_addr   = eth_validate_addr,
        .ndo_fix_features    = xennet_fix_features,
        .ndo_set_features    = xennet_set_features,
+       .ndo_select_queue    = xennet_select_queue,
 #ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller = xennet_poll_controller,
 #endif
@@ -1258,66 +1330,30 @@ static const struct net_device_ops xennet_netdev_ops = {
 
 static struct net_device *xennet_create_dev(struct xenbus_device *dev)
 {
-       int i, err;
+       int err;
        struct net_device *netdev;
        struct netfront_info *np;
 
-       netdev = alloc_etherdev(sizeof(struct netfront_info));
+       netdev = alloc_etherdev_mq(sizeof(struct netfront_info), xennet_max_queues);
        if (!netdev)
                return ERR_PTR(-ENOMEM);
 
        np                   = netdev_priv(netdev);
        np->xbdev            = dev;
 
-       spin_lock_init(&np->tx_lock);
-       spin_lock_init(&np->rx_lock);
-
-       skb_queue_head_init(&np->rx_batch);
-       np->rx_target     = RX_DFL_MIN_TARGET;
-       np->rx_min_target = RX_DFL_MIN_TARGET;
-       np->rx_max_target = RX_MAX_TARGET;
-
-       init_timer(&np->rx_refill_timer);
-       np->rx_refill_timer.data = (unsigned long)netdev;
-       np->rx_refill_timer.function = rx_refill_timeout;
+       /* No need to use rtnl_lock() before the call below as it
+        * happens before register_netdev().
+        */
+       netif_set_real_num_tx_queues(netdev, 0);
+       np->queues = NULL;
 
        err = -ENOMEM;
        np->stats = netdev_alloc_pcpu_stats(struct netfront_stats);
        if (np->stats == NULL)
                goto exit;
 
-       /* Initialise tx_skbs as a free chain containing every entry. */
-       np->tx_skb_freelist = 0;
-       for (i = 0; i < NET_TX_RING_SIZE; i++) {
-               skb_entry_set_link(&np->tx_skbs[i], i+1);
-               np->grant_tx_ref[i] = GRANT_INVALID_REF;
-               np->grant_tx_page[i] = NULL;
-       }
-
-       /* Clear out rx_skbs */
-       for (i = 0; i < NET_RX_RING_SIZE; i++) {
-               np->rx_skbs[i] = NULL;
-               np->grant_rx_ref[i] = GRANT_INVALID_REF;
-       }
-
-       /* A grant for every tx ring slot */
-       if (gnttab_alloc_grant_references(TX_MAX_TARGET,
-                                         &np->gref_tx_head) < 0) {
-               pr_alert("can't alloc tx grant refs\n");
-               err = -ENOMEM;
-               goto exit_free_stats;
-       }
-       /* A grant for every rx ring slot */
-       if (gnttab_alloc_grant_references(RX_MAX_TARGET,
-                                         &np->gref_rx_head) < 0) {
-               pr_alert("can't alloc rx grant refs\n");
-               err = -ENOMEM;
-               goto exit_free_tx;
-       }
-
        netdev->netdev_ops      = &xennet_netdev_ops;
 
-       netif_napi_add(netdev, &np->napi, xennet_poll, 64);
        netdev->features        = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
                                  NETIF_F_GSO_ROBUST;
        netdev->hw_features     = NETIF_F_SG |
@@ -1332,7 +1368,7 @@ static struct net_device *xennet_create_dev(struct xenbus_device *dev)
          */
        netdev->features |= netdev->hw_features;
 
-       SET_ETHTOOL_OPS(netdev, &xennet_ethtool_ops);
+       netdev->ethtool_ops = &xennet_ethtool_ops;
        SET_NETDEV_DEV(netdev, &dev->dev);
 
        netif_set_gso_max_size(netdev, XEN_NETIF_MAX_TX_SIZE - MAX_TCP_HEADER);
@@ -1343,10 +1379,6 @@ static struct net_device *xennet_create_dev(struct xenbus_device *dev)
 
        return netdev;
 
- exit_free_tx:
-       gnttab_free_grant_references(np->gref_tx_head);
- exit_free_stats:
-       free_percpu(np->stats);
  exit:
        free_netdev(netdev);
        return ERR_PTR(err);
@@ -1404,30 +1436,36 @@ static void xennet_end_access(int ref, void *page)
 
 static void xennet_disconnect_backend(struct netfront_info *info)
 {
-       /* Stop old i/f to prevent errors whilst we rebuild the state. */
-       spin_lock_bh(&info->rx_lock);
-       spin_lock_irq(&info->tx_lock);
-       netif_carrier_off(info->netdev);
-       spin_unlock_irq(&info->tx_lock);
-       spin_unlock_bh(&info->rx_lock);
-
-       if (info->tx_irq && (info->tx_irq == info->rx_irq))
-               unbind_from_irqhandler(info->tx_irq, info);
-       if (info->tx_irq && (info->tx_irq != info->rx_irq)) {
-               unbind_from_irqhandler(info->tx_irq, info);
-               unbind_from_irqhandler(info->rx_irq, info);
-       }
-       info->tx_evtchn = info->rx_evtchn = 0;
-       info->tx_irq = info->rx_irq = 0;
+       unsigned int i = 0;
+       struct netfront_queue *queue = NULL;
+       unsigned int num_queues = info->netdev->real_num_tx_queues;
+
+       for (i = 0; i < num_queues; ++i) {
+               /* Stop old i/f to prevent errors whilst we rebuild the state. */
+               spin_lock_bh(&queue->rx_lock);
+               spin_lock_irq(&queue->tx_lock);
+               netif_carrier_off(queue->info->netdev);
+               spin_unlock_irq(&queue->tx_lock);
+               spin_unlock_bh(&queue->rx_lock);
+
+               if (queue->tx_irq && (queue->tx_irq == queue->rx_irq))
+                       unbind_from_irqhandler(queue->tx_irq, queue);
+               if (queue->tx_irq && (queue->tx_irq != queue->rx_irq)) {
+                       unbind_from_irqhandler(queue->tx_irq, queue);
+                       unbind_from_irqhandler(queue->rx_irq, queue);
+               }
+               queue->tx_evtchn = queue->rx_evtchn = 0;
+               queue->tx_irq = queue->rx_irq = 0;
 
-       /* End access and free the pages */
-       xennet_end_access(info->tx_ring_ref, info->tx.sring);
-       xennet_end_access(info->rx_ring_ref, info->rx.sring);
+               /* End access and free the pages */
+               xennet_end_access(queue->tx_ring_ref, queue->tx.sring);
+               xennet_end_access(queue->rx_ring_ref, queue->rx.sring);
 
-       info->tx_ring_ref = GRANT_INVALID_REF;
-       info->rx_ring_ref = GRANT_INVALID_REF;
-       info->tx.sring = NULL;
-       info->rx.sring = NULL;
+               queue->tx_ring_ref = GRANT_INVALID_REF;
+               queue->rx_ring_ref = GRANT_INVALID_REF;
+               queue->tx.sring = NULL;
+               queue->rx.sring = NULL;
+       }
 }
 
 /**
@@ -1468,100 +1506,86 @@ static int xen_net_read_mac(struct xenbus_device *dev, u8 mac[])
        return 0;
 }
 
-static int setup_netfront_single(struct netfront_info *info)
+static int setup_netfront_single(struct netfront_queue *queue)
 {
        int err;
 
-       err = xenbus_alloc_evtchn(info->xbdev, &info->tx_evtchn);
+       err = xenbus_alloc_evtchn(queue->info->xbdev, &queue->tx_evtchn);
        if (err < 0)
                goto fail;
 
-       err = bind_evtchn_to_irqhandler(info->tx_evtchn,
+       err = bind_evtchn_to_irqhandler(queue->tx_evtchn,
                                        xennet_interrupt,
-                                       0, info->netdev->name, info);
+                                       0, queue->info->netdev->name, queue);
        if (err < 0)
                goto bind_fail;
-       info->rx_evtchn = info->tx_evtchn;
-       info->rx_irq = info->tx_irq = err;
+       queue->rx_evtchn = queue->tx_evtchn;
+       queue->rx_irq = queue->tx_irq = err;
 
        return 0;
 
 bind_fail:
-       xenbus_free_evtchn(info->xbdev, info->tx_evtchn);
-       info->tx_evtchn = 0;
+       xenbus_free_evtchn(queue->info->xbdev, queue->tx_evtchn);
+       queue->tx_evtchn = 0;
 fail:
        return err;
 }
 
-static int setup_netfront_split(struct netfront_info *info)
+static int setup_netfront_split(struct netfront_queue *queue)
 {
        int err;
 
-       err = xenbus_alloc_evtchn(info->xbdev, &info->tx_evtchn);
+       err = xenbus_alloc_evtchn(queue->info->xbdev, &queue->tx_evtchn);
        if (err < 0)
                goto fail;
-       err = xenbus_alloc_evtchn(info->xbdev, &info->rx_evtchn);
+       err = xenbus_alloc_evtchn(queue->info->xbdev, &queue->rx_evtchn);
        if (err < 0)
                goto alloc_rx_evtchn_fail;
 
-       snprintf(info->tx_irq_name, sizeof(info->tx_irq_name),
-                "%s-tx", info->netdev->name);
-       err = bind_evtchn_to_irqhandler(info->tx_evtchn,
+       snprintf(queue->tx_irq_name, sizeof(queue->tx_irq_name),
+                "%s-tx", queue->name);
+       err = bind_evtchn_to_irqhandler(queue->tx_evtchn,
                                        xennet_tx_interrupt,
-                                       0, info->tx_irq_name, info);
+                                       0, queue->tx_irq_name, queue);
        if (err < 0)
                goto bind_tx_fail;
-       info->tx_irq = err;
+       queue->tx_irq = err;
 
-       snprintf(info->rx_irq_name, sizeof(info->rx_irq_name),
-                "%s-rx", info->netdev->name);
-       err = bind_evtchn_to_irqhandler(info->rx_evtchn,
+       snprintf(queue->rx_irq_name, sizeof(queue->rx_irq_name),
+                "%s-rx", queue->name);
+       err = bind_evtchn_to_irqhandler(queue->rx_evtchn,
                                        xennet_rx_interrupt,
-                                       0, info->rx_irq_name, info);
+                                       0, queue->rx_irq_name, queue);
        if (err < 0)
                goto bind_rx_fail;
-       info->rx_irq = err;
+       queue->rx_irq = err;
 
        return 0;
 
 bind_rx_fail:
-       unbind_from_irqhandler(info->tx_irq, info);
-       info->tx_irq = 0;
+       unbind_from_irqhandler(queue->tx_irq, queue);
+       queue->tx_irq = 0;
 bind_tx_fail:
-       xenbus_free_evtchn(info->xbdev, info->rx_evtchn);
-       info->rx_evtchn = 0;
+       xenbus_free_evtchn(queue->info->xbdev, queue->rx_evtchn);
+       queue->rx_evtchn = 0;
 alloc_rx_evtchn_fail:
-       xenbus_free_evtchn(info->xbdev, info->tx_evtchn);
-       info->tx_evtchn = 0;
+       xenbus_free_evtchn(queue->info->xbdev, queue->tx_evtchn);
+       queue->tx_evtchn = 0;
 fail:
        return err;
 }
 
-static int setup_netfront(struct xenbus_device *dev, struct netfront_info *info)
+static int setup_netfront(struct xenbus_device *dev,
+                       struct netfront_queue *queue, unsigned int feature_split_evtchn)
 {
        struct xen_netif_tx_sring *txs;
        struct xen_netif_rx_sring *rxs;
        int err;
-       struct net_device *netdev = info->netdev;
-       unsigned int feature_split_evtchn;
 
-       info->tx_ring_ref = GRANT_INVALID_REF;
-       info->rx_ring_ref = GRANT_INVALID_REF;
-       info->rx.sring = NULL;
-       info->tx.sring = NULL;
-       netdev->irq = 0;
-
-       err = xenbus_scanf(XBT_NIL, info->xbdev->otherend,
-                          "feature-split-event-channels", "%u",
-                          &feature_split_evtchn);
-       if (err < 0)
-               feature_split_evtchn = 0;
-
-       err = xen_net_read_mac(dev, netdev->dev_addr);
-       if (err) {
-               xenbus_dev_fatal(dev, err, "parsing %s/mac", dev->nodename);
-               goto fail;
-       }
+       queue->tx_ring_ref = GRANT_INVALID_REF;
+       queue->rx_ring_ref = GRANT_INVALID_REF;
+       queue->rx.sring = NULL;
+       queue->tx.sring = NULL;
 
        txs = (struct xen_netif_tx_sring *)get_zeroed_page(GFP_NOIO | __GFP_HIGH);
        if (!txs) {
@@ -1570,13 +1594,13 @@ static int setup_netfront(struct xenbus_device *dev, struct netfront_info *info)
                goto fail;
        }
        SHARED_RING_INIT(txs);
-       FRONT_RING_INIT(&info->tx, txs, PAGE_SIZE);
+       FRONT_RING_INIT(&queue->tx, txs, PAGE_SIZE);
 
        err = xenbus_grant_ring(dev, virt_to_mfn(txs));
        if (err < 0)
                goto grant_tx_ring_fail;
+       queue->tx_ring_ref = err;
 
-       info->tx_ring_ref = err;
        rxs = (struct xen_netif_rx_sring *)get_zeroed_page(GFP_NOIO | __GFP_HIGH);
        if (!rxs) {
                err = -ENOMEM;
@@ -1584,21 +1608,21 @@ static int setup_netfront(struct xenbus_device *dev, struct netfront_info *info)
                goto alloc_rx_ring_fail;
        }
        SHARED_RING_INIT(rxs);
-       FRONT_RING_INIT(&info->rx, rxs, PAGE_SIZE);
+       FRONT_RING_INIT(&queue->rx, rxs, PAGE_SIZE);
 
        err = xenbus_grant_ring(dev, virt_to_mfn(rxs));
        if (err < 0)
                goto grant_rx_ring_fail;
-       info->rx_ring_ref = err;
+       queue->rx_ring_ref = err;
 
        if (feature_split_evtchn)
-               err = setup_netfront_split(info);
+               err = setup_netfront_split(queue);
        /* setup single event channel if
         *  a) feature-split-event-channels == 0
         *  b) feature-split-event-channels == 1 but failed to setup
         */
        if (!feature_split_evtchn || (feature_split_evtchn && err))
-               err = setup_netfront_single(info);
+               err = setup_netfront_single(queue);
 
        if (err)
                goto alloc_evtchn_fail;
@@ -1609,17 +1633,163 @@ static int setup_netfront(struct xenbus_device *dev, struct netfront_info *info)
         * granted pages because backend is not accessing it at this point.
         */
 alloc_evtchn_fail:
-       gnttab_end_foreign_access_ref(info->rx_ring_ref, 0);
+       gnttab_end_foreign_access_ref(queue->rx_ring_ref, 0);
 grant_rx_ring_fail:
        free_page((unsigned long)rxs);
 alloc_rx_ring_fail:
-       gnttab_end_foreign_access_ref(info->tx_ring_ref, 0);
+       gnttab_end_foreign_access_ref(queue->tx_ring_ref, 0);
 grant_tx_ring_fail:
        free_page((unsigned long)txs);
 fail:
        return err;
 }
 
+/* Queue-specific initialisation
+ * This used to be done in xennet_create_dev() but must now
+ * be run per-queue.
+ */
+static int xennet_init_queue(struct netfront_queue *queue)
+{
+       unsigned short i;
+       int err = 0;
+
+       spin_lock_init(&queue->tx_lock);
+       spin_lock_init(&queue->rx_lock);
+
+       skb_queue_head_init(&queue->rx_batch);
+       queue->rx_target     = RX_DFL_MIN_TARGET;
+       queue->rx_min_target = RX_DFL_MIN_TARGET;
+       queue->rx_max_target = RX_MAX_TARGET;
+
+       init_timer(&queue->rx_refill_timer);
+       queue->rx_refill_timer.data = (unsigned long)queue;
+       queue->rx_refill_timer.function = rx_refill_timeout;
+
+       snprintf(queue->name, sizeof(queue->name), "%s-q%u",
+                queue->info->netdev->name, queue->id);
+
+       /* Initialise tx_skbs as a free chain containing every entry. */
+       queue->tx_skb_freelist = 0;
+       for (i = 0; i < NET_TX_RING_SIZE; i++) {
+               skb_entry_set_link(&queue->tx_skbs[i], i+1);
+               queue->grant_tx_ref[i] = GRANT_INVALID_REF;
+               queue->grant_tx_page[i] = NULL;
+       }
+
+       /* Clear out rx_skbs */
+       for (i = 0; i < NET_RX_RING_SIZE; i++) {
+               queue->rx_skbs[i] = NULL;
+               queue->grant_rx_ref[i] = GRANT_INVALID_REF;
+       }
+
+       /* A grant for every tx ring slot */
+       if (gnttab_alloc_grant_references(TX_MAX_TARGET,
+                                         &queue->gref_tx_head) < 0) {
+               pr_alert("can't alloc tx grant refs\n");
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       /* A grant for every rx ring slot */
+       if (gnttab_alloc_grant_references(RX_MAX_TARGET,
+                                         &queue->gref_rx_head) < 0) {
+               pr_alert("can't alloc rx grant refs\n");
+               err = -ENOMEM;
+               goto exit_free_tx;
+       }
+
+       netif_napi_add(queue->info->netdev, &queue->napi, xennet_poll, 64);
+
+       return 0;
+
+ exit_free_tx:
+       gnttab_free_grant_references(queue->gref_tx_head);
+ exit:
+       return err;
+}
+
+static int write_queue_xenstore_keys(struct netfront_queue *queue,
+                          struct xenbus_transaction *xbt, int write_hierarchical)
+{
+       /* Write the queue-specific keys into XenStore in the traditional
+        * way for a single queue, or in a queue subkeys for multiple
+        * queues.
+        */
+       struct xenbus_device *dev = queue->info->xbdev;
+       int err;
+       const char *message;
+       char *path;
+       size_t pathsize;
+
+       /* Choose the correct place to write the keys */
+       if (write_hierarchical) {
+               pathsize = strlen(dev->nodename) + 10;
+               path = kzalloc(pathsize, GFP_KERNEL);
+               if (!path) {
+                       err = -ENOMEM;
+                       message = "out of memory while writing ring references";
+                       goto error;
+               }
+               snprintf(path, pathsize, "%s/queue-%u",
+                               dev->nodename, queue->id);
+       } else {
+               path = (char *)dev->nodename;
+       }
+
+       /* Write ring references */
+       err = xenbus_printf(*xbt, path, "tx-ring-ref", "%u",
+                       queue->tx_ring_ref);
+       if (err) {
+               message = "writing tx-ring-ref";
+               goto error;
+       }
+
+       err = xenbus_printf(*xbt, path, "rx-ring-ref", "%u",
+                       queue->rx_ring_ref);
+       if (err) {
+               message = "writing rx-ring-ref";
+               goto error;
+       }
+
+       /* Write event channels; taking into account both shared
+        * and split event channel scenarios.
+        */
+       if (queue->tx_evtchn == queue->rx_evtchn) {
+               /* Shared event channel */
+               err = xenbus_printf(*xbt, path,
+                               "event-channel", "%u", queue->tx_evtchn);
+               if (err) {
+                       message = "writing event-channel";
+                       goto error;
+               }
+       } else {
+               /* Split event channels */
+               err = xenbus_printf(*xbt, path,
+                               "event-channel-tx", "%u", queue->tx_evtchn);
+               if (err) {
+                       message = "writing event-channel-tx";
+                       goto error;
+               }
+
+               err = xenbus_printf(*xbt, path,
+                               "event-channel-rx", "%u", queue->rx_evtchn);
+               if (err) {
+                       message = "writing event-channel-rx";
+                       goto error;
+               }
+       }
+
+       if (write_hierarchical)
+               kfree(path);
+       return 0;
+
+error:
+       if (write_hierarchical)
+               kfree(path);
+       xenbus_dev_fatal(dev, err, "%s", message);
+       return err;
+}
+
 /* Common code used when first setting up, and when resuming. */
 static int talk_to_netback(struct xenbus_device *dev,
                           struct netfront_info *info)
@@ -1627,11 +1797,83 @@ static int talk_to_netback(struct xenbus_device *dev,
        const char *message;
        struct xenbus_transaction xbt;
        int err;
+       unsigned int feature_split_evtchn;
+       unsigned int i = 0;
+       unsigned int max_queues = 0;
+       struct netfront_queue *queue = NULL;
+       unsigned int num_queues = 1;
 
-       /* Create shared ring, alloc event channel. */
-       err = setup_netfront(dev, info);
-       if (err)
+       info->netdev->irq = 0;
+
+       /* Check if backend supports multiple queues */
+       err = xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+                          "multi-queue-max-queues", "%u", &max_queues);
+       if (err < 0)
+               max_queues = 1;
+       num_queues = min(max_queues, xennet_max_queues);
+
+       /* Check feature-split-event-channels */
+       err = xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+                          "feature-split-event-channels", "%u",
+                          &feature_split_evtchn);
+       if (err < 0)
+               feature_split_evtchn = 0;
+
+       /* Read mac addr. */
+       err = xen_net_read_mac(dev, info->netdev->dev_addr);
+       if (err) {
+               xenbus_dev_fatal(dev, err, "parsing %s/mac", dev->nodename);
+               goto out;
+       }
+
+       /* Allocate array of queues */
+       info->queues = kcalloc(num_queues, sizeof(struct netfront_queue), GFP_KERNEL);
+       if (!info->queues) {
+               err = -ENOMEM;
                goto out;
+       }
+       rtnl_lock();
+       netif_set_real_num_tx_queues(info->netdev, num_queues);
+       rtnl_unlock();
+
+       /* Create shared ring, alloc event channel -- for each queue */
+       for (i = 0; i < num_queues; ++i) {
+               queue = &info->queues[i];
+               queue->id = i;
+               queue->info = info;
+               err = xennet_init_queue(queue);
+               if (err) {
+                       /* xennet_init_queue() cleans up after itself on failure,
+                        * but we still have to clean up any previously initialised
+                        * queues. If i > 0, set num_queues to i, then goto
+                        * destroy_ring, which calls xennet_disconnect_backend()
+                        * to tidy up.
+                        */
+                       if (i > 0) {
+                               rtnl_lock();
+                               netif_set_real_num_tx_queues(info->netdev, i);
+                               rtnl_unlock();
+                               goto destroy_ring;
+                       } else {
+                               goto out;
+                       }
+               }
+               err = setup_netfront(dev, queue, feature_split_evtchn);
+               if (err) {
+                       /* As for xennet_init_queue(), setup_netfront() will tidy
+                        * up the current queue on error, but we need to clean up
+                        * those already allocated.
+                        */
+                       if (i > 0) {
+                               rtnl_lock();
+                               netif_set_real_num_tx_queues(info->netdev, i);
+                               rtnl_unlock();
+                               goto destroy_ring;
+                       } else {
+                               goto out;
+                       }
+               }
+       }
 
 again:
        err = xenbus_transaction_start(&xbt);
@@ -1640,41 +1882,29 @@ static int talk_to_netback(struct xenbus_device *dev,
                goto destroy_ring;
        }
 
-       err = xenbus_printf(xbt, dev->nodename, "tx-ring-ref", "%u",
-                           info->tx_ring_ref);
-       if (err) {
-               message = "writing tx ring-ref";
-               goto abort_transaction;
-       }
-       err = xenbus_printf(xbt, dev->nodename, "rx-ring-ref", "%u",
-                           info->rx_ring_ref);
-       if (err) {
-               message = "writing rx ring-ref";
-               goto abort_transaction;
-       }
-
-       if (info->tx_evtchn == info->rx_evtchn) {
-               err = xenbus_printf(xbt, dev->nodename,
-                                   "event-channel", "%u", info->tx_evtchn);
-               if (err) {
-                       message = "writing event-channel";
-                       goto abort_transaction;
-               }
+       if (num_queues == 1) {
+               err = write_queue_xenstore_keys(&info->queues[0], &xbt, 0); /* flat */
+               if (err)
+                       goto abort_transaction_no_dev_fatal;
        } else {
-               err = xenbus_printf(xbt, dev->nodename,
-                                   "event-channel-tx", "%u", info->tx_evtchn);
+               /* Write the number of queues */
+               err = xenbus_printf(xbt, dev->nodename, "multi-queue-num-queues",
+                                   "%u", num_queues);
                if (err) {
-                       message = "writing event-channel-tx";
-                       goto abort_transaction;
+                       message = "writing multi-queue-num-queues";
+                       goto abort_transaction_no_dev_fatal;
                }
-               err = xenbus_printf(xbt, dev->nodename,
-                                   "event-channel-rx", "%u", info->rx_evtchn);
-               if (err) {
-                       message = "writing event-channel-rx";
-                       goto abort_transaction;
+
+               /* Write the keys for each queue */
+               for (i = 0; i < num_queues; ++i) {
+                       queue = &info->queues[i];
+                       err = write_queue_xenstore_keys(queue, &xbt, 1); /* hierarchical */
+                       if (err)
+                               goto abort_transaction_no_dev_fatal;
                }
        }
 
+       /* The remaining keys are not queue-specific */
        err = xenbus_printf(xbt, dev->nodename, "request-rx-copy", "%u",
                            1);
        if (err) {
@@ -1724,10 +1954,16 @@ static int talk_to_netback(struct xenbus_device *dev,
        return 0;
 
  abort_transaction:
-       xenbus_transaction_end(xbt, 1);
        xenbus_dev_fatal(dev, err, "%s", message);
+abort_transaction_no_dev_fatal:
+       xenbus_transaction_end(xbt, 1);
  destroy_ring:
        xennet_disconnect_backend(info);
+       kfree(info->queues);
+       info->queues = NULL;
+       rtnl_lock();
+       netif_set_real_num_tx_queues(info->netdev, 0);
+       rtnl_lock();
  out:
        return err;
 }
@@ -1735,11 +1971,14 @@ static int talk_to_netback(struct xenbus_device *dev,
 static int xennet_connect(struct net_device *dev)
 {
        struct netfront_info *np = netdev_priv(dev);
+       unsigned int num_queues = 0;
        int i, requeue_idx, err;
        struct sk_buff *skb;
        grant_ref_t ref;
        struct xen_netif_rx_request *req;
        unsigned int feature_rx_copy;
+       unsigned int j = 0;
+       struct netfront_queue *queue = NULL;
 
        err = xenbus_scanf(XBT_NIL, np->xbdev->otherend,
                           "feature-rx-copy", "%u", &feature_rx_copy);
@@ -1756,40 +1995,47 @@ static int xennet_connect(struct net_device *dev)
        if (err)
                return err;
 
+       /* talk_to_netback() sets the correct number of queues */
+       num_queues = dev->real_num_tx_queues;
+
        rtnl_lock();
        netdev_update_features(dev);
        rtnl_unlock();
 
-       spin_lock_bh(&np->rx_lock);
-       spin_lock_irq(&np->tx_lock);
+       /* By now, the queue structures have been set up */
+       for (j = 0; j < num_queues; ++j) {
+               queue = &np->queues[j];
+               spin_lock_bh(&queue->rx_lock);
+               spin_lock_irq(&queue->tx_lock);
 
-       /* Step 1: Discard all pending TX packet fragments. */
-       xennet_release_tx_bufs(np);
+               /* Step 1: Discard all pending TX packet fragments. */
+               xennet_release_tx_bufs(queue);
 
-       /* Step 2: Rebuild the RX buffer freelist and the RX ring itself. */
-       for (requeue_idx = 0, i = 0; i < NET_RX_RING_SIZE; i++) {
-               skb_frag_t *frag;
-               const struct page *page;
-               if (!np->rx_skbs[i])
-                       continue;
+               /* Step 2: Rebuild the RX buffer freelist and the RX ring itself. */
+               for (requeue_idx = 0, i = 0; i < NET_RX_RING_SIZE; i++) {
+                       skb_frag_t *frag;
+                       const struct page *page;
+                       if (!queue->rx_skbs[i])
+                               continue;
 
-               skb = np->rx_skbs[requeue_idx] = xennet_get_rx_skb(np, i);
-               ref = np->grant_rx_ref[requeue_idx] = xennet_get_rx_ref(np, i);
-               req = RING_GET_REQUEST(&np->rx, requeue_idx);
+                       skb = queue->rx_skbs[requeue_idx] = xennet_get_rx_skb(queue, i);
+                       ref = queue->grant_rx_ref[requeue_idx] = xennet_get_rx_ref(queue, i);
+                       req = RING_GET_REQUEST(&queue->rx, requeue_idx);
 
-               frag = &skb_shinfo(skb)->frags[0];
-               page = skb_frag_page(frag);
-               gnttab_grant_foreign_access_ref(
-                       ref, np->xbdev->otherend_id,
-                       pfn_to_mfn(page_to_pfn(page)),
-                       0);
-               req->gref = ref;
-               req->id   = requeue_idx;
+                       frag = &skb_shinfo(skb)->frags[0];
+                       page = skb_frag_page(frag);
+                       gnttab_grant_foreign_access_ref(
+                               ref, queue->info->xbdev->otherend_id,
+                               pfn_to_mfn(page_to_pfn(page)),
+                               0);
+                       req->gref = ref;
+                       req->id   = requeue_idx;
 
-               requeue_idx++;
-       }
+                       requeue_idx++;
+               }
 
-       np->rx.req_prod_pvt = requeue_idx;
+               queue->rx.req_prod_pvt = requeue_idx;
+       }
 
        /*
         * Step 3: All public and private state should now be sane.  Get
@@ -1798,14 +2044,17 @@ static int xennet_connect(struct net_device *dev)
         * packets.
         */
        netif_carrier_on(np->netdev);
-       notify_remote_via_irq(np->tx_irq);
-       if (np->tx_irq != np->rx_irq)
-               notify_remote_via_irq(np->rx_irq);
-       xennet_tx_buf_gc(dev);
-       xennet_alloc_rx_buffers(dev);
-
-       spin_unlock_irq(&np->tx_lock);
-       spin_unlock_bh(&np->rx_lock);
+       for (j = 0; j < num_queues; ++j) {
+               queue = &np->queues[j];
+               notify_remote_via_irq(queue->tx_irq);
+               if (queue->tx_irq != queue->rx_irq)
+                       notify_remote_via_irq(queue->rx_irq);
+               xennet_tx_buf_gc(queue);
+               xennet_alloc_rx_buffers(queue);
+
+               spin_unlock_irq(&queue->tx_lock);
+               spin_unlock_bh(&queue->rx_lock);
+       }
 
        return 0;
 }
@@ -1878,7 +2127,7 @@ static void xennet_get_ethtool_stats(struct net_device *dev,
        int i;
 
        for (i = 0; i < ARRAY_SIZE(xennet_stats); i++)
-               data[i] = *(unsigned long *)(np + xennet_stats[i].offset);
+               data[i] = atomic_read((atomic_t *)(np + xennet_stats[i].offset));
 }
 
 static void xennet_get_strings(struct net_device *dev, u32 stringset, u8 * data)
@@ -1909,8 +2158,12 @@ static ssize_t show_rxbuf_min(struct device *dev,
 {
        struct net_device *netdev = to_net_dev(dev);
        struct netfront_info *info = netdev_priv(netdev);
+       unsigned int num_queues = netdev->real_num_tx_queues;
 
-       return sprintf(buf, "%u\n", info->rx_min_target);
+       if (num_queues)
+               return sprintf(buf, "%u\n", info->queues[0].rx_min_target);
+       else
+               return sprintf(buf, "%u\n", RX_MIN_TARGET);
 }
 
 static ssize_t store_rxbuf_min(struct device *dev,
@@ -1919,8 +2172,11 @@ static ssize_t store_rxbuf_min(struct device *dev,
 {
        struct net_device *netdev = to_net_dev(dev);
        struct netfront_info *np = netdev_priv(netdev);
+       unsigned int num_queues = netdev->real_num_tx_queues;
        char *endp;
        unsigned long target;
+       unsigned int i;
+       struct netfront_queue *queue;
 
        if (!capable(CAP_NET_ADMIN))
                return -EPERM;
@@ -1934,16 +2190,19 @@ static ssize_t store_rxbuf_min(struct device *dev,
        if (target > RX_MAX_TARGET)
                target = RX_MAX_TARGET;
 
-       spin_lock_bh(&np->rx_lock);
-       if (target > np->rx_max_target)
-               np->rx_max_target = target;
-       np->rx_min_target = target;
-       if (target > np->rx_target)
-               np->rx_target = target;
+       for (i = 0; i < num_queues; ++i) {
+               queue = &np->queues[i];
+               spin_lock_bh(&queue->rx_lock);
+               if (target > queue->rx_max_target)
+                       queue->rx_max_target = target;
+               queue->rx_min_target = target;
+               if (target > queue->rx_target)
+                       queue->rx_target = target;
 
-       xennet_alloc_rx_buffers(netdev);
+               xennet_alloc_rx_buffers(queue);
 
-       spin_unlock_bh(&np->rx_lock);
+               spin_unlock_bh(&queue->rx_lock);
+       }
        return len;
 }
 
@@ -1952,8 +2211,12 @@ static ssize_t show_rxbuf_max(struct device *dev,
 {
        struct net_device *netdev = to_net_dev(dev);
        struct netfront_info *info = netdev_priv(netdev);
+       unsigned int num_queues = netdev->real_num_tx_queues;
 
-       return sprintf(buf, "%u\n", info->rx_max_target);
+       if (num_queues)
+               return sprintf(buf, "%u\n", info->queues[0].rx_max_target);
+       else
+               return sprintf(buf, "%u\n", RX_MAX_TARGET);
 }
 
 static ssize_t store_rxbuf_max(struct device *dev,
@@ -1962,8 +2225,11 @@ static ssize_t store_rxbuf_max(struct device *dev,
 {
        struct net_device *netdev = to_net_dev(dev);
        struct netfront_info *np = netdev_priv(netdev);
+       unsigned int num_queues = netdev->real_num_tx_queues;
        char *endp;
        unsigned long target;
+       unsigned int i = 0;
+       struct netfront_queue *queue = NULL;
 
        if (!capable(CAP_NET_ADMIN))
                return -EPERM;
@@ -1977,16 +2243,19 @@ static ssize_t store_rxbuf_max(struct device *dev,
        if (target > RX_MAX_TARGET)
                target = RX_MAX_TARGET;
 
-       spin_lock_bh(&np->rx_lock);
-       if (target < np->rx_min_target)
-               np->rx_min_target = target;
-       np->rx_max_target = target;
-       if (target < np->rx_target)
-               np->rx_target = target;
+       for (i = 0; i < num_queues; ++i) {
+               queue = &np->queues[i];
+               spin_lock_bh(&queue->rx_lock);
+               if (target < queue->rx_min_target)
+                       queue->rx_min_target = target;
+               queue->rx_max_target = target;
+               if (target < queue->rx_target)
+                       queue->rx_target = target;
 
-       xennet_alloc_rx_buffers(netdev);
+               xennet_alloc_rx_buffers(queue);
 
-       spin_unlock_bh(&np->rx_lock);
+               spin_unlock_bh(&queue->rx_lock);
+       }
        return len;
 }
 
@@ -1995,8 +2264,12 @@ static ssize_t show_rxbuf_cur(struct device *dev,
 {
        struct net_device *netdev = to_net_dev(dev);
        struct netfront_info *info = netdev_priv(netdev);
+       unsigned int num_queues = netdev->real_num_tx_queues;
 
-       return sprintf(buf, "%u\n", info->rx_target);
+       if (num_queues)
+               return sprintf(buf, "%u\n", info->queues[0].rx_target);
+       else
+               return sprintf(buf, "0\n");
 }
 
 static struct device_attribute xennet_attrs[] = {
@@ -2043,6 +2316,9 @@ static const struct xenbus_device_id netfront_ids[] = {
 static int xennet_remove(struct xenbus_device *dev)
 {
        struct netfront_info *info = dev_get_drvdata(&dev->dev);
+       unsigned int num_queues = info->netdev->real_num_tx_queues;
+       struct netfront_queue *queue = NULL;
+       unsigned int i = 0;
 
        dev_dbg(&dev->dev, "%s\n", dev->nodename);
 
@@ -2052,7 +2328,15 @@ static int xennet_remove(struct xenbus_device *dev)
 
        unregister_netdev(info->netdev);
 
-       del_timer_sync(&info->rx_refill_timer);
+       for (i = 0; i < num_queues; ++i) {
+               queue = &info->queues[i];
+               del_timer_sync(&queue->rx_refill_timer);
+       }
+
+       if (num_queues) {
+               kfree(info->queues);
+               info->queues = NULL;
+       }
 
        free_percpu(info->stats);
 
@@ -2078,6 +2362,9 @@ static int __init netif_init(void)
 
        pr_info("Initialising Xen virtual ethernet driver\n");
 
+       /* Allow as many queues as there are CPUs, by default */
+       xennet_max_queues = num_online_cpus();
+
        return xenbus_register_frontend(&netfront_driver);
 }
 module_init(netif_init);
index 65d4ca19d1328ec737c68684c30de01fc8347b8d..26c66a1265518c32f5262d3aa98afb668e2715ff 100644 (file)
@@ -71,5 +71,6 @@ config NFC_PORT100
 source "drivers/nfc/pn544/Kconfig"
 source "drivers/nfc/microread/Kconfig"
 source "drivers/nfc/nfcmrvl/Kconfig"
+source "drivers/nfc/st21nfca/Kconfig"
 
 endmenu
index ae42a3fa60c981b965bbb0cdb004b7c3c591d01d..23225b0287fdf2e7b6f809466e58b5cc5fc53d2c 100644 (file)
@@ -11,5 +11,6 @@ obj-$(CONFIG_NFC_SIM)         += nfcsim.o
 obj-$(CONFIG_NFC_PORT100)      += port100.o
 obj-$(CONFIG_NFC_MRVL)         += nfcmrvl/
 obj-$(CONFIG_NFC_TRF7970A)     += trf7970a.o
+obj-$(CONFIG_NFC_ST21NFCA)  += st21nfca/
 
 ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
index f2acd85be86ea1d2016562c38aa4f9b6767ae754..440291ab7263202fc8017c3b9f09f8318f5f872a 100644 (file)
@@ -22,6 +22,8 @@
 #include <linux/module.h>
 #include <linux/i2c.h>
 #include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
 #include <linux/miscdevice.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
@@ -857,6 +859,92 @@ static void pn544_hci_i2c_fw_work(struct work_struct *work)
        }
 }
 
+#ifdef CONFIG_OF
+
+static int pn544_hci_i2c_of_request_resources(struct i2c_client *client)
+{
+       struct pn544_i2c_phy *phy = i2c_get_clientdata(client);
+       struct device_node *pp;
+       int ret;
+
+       pp = client->dev.of_node;
+       if (!pp) {
+               ret = -ENODEV;
+               goto err_dt;
+       }
+
+       /* Obtention of EN GPIO from device tree */
+       ret = of_get_named_gpio(pp, "enable-gpios", 0);
+       if (ret < 0) {
+               if (ret != -EPROBE_DEFER)
+                       nfc_err(&client->dev,
+                               "Failed to get EN gpio, error: %d\n", ret);
+               goto err_dt;
+       }
+       phy->gpio_en = ret;
+
+       /* Configuration of EN GPIO */
+       ret = gpio_request(phy->gpio_en, "pn544_en");
+       if (ret) {
+               nfc_err(&client->dev, "Fail EN pin\n");
+               goto err_dt;
+       }
+       ret = gpio_direction_output(phy->gpio_en, 0);
+       if (ret) {
+               nfc_err(&client->dev, "Fail EN pin direction\n");
+               goto err_gpio_en;
+       }
+
+       /* Obtention of FW GPIO from device tree */
+       ret = of_get_named_gpio(pp, "firmware-gpios", 0);
+       if (ret < 0) {
+               if (ret != -EPROBE_DEFER)
+                       nfc_err(&client->dev,
+                               "Failed to get FW gpio, error: %d\n", ret);
+               goto err_gpio_en;
+       }
+       phy->gpio_fw = ret;
+
+       /* Configuration of FW GPIO */
+       ret = gpio_request(phy->gpio_fw, "pn544_fw");
+       if (ret) {
+               nfc_err(&client->dev, "Fail FW pin\n");
+               goto err_gpio_en;
+       }
+       ret = gpio_direction_output(phy->gpio_fw, 0);
+       if (ret) {
+               nfc_err(&client->dev, "Fail FW pin direction\n");
+               goto err_gpio_fw;
+       }
+
+       /* IRQ */
+       ret = irq_of_parse_and_map(pp, 0);
+       if (ret < 0) {
+               nfc_err(&client->dev,
+                       "Unable to get irq, error: %d\n", ret);
+               goto err_gpio_fw;
+       }
+       client->irq = ret;
+
+       return 0;
+
+err_gpio_fw:
+       gpio_free(phy->gpio_fw);
+err_gpio_en:
+       gpio_free(phy->gpio_en);
+err_dt:
+       return ret;
+}
+
+#else
+
+static int pn544_hci_i2c_of_request_resources(struct i2c_client *client)
+{
+       return -ENODEV;
+}
+
+#endif
+
 static int pn544_hci_i2c_probe(struct i2c_client *client,
                               const struct i2c_device_id *id)
 {
@@ -887,25 +975,36 @@ static int pn544_hci_i2c_probe(struct i2c_client *client,
        i2c_set_clientdata(client, phy);
 
        pdata = client->dev.platform_data;
-       if (pdata == NULL) {
-               nfc_err(&client->dev, "No platform data\n");
-               return -EINVAL;
-       }
 
-       if (pdata->request_resources == NULL) {
-               nfc_err(&client->dev, "request_resources() missing\n");
-               return -EINVAL;
-       }
+       /* No platform data, using device tree. */
+       if (!pdata && client->dev.of_node) {
+               r = pn544_hci_i2c_of_request_resources(client);
+               if (r) {
+                       nfc_err(&client->dev, "No DT data\n");
+                       return r;
+               }
+       /* Using platform data. */
+       } else if (pdata) {
 
-       r = pdata->request_resources(client);
-       if (r) {
-               nfc_err(&client->dev, "Cannot get platform resources\n");
-               return r;
-       }
+               if (pdata->request_resources == NULL) {
+                       nfc_err(&client->dev, "request_resources() missing\n");
+                       return -EINVAL;
+               }
 
-       phy->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE);
-       phy->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET);
-       phy->gpio_irq = pdata->get_gpio(NFC_GPIO_IRQ);
+               r = pdata->request_resources(client);
+               if (r) {
+                       nfc_err(&client->dev,
+                               "Cannot get platform resources\n");
+                       return r;
+               }
+
+               phy->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE);
+               phy->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET);
+               phy->gpio_irq = pdata->get_gpio(NFC_GPIO_IRQ);
+       } else {
+               nfc_err(&client->dev, "No platform data\n");
+               return -EINVAL;
+       }
 
        pn544_hci_i2c_platform_init(phy);
 
@@ -930,8 +1029,12 @@ static int pn544_hci_i2c_probe(struct i2c_client *client,
        free_irq(client->irq, phy);
 
 err_rti:
-       if (pdata->free_resources != NULL)
+       if (!pdata) {
+               gpio_free(phy->gpio_en);
+               gpio_free(phy->gpio_fw);
+       } else if (pdata->free_resources) {
                pdata->free_resources();
+       }
 
        return r;
 }
@@ -953,15 +1056,30 @@ static int pn544_hci_i2c_remove(struct i2c_client *client)
                pn544_hci_i2c_disable(phy);
 
        free_irq(client->irq, phy);
-       if (pdata->free_resources)
+
+       /* No platform data, GPIOs have been requested by this driver */
+       if (!pdata) {
+               gpio_free(phy->gpio_en);
+               gpio_free(phy->gpio_fw);
+       /* Using platform data */
+       } else if (pdata->free_resources) {
                pdata->free_resources();
+       }
 
        return 0;
 }
 
+static const struct of_device_id of_pn544_i2c_match[] = {
+       { .compatible = "nxp,pn544-i2c", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, of_pn544_i2c_match);
+
 static struct i2c_driver pn544_hci_i2c_driver = {
        .driver = {
                   .name = PN544_HCI_I2C_DRIVER_NAME,
+                  .owner  = THIS_MODULE,
+                  .of_match_table = of_match_ptr(of_pn544_i2c_match),
                  },
        .probe = pn544_hci_i2c_probe,
        .id_table = pn544_hci_i2c_id_table,
index b7a372af5eb75c7a99a108e76faedd8c7bb7c1c4..4ac4d31f6c598309a9c9e35f871cd0316419ba5f 100644 (file)
@@ -28,7 +28,8 @@
                           NFC_PROTO_MIFARE_MASK   | \
                           NFC_PROTO_FELICA_MASK   | \
                           NFC_PROTO_NFC_DEP_MASK  | \
-                          NFC_PROTO_ISO14443_MASK)
+                          NFC_PROTO_ISO14443_MASK | \
+                          NFC_PROTO_ISO14443_B_MASK)
 
 #define PORT100_CAPABILITIES (NFC_DIGITAL_DRV_CAPS_IN_CRC | \
                              NFC_DIGITAL_DRV_CAPS_TG_CRC)
@@ -120,6 +121,7 @@ struct port100_in_rf_setting {
 #define PORT100_COMM_TYPE_IN_212F 0x01
 #define PORT100_COMM_TYPE_IN_424F 0x02
 #define PORT100_COMM_TYPE_IN_106A 0x03
+#define PORT100_COMM_TYPE_IN_106B 0x07
 
 static const struct port100_in_rf_setting in_rf_settings[] = {
        [NFC_DIGITAL_RF_TECH_212F] = {
@@ -140,6 +142,12 @@ static const struct port100_in_rf_setting in_rf_settings[] = {
                .in_recv_set_number = 15,
                .in_recv_comm_type  = PORT100_COMM_TYPE_IN_106A,
        },
+       [NFC_DIGITAL_RF_TECH_106B] = {
+               .in_send_set_number = 3,
+               .in_send_comm_type  = PORT100_COMM_TYPE_IN_106B,
+               .in_recv_set_number = 15,
+               .in_recv_comm_type  = PORT100_COMM_TYPE_IN_106B,
+       },
        /* Ensures the array has NFC_DIGITAL_RF_TECH_LAST elements */
        [NFC_DIGITAL_RF_TECH_LAST] = { 0 },
 };
@@ -340,6 +348,32 @@ in_protocols[][PORT100_IN_MAX_NUM_PROTOCOLS + 1] = {
        [NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED] = {
                { PORT100_IN_PROT_END, 0 },
        },
+       [NFC_DIGITAL_FRAMING_NFCB] = {
+               { PORT100_IN_PROT_INITIAL_GUARD_TIME,     20 },
+               { 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,                 1 },
+               { PORT100_IN_PROT_CHECK_SOF,               1 },
+               { PORT100_IN_PROT_ADD_EOF,                 1 },
+               { PORT100_IN_PROT_CHECK_EOF,               1 },
+               { 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_NFCB_T4T] = {
+               /* nfc_digital_framing_nfcb */
+               { PORT100_IN_PROT_END,                     0 },
+       },
        /* Ensures the array has NFC_DIGITAL_FRAMING_LAST elements */
        [NFC_DIGITAL_FRAMING_LAST] = {
                { PORT100_IN_PROT_END, 0 },
diff --git a/drivers/nfc/st21nfca/Kconfig b/drivers/nfc/st21nfca/Kconfig
new file mode 100644 (file)
index 0000000..ee459f0
--- /dev/null
@@ -0,0 +1,23 @@
+config NFC_ST21NFCA
+       tristate "STMicroelectronics ST21NFCA NFC driver"
+       depends on NFC_HCI
+       select CRC_CCITT
+       default n
+       ---help---
+         STMicroelectronics ST21NFCA core driver. It implements the chipset
+         HCI logic and hooks into the NFC kernel APIs. Physical layers will
+         register against it.
+
+         To compile this driver as a module, choose m here. The module will
+         be called st21nfca.
+         Say N if unsure.
+
+config NFC_ST21NFCA_I2C
+       tristate "NFC ST21NFCA i2c support"
+       depends on NFC_ST21NFCA && I2C && NFC_SHDLC
+       ---help---
+         This module adds support for the STMicroelectronics st21nfca i2c interface.
+         Select this if your platform is using the i2c bus.
+
+         If you choose to build a module, it'll be called st21nfca_i2c.
+         Say N if unsure.
diff --git a/drivers/nfc/st21nfca/Makefile b/drivers/nfc/st21nfca/Makefile
new file mode 100644 (file)
index 0000000..038ed09
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# Makefile for ST21NFCA HCI based NFC driver
+#
+
+st21nfca_i2c-objs  = i2c.o
+
+obj-$(CONFIG_NFC_ST21NFCA)     += st21nfca.o
+obj-$(CONFIG_NFC_ST21NFCA_I2C) += st21nfca_i2c.o
diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c
new file mode 100644 (file)
index 0000000..3f954ed
--- /dev/null
@@ -0,0 +1,724 @@
+/*
+ * I2C Link Layer for ST21NFCA HCI based Driver
+ * Copyright (C) 2014  STMicroelectronics SAS. All rights reserved.
+ *
+ * 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 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/crc-ccitt.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/miscdevice.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/nfc.h>
+#include <linux/firmware.h>
+#include <linux/unaligned/access_ok.h>
+#include <linux/platform_data/st21nfca.h>
+
+#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
+#include <net/nfc/nfc.h>
+
+#include "st21nfca.h"
+
+/*
+ * Every frame starts with ST21NFCA_SOF_EOF and ends with ST21NFCA_SOF_EOF.
+ * Because ST21NFCA_SOF_EOF is a possible data value, there is a mecanism
+ * called byte stuffing has been introduced.
+ *
+ * if byte == ST21NFCA_SOF_EOF or ST21NFCA_ESCAPE_BYTE_STUFFING
+ * - insert ST21NFCA_ESCAPE_BYTE_STUFFING (escape byte)
+ * - xor byte with ST21NFCA_BYTE_STUFFING_MASK
+ */
+#define ST21NFCA_SOF_EOF               0x7e
+#define ST21NFCA_BYTE_STUFFING_MASK    0x20
+#define ST21NFCA_ESCAPE_BYTE_STUFFING  0x7d
+
+/* SOF + 00 */
+#define ST21NFCA_FRAME_HEADROOM                        2
+
+/* 2 bytes crc + EOF */
+#define ST21NFCA_FRAME_TAILROOM 3
+#define IS_START_OF_FRAME(buf) (buf[0] == ST21NFCA_SOF_EOF && \
+                               buf[1] == 0)
+
+#define ST21NFCA_HCI_I2C_DRIVER_NAME "st21nfca_hci_i2c"
+
+static struct i2c_device_id st21nfca_hci_i2c_id_table[] = {
+       {ST21NFCA_HCI_DRIVER_NAME, 0},
+       {}
+};
+
+MODULE_DEVICE_TABLE(i2c, st21nfca_hci_i2c_id_table);
+
+struct st21nfca_i2c_phy {
+       struct i2c_client *i2c_dev;
+       struct nfc_hci_dev *hdev;
+
+       unsigned int gpio_ena;
+       unsigned int gpio_irq;
+       unsigned int irq_polarity;
+
+       struct sk_buff *pending_skb;
+       int current_read_len;
+       /*
+        * crc might have fail because i2c macro
+        * is disable due to other interface activity
+        */
+       int crc_trials;
+
+       int powered;
+       int run_mode;
+
+       /*
+        * < 0 if hardware error occured (e.g. i2c err)
+        * and prevents normal operation.
+        */
+       int hard_fault;
+       struct mutex phy_lock;
+};
+static u8 len_seq[] = { 13, 24, 15, 29 };
+static u16 wait_tab[] = { 2, 3, 5, 15, 20, 40};
+
+#define I2C_DUMP_SKB(info, skb)                                        \
+do {                                                           \
+       pr_debug("%s:\n", info);                                \
+       print_hex_dump(KERN_DEBUG, "i2c: ", DUMP_PREFIX_OFFSET, \
+                      16, 1, (skb)->data, (skb)->len, 0);      \
+} while (0)
+
+/*
+ * In order to get the CLF in a known state we generate an internal reboot
+ * using a proprietary command.
+ * Once the reboot is completed, we expect to receive a ST21NFCA_SOF_EOF
+ * fill buffer.
+ */
+static int st21nfca_hci_platform_init(struct st21nfca_i2c_phy *phy)
+{
+       u16 wait_reboot[] = { 50, 300, 1000 };
+       char reboot_cmd[] = { 0x7E, 0x66, 0x48, 0xF6, 0x7E };
+       u8 tmp[ST21NFCA_HCI_LLC_MAX_SIZE];
+       int i, r = -1;
+
+       for (i = 0; i < ARRAY_SIZE(wait_reboot) && r < 0; i++) {
+               r = i2c_master_send(phy->i2c_dev, reboot_cmd,
+                                   sizeof(reboot_cmd));
+               if (r < 0)
+                       msleep(wait_reboot[i]);
+       }
+       if (r < 0)
+               return r;
+
+       /* CLF is spending about 20ms to do an internal reboot */
+       msleep(20);
+       r = -1;
+       for (i = 0; i < ARRAY_SIZE(wait_reboot) && r < 0; i++) {
+               r = i2c_master_recv(phy->i2c_dev, tmp,
+                                   ST21NFCA_HCI_LLC_MAX_SIZE);
+               if (r < 0)
+                       msleep(wait_reboot[i]);
+       }
+       if (r < 0)
+               return r;
+
+       for (i = 0; i < ST21NFCA_HCI_LLC_MAX_SIZE &&
+               tmp[i] == ST21NFCA_SOF_EOF; i++)
+               ;
+
+       if (r != ST21NFCA_HCI_LLC_MAX_SIZE)
+               return -ENODEV;
+
+       usleep_range(1000, 1500);
+       return 0;
+}
+
+static int st21nfca_hci_i2c_enable(void *phy_id)
+{
+       struct st21nfca_i2c_phy *phy = phy_id;
+
+       gpio_set_value(phy->gpio_ena, 1);
+       phy->powered = 1;
+       phy->run_mode = ST21NFCA_HCI_MODE;
+
+       usleep_range(10000, 15000);
+
+       return 0;
+}
+
+static void st21nfca_hci_i2c_disable(void *phy_id)
+{
+       struct st21nfca_i2c_phy *phy = phy_id;
+
+       pr_info("\n");
+       gpio_set_value(phy->gpio_ena, 0);
+
+       phy->powered = 0;
+}
+
+static void st21nfca_hci_add_len_crc(struct sk_buff *skb)
+{
+       u16 crc;
+       u8 tmp;
+
+       *skb_push(skb, 1) = 0;
+
+       crc = crc_ccitt(0xffff, skb->data, skb->len);
+       crc = ~crc;
+
+       tmp = crc & 0x00ff;
+       *skb_put(skb, 1) = tmp;
+
+       tmp = (crc >> 8) & 0x00ff;
+       *skb_put(skb, 1) = tmp;
+}
+
+static void st21nfca_hci_remove_len_crc(struct sk_buff *skb)
+{
+       skb_pull(skb, ST21NFCA_FRAME_HEADROOM);
+       skb_trim(skb, skb->len - ST21NFCA_FRAME_TAILROOM);
+}
+
+/*
+ * Writing a frame must not return the number of written bytes.
+ * It must return either zero for success, or <0 for error.
+ * In addition, it must not alter the skb
+ */
+static int st21nfca_hci_i2c_write(void *phy_id, struct sk_buff *skb)
+{
+       int r = -1, i, j;
+       struct st21nfca_i2c_phy *phy = phy_id;
+       struct i2c_client *client = phy->i2c_dev;
+       u8 tmp[ST21NFCA_HCI_LLC_MAX_SIZE * 2];
+
+       I2C_DUMP_SKB("st21nfca_hci_i2c_write", skb);
+
+
+       if (phy->hard_fault != 0)
+               return phy->hard_fault;
+
+       /*
+        * Compute CRC before byte stuffing computation on frame
+        * Note st21nfca_hci_add_len_crc is doing a byte stuffing
+        * on its own value
+        */
+       st21nfca_hci_add_len_crc(skb);
+
+       /* add ST21NFCA_SOF_EOF on tail */
+       *skb_put(skb, 1) = ST21NFCA_SOF_EOF;
+       /* add ST21NFCA_SOF_EOF on head */
+       *skb_push(skb, 1) = ST21NFCA_SOF_EOF;
+
+       /*
+        * Compute byte stuffing
+        * if byte == ST21NFCA_SOF_EOF or ST21NFCA_ESCAPE_BYTE_STUFFING
+        * insert ST21NFCA_ESCAPE_BYTE_STUFFING (escape byte)
+        * xor byte with ST21NFCA_BYTE_STUFFING_MASK
+        */
+       tmp[0] = skb->data[0];
+       for (i = 1, j = 1; i < skb->len - 1; i++, j++) {
+               if (skb->data[i] == ST21NFCA_SOF_EOF
+                   || skb->data[i] == ST21NFCA_ESCAPE_BYTE_STUFFING) {
+                       tmp[j] = ST21NFCA_ESCAPE_BYTE_STUFFING;
+                       j++;
+                       tmp[j] = skb->data[i] ^ ST21NFCA_BYTE_STUFFING_MASK;
+               } else {
+                       tmp[j] = skb->data[i];
+               }
+       }
+       tmp[j] = skb->data[i];
+       j++;
+
+       /*
+        * Manage sleep mode
+        * Try 3 times to send data with delay between each
+        */
+       mutex_lock(&phy->phy_lock);
+       for (i = 0; i < ARRAY_SIZE(wait_tab) && r < 0; i++) {
+               r = i2c_master_send(client, tmp, j);
+               if (r < 0)
+                       msleep(wait_tab[i]);
+       }
+       mutex_unlock(&phy->phy_lock);
+
+       if (r >= 0) {
+               if (r != j)
+                       r = -EREMOTEIO;
+               else
+                       r = 0;
+       }
+
+       st21nfca_hci_remove_len_crc(skb);
+
+       return r;
+}
+
+static int get_frame_size(u8 *buf, int buflen)
+{
+       int len = 0;
+       if (buf[len + 1] == ST21NFCA_SOF_EOF)
+               return 0;
+
+       for (len = 1; len < buflen && buf[len] != ST21NFCA_SOF_EOF; len++)
+               ;
+
+       return len;
+}
+
+static int check_crc(u8 *buf, int buflen)
+{
+       u16 crc;
+
+       crc = crc_ccitt(0xffff, buf, buflen - 2);
+       crc = ~crc;
+
+       if (buf[buflen - 2] != (crc & 0xff) || buf[buflen - 1] != (crc >> 8)) {
+               pr_err(ST21NFCA_HCI_DRIVER_NAME
+                      ": CRC error 0x%x != 0x%x 0x%x\n", crc, buf[buflen - 1],
+                      buf[buflen - 2]);
+
+               pr_info(DRIVER_DESC ": %s : BAD CRC\n", __func__);
+               print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE,
+                              16, 2, buf, buflen, false);
+               return -EPERM;
+       }
+       return 0;
+}
+
+/*
+ * Prepare received data for upper layer.
+ * Received data include byte stuffing, crc and sof/eof
+ * which is not usable by hci part.
+ * returns:
+ * frame size without sof/eof, header and byte stuffing
+ * -EBADMSG : frame was incorrect and discarded
+ */
+static int st21nfca_hci_i2c_repack(struct sk_buff *skb)
+{
+       int i, j, r, size;
+       if (skb->len < 1 || (skb->len > 1 && skb->data[1] != 0))
+               return -EBADMSG;
+
+       size = get_frame_size(skb->data, skb->len);
+       if (size > 0) {
+               skb_trim(skb, size);
+               /* remove ST21NFCA byte stuffing for upper layer */
+               for (i = 1, j = 0; i < skb->len; i++) {
+                       if (skb->data[i + j] ==
+                                       (u8) ST21NFCA_ESCAPE_BYTE_STUFFING) {
+                               skb->data[i] = skb->data[i + j + 1]
+                                               | ST21NFCA_BYTE_STUFFING_MASK;
+                               i++;
+                               j++;
+                       }
+                       skb->data[i] = skb->data[i + j];
+               }
+               /* remove byte stuffing useless byte */
+               skb_trim(skb, i - j);
+               /* remove ST21NFCA_SOF_EOF from head */
+               skb_pull(skb, 1);
+
+               r = check_crc(skb->data, skb->len);
+               if (r != 0) {
+                       i = 0;
+                       return -EBADMSG;
+               }
+
+               /* remove headbyte */
+               skb_pull(skb, 1);
+               /* remove crc. Byte Stuffing is already removed here */
+               skb_trim(skb, skb->len - 2);
+               return skb->len;
+       }
+       return 0;
+}
+
+/*
+ * Reads an shdlc frame and returns it in a newly allocated sk_buff. Guarantees
+ * that i2c bus will be flushed and that next read will start on a new frame.
+ * returned skb contains only LLC header and payload.
+ * returns:
+ * frame size : if received frame is complete (find ST21NFCA_SOF_EOF at
+ * end of read)
+ * -EAGAIN : if received frame is incomplete (not find ST21NFCA_SOF_EOF
+ * at end of read)
+ * -EREMOTEIO : i2c read error (fatal)
+ * -EBADMSG : frame was incorrect and discarded
+ * (value returned from st21nfca_hci_i2c_repack)
+ * -EIO : if no ST21NFCA_SOF_EOF is found after reaching
+ * the read length end sequence
+ */
+static int st21nfca_hci_i2c_read(struct st21nfca_i2c_phy *phy,
+                                struct sk_buff *skb)
+{
+       int r, i;
+       u8 len;
+       u8 buf[ST21NFCA_HCI_LLC_MAX_PAYLOAD];
+       struct i2c_client *client = phy->i2c_dev;
+
+       if (phy->current_read_len < ARRAY_SIZE(len_seq)) {
+               len = len_seq[phy->current_read_len];
+
+               /*
+                * Add retry mecanism
+                * Operation on I2C interface may fail in case of operation on
+                * RF or SWP interface
+                */
+               r = 0;
+               mutex_lock(&phy->phy_lock);
+               for (i = 0; i < ARRAY_SIZE(wait_tab) && r <= 0; i++) {
+                       r = i2c_master_recv(client, buf, len);
+                       if (r < 0)
+                               msleep(wait_tab[i]);
+               }
+               mutex_unlock(&phy->phy_lock);
+
+               if (r != len) {
+                       phy->current_read_len = 0;
+                       return -EREMOTEIO;
+               }
+
+               /*
+                * The first read sequence does not start with SOF.
+                * Data is corrupeted so we drop it.
+                */
+               if (!phy->current_read_len && buf[0] != ST21NFCA_SOF_EOF) {
+                       skb_trim(skb, 0);
+                       phy->current_read_len = 0;
+                       return -EIO;
+               } else if (phy->current_read_len &&
+                       IS_START_OF_FRAME(buf)) {
+                       /*
+                        * Previous frame transmission was interrupted and
+                        * the frame got repeated.
+                        * Received frame start with ST21NFCA_SOF_EOF + 00.
+                        */
+                       skb_trim(skb, 0);
+                       phy->current_read_len = 0;
+               }
+
+               memcpy(skb_put(skb, len), buf, len);
+
+               if (skb->data[skb->len - 1] == ST21NFCA_SOF_EOF) {
+                       phy->current_read_len = 0;
+                       return st21nfca_hci_i2c_repack(skb);
+               }
+               phy->current_read_len++;
+               return -EAGAIN;
+       }
+       return -EIO;
+}
+
+/*
+ * Reads an shdlc frame from the chip. This is not as straightforward as it
+ * seems. The frame format is data-crc, and corruption can occur anywhere
+ * while transiting on i2c bus, such that we could read an invalid data.
+ * The tricky case is when we read a corrupted data or crc. We must detect
+ * this here in order to determine that data can be transmitted to the hci
+ * core. This is the reason why we check the crc here.
+ * The CLF will repeat a frame until we send a RR on that frame.
+ *
+ * On ST21NFCA, IRQ goes in idle when read starts. As no size information are
+ * available in the incoming data, other IRQ might come. Every IRQ will trigger
+ * a read sequence with different length and will fill the current frame.
+ * The reception is complete once we reach a ST21NFCA_SOF_EOF.
+ */
+static irqreturn_t st21nfca_hci_irq_thread_fn(int irq, void *phy_id)
+{
+       struct st21nfca_i2c_phy *phy = phy_id;
+       struct i2c_client *client;
+
+       int r;
+
+       if (!phy || irq != phy->i2c_dev->irq) {
+               WARN_ON_ONCE(1);
+               return IRQ_NONE;
+       }
+
+       client = phy->i2c_dev;
+       dev_dbg(&client->dev, "IRQ\n");
+
+       if (phy->hard_fault != 0)
+               return IRQ_HANDLED;
+
+       r = st21nfca_hci_i2c_read(phy, phy->pending_skb);
+       if (r == -EREMOTEIO) {
+               phy->hard_fault = r;
+
+               nfc_hci_recv_frame(phy->hdev, NULL);
+
+               return IRQ_HANDLED;
+       } else if (r == -EAGAIN || r == -EIO) {
+               return IRQ_HANDLED;
+       } else if (r == -EBADMSG && phy->crc_trials < ARRAY_SIZE(wait_tab)) {
+               /*
+                * With ST21NFCA, only one interface (I2C, RF or SWP)
+                * may be active at a time.
+                * Having incorrect crc is usually due to i2c macrocell
+                * deactivation in the middle of a transmission.
+                * It may generate corrupted data on i2c.
+                * We give sometime to get i2c back.
+                * The complete frame will be repeated.
+                */
+               msleep(wait_tab[phy->crc_trials]);
+               phy->crc_trials++;
+               phy->current_read_len = 0;
+               kfree_skb(phy->pending_skb);
+       } else if (r > 0) {
+               /*
+                * We succeeded to read data from the CLF and
+                * data is valid.
+                * Reset counter.
+                */
+               nfc_hci_recv_frame(phy->hdev, phy->pending_skb);
+               phy->crc_trials = 0;
+       }
+
+       phy->pending_skb = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE * 2, GFP_KERNEL);
+       if (phy->pending_skb == NULL) {
+               phy->hard_fault = -ENOMEM;
+               nfc_hci_recv_frame(phy->hdev, NULL);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static struct nfc_phy_ops i2c_phy_ops = {
+       .write = st21nfca_hci_i2c_write,
+       .enable = st21nfca_hci_i2c_enable,
+       .disable = st21nfca_hci_i2c_disable,
+};
+
+#ifdef CONFIG_OF
+static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client)
+{
+       struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client);
+       struct device_node *pp;
+       int gpio;
+       int r;
+
+       pp = client->dev.of_node;
+       if (!pp)
+               return -ENODEV;
+
+       /* Get GPIO from device tree */
+       gpio = of_get_named_gpio(pp, "enable-gpios", 0);
+       if (gpio < 0) {
+               nfc_err(&client->dev, "Failed to retrieve enable-gpios from device tree\n");
+               return gpio;
+       }
+
+       /* GPIO request and configuration */
+       r = devm_gpio_request(&client->dev, gpio, "clf_enable");
+       if (r) {
+               nfc_err(&client->dev, "Failed to request enable pin\n");
+               return -ENODEV;
+       }
+
+       r = gpio_direction_output(gpio, 1);
+       if (r) {
+               nfc_err(&client->dev, "Failed to set enable pin direction as output\n");
+               return -ENODEV;
+       }
+       phy->gpio_ena = gpio;
+
+       /* IRQ */
+       r = irq_of_parse_and_map(pp, 0);
+       if (r < 0) {
+               nfc_err(&client->dev,
+                               "Unable to get irq, error: %d\n", r);
+               return r;
+       }
+
+       phy->irq_polarity = irq_get_trigger_type(r);
+       client->irq = r;
+
+       return 0;
+}
+#else
+static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client)
+{
+       return -ENODEV;
+}
+#endif
+
+static int st21nfca_hci_i2c_request_resources(struct i2c_client *client)
+{
+       struct st21nfca_nfc_platform_data *pdata;
+       struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client);
+       int r;
+       int irq;
+
+       pdata = client->dev.platform_data;
+       if (pdata == NULL) {
+               nfc_err(&client->dev, "No platform data\n");
+               return -EINVAL;
+       }
+
+       /* store for later use */
+       phy->gpio_irq = pdata->gpio_irq;
+       phy->gpio_ena = pdata->gpio_ena;
+       phy->irq_polarity = pdata->irq_polarity;
+
+       r = devm_gpio_request(&client->dev, phy->gpio_irq, "wake_up");
+       if (r) {
+               pr_err("%s : gpio_request failed\n", __FILE__);
+               return -ENODEV;
+       }
+
+       r = gpio_direction_input(phy->gpio_irq);
+       if (r) {
+               pr_err("%s : gpio_direction_input failed\n", __FILE__);
+               return -ENODEV;
+       }
+
+       if (phy->gpio_ena > 0) {
+               r = devm_gpio_request(&client->dev,
+                                       phy->gpio_ena, "clf_enable");
+               if (r) {
+                       pr_err("%s : ena gpio_request failed\n", __FILE__);
+                       return -ENODEV;
+               }
+               r = gpio_direction_output(phy->gpio_ena, 1);
+
+               if (r) {
+                       pr_err("%s : ena gpio_direction_output failed\n",
+                              __FILE__);
+                       return -ENODEV;
+               }
+       }
+
+       /* IRQ */
+       irq = gpio_to_irq(phy->gpio_irq);
+       if (irq < 0) {
+               nfc_err(&client->dev,
+                               "Unable to get irq number for GPIO %d error %d\n",
+                               phy->gpio_irq, r);
+               return -ENODEV;
+       }
+       client->irq = irq;
+
+       return 0;
+}
+
+static int st21nfca_hci_i2c_probe(struct i2c_client *client,
+                                 const struct i2c_device_id *id)
+{
+       struct st21nfca_i2c_phy *phy;
+       struct st21nfca_nfc_platform_data *pdata;
+       int r;
+
+       dev_dbg(&client->dev, "%s\n", __func__);
+       dev_dbg(&client->dev, "IRQ: %d\n", client->irq);
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               nfc_err(&client->dev, "Need I2C_FUNC_I2C\n");
+               return -ENODEV;
+       }
+
+       phy = devm_kzalloc(&client->dev, sizeof(struct st21nfca_i2c_phy),
+                          GFP_KERNEL);
+       if (!phy) {
+               nfc_err(&client->dev,
+                       "Cannot allocate memory for st21nfca i2c phy.\n");
+               return -ENOMEM;
+       }
+
+       phy->i2c_dev = client;
+       phy->pending_skb = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE * 2, GFP_KERNEL);
+       if (phy->pending_skb == NULL)
+               return -ENOMEM;
+
+       phy->current_read_len = 0;
+       phy->crc_trials = 0;
+       mutex_init(&phy->phy_lock);
+       i2c_set_clientdata(client, phy);
+
+       pdata = client->dev.platform_data;
+       if (!pdata && client->dev.of_node) {
+               r = st21nfca_hci_i2c_of_request_resources(client);
+               if (r) {
+                       nfc_err(&client->dev, "No platform data\n");
+                       return r;
+               }
+       } else if (pdata) {
+               r = st21nfca_hci_i2c_request_resources(client);
+               if (r) {
+                       nfc_err(&client->dev, "Cannot get platform resources\n");
+                       return r;
+               }
+       } else {
+               nfc_err(&client->dev, "st21nfca platform resources not available\n");
+               return -ENODEV;
+       }
+
+       r = st21nfca_hci_platform_init(phy);
+       if (r < 0) {
+               nfc_err(&client->dev, "Unable to reboot st21nfca\n");
+               return -ENODEV;
+       }
+
+       r = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+                               st21nfca_hci_irq_thread_fn,
+                               phy->irq_polarity | IRQF_ONESHOT,
+                               ST21NFCA_HCI_DRIVER_NAME, phy);
+       if (r < 0) {
+               nfc_err(&client->dev, "Unable to register IRQ handler\n");
+               return r;
+       }
+
+       return st21nfca_hci_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME,
+                              ST21NFCA_FRAME_HEADROOM, ST21NFCA_FRAME_TAILROOM,
+                              ST21NFCA_HCI_LLC_MAX_PAYLOAD, &phy->hdev);
+}
+
+static int st21nfca_hci_i2c_remove(struct i2c_client *client)
+{
+       struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client);
+
+       dev_dbg(&client->dev, "%s\n", __func__);
+
+       st21nfca_hci_remove(phy->hdev);
+
+       if (phy->powered)
+               st21nfca_hci_i2c_disable(phy);
+
+       return 0;
+}
+
+static const struct of_device_id of_st21nfca_i2c_match[] = {
+       { .compatible = "st,st21nfca_i2c", },
+       {}
+};
+
+static struct i2c_driver st21nfca_hci_i2c_driver = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = ST21NFCA_HCI_I2C_DRIVER_NAME,
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(of_st21nfca_i2c_match),
+       },
+       .probe = st21nfca_hci_i2c_probe,
+       .id_table = st21nfca_hci_i2c_id_table,
+       .remove = st21nfca_hci_i2c_remove,
+};
+
+module_i2c_driver(st21nfca_hci_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/nfc/st21nfca/st21nfca.c b/drivers/nfc/st21nfca/st21nfca.c
new file mode 100644 (file)
index 0000000..51e0f00
--- /dev/null
@@ -0,0 +1,698 @@
+/*
+ * HCI based Driver for STMicroelectronics NFC Chip
+ *
+ * Copyright (C) 2014  STMicroelectronics SAS. All rights reserved.
+ *
+ * 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 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
+
+#include "st21nfca.h"
+
+#define DRIVER_DESC "HCI NFC driver for ST21NFCA"
+
+#define FULL_VERSION_LEN 3
+
+/* Proprietary gates, events, commands and registers */
+
+/* Commands that apply to all RF readers */
+#define ST21NFCA_RF_READER_CMD_PRESENCE_CHECK  0x30
+
+#define ST21NFCA_RF_READER_ISO15693_GATE       0x12
+#define ST21NFCA_RF_READER_ISO15693_INVENTORY 0x01
+
+/*
+ * Reader gate for communication with contact-less cards using Type A
+ * protocol ISO14443-3 but not compliant with ISO14443-4
+ */
+#define ST21NFCA_RF_READER_14443_3_A_GATE      0x15
+#define ST21NFCA_RF_READER_14443_3_A_UID       0x02
+#define ST21NFCA_RF_READER_14443_3_A_ATQA      0x03
+#define ST21NFCA_RF_READER_14443_3_A_SAK       0x04
+
+#define ST21NFCA_DEVICE_MGNT_GATE              0x01
+#define ST21NFCA_DEVICE_MGNT_PIPE              0x02
+
+#define ST21NFCA_DM_GETINFO         0x13
+#define ST21NFCA_DM_GETINFO_PIPE_LIST       0x02
+#define ST21NFCA_DM_GETINFO_PIPE_INFO       0x01
+#define ST21NFCA_DM_PIPE_CREATED        0x02
+#define ST21NFCA_DM_PIPE_OPEN           0x04
+#define ST21NFCA_DM_RF_ACTIVE           0x80
+
+#define ST21NFCA_DM_IS_PIPE_OPEN(p) \
+       ((p & 0x0f) == (ST21NFCA_DM_PIPE_CREATED | ST21NFCA_DM_PIPE_OPEN))
+
+#define ST21NFCA_NFC_MODE      0x03    /* NFC_MODE parameter*/
+
+static DECLARE_BITMAP(dev_mask, ST21NFCA_NUM_DEVICES);
+
+static struct nfc_hci_gate st21nfca_gates[] = {
+       {NFC_HCI_ADMIN_GATE, NFC_HCI_ADMIN_PIPE},
+       {NFC_HCI_LOOPBACK_GATE, NFC_HCI_INVALID_PIPE},
+       {NFC_HCI_ID_MGMT_GATE, NFC_HCI_INVALID_PIPE},
+       {NFC_HCI_LINK_MGMT_GATE, NFC_HCI_LINK_MGMT_PIPE},
+       {NFC_HCI_RF_READER_B_GATE, NFC_HCI_INVALID_PIPE},
+       {NFC_HCI_RF_READER_A_GATE, NFC_HCI_INVALID_PIPE},
+       {ST21NFCA_DEVICE_MGNT_GATE, ST21NFCA_DEVICE_MGNT_PIPE},
+       {ST21NFCA_RF_READER_F_GATE, NFC_HCI_INVALID_PIPE},
+       {ST21NFCA_RF_READER_14443_3_A_GATE, NFC_HCI_INVALID_PIPE},
+       {ST21NFCA_RF_READER_ISO15693_GATE, NFC_HCI_INVALID_PIPE},
+};
+
+struct st21nfca_pipe_info {
+       u8 pipe_state;
+       u8 src_host_id;
+       u8 src_gate_id;
+       u8 dst_host_id;
+       u8 dst_gate_id;
+} __packed;
+
+/* Largest headroom needed for outgoing custom commands */
+#define ST21NFCA_CMDS_HEADROOM  7
+
+static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev)
+{
+       int i, j, r;
+       struct sk_buff *skb_pipe_list, *skb_pipe_info;
+       struct st21nfca_pipe_info *info;
+
+       u8 pipe_list[] = { ST21NFCA_DM_GETINFO_PIPE_LIST,
+               NFC_HCI_TERMINAL_HOST_ID
+       };
+       u8 pipe_info[] = { ST21NFCA_DM_GETINFO_PIPE_INFO,
+               NFC_HCI_TERMINAL_HOST_ID, 0
+       };
+
+       skb_pipe_list = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE, GFP_KERNEL);
+       if (!skb_pipe_list) {
+               r = -ENOMEM;
+               goto free_list;
+       }
+
+       skb_pipe_info = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE, GFP_KERNEL);
+       if (!skb_pipe_info) {
+               r = -ENOMEM;
+               goto free_info;
+       }
+
+       /* On ST21NFCA device pipes number are dynamics
+        * A maximum of 16 pipes can be created at the same time
+        * If pipes are already created, hci_dev_up will fail.
+        * Doing a clear all pipe is a bad idea because:
+        * - It does useless EEPROM cycling
+        * - It might cause issue for secure elements support
+        * (such as removing connectivity or APDU reader pipe)
+        * A better approach on ST21NFCA is to:
+        * - get a pipe list for each host.
+        * (eg: NFC_HCI_HOST_CONTROLLER_ID for now).
+        * (TODO Later on UICC HOST and eSE HOST)
+        * - get pipe information
+        * - match retrieved pipe list in st21nfca_gates
+        * ST21NFCA_DEVICE_MGNT_GATE is a proprietary gate
+        * with ST21NFCA_DEVICE_MGNT_PIPE.
+        * Pipe can be closed and need to be open.
+        */
+       r = nfc_hci_connect_gate(hdev, NFC_HCI_HOST_CONTROLLER_ID,
+               ST21NFCA_DEVICE_MGNT_GATE, ST21NFCA_DEVICE_MGNT_PIPE);
+       if (r < 0)
+               goto free_info;
+
+       /* Get pipe list */
+       r = nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+                       ST21NFCA_DM_GETINFO, pipe_list, sizeof(pipe_list),
+                       &skb_pipe_list);
+       if (r < 0)
+               goto free_info;
+
+       /* Complete the existing gate_pipe table */
+       for (i = 0; i < skb_pipe_list->len; i++) {
+               pipe_info[2] = skb_pipe_list->data[i];
+               r = nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+                                       ST21NFCA_DM_GETINFO, pipe_info,
+                                       sizeof(pipe_info), &skb_pipe_info);
+
+               if (r)
+                       continue;
+
+               /*
+                * Match pipe ID and gate ID
+                * Output format from ST21NFC_DM_GETINFO is:
+                * - pipe state (1byte)
+                * - source hid (1byte)
+                * - source gid (1byte)
+                * - destination hid (1byte)
+                * - destination gid (1byte)
+                */
+               info = (struct st21nfca_pipe_info *) skb_pipe_info->data;
+               for (j = 0; (j < ARRAY_SIZE(st21nfca_gates)) &&
+                       (st21nfca_gates[j].gate != info->dst_gate_id);
+                       j++)
+                       ;
+
+               if (j < ARRAY_SIZE(st21nfca_gates) &&
+                       st21nfca_gates[j].gate == info->dst_gate_id &&
+                       ST21NFCA_DM_IS_PIPE_OPEN(info->pipe_state)) {
+                       st21nfca_gates[j].pipe = pipe_info[2];
+                       hdev->gate2pipe[st21nfca_gates[j].gate] =
+                               st21nfca_gates[j].pipe;
+               }
+       }
+
+       /*
+        * 3 gates have a well known pipe ID.
+        * They will never appear in the pipe list
+        */
+       if (skb_pipe_list->len + 3 < ARRAY_SIZE(st21nfca_gates)) {
+               for (i = skb_pipe_list->len + 3;
+                               i < ARRAY_SIZE(st21nfca_gates); i++) {
+                       r = nfc_hci_connect_gate(hdev,
+                                       NFC_HCI_HOST_CONTROLLER_ID,
+                                       st21nfca_gates[i].gate,
+                                       st21nfca_gates[i].pipe);
+                       if (r < 0)
+                               goto free_info;
+               }
+       }
+
+       memcpy(hdev->init_data.gates, st21nfca_gates, sizeof(st21nfca_gates));
+free_info:
+       kfree_skb(skb_pipe_info);
+free_list:
+       kfree_skb(skb_pipe_list);
+       return r;
+}
+
+static int st21nfca_hci_open(struct nfc_hci_dev *hdev)
+{
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+       int r;
+
+       mutex_lock(&info->info_lock);
+
+       if (info->state != ST21NFCA_ST_COLD) {
+               r = -EBUSY;
+               goto out;
+       }
+
+       r = info->phy_ops->enable(info->phy_id);
+
+       if (r == 0)
+               info->state = ST21NFCA_ST_READY;
+
+out:
+       mutex_unlock(&info->info_lock);
+       return r;
+}
+
+static void st21nfca_hci_close(struct nfc_hci_dev *hdev)
+{
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+       mutex_lock(&info->info_lock);
+
+       if (info->state == ST21NFCA_ST_COLD)
+               goto out;
+
+       info->phy_ops->disable(info->phy_id);
+       info->state = ST21NFCA_ST_COLD;
+
+out:
+       mutex_unlock(&info->info_lock);
+}
+
+static int st21nfca_hci_ready(struct nfc_hci_dev *hdev)
+{
+       struct sk_buff *skb;
+
+       u8 param;
+       int r;
+
+       param = NFC_HCI_UICC_HOST_ID;
+       r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE,
+                             NFC_HCI_ADMIN_WHITELIST, &param, 1);
+       if (r < 0)
+               return r;
+
+       /* Set NFC_MODE in device management gate to enable */
+       r = nfc_hci_get_param(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+                             ST21NFCA_NFC_MODE, &skb);
+       if (r < 0)
+               return r;
+
+       if (skb->data[0] == 0) {
+               kfree_skb(skb);
+               param = 1;
+
+               r = nfc_hci_set_param(hdev, ST21NFCA_DEVICE_MGNT_GATE,
+                                       ST21NFCA_NFC_MODE, &param, 1);
+               if (r < 0)
+                       return r;
+       }
+
+       r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
+                              NFC_HCI_EVT_END_OPERATION, NULL, 0);
+       if (r < 0)
+               return r;
+
+       r = nfc_hci_get_param(hdev, NFC_HCI_ID_MGMT_GATE,
+                             NFC_HCI_ID_MGMT_VERSION_SW, &skb);
+       if (r < 0)
+               return r;
+
+       if (skb->len != FULL_VERSION_LEN) {
+               kfree_skb(skb);
+               return -EINVAL;
+       }
+
+       print_hex_dump(KERN_DEBUG, "FULL VERSION SOFTWARE INFO: ",
+                      DUMP_PREFIX_NONE, 16, 1,
+                      skb->data, FULL_VERSION_LEN, false);
+
+       kfree_skb(skb);
+
+       return 0;
+}
+
+static int st21nfca_hci_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+{
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+       return info->phy_ops->write(info->phy_id, skb);
+}
+
+static int st21nfca_hci_start_poll(struct nfc_hci_dev *hdev,
+                                  u32 im_protocols, u32 tm_protocols)
+{
+       int r;
+
+       pr_info(DRIVER_DESC ": %s protocols 0x%x 0x%x\n",
+               __func__, im_protocols, tm_protocols);
+
+       r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
+                              NFC_HCI_EVT_END_OPERATION, NULL, 0);
+       if (r < 0)
+               return r;
+       if (im_protocols) {
+               /*
+                * enable polling according to im_protocols & tm_protocols
+                * - CLOSE pipe according to im_protocols & tm_protocols
+                */
+               if ((NFC_HCI_RF_READER_B_GATE & im_protocols) == 0) {
+                       r = nfc_hci_disconnect_gate(hdev,
+                                       NFC_HCI_RF_READER_B_GATE);
+                       if (r < 0)
+                               return r;
+               }
+
+               if ((NFC_HCI_RF_READER_A_GATE & im_protocols) == 0) {
+                       r = nfc_hci_disconnect_gate(hdev,
+                                       NFC_HCI_RF_READER_A_GATE);
+                       if (r < 0)
+                               return r;
+               }
+
+               if ((ST21NFCA_RF_READER_F_GATE & im_protocols) == 0) {
+                       r = nfc_hci_disconnect_gate(hdev,
+                                       ST21NFCA_RF_READER_F_GATE);
+                       if (r < 0)
+                               return r;
+               }
+
+               if ((ST21NFCA_RF_READER_14443_3_A_GATE & im_protocols) == 0) {
+                       r = nfc_hci_disconnect_gate(hdev,
+                                       ST21NFCA_RF_READER_14443_3_A_GATE);
+                       if (r < 0)
+                               return r;
+               }
+
+               if ((ST21NFCA_RF_READER_ISO15693_GATE & im_protocols) == 0) {
+                       r = nfc_hci_disconnect_gate(hdev,
+                                       ST21NFCA_RF_READER_ISO15693_GATE);
+                       if (r < 0)
+                               return r;
+               }
+
+               r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
+                                      NFC_HCI_EVT_READER_REQUESTED, NULL, 0);
+               if (r < 0)
+                       nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
+                                          NFC_HCI_EVT_END_OPERATION, NULL, 0);
+       }
+       return r;
+}
+
+static int st21nfca_get_iso14443_3_atqa(struct nfc_hci_dev *hdev, u16 *atqa)
+{
+       int r;
+       struct sk_buff *atqa_skb = NULL;
+
+       r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_14443_3_A_GATE,
+                             ST21NFCA_RF_READER_14443_3_A_ATQA, &atqa_skb);
+       if (r < 0)
+               goto exit;
+
+       if (atqa_skb->len != 2) {
+               r = -EPROTO;
+               goto exit;
+       }
+
+       *atqa = be16_to_cpu(*(__be16 *) atqa_skb->data);
+
+exit:
+       kfree_skb(atqa_skb);
+       return r;
+}
+
+static int st21nfca_get_iso14443_3_sak(struct nfc_hci_dev *hdev, u8 *sak)
+{
+       int r;
+       struct sk_buff *sak_skb = NULL;
+
+       r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_14443_3_A_GATE,
+                             ST21NFCA_RF_READER_14443_3_A_SAK, &sak_skb);
+       if (r < 0)
+               goto exit;
+
+       if (sak_skb->len != 1) {
+               r = -EPROTO;
+               goto exit;
+       }
+
+       *sak = sak_skb->data[0];
+
+exit:
+       kfree_skb(sak_skb);
+       return r;
+}
+
+static int st21nfca_get_iso14443_3_uid(struct nfc_hci_dev *hdev, u8 *gate,
+                                      int *len)
+{
+       int r;
+       struct sk_buff *uid_skb = NULL;
+
+       r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_14443_3_A_GATE,
+                             ST21NFCA_RF_READER_14443_3_A_UID, &uid_skb);
+       if (r < 0)
+               goto exit;
+
+       if (uid_skb->len == 0 || uid_skb->len > NFC_NFCID1_MAXSIZE) {
+               r = -EPROTO;
+               goto exit;
+       }
+
+       gate = uid_skb->data;
+       *len = uid_skb->len;
+exit:
+       kfree_skb(uid_skb);
+       return r;
+}
+
+static int st21nfca_get_iso15693_inventory(struct nfc_hci_dev *hdev,
+                                          struct nfc_target *target)
+{
+       int r;
+       struct sk_buff *inventory_skb = NULL;
+
+       r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_ISO15693_GATE,
+                             ST21NFCA_RF_READER_ISO15693_INVENTORY,
+                             &inventory_skb);
+       if (r < 0)
+               goto exit;
+
+       skb_pull(inventory_skb, 2);
+
+       if (inventory_skb->len == 0 ||
+           inventory_skb->len > NFC_ISO15693_UID_MAXSIZE) {
+               r = -EPROTO;
+               goto exit;
+       }
+
+       memcpy(target->iso15693_uid, inventory_skb->data, inventory_skb->len);
+       target->iso15693_dsfid  = inventory_skb->data[1];
+       target->is_iso15693 = 1;
+exit:
+       kfree_skb(inventory_skb);
+       return r;
+}
+
+static int st21nfca_hci_target_from_gate(struct nfc_hci_dev *hdev, u8 gate,
+                                        struct nfc_target *target)
+{
+       int r, len;
+       u16 atqa;
+       u8 sak;
+       u8 uid[NFC_NFCID1_MAXSIZE];
+
+       switch (gate) {
+       case ST21NFCA_RF_READER_F_GATE:
+               target->supported_protocols = NFC_PROTO_FELICA_MASK;
+               break;
+       case ST21NFCA_RF_READER_14443_3_A_GATE:
+               /* ISO14443-3 type 1 or 2 tags */
+               r = st21nfca_get_iso14443_3_atqa(hdev, &atqa);
+               if (r < 0)
+                       return r;
+               if (atqa == 0x000c) {
+                       target->supported_protocols = NFC_PROTO_JEWEL_MASK;
+                       target->sens_res = 0x0c00;
+               } else {
+                       r = st21nfca_get_iso14443_3_sak(hdev, &sak);
+                       if (r < 0)
+                               return r;
+
+                       r = st21nfca_get_iso14443_3_uid(hdev, uid, &len);
+                       if (r < 0)
+                               return r;
+
+                       target->supported_protocols =
+                           nfc_hci_sak_to_protocol(sak);
+                       if (target->supported_protocols == 0xffffffff)
+                               return -EPROTO;
+
+                       target->sens_res = atqa;
+                       target->sel_res = sak;
+                       memcpy(target->nfcid1, uid, len);
+                       target->nfcid1_len = len;
+               }
+
+               break;
+       case ST21NFCA_RF_READER_ISO15693_GATE:
+               target->supported_protocols = NFC_PROTO_ISO15693_MASK;
+               r = st21nfca_get_iso15693_inventory(hdev, target);
+               if (r < 0)
+                       return r;
+               break;
+       default:
+               return -EPROTO;
+       }
+
+       return 0;
+}
+
+#define ST21NFCA_CB_TYPE_READER_ISO15693 1
+static void st21nfca_hci_data_exchange_cb(void *context, struct sk_buff *skb,
+                                         int err)
+{
+       struct st21nfca_hci_info *info = context;
+
+       switch (info->async_cb_type) {
+       case ST21NFCA_CB_TYPE_READER_ISO15693:
+               if (err == 0)
+                       skb_trim(skb, skb->len - 1);
+               info->async_cb(info->async_cb_context, skb, err);
+               break;
+       default:
+               if (err == 0)
+                       kfree_skb(skb);
+               break;
+       }
+}
+
+/*
+ * Returns:
+ * <= 0: driver handled the data exchange
+ *    1: driver doesn't especially handle, please do standard processing
+ */
+static int st21nfca_hci_im_transceive(struct nfc_hci_dev *hdev,
+                                     struct nfc_target *target,
+                                     struct sk_buff *skb,
+                                     data_exchange_cb_t cb, void *cb_context)
+{
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+       pr_info(DRIVER_DESC ": %s for gate=%d len=%d\n", __func__,
+               target->hci_reader_gate, skb->len);
+
+       switch (target->hci_reader_gate) {
+       case ST21NFCA_RF_READER_F_GATE:
+               *skb_push(skb, 1) = 0x1a;
+               return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+                                             ST21NFCA_WR_XCHG_DATA, skb->data,
+                                             skb->len, cb, cb_context);
+       case ST21NFCA_RF_READER_14443_3_A_GATE:
+               *skb_push(skb, 1) = 0x1a;       /* CTR, see spec:10.2.2.1 */
+
+               return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+                                             ST21NFCA_WR_XCHG_DATA, skb->data,
+                                             skb->len, cb, cb_context);
+       case ST21NFCA_RF_READER_ISO15693_GATE:
+               info->async_cb_type = ST21NFCA_CB_TYPE_READER_ISO15693;
+               info->async_cb = cb;
+               info->async_cb_context = cb_context;
+
+               *skb_push(skb, 1) = 0x17;
+
+               return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+                                             ST21NFCA_WR_XCHG_DATA, skb->data,
+                                             skb->len,
+                                             st21nfca_hci_data_exchange_cb,
+                                             info);
+               break;
+       default:
+               return 1;
+       }
+}
+
+static int st21nfca_hci_check_presence(struct nfc_hci_dev *hdev,
+                                      struct nfc_target *target)
+{
+       u8 fwi = 0x11;
+       switch (target->hci_reader_gate) {
+       case NFC_HCI_RF_READER_A_GATE:
+       case NFC_HCI_RF_READER_B_GATE:
+               /*
+                * PRESENCE_CHECK on those gates is available
+                * However, the answer to this command is taking 3 * fwi
+                * if the card is no present.
+                * Instead, we send an empty I-Frame with a very short
+                * configurable fwi ~604µs.
+                */
+               return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
+                                       ST21NFCA_WR_XCHG_DATA, &fwi, 1, NULL);
+       case ST21NFCA_RF_READER_14443_3_A_GATE:
+               return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
+                                       ST21NFCA_RF_READER_CMD_PRESENCE_CHECK,
+                                       NULL, 0, NULL);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static struct nfc_hci_ops st21nfca_hci_ops = {
+       .open = st21nfca_hci_open,
+       .close = st21nfca_hci_close,
+       .load_session = st21nfca_hci_load_session,
+       .hci_ready = st21nfca_hci_ready,
+       .xmit = st21nfca_hci_xmit,
+       .start_poll = st21nfca_hci_start_poll,
+       .target_from_gate = st21nfca_hci_target_from_gate,
+       .im_transceive = st21nfca_hci_im_transceive,
+       .check_presence = st21nfca_hci_check_presence,
+};
+
+int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops,
+                      char *llc_name, int phy_headroom, int phy_tailroom,
+                      int phy_payload, struct nfc_hci_dev **hdev)
+{
+       struct st21nfca_hci_info *info;
+       int r = 0;
+       int dev_num;
+       u32 protocols;
+       struct nfc_hci_init_data init_data;
+       unsigned long quirks = 0;
+
+       info = kzalloc(sizeof(struct st21nfca_hci_info), GFP_KERNEL);
+       if (!info) {
+               r = -ENOMEM;
+               goto err_alloc_hdev;
+       }
+
+       info->phy_ops = phy_ops;
+       info->phy_id = phy_id;
+       info->state = ST21NFCA_ST_COLD;
+       mutex_init(&info->info_lock);
+
+       init_data.gate_count = ARRAY_SIZE(st21nfca_gates);
+
+       memcpy(init_data.gates, st21nfca_gates, sizeof(st21nfca_gates));
+
+       /*
+        * Session id must include the driver name + i2c bus addr
+        * persistent info to discriminate 2 identical chips
+        */
+       dev_num = find_first_zero_bit(dev_mask, ST21NFCA_NUM_DEVICES);
+       if (dev_num >= ST21NFCA_NUM_DEVICES)
+               goto err_alloc_hdev;
+
+       scnprintf(init_data.session_id, sizeof(init_data.session_id), "%s%2x",
+                 "ST21AH", dev_num);
+
+       protocols = NFC_PROTO_JEWEL_MASK |
+           NFC_PROTO_MIFARE_MASK |
+           NFC_PROTO_FELICA_MASK |
+           NFC_PROTO_ISO14443_MASK |
+           NFC_PROTO_ISO14443_B_MASK |
+           NFC_PROTO_ISO15693_MASK;
+
+       set_bit(NFC_HCI_QUIRK_SHORT_CLEAR, &quirks);
+
+       info->hdev =
+           nfc_hci_allocate_device(&st21nfca_hci_ops, &init_data, quirks,
+                                   protocols, llc_name,
+                                   phy_headroom + ST21NFCA_CMDS_HEADROOM,
+                                   phy_tailroom, phy_payload);
+
+       if (!info->hdev) {
+               pr_err("Cannot allocate nfc hdev.\n");
+               r = -ENOMEM;
+               goto err_alloc_hdev;
+       }
+
+       nfc_hci_set_clientdata(info->hdev, info);
+
+       r = nfc_hci_register_device(info->hdev);
+       if (r)
+               goto err_regdev;
+
+       *hdev = info->hdev;
+
+       return 0;
+
+err_regdev:
+       nfc_hci_free_device(info->hdev);
+
+err_alloc_hdev:
+       kfree(info);
+
+       return r;
+}
+EXPORT_SYMBOL(st21nfca_hci_probe);
+
+void st21nfca_hci_remove(struct nfc_hci_dev *hdev)
+{
+       struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+       nfc_hci_unregister_device(hdev);
+       nfc_hci_free_device(hdev);
+       kfree(info);
+}
+EXPORT_SYMBOL(st21nfca_hci_remove);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/nfc/st21nfca/st21nfca.h b/drivers/nfc/st21nfca/st21nfca.h
new file mode 100644 (file)
index 0000000..334cd90
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014  STMicroelectronics SAS. All rights reserved.
+ *
+ * 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 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LOCAL_ST21NFCA_H_
+#define __LOCAL_ST21NFCA_H_
+
+#include <net/nfc/hci.h>
+
+#define HCI_MODE 0
+
+/* framing in HCI mode */
+#define ST21NFCA_SOF_EOF_LEN    2
+
+/* Almost every time value is 0 */
+#define ST21NFCA_HCI_LLC_LEN    1
+
+/* Size in worst case :
+ * In normal case CRC len = 2 but byte stuffing
+ * may appear in case one CRC byte = ST21NFCA_SOF_EOF
+ */
+#define ST21NFCA_HCI_LLC_CRC    4
+
+#define ST21NFCA_HCI_LLC_LEN_CRC        (ST21NFCA_SOF_EOF_LEN + \
+                                               ST21NFCA_HCI_LLC_LEN + \
+                                               ST21NFCA_HCI_LLC_CRC)
+#define ST21NFCA_HCI_LLC_MIN_SIZE       (1 + ST21NFCA_HCI_LLC_LEN_CRC)
+
+/* Worst case when adding byte stuffing between each byte */
+#define ST21NFCA_HCI_LLC_MAX_PAYLOAD    29
+#define ST21NFCA_HCI_LLC_MAX_SIZE       (ST21NFCA_HCI_LLC_LEN_CRC + 1 + \
+                                       ST21NFCA_HCI_LLC_MAX_PAYLOAD)
+
+#define DRIVER_DESC "HCI NFC driver for ST21NFCA"
+
+#define ST21NFCA_HCI_MODE 0
+
+#define ST21NFCA_NUM_DEVICES 256
+
+int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops,
+                      char *llc_name, int phy_headroom, int phy_tailroom,
+                      int phy_payload, struct nfc_hci_dev **hdev);
+void st21nfca_hci_remove(struct nfc_hci_dev *hdev);
+
+enum st21nfca_state {
+       ST21NFCA_ST_COLD,
+       ST21NFCA_ST_READY,
+};
+
+struct st21nfca_hci_info {
+       struct nfc_phy_ops *phy_ops;
+       void *phy_id;
+
+       struct nfc_hci_dev *hdev;
+
+       enum st21nfca_state state;
+
+       struct mutex info_lock;
+
+       int async_cb_type;
+       data_exchange_cb_t async_cb;
+       void *async_cb_context;
+
+} __packed;
+
+/* Reader RF commands */
+#define ST21NFCA_WR_XCHG_DATA            0x10
+
+#define ST21NFCA_RF_READER_F_GATE               0x14
+#define ST21NFCA_RF_READER_F_DATARATE 0x01
+#define ST21NFCA_RF_READER_F_DATARATE_106 0x01
+#define ST21NFCA_RF_READER_F_DATARATE_212 0x02
+#define ST21NFCA_RF_READER_F_DATARATE_424 0x04
+
+#endif /* __LOCAL_ST21NFCA_H_ */
index d9babe986473f11de9a4c62eabac003a3c313e02..3b78b031e617df51e98101a231519a000cd159ed 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/device.h>
 #include <linux/netdevice.h>
 #include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
 #include <linux/nfc.h>
 #include <linux/skbuff.h>
 #include <linux/delay.h>
  * only the SRX bit set, it means that all of the data has been received
  * (once what's in the fifo has been read).  However, depending on timing
  * an interrupt status with only the SRX bit set may not be recived.  In
- * those cases, the timeout mechanism is used to wait 5 ms in case more
- * data arrives.  After 5 ms, it is assumed that all of the data has been
+ * those cases, the timeout mechanism is used to wait 20 ms in case more
+ * data arrives.  After 20 ms, it is assumed that all of the data has been
  * received and the accumulated rx data is sent upstream.  The
  * 'TRF7970A_ST_WAIT_FOR_RX_DATA_CONT' state is used for this purpose
  * (i.e., it indicates that some data has been received but we're not sure
  * if there is more coming so a timeout in this state means all data has
- * been received and there isn't an error).  The delay is 5 ms since delays
- * over 2 ms have been observed during testing (a little extra just in case).
+ * been received and there isn't an error).  The delay is 20 ms since delays
+ * of ~16 ms have been observed during testing.
  *
  * Type 2 write and sector select commands respond with a 4-bit ACK or NACK.
  * Having only 4 bits in the FIFO won't normally generate an interrupt so
 
 #define TRF7970A_SUPPORTED_PROTOCOLS \
                (NFC_PROTO_MIFARE_MASK | NFC_PROTO_ISO14443_MASK |      \
+                NFC_PROTO_ISO14443_B_MASK | NFC_PROTO_FELICA_MASK | \
                 NFC_PROTO_ISO15693_MASK)
 
+#define TRF7970A_AUTOSUSPEND_DELAY             30000 /* 30 seconds */
+
 /* TX data must be prefixed with a FIFO reset cmd, a cmd that depends
  * on what the current framing is, the address of the TX length byte 1
  * register (0x1d), and the 2 byte length of the data to be transmitted.
 /* TX length is 3 nibbles long ==> 4KB - 1 bytes max */
 #define TRF7970A_TX_MAX                                (4096 - 1)
 
-#define TRF7970A_WAIT_FOR_RX_DATA_TIMEOUT      5
+#define TRF7970A_WAIT_FOR_RX_DATA_TIMEOUT      20
 #define TRF7970A_WAIT_FOR_FIFO_DRAIN_TIMEOUT   3
 #define TRF7970A_WAIT_TO_ISSUE_ISO15693_EOF    20
 
@@ -330,13 +334,15 @@ struct trf7970a {
        struct regulator                *regulator;
        struct nfc_digital_dev          *ddev;
        u32                             quirks;
-       bool                            powering_up;
        bool                            aborting;
        struct sk_buff                  *tx_skb;
        struct sk_buff                  *rx_skb;
        nfc_digital_cmd_complete_t      cb;
        void                            *cb_arg;
+       u8                              chip_status_ctrl;
        u8                              iso_ctrl;
+       u8                              iso_ctrl_tech;
+       u8                              modulator_sys_clk_ctrl;
        u8                              special_fcn_reg1;
        int                             technology;
        int                             framing;
@@ -681,7 +687,9 @@ static irqreturn_t trf7970a_irq(int irq, void *dev_id)
                        trf->ignore_timeout =
                                !cancel_delayed_work(&trf->timeout_work);
                        trf7970a_drain_fifo(trf, status);
-               } else if (!(status & TRF7970A_IRQ_STATUS_TX)) {
+               } else if (status == TRF7970A_IRQ_STATUS_TX) {
+                       trf7970a_cmd(trf, TRF7970A_CMD_FIFO_RESET);
+               } else {
                        trf7970a_send_err_upstream(trf, -EIO);
                }
                break;
@@ -757,8 +765,8 @@ static int trf7970a_init(struct trf7970a *trf)
        if (ret)
                goto err_out;
 
-       ret = trf7970a_write(trf, TRF7970A_MODULATOR_SYS_CLK_CTRL,
-                       TRF7970A_MODULATOR_DEPTH_OOK);
+       /* Must clear NFC Target Detection Level reg due to erratum */
+       ret = trf7970a_write(trf, TRF7970A_NFC_TARGET_LEVEL, 0);
        if (ret)
                goto err_out;
 
@@ -774,12 +782,7 @@ static int trf7970a_init(struct trf7970a *trf)
 
        trf->special_fcn_reg1 = 0;
 
-       ret = trf7970a_write(trf, TRF7970A_CHIP_STATUS_CTRL,
-                       TRF7970A_CHIP_STATUS_RF_ON |
-                               TRF7970A_CHIP_STATUS_VRS5_3);
-       if (ret)
-               goto err_out;
-
+       trf->iso_ctrl = 0xff;
        return 0;
 
 err_out:
@@ -791,53 +794,29 @@ static void trf7970a_switch_rf_off(struct trf7970a *trf)
 {
        dev_dbg(trf->dev, "Switching rf off\n");
 
-       gpio_set_value(trf->en_gpio, 0);
-       gpio_set_value(trf->en2_gpio, 0);
+       trf->chip_status_ctrl &= ~TRF7970A_CHIP_STATUS_RF_ON;
+
+       trf7970a_write(trf, TRF7970A_CHIP_STATUS_CTRL, trf->chip_status_ctrl);
 
        trf->aborting = false;
        trf->state = TRF7970A_ST_OFF;
+
+       pm_runtime_mark_last_busy(trf->dev);
+       pm_runtime_put_autosuspend(trf->dev);
 }
 
-static int trf7970a_switch_rf_on(struct trf7970a *trf)
+static void trf7970a_switch_rf_on(struct trf7970a *trf)
 {
-       unsigned long delay;
-       int ret;
-
        dev_dbg(trf->dev, "Switching rf on\n");
 
-       if (trf->powering_up)
-               usleep_range(5000, 6000);
-
-       gpio_set_value(trf->en2_gpio, 1);
-       usleep_range(1000, 2000);
-       gpio_set_value(trf->en_gpio, 1);
+       pm_runtime_get_sync(trf->dev);
 
-       /* The delay between enabling the trf7970a and issuing the first
-        * command is significantly longer the very first time after powering
-        * up.  Make sure the longer delay is only done the first time.
-        */
-       if (trf->powering_up) {
-               delay = 20000;
-               trf->powering_up = false;
-       } else {
-               delay = 5000;
-       }
-
-       usleep_range(delay, delay + 1000);
-
-       ret = trf7970a_init(trf);
-       if (ret)
-               trf7970a_switch_rf_off(trf);
-       else
-               trf->state = TRF7970A_ST_IDLE;
-
-       return ret;
+       trf->state = TRF7970A_ST_IDLE;
 }
 
 static int trf7970a_switch_rf(struct nfc_digital_dev *ddev, bool on)
 {
        struct trf7970a *trf = nfc_digital_get_drvdata(ddev);
-       int ret = 0;
 
        dev_dbg(trf->dev, "Switching RF - state: %d, on: %d\n", trf->state, on);
 
@@ -846,7 +825,7 @@ static int trf7970a_switch_rf(struct nfc_digital_dev *ddev, bool on)
        if (on) {
                switch (trf->state) {
                case TRF7970A_ST_OFF:
-                       ret = trf7970a_switch_rf_on(trf);
+                       trf7970a_switch_rf_on(trf);
                        break;
                case TRF7970A_ST_IDLE:
                case TRF7970A_ST_IDLE_RX_BLOCKED:
@@ -871,7 +850,7 @@ static int trf7970a_switch_rf(struct nfc_digital_dev *ddev, bool on)
        }
 
        mutex_unlock(&trf->lock);
-       return ret;
+       return 0;
 }
 
 static int trf7970a_config_rf_tech(struct trf7970a *trf, int tech)
@@ -882,10 +861,24 @@ static int trf7970a_config_rf_tech(struct trf7970a *trf, int tech)
 
        switch (tech) {
        case NFC_DIGITAL_RF_TECH_106A:
-               trf->iso_ctrl = TRF7970A_ISO_CTRL_14443A_106;
+               trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_14443A_106;
+               trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_OOK;
+               break;
+       case NFC_DIGITAL_RF_TECH_106B:
+               trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_14443B_106;
+               trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10;
+               break;
+       case NFC_DIGITAL_RF_TECH_212F:
+               trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_FELICA_212;
+               trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10;
+               break;
+       case NFC_DIGITAL_RF_TECH_424F:
+               trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_FELICA_424;
+               trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10;
                break;
        case NFC_DIGITAL_RF_TECH_ISO15693:
-               trf->iso_ctrl = TRF7970A_ISO_CTRL_15693_SGL_1OF4_2648;
+               trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_15693_SGL_1OF4_2648;
+               trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_OOK;
                break;
        default:
                dev_dbg(trf->dev, "Unsupported rf technology: %d\n", tech);
@@ -899,24 +892,31 @@ static int trf7970a_config_rf_tech(struct trf7970a *trf, int tech)
 
 static int trf7970a_config_framing(struct trf7970a *trf, int framing)
 {
+       u8 iso_ctrl = trf->iso_ctrl_tech;
+       int ret;
+
        dev_dbg(trf->dev, "framing: %d\n", framing);
 
        switch (framing) {
        case NFC_DIGITAL_FRAMING_NFCA_SHORT:
        case NFC_DIGITAL_FRAMING_NFCA_STANDARD:
                trf->tx_cmd = TRF7970A_CMD_TRANSMIT_NO_CRC;
-               trf->iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N;
+               iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N;
                break;
        case NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A:
        case NFC_DIGITAL_FRAMING_NFCA_T4T:
+       case NFC_DIGITAL_FRAMING_NFCB:
+       case NFC_DIGITAL_FRAMING_NFCB_T4T:
+       case NFC_DIGITAL_FRAMING_NFCF:
+       case NFC_DIGITAL_FRAMING_NFCF_T3T:
        case NFC_DIGITAL_FRAMING_ISO15693_INVENTORY:
        case NFC_DIGITAL_FRAMING_ISO15693_T5T:
                trf->tx_cmd = TRF7970A_CMD_TRANSMIT;
-               trf->iso_ctrl &= ~TRF7970A_ISO_CTRL_RX_CRC_N;
+               iso_ctrl &= ~TRF7970A_ISO_CTRL_RX_CRC_N;
                break;
        case NFC_DIGITAL_FRAMING_NFCA_T2T:
                trf->tx_cmd = TRF7970A_CMD_TRANSMIT;
-               trf->iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N;
+               iso_ctrl |= TRF7970A_ISO_CTRL_RX_CRC_N;
                break;
        default:
                dev_dbg(trf->dev, "Unsupported Framing: %d\n", framing);
@@ -925,24 +925,46 @@ static int trf7970a_config_framing(struct trf7970a *trf, int framing)
 
        trf->framing = framing;
 
-       return trf7970a_write(trf, TRF7970A_ISO_CTRL, trf->iso_ctrl);
+       if (iso_ctrl != trf->iso_ctrl) {
+               ret = trf7970a_write(trf, TRF7970A_ISO_CTRL, iso_ctrl);
+               if (ret)
+                       return ret;
+
+               trf->iso_ctrl = iso_ctrl;
+
+               ret = trf7970a_write(trf, TRF7970A_MODULATOR_SYS_CLK_CTRL,
+                               trf->modulator_sys_clk_ctrl);
+               if (ret)
+                       return ret;
+       }
+
+       if (!(trf->chip_status_ctrl & TRF7970A_CHIP_STATUS_RF_ON)) {
+               ret = trf7970a_write(trf, TRF7970A_CHIP_STATUS_CTRL,
+                               trf->chip_status_ctrl |
+                                       TRF7970A_CHIP_STATUS_RF_ON);
+               if (ret)
+                       return ret;
+
+               trf->chip_status_ctrl |= TRF7970A_CHIP_STATUS_RF_ON;
+
+               usleep_range(5000, 6000);
+       }
+
+       return 0;
 }
 
 static int trf7970a_in_configure_hw(struct nfc_digital_dev *ddev, int type,
                int param)
 {
        struct trf7970a *trf = nfc_digital_get_drvdata(ddev);
-       int ret = 0;
+       int ret;
 
        dev_dbg(trf->dev, "Configure hw - type: %d, param: %d\n", type, param);
 
        mutex_lock(&trf->lock);
 
-       if (trf->state == TRF7970A_ST_OFF) {
-               ret = trf7970a_switch_rf_on(trf);
-               if (ret)
-                       goto err_out;
-       }
+       if (trf->state == TRF7970A_ST_OFF)
+               trf7970a_switch_rf_on(trf);
 
        switch (type) {
        case NFC_DIGITAL_CONFIG_RF_TECH:
@@ -956,7 +978,6 @@ static int trf7970a_in_configure_hw(struct nfc_digital_dev *ddev, int type,
                ret = -EINVAL;
        }
 
-err_out:
        mutex_unlock(&trf->lock);
        return ret;
 }
@@ -1191,7 +1212,18 @@ static void trf7970a_abort_cmd(struct nfc_digital_dev *ddev)
        dev_dbg(trf->dev, "Abort process initiated\n");
 
        mutex_lock(&trf->lock);
-       trf->aborting = true;
+
+       switch (trf->state) {
+       case TRF7970A_ST_WAIT_FOR_TX_FIFO:
+       case TRF7970A_ST_WAIT_FOR_RX_DATA:
+       case TRF7970A_ST_WAIT_FOR_RX_DATA_CONT:
+       case TRF7970A_ST_WAIT_TO_ISSUE_EOF:
+               trf->aborting = true;
+               break;
+       default:
+               break;
+       }
+
        mutex_unlock(&trf->lock);
 }
 
@@ -1206,12 +1238,25 @@ static struct nfc_digital_ops trf7970a_nfc_ops = {
        .abort_cmd              = trf7970a_abort_cmd,
 };
 
+static int trf7970a_get_autosuspend_delay(struct device_node *np)
+{
+       int autosuspend_delay, ret;
+
+       ret = of_property_read_u32(np, "autosuspend-delay", &autosuspend_delay);
+       if (ret)
+               autosuspend_delay = TRF7970A_AUTOSUSPEND_DELAY;
+
+       of_node_put(np);
+
+       return autosuspend_delay;
+}
+
 static int trf7970a_probe(struct spi_device *spi)
 {
        struct device_node *np = spi->dev.of_node;
        const struct spi_device_id *id = spi_get_device_id(spi);
        struct trf7970a *trf;
-       int ret;
+       int uvolts, autosuspend_delay, ret;
 
        if (!np) {
                dev_err(&spi->dev, "No Device Tree entry\n");
@@ -1281,7 +1326,10 @@ static int trf7970a_probe(struct spi_device *spi)
                goto err_destroy_lock;
        }
 
-       trf->powering_up = true;
+       uvolts = regulator_get_voltage(trf->regulator);
+
+       if (uvolts > 4000000)
+               trf->chip_status_ctrl = TRF7970A_CHIP_STATUS_VRS5_3;
 
        trf->ddev = nfc_digital_allocate_device(&trf7970a_nfc_ops,
                        TRF7970A_SUPPORTED_PROTOCOLS,
@@ -1297,6 +1345,12 @@ static int trf7970a_probe(struct spi_device *spi)
        nfc_digital_set_drvdata(trf->ddev, trf);
        spi_set_drvdata(spi, trf);
 
+       autosuspend_delay = trf7970a_get_autosuspend_delay(np);
+
+       pm_runtime_set_autosuspend_delay(trf->dev, autosuspend_delay);
+       pm_runtime_use_autosuspend(trf->dev);
+       pm_runtime_enable(trf->dev);
+
        ret = nfc_digital_register_device(trf->ddev);
        if (ret) {
                dev_err(trf->dev, "Can't register NFC digital device: %d\n",
@@ -1307,6 +1361,7 @@ static int trf7970a_probe(struct spi_device *spi)
        return 0;
 
 err_free_ddev:
+       pm_runtime_disable(trf->dev);
        nfc_digital_free_device(trf->ddev);
 err_disable_regulator:
        regulator_disable(trf->regulator);
@@ -1321,15 +1376,16 @@ static int trf7970a_remove(struct spi_device *spi)
 
        mutex_lock(&trf->lock);
 
-       trf7970a_switch_rf_off(trf);
-       trf7970a_init(trf);
-
        switch (trf->state) {
        case TRF7970A_ST_WAIT_FOR_TX_FIFO:
        case TRF7970A_ST_WAIT_FOR_RX_DATA:
        case TRF7970A_ST_WAIT_FOR_RX_DATA_CONT:
        case TRF7970A_ST_WAIT_TO_ISSUE_EOF:
                trf7970a_send_err_upstream(trf, -ECANCELED);
+               /* FALLTHROUGH */
+       case TRF7970A_ST_IDLE:
+       case TRF7970A_ST_IDLE_RX_BLOCKED:
+               pm_runtime_put_sync(trf->dev);
                break;
        default:
                break;
@@ -1337,6 +1393,8 @@ static int trf7970a_remove(struct spi_device *spi)
 
        mutex_unlock(&trf->lock);
 
+       pm_runtime_disable(trf->dev);
+
        nfc_digital_unregister_device(trf->ddev);
        nfc_digital_free_device(trf->ddev);
 
@@ -1347,6 +1405,70 @@ static int trf7970a_remove(struct spi_device *spi)
        return 0;
 }
 
+#ifdef CONFIG_PM_RUNTIME
+static int trf7970a_pm_runtime_suspend(struct device *dev)
+{
+       struct spi_device *spi = container_of(dev, struct spi_device, dev);
+       struct trf7970a *trf = spi_get_drvdata(spi);
+       int ret;
+
+       dev_dbg(dev, "Runtime suspend\n");
+
+       if (trf->state != TRF7970A_ST_OFF) {
+               dev_dbg(dev, "Can't suspend - not in OFF state (%d)\n",
+                               trf->state);
+               return -EBUSY;
+       }
+
+       gpio_set_value(trf->en_gpio, 0);
+       gpio_set_value(trf->en2_gpio, 0);
+
+       ret = regulator_disable(trf->regulator);
+       if (ret)
+               dev_err(dev, "%s - Can't disable VIN: %d\n", __func__, ret);
+
+       return ret;
+}
+
+static int trf7970a_pm_runtime_resume(struct device *dev)
+{
+       struct spi_device *spi = container_of(dev, struct spi_device, dev);
+       struct trf7970a *trf = spi_get_drvdata(spi);
+       int ret;
+
+       dev_dbg(dev, "Runtime resume\n");
+
+       ret = regulator_enable(trf->regulator);
+       if (ret) {
+               dev_err(dev, "%s - Can't enable VIN: %d\n", __func__, ret);
+               return ret;
+       }
+
+       usleep_range(5000, 6000);
+
+       gpio_set_value(trf->en2_gpio, 1);
+       usleep_range(1000, 2000);
+       gpio_set_value(trf->en_gpio, 1);
+
+       usleep_range(20000, 21000);
+
+       ret = trf7970a_init(trf);
+       if (ret) {
+               dev_err(dev, "%s - Can't initialize: %d\n", __func__, ret);
+               return ret;
+       }
+
+       pm_runtime_mark_last_busy(dev);
+
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops trf7970a_pm_ops = {
+       SET_RUNTIME_PM_OPS(trf7970a_pm_runtime_suspend,
+                       trf7970a_pm_runtime_resume, NULL)
+};
+
 static const struct spi_device_id trf7970a_id_table[] = {
        { "trf7970a", TRF7970A_QUIRK_IRQ_STATUS_READ_ERRATA },
        { }
@@ -1360,6 +1482,7 @@ static struct spi_driver trf7970a_spi_driver = {
        .driver         = {
                .name   = "trf7970a",
                .owner  = THIS_MODULE,
+               .pm     = &trf7970a_pm_ops,
        },
 };
 
index 9a95831bd065c2ba1c5af83f6a73927a3b9d8181..fb4a5983064824cad8338cad9327121a615566b4 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/netdevice.h>
 #include <linux/err.h>
 #include <linux/phy.h>
+#include <linux/phy_fixed.h>
 #include <linux/of.h>
 #include <linux/of_irq.h>
 #include <linux/of_mdio.h>
 MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
 MODULE_LICENSE("GPL");
 
-static void of_set_phy_supported(struct phy_device *phydev, u32 max_speed)
-{
-       /* The default values for phydev->supported are provided by the PHY
-        * driver "features" member, we want to reset to sane defaults fist
-        * before supporting higher speeds.
-        */
-       phydev->supported &= PHY_DEFAULT_FEATURES;
-
-       switch (max_speed) {
-       default:
-               return;
-
-       case SPEED_1000:
-               phydev->supported |= PHY_1000BT_FEATURES;
-       case SPEED_100:
-               phydev->supported |= PHY_100BT_FEATURES;
-       case SPEED_10:
-               phydev->supported |= PHY_10BT_FEATURES;
-       }
-}
-
 /* Extract the clause 22 phy ID from the compatible string of the form
  * ethernet-phy-idAAAA.BBBB */
 static int of_get_phy_id(struct device_node *device, u32 *phy_id)
@@ -66,7 +46,6 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi
        struct phy_device *phy;
        bool is_c45;
        int rc;
-       u32 max_speed = 0;
        u32 phy_id;
 
        is_c45 = of_device_is_compatible(child,
@@ -103,17 +82,33 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi
                return 1;
        }
 
-       /* Set phydev->supported based on the "max-speed" property
-        * if present */
-       if (!of_property_read_u32(child, "max-speed", &max_speed))
-               of_set_phy_supported(phy, max_speed);
-
        dev_dbg(&mdio->dev, "registered phy %s at address %i\n",
                child->name, addr);
 
        return 0;
 }
 
+static int of_mdio_parse_addr(struct device *dev, const struct device_node *np)
+{
+       u32 addr;
+       int ret;
+
+       ret = of_property_read_u32(np, "reg", &addr);
+       if (ret < 0) {
+               dev_err(dev, "%s has invalid PHY address\n", np->full_name);
+               return ret;
+       }
+
+       /* A PHY must have a reg property in the range [0-31] */
+       if (addr >= PHY_MAX_ADDR) {
+               dev_err(dev, "%s PHY address %i is too large\n",
+                       np->full_name, addr);
+               return -EINVAL;
+       }
+
+       return addr;
+}
+
 /**
  * of_mdiobus_register - Register mii_bus and create PHYs from the device tree
  * @mdio: pointer to mii_bus structure
@@ -126,9 +121,8 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
 {
        struct device_node *child;
        const __be32 *paddr;
-       u32 addr;
        bool scanphys = false;
-       int rc, i, len;
+       int addr, rc, i;
 
        /* Mask out all PHYs from auto probing.  Instead the PHYs listed in
         * the device tree are populated after the bus has been registered */
@@ -148,19 +142,9 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
 
        /* Loop over the child nodes and register a phy_device for each one */
        for_each_available_child_of_node(np, child) {
-               /* A PHY must have a reg property in the range [0-31] */
-               paddr = of_get_property(child, "reg", &len);
-               if (!paddr || len < sizeof(*paddr)) {
+               addr = of_mdio_parse_addr(&mdio->dev, child);
+               if (addr < 0) {
                        scanphys = true;
-                       dev_err(&mdio->dev, "%s has invalid PHY address\n",
-                               child->full_name);
-                       continue;
-               }
-
-               addr = be32_to_cpup(paddr);
-               if (addr >= PHY_MAX_ADDR) {
-                       dev_err(&mdio->dev, "%s PHY address %i is too large\n",
-                               child->full_name, addr);
                        continue;
                }
 
@@ -175,7 +159,7 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
        /* auto scan for PHYs with empty reg property */
        for_each_available_child_of_node(np, child) {
                /* Skip PHYs with reg property set */
-               paddr = of_get_property(child, "reg", &len);
+               paddr = of_get_property(child, "reg", NULL);
                if (paddr)
                        continue;
 
@@ -198,6 +182,40 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
 }
 EXPORT_SYMBOL(of_mdiobus_register);
 
+/**
+ * of_mdiobus_link_phydev - Find a device node for a phy
+ * @mdio: pointer to mii_bus structure
+ * @phydev: phydev for which the of_node pointer should be set
+ *
+ * Walk the list of subnodes of a mdio bus and look for a node that matches the
+ * phy's address with its 'reg' property. If found, set the of_node pointer for
+ * the phy. This allows auto-probed pyh devices to be supplied with information
+ * passed in via DT.
+ */
+void of_mdiobus_link_phydev(struct mii_bus *mdio,
+                           struct phy_device *phydev)
+{
+       struct device *dev = &phydev->dev;
+       struct device_node *child;
+
+       if (dev->of_node || !mdio->dev.of_node)
+               return;
+
+       for_each_available_child_of_node(mdio->dev.of_node, child) {
+               int addr;
+
+               addr = of_mdio_parse_addr(&mdio->dev, child);
+               if (addr < 0)
+                       continue;
+
+               if (addr == phydev->addr) {
+                       dev->of_node = child;
+                       return;
+               }
+       }
+}
+EXPORT_SYMBOL(of_mdiobus_link_phydev);
+
 /* Helper function for of_phy_find_device */
 static int of_phy_match(struct device *dev, void *phy_np)
 {
@@ -244,44 +262,6 @@ struct phy_device *of_phy_connect(struct net_device *dev,
 }
 EXPORT_SYMBOL(of_phy_connect);
 
-/**
- * of_phy_connect_fixed_link - Parse fixed-link property and return a dummy phy
- * @dev: pointer to net_device claiming the phy
- * @hndlr: Link state callback for the network device
- * @iface: PHY data interface type
- *
- * This function is a temporary stop-gap and will be removed soon.  It is
- * only to support the fs_enet, ucc_geth and gianfar Ethernet drivers.  Do
- * not call this function from new drivers.
- */
-struct phy_device *of_phy_connect_fixed_link(struct net_device *dev,
-                                            void (*hndlr)(struct net_device *),
-                                            phy_interface_t iface)
-{
-       struct device_node *net_np;
-       char bus_id[MII_BUS_ID_SIZE + 3];
-       struct phy_device *phy;
-       const __be32 *phy_id;
-       int sz;
-
-       if (!dev->dev.parent)
-               return NULL;
-
-       net_np = dev->dev.parent->of_node;
-       if (!net_np)
-               return NULL;
-
-       phy_id = of_get_property(net_np, "fixed-link", &sz);
-       if (!phy_id || sz < sizeof(*phy_id))
-               return NULL;
-
-       sprintf(bus_id, PHY_ID_FMT, "fixed-0", be32_to_cpu(phy_id[0]));
-
-       phy = phy_connect(dev, bus_id, hndlr, iface);
-       return IS_ERR(phy) ? NULL : phy;
-}
-EXPORT_SYMBOL(of_phy_connect_fixed_link);
-
 /**
  * of_phy_attach - Attach to a PHY without starting the state machine
  * @dev: pointer to net_device claiming the phy
@@ -301,3 +281,69 @@ struct phy_device *of_phy_attach(struct net_device *dev,
        return phy_attach_direct(dev, phy, flags, iface) ? NULL : phy;
 }
 EXPORT_SYMBOL(of_phy_attach);
+
+#if defined(CONFIG_FIXED_PHY)
+/*
+ * of_phy_is_fixed_link() and of_phy_register_fixed_link() must
+ * support two DT bindings:
+ * - the old DT binding, where 'fixed-link' was a property with 5
+ *   cells encoding various informations about the fixed PHY
+ * - the new DT binding, where 'fixed-link' is a sub-node of the
+ *   Ethernet device.
+ */
+bool of_phy_is_fixed_link(struct device_node *np)
+{
+       struct device_node *dn;
+       int len;
+
+       /* New binding */
+       dn = of_get_child_by_name(np, "fixed-link");
+       if (dn) {
+               of_node_put(dn);
+               return true;
+       }
+
+       /* Old binding */
+       if (of_get_property(np, "fixed-link", &len) &&
+           len == (5 * sizeof(__be32)))
+               return true;
+
+       return false;
+}
+EXPORT_SYMBOL(of_phy_is_fixed_link);
+
+int of_phy_register_fixed_link(struct device_node *np)
+{
+       struct fixed_phy_status status = {};
+       struct device_node *fixed_link_node;
+       const __be32 *fixed_link_prop;
+       int len;
+
+       /* New binding */
+       fixed_link_node = of_get_child_by_name(np, "fixed-link");
+       if (fixed_link_node) {
+               status.link = 1;
+               status.duplex = of_property_read_bool(np, "full-duplex");
+               if (of_property_read_u32(fixed_link_node, "speed", &status.speed))
+                       return -EINVAL;
+               status.pause = of_property_read_bool(np, "pause");
+               status.asym_pause = of_property_read_bool(np, "asym-pause");
+               of_node_put(fixed_link_node);
+               return fixed_phy_register(PHY_POLL, &status, np);
+       }
+
+       /* Old binding */
+       fixed_link_prop = of_get_property(np, "fixed-link", &len);
+       if (fixed_link_prop && len == (5 * sizeof(__be32))) {
+               status.link = 1;
+               status.duplex = be32_to_cpu(fixed_link_prop[1]);
+               status.speed = be32_to_cpu(fixed_link_prop[2]);
+               status.pause = be32_to_cpu(fixed_link_prop[3]);
+               status.asym_pause = be32_to_cpu(fixed_link_prop[4]);
+               return fixed_phy_register(PHY_POLL, &status, np);
+       }
+
+       return -ENODEV;
+}
+EXPORT_SYMBOL(of_phy_register_fixed_link);
+#endif
index e25d2bc898e5b6e4eb7b43e6df87129a2a19781e..296b0ec8744da915763f8444c2ae8e902376c33e 100644 (file)
@@ -142,7 +142,10 @@ static int ptp_clock_adjtime(struct posix_clock *pc, struct timex *tx)
                delta = ktime_to_ns(kt);
                err = ops->adjtime(ops, delta);
        } else if (tx->modes & ADJ_FREQUENCY) {
-               err = ops->adjfreq(ops, scaled_ppm_to_ppb(tx->freq));
+               s32 ppb = scaled_ppm_to_ppb(tx->freq);
+               if (ppb > ops->max_adj || ppb < -ops->max_adj)
+                       return -ERANGE;
+               err = ops->adjfreq(ops, ppb);
                ptp->dialed_frequency = tx->freq;
        } else if (tx->modes == 0) {
                tx->freq = ptp->dialed_frequency;
index fd7b3bd807896556d0743660069aff8a44c3e072..d837c3c5330fab5c2c77548f31996db3dacad763 100644 (file)
@@ -3348,7 +3348,7 @@ static int __init claw_init(void)
        }
        CLAW_DBF_TEXT(2, setup, "init_mod");
        claw_root_dev = root_device_register("claw");
-       ret = PTR_RET(claw_root_dev);
+       ret = PTR_ERR_OR_ZERO(claw_root_dev);
        if (ret)
                goto register_err;
        ret = ccw_driver_register(&claw_ccw_driver);
index 70b3a023100ef769180d8234f2ac39c3caa91232..03b6ad035577e28553da16fbf4481d9c249a9e6d 100644 (file)
@@ -1837,7 +1837,7 @@ static int __init ctcm_init(void)
        if (ret)
                goto out_err;
        ctcm_root_dev = root_device_register("ctcm");
-       ret = PTR_RET(ctcm_root_dev);
+       ret = PTR_ERR_OR_ZERO(ctcm_root_dev);
        if (ret)
                goto register_err;
        ret = ccw_driver_register(&ctcm_ccw_driver);
index 985b5dcbdac8b348dc6394898ad55bcddf9b8aa9..6bcfbbb20f04c6525fa3363e66422009fe6072b6 100644 (file)
@@ -34,8 +34,9 @@ static ssize_t ctcm_buffer_write(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t count)
 {
        struct net_device *ndev;
-       int bs1;
+       unsigned int bs1;
        struct ctcm_priv *priv = dev_get_drvdata(dev);
+       int rc;
 
        ndev = priv->channel[CTCM_READ]->netdev;
        if (!(priv && priv->channel[CTCM_READ] && ndev)) {
@@ -43,7 +44,9 @@ static ssize_t ctcm_buffer_write(struct device *dev,
                return -ENODEV;
        }
 
-       sscanf(buf, "%u", &bs1);
+       rc = sscanf(buf, "%u", &bs1);
+       if (rc != 1)
+               goto einval;
        if (bs1 > CTCM_BUFSIZE_LIMIT)
                                        goto einval;
        if (bs1 < (576 + LL_HEADER_LENGTH + 2))
@@ -143,13 +146,14 @@ static ssize_t ctcm_proto_show(struct device *dev,
 static ssize_t ctcm_proto_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t count)
 {
-       int value;
+       int value, rc;
        struct ctcm_priv *priv = dev_get_drvdata(dev);
 
        if (!priv)
                return -ENODEV;
-       sscanf(buf, "%u", &value);
-       if (!((value == CTCM_PROTO_S390)  ||
+       rc = sscanf(buf, "%d", &value);
+       if ((rc != 1) ||
+           !((value == CTCM_PROTO_S390)  ||
              (value == CTCM_PROTO_LINUX) ||
              (value == CTCM_PROTO_MPC) ||
              (value == CTCM_PROTO_OS390)))
index c461f2aac610ea6a8580c504c9a2fbb9dcb92979..0a7d87c372b8414e17c41e05b50cf974e5ba0bd3 100644 (file)
@@ -1943,14 +1943,16 @@ static ssize_t
 lcs_portno_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 {
         struct lcs_card *card;
-        int value;
+       int value, rc;
 
        card = dev_get_drvdata(dev);
 
         if (!card)
                 return 0;
 
-        sscanf(buf, "%u", &value);
+       rc = sscanf(buf, "%d", &value);
+       if (rc != 1)
+               return -EINVAL;
         /* TODO: sanity checks */
         card->portno = value;
 
@@ -1997,14 +1999,17 @@ static ssize_t
 lcs_timeout_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 {
         struct lcs_card *card;
-        int value;
+       unsigned int value;
+       int rc;
 
        card = dev_get_drvdata(dev);
 
         if (!card)
                 return 0;
 
-        sscanf(buf, "%u", &value);
+       rc = sscanf(buf, "%u", &value);
+       if (rc != 1)
+               return -EINVAL;
         /* TODO: sanity checks */
         card->lancmd_timeout = value;
 
@@ -2442,7 +2447,7 @@ __init lcs_init_module(void)
        if (rc)
                goto out_err;
        lcs_root_dev = root_device_register("lcs");
-       rc = PTR_RET(lcs_root_dev);
+       rc = PTR_ERR_OR_ZERO(lcs_root_dev);
        if (rc)
                goto register_err;
        rc = ccw_driver_register(&lcs_ccw_driver);
index 5333b2c018e781541905e855c7cf3ff0a5d84a9e..a2088af51cc5d809d7513f0c0cb628c0b81910b6 100644 (file)
@@ -268,10 +268,8 @@ static inline int qeth_is_ipa_enabled(struct qeth_ipa_info *ipa,
 #define QETH_NO_PRIO_QUEUEING 0
 #define QETH_PRIO_Q_ING_PREC  1
 #define QETH_PRIO_Q_ING_TOS   2
-#define IP_TOS_LOWDELAY 0x10
-#define IP_TOS_HIGHTHROUGHPUT 0x08
-#define IP_TOS_HIGHRELIABILITY 0x04
-#define IP_TOS_NOTIMPORTANT 0x02
+#define QETH_PRIO_Q_ING_SKB   3
+#define QETH_PRIO_Q_ING_VLAN  4
 
 /* Packing */
 #define QETH_LOW_WATERMARK_PACK  2
index e89f38c3117606dd8d4a83226c44baf2e4f22bb1..f54bec54d677635154f190fc73e26f896d17bd34 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/kthread.h>
 #include <linux/slab.h>
 #include <net/iucv/af_iucv.h>
+#include <net/dsfield.h>
 
 #include <asm/ebcdic.h>
 #include <asm/chpid.h>
@@ -1013,7 +1014,7 @@ static long __qeth_check_irb_error(struct ccw_device *cdev,
 
        card = CARD_FROM_CDEV(cdev);
 
-       if (!IS_ERR(irb))
+       if (!card || !IS_ERR(irb))
                return 0;
 
        switch (PTR_ERR(irb)) {
@@ -1029,7 +1030,7 @@ static long __qeth_check_irb_error(struct ccw_device *cdev,
                QETH_CARD_TEXT(card, 2, "ckirberr");
                QETH_CARD_TEXT_(card, 2, "  rc%d", -ETIMEDOUT);
                if (intparm == QETH_RCD_PARM) {
-                       if (card && (card->data.ccwdev == cdev)) {
+                       if (card->data.ccwdev == cdev) {
                                card->data.state = CH_STATE_DOWN;
                                wake_up(&card->wait_q);
                        }
@@ -3662,42 +3663,56 @@ void qeth_qdio_output_handler(struct ccw_device *ccwdev,
 }
 EXPORT_SYMBOL_GPL(qeth_qdio_output_handler);
 
+/**
+ * Note: Function assumes that we have 4 outbound queues.
+ */
 int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb,
                        int ipv, int cast_type)
 {
-       if (!ipv && (card->info.type == QETH_CARD_TYPE_OSD ||
-                    card->info.type == QETH_CARD_TYPE_OSX))
-               return card->qdio.default_out_queue;
-       switch (card->qdio.no_out_queues) {
-       case 4:
-               if (cast_type && card->info.is_multicast_different)
-                       return card->info.is_multicast_different &
-                               (card->qdio.no_out_queues - 1);
-               if (card->qdio.do_prio_queueing && (ipv == 4)) {
-                       const u8 tos = ip_hdr(skb)->tos;
-
-                       if (card->qdio.do_prio_queueing ==
-                               QETH_PRIO_Q_ING_TOS) {
-                               if (tos & IP_TOS_NOTIMPORTANT)
-                                       return 3;
-                               if (tos & IP_TOS_HIGHRELIABILITY)
-                                       return 2;
-                               if (tos & IP_TOS_HIGHTHROUGHPUT)
-                                       return 1;
-                               if (tos & IP_TOS_LOWDELAY)
-                                       return 0;
-                       }
-                       if (card->qdio.do_prio_queueing ==
-                               QETH_PRIO_Q_ING_PREC)
-                               return 3 - (tos >> 6);
-               } else if (card->qdio.do_prio_queueing && (ipv == 6)) {
-                       /* TODO: IPv6!!! */
+       __be16 *tci;
+       u8 tos;
+
+       if (cast_type && card->info.is_multicast_different)
+               return card->info.is_multicast_different &
+                       (card->qdio.no_out_queues - 1);
+
+       switch (card->qdio.do_prio_queueing) {
+       case QETH_PRIO_Q_ING_TOS:
+       case QETH_PRIO_Q_ING_PREC:
+               switch (ipv) {
+               case 4:
+                       tos = ipv4_get_dsfield(ip_hdr(skb));
+                       break;
+               case 6:
+                       tos = ipv6_get_dsfield(ipv6_hdr(skb));
+                       break;
+               default:
+                       return card->qdio.default_out_queue;
                }
-               return card->qdio.default_out_queue;
-       case 1: /* fallthrough for single-out-queue 1920-device */
+               if (card->qdio.do_prio_queueing == QETH_PRIO_Q_ING_PREC)
+                       return ~tos >> 6 & 3;
+               if (tos & IPTOS_MINCOST)
+                       return 3;
+               if (tos & IPTOS_RELIABILITY)
+                       return 2;
+               if (tos & IPTOS_THROUGHPUT)
+                       return 1;
+               if (tos & IPTOS_LOWDELAY)
+                       return 0;
+               break;
+       case QETH_PRIO_Q_ING_SKB:
+               if (skb->priority > 5)
+                       return 0;
+               return ~skb->priority >> 1 & 3;
+       case QETH_PRIO_Q_ING_VLAN:
+               tci = &((struct ethhdr *)skb->data)->h_proto;
+               if (*tci == ETH_P_8021Q)
+                       return ~*(tci + 1) >> (VLAN_PRIO_SHIFT + 1) & 3;
+               break;
        default:
-               return card->qdio.default_out_queue;
+               break;
        }
+       return card->qdio.default_out_queue;
 }
 EXPORT_SYMBOL_GPL(qeth_get_priority_queue);
 
@@ -5703,6 +5718,7 @@ int qeth_core_ethtool_get_settings(struct net_device *netdev,
        struct qeth_card *card = netdev->ml_priv;
        enum qeth_link_types link_type;
        struct carrier_info carrier_info;
+       u32 speed;
 
        if ((card->info.type == QETH_CARD_TYPE_IQD) || (card->info.guestlan))
                link_type = QETH_LINK_TYPE_10GBIT_ETH;
@@ -5717,28 +5733,29 @@ int qeth_core_ethtool_get_settings(struct net_device *netdev,
        case QETH_LINK_TYPE_FAST_ETH:
        case QETH_LINK_TYPE_LANE_ETH100:
                qeth_set_ecmd_adv_sup(ecmd, SPEED_100, PORT_TP);
-               ecmd->speed = SPEED_100;
+               speed = SPEED_100;
                ecmd->port = PORT_TP;
                break;
 
        case QETH_LINK_TYPE_GBIT_ETH:
        case QETH_LINK_TYPE_LANE_ETH1000:
                qeth_set_ecmd_adv_sup(ecmd, SPEED_1000, PORT_FIBRE);
-               ecmd->speed = SPEED_1000;
+               speed = SPEED_1000;
                ecmd->port = PORT_FIBRE;
                break;
 
        case QETH_LINK_TYPE_10GBIT_ETH:
                qeth_set_ecmd_adv_sup(ecmd, SPEED_10000, PORT_FIBRE);
-               ecmd->speed = SPEED_10000;
+               speed = SPEED_10000;
                ecmd->port = PORT_FIBRE;
                break;
 
        default:
                qeth_set_ecmd_adv_sup(ecmd, SPEED_10, PORT_TP);
-               ecmd->speed = SPEED_10;
+               speed = SPEED_10;
                ecmd->port = PORT_TP;
        }
+       ethtool_cmd_speed_set(ecmd, speed);
 
        /* Check if we can obtain more accurate information.     */
        /* If QUERY_CARD_INFO command is not supported or fails, */
@@ -5783,18 +5800,19 @@ int qeth_core_ethtool_get_settings(struct net_device *netdev,
 
        switch (carrier_info.port_speed) {
        case CARD_INFO_PORTS_10M:
-               ecmd->speed = SPEED_10;
+               speed = SPEED_10;
                break;
        case CARD_INFO_PORTS_100M:
-               ecmd->speed = SPEED_100;
+               speed = SPEED_100;
                break;
        case CARD_INFO_PORTS_1G:
-               ecmd->speed = SPEED_1000;
+               speed = SPEED_1000;
                break;
        case CARD_INFO_PORTS_10G:
-               ecmd->speed = SPEED_10000;
+               speed = SPEED_10000;
                break;
        }
+       ethtool_cmd_speed_set(ecmd, speed);
 
        return 0;
 }
@@ -5816,7 +5834,7 @@ static int __init qeth_core_init(void)
        if (rc)
                goto out_err;
        qeth_core_root_dev = root_device_register("qeth");
-       rc = PTR_RET(qeth_core_root_dev);
+       rc = PTR_ERR_OR_ZERO(qeth_core_root_dev);
        if (rc)
                goto register_err;
        qeth_core_header_cache = kmem_cache_create("qeth_hdr",
index 425c0ecf1f3b9fd2ae3c2c55f61b1ad10f08c028..8a25a2be9890e7e09af1c9845c5b9b9773472f00 100644 (file)
@@ -217,6 +217,10 @@ static ssize_t qeth_dev_prioqing_show(struct device *dev,
                return sprintf(buf, "%s\n", "by precedence");
        case QETH_PRIO_Q_ING_TOS:
                return sprintf(buf, "%s\n", "by type of service");
+       case QETH_PRIO_Q_ING_SKB:
+               return sprintf(buf, "%s\n", "by skb-priority");
+       case QETH_PRIO_Q_ING_VLAN:
+               return sprintf(buf, "%s\n", "by VLAN headers");
        default:
                return sprintf(buf, "always queue %i\n",
                               card->qdio.default_out_queue);
@@ -250,11 +254,23 @@ static ssize_t qeth_dev_prioqing_store(struct device *dev,
        }
 
        tmp = strsep((char **) &buf, "\n");
-       if (!strcmp(tmp, "prio_queueing_prec"))
+       if (!strcmp(tmp, "prio_queueing_prec")) {
                card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_PREC;
-       else if (!strcmp(tmp, "prio_queueing_tos"))
+               card->qdio.default_out_queue = QETH_DEFAULT_QUEUE;
+       } else if (!strcmp(tmp, "prio_queueing_skb")) {
+               card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_SKB;
+               card->qdio.default_out_queue = QETH_DEFAULT_QUEUE;
+       } else if (!strcmp(tmp, "prio_queueing_tos")) {
                card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_TOS;
-       else if (!strcmp(tmp, "no_prio_queueing:0")) {
+               card->qdio.default_out_queue = QETH_DEFAULT_QUEUE;
+       } else if (!strcmp(tmp, "prio_queueing_vlan")) {
+               if (!card->options.layer2) {
+                       rc = -ENOTSUPP;
+                       goto out;
+               }
+               card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_VLAN;
+               card->qdio.default_out_queue = QETH_DEFAULT_QUEUE;
+       } else if (!strcmp(tmp, "no_prio_queueing:0")) {
                card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING;
                card->qdio.default_out_queue = 0;
        } else if (!strcmp(tmp, "no_prio_queueing:1")) {
index 8dea3f12ccc1714defe7d4d65869817dd6b69135..5ef5b4f45758cd226bb58becde5a3fa81ff33bb2 100644 (file)
@@ -725,15 +725,20 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        int elements = 0;
        struct qeth_card *card = dev->ml_priv;
        struct sk_buff *new_skb = skb;
-       int ipv = qeth_get_ip_version(skb);
        int cast_type = qeth_l2_get_cast_type(card, skb);
-       struct qeth_qdio_out_q *queue = card->qdio.out_qs
-               [qeth_get_priority_queue(card, skb, ipv, cast_type)];
+       struct qeth_qdio_out_q *queue;
        int tx_bytes = skb->len;
        int data_offset = -1;
        int elements_needed = 0;
        int hd_len = 0;
 
+       if (card->qdio.do_prio_queueing || (cast_type &&
+                                       card->info.is_multicast_different))
+               queue = card->qdio.out_qs[qeth_get_priority_queue(card, skb,
+                                       qeth_get_ip_version(skb), cast_type)];
+       else
+               queue = card->qdio.out_qs[card->qdio.default_out_queue];
+
        if ((card->state != CARD_STATE_UP) || !card->lan_online) {
                card->stats.tx_carrier_errors++;
                goto tx_drop;
@@ -964,10 +969,9 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
        card->dev->watchdog_timeo = QETH_TX_TIMEOUT;
        card->dev->mtu = card->info.initial_mtu;
        card->dev->netdev_ops = &qeth_l2_netdev_ops;
-       if (card->info.type != QETH_CARD_TYPE_OSN)
-               SET_ETHTOOL_OPS(card->dev, &qeth_l2_ethtool_ops);
-       else
-               SET_ETHTOOL_OPS(card->dev, &qeth_l2_osn_ops);
+       card->dev->ethtool_ops =
+               (card->info.type != QETH_CARD_TYPE_OSN) ?
+               &qeth_l2_ethtool_ops : &qeth_l2_osn_ops;
        card->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
        card->info.broadcast_capable = 1;
        qeth_l2_request_initial_mac(card);
index 3524d34ff694c273afefc7d85bfa17b6bce38af9..14e0b5810e8c1cde11a8835552158bab8b59dd1a 100644 (file)
@@ -63,7 +63,7 @@ void qeth_l3_ipaddr4_to_string(const __u8 *addr, char *buf)
 int qeth_l3_string_to_ipaddr4(const char *buf, __u8 *addr)
 {
        int count = 0, rc = 0;
-       int in[4];
+       unsigned int in[4];
        char c;
 
        rc = sscanf(buf, "%u.%u.%u.%u%c",
@@ -1659,7 +1659,7 @@ static void qeth_l3_add_vlan_mc(struct qeth_card *card)
        for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) {
                struct net_device *netdev;
 
-               netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q),
+               netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q),
                                              vid);
                if (netdev == NULL ||
                    !(netdev->flags & IFF_UP))
@@ -1721,7 +1721,7 @@ static void qeth_l3_add_vlan_mc6(struct qeth_card *card)
        for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) {
                struct net_device *netdev;
 
-               netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q),
+               netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q),
                                              vid);
                if (netdev == NULL ||
                    !(netdev->flags & IFF_UP))
@@ -1766,7 +1766,7 @@ static void qeth_l3_free_vlan_addresses4(struct qeth_card *card,
 
        QETH_CARD_TEXT(card, 4, "frvaddr4");
 
-       netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q), vid);
+       netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), vid);
        if (!netdev)
                return;
        in_dev = in_dev_get(netdev);
@@ -1796,7 +1796,7 @@ static void qeth_l3_free_vlan_addresses6(struct qeth_card *card,
 
        QETH_CARD_TEXT(card, 4, "frvaddr6");
 
-       netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q), vid);
+       netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q), vid);
        if (!netdev)
                return;
        in6_dev = in6_dev_get(netdev);
@@ -2089,7 +2089,7 @@ static int qeth_l3_verify_vlan_dev(struct net_device *dev,
                struct net_device *netdev;
 
                rcu_read_lock();
-               netdev = __vlan_find_dev_deep(card->dev, htons(ETH_P_8021Q),
+               netdev = __vlan_find_dev_deep_rcu(card->dev, htons(ETH_P_8021Q),
                                              vid);
                rcu_read_unlock();
                if (netdev == dev) {
@@ -2926,8 +2926,11 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        struct sk_buff *new_skb = NULL;
        int ipv = qeth_get_ip_version(skb);
        int cast_type = qeth_l3_get_cast_type(card, skb);
-       struct qeth_qdio_out_q *queue = card->qdio.out_qs
-               [qeth_get_priority_queue(card, skb, ipv, cast_type)];
+       struct qeth_qdio_out_q *queue =
+               card->qdio.out_qs[card->qdio.do_prio_queueing
+                       || (cast_type && card->info.is_multicast_different) ?
+                       qeth_get_priority_queue(card, skb, ipv, cast_type) :
+                       card->qdio.default_out_queue];
        int tx_bytes = skb->len;
        bool large_send;
        int data_offset = -1;
@@ -3298,7 +3301,7 @@ static int qeth_l3_setup_netdev(struct qeth_card *card)
        card->dev->ml_priv = card;
        card->dev->watchdog_timeo = QETH_TX_TIMEOUT;
        card->dev->mtu = card->info.initial_mtu;
-       SET_ETHTOOL_OPS(card->dev, &qeth_l3_ethtool_ops);
+       card->dev->ethtool_ops = &qeth_l3_ethtool_ops;
        card->dev->features |=  NETIF_F_HW_VLAN_CTAG_TX |
                                NETIF_F_HW_VLAN_CTAG_RX |
                                NETIF_F_HW_VLAN_CTAG_FILTER;
index 02832d64d9187ea0ece202820bd11e1902979cdb..baca5897039fcaf23ea001b69eaef368564762c9 100644 (file)
@@ -1773,6 +1773,7 @@ config SCSI_BFA_FC
 config SCSI_VIRTIO
        tristate "virtio-scsi support"
        depends on VIRTIO
+       select BLK_DEV_INTEGRITY
        help
           This is the virtual HBA driver for virtio.  If the kernel will
           be used in a virtual machine, say Y or M.
index 11854845393bf9cc13688ff3895339997822fa61..a669f2d11c314e380eb2aa90714b91eb0064f781 100644 (file)
@@ -244,7 +244,7 @@ iscsi_sw_tcp_conn_restore_callbacks(struct iscsi_conn *conn)
        sk->sk_data_ready   = tcp_sw_conn->old_data_ready;
        sk->sk_state_change = tcp_sw_conn->old_state_change;
        sk->sk_write_space  = tcp_sw_conn->old_write_space;
-       sk->sk_no_check  = 0;
+       sk->sk_no_check_tx = 0;
        write_unlock_bh(&sk->sk_callback_lock);
 }
 
index ecd7bd304efebb51ce99580e7810edb6a3cf84be..3d1bc67bac9dc58ac11b9785694e568b3c3ab285 100644 (file)
@@ -338,7 +338,7 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
        struct iscsi_session *session = conn->session;
        struct scsi_cmnd *sc = task->sc;
        struct iscsi_scsi_req *hdr;
-       unsigned hdrlength, cmd_len;
+       unsigned hdrlength, cmd_len, transfer_length;
        itt_t itt;
        int rc;
 
@@ -391,11 +391,11 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
        if (scsi_get_prot_op(sc) != SCSI_PROT_NORMAL)
                task->protected = true;
 
+       transfer_length = scsi_transfer_length(sc);
+       hdr->data_length = cpu_to_be32(transfer_length);
        if (sc->sc_data_direction == DMA_TO_DEVICE) {
-               unsigned out_len = scsi_out(sc)->length;
                struct iscsi_r2t_info *r2t = &task->unsol_r2t;
 
-               hdr->data_length = cpu_to_be32(out_len);
                hdr->flags |= ISCSI_FLAG_CMD_WRITE;
                /*
                 * Write counters:
@@ -414,18 +414,19 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
                memset(r2t, 0, sizeof(*r2t));
 
                if (session->imm_data_en) {
-                       if (out_len >= session->first_burst)
+                       if (transfer_length >= session->first_burst)
                                task->imm_count = min(session->first_burst,
                                                        conn->max_xmit_dlength);
                        else
-                               task->imm_count = min(out_len,
-                                                       conn->max_xmit_dlength);
+                               task->imm_count = min(transfer_length,
+                                                     conn->max_xmit_dlength);
                        hton24(hdr->dlength, task->imm_count);
                } else
                        zero_data(hdr->dlength);
 
                if (!session->initial_r2t_en) {
-                       r2t->data_length = min(session->first_burst, out_len) -
+                       r2t->data_length = min(session->first_burst,
+                                              transfer_length) -
                                               task->imm_count;
                        r2t->data_offset = task->imm_count;
                        r2t->ttt = cpu_to_be32(ISCSI_RESERVED_TAG);
@@ -438,7 +439,6 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
        } else {
                hdr->flags |= ISCSI_FLAG_CMD_FINAL;
                zero_data(hdr->dlength);
-               hdr->data_length = cpu_to_be32(scsi_in(sc)->length);
 
                if (sc->sc_data_direction == DMA_FROM_DEVICE)
                        hdr->flags |= ISCSI_FLAG_CMD_READ;
@@ -466,7 +466,7 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
                          scsi_bidi_cmnd(sc) ? "bidirectional" :
                          sc->sc_data_direction == DMA_TO_DEVICE ?
                          "write" : "read", conn->id, sc, sc->cmnd[0],
-                         task->itt, scsi_bufflen(sc),
+                         task->itt, transfer_length,
                          scsi_bidi_cmnd(sc) ? scsi_in(sc)->length : 0,
                          session->cmdsn,
                          session->max_cmdsn - session->exp_cmdsn + 1);
index b1d10f9935c7caac0f85cc7cdb17ccee12c9e8df..8d85ed8d89170265647938f5bfe9fc8f8d77d185 100644 (file)
@@ -104,7 +104,6 @@ static void qlt_reject_free_srr_imm(struct scsi_qla_host *ha,
 /*
  * Global Variables
  */
-static struct kmem_cache *qla_tgt_cmd_cachep;
 static struct kmem_cache *qla_tgt_mgmt_cmd_cachep;
 static mempool_t *qla_tgt_mgmt_cmd_mempool;
 static struct workqueue_struct *qla_tgt_wq;
@@ -2705,6 +2704,8 @@ static void qlt_send_term_exchange(struct scsi_qla_host *vha,
 
 void qlt_free_cmd(struct qla_tgt_cmd *cmd)
 {
+       struct qla_tgt_sess *sess = cmd->sess;
+
        ql_dbg(ql_dbg_tgt, cmd->vha, 0xe074,
            "%s: se_cmd[%p] ox_id %04x\n",
            __func__, &cmd->se_cmd,
@@ -2713,7 +2714,12 @@ void qlt_free_cmd(struct qla_tgt_cmd *cmd)
        BUG_ON(cmd->sg_mapped);
        if (unlikely(cmd->free_sg))
                kfree(cmd->sg);
-       kmem_cache_free(qla_tgt_cmd_cachep, cmd);
+
+       if (!sess || !sess->se_sess) {
+               WARN_ON(1);
+               return;
+       }
+       percpu_ida_free(&sess->se_sess->sess_tag_pool, cmd->se_cmd.map_tag);
 }
 EXPORT_SYMBOL(qlt_free_cmd);
 
@@ -3075,13 +3081,12 @@ static struct qla_tgt_sess *qlt_make_local_sess(struct scsi_qla_host *,
 /*
  * Process context for I/O path into tcm_qla2xxx code
  */
-static void qlt_do_work(struct work_struct *work)
+static void __qlt_do_work(struct qla_tgt_cmd *cmd)
 {
-       struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work);
        scsi_qla_host_t *vha = cmd->vha;
        struct qla_hw_data *ha = vha->hw;
        struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
-       struct qla_tgt_sess *sess = NULL;
+       struct qla_tgt_sess *sess = cmd->sess;
        struct atio_from_isp *atio = &cmd->atio;
        unsigned char *cdb;
        unsigned long flags;
@@ -3091,41 +3096,6 @@ static void qlt_do_work(struct work_struct *work)
        if (tgt->tgt_stop)
                goto out_term;
 
-       spin_lock_irqsave(&ha->hardware_lock, flags);
-       sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha,
-           atio->u.isp24.fcp_hdr.s_id);
-       /* Do kref_get() before dropping qla_hw_data->hardware_lock. */
-       if (sess)
-               kref_get(&sess->se_sess->sess_kref);
-       spin_unlock_irqrestore(&ha->hardware_lock, flags);
-
-       if (unlikely(!sess)) {
-               uint8_t *s_id = atio->u.isp24.fcp_hdr.s_id;
-
-               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf022,
-                       "qla_target(%d): Unable to find wwn login"
-                       " (s_id %x:%x:%x), trying to create it manually\n",
-                       vha->vp_idx, s_id[0], s_id[1], s_id[2]);
-
-               if (atio->u.raw.entry_count > 1) {
-                       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf023,
-                               "Dropping multy entry cmd %p\n", cmd);
-                       goto out_term;
-               }
-
-               mutex_lock(&vha->vha_tgt.tgt_mutex);
-               sess = qlt_make_local_sess(vha, s_id);
-               /* sess has an extra creation ref. */
-               mutex_unlock(&vha->vha_tgt.tgt_mutex);
-
-               if (!sess)
-                       goto out_term;
-       }
-
-       cmd->sess = sess;
-       cmd->loop_id = sess->loop_id;
-       cmd->conf_compl_supported = sess->conf_compl_supported;
-
        cdb = &atio->u.isp24.fcp_cmnd.cdb[0];
        cmd->tag = atio->u.isp24.exchange_addr;
        cmd->unpacked_lun = scsilun_to_int(
@@ -3153,8 +3123,8 @@ static void qlt_do_work(struct work_struct *work)
                cmd, &cmd->se_cmd, cmd->unpacked_lun, cmd->tag, data_length,
                cmd->atio.u.isp24.fcp_hdr.ox_id);
 
-       ret = vha->hw->tgt.tgt_ops->handle_cmd(vha, cmd, cdb, data_length,
-           fcp_task_attr, data_dir, bidi);
+       ret = ha->tgt.tgt_ops->handle_cmd(vha, cmd, cdb, data_length,
+                                         fcp_task_attr, data_dir, bidi);
        if (ret != 0)
                goto out_term;
        /*
@@ -3173,17 +3143,114 @@ static void qlt_do_work(struct work_struct *work)
         */
        spin_lock_irqsave(&ha->hardware_lock, flags);
        qlt_send_term_exchange(vha, NULL, &cmd->atio, 1);
-       kmem_cache_free(qla_tgt_cmd_cachep, cmd);
-       if (sess)
+       percpu_ida_free(&sess->se_sess->sess_tag_pool, cmd->se_cmd.map_tag);
+       ha->tgt.tgt_ops->put_sess(sess);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+static void qlt_do_work(struct work_struct *work)
+{
+       struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work);
+
+       __qlt_do_work(cmd);
+}
+
+static struct qla_tgt_cmd *qlt_get_tag(scsi_qla_host_t *vha,
+                                      struct qla_tgt_sess *sess,
+                                      struct atio_from_isp *atio)
+{
+       struct se_session *se_sess = sess->se_sess;
+       struct qla_tgt_cmd *cmd;
+       int tag;
+
+       tag = percpu_ida_alloc(&se_sess->sess_tag_pool, TASK_RUNNING);
+       if (tag < 0)
+               return NULL;
+
+       cmd = &((struct qla_tgt_cmd *)se_sess->sess_cmd_map)[tag];
+       memset(cmd, 0, sizeof(struct qla_tgt_cmd));
+
+       memcpy(&cmd->atio, atio, sizeof(*atio));
+       cmd->state = QLA_TGT_STATE_NEW;
+       cmd->tgt = vha->vha_tgt.qla_tgt;
+       cmd->vha = vha;
+       cmd->se_cmd.map_tag = tag;
+       cmd->sess = sess;
+       cmd->loop_id = sess->loop_id;
+       cmd->conf_compl_supported = sess->conf_compl_supported;
+
+       return cmd;
+}
+
+static void qlt_send_busy(struct scsi_qla_host *, struct atio_from_isp *,
+                         uint16_t);
+
+static void qlt_create_sess_from_atio(struct work_struct *work)
+{
+       struct qla_tgt_sess_op *op = container_of(work,
+                                       struct qla_tgt_sess_op, work);
+       scsi_qla_host_t *vha = op->vha;
+       struct qla_hw_data *ha = vha->hw;
+       struct qla_tgt_sess *sess;
+       struct qla_tgt_cmd *cmd;
+       unsigned long flags;
+       uint8_t *s_id = op->atio.u.isp24.fcp_hdr.s_id;
+
+       ql_dbg(ql_dbg_tgt_mgt, vha, 0xf022,
+               "qla_target(%d): Unable to find wwn login"
+               " (s_id %x:%x:%x), trying to create it manually\n",
+               vha->vp_idx, s_id[0], s_id[1], s_id[2]);
+
+       if (op->atio.u.raw.entry_count > 1) {
+               ql_dbg(ql_dbg_tgt_mgt, vha, 0xf023,
+                       "Dropping multy entry atio %p\n", &op->atio);
+               goto out_term;
+       }
+
+       mutex_lock(&vha->vha_tgt.tgt_mutex);
+       sess = qlt_make_local_sess(vha, s_id);
+       /* sess has an extra creation ref. */
+       mutex_unlock(&vha->vha_tgt.tgt_mutex);
+
+       if (!sess)
+               goto out_term;
+       /*
+        * Now obtain a pre-allocated session tag using the original op->atio
+        * packet header, and dispatch into __qlt_do_work() using the existing
+        * process context.
+        */
+       cmd = qlt_get_tag(vha, sess, &op->atio);
+       if (!cmd) {
+               spin_lock_irqsave(&ha->hardware_lock, flags);
+               qlt_send_busy(vha, &op->atio, SAM_STAT_BUSY);
                ha->tgt.tgt_ops->put_sess(sess);
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+               kfree(op);
+               return;
+       }
+       /*
+        * __qlt_do_work() will call ha->tgt.tgt_ops->put_sess() to release
+        * the extra reference taken above by qlt_make_local_sess()
+        */
+       __qlt_do_work(cmd);
+       kfree(op);
+       return;
+
+out_term:
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       qlt_send_term_exchange(vha, NULL, &op->atio, 1);
        spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       kfree(op);
+
 }
 
 /* ha->hardware_lock supposed to be held on entry */
 static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha,
        struct atio_from_isp *atio)
 {
+       struct qla_hw_data *ha = vha->hw;
        struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
+       struct qla_tgt_sess *sess;
        struct qla_tgt_cmd *cmd;
 
        if (unlikely(tgt->tgt_stop)) {
@@ -3192,18 +3259,31 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha,
                return -EFAULT;
        }
 
-       cmd = kmem_cache_zalloc(qla_tgt_cmd_cachep, GFP_ATOMIC);
+       sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha, atio->u.isp24.fcp_hdr.s_id);
+       if (unlikely(!sess)) {
+               struct qla_tgt_sess_op *op = kzalloc(sizeof(struct qla_tgt_sess_op),
+                                                    GFP_ATOMIC);
+               if (!op)
+                       return -ENOMEM;
+
+               memcpy(&op->atio, atio, sizeof(*atio));
+               INIT_WORK(&op->work, qlt_create_sess_from_atio);
+               queue_work(qla_tgt_wq, &op->work);
+               return 0;
+       }
+       /*
+        * Do kref_get() before returning + dropping qla_hw_data->hardware_lock.
+        */
+       kref_get(&sess->se_sess->sess_kref);
+
+       cmd = qlt_get_tag(vha, sess, atio);
        if (!cmd) {
                ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05e,
                    "qla_target(%d): Allocation of cmd failed\n", vha->vp_idx);
+               ha->tgt.tgt_ops->put_sess(sess);
                return -ENOMEM;
        }
 
-       memcpy(&cmd->atio, atio, sizeof(*atio));
-       cmd->state = QLA_TGT_STATE_NEW;
-       cmd->tgt = vha->vha_tgt.qla_tgt;
-       cmd->vha = vha;
-
        INIT_WORK(&cmd->work, qlt_do_work);
        queue_work(qla_tgt_wq, &cmd->work);
        return 0;
@@ -5501,23 +5581,13 @@ int __init qlt_init(void)
        if (!QLA_TGT_MODE_ENABLED())
                return 0;
 
-       qla_tgt_cmd_cachep = kmem_cache_create("qla_tgt_cmd_cachep",
-           sizeof(struct qla_tgt_cmd), __alignof__(struct qla_tgt_cmd), 0,
-           NULL);
-       if (!qla_tgt_cmd_cachep) {
-               ql_log(ql_log_fatal, NULL, 0xe06c,
-                   "kmem_cache_create for qla_tgt_cmd_cachep failed\n");
-               return -ENOMEM;
-       }
-
        qla_tgt_mgmt_cmd_cachep = kmem_cache_create("qla_tgt_mgmt_cmd_cachep",
            sizeof(struct qla_tgt_mgmt_cmd), __alignof__(struct
            qla_tgt_mgmt_cmd), 0, NULL);
        if (!qla_tgt_mgmt_cmd_cachep) {
                ql_log(ql_log_fatal, NULL, 0xe06d,
                    "kmem_cache_create for qla_tgt_mgmt_cmd_cachep failed\n");
-               ret = -ENOMEM;
-               goto out;
+               return -ENOMEM;
        }
 
        qla_tgt_mgmt_cmd_mempool = mempool_create(25, mempool_alloc_slab,
@@ -5545,8 +5615,6 @@ int __init qlt_init(void)
        mempool_destroy(qla_tgt_mgmt_cmd_mempool);
 out_mgmt_cmd_cachep:
        kmem_cache_destroy(qla_tgt_mgmt_cmd_cachep);
-out:
-       kmem_cache_destroy(qla_tgt_cmd_cachep);
        return ret;
 }
 
@@ -5558,5 +5626,4 @@ void qlt_exit(void)
        destroy_workqueue(qla_tgt_wq);
        mempool_destroy(qla_tgt_mgmt_cmd_mempool);
        kmem_cache_destroy(qla_tgt_mgmt_cmd_cachep);
-       kmem_cache_destroy(qla_tgt_cmd_cachep);
 }
index f873e10451d29758ffd65b87a7b400af301e38c7..5c9f185a8ebd8384df5d4d4010e27c9574001238 100644 (file)
@@ -870,6 +870,12 @@ struct qla_tgt {
        struct list_head tgt_list_entry;
 };
 
+struct qla_tgt_sess_op {
+       struct scsi_qla_host *vha;
+       struct atio_from_isp atio;
+       struct work_struct work;
+};
+
 /*
  * Equivilant to IT Nexus (Initiator-Target)
  */
index 896cb23adb77f2e0fcb32de955a4c1c1ec5c035a..e2beab962096cd10d18cf1df63920c79fad216b3 100644 (file)
@@ -1501,6 +1501,8 @@ static int tcm_qla2xxx_check_initiator_node_acl(
        struct qla_tgt_sess *sess = qla_tgt_sess;
        unsigned char port_name[36];
        unsigned long flags;
+       int num_tags = (ha->fw_xcb_count) ? ha->fw_xcb_count :
+                      TCM_QLA2XXX_DEFAULT_TAGS;
 
        lport = vha->vha_tgt.target_lport_ptr;
        if (!lport) {
@@ -1518,7 +1520,9 @@ static int tcm_qla2xxx_check_initiator_node_acl(
        }
        se_tpg = &tpg->se_tpg;
 
-       se_sess = transport_init_session(TARGET_PROT_NORMAL);
+       se_sess = transport_init_session_tags(num_tags,
+                                             sizeof(struct qla_tgt_cmd),
+                                             TARGET_PROT_NORMAL);
        if (IS_ERR(se_sess)) {
                pr_err("Unable to initialize struct se_session\n");
                return PTR_ERR(se_sess);
index 33aaac8c7d5936bbdd3e2d9f934f2c92f2083331..10c002145648cfb2deeda14c0d83568025ec5bef 100644 (file)
@@ -4,6 +4,11 @@
 #define TCM_QLA2XXX_VERSION    "v0.1"
 /* length of ASCII WWPNs including pad */
 #define TCM_QLA2XXX_NAMELEN    32
+/*
+ * Number of pre-allocated per-session tags, based upon the worst-case
+ * per port number of iocbs
+ */
+#define TCM_QLA2XXX_DEFAULT_TAGS 2088
 
 #include "qla_target.h"
 
index 99fdb94039442b50694e66d53f63cab55a5efdae..89ee5929eb6de4060536e89885aba5f13f19577c 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/virtio_config.h>
 #include <linux/virtio_scsi.h>
 #include <linux/cpu.h>
+#include <linux/blkdev.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_cmnd.h>
@@ -37,6 +38,7 @@ struct virtio_scsi_cmd {
        struct completion *comp;
        union {
                struct virtio_scsi_cmd_req       cmd;
+               struct virtio_scsi_cmd_req_pi    cmd_pi;
                struct virtio_scsi_ctrl_tmf_req  tmf;
                struct virtio_scsi_ctrl_an_req   an;
        } req;
@@ -399,7 +401,7 @@ static int virtscsi_add_cmd(struct virtqueue *vq,
                            size_t req_size, size_t resp_size)
 {
        struct scsi_cmnd *sc = cmd->sc;
-       struct scatterlist *sgs[4], req, resp;
+       struct scatterlist *sgs[6], req, resp;
        struct sg_table *out, *in;
        unsigned out_num = 0, in_num = 0;
 
@@ -417,16 +419,24 @@ static int virtscsi_add_cmd(struct virtqueue *vq,
        sgs[out_num++] = &req;
 
        /* Data-out buffer.  */
-       if (out)
+       if (out) {
+               /* Place WRITE protection SGLs before Data OUT payload */
+               if (scsi_prot_sg_count(sc))
+                       sgs[out_num++] = scsi_prot_sglist(sc);
                sgs[out_num++] = out->sgl;
+       }
 
        /* Response header.  */
        sg_init_one(&resp, &cmd->resp, resp_size);
        sgs[out_num + in_num++] = &resp;
 
        /* Data-in buffer */
-       if (in)
+       if (in) {
+               /* Place READ protection SGLs before Data IN payload */
+               if (scsi_prot_sg_count(sc))
+                       sgs[out_num + in_num++] = scsi_prot_sglist(sc);
                sgs[out_num + in_num++] = in->sgl;
+       }
 
        return virtqueue_add_sgs(vq, sgs, out_num, in_num, cmd, GFP_ATOMIC);
 }
@@ -451,12 +461,45 @@ static int virtscsi_kick_cmd(struct virtio_scsi_vq *vq,
        return err;
 }
 
+static void virtio_scsi_init_hdr(struct virtio_scsi_cmd_req *cmd,
+                                struct scsi_cmnd *sc)
+{
+       cmd->lun[0] = 1;
+       cmd->lun[1] = sc->device->id;
+       cmd->lun[2] = (sc->device->lun >> 8) | 0x40;
+       cmd->lun[3] = sc->device->lun & 0xff;
+       cmd->tag = (unsigned long)sc;
+       cmd->task_attr = VIRTIO_SCSI_S_SIMPLE;
+       cmd->prio = 0;
+       cmd->crn = 0;
+}
+
+static void virtio_scsi_init_hdr_pi(struct virtio_scsi_cmd_req_pi *cmd_pi,
+                                   struct scsi_cmnd *sc)
+{
+       struct request *rq = sc->request;
+       struct blk_integrity *bi;
+
+       virtio_scsi_init_hdr((struct virtio_scsi_cmd_req *)cmd_pi, sc);
+
+       if (!rq || !scsi_prot_sg_count(sc))
+               return;
+
+       bi = blk_get_integrity(rq->rq_disk);
+
+       if (sc->sc_data_direction == DMA_TO_DEVICE)
+               cmd_pi->pi_bytesout = blk_rq_sectors(rq) * bi->tuple_size;
+       else if (sc->sc_data_direction == DMA_FROM_DEVICE)
+               cmd_pi->pi_bytesin = blk_rq_sectors(rq) * bi->tuple_size;
+}
+
 static int virtscsi_queuecommand(struct virtio_scsi *vscsi,
                                 struct virtio_scsi_vq *req_vq,
                                 struct scsi_cmnd *sc)
 {
        struct Scsi_Host *shost = virtio_scsi_host(vscsi->vdev);
        struct virtio_scsi_cmd *cmd = scsi_cmd_priv(sc);
+       int req_size;
 
        BUG_ON(scsi_sg_count(sc) > shost->sg_tablesize);
 
@@ -468,22 +511,20 @@ static int virtscsi_queuecommand(struct virtio_scsi *vscsi,
 
        memset(cmd, 0, sizeof(*cmd));
        cmd->sc = sc;
-       cmd->req.cmd = (struct virtio_scsi_cmd_req){
-               .lun[0] = 1,
-               .lun[1] = sc->device->id,
-               .lun[2] = (sc->device->lun >> 8) | 0x40,
-               .lun[3] = sc->device->lun & 0xff,
-               .tag = (unsigned long)sc,
-               .task_attr = VIRTIO_SCSI_S_SIMPLE,
-               .prio = 0,
-               .crn = 0,
-       };
 
        BUG_ON(sc->cmd_len > VIRTIO_SCSI_CDB_SIZE);
-       memcpy(cmd->req.cmd.cdb, sc->cmnd, sc->cmd_len);
 
-       if (virtscsi_kick_cmd(req_vq, cmd,
-                             sizeof cmd->req.cmd, sizeof cmd->resp.cmd) != 0)
+       if (virtio_has_feature(vscsi->vdev, VIRTIO_SCSI_F_T10_PI)) {
+               virtio_scsi_init_hdr_pi(&cmd->req.cmd_pi, sc);
+               memcpy(cmd->req.cmd_pi.cdb, sc->cmnd, sc->cmd_len);
+               req_size = sizeof(cmd->req.cmd_pi);
+       } else {
+               virtio_scsi_init_hdr(&cmd->req.cmd, sc);
+               memcpy(cmd->req.cmd.cdb, sc->cmnd, sc->cmd_len);
+               req_size = sizeof(cmd->req.cmd);
+       }
+
+       if (virtscsi_kick_cmd(req_vq, cmd, req_size, sizeof(cmd->resp.cmd)) != 0)
                return SCSI_MLQUEUE_HOST_BUSY;
        return 0;
 }
@@ -820,7 +861,7 @@ static int virtscsi_probe(struct virtio_device *vdev)
 {
        struct Scsi_Host *shost;
        struct virtio_scsi *vscsi;
-       int err;
+       int err, host_prot;
        u32 sg_elems, num_targets;
        u32 cmd_per_lun;
        u32 num_queues;
@@ -870,6 +911,16 @@ static int virtscsi_probe(struct virtio_device *vdev)
        shost->max_id = num_targets;
        shost->max_channel = 0;
        shost->max_cmd_len = VIRTIO_SCSI_CDB_SIZE;
+
+       if (virtio_has_feature(vdev, VIRTIO_SCSI_F_T10_PI)) {
+               host_prot = SHOST_DIF_TYPE1_PROTECTION | SHOST_DIF_TYPE2_PROTECTION |
+                           SHOST_DIF_TYPE3_PROTECTION | SHOST_DIX_TYPE1_PROTECTION |
+                           SHOST_DIX_TYPE2_PROTECTION | SHOST_DIX_TYPE3_PROTECTION;
+
+               scsi_host_set_prot(shost, host_prot);
+               scsi_host_set_guard(shost, SHOST_DIX_GUARD_CRC);
+       }
+
        err = scsi_add_host(shost, &vdev->dev);
        if (err)
                goto scsi_add_host_failed;
@@ -939,6 +990,7 @@ static struct virtio_device_id id_table[] = {
 static unsigned int features[] = {
        VIRTIO_SCSI_F_HOTPLUG,
        VIRTIO_SCSI_F_CHANGE,
+       VIRTIO_SCSI_F_T10_PI,
 };
 
 static struct virtio_driver virtio_scsi_driver = {
index 0901ef5d6e8ad4af2868c9f70b76d37513bfbfab..08356b6955a4dc4cb618fc2ff4de1f9169adad2f 100644 (file)
@@ -4605,7 +4605,7 @@ static int et131x_pci_setup(struct pci_dev *pdev,
        netdev->netdev_ops     = &et131x_netdev_ops;
 
        SET_NETDEV_DEV(netdev, &pdev->dev);
-       SET_ETHTOOL_OPS(netdev, &et131x_ethtool_ops);
+       netdev->ethtool_ops = &et131x_ethtool_ops;
 
        adapter = et131x_adapter_init(netdev, pdev);
 
index d6421b9b5981c2265a1c796a03ade4ec5416e878..a6158bef58e54c63dc19ebfcb3ef89daf2b0874e 100644 (file)
@@ -2249,7 +2249,7 @@ struct net_device *init_ft1000_card(struct pcmcia_device *link,
 
        ft1000InitProc(dev);
        ft1000_card_present = 1;
-       SET_ETHTOOL_OPS(dev, &ops);
+       dev->ethtool_ops = &ops;
        printk(KERN_INFO "ft1000: %s: addr 0x%04lx irq %d, MAC addr %pM\n",
                        dev->name, dev->base_addr, dev->irq, dev->dev_addr);
        return dev;
index ded31ea6bd39545382d4d64b30cc523c18bb1d1f..cbf455d66f73e2942f5218450e367f2d65102752 100644 (file)
@@ -396,7 +396,7 @@ static void iss_video_buf_queue(struct vb2_buffer *vb)
        }
 }
 
-static struct vb2_ops iss_video_vb2ops = {
+static const struct vb2_ops iss_video_vb2ops = {
        .queue_setup    = iss_video_queue_setup,
        .buf_prepare    = iss_video_buf_prepare,
        .buf_queue      = iss_video_buf_queue,
index 75d7c63cb41328e8f88a86fdbf93ee091a147abd..e320d6bae913ed44be09d219e30af34e84f64dc2 100644 (file)
@@ -1067,7 +1067,7 @@ static int xlr_net_probe(struct platform_device *pdev)
        xlr_set_rx_mode(ndev);
 
        priv->num_rx_desc += MAX_NUM_DESC_SPILL;
-       SET_ETHTOOL_OPS(ndev, &xlr_ethtool_ops);
+       ndev->ethtool_ops = &xlr_ethtool_ops;
        SET_NETDEV_DEV(ndev, &pdev->dev);
 
        /* Common registers, do one time initialization */
index ff7214aac9dd6ed05c524c69fa23e35767d2dcce..da9dd6bc56600f2fe094df6a59d3836ad491be72 100644 (file)
@@ -469,7 +469,7 @@ int cvm_oct_common_init(struct net_device *dev)
 
        /* We do our own locking, Linux doesn't need to */
        dev->features |= NETIF_F_LLTX;
-       SET_ETHTOOL_OPS(dev, &cvm_oct_ethtool_ops);
+       dev->ethtool_ops = &cvm_oct_ethtool_ops;
 
        cvm_oct_phy_setup_device(dev);
        cvm_oct_set_mac_filter(dev);
index 76ea356163b68c0c41000980b98a30e6f95ca009..7f6accd59986772717d99d0b05f14c8a1adaaa94 100644 (file)
@@ -322,7 +322,7 @@ static void _rtl_add_wowlan_patterns(struct ieee80211_hw *hw,
        struct rtl_mac *mac = &(rtlpriv->mac80211);
        struct cfg80211_pkt_pattern *patterns = wow->patterns;
        struct rtl_wow_pattern rtl_pattern;
-       u8 *pattern_os, *mask_os;
+       const u8 *pattern_os, *mask_os;
        u8 mask[MAX_WOL_BIT_MASK_SIZE] = {0};
        u8 content[MAX_WOL_PATTERN_SIZE] = {0};
        u8 broadcast_addr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
@@ -1561,7 +1561,7 @@ static void rtl_op_rfkill_poll(struct ieee80211_hw *hw)
  * before switch channle or power save, or tx buffer packet
  * maybe send after offchannel or rf sleep, this may cause
  * dis-association by AP */
-static void rtl_op_flush(struct ieee80211_hw *hw,
+static void rtl_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                         u32 queues, bool drop)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
index 0c9f5cebfb4271f059ff4eefa701dd96de466f77..f0839f6a9345073a23f5b2db4d4f613a071e9369 100644 (file)
@@ -1227,7 +1227,7 @@ static int cfg80211_rtw_set_default_key(struct wiphy *wiphy,
 
 static int cfg80211_rtw_get_station(struct wiphy *wiphy,
                                    struct net_device *ndev,
-                                   u8 *mac, struct station_info *sinfo)
+                                   const u8 *mac, struct station_info *sinfo)
 {
        int ret = 0;
        struct rtw_adapter *padapter = wiphy_to_adapter(wiphy);
@@ -2903,7 +2903,7 @@ static int cfg80211_rtw_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
 }
 
 static int cfg80211_rtw_add_station(struct wiphy *wiphy,
-                                   struct net_device *ndev, u8 *mac,
+                                   struct net_device *ndev, const u8 *mac,
                                    struct station_parameters *params)
 {
        DBG_8723A("%s(%s)\n", __func__, ndev->name);
@@ -2912,7 +2912,7 @@ static int cfg80211_rtw_add_station(struct wiphy *wiphy,
 }
 
 static int cfg80211_rtw_del_station(struct wiphy *wiphy,
-                                   struct net_device *ndev, u8 *mac)
+                                   struct net_device *ndev, const u8 *mac)
 {
        int ret = 0;
        struct list_head *phead, *plist, *ptmp;
@@ -2988,7 +2988,7 @@ static int cfg80211_rtw_del_station(struct wiphy *wiphy,
 }
 
 static int cfg80211_rtw_change_station(struct wiphy *wiphy,
-                                      struct net_device *ndev, u8 *mac,
+                                      struct net_device *ndev, const u8 *mac,
                                       struct station_parameters *params)
 {
        DBG_8723A("%s(%s)\n", __func__, ndev->name);
index 9a37408708f4a84fcf5c4da2afb53fe5a4b8325c..046be2ce9c1a8a12070de43f8b489d5fae7defe4 100644 (file)
@@ -1278,7 +1278,9 @@ static void rtl_op_rfkill_poll(struct ieee80211_hw *hw)
  * before switch channel or power save, or tx buffer packet
  * maybe send after offchannel or rf sleep, this may cause
  * dis-association by AP */
-static void rtl_op_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+static void rtl_op_flush(struct ieee80211_hw *hw,
+                        struct ieee80211_vif *vif,
+                        u32 queues, bool drop)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
 
index f76f95c29617b3b7058a1999b7896d4e3d8a875a..723319ee08f39af6a7a27b21de6f8467742717cb 100644 (file)
@@ -84,7 +84,7 @@ static int prism2_domibset_uint32(wlandevice_t *wlandev, u32 did, u32 data)
 }
 
 static int prism2_domibset_pstr32(wlandevice_t *wlandev,
-                                 u32 did, u8 len, u8 *data)
+                                 u32 did, u8 len, const u8 *data)
 {
        struct p80211msg_dot11req_mibset msg;
        p80211item_pstr32_t *mibitem =
@@ -298,7 +298,7 @@ static int prism2_set_default_key(struct wiphy *wiphy, struct net_device *dev,
 
 
 static int prism2_get_station(struct wiphy *wiphy, struct net_device *dev,
-                             u8 *mac, struct station_info *sinfo)
+                             const u8 *mac, struct station_info *sinfo)
 {
        wlandevice_t *wlandev = dev->ml_priv;
        struct p80211msg_lnxreq_commsquality quality;
index 9189bc0a87aef18df10bf2c0003d2c1e4c0bc65a..5663f4d19d028120ef51efb689dbff39d47e9e4c 100644 (file)
@@ -300,7 +300,7 @@ bool iscsit_check_np_match(
                port = ntohs(sock_in->sin_port);
        }
 
-       if ((ip_match == true) && (np->np_port == port) &&
+       if (ip_match && (np->np_port == port) &&
            (np->np_network_transport == network_transport))
                return true;
 
@@ -325,7 +325,7 @@ static struct iscsi_np *iscsit_get_np(
                }
 
                match = iscsit_check_np_match(sockaddr, np, network_transport);
-               if (match == true) {
+               if (match) {
                        /*
                         * Increment the np_exports reference count now to
                         * prevent iscsit_del_np() below from being called
@@ -1121,7 +1121,7 @@ iscsit_get_immediate_data(struct iscsi_cmd *cmd, struct iscsi_scsi_req *hdr,
        /*
         * Special case for Unsupported SAM WRITE Opcodes and ImmediateData=Yes.
         */
-       if (dump_payload == true)
+       if (dump_payload)
                goto after_immediate_data;
 
        immed_ret = iscsit_handle_immediate_data(cmd, hdr,
@@ -3390,7 +3390,9 @@ static bool iscsit_check_inaddr_any(struct iscsi_np *np)
 
 #define SENDTARGETS_BUF_LIMIT 32768U
 
-static int iscsit_build_sendtargets_response(struct iscsi_cmd *cmd)
+static int
+iscsit_build_sendtargets_response(struct iscsi_cmd *cmd,
+                                 enum iscsit_transport_type network_transport)
 {
        char *payload = NULL;
        struct iscsi_conn *conn = cmd->conn;
@@ -3467,6 +3469,9 @@ static int iscsit_build_sendtargets_response(struct iscsi_cmd *cmd)
                                struct iscsi_np *np = tpg_np->tpg_np;
                                bool inaddr_any = iscsit_check_inaddr_any(np);
 
+                               if (np->np_network_transport != network_transport)
+                                       continue;
+
                                if (!target_name_printed) {
                                        len = sprintf(buf, "TargetName=%s",
                                                      tiqn->tiqn);
@@ -3485,10 +3490,8 @@ static int iscsit_build_sendtargets_response(struct iscsi_cmd *cmd)
 
                                len = sprintf(buf, "TargetAddress="
                                        "%s:%hu,%hu",
-                                       (inaddr_any == false) ?
-                                               np->np_ip : conn->local_ip,
-                                       (inaddr_any == false) ?
-                                               np->np_port : conn->local_port,
+                                       inaddr_any ? conn->local_ip : np->np_ip,
+                                       inaddr_any ? conn->local_port : np->np_port,
                                        tpg->tpgt);
                                len += 1;
 
@@ -3520,11 +3523,12 @@ static int iscsit_build_sendtargets_response(struct iscsi_cmd *cmd)
 
 int
 iscsit_build_text_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
-                     struct iscsi_text_rsp *hdr)
+                     struct iscsi_text_rsp *hdr,
+                     enum iscsit_transport_type network_transport)
 {
        int text_length, padding;
 
-       text_length = iscsit_build_sendtargets_response(cmd);
+       text_length = iscsit_build_sendtargets_response(cmd, network_transport);
        if (text_length < 0)
                return text_length;
 
@@ -3562,7 +3566,7 @@ static int iscsit_send_text_rsp(
        u32 tx_size = 0;
        int text_length, iov_count = 0, rc;
 
-       rc = iscsit_build_text_rsp(cmd, conn, hdr);
+       rc = iscsit_build_text_rsp(cmd, conn, hdr, ISCSI_TCP);
        if (rc < 0)
                return rc;
 
@@ -4234,8 +4238,6 @@ int iscsit_close_connection(
        if (conn->conn_transport->iscsit_wait_conn)
                conn->conn_transport->iscsit_wait_conn(conn);
 
-       iscsit_free_queue_reqs_for_conn(conn);
-
        /*
         * During Connection recovery drop unacknowledged out of order
         * commands for this connection, and prepare the other commands
@@ -4252,6 +4254,7 @@ int iscsit_close_connection(
                iscsit_clear_ooo_cmdsns_for_conn(conn);
                iscsit_release_commands_from_conn(conn);
        }
+       iscsit_free_queue_reqs_for_conn(conn);
 
        /*
         * Handle decrementing session or connection usage count if
index de77d9aa22c6329b41d515a157e49d54451edf50..19b842c3e0b39222e125c04f165fccbcf68f338d 100644 (file)
@@ -71,6 +71,40 @@ static void chap_gen_challenge(
                        challenge_asciihex);
 }
 
+static int chap_check_algorithm(const char *a_str)
+{
+       char *tmp, *orig, *token;
+
+       tmp = kstrdup(a_str, GFP_KERNEL);
+       if (!tmp) {
+               pr_err("Memory allocation failed for CHAP_A temporary buffer\n");
+               return CHAP_DIGEST_UNKNOWN;
+       }
+       orig = tmp;
+
+       token = strsep(&tmp, "=");
+       if (!token)
+               goto out;
+
+       if (strcmp(token, "CHAP_A")) {
+               pr_err("Unable to locate CHAP_A key\n");
+               goto out;
+       }
+       while (token) {
+               token = strsep(&tmp, ",");
+               if (!token)
+                       goto out;
+
+               if (!strncmp(token, "5", 1)) {
+                       pr_debug("Selected MD5 Algorithm\n");
+                       kfree(orig);
+                       return CHAP_DIGEST_MD5;
+               }
+       }
+out:
+       kfree(orig);
+       return CHAP_DIGEST_UNKNOWN;
+}
 
 static struct iscsi_chap *chap_server_open(
        struct iscsi_conn *conn,
@@ -79,6 +113,7 @@ static struct iscsi_chap *chap_server_open(
        char *aic_str,
        unsigned int *aic_len)
 {
+       int ret;
        struct iscsi_chap *chap;
 
        if (!(auth->naf_flags & NAF_USERID_SET) ||
@@ -93,21 +128,24 @@ static struct iscsi_chap *chap_server_open(
                return NULL;
 
        chap = conn->auth_protocol;
-       /*
-        * We only support MD5 MDA presently.
-        */
-       if (strncmp(a_str, "CHAP_A=5", 8)) {
-               pr_err("CHAP_A is not MD5.\n");
+       ret = chap_check_algorithm(a_str);
+       switch (ret) {
+       case CHAP_DIGEST_MD5:
+               pr_debug("[server] Got CHAP_A=5\n");
+               /*
+                * Send back CHAP_A set to MD5.
+               */
+               *aic_len = sprintf(aic_str, "CHAP_A=5");
+               *aic_len += 1;
+               chap->digest_type = CHAP_DIGEST_MD5;
+               pr_debug("[server] Sending CHAP_A=%d\n", chap->digest_type);
+               break;
+       case CHAP_DIGEST_UNKNOWN:
+       default:
+               pr_err("Unsupported CHAP_A value\n");
                return NULL;
        }
-       pr_debug("[server] Got CHAP_A=5\n");
-       /*
-        * Send back CHAP_A set to MD5.
-        */
-       *aic_len = sprintf(aic_str, "CHAP_A=5");
-       *aic_len += 1;
-       chap->digest_type = CHAP_DIGEST_MD5;
-       pr_debug("[server] Sending CHAP_A=%d\n", chap->digest_type);
+
        /*
         * Set Identifier.
         */
@@ -313,6 +351,16 @@ static int chap_server_compute_md5(
                pr_err("Unable to convert incoming challenge\n");
                goto out;
        }
+       /*
+        * During mutual authentication, the CHAP_C generated by the
+        * initiator must not match the original CHAP_C generated by
+        * the target.
+        */
+       if (!memcmp(challenge_binhex, chap->challenge, CHAP_CHALLENGE_LENGTH)) {
+               pr_err("initiator CHAP_C matches target CHAP_C, failing"
+                      " login attempt\n");
+               goto out;
+       }
        /*
         * Generate CHAP_N and CHAP_R for mutual authentication.
         */
index 2f463c09626d9232df18fd391daf716440ee543a..d22f7b96a06ca98aa3bd83f669d92eb686cfcec9 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef _ISCSI_CHAP_H_
 #define _ISCSI_CHAP_H_
 
+#define CHAP_DIGEST_UNKNOWN    0
 #define CHAP_DIGEST_MD5                5
 #define CHAP_DIGEST_SHA                6
 
index d9b1d88e1ad382f07ff8d64c5374b3caaac3a3ce..fecb69535a1583abe6f70f663f1ecd18142c555f 100644 (file)
@@ -1145,7 +1145,7 @@ iscsit_conn_set_transport(struct iscsi_conn *conn, struct iscsit_transport *t)
 void iscsi_target_login_sess_out(struct iscsi_conn *conn,
                struct iscsi_np *np, bool zero_tsih, bool new_sess)
 {
-       if (new_sess == false)
+       if (!new_sess)
                goto old_sess_out;
 
        pr_err("iSCSI Login negotiation failed.\n");
index 75b685960e80d31e2d6e19439a98257fea41ab15..62a095f36bf2f78b77d2a9b75cea99b9c2f14ecb 100644 (file)
@@ -404,7 +404,7 @@ static void iscsi_target_sk_data_ready(struct sock *sk)
        }
 
        rc = schedule_delayed_work(&conn->login_work, 0);
-       if (rc == false) {
+       if (!rc) {
                pr_debug("iscsi_target_sk_data_ready, schedule_delayed_work"
                         " got false\n");
        }
@@ -513,7 +513,7 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
        state = (tpg->tpg_state == TPG_STATE_ACTIVE);
        spin_unlock(&tpg->tpg_state_lock);
 
-       if (state == false) {
+       if (!state) {
                pr_debug("iscsi_target_do_login_rx: tpg_state != TPG_STATE_ACTIVE\n");
                iscsi_target_restore_sock_callbacks(conn);
                iscsi_target_login_drop(conn, login);
@@ -528,7 +528,7 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
                state = iscsi_target_sk_state_check(sk);
                read_unlock_bh(&sk->sk_callback_lock);
 
-               if (state == false) {
+               if (!state) {
                        pr_debug("iscsi_target_do_login_rx, TCP state CLOSE\n");
                        iscsi_target_restore_sock_callbacks(conn);
                        iscsi_target_login_drop(conn, login);
@@ -773,6 +773,12 @@ static int iscsi_target_handle_csg_zero(
                }
 
                goto do_auth;
+       } else if (!payload_length) {
+               pr_err("Initiator sent zero length security payload,"
+                      " login failed\n");
+               iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
+                                   ISCSI_LOGIN_STATUS_AUTH_FAILED);
+               return -1;
        }
 
        if (login->first_request)
index 4d2e23fc76fda72be2b7ec59c801f0696a8470e3..02f9de26f38ab930b9810a20abaf8b972f42f93d 100644 (file)
@@ -474,10 +474,10 @@ int iscsi_set_keys_to_negotiate(
                if (!strcmp(param->name, AUTHMETHOD)) {
                        SET_PSTATE_NEGOTIATE(param);
                } else if (!strcmp(param->name, HEADERDIGEST)) {
-                       if (iser == false)
+                       if (!iser)
                                SET_PSTATE_NEGOTIATE(param);
                } else if (!strcmp(param->name, DATADIGEST)) {
-                       if (iser == false)
+                       if (!iser)
                                SET_PSTATE_NEGOTIATE(param);
                } else if (!strcmp(param->name, MAXCONNECTIONS)) {
                        SET_PSTATE_NEGOTIATE(param);
@@ -497,7 +497,7 @@ int iscsi_set_keys_to_negotiate(
                } else if (!strcmp(param->name, IMMEDIATEDATA)) {
                        SET_PSTATE_NEGOTIATE(param);
                } else if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) {
-                       if (iser == false)
+                       if (!iser)
                                SET_PSTATE_NEGOTIATE(param);
                } else if (!strcmp(param->name, MAXXMITDATASEGMENTLENGTH)) {
                        continue;
@@ -528,13 +528,13 @@ int iscsi_set_keys_to_negotiate(
                } else if (!strcmp(param->name, OFMARKINT)) {
                        SET_PSTATE_NEGOTIATE(param);
                } else if (!strcmp(param->name, RDMAEXTENSIONS)) {
-                       if (iser == true)
+                       if (iser)
                                SET_PSTATE_NEGOTIATE(param);
                } else if (!strcmp(param->name, INITIATORRECVDATASEGMENTLENGTH)) {
-                       if (iser == true)
+                       if (iser)
                                SET_PSTATE_NEGOTIATE(param);
                } else if (!strcmp(param->name, TARGETRECVDATASEGMENTLENGTH)) {
-                       if (iser == true)
+                       if (iser)
                                SET_PSTATE_NEGOTIATE(param);
                }
        }
@@ -1605,7 +1605,7 @@ int iscsi_decode_text_input(
 
        tmpbuf = kzalloc(length + 1, GFP_KERNEL);
        if (!tmpbuf) {
-               pr_err("Unable to allocate memory for tmpbuf.\n");
+               pr_err("Unable to allocate %u + 1 bytes for tmpbuf.\n", length);
                return -1;
        }
 
index 1431e8400d28b41c4ca21fc0563a8e3df1b7828a..c3cb5c15efdaa4fe1e5ea2c4a90cd6eb0e167a15 100644 (file)
@@ -189,7 +189,7 @@ static void iscsit_clear_tpg_np_login_thread(
        iscsit_reset_np_thread(tpg_np->tpg_np, tpg_np, tpg, shutdown);
 }
 
-void iscsit_clear_tpg_np_login_threads(
+static void iscsit_clear_tpg_np_login_threads(
        struct iscsi_portal_group *tpg,
        bool shutdown)
 {
@@ -276,8 +276,6 @@ int iscsit_tpg_del_portal_group(
        tpg->tpg_state = TPG_STATE_INACTIVE;
        spin_unlock(&tpg->tpg_state_lock);
 
-       iscsit_clear_tpg_np_login_threads(tpg, true);
-
        if (iscsit_release_sessions_for_tpg(tpg, force) < 0) {
                pr_err("Unable to delete iSCSI Target Portal Group:"
                        " %hu while active sessions exist, and force=0\n",
@@ -453,7 +451,7 @@ static bool iscsit_tpg_check_network_portal(
 
                        match = iscsit_check_np_match(sockaddr, np,
                                                network_transport);
-                       if (match == true)
+                       if (match)
                                break;
                }
                spin_unlock(&tpg->tpg_np_lock);
@@ -475,7 +473,7 @@ struct iscsi_tpg_np *iscsit_tpg_add_network_portal(
 
        if (!tpg_np_parent) {
                if (iscsit_tpg_check_network_portal(tpg->tpg_tiqn, sockaddr,
-                               network_transport) == true) {
+                               network_transport)) {
                        pr_err("Network Portal: %s already exists on a"
                                " different TPG on %s\n", ip_str,
                                tpg->tpg_tiqn->tiqn);
index 0a182f2aa8a25ea07cad39ac58a0d954010abd8b..e7265337bc43c1f4cf789d4e4c980d8fdbd6add6 100644 (file)
@@ -8,7 +8,6 @@ extern struct iscsi_portal_group *iscsit_get_tpg_from_np(struct iscsi_tiqn *,
                        struct iscsi_np *, struct iscsi_tpg_np **);
 extern int iscsit_get_tpg(struct iscsi_portal_group *);
 extern void iscsit_put_tpg(struct iscsi_portal_group *);
-extern void iscsit_clear_tpg_np_login_threads(struct iscsi_portal_group *, bool);
 extern void iscsit_tpg_dump_params(struct iscsi_portal_group *);
 extern int iscsit_tpg_add_portal_group(struct iscsi_tiqn *, struct iscsi_portal_group *);
 extern int iscsit_tpg_del_portal_group(struct iscsi_tiqn *, struct iscsi_portal_group *,
index 73ab75ddaf42e9b3fc8e378cf3ae3669d1e040e8..6d2f37578b29cc0509d3898b7910bb35cd9d484c 100644 (file)
@@ -179,7 +179,7 @@ static void tcm_loop_submission_work(struct work_struct *work)
        struct tcm_loop_hba *tl_hba;
        struct tcm_loop_tpg *tl_tpg;
        struct scatterlist *sgl_bidi = NULL;
-       u32 sgl_bidi_count = 0;
+       u32 sgl_bidi_count = 0, transfer_length;
        int rc;
 
        tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host);
@@ -213,12 +213,21 @@ static void tcm_loop_submission_work(struct work_struct *work)
 
        }
 
-       if (!scsi_prot_sg_count(sc) && scsi_get_prot_op(sc) != SCSI_PROT_NORMAL)
+       transfer_length = scsi_transfer_length(sc);
+       if (!scsi_prot_sg_count(sc) &&
+           scsi_get_prot_op(sc) != SCSI_PROT_NORMAL) {
                se_cmd->prot_pto = true;
+               /*
+                * loopback transport doesn't support
+                * WRITE_GENERATE, READ_STRIP protection
+                * information operations, go ahead unprotected.
+                */
+               transfer_length = scsi_bufflen(sc);
+       }
 
        rc = target_submit_cmd_map_sgls(se_cmd, tl_nexus->se_sess, sc->cmnd,
                        &tl_cmd->tl_sense_buf[0], tl_cmd->sc->device->lun,
-                       scsi_bufflen(sc), tcm_loop_sam_attr(sc),
+                       transfer_length, tcm_loop_sam_attr(sc),
                        sc->sc_data_direction, 0,
                        scsi_sglist(sc), scsi_sg_count(sc),
                        sgl_bidi, sgl_bidi_count,
index e0229592ec5509656aed292970af719be1f9111d..bd78d9235ac645678aeaacc3a564c1c4c13c5742 100644 (file)
@@ -81,7 +81,7 @@ sbc_emulate_readcapacity(struct se_cmd *cmd)
                transport_kunmap_data_sg(cmd);
        }
 
-       target_complete_cmd(cmd, GOOD);
+       target_complete_cmd_with_length(cmd, GOOD, 8);
        return 0;
 }
 
@@ -137,7 +137,7 @@ sbc_emulate_readcapacity_16(struct se_cmd *cmd)
                transport_kunmap_data_sg(cmd);
        }
 
-       target_complete_cmd(cmd, GOOD);
+       target_complete_cmd_with_length(cmd, GOOD, 32);
        return 0;
 }
 
@@ -176,24 +176,6 @@ static inline u32 sbc_get_size(struct se_cmd *cmd, u32 sectors)
        return cmd->se_dev->dev_attrib.block_size * sectors;
 }
 
-static int sbc_check_valid_sectors(struct se_cmd *cmd)
-{
-       struct se_device *dev = cmd->se_dev;
-       unsigned long long end_lba;
-       u32 sectors;
-
-       sectors = cmd->data_length / dev->dev_attrib.block_size;
-       end_lba = dev->transport->get_blocks(dev) + 1;
-
-       if (cmd->t_task_lba + sectors > end_lba) {
-               pr_err("target: lba %llu, sectors %u exceeds end lba %llu\n",
-                       cmd->t_task_lba, sectors, end_lba);
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
 static inline u32 transport_get_sectors_6(unsigned char *cdb)
 {
        /*
@@ -665,8 +647,19 @@ sbc_check_prot(struct se_device *dev, struct se_cmd *cmd, unsigned char *cdb,
 
        cmd->prot_type = dev->dev_attrib.pi_prot_type;
        cmd->prot_length = dev->prot_length * sectors;
-       pr_debug("%s: prot_type=%d, prot_length=%d prot_op=%d prot_checks=%d\n",
-                __func__, cmd->prot_type, cmd->prot_length,
+
+       /**
+        * In case protection information exists over the wire
+        * we modify command data length to describe pure data.
+        * The actual transfer length is data length + protection
+        * length
+        **/
+       if (protect)
+               cmd->data_length = sectors * dev->dev_attrib.block_size;
+
+       pr_debug("%s: prot_type=%d, data_length=%d, prot_length=%d "
+                "prot_op=%d prot_checks=%d\n",
+                __func__, cmd->prot_type, cmd->data_length, cmd->prot_length,
                 cmd->prot_op, cmd->prot_checks);
 
        return true;
@@ -877,15 +870,6 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                break;
        case SYNCHRONIZE_CACHE:
        case SYNCHRONIZE_CACHE_16:
-               if (!ops->execute_sync_cache) {
-                       size = 0;
-                       cmd->execute_cmd = sbc_emulate_noop;
-                       break;
-               }
-
-               /*
-                * Extract LBA and range to be flushed for emulated SYNCHRONIZE_CACHE
-                */
                if (cdb[0] == SYNCHRONIZE_CACHE) {
                        sectors = transport_get_sectors_10(cdb);
                        cmd->t_task_lba = transport_lba_32(cdb);
@@ -893,18 +877,12 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                        sectors = transport_get_sectors_16(cdb);
                        cmd->t_task_lba = transport_lba_64(cdb);
                }
-
-               size = sbc_get_size(cmd, sectors);
-
-               /*
-                * Check to ensure that LBA + Range does not exceed past end of
-                * device for IBLOCK and FILEIO ->do_sync_cache() backend calls
-                */
-               if (cmd->t_task_lba || sectors) {
-                       if (sbc_check_valid_sectors(cmd) < 0)
-                               return TCM_ADDRESS_OUT_OF_RANGE;
+               if (ops->execute_sync_cache) {
+                       cmd->execute_cmd = ops->execute_sync_cache;
+                       goto check_lba;
                }
-               cmd->execute_cmd = ops->execute_sync_cache;
+               size = 0;
+               cmd->execute_cmd = sbc_emulate_noop;
                break;
        case UNMAP:
                if (!ops->execute_unmap)
@@ -947,8 +925,10 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                break;
        case VERIFY:
                size = 0;
+               sectors = transport_get_sectors_10(cdb);
+               cmd->t_task_lba = transport_lba_32(cdb);
                cmd->execute_cmd = sbc_emulate_noop;
-               break;
+               goto check_lba;
        case REZERO_UNIT:
        case SEEK_6:
        case SEEK_10:
@@ -988,7 +968,7 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                                dev->dev_attrib.hw_max_sectors);
                        return TCM_INVALID_CDB_FIELD;
                }
-
+check_lba:
                end_lba = dev->transport->get_blocks(dev) + 1;
                if (cmd->t_task_lba + sectors > end_lba) {
                        pr_err("cmd exceeds last lba %llu "
index 8653666612a802f5cbfcde2c4d82ac7d20ca9294..6cd7222738fc4697f8c1be7c337ef7418fd09075 100644 (file)
@@ -129,15 +129,10 @@ static sense_reason_t
 spc_emulate_evpd_80(struct se_cmd *cmd, unsigned char *buf)
 {
        struct se_device *dev = cmd->se_dev;
-       u16 len = 0;
+       u16 len;
 
        if (dev->dev_flags & DF_EMULATED_VPD_UNIT_SERIAL) {
-               u32 unit_serial_len;
-
-               unit_serial_len = strlen(dev->t10_wwn.unit_serial);
-               unit_serial_len++; /* For NULL Terminator */
-
-               len += sprintf(&buf[4], "%s", dev->t10_wwn.unit_serial);
+               len = sprintf(&buf[4], "%s", dev->t10_wwn.unit_serial);
                len++; /* Extra Byte for NULL Terminator */
                buf[3] = len;
        }
@@ -721,6 +716,7 @@ spc_emulate_inquiry(struct se_cmd *cmd)
        unsigned char *buf;
        sense_reason_t ret;
        int p;
+       int len = 0;
 
        buf = kzalloc(SE_INQUIRY_BUF, GFP_KERNEL);
        if (!buf) {
@@ -742,6 +738,7 @@ spc_emulate_inquiry(struct se_cmd *cmd)
                }
 
                ret = spc_emulate_inquiry_std(cmd, buf);
+               len = buf[4] + 5;
                goto out;
        }
 
@@ -749,6 +746,7 @@ spc_emulate_inquiry(struct se_cmd *cmd)
                if (cdb[2] == evpd_handlers[p].page) {
                        buf[1] = cdb[2];
                        ret = evpd_handlers[p].emulate(cmd, buf);
+                       len = get_unaligned_be16(&buf[2]) + 4;
                        goto out;
                }
        }
@@ -765,7 +763,7 @@ spc_emulate_inquiry(struct se_cmd *cmd)
        kfree(buf);
 
        if (!ret)
-               target_complete_cmd(cmd, GOOD);
+               target_complete_cmd_with_length(cmd, GOOD, len);
        return ret;
 }
 
@@ -1103,7 +1101,7 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd)
                transport_kunmap_data_sg(cmd);
        }
 
-       target_complete_cmd(cmd, GOOD);
+       target_complete_cmd_with_length(cmd, GOOD, length);
        return 0;
 }
 
@@ -1279,7 +1277,7 @@ sense_reason_t spc_emulate_report_luns(struct se_cmd *cmd)
        buf[3] = (lun_count & 0xff);
        transport_kunmap_data_sg(cmd);
 
-       target_complete_cmd(cmd, GOOD);
+       target_complete_cmd_with_length(cmd, GOOD, 8 + lun_count * 8);
        return 0;
 }
 EXPORT_SYMBOL(spc_emulate_report_luns);
index 2179feed0d63aa83017dff321ddc86c11e8432d4..7fa62fc93e0b52d70ac49c67f5455a139935f55c 100644 (file)
@@ -504,7 +504,7 @@ void transport_deregister_session(struct se_session *se_sess)
         * ->acl_free_comp caller to wakeup configfs se_node_acl->acl_group
         * removal context.
         */
-       if (se_nacl && comp_nacl == true)
+       if (se_nacl && comp_nacl)
                target_put_nacl(se_nacl);
 
        transport_free_session(se_sess);
@@ -562,7 +562,7 @@ static int transport_cmd_check_stop(struct se_cmd *cmd, bool remove_from_lists,
 
                spin_unlock_irqrestore(&cmd->t_state_lock, flags);
 
-               complete(&cmd->t_transport_stop_comp);
+               complete_all(&cmd->t_transport_stop_comp);
                return 1;
        }
 
@@ -687,7 +687,7 @@ void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status)
        if (cmd->transport_state & CMD_T_ABORTED &&
            cmd->transport_state & CMD_T_STOP) {
                spin_unlock_irqrestore(&cmd->t_state_lock, flags);
-               complete(&cmd->t_transport_stop_comp);
+               complete_all(&cmd->t_transport_stop_comp);
                return;
        } else if (!success) {
                INIT_WORK(&cmd->work, target_complete_failure_work);
@@ -703,6 +703,23 @@ void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status)
 }
 EXPORT_SYMBOL(target_complete_cmd);
 
+void target_complete_cmd_with_length(struct se_cmd *cmd, u8 scsi_status, int length)
+{
+       if (scsi_status == SAM_STAT_GOOD && length < cmd->data_length) {
+               if (cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) {
+                       cmd->residual_count += cmd->data_length - length;
+               } else {
+                       cmd->se_cmd_flags |= SCF_UNDERFLOW_BIT;
+                       cmd->residual_count = cmd->data_length - length;
+               }
+
+               cmd->data_length = length;
+       }
+
+       target_complete_cmd(cmd, scsi_status);
+}
+EXPORT_SYMBOL(target_complete_cmd_with_length);
+
 static void target_add_to_state_list(struct se_cmd *cmd)
 {
        struct se_device *dev = cmd->se_dev;
@@ -1761,7 +1778,7 @@ void target_execute_cmd(struct se_cmd *cmd)
                        cmd->se_tfo->get_task_tag(cmd));
 
                spin_unlock_irq(&cmd->t_state_lock);
-               complete(&cmd->t_transport_stop_comp);
+               complete_all(&cmd->t_transport_stop_comp);
                return;
        }
 
@@ -2363,7 +2380,7 @@ int target_get_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd,
         * fabric acknowledgement that requires two target_put_sess_cmd()
         * invocations before se_cmd descriptor release.
         */
-       if (ack_kref == true) {
+       if (ack_kref) {
                kref_get(&se_cmd->cmd_kref);
                se_cmd->se_cmd_flags |= SCF_ACK_KREF;
        }
@@ -2407,6 +2424,10 @@ static void target_release_cmd_kref(struct kref *kref)
  */
 int target_put_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd)
 {
+       if (!se_sess) {
+               se_cmd->se_tfo->release_cmd(se_cmd);
+               return 1;
+       }
        return kref_put_spinlock_irqsave(&se_cmd->cmd_kref, target_release_cmd_kref,
                        &se_sess->sess_cmd_lock);
 }
@@ -2934,6 +2955,12 @@ static void target_tmr_work(struct work_struct *work)
 int transport_generic_handle_tmr(
        struct se_cmd *cmd)
 {
+       unsigned long flags;
+
+       spin_lock_irqsave(&cmd->t_state_lock, flags);
+       cmd->transport_state |= CMD_T_ACTIVE;
+       spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+
        INIT_WORK(&cmd->work, target_tmr_work);
        queue_work(cmd->se_dev->tmr_wq, &cmd->work);
        return 0;
index 669c536fd959575da69816e4a8714b5f78a362c7..e9186cdf35e962d12abe59429370306579e9a25d 100644 (file)
@@ -70,7 +70,7 @@ static int target_xcopy_locate_se_dev_e4(struct se_cmd *se_cmd, struct xcopy_op
        unsigned char tmp_dev_wwn[XCOPY_NAA_IEEE_REGEX_LEN], *dev_wwn;
        int rc;
 
-       if (src == true)
+       if (src)
                dev_wwn = &xop->dst_tid_wwn[0];
        else
                dev_wwn = &xop->src_tid_wwn[0];
@@ -88,7 +88,7 @@ static int target_xcopy_locate_se_dev_e4(struct se_cmd *se_cmd, struct xcopy_op
                if (rc != 0)
                        continue;
 
-               if (src == true) {
+               if (src) {
                        xop->dst_dev = se_dev;
                        pr_debug("XCOPY 0xe4: Setting xop->dst_dev: %p from located"
                                " se_dev\n", xop->dst_dev);
@@ -166,7 +166,7 @@ static int target_xcopy_parse_tiddesc_e4(struct se_cmd *se_cmd, struct xcopy_op
                return -EINVAL;
        }
 
-       if (src == true) {
+       if (src) {
                memcpy(&xop->src_tid_wwn[0], &desc[8], XCOPY_NAA_IEEE_REGEX_LEN);
                /*
                 * Determine if the source designator matches the local device
@@ -236,7 +236,7 @@ static int target_xcopy_parse_target_descriptors(struct se_cmd *se_cmd,
                        /*
                         * Assume target descriptors are in source -> destination order..
                         */
-                       if (src == true)
+                       if (src)
                                src = false;
                        else
                                src = true;
@@ -560,7 +560,7 @@ static int target_xcopy_init_pt_lun(
         * reservations.  The pt_cmd->se_lun pointer will be setup from within
         * target_xcopy_setup_pt_port()
         */
-       if (remote_port == false) {
+       if (!remote_port) {
                pt_cmd->se_cmd_flags |= SCF_SE_LUN_CMD | SCF_CMD_XCOPY_PASSTHROUGH;
                return 0;
        }
index f5fd515b2bee266dd9c8279ea804bc7372d955f3..be0c0d08c56a91ff9acc97e8db92868c39c25f0c 100644 (file)
@@ -128,6 +128,7 @@ int ft_queue_status(struct se_cmd *se_cmd)
        struct fc_lport *lport;
        struct fc_exch *ep;
        size_t len;
+       int rc;
 
        if (cmd->aborted)
                return 0;
@@ -137,9 +138,10 @@ int ft_queue_status(struct se_cmd *se_cmd)
        len = sizeof(*fcp) + se_cmd->scsi_sense_length;
        fp = fc_frame_alloc(lport, len);
        if (!fp) {
-               /* XXX shouldn't just drop it - requeue and retry? */
-               return 0;
+               se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL;
+               return -ENOMEM;
        }
+
        fcp = fc_frame_payload_get(fp, len);
        memset(fcp, 0, len);
        fcp->resp.fr_status = se_cmd->scsi_status;
@@ -170,7 +172,18 @@ int ft_queue_status(struct se_cmd *se_cmd)
        fc_fill_fc_hdr(fp, FC_RCTL_DD_CMD_STATUS, ep->did, ep->sid, FC_TYPE_FCP,
                       FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ, 0);
 
-       lport->tt.seq_send(lport, cmd->seq, fp);
+       rc = lport->tt.seq_send(lport, cmd->seq, fp);
+       if (rc) {
+               pr_info_ratelimited("%s: Failed to send response frame %p, "
+                                   "xid <0x%x>\n", __func__, fp, ep->xid);
+               /*
+                * Generate a TASK_SET_FULL status to notify the initiator
+                * to reduce it's queue_depth after the se_cmd response has
+                * been re-queued by target-core.
+                */
+               se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL;
+               return -ENOMEM;
+       }
        lport->tt.exch_done(cmd->seq);
        return 0;
 }
index e415af32115a80bd7927627e826731b2c8b461c3..97b486c3dda136a21824d02ea0f70c828b214d46 100644 (file)
@@ -82,6 +82,10 @@ int ft_queue_data_in(struct se_cmd *se_cmd)
 
        if (cmd->aborted)
                return 0;
+
+       if (se_cmd->scsi_status == SAM_STAT_TASK_SET_FULL)
+               goto queue_status;
+
        ep = fc_seq_exch(cmd->seq);
        lport = ep->lp;
        cmd->seq = lport->tt.seq_start_next(cmd->seq);
@@ -178,14 +182,23 @@ int ft_queue_data_in(struct se_cmd *se_cmd)
                               FC_TYPE_FCP, f_ctl, fh_off);
                error = lport->tt.seq_send(lport, seq, fp);
                if (error) {
-                       /* XXX For now, initiator will retry */
-                       pr_err_ratelimited("%s: Failed to send frame %p, "
+                       pr_info_ratelimited("%s: Failed to send frame %p, "
                                                "xid <0x%x>, remaining %zu, "
                                                "lso_max <0x%x>\n",
                                                __func__, fp, ep->xid,
                                                remaining, lport->lso_max);
+                       /*
+                        * Go ahead and set TASK_SET_FULL status ignoring the
+                        * rest of the DataIN, and immediately attempt to
+                        * send the response via ft_queue_status() in order
+                        * to notify the initiator that it should reduce it's
+                        * per LUN queue_depth.
+                        */
+                       se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL;
+                       break;
                }
        }
+queue_status:
        return ft_queue_status(se_cmd);
 }
 
index fe0880d0873e04c973c1db5699c947b9ffc1d69a..3d78a8844e438f7e1904c5429655af8a4708d95d 100644 (file)
@@ -793,7 +793,7 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g,
 
        net->netdev_ops = &eth_netdev_ops;
 
-       SET_ETHTOOL_OPS(net, &ops);
+       net->ethtool_ops = &ops;
 
        dev->gadget = g;
        SET_NETDEV_DEV(net, &g->dev);
@@ -850,7 +850,7 @@ struct net_device *gether_setup_name_default(const char *netname)
 
        net->netdev_ops = &eth_netdev_ops;
 
-       SET_ETHTOOL_OPS(net, &ops);
+       net->ethtool_ops = &ops;
        SET_NETDEV_DEVTYPE(net, &gadget_type);
 
        return net;
index e9c280f55819fae38704cc43dfad33be6c8c40cf..4f4ffa4c604e081755a3b77dba0fccb24c6ded7d 100644 (file)
@@ -57,7 +57,8 @@
 #define TCM_VHOST_MAX_CDB_SIZE 32
 #define TCM_VHOST_DEFAULT_TAGS 256
 #define TCM_VHOST_PREALLOC_SGLS 2048
-#define TCM_VHOST_PREALLOC_PAGES 2048
+#define TCM_VHOST_PREALLOC_UPAGES 2048
+#define TCM_VHOST_PREALLOC_PROT_SGLS 512
 
 struct vhost_scsi_inflight {
        /* Wait for the flush operation to finish */
@@ -79,10 +80,12 @@ struct tcm_vhost_cmd {
        u64 tvc_tag;
        /* The number of scatterlists associated with this cmd */
        u32 tvc_sgl_count;
+       u32 tvc_prot_sgl_count;
        /* Saved unpacked SCSI LUN for tcm_vhost_submission_work() */
        u32 tvc_lun;
        /* Pointer to the SGL formatted memory from virtio-scsi */
        struct scatterlist *tvc_sgl;
+       struct scatterlist *tvc_prot_sgl;
        struct page **tvc_upages;
        /* Pointer to response */
        struct virtio_scsi_cmd_resp __user *tvc_resp;
@@ -166,7 +169,8 @@ enum {
 };
 
 enum {
-       VHOST_SCSI_FEATURES = VHOST_FEATURES | (1ULL << VIRTIO_SCSI_F_HOTPLUG)
+       VHOST_SCSI_FEATURES = VHOST_FEATURES | (1ULL << VIRTIO_SCSI_F_HOTPLUG) |
+                                              (1ULL << VIRTIO_SCSI_F_T10_PI)
 };
 
 #define VHOST_SCSI_MAX_TARGET  256
@@ -456,12 +460,16 @@ static void tcm_vhost_release_cmd(struct se_cmd *se_cmd)
        struct tcm_vhost_cmd *tv_cmd = container_of(se_cmd,
                                struct tcm_vhost_cmd, tvc_se_cmd);
        struct se_session *se_sess = se_cmd->se_sess;
+       int i;
 
        if (tv_cmd->tvc_sgl_count) {
-               u32 i;
                for (i = 0; i < tv_cmd->tvc_sgl_count; i++)
                        put_page(sg_page(&tv_cmd->tvc_sgl[i]));
        }
+       if (tv_cmd->tvc_prot_sgl_count) {
+               for (i = 0; i < tv_cmd->tvc_prot_sgl_count; i++)
+                       put_page(sg_page(&tv_cmd->tvc_prot_sgl[i]));
+       }
 
        tcm_vhost_put_inflight(tv_cmd->inflight);
        percpu_ida_free(&se_sess->sess_tag_pool, se_cmd->map_tag);
@@ -713,16 +721,14 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work)
 }
 
 static struct tcm_vhost_cmd *
-vhost_scsi_get_tag(struct vhost_virtqueue *vq,
-                       struct tcm_vhost_tpg *tpg,
-                       struct virtio_scsi_cmd_req *v_req,
-                       u32 exp_data_len,
-                       int data_direction)
+vhost_scsi_get_tag(struct vhost_virtqueue *vq, struct tcm_vhost_tpg *tpg,
+                  unsigned char *cdb, u64 scsi_tag, u16 lun, u8 task_attr,
+                  u32 exp_data_len, int data_direction)
 {
        struct tcm_vhost_cmd *cmd;
        struct tcm_vhost_nexus *tv_nexus;
        struct se_session *se_sess;
-       struct scatterlist *sg;
+       struct scatterlist *sg, *prot_sg;
        struct page **pages;
        int tag;
 
@@ -741,19 +747,24 @@ vhost_scsi_get_tag(struct vhost_virtqueue *vq,
 
        cmd = &((struct tcm_vhost_cmd *)se_sess->sess_cmd_map)[tag];
        sg = cmd->tvc_sgl;
+       prot_sg = cmd->tvc_prot_sgl;
        pages = cmd->tvc_upages;
        memset(cmd, 0, sizeof(struct tcm_vhost_cmd));
 
        cmd->tvc_sgl = sg;
+       cmd->tvc_prot_sgl = prot_sg;
        cmd->tvc_upages = pages;
        cmd->tvc_se_cmd.map_tag = tag;
-       cmd->tvc_tag = v_req->tag;
-       cmd->tvc_task_attr = v_req->task_attr;
+       cmd->tvc_tag = scsi_tag;
+       cmd->tvc_lun = lun;
+       cmd->tvc_task_attr = task_attr;
        cmd->tvc_exp_data_len = exp_data_len;
        cmd->tvc_data_direction = data_direction;
        cmd->tvc_nexus = tv_nexus;
        cmd->inflight = tcm_vhost_get_inflight(vq);
 
+       memcpy(cmd->tvc_cdb, cdb, TCM_VHOST_MAX_CDB_SIZE);
+
        return cmd;
 }
 
@@ -767,35 +778,28 @@ vhost_scsi_map_to_sgl(struct tcm_vhost_cmd *tv_cmd,
                      struct scatterlist *sgl,
                      unsigned int sgl_count,
                      struct iovec *iov,
-                     int write)
+                     struct page **pages,
+                     bool write)
 {
        unsigned int npages = 0, pages_nr, offset, nbytes;
        struct scatterlist *sg = sgl;
        void __user *ptr = iov->iov_base;
        size_t len = iov->iov_len;
-       struct page **pages;
        int ret, i;
 
-       if (sgl_count > TCM_VHOST_PREALLOC_SGLS) {
-               pr_err("vhost_scsi_map_to_sgl() psgl_count: %u greater than"
-                      " preallocated TCM_VHOST_PREALLOC_SGLS: %u\n",
-                       sgl_count, TCM_VHOST_PREALLOC_SGLS);
-               return -ENOBUFS;
-       }
-
        pages_nr = iov_num_pages(iov);
-       if (pages_nr > sgl_count)
+       if (pages_nr > sgl_count) {
+               pr_err("vhost_scsi_map_to_sgl() pages_nr: %u greater than"
+                      " sgl_count: %u\n", pages_nr, sgl_count);
                return -ENOBUFS;
-
-       if (pages_nr > TCM_VHOST_PREALLOC_PAGES) {
+       }
+       if (pages_nr > TCM_VHOST_PREALLOC_UPAGES) {
                pr_err("vhost_scsi_map_to_sgl() pages_nr: %u greater than"
-                      " preallocated TCM_VHOST_PREALLOC_PAGES: %u\n",
-                       pages_nr, TCM_VHOST_PREALLOC_PAGES);
+                      " preallocated TCM_VHOST_PREALLOC_UPAGES: %u\n",
+                       pages_nr, TCM_VHOST_PREALLOC_UPAGES);
                return -ENOBUFS;
        }
 
-       pages = tv_cmd->tvc_upages;
-
        ret = get_user_pages_fast((unsigned long)ptr, pages_nr, write, pages);
        /* No pages were pinned */
        if (ret < 0)
@@ -825,33 +829,32 @@ vhost_scsi_map_to_sgl(struct tcm_vhost_cmd *tv_cmd,
 static int
 vhost_scsi_map_iov_to_sgl(struct tcm_vhost_cmd *cmd,
                          struct iovec *iov,
-                         unsigned int niov,
-                         int write)
+                         int niov,
+                         bool write)
 {
-       int ret;
-       unsigned int i;
-       u32 sgl_count;
-       struct scatterlist *sg;
+       struct scatterlist *sg = cmd->tvc_sgl;
+       unsigned int sgl_count = 0;
+       int ret, i;
 
-       /*
-        * Find out how long sglist needs to be
-        */
-       sgl_count = 0;
        for (i = 0; i < niov; i++)
                sgl_count += iov_num_pages(&iov[i]);
 
-       /* TODO overflow checking */
+       if (sgl_count > TCM_VHOST_PREALLOC_SGLS) {
+               pr_err("vhost_scsi_map_iov_to_sgl() sgl_count: %u greater than"
+                       " preallocated TCM_VHOST_PREALLOC_SGLS: %u\n",
+                       sgl_count, TCM_VHOST_PREALLOC_SGLS);
+               return -ENOBUFS;
+       }
 
-       sg = cmd->tvc_sgl;
        pr_debug("%s sg %p sgl_count %u\n", __func__, sg, sgl_count);
        sg_init_table(sg, sgl_count);
-
        cmd->tvc_sgl_count = sgl_count;
 
-       pr_debug("Mapping %u iovecs for %u pages\n", niov, sgl_count);
+       pr_debug("Mapping iovec %p for %u pages\n", &iov[0], sgl_count);
+
        for (i = 0; i < niov; i++) {
                ret = vhost_scsi_map_to_sgl(cmd, sg, sgl_count, &iov[i],
-                                           write);
+                                           cmd->tvc_upages, write);
                if (ret < 0) {
                        for (i = 0; i < cmd->tvc_sgl_count; i++)
                                put_page(sg_page(&cmd->tvc_sgl[i]));
@@ -859,31 +862,70 @@ vhost_scsi_map_iov_to_sgl(struct tcm_vhost_cmd *cmd,
                        cmd->tvc_sgl_count = 0;
                        return ret;
                }
-
                sg += ret;
                sgl_count -= ret;
        }
        return 0;
 }
 
+static int
+vhost_scsi_map_iov_to_prot(struct tcm_vhost_cmd *cmd,
+                          struct iovec *iov,
+                          int niov,
+                          bool write)
+{
+       struct scatterlist *prot_sg = cmd->tvc_prot_sgl;
+       unsigned int prot_sgl_count = 0;
+       int ret, i;
+
+       for (i = 0; i < niov; i++)
+               prot_sgl_count += iov_num_pages(&iov[i]);
+
+       if (prot_sgl_count > TCM_VHOST_PREALLOC_PROT_SGLS) {
+               pr_err("vhost_scsi_map_iov_to_prot() sgl_count: %u greater than"
+                       " preallocated TCM_VHOST_PREALLOC_PROT_SGLS: %u\n",
+                       prot_sgl_count, TCM_VHOST_PREALLOC_PROT_SGLS);
+               return -ENOBUFS;
+       }
+
+       pr_debug("%s prot_sg %p prot_sgl_count %u\n", __func__,
+                prot_sg, prot_sgl_count);
+       sg_init_table(prot_sg, prot_sgl_count);
+       cmd->tvc_prot_sgl_count = prot_sgl_count;
+
+       for (i = 0; i < niov; i++) {
+               ret = vhost_scsi_map_to_sgl(cmd, prot_sg, prot_sgl_count, &iov[i],
+                                           cmd->tvc_upages, write);
+               if (ret < 0) {
+                       for (i = 0; i < cmd->tvc_prot_sgl_count; i++)
+                               put_page(sg_page(&cmd->tvc_prot_sgl[i]));
+
+                       cmd->tvc_prot_sgl_count = 0;
+                       return ret;
+               }
+               prot_sg += ret;
+               prot_sgl_count -= ret;
+       }
+       return 0;
+}
+
 static void tcm_vhost_submission_work(struct work_struct *work)
 {
        struct tcm_vhost_cmd *cmd =
                container_of(work, struct tcm_vhost_cmd, work);
        struct tcm_vhost_nexus *tv_nexus;
        struct se_cmd *se_cmd = &cmd->tvc_se_cmd;
-       struct scatterlist *sg_ptr, *sg_bidi_ptr = NULL;
-       int rc, sg_no_bidi = 0;
+       struct scatterlist *sg_ptr, *sg_prot_ptr = NULL;
+       int rc;
 
+       /* FIXME: BIDI operation */
        if (cmd->tvc_sgl_count) {
                sg_ptr = cmd->tvc_sgl;
-/* FIXME: Fix BIDI operation in tcm_vhost_submission_work() */
-#if 0
-               if (se_cmd->se_cmd_flags & SCF_BIDI) {
-                       sg_bidi_ptr = NULL;
-                       sg_no_bidi = 0;
-               }
-#endif
+
+               if (cmd->tvc_prot_sgl_count)
+                       sg_prot_ptr = cmd->tvc_prot_sgl;
+               else
+                       se_cmd->prot_pto = true;
        } else {
                sg_ptr = NULL;
        }
@@ -894,7 +936,7 @@ static void tcm_vhost_submission_work(struct work_struct *work)
                        cmd->tvc_lun, cmd->tvc_exp_data_len,
                        cmd->tvc_task_attr, cmd->tvc_data_direction,
                        TARGET_SCF_ACK_KREF, sg_ptr, cmd->tvc_sgl_count,
-                       sg_bidi_ptr, sg_no_bidi, NULL, 0);
+                       NULL, 0, sg_prot_ptr, cmd->tvc_prot_sgl_count);
        if (rc < 0) {
                transport_send_check_condition_and_sense(se_cmd,
                                TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0);
@@ -926,12 +968,18 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
 {
        struct tcm_vhost_tpg **vs_tpg;
        struct virtio_scsi_cmd_req v_req;
+       struct virtio_scsi_cmd_req_pi v_req_pi;
        struct tcm_vhost_tpg *tpg;
        struct tcm_vhost_cmd *cmd;
-       u32 exp_data_len, data_first, data_num, data_direction;
+       u64 tag;
+       u32 exp_data_len, data_first, data_num, data_direction, prot_first;
        unsigned out, in, i;
-       int head, ret;
-       u8 target;
+       int head, ret, data_niov, prot_niov, prot_bytes;
+       size_t req_size;
+       u16 lun;
+       u8 *target, *lunp, task_attr;
+       bool hdr_pi;
+       void *req, *cdb;
 
        mutex_lock(&vq->mutex);
        /*
@@ -962,7 +1010,7 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
                        break;
                }
 
-/* FIXME: BIDI operation */
+               /* FIXME: BIDI operation */
                if (out == 1 && in == 1) {
                        data_direction = DMA_NONE;
                        data_first = 0;
@@ -992,29 +1040,38 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
                        break;
                }
 
-               if (unlikely(vq->iov[0].iov_len != sizeof(v_req))) {
-                       vq_err(vq, "Expecting virtio_scsi_cmd_req, got %zu"
-                               " bytes\n", vq->iov[0].iov_len);
+               if (vhost_has_feature(vq, VIRTIO_SCSI_F_T10_PI)) {
+                       req = &v_req_pi;
+                       lunp = &v_req_pi.lun[0];
+                       target = &v_req_pi.lun[1];
+                       req_size = sizeof(v_req_pi);
+                       hdr_pi = true;
+               } else {
+                       req = &v_req;
+                       lunp = &v_req.lun[0];
+                       target = &v_req.lun[1];
+                       req_size = sizeof(v_req);
+                       hdr_pi = false;
+               }
+
+               if (unlikely(vq->iov[0].iov_len < req_size)) {
+                       pr_err("Expecting virtio-scsi header: %zu, got %zu\n",
+                              req_size, vq->iov[0].iov_len);
                        break;
                }
-               pr_debug("Calling __copy_from_user: vq->iov[0].iov_base: %p,"
-                       " len: %zu\n", vq->iov[0].iov_base, sizeof(v_req));
-               ret = __copy_from_user(&v_req, vq->iov[0].iov_base,
-                               sizeof(v_req));
+               ret = memcpy_fromiovecend(req, &vq->iov[0], 0, req_size);
                if (unlikely(ret)) {
                        vq_err(vq, "Faulted on virtio_scsi_cmd_req\n");
                        break;
                }
 
                /* virtio-scsi spec requires byte 0 of the lun to be 1 */
-               if (unlikely(v_req.lun[0] != 1)) {
+               if (unlikely(*lunp != 1)) {
                        vhost_scsi_send_bad_target(vs, vq, head, out);
                        continue;
                }
 
-               /* Extract the tpgt */
-               target = v_req.lun[1];
-               tpg = ACCESS_ONCE(vs_tpg[target]);
+               tpg = ACCESS_ONCE(vs_tpg[*target]);
 
                /* Target does not exist, fail the request */
                if (unlikely(!tpg)) {
@@ -1022,17 +1079,79 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
                        continue;
                }
 
+               data_niov = data_num;
+               prot_niov = prot_first = prot_bytes = 0;
+               /*
+                * Determine if any protection information iovecs are preceeding
+                * the actual data payload, and adjust data_first + data_niov
+                * values accordingly for vhost_scsi_map_iov_to_sgl() below.
+                *
+                * Also extract virtio_scsi header bits for vhost_scsi_get_tag()
+                */
+               if (hdr_pi) {
+                       if (v_req_pi.pi_bytesout) {
+                               if (data_direction != DMA_TO_DEVICE) {
+                                       vq_err(vq, "Received non zero do_pi_niov"
+                                               ", but wrong data_direction\n");
+                                       goto err_cmd;
+                               }
+                               prot_bytes = v_req_pi.pi_bytesout;
+                       } else if (v_req_pi.pi_bytesin) {
+                               if (data_direction != DMA_FROM_DEVICE) {
+                                       vq_err(vq, "Received non zero di_pi_niov"
+                                               ", but wrong data_direction\n");
+                                       goto err_cmd;
+                               }
+                               prot_bytes = v_req_pi.pi_bytesin;
+                       }
+                       if (prot_bytes) {
+                               int tmp = 0;
+
+                               for (i = 0; i < data_num; i++) {
+                                       tmp += vq->iov[data_first + i].iov_len;
+                                       prot_niov++;
+                                       if (tmp >= prot_bytes)
+                                               break;
+                               }
+                               prot_first = data_first;
+                               data_first += prot_niov;
+                               data_niov = data_num - prot_niov;
+                       }
+                       tag = v_req_pi.tag;
+                       task_attr = v_req_pi.task_attr;
+                       cdb = &v_req_pi.cdb[0];
+                       lun = ((v_req_pi.lun[2] << 8) | v_req_pi.lun[3]) & 0x3FFF;
+               } else {
+                       tag = v_req.tag;
+                       task_attr = v_req.task_attr;
+                       cdb = &v_req.cdb[0];
+                       lun = ((v_req.lun[2] << 8) | v_req.lun[3]) & 0x3FFF;
+               }
                exp_data_len = 0;
-               for (i = 0; i < data_num; i++)
+               for (i = 0; i < data_niov; i++)
                        exp_data_len += vq->iov[data_first + i].iov_len;
+               /*
+                * Check that the recieved CDB size does not exceeded our
+                * hardcoded max for vhost-scsi
+                *
+                * TODO what if cdb was too small for varlen cdb header?
+                */
+               if (unlikely(scsi_command_size(cdb) > TCM_VHOST_MAX_CDB_SIZE)) {
+                       vq_err(vq, "Received SCSI CDB with command_size: %d that"
+                               " exceeds SCSI_MAX_VARLEN_CDB_SIZE: %d\n",
+                               scsi_command_size(cdb), TCM_VHOST_MAX_CDB_SIZE);
+                       goto err_cmd;
+               }
 
-               cmd = vhost_scsi_get_tag(vq, tpg, &v_req,
-                                        exp_data_len, data_direction);
+               cmd = vhost_scsi_get_tag(vq, tpg, cdb, tag, lun, task_attr,
+                                        exp_data_len + prot_bytes,
+                                        data_direction);
                if (IS_ERR(cmd)) {
                        vq_err(vq, "vhost_scsi_get_tag failed %ld\n",
                                        PTR_ERR(cmd));
                        goto err_cmd;
                }
+
                pr_debug("Allocated tv_cmd: %p exp_data_len: %d, data_direction"
                        ": %d\n", cmd, exp_data_len, data_direction);
 
@@ -1040,40 +1159,28 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
                cmd->tvc_vq = vq;
                cmd->tvc_resp = vq->iov[out].iov_base;
 
-               /*
-                * Copy in the recieved CDB descriptor into cmd->tvc_cdb
-                * that will be used by tcm_vhost_new_cmd_map() and down into
-                * target_setup_cmd_from_cdb()
-                */
-               memcpy(cmd->tvc_cdb, v_req.cdb, TCM_VHOST_MAX_CDB_SIZE);
-               /*
-                * Check that the recieved CDB size does not exceeded our
-                * hardcoded max for tcm_vhost
-                */
-               /* TODO what if cdb was too small for varlen cdb header? */
-               if (unlikely(scsi_command_size(cmd->tvc_cdb) >
-                                       TCM_VHOST_MAX_CDB_SIZE)) {
-                       vq_err(vq, "Received SCSI CDB with command_size: %d that"
-                               " exceeds SCSI_MAX_VARLEN_CDB_SIZE: %d\n",
-                               scsi_command_size(cmd->tvc_cdb),
-                               TCM_VHOST_MAX_CDB_SIZE);
-                       goto err_free;
-               }
-               cmd->tvc_lun = ((v_req.lun[2] << 8) | v_req.lun[3]) & 0x3FFF;
-
                pr_debug("vhost_scsi got command opcode: %#02x, lun: %d\n",
                        cmd->tvc_cdb[0], cmd->tvc_lun);
 
+               if (prot_niov) {
+                       ret = vhost_scsi_map_iov_to_prot(cmd,
+                                       &vq->iov[prot_first], prot_niov,
+                                       data_direction == DMA_FROM_DEVICE);
+                       if (unlikely(ret)) {
+                               vq_err(vq, "Failed to map iov to"
+                                       " prot_sgl\n");
+                               goto err_free;
+                       }
+               }
                if (data_direction != DMA_NONE) {
                        ret = vhost_scsi_map_iov_to_sgl(cmd,
-                                       &vq->iov[data_first], data_num,
+                                       &vq->iov[data_first], data_niov,
                                        data_direction == DMA_FROM_DEVICE);
                        if (unlikely(ret)) {
                                vq_err(vq, "Failed to map iov to sgl\n");
                                goto err_free;
                        }
                }
-
                /*
                 * Save the descriptor from vhost_get_vq_desc() to be used to
                 * complete the virtio-scsi request in TCM callback context via
@@ -1716,6 +1823,7 @@ static void tcm_vhost_free_cmd_map_res(struct tcm_vhost_nexus *nexus,
                tv_cmd = &((struct tcm_vhost_cmd *)se_sess->sess_cmd_map)[i];
 
                kfree(tv_cmd->tvc_sgl);
+               kfree(tv_cmd->tvc_prot_sgl);
                kfree(tv_cmd->tvc_upages);
        }
 }
@@ -1750,7 +1858,7 @@ static int tcm_vhost_make_nexus(struct tcm_vhost_tpg *tpg,
        tv_nexus->tvn_se_sess = transport_init_session_tags(
                                        TCM_VHOST_DEFAULT_TAGS,
                                        sizeof(struct tcm_vhost_cmd),
-                                       TARGET_PROT_NORMAL);
+                                       TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS);
        if (IS_ERR(tv_nexus->tvn_se_sess)) {
                mutex_unlock(&tpg->tv_tpg_mutex);
                kfree(tv_nexus);
@@ -1769,12 +1877,20 @@ static int tcm_vhost_make_nexus(struct tcm_vhost_tpg *tpg,
                }
 
                tv_cmd->tvc_upages = kzalloc(sizeof(struct page *) *
-                                       TCM_VHOST_PREALLOC_PAGES, GFP_KERNEL);
+                                       TCM_VHOST_PREALLOC_UPAGES, GFP_KERNEL);
                if (!tv_cmd->tvc_upages) {
                        mutex_unlock(&tpg->tv_tpg_mutex);
                        pr_err("Unable to allocate tv_cmd->tvc_upages\n");
                        goto out;
                }
+
+               tv_cmd->tvc_prot_sgl = kzalloc(sizeof(struct scatterlist) *
+                                       TCM_VHOST_PREALLOC_PROT_SGLS, GFP_KERNEL);
+               if (!tv_cmd->tvc_prot_sgl) {
+                       mutex_unlock(&tpg->tv_tpg_mutex);
+                       pr_err("Unable to allocate tv_cmd->tvc_prot_sgl\n");
+                       goto out;
+               }
        }
        /*
         * Since we are running in 'demo mode' this call with generate a
index cbb09ce9730ac0494dc43fbef5d7d6f2ddd05b0b..5747417069cadf3501cf8210a7cb834b6b29c942 100644 (file)
@@ -4,10 +4,10 @@
 
 # Create $(fwabs) from $(CONFIG_EXTRA_FIRMWARE_DIR) -- if it doesn't have a
 # leading /, it's relative to $(srctree).
-fwdir := $(subst ",,$(CONFIG_EXTRA_FIRMWARE_DIR))
+fwdir := $(subst $(quote),,$(CONFIG_EXTRA_FIRMWARE_DIR))
 fwabs := $(addprefix $(srctree)/,$(filter-out /%,$(fwdir)))$(filter /%,$(fwdir))
 
-fw-external-y := $(subst ",,$(CONFIG_EXTRA_FIRMWARE))
+fw-external-y := $(subst $(quote),,$(CONFIG_EXTRA_FIRMWARE))
 
 # There are three cases to care about:
 # 1. Building kernel with CONFIG_FIRMWARE_IN_KERNEL=y -- $(fw-shipped-y) should
@@ -138,12 +138,6 @@ fw-shipped-$(CONFIG_YAM) += yam/1200.bin yam/9600.bin
 
 fw-shipped-all := $(fw-shipped-y) $(fw-shipped-m) $(fw-shipped-)
 
-# Directories which we _might_ need to create, so we have a rule for them.
-firmware-dirs := $(sort $(addprefix $(objtree)/$(obj)/,$(dir $(fw-external-y) $(fw-shipped-all))))
-
-quiet_cmd_mkdir = MKDIR   $(patsubst $(objtree)/%,%,$@)
-      cmd_mkdir = mkdir -p $@
-
 quiet_cmd_ihex  = IHEX    $@
       cmd_ihex  = $(OBJCOPY) -Iihex -Obinary $< $@
 
@@ -184,21 +178,10 @@ wordsize_deps := $(wildcard include/config/64bit.h include/config/32bit.h \
                include/config/superh32.h include/config/superh64.h \
                include/config/x86_32.h include/config/x86_64.h)
 
-# Workaround for make < 3.81, where .SECONDEXPANSION doesn't work.
-# It'll end up depending on these targets, so make them a PHONY rule which
-# depends on _all_ the directories in $(firmware-dirs), and it'll work out OK.
-PHONY += $(objtree)/$$(%) $(objtree)/$(obj)/$$(%)
-$(objtree)/$$(%) $(objtree)/$(obj)/$$(%): $(firmware-dirs)
-       @true
-
-# For the $$(dir %) trick, where we need % to be expanded first.
-.SECONDEXPANSION:
-
-$(patsubst %,$(obj)/%.gen.S, $(fw-shipped-y)): %: $(wordsize_deps) \
-               | $(objtree)/$$(dir %)
+$(patsubst %,$(obj)/%.gen.S, $(fw-shipped-y)): %: $(wordsize_deps)
        $(call cmd,fwbin,$(patsubst %.gen.S,%,$@))
 $(patsubst %,$(obj)/%.gen.S, $(fw-external-y)): %: $(wordsize_deps) \
-               include/config/extra/firmware/dir.h | $(objtree)/$$(dir %)
+               include/config/extra/firmware/dir.h
        $(call cmd,fwbin,$(fwabs)/$(patsubst $(obj)/%.gen.S,%,$@))
 
 # The .o files depend on the binaries directly; the .S files don't.
@@ -207,7 +190,7 @@ $(patsubst %,$(obj)/%.gen.o, $(fw-external-y)): $(obj)/%.gen.o: $(fwdir)/%
 
 # .ihex is used just as a simple way to hold binary files in a source tree
 # where binaries are frowned upon. They are directly converted with objcopy.
-$(obj)/%: $(obj)/%.ihex | $(objtree)/$(obj)/$$(dir %)
+$(obj)/%: $(obj)/%.ihex
        $(call cmd,ihex)
 
 # Don't depend on ihex2fw if we're installing and it already exists.
@@ -226,16 +209,13 @@ endif
 # is actually meaningful, because the firmware has to be loaded in a certain
 # order rather than as a single binary blob. Thus, we convert them into our
 # more compact binary representation of ihex records (<linux/ihex.h>)
-$(obj)/%.fw: $(obj)/%.HEX $(ihex2fw_dep) | $(objtree)/$(obj)/$$(dir %)
+$(obj)/%.fw: $(obj)/%.HEX $(ihex2fw_dep)
        $(call cmd,ihex2fw)
 
 # .H16 is our own modified form of Intel HEX, with 16-bit length for records.
-$(obj)/%.fw: $(obj)/%.H16 $(ihex2fw_dep) | $(objtree)/$(obj)/$$(dir %)
+$(obj)/%.fw: $(obj)/%.H16 $(ihex2fw_dep)
        $(call cmd,h16tofw)
 
-$(firmware-dirs):
-       $(call cmd,mkdir)
-
 obj-y                           += $(patsubst %,%.gen.o, $(fw-external-y))
 obj-$(CONFIG_FIRMWARE_IN_KERNEL) += $(patsubst %,%.gen.o, $(fw-shipped-y))
 
index 21887d63dad589a53e3da21fccb00c45a5c28ab6..469f2e8657e8426bfb3ad94fb12dde25e06ccb18 100644 (file)
@@ -104,12 +104,6 @@ int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type)
        umode_t new_mode = inode->i_mode, old_mode = inode->i_mode;
        struct dentry *dentry;
 
-       if (acl) {
-               ret = posix_acl_valid(acl);
-               if (ret < 0)
-                       goto out;
-       }
-
        switch (type) {
        case ACL_TYPE_ACCESS:
                name = POSIX_ACL_XATTR_ACCESS;
index 4f3f69079f362280379edf3b13c4766247c764fa..90b3954d48edfa88b10b22dd98d5fcc0bc6c6ab7 100644 (file)
@@ -211,18 +211,15 @@ static int readpage_nounlock(struct file *filp, struct page *page)
                SetPageError(page);
                ceph_fscache_readpage_cancel(inode, page);
                goto out;
-       } else {
-               if (err < PAGE_CACHE_SIZE) {
-               /* zero fill remainder of page */
-                       zero_user_segment(page, err, PAGE_CACHE_SIZE);
-               } else {
-                       flush_dcache_page(page);
-               }
        }
-       SetPageUptodate(page);
+       if (err < PAGE_CACHE_SIZE)
+               /* zero fill remainder of page */
+               zero_user_segment(page, err, PAGE_CACHE_SIZE);
+       else
+               flush_dcache_page(page);
 
-       if (err >= 0)
-               ceph_readpage_to_fscache(inode, page);
+       SetPageUptodate(page);
+       ceph_readpage_to_fscache(inode, page);
 
 out:
        return err < 0 ? err : 0;
index c561b628ebce519d111d159f541b9df88242a5b1..1fde164b74b54a258cdff8e7c89f52191713c986 100644 (file)
@@ -221,8 +221,8 @@ int ceph_unreserve_caps(struct ceph_mds_client *mdsc,
        return 0;
 }
 
-static struct ceph_cap *get_cap(struct ceph_mds_client *mdsc,
-                               struct ceph_cap_reservation *ctx)
+struct ceph_cap *ceph_get_cap(struct ceph_mds_client *mdsc,
+                             struct ceph_cap_reservation *ctx)
 {
        struct ceph_cap *cap = NULL;
 
@@ -508,15 +508,14 @@ static void __check_cap_issue(struct ceph_inode_info *ci, struct ceph_cap *cap,
  * it is < 0.  (This is so we can atomically add the cap and add an
  * open file reference to it.)
  */
-int ceph_add_cap(struct inode *inode,
-                struct ceph_mds_session *session, u64 cap_id,
-                int fmode, unsigned issued, unsigned wanted,
-                unsigned seq, unsigned mseq, u64 realmino, int flags,
-                struct ceph_cap_reservation *caps_reservation)
+void ceph_add_cap(struct inode *inode,
+                 struct ceph_mds_session *session, u64 cap_id,
+                 int fmode, unsigned issued, unsigned wanted,
+                 unsigned seq, unsigned mseq, u64 realmino, int flags,
+                 struct ceph_cap **new_cap)
 {
        struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
        struct ceph_inode_info *ci = ceph_inode(inode);
-       struct ceph_cap *new_cap = NULL;
        struct ceph_cap *cap;
        int mds = session->s_mds;
        int actual_wanted;
@@ -531,20 +530,10 @@ int ceph_add_cap(struct inode *inode,
        if (fmode >= 0)
                wanted |= ceph_caps_for_mode(fmode);
 
-retry:
-       spin_lock(&ci->i_ceph_lock);
        cap = __get_cap_for_mds(ci, mds);
        if (!cap) {
-               if (new_cap) {
-                       cap = new_cap;
-                       new_cap = NULL;
-               } else {
-                       spin_unlock(&ci->i_ceph_lock);
-                       new_cap = get_cap(mdsc, caps_reservation);
-                       if (new_cap == NULL)
-                               return -ENOMEM;
-                       goto retry;
-               }
+               cap = *new_cap;
+               *new_cap = NULL;
 
                cap->issued = 0;
                cap->implemented = 0;
@@ -562,9 +551,6 @@ int ceph_add_cap(struct inode *inode,
                session->s_nr_caps++;
                spin_unlock(&session->s_cap_lock);
        } else {
-               if (new_cap)
-                       ceph_put_cap(mdsc, new_cap);
-
                /*
                 * auth mds of the inode changed. we received the cap export
                 * message, but still haven't received the cap import message.
@@ -626,7 +612,6 @@ int ceph_add_cap(struct inode *inode,
                        ci->i_auth_cap = cap;
                        cap->mds_wanted = wanted;
                }
-               ci->i_cap_exporting_issued = 0;
        } else {
                WARN_ON(ci->i_auth_cap == cap);
        }
@@ -648,9 +633,6 @@ int ceph_add_cap(struct inode *inode,
 
        if (fmode >= 0)
                __ceph_get_fmode(ci, fmode);
-       spin_unlock(&ci->i_ceph_lock);
-       wake_up_all(&ci->i_cap_wq);
-       return 0;
 }
 
 /*
@@ -685,7 +667,7 @@ static int __cap_is_valid(struct ceph_cap *cap)
  */
 int __ceph_caps_issued(struct ceph_inode_info *ci, int *implemented)
 {
-       int have = ci->i_snap_caps | ci->i_cap_exporting_issued;
+       int have = ci->i_snap_caps;
        struct ceph_cap *cap;
        struct rb_node *p;
 
@@ -900,7 +882,7 @@ int __ceph_caps_mds_wanted(struct ceph_inode_info *ci)
  */
 static int __ceph_is_any_caps(struct ceph_inode_info *ci)
 {
-       return !RB_EMPTY_ROOT(&ci->i_caps) || ci->i_cap_exporting_issued;
+       return !RB_EMPTY_ROOT(&ci->i_caps);
 }
 
 int ceph_is_any_caps(struct inode *inode)
@@ -2397,32 +2379,30 @@ static void invalidate_aliases(struct inode *inode)
  * actually be a revocation if it specifies a smaller cap set.)
  *
  * caller holds s_mutex and i_ceph_lock, we drop both.
- *
- * return value:
- *  0 - ok
- *  1 - check_caps on auth cap only (writeback)
- *  2 - check_caps (ack revoke)
  */
-static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant,
+static void handle_cap_grant(struct ceph_mds_client *mdsc,
+                            struct inode *inode, struct ceph_mds_caps *grant,
+                            void *snaptrace, int snaptrace_len,
+                            struct ceph_buffer *xattr_buf,
                             struct ceph_mds_session *session,
-                            struct ceph_cap *cap,
-                            struct ceph_buffer *xattr_buf)
-               __releases(ci->i_ceph_lock)
+                            struct ceph_cap *cap, int issued)
+       __releases(ci->i_ceph_lock)
 {
        struct ceph_inode_info *ci = ceph_inode(inode);
        int mds = session->s_mds;
        int seq = le32_to_cpu(grant->seq);
        int newcaps = le32_to_cpu(grant->caps);
-       int issued, implemented, used, wanted, dirty;
+       int used, wanted, dirty;
        u64 size = le64_to_cpu(grant->size);
        u64 max_size = le64_to_cpu(grant->max_size);
        struct timespec mtime, atime, ctime;
        int check_caps = 0;
-       int wake = 0;
-       int writeback = 0;
-       int queue_invalidate = 0;
-       int deleted_inode = 0;
-       int queue_revalidate = 0;
+       bool wake = 0;
+       bool writeback = 0;
+       bool queue_trunc = 0;
+       bool queue_invalidate = 0;
+       bool queue_revalidate = 0;
+       bool deleted_inode = 0;
 
        dout("handle_cap_grant inode %p cap %p mds%d seq %d %s\n",
             inode, cap, mds, seq, ceph_cap_string(newcaps));
@@ -2466,16 +2446,13 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant,
        }
 
        /* side effects now are allowed */
-
-       issued = __ceph_caps_issued(ci, &implemented);
-       issued |= implemented | __ceph_caps_dirty(ci);
-
        cap->cap_gen = session->s_cap_gen;
        cap->seq = seq;
 
        __check_cap_issue(ci, cap, newcaps);
 
-       if ((issued & CEPH_CAP_AUTH_EXCL) == 0) {
+       if ((newcaps & CEPH_CAP_AUTH_SHARED) &&
+           (issued & CEPH_CAP_AUTH_EXCL) == 0) {
                inode->i_mode = le32_to_cpu(grant->mode);
                inode->i_uid = make_kuid(&init_user_ns, le32_to_cpu(grant->uid));
                inode->i_gid = make_kgid(&init_user_ns, le32_to_cpu(grant->gid));
@@ -2484,7 +2461,8 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant,
                     from_kgid(&init_user_ns, inode->i_gid));
        }
 
-       if ((issued & CEPH_CAP_LINK_EXCL) == 0) {
+       if ((newcaps & CEPH_CAP_AUTH_SHARED) &&
+           (issued & CEPH_CAP_LINK_EXCL) == 0) {
                set_nlink(inode, le32_to_cpu(grant->nlink));
                if (inode->i_nlink == 0 &&
                    (newcaps & (CEPH_CAP_LINK_SHARED | CEPH_CAP_LINK_EXCL)))
@@ -2511,30 +2489,35 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant,
        if ((issued & CEPH_CAP_FILE_CACHE) && ci->i_rdcache_gen > 1)
                queue_revalidate = 1;
 
-       /* size/ctime/mtime/atime? */
-       ceph_fill_file_size(inode, issued,
-                           le32_to_cpu(grant->truncate_seq),
-                           le64_to_cpu(grant->truncate_size), size);
-       ceph_decode_timespec(&mtime, &grant->mtime);
-       ceph_decode_timespec(&atime, &grant->atime);
-       ceph_decode_timespec(&ctime, &grant->ctime);
-       ceph_fill_file_time(inode, issued,
-                           le32_to_cpu(grant->time_warp_seq), &ctime, &mtime,
-                           &atime);
-
-
-       /* file layout may have changed */
-       ci->i_layout = grant->layout;
-
-       /* max size increase? */
-       if (ci->i_auth_cap == cap && max_size != ci->i_max_size) {
-               dout("max_size %lld -> %llu\n", ci->i_max_size, max_size);
-               ci->i_max_size = max_size;
-               if (max_size >= ci->i_wanted_max_size) {
-                       ci->i_wanted_max_size = 0;  /* reset */
-                       ci->i_requested_max_size = 0;
+       if (newcaps & CEPH_CAP_ANY_RD) {
+               /* ctime/mtime/atime? */
+               ceph_decode_timespec(&mtime, &grant->mtime);
+               ceph_decode_timespec(&atime, &grant->atime);
+               ceph_decode_timespec(&ctime, &grant->ctime);
+               ceph_fill_file_time(inode, issued,
+                                   le32_to_cpu(grant->time_warp_seq),
+                                   &ctime, &mtime, &atime);
+       }
+
+       if (newcaps & (CEPH_CAP_ANY_FILE_RD | CEPH_CAP_ANY_FILE_WR)) {
+               /* file layout may have changed */
+               ci->i_layout = grant->layout;
+               /* size/truncate_seq? */
+               queue_trunc = ceph_fill_file_size(inode, issued,
+                                       le32_to_cpu(grant->truncate_seq),
+                                       le64_to_cpu(grant->truncate_size),
+                                       size);
+               /* max size increase? */
+               if (ci->i_auth_cap == cap && max_size != ci->i_max_size) {
+                       dout("max_size %lld -> %llu\n",
+                            ci->i_max_size, max_size);
+                       ci->i_max_size = max_size;
+                       if (max_size >= ci->i_wanted_max_size) {
+                               ci->i_wanted_max_size = 0;  /* reset */
+                               ci->i_requested_max_size = 0;
+                       }
+                       wake = 1;
                }
-               wake = 1;
        }
 
        /* check cap bits */
@@ -2595,6 +2578,23 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant,
 
        spin_unlock(&ci->i_ceph_lock);
 
+       if (le32_to_cpu(grant->op) == CEPH_CAP_OP_IMPORT) {
+               down_write(&mdsc->snap_rwsem);
+               ceph_update_snap_trace(mdsc, snaptrace,
+                                      snaptrace + snaptrace_len, false);
+               downgrade_write(&mdsc->snap_rwsem);
+               kick_flushing_inode_caps(mdsc, session, inode);
+               up_read(&mdsc->snap_rwsem);
+               if (newcaps & ~issued)
+                       wake = 1;
+       }
+
+       if (queue_trunc) {
+               ceph_queue_vmtruncate(inode);
+               ceph_queue_revalidate(inode);
+       } else if (queue_revalidate)
+               ceph_queue_revalidate(inode);
+
        if (writeback)
                /*
                 * queue inode for writeback: we can't actually call
@@ -2606,8 +2606,6 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant,
                ceph_queue_invalidate(inode);
        if (deleted_inode)
                invalidate_aliases(inode);
-       if (queue_revalidate)
-               ceph_queue_revalidate(inode);
        if (wake)
                wake_up_all(&ci->i_cap_wq);
 
@@ -2784,7 +2782,7 @@ static void handle_cap_export(struct inode *inode, struct ceph_mds_caps *ex,
 {
        struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
        struct ceph_mds_session *tsession = NULL;
-       struct ceph_cap *cap, *tcap;
+       struct ceph_cap *cap, *tcap, *new_cap = NULL;
        struct ceph_inode_info *ci = ceph_inode(inode);
        u64 t_cap_id;
        unsigned mseq = le32_to_cpu(ex->migrate_seq);
@@ -2807,7 +2805,7 @@ static void handle_cap_export(struct inode *inode, struct ceph_mds_caps *ex,
 retry:
        spin_lock(&ci->i_ceph_lock);
        cap = __get_cap_for_mds(ci, mds);
-       if (!cap)
+       if (!cap || cap->cap_id != le64_to_cpu(ex->cap_id))
                goto out_unlock;
 
        if (target < 0) {
@@ -2846,15 +2844,14 @@ static void handle_cap_export(struct inode *inode, struct ceph_mds_caps *ex,
                }
                __ceph_remove_cap(cap, false);
                goto out_unlock;
-       }
-
-       if (tsession) {
-               int flag = (cap == ci->i_auth_cap) ? CEPH_CAP_FLAG_AUTH : 0;
-               spin_unlock(&ci->i_ceph_lock);
+       } else if (tsession) {
                /* add placeholder for the export tagert */
+               int flag = (cap == ci->i_auth_cap) ? CEPH_CAP_FLAG_AUTH : 0;
                ceph_add_cap(inode, tsession, t_cap_id, -1, issued, 0,
-                            t_seq - 1, t_mseq, (u64)-1, flag, NULL);
-               goto retry;
+                            t_seq - 1, t_mseq, (u64)-1, flag, &new_cap);
+
+               __ceph_remove_cap(cap, false);
+               goto out_unlock;
        }
 
        spin_unlock(&ci->i_ceph_lock);
@@ -2873,6 +2870,7 @@ static void handle_cap_export(struct inode *inode, struct ceph_mds_caps *ex,
                                          SINGLE_DEPTH_NESTING);
                }
                ceph_add_cap_releases(mdsc, tsession);
+               new_cap = ceph_get_cap(mdsc, NULL);
        } else {
                WARN_ON(1);
                tsession = NULL;
@@ -2887,24 +2885,27 @@ static void handle_cap_export(struct inode *inode, struct ceph_mds_caps *ex,
                mutex_unlock(&tsession->s_mutex);
                ceph_put_mds_session(tsession);
        }
+       if (new_cap)
+               ceph_put_cap(mdsc, new_cap);
 }
 
 /*
- * Handle cap IMPORT.  If there are temp bits from an older EXPORT,
- * clean them up.
+ * Handle cap IMPORT.
  *
- * caller holds s_mutex.
+ * caller holds s_mutex. acquires i_ceph_lock
  */
 static void handle_cap_import(struct ceph_mds_client *mdsc,
                              struct inode *inode, struct ceph_mds_caps *im,
                              struct ceph_mds_cap_peer *ph,
                              struct ceph_mds_session *session,
-                             void *snaptrace, int snaptrace_len)
+                             struct ceph_cap **target_cap, int *old_issued)
+       __acquires(ci->i_ceph_lock)
 {
        struct ceph_inode_info *ci = ceph_inode(inode);
-       struct ceph_cap *cap;
+       struct ceph_cap *cap, *ocap, *new_cap = NULL;
        int mds = session->s_mds;
-       unsigned issued = le32_to_cpu(im->caps);
+       int issued;
+       unsigned caps = le32_to_cpu(im->caps);
        unsigned wanted = le32_to_cpu(im->wanted);
        unsigned seq = le32_to_cpu(im->seq);
        unsigned mseq = le32_to_cpu(im->migrate_seq);
@@ -2924,40 +2925,52 @@ static void handle_cap_import(struct ceph_mds_client *mdsc,
        dout("handle_cap_import inode %p ci %p mds%d mseq %d peer %d\n",
             inode, ci, mds, mseq, peer);
 
+retry:
        spin_lock(&ci->i_ceph_lock);
-       cap = peer >= 0 ? __get_cap_for_mds(ci, peer) : NULL;
-       if (cap && cap->cap_id == p_cap_id) {
+       cap = __get_cap_for_mds(ci, mds);
+       if (!cap) {
+               if (!new_cap) {
+                       spin_unlock(&ci->i_ceph_lock);
+                       new_cap = ceph_get_cap(mdsc, NULL);
+                       goto retry;
+               }
+               cap = new_cap;
+       } else {
+               if (new_cap) {
+                       ceph_put_cap(mdsc, new_cap);
+                       new_cap = NULL;
+               }
+       }
+
+       __ceph_caps_issued(ci, &issued);
+       issued |= __ceph_caps_dirty(ci);
+
+       ceph_add_cap(inode, session, cap_id, -1, caps, wanted, seq, mseq,
+                    realmino, CEPH_CAP_FLAG_AUTH, &new_cap);
+
+       ocap = peer >= 0 ? __get_cap_for_mds(ci, peer) : NULL;
+       if (ocap && ocap->cap_id == p_cap_id) {
                dout(" remove export cap %p mds%d flags %d\n",
-                    cap, peer, ph->flags);
+                    ocap, peer, ph->flags);
                if ((ph->flags & CEPH_CAP_FLAG_AUTH) &&
-                   (cap->seq != le32_to_cpu(ph->seq) ||
-                    cap->mseq != le32_to_cpu(ph->mseq))) {
+                   (ocap->seq != le32_to_cpu(ph->seq) ||
+                    ocap->mseq != le32_to_cpu(ph->mseq))) {
                        pr_err("handle_cap_import: mismatched seq/mseq: "
                               "ino (%llx.%llx) mds%d seq %d mseq %d "
                               "importer mds%d has peer seq %d mseq %d\n",
-                              ceph_vinop(inode), peer, cap->seq,
-                              cap->mseq, mds, le32_to_cpu(ph->seq),
+                              ceph_vinop(inode), peer, ocap->seq,
+                              ocap->mseq, mds, le32_to_cpu(ph->seq),
                               le32_to_cpu(ph->mseq));
                }
-               ci->i_cap_exporting_issued = cap->issued;
-               __ceph_remove_cap(cap, (ph->flags & CEPH_CAP_FLAG_RELEASE));
+               __ceph_remove_cap(ocap, (ph->flags & CEPH_CAP_FLAG_RELEASE));
        }
 
        /* make sure we re-request max_size, if necessary */
        ci->i_wanted_max_size = 0;
        ci->i_requested_max_size = 0;
-       spin_unlock(&ci->i_ceph_lock);
-
-       down_write(&mdsc->snap_rwsem);
-       ceph_update_snap_trace(mdsc, snaptrace, snaptrace+snaptrace_len,
-                              false);
-       downgrade_write(&mdsc->snap_rwsem);
-       ceph_add_cap(inode, session, cap_id, -1,
-                    issued, wanted, seq, mseq, realmino, CEPH_CAP_FLAG_AUTH,
-                    NULL /* no caps context */);
-       kick_flushing_inode_caps(mdsc, session, inode);
-       up_read(&mdsc->snap_rwsem);
 
+       *old_issued = issued;
+       *target_cap = cap;
 }
 
 /*
@@ -2977,7 +2990,7 @@ void ceph_handle_caps(struct ceph_mds_session *session,
        struct ceph_mds_caps *h;
        struct ceph_mds_cap_peer *peer = NULL;
        int mds = session->s_mds;
-       int op;
+       int op, issued;
        u32 seq, mseq;
        struct ceph_vino vino;
        u64 cap_id;
@@ -3069,7 +3082,10 @@ void ceph_handle_caps(struct ceph_mds_session *session,
 
        case CEPH_CAP_OP_IMPORT:
                handle_cap_import(mdsc, inode, h, peer, session,
-                                 snaptrace, snaptrace_len);
+                                 &cap, &issued);
+               handle_cap_grant(mdsc, inode, h,  snaptrace, snaptrace_len,
+                                msg->middle, session, cap, issued);
+               goto done_unlocked;
        }
 
        /* the rest require a cap */
@@ -3086,8 +3102,10 @@ void ceph_handle_caps(struct ceph_mds_session *session,
        switch (op) {
        case CEPH_CAP_OP_REVOKE:
        case CEPH_CAP_OP_GRANT:
-       case CEPH_CAP_OP_IMPORT:
-               handle_cap_grant(inode, h, session, cap, msg->middle);
+               __ceph_caps_issued(ci, &issued);
+               issued |= __ceph_caps_dirty(ci);
+               handle_cap_grant(mdsc, inode, h, NULL, 0, msg->middle,
+                                session, cap, issued);
                goto done_unlocked;
 
        case CEPH_CAP_OP_FLUSH_ACK:
index 00d6af6a32ec9a37f204705da091122e7ddd7151..8d7d782f43822d2fd29b1f6fe941093ff7e247d3 100644 (file)
@@ -169,7 +169,7 @@ static struct dentry *__get_parent(struct super_block *sb,
        return dentry;
 }
 
-struct dentry *ceph_get_parent(struct dentry *child)
+static struct dentry *ceph_get_parent(struct dentry *child)
 {
        /* don't re-export snaps */
        if (ceph_snap(child->d_inode) != CEPH_NOSNAP)
index e4fff9ff1c27c890c939c8836afe1d60504d6ab7..04c89c266cecffa2396d0b63774d4811063bbb0f 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/writeback.h>
 #include <linux/vmalloc.h>
 #include <linux/posix_acl.h>
+#include <linux/random.h>
 
 #include "super.h"
 #include "mds_client.h"
@@ -179,9 +180,8 @@ struct ceph_inode_frag *__ceph_find_frag(struct ceph_inode_info *ci, u32 f)
  * specified, copy the frag delegation info to the caller if
  * it is present.
  */
-u32 ceph_choose_frag(struct ceph_inode_info *ci, u32 v,
-                    struct ceph_inode_frag *pfrag,
-                    int *found)
+static u32 __ceph_choose_frag(struct ceph_inode_info *ci, u32 v,
+                             struct ceph_inode_frag *pfrag, int *found)
 {
        u32 t = ceph_frag_make(0, 0);
        struct ceph_inode_frag *frag;
@@ -191,7 +191,6 @@ u32 ceph_choose_frag(struct ceph_inode_info *ci, u32 v,
        if (found)
                *found = 0;
 
-       mutex_lock(&ci->i_fragtree_mutex);
        while (1) {
                WARN_ON(!ceph_frag_contains_value(t, v));
                frag = __ceph_find_frag(ci, t);
@@ -220,10 +219,19 @@ u32 ceph_choose_frag(struct ceph_inode_info *ci, u32 v,
        }
        dout("choose_frag(%x) = %x\n", v, t);
 
-       mutex_unlock(&ci->i_fragtree_mutex);
        return t;
 }
 
+u32 ceph_choose_frag(struct ceph_inode_info *ci, u32 v,
+                    struct ceph_inode_frag *pfrag, int *found)
+{
+       u32 ret;
+       mutex_lock(&ci->i_fragtree_mutex);
+       ret = __ceph_choose_frag(ci, v, pfrag, found);
+       mutex_unlock(&ci->i_fragtree_mutex);
+       return ret;
+}
+
 /*
  * Process dirfrag (delegation) info from the mds.  Include leaf
  * fragment in tree ONLY if ndist > 0.  Otherwise, only
@@ -237,11 +245,17 @@ static int ceph_fill_dirfrag(struct inode *inode,
        u32 id = le32_to_cpu(dirinfo->frag);
        int mds = le32_to_cpu(dirinfo->auth);
        int ndist = le32_to_cpu(dirinfo->ndist);
+       int diri_auth = -1;
        int i;
        int err = 0;
 
+       spin_lock(&ci->i_ceph_lock);
+       if (ci->i_auth_cap)
+               diri_auth = ci->i_auth_cap->mds;
+       spin_unlock(&ci->i_ceph_lock);
+
        mutex_lock(&ci->i_fragtree_mutex);
-       if (ndist == 0) {
+       if (ndist == 0 && mds == diri_auth) {
                /* no delegation info needed. */
                frag = __ceph_find_frag(ci, id);
                if (!frag)
@@ -286,6 +300,75 @@ static int ceph_fill_dirfrag(struct inode *inode,
        return err;
 }
 
+static int ceph_fill_fragtree(struct inode *inode,
+                             struct ceph_frag_tree_head *fragtree,
+                             struct ceph_mds_reply_dirfrag *dirinfo)
+{
+       struct ceph_inode_info *ci = ceph_inode(inode);
+       struct ceph_inode_frag *frag;
+       struct rb_node *rb_node;
+       int i;
+       u32 id, nsplits;
+       bool update = false;
+
+       mutex_lock(&ci->i_fragtree_mutex);
+       nsplits = le32_to_cpu(fragtree->nsplits);
+       if (nsplits) {
+               i = prandom_u32() % nsplits;
+               id = le32_to_cpu(fragtree->splits[i].frag);
+               if (!__ceph_find_frag(ci, id))
+                       update = true;
+       } else if (!RB_EMPTY_ROOT(&ci->i_fragtree)) {
+               rb_node = rb_first(&ci->i_fragtree);
+               frag = rb_entry(rb_node, struct ceph_inode_frag, node);
+               if (frag->frag != ceph_frag_make(0, 0) || rb_next(rb_node))
+                       update = true;
+       }
+       if (!update && dirinfo) {
+               id = le32_to_cpu(dirinfo->frag);
+               if (id != __ceph_choose_frag(ci, id, NULL, NULL))
+                       update = true;
+       }
+       if (!update)
+               goto out_unlock;
+
+       dout("fill_fragtree %llx.%llx\n", ceph_vinop(inode));
+       rb_node = rb_first(&ci->i_fragtree);
+       for (i = 0; i < nsplits; i++) {
+               id = le32_to_cpu(fragtree->splits[i].frag);
+               frag = NULL;
+               while (rb_node) {
+                       frag = rb_entry(rb_node, struct ceph_inode_frag, node);
+                       if (ceph_frag_compare(frag->frag, id) >= 0) {
+                               if (frag->frag != id)
+                                       frag = NULL;
+                               else
+                                       rb_node = rb_next(rb_node);
+                               break;
+                       }
+                       rb_node = rb_next(rb_node);
+                       rb_erase(&frag->node, &ci->i_fragtree);
+                       kfree(frag);
+                       frag = NULL;
+               }
+               if (!frag) {
+                       frag = __get_or_create_frag(ci, id);
+                       if (IS_ERR(frag))
+                               continue;
+               }
+               frag->split_by = le32_to_cpu(fragtree->splits[i].by);
+               dout(" frag %x split by %d\n", frag->frag, frag->split_by);
+       }
+       while (rb_node) {
+               frag = rb_entry(rb_node, struct ceph_inode_frag, node);
+               rb_node = rb_next(rb_node);
+               rb_erase(&frag->node, &ci->i_fragtree);
+               kfree(frag);
+       }
+out_unlock:
+       mutex_unlock(&ci->i_fragtree_mutex);
+       return 0;
+}
 
 /*
  * initialize a newly allocated inode.
@@ -341,7 +424,6 @@ struct inode *ceph_alloc_inode(struct super_block *sb)
        INIT_LIST_HEAD(&ci->i_cap_snaps);
        ci->i_head_snapc = NULL;
        ci->i_snap_caps = 0;
-       ci->i_cap_exporting_issued = 0;
 
        for (i = 0; i < CEPH_FILE_MODE_NUM; i++)
                ci->i_nr_by_mode[i] = 0;
@@ -407,7 +489,7 @@ void ceph_destroy_inode(struct inode *inode)
 
        /*
         * we may still have a snap_realm reference if there are stray
-        * caps in i_cap_exporting_issued or i_snap_caps.
+        * caps in i_snap_caps.
         */
        if (ci->i_snap_realm) {
                struct ceph_mds_client *mdsc =
@@ -582,22 +664,26 @@ static int fill_inode(struct inode *inode,
                      unsigned long ttl_from, int cap_fmode,
                      struct ceph_cap_reservation *caps_reservation)
 {
+       struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
        struct ceph_mds_reply_inode *info = iinfo->in;
        struct ceph_inode_info *ci = ceph_inode(inode);
-       int i;
-       int issued = 0, implemented;
+       int issued = 0, implemented, new_issued;
        struct timespec mtime, atime, ctime;
-       u32 nsplits;
-       struct ceph_inode_frag *frag;
-       struct rb_node *rb_node;
        struct ceph_buffer *xattr_blob = NULL;
+       struct ceph_cap *new_cap = NULL;
        int err = 0;
-       int queue_trunc = 0;
+       bool wake = false;
+       bool queue_trunc = false;
+       bool new_version = false;
 
        dout("fill_inode %p ino %llx.%llx v %llu had %llu\n",
             inode, ceph_vinop(inode), le64_to_cpu(info->version),
             ci->i_version);
 
+       /* prealloc new cap struct */
+       if (info->cap.caps && ceph_snap(inode) == CEPH_NOSNAP)
+               new_cap = ceph_get_cap(mdsc, caps_reservation);
+
        /*
         * prealloc xattr data, if it looks like we'll need it.  only
         * if len > 4 (meaning there are actually xattrs; the first 4
@@ -623,19 +709,23 @@ static int fill_inode(struct inode *inode,
         *   3    2     skip
         *   3    3     update
         */
-       if (le64_to_cpu(info->version) > 0 &&
-           (ci->i_version & ~1) >= le64_to_cpu(info->version))
-               goto no_change;
-       
+       if (ci->i_version == 0 ||
+           ((info->cap.flags & CEPH_CAP_FLAG_AUTH) &&
+            le64_to_cpu(info->version) > (ci->i_version & ~1)))
+               new_version = true;
+
        issued = __ceph_caps_issued(ci, &implemented);
        issued |= implemented | __ceph_caps_dirty(ci);
+       new_issued = ~issued & le32_to_cpu(info->cap.caps);
 
        /* update inode */
        ci->i_version = le64_to_cpu(info->version);
        inode->i_version++;
        inode->i_rdev = le32_to_cpu(info->rdev);
+       inode->i_blkbits = fls(le32_to_cpu(info->layout.fl_stripe_unit)) - 1;
 
-       if ((issued & CEPH_CAP_AUTH_EXCL) == 0) {
+       if ((new_version || (new_issued & CEPH_CAP_AUTH_SHARED)) &&
+           (issued & CEPH_CAP_AUTH_EXCL) == 0) {
                inode->i_mode = le32_to_cpu(info->mode);
                inode->i_uid = make_kuid(&init_user_ns, le32_to_cpu(info->uid));
                inode->i_gid = make_kgid(&init_user_ns, le32_to_cpu(info->gid));
@@ -644,23 +734,35 @@ static int fill_inode(struct inode *inode,
                     from_kgid(&init_user_ns, inode->i_gid));
        }
 
-       if ((issued & CEPH_CAP_LINK_EXCL) == 0)
+       if ((new_version || (new_issued & CEPH_CAP_LINK_SHARED)) &&
+           (issued & CEPH_CAP_LINK_EXCL) == 0)
                set_nlink(inode, le32_to_cpu(info->nlink));
 
-       /* be careful with mtime, atime, size */
-       ceph_decode_timespec(&atime, &info->atime);
-       ceph_decode_timespec(&mtime, &info->mtime);
-       ceph_decode_timespec(&ctime, &info->ctime);
-       queue_trunc = ceph_fill_file_size(inode, issued,
-                                         le32_to_cpu(info->truncate_seq),
-                                         le64_to_cpu(info->truncate_size),
-                                         le64_to_cpu(info->size));
-       ceph_fill_file_time(inode, issued,
-                           le32_to_cpu(info->time_warp_seq),
-                           &ctime, &mtime, &atime);
-
-       ci->i_layout = info->layout;
-       inode->i_blkbits = fls(le32_to_cpu(info->layout.fl_stripe_unit)) - 1;
+       if (new_version || (new_issued & CEPH_CAP_ANY_RD)) {
+               /* be careful with mtime, atime, size */
+               ceph_decode_timespec(&atime, &info->atime);
+               ceph_decode_timespec(&mtime, &info->mtime);
+               ceph_decode_timespec(&ctime, &info->ctime);
+               ceph_fill_file_time(inode, issued,
+                               le32_to_cpu(info->time_warp_seq),
+                               &ctime, &mtime, &atime);
+       }
+
+       if (new_version ||
+           (new_issued & (CEPH_CAP_ANY_FILE_RD | CEPH_CAP_ANY_FILE_WR))) {
+               ci->i_layout = info->layout;
+               queue_trunc = ceph_fill_file_size(inode, issued,
+                                       le32_to_cpu(info->truncate_seq),
+                                       le64_to_cpu(info->truncate_size),
+                                       le64_to_cpu(info->size));
+               /* only update max_size on auth cap */
+               if ((info->cap.flags & CEPH_CAP_FLAG_AUTH) &&
+                   ci->i_max_size != le64_to_cpu(info->max_size)) {
+                       dout("max_size %lld -> %llu\n", ci->i_max_size,
+                                       le64_to_cpu(info->max_size));
+                       ci->i_max_size = le64_to_cpu(info->max_size);
+               }
+       }
 
        /* xattrs */
        /* note that if i_xattrs.len <= 4, i_xattrs.data will still be NULL. */
@@ -745,58 +847,6 @@ static int fill_inode(struct inode *inode,
                dout(" marking %p complete (empty)\n", inode);
                __ceph_dir_set_complete(ci, atomic_read(&ci->i_release_count));
        }
-no_change:
-       /* only update max_size on auth cap */
-       if ((info->cap.flags & CEPH_CAP_FLAG_AUTH) &&
-           ci->i_max_size != le64_to_cpu(info->max_size)) {
-               dout("max_size %lld -> %llu\n", ci->i_max_size,
-                    le64_to_cpu(info->max_size));
-               ci->i_max_size = le64_to_cpu(info->max_size);
-       }
-
-       spin_unlock(&ci->i_ceph_lock);
-
-       /* queue truncate if we saw i_size decrease */
-       if (queue_trunc)
-               ceph_queue_vmtruncate(inode);
-
-       /* populate frag tree */
-       /* FIXME: move me up, if/when version reflects fragtree changes */
-       nsplits = le32_to_cpu(info->fragtree.nsplits);
-       mutex_lock(&ci->i_fragtree_mutex);
-       rb_node = rb_first(&ci->i_fragtree);
-       for (i = 0; i < nsplits; i++) {
-               u32 id = le32_to_cpu(info->fragtree.splits[i].frag);
-               frag = NULL;
-               while (rb_node) {
-                       frag = rb_entry(rb_node, struct ceph_inode_frag, node);
-                       if (ceph_frag_compare(frag->frag, id) >= 0) {
-                               if (frag->frag != id)
-                                       frag = NULL;
-                               else
-                                       rb_node = rb_next(rb_node);
-                               break;
-                       }
-                       rb_node = rb_next(rb_node);
-                       rb_erase(&frag->node, &ci->i_fragtree);
-                       kfree(frag);
-                       frag = NULL;
-               }
-               if (!frag) {
-                       frag = __get_or_create_frag(ci, id);
-                       if (IS_ERR(frag))
-                               continue;
-               }
-               frag->split_by = le32_to_cpu(info->fragtree.splits[i].by);
-               dout(" frag %x split by %d\n", frag->frag, frag->split_by);
-       }
-       while (rb_node) {
-               frag = rb_entry(rb_node, struct ceph_inode_frag, node);
-               rb_node = rb_next(rb_node);
-               rb_erase(&frag->node, &ci->i_fragtree);
-               kfree(frag);
-       }
-       mutex_unlock(&ci->i_fragtree_mutex);
 
        /* were we issued a capability? */
        if (info->cap.caps) {
@@ -809,30 +859,41 @@ static int fill_inode(struct inode *inode,
                                     le32_to_cpu(info->cap.seq),
                                     le32_to_cpu(info->cap.mseq),
                                     le64_to_cpu(info->cap.realm),
-                                    info->cap.flags,
-                                    caps_reservation);
+                                    info->cap.flags, &new_cap);
+                       wake = true;
                } else {
-                       spin_lock(&ci->i_ceph_lock);
                        dout(" %p got snap_caps %s\n", inode,
                             ceph_cap_string(le32_to_cpu(info->cap.caps)));
                        ci->i_snap_caps |= le32_to_cpu(info->cap.caps);
                        if (cap_fmode >= 0)
                                __ceph_get_fmode(ci, cap_fmode);
-                       spin_unlock(&ci->i_ceph_lock);
                }
        } else if (cap_fmode >= 0) {
                pr_warn("mds issued no caps on %llx.%llx\n",
                           ceph_vinop(inode));
                __ceph_get_fmode(ci, cap_fmode);
        }
+       spin_unlock(&ci->i_ceph_lock);
+
+       if (wake)
+               wake_up_all(&ci->i_cap_wq);
+
+       /* queue truncate if we saw i_size decrease */
+       if (queue_trunc)
+               ceph_queue_vmtruncate(inode);
+
+       /* populate frag tree */
+       if (S_ISDIR(inode->i_mode))
+               ceph_fill_fragtree(inode, &info->fragtree, dirinfo);
 
        /* update delegation info? */
        if (dirinfo)
                ceph_fill_dirfrag(inode, dirinfo);
 
        err = 0;
-
 out:
+       if (new_cap)
+               ceph_put_cap(mdsc, new_cap);
        if (xattr_blob)
                ceph_buffer_put(xattr_blob);
        return err;
@@ -1485,7 +1546,7 @@ static void ceph_invalidate_work(struct work_struct *work)
        orig_gen = ci->i_rdcache_gen;
        spin_unlock(&ci->i_ceph_lock);
 
-       truncate_inode_pages(inode->i_mapping, 0);
+       truncate_pagecache(inode, 0);
 
        spin_lock(&ci->i_ceph_lock);
        if (orig_gen == ci->i_rdcache_gen &&
@@ -1588,7 +1649,7 @@ void __ceph_do_pending_vmtruncate(struct inode *inode)
             ci->i_truncate_pending, to);
        spin_unlock(&ci->i_ceph_lock);
 
-       truncate_inode_pages(inode->i_mapping, to);
+       truncate_pagecache(inode, to);
 
        spin_lock(&ci->i_ceph_lock);
        if (to == ci->i_truncate_size) {
index 9a33b98cb0000df58714bbc842db7a80b9999099..92a2548278fca0c52609d120db33cf070db621c6 100644 (file)
@@ -1558,6 +1558,8 @@ ceph_mdsc_create_request(struct ceph_mds_client *mdsc, int op, int mode)
        init_completion(&req->r_safe_completion);
        INIT_LIST_HEAD(&req->r_unsafe_item);
 
+       req->r_stamp = CURRENT_TIME;
+
        req->r_op = op;
        req->r_direct_mode = mode;
        return req;
@@ -1783,7 +1785,8 @@ static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc,
        }
 
        len = sizeof(*head) +
-               pathlen1 + pathlen2 + 2*(1 + sizeof(u32) + sizeof(u64));
+               pathlen1 + pathlen2 + 2*(1 + sizeof(u32) + sizeof(u64)) +
+               sizeof(struct timespec);
 
        /* calculate (max) length for cap releases */
        len += sizeof(struct ceph_mds_request_release) *
@@ -1800,6 +1803,7 @@ static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc,
                goto out_free2;
        }
 
+       msg->hdr.version = 2;
        msg->hdr.tid = cpu_to_le64(req->r_tid);
 
        head = msg->front.iov_base;
@@ -1836,6 +1840,9 @@ static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc,
                      mds, req->r_old_inode_drop, req->r_old_inode_unless, 0);
        head->num_releases = cpu_to_le16(releases);
 
+       /* time stamp */
+       ceph_encode_copy(&p, &req->r_stamp, sizeof(req->r_stamp));
+
        BUG_ON(p > end);
        msg->front.iov_len = p - msg->front.iov_base;
        msg->hdr.front_len = cpu_to_le32(msg->front.iov_len);
index e90cfccf93bd9e90a21ef3c22ebfa9b54e43b3a1..e00737cf523c0ae237121fe0fdcef0d147b2d9f2 100644 (file)
@@ -194,6 +194,7 @@ struct ceph_mds_request {
        int r_fmode;        /* file mode, if expecting cap */
        kuid_t r_uid;
        kgid_t r_gid;
+       struct timespec r_stamp;
 
        /* for choosing which mds to send this request to */
        int r_direct_mode;
index ead05cc1f447562271578131ab25769257080915..12b20744e386f1281f454f6fb636729beb01e873 100644 (file)
@@ -292,7 +292,6 @@ struct ceph_inode_info {
        struct ceph_snap_context *i_head_snapc;  /* set if wr_buffer_head > 0 or
                                                    dirty|flushing caps */
        unsigned i_snap_caps;           /* cap bits for snapped files */
-       unsigned i_cap_exporting_issued;
 
        int i_nr_by_mode[CEPH_FILE_MODE_NUM];  /* open file counts */
 
@@ -775,11 +774,13 @@ static inline void ceph_forget_all_cached_acls(struct inode *inode)
 extern const char *ceph_cap_string(int c);
 extern void ceph_handle_caps(struct ceph_mds_session *session,
                             struct ceph_msg *msg);
-extern int ceph_add_cap(struct inode *inode,
-                       struct ceph_mds_session *session, u64 cap_id,
-                       int fmode, unsigned issued, unsigned wanted,
-                       unsigned cap, unsigned seq, u64 realmino, int flags,
-                       struct ceph_cap_reservation *caps_reservation);
+extern struct ceph_cap *ceph_get_cap(struct ceph_mds_client *mdsc,
+                                    struct ceph_cap_reservation *ctx);
+extern void ceph_add_cap(struct inode *inode,
+                        struct ceph_mds_session *session, u64 cap_id,
+                        int fmode, unsigned issued, unsigned wanted,
+                        unsigned cap, unsigned seq, u64 realmino, int flags,
+                        struct ceph_cap **new_cap);
 extern void __ceph_remove_cap(struct ceph_cap *cap, bool queue_release);
 extern void ceph_put_cap(struct ceph_mds_client *mdsc,
                         struct ceph_cap *cap);
index 238b7aa26f68ab538df0cc219073a3d26541cc11..a3d33fe592d6d95619506b6f4aac33621284d3f8 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1046,13 +1046,13 @@ EXPORT_SYMBOL_GPL(get_task_comm);
  * so that a new one can be started
  */
 
-void set_task_comm(struct task_struct *tsk, const char *buf)
+void __set_task_comm(struct task_struct *tsk, const char *buf, bool exec)
 {
        task_lock(tsk);
        trace_task_rename(tsk, buf);
        strlcpy(tsk->comm, buf, sizeof(tsk->comm));
        task_unlock(tsk);
-       perf_event_comm(tsk);
+       perf_event_comm(tsk, exec);
 }
 
 int flush_old_exec(struct linux_binprm * bprm)
@@ -1110,7 +1110,8 @@ void setup_new_exec(struct linux_binprm * bprm)
        else
                set_dumpable(current->mm, suid_dumpable);
 
-       set_task_comm(current, kbasename(bprm->filename));
+       perf_event_exec();
+       __set_task_comm(current, kbasename(bprm->filename), true);
 
        /* Set the new mm task size. We have to do that late because it may
         * depend on TIF_32BIT which is only updated in flush_thread() on
diff --git a/include/asm-generic/qrwlock.h b/include/asm-generic/qrwlock.h
new file mode 100644 (file)
index 0000000..6383d54
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Queue read/write lock
+ *
+ * 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.
+ *
+ * (C) Copyright 2013-2014 Hewlett-Packard Development Company, L.P.
+ *
+ * Authors: Waiman Long <waiman.long@hp.com>
+ */
+#ifndef __ASM_GENERIC_QRWLOCK_H
+#define __ASM_GENERIC_QRWLOCK_H
+
+#include <linux/atomic.h>
+#include <asm/barrier.h>
+#include <asm/processor.h>
+
+#include <asm-generic/qrwlock_types.h>
+
+/*
+ * Writer states & reader shift and bias
+ */
+#define        _QW_WAITING     1               /* A writer is waiting     */
+#define        _QW_LOCKED      0xff            /* A writer holds the lock */
+#define        _QW_WMASK       0xff            /* Writer mask             */
+#define        _QR_SHIFT       8               /* Reader count shift      */
+#define _QR_BIAS       (1U << _QR_SHIFT)
+
+/*
+ * External function declarations
+ */
+extern void queue_read_lock_slowpath(struct qrwlock *lock);
+extern void queue_write_lock_slowpath(struct qrwlock *lock);
+
+/**
+ * queue_read_can_lock- would read_trylock() succeed?
+ * @lock: Pointer to queue rwlock structure
+ */
+static inline int queue_read_can_lock(struct qrwlock *lock)
+{
+       return !(atomic_read(&lock->cnts) & _QW_WMASK);
+}
+
+/**
+ * queue_write_can_lock- would write_trylock() succeed?
+ * @lock: Pointer to queue rwlock structure
+ */
+static inline int queue_write_can_lock(struct qrwlock *lock)
+{
+       return !atomic_read(&lock->cnts);
+}
+
+/**
+ * queue_read_trylock - try to acquire read lock of a queue rwlock
+ * @lock : Pointer to queue rwlock structure
+ * Return: 1 if lock acquired, 0 if failed
+ */
+static inline int queue_read_trylock(struct qrwlock *lock)
+{
+       u32 cnts;
+
+       cnts = atomic_read(&lock->cnts);
+       if (likely(!(cnts & _QW_WMASK))) {
+               cnts = (u32)atomic_add_return(_QR_BIAS, &lock->cnts);
+               if (likely(!(cnts & _QW_WMASK)))
+                       return 1;
+               atomic_sub(_QR_BIAS, &lock->cnts);
+       }
+       return 0;
+}
+
+/**
+ * queue_write_trylock - try to acquire write lock of a queue rwlock
+ * @lock : Pointer to queue rwlock structure
+ * Return: 1 if lock acquired, 0 if failed
+ */
+static inline int queue_write_trylock(struct qrwlock *lock)
+{
+       u32 cnts;
+
+       cnts = atomic_read(&lock->cnts);
+       if (unlikely(cnts))
+               return 0;
+
+       return likely(atomic_cmpxchg(&lock->cnts,
+                                    cnts, cnts | _QW_LOCKED) == cnts);
+}
+/**
+ * queue_read_lock - acquire read lock of a queue rwlock
+ * @lock: Pointer to queue rwlock structure
+ */
+static inline void queue_read_lock(struct qrwlock *lock)
+{
+       u32 cnts;
+
+       cnts = atomic_add_return(_QR_BIAS, &lock->cnts);
+       if (likely(!(cnts & _QW_WMASK)))
+               return;
+
+       /* The slowpath will decrement the reader count, if necessary. */
+       queue_read_lock_slowpath(lock);
+}
+
+/**
+ * queue_write_lock - acquire write lock of a queue rwlock
+ * @lock : Pointer to queue rwlock structure
+ */
+static inline void queue_write_lock(struct qrwlock *lock)
+{
+       /* Optimize for the unfair lock case where the fair flag is 0. */
+       if (atomic_cmpxchg(&lock->cnts, 0, _QW_LOCKED) == 0)
+               return;
+
+       queue_write_lock_slowpath(lock);
+}
+
+/**
+ * queue_read_unlock - release read lock of a queue rwlock
+ * @lock : Pointer to queue rwlock structure
+ */
+static inline void queue_read_unlock(struct qrwlock *lock)
+{
+       /*
+        * Atomically decrement the reader count
+        */
+       smp_mb__before_atomic();
+       atomic_sub(_QR_BIAS, &lock->cnts);
+}
+
+#ifndef queue_write_unlock
+/**
+ * queue_write_unlock - release write lock of a queue rwlock
+ * @lock : Pointer to queue rwlock structure
+ */
+static inline void queue_write_unlock(struct qrwlock *lock)
+{
+       /*
+        * If the writer field is atomic, it can be cleared directly.
+        * Otherwise, an atomic subtraction will be used to clear it.
+        */
+       smp_mb__before_atomic();
+       atomic_sub(_QW_LOCKED, &lock->cnts);
+}
+#endif
+
+/*
+ * Remapping rwlock architecture specific functions to the corresponding
+ * queue rwlock functions.
+ */
+#define arch_read_can_lock(l)  queue_read_can_lock(l)
+#define arch_write_can_lock(l) queue_write_can_lock(l)
+#define arch_read_lock(l)      queue_read_lock(l)
+#define arch_write_lock(l)     queue_write_lock(l)
+#define arch_read_trylock(l)   queue_read_trylock(l)
+#define arch_write_trylock(l)  queue_write_trylock(l)
+#define arch_read_unlock(l)    queue_read_unlock(l)
+#define arch_write_unlock(l)   queue_write_unlock(l)
+
+#endif /* __ASM_GENERIC_QRWLOCK_H */
diff --git a/include/asm-generic/qrwlock_types.h b/include/asm-generic/qrwlock_types.h
new file mode 100644 (file)
index 0000000..4d76f24
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef __ASM_GENERIC_QRWLOCK_TYPES_H
+#define __ASM_GENERIC_QRWLOCK_TYPES_H
+
+#include <linux/types.h>
+#include <asm/spinlock_types.h>
+
+/*
+ * The queue read/write lock data structure
+ */
+
+typedef struct qrwlock {
+       atomic_t                cnts;
+       arch_spinlock_t         lock;
+} arch_rwlock_t;
+
+#define        __ARCH_RW_LOCK_UNLOCKED {               \
+       .cnts = ATOMIC_INIT(0),                 \
+       .lock = __ARCH_SPIN_LOCK_UNLOCKED,      \
+}
+
+#endif /* __ASM_GENERIC_QRWLOCK_TYPES_H */
index d647637cd699df949b01616112878cc9b1ce1ef7..471ba48c7ae40608c540bfda459b33ae0f4bb3b7 100644 (file)
 #define BRANCH_PROFILE()
 #endif
 
+#ifdef CONFIG_KPROBES
+#define KPROBE_BLACKLIST()     . = ALIGN(8);                                 \
+                               VMLINUX_SYMBOL(__start_kprobe_blacklist) = .; \
+                               *(_kprobe_blacklist)                          \
+                               VMLINUX_SYMBOL(__stop_kprobe_blacklist) = .;
+#else
+#define KPROBE_BLACKLIST()
+#endif
+
 #ifdef CONFIG_EVENT_TRACING
 #define FTRACE_EVENTS()        . = ALIGN(8);                                   \
                        VMLINUX_SYMBOL(__start_ftrace_events) = .;      \
        *(.init.rodata)                                                 \
        FTRACE_EVENTS()                                                 \
        TRACE_SYSCALLS()                                                \
+       KPROBE_BLACKLIST()                                              \
        MEM_DISCARD(init.rodata)                                        \
        CLK_OF_TABLES()                                                 \
        RESERVEDMEM_OF_TABLES()                                         \
index 8598f8eacb20e175aacb638edd41e514afc7f9d8..a495a959e8a754939b9c8c9d9bd748a73f54f587 100644 (file)
@@ -36,6 +36,8 @@ struct ath9k_platform_data {
 
        int (*get_mac_revision)(void);
        int (*external_reset)(void);
+
+       bool use_eeprom;
 };
 
 #endif /* _LINUX_ATH9K_PLATFORM_H */
index 78c6c52073ad62948a7d750951ed94d232957048..a0875001b13c84ad70a9b2909654e9ffb6824c58 100644 (file)
@@ -10,8 +10,8 @@
  *
  */
 
-#ifndef CAN_CORE_H
-#define CAN_CORE_H
+#ifndef _CAN_CORE_H
+#define _CAN_CORE_H
 
 #include <linux/can.h>
 #include <linux/skbuff.h>
@@ -58,4 +58,4 @@ extern void can_rx_unregister(struct net_device *dev, canid_t can_id,
 extern int can_send(struct sk_buff *skb, int loop);
 extern int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
 
-#endif /* CAN_CORE_H */
+#endif /* !_CAN_CORE_H */
index 3ce5e526525f852f37ea242700363036c85068ca..6992afc6ba7f96fda9dba202f771fe7e28f807fe 100644 (file)
@@ -10,8 +10,8 @@
  *
  */
 
-#ifndef CAN_DEV_H
-#define CAN_DEV_H
+#ifndef _CAN_DEV_H
+#define _CAN_DEV_H
 
 #include <linux/can.h>
 #include <linux/can/netlink.h>
@@ -132,4 +132,4 @@ struct sk_buff *alloc_canfd_skb(struct net_device *dev,
 struct sk_buff *alloc_can_err_skb(struct net_device *dev,
                                  struct can_frame **cf);
 
-#endif /* CAN_DEV_H */
+#endif /* !_CAN_DEV_H */
index 9c1167baf273e7e62cb29dbfb89266e11f1c29d6..e0475c5cbb92aac6fe1163bc2ac65a4664048152 100644 (file)
@@ -6,8 +6,8 @@
  * published by the Free Software Foundation.
  */
 
-#ifndef CAN_LED_H
-#define CAN_LED_H
+#ifndef _CAN_LED_H
+#define _CAN_LED_H
 
 #include <linux/if.h>
 #include <linux/leds.h>
@@ -48,4 +48,4 @@ static inline void can_led_notifier_exit(void)
 
 #endif
 
-#endif
+#endif /* !_CAN_LED_H */
index 7702641f87ee032b76d2cfa68fdd5f75aec4ec2b..78b2d44f04cffc83f7ce7feb4cd08c94cea84ce8 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef _CAN_PLATFORM_CC770_H_
-#define _CAN_PLATFORM_CC770_H_
+#ifndef _CAN_PLATFORM_CC770_H
+#define _CAN_PLATFORM_CC770_H
 
 /* CPU Interface Register (0x02) */
 #define CPUIF_CEN      0x01    /* Clock Out Enable */
@@ -30,4 +30,4 @@ struct cc770_platform_data {
        u8 bcr;         /* Bus Configuration Register */
 };
 
-#endif /* !_CAN_PLATFORM_CC770_H_ */
+#endif /* !_CAN_PLATFORM_CC770_H */
index dc029dba7a030d384d9389038b6f7d600eee3dbf..d44fcae274ff2a0877c06091bfaafe68e0cc8d6d 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef __CAN_PLATFORM_MCP251X_H__
-#define __CAN_PLATFORM_MCP251X_H__
+#ifndef _CAN_PLATFORM_MCP251X_H
+#define _CAN_PLATFORM_MCP251X_H
 
 /*
  *
@@ -18,4 +18,4 @@ struct mcp251x_platform_data {
        unsigned long oscillator_frequency;
 };
 
-#endif /* __CAN_PLATFORM_MCP251X_H__ */
+#endif /* !_CAN_PLATFORM_MCP251X_H */
diff --git a/include/linux/can/platform/rcar_can.h b/include/linux/can/platform/rcar_can.h
new file mode 100644 (file)
index 0000000..0f4a2f3
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef _CAN_PLATFORM_RCAR_CAN_H_
+#define _CAN_PLATFORM_RCAR_CAN_H_
+
+#include <linux/types.h>
+
+/* Clock Select Register settings */
+enum CLKR {
+       CLKR_CLKP1 = 0, /* Peripheral clock (clkp1) */
+       CLKR_CLKP2 = 1, /* Peripheral clock (clkp2) */
+       CLKR_CLKEXT = 3 /* Externally input clock */
+};
+
+struct rcar_can_platform_data {
+       enum CLKR clock_select; /* Clock source select */
+};
+
+#endif /* !_CAN_PLATFORM_RCAR_CAN_H_ */
index 96f8fcc78d787a3b826967b77328ea6f4e1dc8ef..93570b61ec6c58bfa433e6b4f710fb9a4e466121 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef _CAN_PLATFORM_SJA1000_H_
-#define _CAN_PLATFORM_SJA1000_H_
+#ifndef _CAN_PLATFORM_SJA1000_H
+#define _CAN_PLATFORM_SJA1000_H
 
 /* clock divider register */
 #define CDR_CLKOUT_MASK 0x07
@@ -32,4 +32,4 @@ struct sja1000_platform_data {
        u8 cdr;         /* clock divider register */
 };
 
-#endif /* !_CAN_PLATFORM_SJA1000_H_ */
+#endif /* !_CAN_PLATFORM_SJA1000_H */
index af17cb3f7a8402bdd3816911abb9f98ebf60632f..a52f47ca6c8ad9c5159c34b4f568b5d84eccbd18 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef __CAN_PLATFORM_TI_HECC_H__
-#define __CAN_PLATFORM_TI_HECC_H__
+#ifndef _CAN_PLATFORM_TI_HECC_H
+#define _CAN_PLATFORM_TI_HECC_H
 
 /*
  * TI HECC (High End CAN Controller) driver platform header
@@ -41,4 +41,4 @@ struct ti_hecc_platform_data {
        u32 version;
        void (*transceiver_switch) (int);
 };
-#endif
+#endif /* !_CAN_PLATFORM_TI_HECC_H */
index f9bbbb472663af08aef78ac152e8293f36736ea4..cc00d15c6107be8893b024e3eabb2ef1ba243016 100644 (file)
@@ -7,8 +7,8 @@
  *
  */
 
-#ifndef CAN_SKB_H
-#define CAN_SKB_H
+#ifndef _CAN_SKB_H
+#define _CAN_SKB_H
 
 #include <linux/types.h>
 #include <linux/skbuff.h>
@@ -80,4 +80,4 @@ static inline struct sk_buff *can_create_echo_skb(struct sk_buff *skb)
        return skb;
 }
 
-#endif /* CAN_SKB_H */
+#endif /* !_CAN_SKB_H */
index 5f6db18d72e8845748a5b506949a1d7d1247cf06..3c97d5e9b951ed419bfb1663be0c4f1c9d416fd7 100644 (file)
@@ -625,6 +625,8 @@ int ceph_flags_to_mode(int flags);
                           CEPH_CAP_LINK_EXCL |         \
                           CEPH_CAP_XATTR_EXCL |        \
                           CEPH_CAP_FILE_EXCL)
+#define CEPH_CAP_ANY_FILE_RD (CEPH_CAP_FILE_RD | CEPH_CAP_FILE_CACHE | \
+                             CEPH_CAP_FILE_SHARED)
 #define CEPH_CAP_ANY_FILE_WR (CEPH_CAP_FILE_WR | CEPH_CAP_FILE_BUFFER |        \
                              CEPH_CAP_FILE_EXCL)
 #define CEPH_CAP_ANY_WR   (CEPH_CAP_ANY_EXCL | CEPH_CAP_ANY_FILE_WR)
index a486f390dfbeeb9d4004921bb973852bd586a808..deb47e45ac7c29b11642842a733a351f05bedbb3 100644 (file)
@@ -40,9 +40,9 @@ struct ceph_mon_request {
 };
 
 /*
- * ceph_mon_generic_request is being used for the statfs and poolop requests
- * which are bening done a bit differently because we need to get data back
- * to the caller
+ * ceph_mon_generic_request is being used for the statfs, poolop and
+ * mon_get_version requests which are being done a bit differently
+ * because we need to get data back to the caller
  */
 struct ceph_mon_generic_request {
        struct kref kref;
@@ -104,10 +104,15 @@ extern int ceph_monc_got_mdsmap(struct ceph_mon_client *monc, u32 have);
 extern int ceph_monc_got_osdmap(struct ceph_mon_client *monc, u32 have);
 
 extern void ceph_monc_request_next_osdmap(struct ceph_mon_client *monc);
+extern int ceph_monc_wait_osdmap(struct ceph_mon_client *monc, u32 epoch,
+                                unsigned long timeout);
 
 extern int ceph_monc_do_statfs(struct ceph_mon_client *monc,
                               struct ceph_statfs *buf);
 
+extern int ceph_monc_do_get_version(struct ceph_mon_client *monc,
+                                   const char *what, u64 *newest);
+
 extern int ceph_monc_open_session(struct ceph_mon_client *monc);
 
 extern int ceph_monc_validate_auth(struct ceph_mon_client *monc);
index 64fdfe1cfcf0c8848ef80a7953dc975fdab7c501..d5ad7b1118fc10748377d9a90d2e960e0f7b611a 100644 (file)
@@ -383,7 +383,9 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect);
 /* Ignore/forbid kprobes attach on very low level functions marked by this attribute: */
 #ifdef CONFIG_KPROBES
 # define __kprobes     __attribute__((__section__(".kprobes.text")))
+# define nokprobe_inline       __always_inline
 #else
 # define __kprobes
+# define nokprobe_inline       inline
 #endif
 #endif /* __LINUX_COMPILER_H */
index 3557ea7b2049fb0f930efd1b4d7f8b815fb6fd4d..2997af6d2ccd257a15612c45875d80308cb99d73 100644 (file)
@@ -142,6 +142,13 @@ static inline unsigned int cpumask_any_but(const struct cpumask *mask,
        return 1;
 }
 
+static inline int cpumask_set_cpu_local_first(int i, int numa_node, cpumask_t *dstp)
+{
+       set_bit(0, cpumask_bits(dstp));
+
+       return 0;
+}
+
 #define for_each_cpu(cpu, mask)                        \
        for ((cpu) = 0; (cpu) < 1; (cpu)++, (void)mask)
 #define for_each_cpu_not(cpu, mask)            \
@@ -192,6 +199,7 @@ static inline unsigned int cpumask_next_zero(int n, const struct cpumask *srcp)
 
 int cpumask_next_and(int n, const struct cpumask *, const struct cpumask *);
 int cpumask_any_but(const struct cpumask *mask, unsigned int cpu);
+int cpumask_set_cpu_local_first(int i, int numa_node, cpumask_t *dstp);
 
 /**
  * for_each_cpu - iterate over every cpu in a mask
index 1786e772d5c6e9a2634eb13d916d8d8bd6c25fcb..d590765106f3d226449ccf5228181baef53c4c17 100644 (file)
@@ -2,13 +2,13 @@
 #define _LINUX_CRC7_H
 #include <linux/types.h>
 
-extern const u8 crc7_syndrome_table[256];
+extern const u8 crc7_be_syndrome_table[256];
 
-static inline u8 crc7_byte(u8 crc, u8 data)
+static inline u8 crc7_be_byte(u8 crc, u8 data)
 {
-       return crc7_syndrome_table[(crc << 1) ^ data];
+       return crc7_be_syndrome_table[crc ^ data];
 }
 
-extern u8 crc7(u8 crc, const u8 *buffer, size_t len);
+extern u8 crc7_be(u8 crc, const u8 *buffer, size_t len);
 
 #endif
index 0a114d05f68d35bd275924a7290a6c3b75ef17fd..e658229fee39c17ea8673eadcf7b49cfdce508e1 100644 (file)
@@ -154,13 +154,20 @@ static inline u32 ethtool_rxfh_indir_default(u32 index, u32 n_rx_rings)
  * @reset: Reset (part of) the device, as specified by a bitmask of
  *     flags from &enum ethtool_reset_flags.  Returns a negative
  *     error code or zero.
+ * @get_rxfh_key_size: Get the size of the RX flow hash key.
+ *     Returns zero if not supported for this specific device.
  * @get_rxfh_indir_size: Get the size of the RX flow hash indirection table.
  *     Returns zero if not supported for this specific device.
- * @get_rxfh_indir: Get the contents of the RX flow hash indirection table.
- *     Will not be called if @get_rxfh_indir_size returns zero.
+ * @get_rxfh: Get the contents of the RX flow hash indirection table and hash
+ *     key.
+ *     Will only be called if one or both of @get_rxfh_indir_size and
+ *     @get_rxfh_key_size are implemented and return non-zero.
  *     Returns a negative error code or zero.
- * @set_rxfh_indir: Set the contents of the RX flow hash indirection table.
- *     Will not be called if @get_rxfh_indir_size returns zero.
+ * @set_rxfh: Set the contents of the RX flow hash indirection table and/or
+ *     hash key.  In case only the indirection table or hash key is to be
+ *     changed, the other argument will be %NULL.
+ *     Will only be called if one or both of @get_rxfh_indir_size and
+ *     @get_rxfh_key_size are implemented and return non-zero.
  *     Returns a negative error code or zero.
  * @get_channels: Get number of channels.
  * @set_channels: Set number of channels.  Returns a negative error code or
@@ -232,9 +239,11 @@ struct ethtool_ops {
        int     (*set_rxnfc)(struct net_device *, struct ethtool_rxnfc *);
        int     (*flash_device)(struct net_device *, struct ethtool_flash *);
        int     (*reset)(struct net_device *, u32 *);
+       u32     (*get_rxfh_key_size)(struct net_device *);
        u32     (*get_rxfh_indir_size)(struct net_device *);
-       int     (*get_rxfh_indir)(struct net_device *, u32 *);
-       int     (*set_rxfh_indir)(struct net_device *, const u32 *);
+       int     (*get_rxfh)(struct net_device *, u32 *indir, u8 *key);
+       int     (*set_rxfh)(struct net_device *, const u32 *indir,
+                           const u8 *key);
        void    (*get_channels)(struct net_device *, struct ethtool_channels *);
        int     (*set_channels)(struct net_device *, struct ethtool_channels *);
        int     (*get_dump_flag)(struct net_device *, struct ethtool_dump *);
index 024fd03e5d182d5670ee2c60005cbea43f8a83e8..a7e3c48d73a70677f88fcbf68c89e62f1caf159f 100644 (file)
 #define BPF_CALL       0x80    /* function call */
 #define BPF_EXIT       0x90    /* function return */
 
+/* Register numbers */
+enum {
+       BPF_REG_0 = 0,
+       BPF_REG_1,
+       BPF_REG_2,
+       BPF_REG_3,
+       BPF_REG_4,
+       BPF_REG_5,
+       BPF_REG_6,
+       BPF_REG_7,
+       BPF_REG_8,
+       BPF_REG_9,
+       BPF_REG_10,
+       __MAX_BPF_REG,
+};
+
 /* BPF has 10 general purpose 64-bit registers and stack frame. */
-#define MAX_BPF_REG    11
+#define MAX_BPF_REG    __MAX_BPF_REG
+
+/* ArgX, context and stack frame pointer register positions. Note,
+ * Arg1, Arg2, Arg3, etc are used as argument mappings of function
+ * calls in BPF_CALL instruction.
+ */
+#define BPF_REG_ARG1   BPF_REG_1
+#define BPF_REG_ARG2   BPF_REG_2
+#define BPF_REG_ARG3   BPF_REG_3
+#define BPF_REG_ARG4   BPF_REG_4
+#define BPF_REG_ARG5   BPF_REG_5
+#define BPF_REG_CTX    BPF_REG_6
+#define BPF_REG_FP     BPF_REG_10
+
+/* Additional register mappings for converted user programs. */
+#define BPF_REG_A      BPF_REG_0
+#define BPF_REG_X      BPF_REG_7
+#define BPF_REG_TMP    BPF_REG_8
 
 /* BPF program can access up to 512 bytes of stack space. */
 #define MAX_BPF_STACK  512
 
-/* Arg1, context and stack frame pointer register positions. */
-#define ARG1_REG       1
-#define CTX_REG                6
-#define FP_REG         10
+/* Helper macros for filter block array initializers. */
+
+/* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */
+
+#define BPF_ALU64_REG(OP, DST, SRC)                            \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ALU64 | BPF_OP(OP) | BPF_X,        \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = 0 })
+
+#define BPF_ALU32_REG(OP, DST, SRC)                            \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ALU | BPF_OP(OP) | BPF_X,          \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = 0 })
+
+/* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */
+
+#define BPF_ALU64_IMM(OP, DST, IMM)                            \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ALU64 | BPF_OP(OP) | BPF_K,        \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+#define BPF_ALU32_IMM(OP, DST, IMM)                            \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ALU | BPF_OP(OP) | BPF_K,          \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+/* Endianess conversion, cpu_to_{l,b}e(), {l,b}e_to_cpu() */
+
+#define BPF_ENDIAN(TYPE, DST, LEN)                             \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ALU | BPF_END | BPF_SRC(TYPE),     \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = LEN })
+
+/* Short form of mov, dst_reg = src_reg */
+
+#define BPF_MOV64_REG(DST, SRC)                                        \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ALU64 | BPF_MOV | BPF_X,           \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = 0 })
+
+#define BPF_MOV32_REG(DST, SRC)                                        \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ALU | BPF_MOV | BPF_X,             \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = 0 })
+
+/* Short form of mov, dst_reg = imm32 */
+
+#define BPF_MOV64_IMM(DST, IMM)                                        \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ALU64 | BPF_MOV | BPF_K,           \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+#define BPF_MOV32_IMM(DST, IMM)                                        \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ALU | BPF_MOV | BPF_K,             \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+/* Short form of mov based on type, BPF_X: dst_reg = src_reg, BPF_K: dst_reg = imm32 */
+
+#define BPF_MOV64_RAW(TYPE, DST, SRC, IMM)                     \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ALU64 | BPF_MOV | BPF_SRC(TYPE),   \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+#define BPF_MOV32_RAW(TYPE, DST, SRC, IMM)                     \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ALU | BPF_MOV | BPF_SRC(TYPE),     \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+/* Direct packet access, R0 = *(uint *) (skb->data + imm32) */
+
+#define BPF_LD_ABS(SIZE, IMM)                                  \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS,     \
+               .dst_reg = 0,                                   \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+/* Indirect packet access, R0 = *(uint *) (skb->data + src_reg + imm32) */
+
+#define BPF_LD_IND(SIZE, SRC, IMM)                             \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_LD | BPF_SIZE(SIZE) | BPF_IND,     \
+               .dst_reg = 0,                                   \
+               .src_reg = SRC,                                 \
+               .off   = 0,                                     \
+               .imm   = IMM })
+
+/* Memory load, dst_reg = *(uint *) (src_reg + off16) */
+
+#define BPF_LDX_MEM(SIZE, DST, SRC, OFF)                       \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM,    \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = OFF,                                   \
+               .imm   = 0 })
+
+/* Memory store, *(uint *) (dst_reg + off16) = src_reg */
+
+#define BPF_STX_MEM(SIZE, DST, SRC, OFF)                       \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM,    \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = OFF,                                   \
+               .imm   = 0 })
+
+/* Memory store, *(uint *) (dst_reg + off16) = imm32 */
+
+#define BPF_ST_MEM(SIZE, DST, OFF, IMM)                                \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM,     \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = OFF,                                   \
+               .imm   = IMM })
+
+/* Conditional jumps against registers, if (dst_reg 'op' src_reg) goto pc + off16 */
+
+#define BPF_JMP_REG(OP, DST, SRC, OFF)                         \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_JMP | BPF_OP(OP) | BPF_X,          \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = OFF,                                   \
+               .imm   = 0 })
+
+/* Conditional jumps against immediates, if (dst_reg 'op' imm32) goto pc + off16 */
+
+#define BPF_JMP_IMM(OP, DST, IMM, OFF)                         \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_JMP | BPF_OP(OP) | BPF_K,          \
+               .dst_reg = DST,                                 \
+               .src_reg = 0,                                   \
+               .off   = OFF,                                   \
+               .imm   = IMM })
+
+/* Function call */
+
+#define BPF_EMIT_CALL(FUNC)                                    \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_JMP | BPF_CALL,                    \
+               .dst_reg = 0,                                   \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = ((FUNC) - __bpf_call_base) })
+
+/* Raw code statement block */
+
+#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM)                 \
+       ((struct sock_filter_int) {                             \
+               .code  = CODE,                                  \
+               .dst_reg = DST,                                 \
+               .src_reg = SRC,                                 \
+               .off   = OFF,                                   \
+               .imm   = IMM })
+
+/* Program exit */
+
+#define BPF_EXIT_INSN()                                                \
+       ((struct sock_filter_int) {                             \
+               .code  = BPF_JMP | BPF_EXIT,                    \
+               .dst_reg = 0,                                   \
+               .src_reg = 0,                                   \
+               .off   = 0,                                     \
+               .imm   = 0 })
+
+#define bytes_to_bpf_size(bytes)                               \
+({                                                             \
+       int bpf_size = -EINVAL;                                 \
+                                                               \
+       if (bytes == sizeof(u8))                                \
+               bpf_size = BPF_B;                               \
+       else if (bytes == sizeof(u16))                          \
+               bpf_size = BPF_H;                               \
+       else if (bytes == sizeof(u32))                          \
+               bpf_size = BPF_W;                               \
+       else if (bytes == sizeof(u64))                          \
+               bpf_size = BPF_DW;                              \
+                                                               \
+       bpf_size;                                               \
+})
+
+/* Macro to invoke filter function. */
+#define SK_RUN_FILTER(filter, ctx)  (*filter->bpf_func)(ctx, filter->insnsi)
 
 struct sock_filter_int {
        __u8    code;           /* opcode */
-       __u8    a_reg:4;        /* dest register */
-       __u8    x_reg:4;        /* source register */
+       __u8    dst_reg:4;      /* dest register */
+       __u8    src_reg:4;      /* source register */
        __s16   off;            /* signed offset */
        __s32   imm;            /* signed immediate constant */
 };
@@ -97,21 +346,16 @@ static inline unsigned int sk_filter_size(unsigned int proglen)
 #define sk_filter_proglen(fprog)                       \
                (fprog->len * sizeof(fprog->filter[0]))
 
-#define SK_RUN_FILTER(filter, ctx)                     \
-               (*filter->bpf_func)(ctx, filter->insnsi)
-
 int sk_filter(struct sock *sk, struct sk_buff *skb);
 
-u32 sk_run_filter_int_seccomp(const struct seccomp_data *ctx,
-                             const struct sock_filter_int *insni);
-u32 sk_run_filter_int_skb(const struct sk_buff *ctx,
-                         const struct sock_filter_int *insni);
+void sk_filter_select_runtime(struct sk_filter *fp);
+void sk_filter_free(struct sk_filter *fp);
 
 int sk_convert_filter(struct sock_filter *prog, int len,
                      struct sock_filter_int *new_prog, int *new_len);
 
 int sk_unattached_filter_create(struct sk_filter **pfp,
-                               struct sock_fprog *fprog);
+                               struct sock_fprog_kern *fprog);
 void sk_unattached_filter_destroy(struct sk_filter *fp);
 
 int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk);
@@ -120,11 +364,48 @@ int sk_detach_filter(struct sock *sk);
 int sk_chk_filter(struct sock_filter *filter, unsigned int flen);
 int sk_get_filter(struct sock *sk, struct sock_filter __user *filter,
                  unsigned int len);
-void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to);
 
 void sk_filter_charge(struct sock *sk, struct sk_filter *fp);
 void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp);
 
+u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
+void bpf_int_jit_compile(struct sk_filter *fp);
+
+#define BPF_ANC                BIT(15)
+
+static inline u16 bpf_anc_helper(const struct sock_filter *ftest)
+{
+       BUG_ON(ftest->code & BPF_ANC);
+
+       switch (ftest->code) {
+       case BPF_LD | BPF_W | BPF_ABS:
+       case BPF_LD | BPF_H | BPF_ABS:
+       case BPF_LD | BPF_B | BPF_ABS:
+#define BPF_ANCILLARY(CODE)    case SKF_AD_OFF + SKF_AD_##CODE:        \
+                               return BPF_ANC | SKF_AD_##CODE
+               switch (ftest->k) {
+               BPF_ANCILLARY(PROTOCOL);
+               BPF_ANCILLARY(PKTTYPE);
+               BPF_ANCILLARY(IFINDEX);
+               BPF_ANCILLARY(NLATTR);
+               BPF_ANCILLARY(NLATTR_NEST);
+               BPF_ANCILLARY(MARK);
+               BPF_ANCILLARY(QUEUE);
+               BPF_ANCILLARY(HATYPE);
+               BPF_ANCILLARY(RXHASH);
+               BPF_ANCILLARY(CPU);
+               BPF_ANCILLARY(ALU_XOR_X);
+               BPF_ANCILLARY(VLAN_TAG);
+               BPF_ANCILLARY(VLAN_TAG_PRESENT);
+               BPF_ANCILLARY(PAY_OFFSET);
+               BPF_ANCILLARY(RANDOM);
+               }
+               /* Fallthrough. */
+       default:
+               return ftest->code;
+       }
+}
+
 #ifdef CONFIG_BPF_JIT
 #include <stdarg.h>
 #include <linux/linkage.h>
@@ -144,85 +425,20 @@ static inline void bpf_jit_dump(unsigned int flen, unsigned int proglen,
 }
 #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);
 }
-#endif
+#endif /* CONFIG_BPF_JIT */
 
 static inline int bpf_tell_extensions(void)
 {
        return SKF_AD_MAX;
 }
 
-enum {
-       BPF_S_RET_K = 1,
-       BPF_S_RET_A,
-       BPF_S_ALU_ADD_K,
-       BPF_S_ALU_ADD_X,
-       BPF_S_ALU_SUB_K,
-       BPF_S_ALU_SUB_X,
-       BPF_S_ALU_MUL_K,
-       BPF_S_ALU_MUL_X,
-       BPF_S_ALU_DIV_X,
-       BPF_S_ALU_MOD_K,
-       BPF_S_ALU_MOD_X,
-       BPF_S_ALU_AND_K,
-       BPF_S_ALU_AND_X,
-       BPF_S_ALU_OR_K,
-       BPF_S_ALU_OR_X,
-       BPF_S_ALU_XOR_K,
-       BPF_S_ALU_XOR_X,
-       BPF_S_ALU_LSH_K,
-       BPF_S_ALU_LSH_X,
-       BPF_S_ALU_RSH_K,
-       BPF_S_ALU_RSH_X,
-       BPF_S_ALU_NEG,
-       BPF_S_LD_W_ABS,
-       BPF_S_LD_H_ABS,
-       BPF_S_LD_B_ABS,
-       BPF_S_LD_W_LEN,
-       BPF_S_LD_W_IND,
-       BPF_S_LD_H_IND,
-       BPF_S_LD_B_IND,
-       BPF_S_LD_IMM,
-       BPF_S_LDX_W_LEN,
-       BPF_S_LDX_B_MSH,
-       BPF_S_LDX_IMM,
-       BPF_S_MISC_TAX,
-       BPF_S_MISC_TXA,
-       BPF_S_ALU_DIV_K,
-       BPF_S_LD_MEM,
-       BPF_S_LDX_MEM,
-       BPF_S_ST,
-       BPF_S_STX,
-       BPF_S_JMP_JA,
-       BPF_S_JMP_JEQ_K,
-       BPF_S_JMP_JEQ_X,
-       BPF_S_JMP_JGE_K,
-       BPF_S_JMP_JGE_X,
-       BPF_S_JMP_JGT_K,
-       BPF_S_JMP_JGT_X,
-       BPF_S_JMP_JSET_K,
-       BPF_S_JMP_JSET_X,
-       /* Ancillary data */
-       BPF_S_ANC_PROTOCOL,
-       BPF_S_ANC_PKTTYPE,
-       BPF_S_ANC_IFINDEX,
-       BPF_S_ANC_NLATTR,
-       BPF_S_ANC_NLATTR_NEST,
-       BPF_S_ANC_MARK,
-       BPF_S_ANC_QUEUE,
-       BPF_S_ANC_HATYPE,
-       BPF_S_ANC_RXHASH,
-       BPF_S_ANC_CPU,
-       BPF_S_ANC_ALU_XOR_X,
-       BPF_S_ANC_VLAN_TAG,
-       BPF_S_ANC_VLAN_TAG_PRESENT,
-       BPF_S_ANC_PAY_OFFSET,
-};
-
 #endif /* __LINUX_FILTER_H__ */
index f194ccb8539c9b7d95af35b00a426ce2a06b61a6..6bff13f740505090eb53be8b4d91e0fe805a5191 100644 (file)
@@ -1711,6 +1711,7 @@ enum ieee80211_eid {
        WLAN_EID_RRM_ENABLED_CAPABILITIES = 70,
        WLAN_EID_MULTIPLE_BSSID = 71,
        WLAN_EID_BSS_COEX_2040 = 72,
+       WLAN_EID_BSS_INTOLERANT_CHL_REPORT = 73,
        WLAN_EID_OVERLAP_BSS_SCAN_PARAM = 74,
        WLAN_EID_RIC_DESCRIPTOR = 75,
        WLAN_EID_MMIE = 76,
index 1085ffeef956589d77dea0a426feedf6450fd20f..fd22789d7b2ed3e8003829d6da9be5e57d21a74c 100644 (file)
 #include <linux/netdevice.h>
 #include <uapi/linux/if_bridge.h>
 
+struct br_ip {
+       union {
+               __be32  ip4;
+#if IS_ENABLED(CONFIG_IPV6)
+               struct in6_addr ip6;
+#endif
+       } u;
+       __be16          proto;
+       __u16           vid;
+};
+
+struct br_ip_list {
+       struct list_head list;
+       struct br_ip addr;
+};
+
 extern void brioctl_set(int (*ioctl_hook)(struct net *, unsigned int, void __user *));
 
 typedef int br_should_route_hook_t(struct sk_buff *skb);
 extern br_should_route_hook_t __rcu *br_should_route_hook;
+int br_multicast_list_adjacent(struct net_device *dev,
+                              struct list_head *br_ip_list);
+bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto);
 
 #endif
index a86784dec3d34fcafb7e5d1b377a9d73b2d7717e..119130e9298b21ef0aa2a75e90a7c756318492a7 100644 (file)
@@ -10,8 +10,9 @@ struct ifla_vf_info {
        __u8 mac[32];
        __u32 vlan;
        __u32 qos;
-       __u32 tx_rate;
        __u32 spoofchk;
        __u32 linkstate;
+       __u32 min_tx_rate;
+       __u32 max_tx_rate;
 };
 #endif /* _LINUX_IF_LINK_H */
index a9a53b12397b0c4fd29a11cbf5f9cbbd18944fd8..6b2c7cf352a5582aff86da52f4feac7ae7d208e4 100644 (file)
@@ -57,6 +57,9 @@ struct macvlan_dev {
        netdev_features_t       tap_features;
        int                     minor;
        int                     nest_level;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       struct netpoll          *netpoll;
+#endif
 };
 
 static inline void macvlan_count_rx(const struct macvlan_dev *vlan,
index b2acc4a1b13c9bd906869bc52d501f53f67d3c87..4967916fe4ac8c6732930d768a2e60fe1bec35c7 100644 (file)
@@ -106,7 +106,7 @@ struct vlan_pcpu_stats {
 
 #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
 
-extern struct net_device *__vlan_find_dev_deep(struct net_device *real_dev,
+extern struct net_device *__vlan_find_dev_deep_rcu(struct net_device *real_dev,
                                               __be16 vlan_proto, u16 vlan_id);
 extern struct net_device *vlan_dev_real_dev(const struct net_device *dev);
 extern u16 vlan_dev_vlan_id(const struct net_device *dev);
@@ -206,7 +206,7 @@ static inline int vlan_get_encap_level(struct net_device *dev)
 }
 #else
 static inline struct net_device *
-__vlan_find_dev_deep(struct net_device *real_dev,
+__vlan_find_dev_deep_rcu(struct net_device *real_dev,
                     __be16 vlan_proto, u16 vlan_id)
 {
        return NULL;
index 5a52f2c94f3f68530466c349024220c255068b4d..44bd6046e6e212db8a8e0146b0aa04ee10a7e70d 100644 (file)
@@ -164,11 +164,6 @@ unsigned capi_cmsg_header(_cmsg * cmsg, __u16 _ApplId,
                          __u8 _Command, __u8 _Subcommand,
                          __u16 _Messagenumber, __u32 _Controller);
 
-/*
- * capi_info2str generated a readable string for Capi2.0 reasons.
- */
-char *capi_info2str(__u16 reason);
-
 /*-----------------------------------------------------------------------*/
 
 /*
index 7bd2ad01e39c625ec4da93f095763b31a38b1f34..f7296e57d614f4f75f31e67754f0edc5ea36b563 100644 (file)
@@ -205,10 +205,10 @@ struct kretprobe_blackpoint {
        void *addr;
 };
 
-struct kprobe_blackpoint {
-       const char *name;
+struct kprobe_blacklist_entry {
+       struct list_head list;
        unsigned long start_addr;
-       unsigned long range;
+       unsigned long end_addr;
 };
 
 #ifdef CONFIG_KPROBES
@@ -265,6 +265,7 @@ extern void arch_disarm_kprobe(struct kprobe *p);
 extern int arch_init_kprobes(void);
 extern void show_registers(struct pt_regs *regs);
 extern void kprobes_inc_nmissed_count(struct kprobe *p);
+extern bool arch_within_kprobe_blacklist(unsigned long addr);
 
 struct kprobe_insn_cache {
        struct mutex mutex;
@@ -476,4 +477,18 @@ static inline int enable_jprobe(struct jprobe *jp)
        return enable_kprobe(&jp->kp);
 }
 
+#ifdef CONFIG_KPROBES
+/*
+ * Blacklist ganerating macro. Specify functions which is not probed
+ * by using this macro.
+ */
+#define __NOKPROBE_SYMBOL(fname)                       \
+static unsigned long __used                            \
+       __attribute__((section("_kprobe_blacklist")))   \
+       _kbl_addr_##fname = (unsigned long)fname;
+#define NOKPROBE_SYMBOL(fname) __NOKPROBE_SYMBOL(fname)
+#else
+#define NOKPROBE_SYMBOL(fname)
+#endif
+
 #endif /* _LINUX_KPROBES_H */
index 31c0cd1c941a6c122506cc3158be203391617460..de9e46e6bcc97aa66b3500cad82bcdaf14402556 100644 (file)
@@ -304,6 +304,30 @@ static inline int ktime_compare(const ktime_t cmp1, const ktime_t cmp2)
        return 0;
 }
 
+/**
+ * ktime_after - Compare if a ktime_t value is bigger than another one.
+ * @cmp1:      comparable1
+ * @cmp2:      comparable2
+ *
+ * Return: true if cmp1 happened after cmp2.
+ */
+static inline bool ktime_after(const ktime_t cmp1, const ktime_t cmp2)
+{
+       return ktime_compare(cmp1, cmp2) > 0;
+}
+
+/**
+ * ktime_before - Compare if a ktime_t value is smaller than another one.
+ * @cmp1:      comparable1
+ * @cmp2:      comparable2
+ *
+ * Return: true if cmp1 happened before cmp2.
+ */
+static inline bool ktime_before(const ktime_t cmp1, const ktime_t cmp2)
+{
+       return ktime_compare(cmp1, cmp2) < 0;
+}
+
 static inline s64 ktime_to_us(const ktime_t kt)
 {
        struct timeval tv = ktime_to_timeval(kt);
index 970c68197c698898df483198995d9a0c558c0770..ec4e3bd83d474e581bb3e4607c3c9c5f4e5319c8 100644 (file)
@@ -586,7 +586,7 @@ void mark_page_dirty(struct kvm *kvm, gfn_t gfn);
 
 void kvm_vcpu_block(struct kvm_vcpu *vcpu);
 void kvm_vcpu_kick(struct kvm_vcpu *vcpu);
-bool kvm_vcpu_yield_to(struct kvm_vcpu *target);
+int kvm_vcpu_yield_to(struct kvm_vcpu *target);
 void kvm_vcpu_on_spin(struct kvm_vcpu *vcpu);
 void kvm_load_guest_fpu(struct kvm_vcpu *vcpu);
 void kvm_put_guest_fpu(struct kvm_vcpu *vcpu);
index 3447bead962015d1dce95a694d85c2d4ba7d4fd2..b12f4bbd064ce891c0f844b4d5180710ff613b0d 100644 (file)
@@ -450,7 +450,6 @@ struct mlx4_caps {
        int                     reserved_qps_base[MLX4_NUM_QP_REGION];
        int                     log_num_macs;
        int                     log_num_vlans;
-       int                     log_num_prios;
        enum mlx4_port_type     port_type[MLX4_MAX_PORTS + 1];
        u8                      supported_type[MLX4_MAX_PORTS + 1];
        u8                      suggested_type[MLX4_MAX_PORTS + 1];
@@ -578,6 +577,9 @@ struct mlx4_cq {
 
        u32                     cons_index;
 
+       u16                     irq;
+       bool                    irq_affinity_change;
+
        __be32                 *set_ci_db;
        __be32                 *arm_db;
        int                     arm_sn;
index c26d0ec2ef3a90b7a988da8c31f9667716c78392..e5a589435e2b7df943a935b4cf6dfd6c46dd74ed 100644 (file)
@@ -42,9 +42,11 @@ enum {
        NETIF_F_TSO6_BIT,               /* ... TCPv6 segmentation */
        NETIF_F_FSO_BIT,                /* ... FCoE segmentation */
        NETIF_F_GSO_GRE_BIT,            /* ... GRE with TSO */
+       NETIF_F_GSO_GRE_CSUM_BIT,       /* ... GRE with csum 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_UDP_TUNNEL_CSUM_BIT,/* ... UDP TUNNEL with TSO & CSUM */
        NETIF_F_GSO_MPLS_BIT,           /* ... MPLS segmentation */
        /**/NETIF_F_GSO_LAST =          /* last bit, see GSO_MASK */
                NETIF_F_GSO_MPLS_BIT,
@@ -111,6 +113,7 @@ 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_GRE_CSUM   __NETIF_F(GSO_GRE_CSUM)
 #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)
index 6c1ae9fd9505e079eb7b186ce860ff8139d7ef9d..abe3de1db932a09016d430ff699a993e6761bc83 100644 (file)
@@ -56,9 +56,6 @@ struct device;
 struct phy_device;
 /* 802.11 specific */
 struct wireless_dev;
-                                       /* source back-compat hooks */
-#define SET_ETHTOOL_OPS(netdev,ops) \
-       ( (netdev)->ethtool_ops = (ops) )
 
 void netdev_set_default_ethtool_ops(struct net_device *dev,
                                    const struct ethtool_ops *ops);
@@ -853,7 +850,8 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,
  *     SR-IOV management functions.
  * int (*ndo_set_vf_mac)(struct net_device *dev, int vf, u8* mac);
  * int (*ndo_set_vf_vlan)(struct net_device *dev, int vf, u16 vlan, u8 qos);
- * int (*ndo_set_vf_tx_rate)(struct net_device *dev, int vf, int rate);
+ * int (*ndo_set_vf_rate)(struct net_device *dev, int vf, int min_tx_rate,
+ *                       int max_tx_rate);
  * int (*ndo_set_vf_spoofchk)(struct net_device *dev, int vf, bool setting);
  * int (*ndo_get_vf_config)(struct net_device *dev,
  *                         int vf, struct ifla_vf_info *ivf);
@@ -1047,8 +1045,9 @@ struct net_device_ops {
                                                  int queue, u8 *mac);
        int                     (*ndo_set_vf_vlan)(struct net_device *dev,
                                                   int queue, u16 vlan, u8 qos);
-       int                     (*ndo_set_vf_tx_rate)(struct net_device *dev,
-                                                     int vf, int rate);
+       int                     (*ndo_set_vf_rate)(struct net_device *dev,
+                                                  int vf, int min_tx_rate,
+                                                  int max_tx_rate);
        int                     (*ndo_set_vf_spoofchk)(struct net_device *dev,
                                                       int vf, bool setting);
        int                     (*ndo_get_vf_config)(struct net_device *dev,
@@ -2634,6 +2633,7 @@ 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);
+int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb);
 int dev_forward_skb(struct net_device *dev, struct sk_buff *skb);
 bool is_skb_forwardable(struct net_device *dev, struct sk_buff *skb);
 
@@ -3003,6 +3003,15 @@ 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);
+int __hw_addr_sync_dev(struct netdev_hw_addr_list *list,
+                      struct net_device *dev,
+                      int (*sync)(struct net_device *, const unsigned char *),
+                      int (*unsync)(struct net_device *,
+                                    const unsigned char *));
+void __hw_addr_unsync_dev(struct netdev_hw_addr_list *list,
+                         struct net_device *dev,
+                         int (*unsync)(struct net_device *,
+                                       const unsigned char *));
 void __hw_addr_init(struct netdev_hw_addr_list *list);
 
 /* Functions used for device addresses handling */
@@ -3023,6 +3032,38 @@ 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);
 
+/**
+ *  __dev_uc_sync - Synchonize device's unicast list
+ *  @dev:  device to sync
+ *  @sync: function to call if address should be added
+ *  @unsync: function to call if address should be removed
+ *
+ *  Add newly added addresses to the interface, and release
+ *  addresses that have been deleted.
+ **/
+static inline int __dev_uc_sync(struct net_device *dev,
+                               int (*sync)(struct net_device *,
+                                           const unsigned char *),
+                               int (*unsync)(struct net_device *,
+                                             const unsigned char *))
+{
+       return __hw_addr_sync_dev(&dev->uc, dev, sync, unsync);
+}
+
+/**
+ *  __dev_uc_unsync - Remove synchonized addresses from device
+ *  @dev:  device to sync
+ *  @unsync: function to call if address should be removed
+ *
+ *  Remove all addresses that were added to the device by dev_uc_sync().
+ **/
+static inline void __dev_uc_unsync(struct net_device *dev,
+                                  int (*unsync)(struct net_device *,
+                                                const unsigned char *))
+{
+       __hw_addr_unsync_dev(&dev->uc, dev, unsync);
+}
+
 /* Functions used for multicast addresses handling */
 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);
@@ -3035,6 +3076,38 @@ 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);
 
+/**
+ *  __dev_mc_sync - Synchonize device's multicast list
+ *  @dev:  device to sync
+ *  @sync: function to call if address should be added
+ *  @unsync: function to call if address should be removed
+ *
+ *  Add newly added addresses to the interface, and release
+ *  addresses that have been deleted.
+ **/
+static inline int __dev_mc_sync(struct net_device *dev,
+                               int (*sync)(struct net_device *,
+                                           const unsigned char *),
+                               int (*unsync)(struct net_device *,
+                                             const unsigned char *))
+{
+       return __hw_addr_sync_dev(&dev->mc, dev, sync, unsync);
+}
+
+/**
+ *  __dev_mc_unsync - Remove synchonized addresses from device
+ *  @dev:  device to sync
+ *  @unsync: function to call if address should be removed
+ *
+ *  Remove all addresses that were added to the device by dev_mc_sync().
+ **/
+static inline void __dev_mc_unsync(struct net_device *dev,
+                                  int (*unsync)(struct net_device *,
+                                                const unsigned char *))
+{
+       __hw_addr_unsync_dev(&dev->mc, dev, unsync);
+}
+
 /* Functions used for secondary unicast and multicast support */
 void dev_set_rx_mode(struct net_device *dev);
 void __dev_set_rx_mode(struct net_device *dev);
@@ -3180,6 +3253,20 @@ const char *netdev_drivername(const struct net_device *dev);
 
 void linkwatch_run_queue(void);
 
+static inline netdev_features_t netdev_intersect_features(netdev_features_t f1,
+                                                         netdev_features_t f2)
+{
+       if (f1 & NETIF_F_GEN_CSUM)
+               f1 |= (NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM);
+       if (f2 & NETIF_F_GEN_CSUM)
+               f2 |= (NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM);
+       f1 &= f2;
+       if (f1 & NETIF_F_GEN_CSUM)
+               f1 &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM);
+
+       return f1;
+}
+
 static inline netdev_features_t netdev_get_wanted_features(
        struct net_device *dev)
 {
index b2e85e59f76085cb0e56294c78b63d3a04a0762a..6ec975748742793fd51c274314a208ea5cb697db 100644 (file)
@@ -3,11 +3,17 @@
 
 #include <uapi/linux/netfilter/nfnetlink_acct.h>
 
+enum {
+       NFACCT_NO_QUOTA         = -1,
+       NFACCT_UNDERQUOTA,
+       NFACCT_OVERQUOTA,
+};
 
 struct nf_acct;
 
 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);
-
+extern int nfnl_acct_overquota(const struct sk_buff *skb,
+                             struct nf_acct *nfacct);
 #endif /* _NFNL_ACCT_H */
index 034cda789a150f3d6a5b8416863bd48469aaacf8..9e572daa15d568cc0d7c82342a0d7fb3ad37c327 100644 (file)
@@ -46,7 +46,8 @@ struct netlink_kernel_cfg {
        unsigned int    flags;
        void            (*input)(struct sk_buff *skb);
        struct mutex    *cb_mutex;
-       void            (*bind)(int group);
+       int             (*bind)(int group);
+       void            (*unbind)(int group);
        bool            (*compare)(struct net *net, struct sock *sk);
 };
 
index c8d7f3965fff913a55f158a53b7bc0944c1d4b61..20163b9a0eae70cfdfba688bab5dc08eed5fcfdb 100644 (file)
@@ -80,6 +80,22 @@ enum {
 
        IEEE802154_ATTR_FRAME_RETRIES,
 
+       IEEE802154_ATTR_LLSEC_ENABLED,
+       IEEE802154_ATTR_LLSEC_SECLEVEL,
+       IEEE802154_ATTR_LLSEC_KEY_MODE,
+       IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT,
+       IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED,
+       IEEE802154_ATTR_LLSEC_KEY_ID,
+       IEEE802154_ATTR_LLSEC_FRAME_COUNTER,
+       IEEE802154_ATTR_LLSEC_KEY_BYTES,
+       IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES,
+       IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS,
+       IEEE802154_ATTR_LLSEC_FRAME_TYPE,
+       IEEE802154_ATTR_LLSEC_CMD_FRAME_ID,
+       IEEE802154_ATTR_LLSEC_SECLEVELS,
+       IEEE802154_ATTR_LLSEC_DEV_OVERRIDE,
+       IEEE802154_ATTR_LLSEC_DEV_KEY_MODE,
+
        __IEEE802154_ATTR_MAX,
 };
 
@@ -134,6 +150,21 @@ enum {
 
        IEEE802154_SET_MACPARAMS,
 
+       IEEE802154_LLSEC_GETPARAMS,
+       IEEE802154_LLSEC_SETPARAMS,
+       IEEE802154_LLSEC_LIST_KEY,
+       IEEE802154_LLSEC_ADD_KEY,
+       IEEE802154_LLSEC_DEL_KEY,
+       IEEE802154_LLSEC_LIST_DEV,
+       IEEE802154_LLSEC_ADD_DEV,
+       IEEE802154_LLSEC_DEL_DEV,
+       IEEE802154_LLSEC_LIST_DEVKEY,
+       IEEE802154_LLSEC_ADD_DEVKEY,
+       IEEE802154_LLSEC_DEL_DEVKEY,
+       IEEE802154_LLSEC_LIST_SECLEVEL,
+       IEEE802154_LLSEC_ADD_SECLEVEL,
+       IEEE802154_LLSEC_DEL_SECLEVEL,
+
        __IEEE802154_CMD_MAX,
 };
 
index 881a7c3571f4617b99e0845995800d98eb4f9349..a70c9493d55a4c01976b093b965cc91b270aaf92 100644 (file)
@@ -22,12 +22,12 @@ extern struct phy_device *of_phy_connect(struct net_device *dev,
 struct phy_device *of_phy_attach(struct net_device *dev,
                                 struct device_node *phy_np, u32 flags,
                                 phy_interface_t iface);
-extern struct phy_device *of_phy_connect_fixed_link(struct net_device *dev,
-                                        void (*hndlr)(struct net_device *),
-                                        phy_interface_t iface);
 
 extern struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np);
 
+extern void of_mdiobus_link_phydev(struct mii_bus *mdio,
+                                  struct phy_device *phydev);
+
 #else /* CONFIG_OF */
 static inline int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
 {
@@ -59,17 +59,30 @@ static inline struct phy_device *of_phy_attach(struct net_device *dev,
        return NULL;
 }
 
-static inline struct phy_device *of_phy_connect_fixed_link(struct net_device *dev,
-                                                          void (*hndlr)(struct net_device *),
-                                                          phy_interface_t iface)
+static inline struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np)
 {
        return NULL;
 }
 
-static inline struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np)
+static inline void of_mdiobus_link_phydev(struct mii_bus *mdio,
+                                         struct phy_device *phydev)
 {
-       return NULL;
 }
 #endif /* CONFIG_OF */
 
+#if defined(CONFIG_OF) && defined(CONFIG_FIXED_PHY)
+extern int of_phy_register_fixed_link(struct device_node *np);
+extern bool of_phy_is_fixed_link(struct device_node *np);
+#else
+static inline int of_phy_register_fixed_link(struct device_node *np)
+{
+       return -ENOSYS;
+}
+static inline bool of_phy_is_fixed_link(struct device_node *np)
+{
+       return false;
+}
+#endif
+
+
 #endif /* __LINUX_OF_MDIO_H */
index a9209118d80f9b61ccef036a3b1e67e91ceee925..707617a8c0f6c647b8f3c9d210b833638c20eb02 100644 (file)
@@ -166,6 +166,11 @@ struct perf_event;
  */
 #define PERF_EVENT_TXN 0x1
 
+/**
+ * pmu::capabilities flags
+ */
+#define PERF_PMU_CAP_NO_INTERRUPT              0x01
+
 /**
  * struct pmu - generic performance monitoring unit
  */
@@ -178,6 +183,11 @@ struct pmu {
        const char                      *name;
        int                             type;
 
+       /*
+        * various common per-pmu feature flags
+        */
+       int                             capabilities;
+
        int * __percpu                  pmu_disable_count;
        struct perf_cpu_context * __percpu pmu_cpu_context;
        int                             task_ctx_nr;
@@ -696,7 +706,8 @@ extern struct perf_guest_info_callbacks *perf_guest_cbs;
 extern int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks);
 extern int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks);
 
-extern void perf_event_comm(struct task_struct *tsk);
+extern void perf_event_exec(void);
+extern void perf_event_comm(struct task_struct *tsk, bool exec);
 extern void perf_event_fork(struct task_struct *tsk);
 
 /* Callchains */
@@ -773,7 +784,7 @@ extern void perf_event_enable(struct perf_event *event);
 extern void perf_event_disable(struct perf_event *event);
 extern int __perf_event_disable(void *info);
 extern void perf_event_task_tick(void);
-#else
+#else /* !CONFIG_PERF_EVENTS: */
 static inline void
 perf_event_task_sched_in(struct task_struct *prev,
                         struct task_struct *task)                      { }
@@ -803,7 +814,8 @@ static inline int perf_unregister_guest_info_callbacks
 (struct perf_guest_info_callbacks *callbacks)                          { return 0; }
 
 static inline void perf_event_mmap(struct vm_area_struct *vma)         { }
-static inline void perf_event_comm(struct task_struct *tsk)            { }
+static inline void perf_event_exec(void)                               { }
+static inline void perf_event_comm(struct task_struct *tsk, bool exec) { }
 static inline void perf_event_fork(struct task_struct *tsk)            { }
 static inline void perf_event_init(void)                               { }
 static inline int  perf_swevent_get_recursion_context(void)            { return -1; }
index 4d0221fd0688df5d572072d8b9f871a5637ff460..864ddafad8cc2f0697b181d9c113ca02ef715613 100644 (file)
@@ -198,6 +198,13 @@ static inline struct mii_bus *mdiobus_alloc(void)
 int mdiobus_register(struct mii_bus *bus);
 void mdiobus_unregister(struct mii_bus *bus);
 void mdiobus_free(struct mii_bus *bus);
+struct mii_bus *devm_mdiobus_alloc_size(struct device *dev, int sizeof_priv);
+static inline struct mii_bus *devm_mdiobus_alloc(struct device *dev)
+{
+       return devm_mdiobus_alloc_size(dev, 0);
+}
+
+void devm_mdiobus_free(struct device *dev, struct mii_bus *bus);
 struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr);
 int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
 int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
@@ -666,6 +673,7 @@ static inline int phy_read_status(struct phy_device *phydev)
        return phydev->drv->read_status(phydev);
 }
 
+int genphy_config_init(struct phy_device *phydev);
 int genphy_setup_forced(struct phy_device *phydev);
 int genphy_restart_aneg(struct phy_device *phydev);
 int genphy_config_aneg(struct phy_device *phydev);
index 509d8f5f984e3985cffe178b8a335498f30bcf50..ae612acebb53d2f0bf121226afde877cb4d32f91 100644 (file)
@@ -9,15 +9,31 @@ struct fixed_phy_status {
        int asym_pause;
 };
 
+struct device_node;
+
 #ifdef CONFIG_FIXED_PHY
 extern int fixed_phy_add(unsigned int irq, int phy_id,
                         struct fixed_phy_status *status);
+extern int fixed_phy_register(unsigned int irq,
+                             struct fixed_phy_status *status,
+                             struct device_node *np);
+extern void fixed_phy_del(int phy_addr);
 #else
 static inline int fixed_phy_add(unsigned int irq, int phy_id,
                                struct fixed_phy_status *status)
 {
        return -ENODEV;
 }
+static inline int fixed_phy_register(unsigned int irq,
+                                    struct fixed_phy_status *status,
+                                    struct device_node *np)
+{
+       return -ENODEV;
+}
+static inline int fixed_phy_del(int phy_addr)
+{
+       return -ENODEV;
+}
 #endif /* CONFIG_FIXED_PHY */
 
 /*
diff --git a/include/linux/platform_data/st21nfca.h b/include/linux/platform_data/st21nfca.h
new file mode 100644 (file)
index 0000000..1730312
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Driver include for the ST21NFCA NFC chip.
+ *
+ * Copyright (C) 2014  STMicroelectronics SAS. All rights reserved.
+ *
+ * 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 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _ST21NFCA_HCI_H_
+#define _ST21NFCA_HCI_H_
+
+#include <linux/i2c.h>
+
+#define ST21NFCA_HCI_DRIVER_NAME "st21nfca_hci"
+
+struct st21nfca_nfc_platform_data {
+       unsigned int gpio_irq;
+       unsigned int gpio_ena;
+       unsigned int irq_polarity;
+};
+
+#endif /* _ST21NFCA_HCI_H_ */
index 4d09f6eab359f8e78b2c5b89b45797dd8fec17de..20bcb55498cd5714028bf7ccf9474b9fa36aa7aa 100644 (file)
  * struct rfkill_gpio_platform_data - platform data for rfkill gpio device.
  * for unused gpio's, the expected value is -1.
  * @name:              name for the gpio rf kill instance
- * @reset_gpio:                GPIO which is used for reseting rfkill switch
- * @shutdown_gpio:     GPIO which is used for shutdown of rfkill switch
- * @power_clk_name:    [optional] name of clk to turn off while blocked
- * @gpio_runtime_close:        clean up platform specific gpio configuration
- * @gpio_runtime_setup:        set up platform specific gpio configuration
  */
 
 struct rfkill_gpio_platform_data {
        char                    *name;
-       int                     reset_gpio;
-       int                     shutdown_gpio;
-       const char              *power_clk_name;
        enum rfkill_type        type;
-       void    (*gpio_runtime_close)(struct platform_device *);
-       int     (*gpio_runtime_setup)(struct platform_device *);
 };
 
 #endif /* __RFKILL_GPIO_H */
index d69cf637a15a3093863cd33f09929b87d73fd0a9..49a4d6f59108f957d4534190c161344981c48e46 100644 (file)
@@ -97,7 +97,7 @@ __ring_buffer_alloc(unsigned long size, unsigned flags, struct lock_class_key *k
        __ring_buffer_alloc((size), (flags), &__key);   \
 })
 
-void ring_buffer_wait(struct ring_buffer *buffer, int cpu);
+int ring_buffer_wait(struct ring_buffer *buffer, int cpu);
 int ring_buffer_poll_wait(struct ring_buffer *buffer, int cpu,
                          struct file *filp, poll_table *poll_table);
 
index 03f3b05e8ec17dda4d7b8dcefcc1e723bc6e606c..8d79708146aa47d642d37e82ca3beea2eaa7c014 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <linux/atomic.h>
 
+struct optimistic_spin_queue;
 struct rw_semaphore;
 
 #ifdef CONFIG_RWSEM_GENERIC_SPINLOCK
@@ -23,9 +24,17 @@ struct rw_semaphore;
 #else
 /* All arch specific implementations share the same struct */
 struct rw_semaphore {
-       long                    count;
-       raw_spinlock_t          wait_lock;
-       struct list_head        wait_list;
+       long count;
+       raw_spinlock_t wait_lock;
+       struct list_head wait_list;
+#ifdef CONFIG_SMP
+       /*
+        * Write owner. Used as a speculative check to see
+        * if the owner is running on the cpu.
+        */
+       struct task_struct *owner;
+       struct optimistic_spin_queue *osq; /* spinner MCS lock */
+#endif
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
        struct lockdep_map      dep_map;
 #endif
@@ -55,11 +64,21 @@ static inline int rwsem_is_locked(struct rw_semaphore *sem)
 # define __RWSEM_DEP_MAP_INIT(lockname)
 #endif
 
+#if defined(CONFIG_SMP) && !defined(CONFIG_RWSEM_GENERIC_SPINLOCK)
+#define __RWSEM_INITIALIZER(name)                      \
+       { RWSEM_UNLOCKED_VALUE,                         \
+         __RAW_SPIN_LOCK_UNLOCKED(name.wait_lock),     \
+         LIST_HEAD_INIT((name).wait_list),             \
+         NULL, /* owner */                             \
+         NULL /* mcs lock */                           \
+         __RWSEM_DEP_MAP_INIT(name) }
+#else
 #define __RWSEM_INITIALIZER(name)                      \
        { RWSEM_UNLOCKED_VALUE,                         \
          __RAW_SPIN_LOCK_UNLOCKED(name.wait_lock),     \
          LIST_HEAD_INIT((name).wait_list)              \
          __RWSEM_DEP_MAP_INIT(name) }
+#endif
 
 #define DECLARE_RWSEM(name) \
        struct rw_semaphore name = __RWSEM_INITIALIZER(name)
index ea74596014a2175b4e379bb955096f386819227e..306f4f0c987a006f43f520413f7de3a780f98a23 100644 (file)
@@ -847,10 +847,10 @@ enum cpu_idle_type {
 };
 
 /*
- * Increase resolution of cpu_power calculations
+ * Increase resolution of cpu_capacity calculations
  */
-#define SCHED_POWER_SHIFT      10
-#define SCHED_POWER_SCALE      (1L << SCHED_POWER_SHIFT)
+#define SCHED_CAPACITY_SHIFT   10
+#define SCHED_CAPACITY_SCALE   (1L << SCHED_CAPACITY_SHIFT)
 
 /*
  * sched-domains (multiprocessor balancing) declarations:
@@ -862,7 +862,7 @@ enum cpu_idle_type {
 #define SD_BALANCE_FORK                0x0008  /* Balance on fork, clone */
 #define SD_BALANCE_WAKE                0x0010  /* Balance on wakeup */
 #define SD_WAKE_AFFINE         0x0020  /* Wake task to waking CPU */
-#define SD_SHARE_CPUPOWER      0x0080  /* Domain members share cpu power */
+#define SD_SHARE_CPUCAPACITY   0x0080  /* Domain members share cpu power */
 #define SD_SHARE_POWERDOMAIN   0x0100  /* Domain members share power domain */
 #define SD_SHARE_PKG_RESOURCES 0x0200  /* Domain members share cpu pkg resources */
 #define SD_SERIALIZE           0x0400  /* Only a single load balancing instance */
@@ -874,7 +874,7 @@ enum cpu_idle_type {
 #ifdef CONFIG_SCHED_SMT
 static inline const int cpu_smt_flags(void)
 {
-       return SD_SHARE_CPUPOWER | SD_SHARE_PKG_RESOURCES;
+       return SD_SHARE_CPUCAPACITY | SD_SHARE_PKG_RESOURCES;
 }
 #endif
 
@@ -1006,7 +1006,7 @@ typedef const int (*sched_domain_flags_f)(void);
 struct sd_data {
        struct sched_domain **__percpu sd;
        struct sched_group **__percpu sg;
-       struct sched_group_power **__percpu sgp;
+       struct sched_group_capacity **__percpu sgc;
 };
 
 struct sched_domain_topology_level {
@@ -2173,7 +2173,7 @@ static inline void sched_autogroup_fork(struct signal_struct *sig) { }
 static inline void sched_autogroup_exit(struct signal_struct *sig) { }
 #endif
 
-extern bool yield_to(struct task_struct *p, bool preempt);
+extern int yield_to(struct task_struct *p, bool preempt);
 extern void set_user_nice(struct task_struct *p, long nice);
 extern int task_prio(const struct task_struct *p);
 /**
@@ -2421,7 +2421,11 @@ extern long do_fork(unsigned long, unsigned long, unsigned long, int __user *, i
 struct task_struct *fork_idle(int);
 extern pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
 
-extern void set_task_comm(struct task_struct *tsk, const char *from);
+extern void __set_task_comm(struct task_struct *tsk, const char *from, bool exec);
+static inline void set_task_comm(struct task_struct *tsk, const char *from)
+{
+       __set_task_comm(tsk, from, false);
+}
 extern char *get_task_comm(char *to, struct task_struct *tsk);
 
 #ifdef CONFIG_SMP
index 08074a8101646d0c438415979124b3a5ff8283bb..5b5cd3189c985f89a055e35d8d1c67c63879d8fc 100644 (file)
@@ -345,6 +345,10 @@ enum {
        SKB_GSO_UDP_TUNNEL = 1 << 9,
 
        SKB_GSO_MPLS = 1 << 10,
+
+       SKB_GSO_UDP_TUNNEL_CSUM = 1 << 11,
+
+       SKB_GSO_GRE_CSUM = 1 << 12,
 };
 
 #if BITS_PER_LONG > 32
@@ -426,7 +430,7 @@ static inline u32 skb_mstamp_us_delta(const struct skb_mstamp *t1,
  *     @csum_start: Offset from skb->head where checksumming should start
  *     @csum_offset: Offset from csum_start where checksum should be stored
  *     @priority: Packet queueing priority
- *     @local_df: allow local fragmentation
+ *     @ignore_df: allow local fragmentation
  *     @cloned: Head may be cloned (check refcnt to be sure)
  *     @ip_summed: Driver fed us an IP checksum
  *     @nohdr: Payload reference only, must not modify header
@@ -514,7 +518,7 @@ struct sk_buff {
        };
        __u32                   priority;
        kmemcheck_bitfield_begin(flags1);
-       __u8                    local_df:1,
+       __u8                    ignore_df:1,
                                cloned:1,
                                ip_summed:2,
                                nohdr:1,
@@ -567,7 +571,10 @@ struct sk_buff {
         * headers if needed
         */
        __u8                    encapsulation:1;
-       /* 6/8 bit hole (depending on ndisc_nodetype presence) */
+       __u8                    encap_hdr_csum:1;
+       __u8                    csum_valid:1;
+       __u8                    csum_complete_sw:1;
+       /* 3/5 bit hole (depending on ndisc_nodetype presence) */
        kmemcheck_bitfield_end(flags2);
 
 #if defined CONFIG_NET_DMA || defined CONFIG_NET_RX_BUSY_POLL
@@ -739,7 +746,13 @@ 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);
+struct sk_buff *__pskb_copy_fclone(struct sk_buff *skb, int headroom,
+                                  gfp_t gfp_mask, bool fclone);
+static inline struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom,
+                                         gfp_t gfp_mask)
+{
+       return __pskb_copy_fclone(skb, headroom, gfp_mask, false);
+}
 
 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,
@@ -2233,6 +2246,14 @@ static inline struct sk_buff *pskb_copy(struct sk_buff *skb,
        return __pskb_copy(skb, skb_headroom(skb), gfp_mask);
 }
 
+
+static inline struct sk_buff *pskb_copy_for_clone(struct sk_buff *skb,
+                                                 gfp_t gfp_mask)
+{
+       return __pskb_copy_fclone(skb, skb_headroom(skb), gfp_mask, true);
+}
+
+
 /**
  *     skb_clone_writable - is the header of a clone writable
  *     @skb: buffer to check
@@ -2716,7 +2737,7 @@ __sum16 __skb_checksum_complete(struct sk_buff *skb);
 
 static inline int skb_csum_unnecessary(const struct sk_buff *skb)
 {
-       return skb->ip_summed & CHECKSUM_UNNECESSARY;
+       return ((skb->ip_summed & CHECKSUM_UNNECESSARY) || skb->csum_valid);
 }
 
 /**
@@ -2741,6 +2762,103 @@ static inline __sum16 skb_checksum_complete(struct sk_buff *skb)
               0 : __skb_checksum_complete(skb);
 }
 
+/* Check if we need to perform checksum complete validation.
+ *
+ * Returns true if checksum complete is needed, false otherwise
+ * (either checksum is unnecessary or zero checksum is allowed).
+ */
+static inline bool __skb_checksum_validate_needed(struct sk_buff *skb,
+                                                 bool zero_okay,
+                                                 __sum16 check)
+{
+       if (skb_csum_unnecessary(skb) || (zero_okay && !check)) {
+               skb->csum_valid = 1;
+               return false;
+       }
+
+       return true;
+}
+
+/* For small packets <= CHECKSUM_BREAK peform checksum complete directly
+ * in checksum_init.
+ */
+#define CHECKSUM_BREAK 76
+
+/* Validate (init) checksum based on checksum complete.
+ *
+ * Return values:
+ *   0: checksum is validated or try to in skb_checksum_complete. In the latter
+ *     case the ip_summed will not be CHECKSUM_UNNECESSARY and the pseudo
+ *     checksum is stored in skb->csum for use in __skb_checksum_complete
+ *   non-zero: value of invalid checksum
+ *
+ */
+static inline __sum16 __skb_checksum_validate_complete(struct sk_buff *skb,
+                                                      bool complete,
+                                                      __wsum psum)
+{
+       if (skb->ip_summed == CHECKSUM_COMPLETE) {
+               if (!csum_fold(csum_add(psum, skb->csum))) {
+                       skb->csum_valid = 1;
+                       return 0;
+               }
+       }
+
+       skb->csum = psum;
+
+       if (complete || skb->len <= CHECKSUM_BREAK) {
+               __sum16 csum;
+
+               csum = __skb_checksum_complete(skb);
+               skb->csum_valid = !csum;
+               return csum;
+       }
+
+       return 0;
+}
+
+static inline __wsum null_compute_pseudo(struct sk_buff *skb, int proto)
+{
+       return 0;
+}
+
+/* Perform checksum validate (init). Note that this is a macro since we only
+ * want to calculate the pseudo header which is an input function if necessary.
+ * First we try to validate without any computation (checksum unnecessary) and
+ * then calculate based on checksum complete calling the function to compute
+ * pseudo header.
+ *
+ * Return values:
+ *   0: checksum is validated or try to in skb_checksum_complete
+ *   non-zero: value of invalid checksum
+ */
+#define __skb_checksum_validate(skb, proto, complete,                  \
+                               zero_okay, check, compute_pseudo)       \
+({                                                                     \
+       __sum16 __ret = 0;                                              \
+       skb->csum_valid = 0;                                            \
+       if (__skb_checksum_validate_needed(skb, zero_okay, check))      \
+               __ret = __skb_checksum_validate_complete(skb,           \
+                               complete, compute_pseudo(skb, proto));  \
+       __ret;                                                          \
+})
+
+#define skb_checksum_init(skb, proto, compute_pseudo)                  \
+       __skb_checksum_validate(skb, proto, false, false, 0, compute_pseudo)
+
+#define skb_checksum_init_zero_check(skb, proto, check, compute_pseudo)        \
+       __skb_checksum_validate(skb, proto, false, true, check, compute_pseudo)
+
+#define skb_checksum_validate(skb, proto, compute_pseudo)              \
+       __skb_checksum_validate(skb, proto, true, false, 0, compute_pseudo)
+
+#define skb_checksum_validate_zero_check(skb, proto, check,            \
+                                        compute_pseudo)                \
+       __skb_checksum_validate_(skb, proto, true, true, check, compute_pseudo)
+
+#define skb_checksum_simple_validate(skb)                              \
+       __skb_checksum_validate(skb, 0, true, false, 0, null_compute_pseudo)
+
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
 void nf_conntrack_destroy(struct nf_conntrack *nfct);
 static inline void nf_conntrack_put(struct nf_conntrack *nfct)
@@ -2895,6 +3013,7 @@ static inline struct sec_path *skb_sec_path(struct sk_buff *skb)
 struct skb_gso_cb {
        int     mac_offset;
        int     encap_level;
+       __u16   csum_start;
 };
 #define SKB_GSO_CB(skb) ((struct skb_gso_cb *)(skb)->cb)
 
@@ -2919,6 +3038,28 @@ static inline int gso_pskb_expand_head(struct sk_buff *skb, int extra)
        return 0;
 }
 
+/* Compute the checksum for a gso segment. First compute the checksum value
+ * from the start of transport header to SKB_GSO_CB(skb)->csum_start, and
+ * then add in skb->csum (checksum from csum_start to end of packet).
+ * skb->csum and csum_start are then updated to reflect the checksum of the
+ * resultant packet starting from the transport header-- the resultant checksum
+ * is in the res argument (i.e. normally zero or ~ of checksum of a pseudo
+ * header.
+ */
+static inline __sum16 gso_make_checksum(struct sk_buff *skb, __wsum res)
+{
+       int plen = SKB_GSO_CB(skb)->csum_start - skb_headroom(skb) -
+           skb_transport_offset(skb);
+       __u16 csum;
+
+       csum = csum_fold(csum_partial(skb_transport_header(skb),
+                                     plen, skb->csum));
+       skb->csum = res;
+       SKB_GSO_CB(skb)->csum_start -= plen;
+
+       return csum;
+}
+
 static inline bool skb_is_gso(const struct sk_buff *skb)
 {
        return skb_shinfo(skb)->gso_size;
index aa327a8105ada0d2502d697c6f66699e8f8c516b..b2b1afbb32024ebbb80be196ea276a7ff53ae43e 100644 (file)
@@ -26,20 +26,6 @@ struct at86rf230_platform_data {
        int rstn;
        int slp_tr;
        int dig2;
-
-       /* Setting the irq_type will configure the driver to request
-        * the platform irq trigger type according to the given value
-        * and configure the interrupt polarity of the device to the
-        * corresponding polarity.
-        *
-        * Allowed values are: IRQF_TRIGGER_RISING, IRQF_TRIGGER_FALLING,
-        *                     IRQF_TRIGGER_HIGH and IRQF_TRIGGER_LOW
-        *
-        * Setting it to 0, the driver does not touch the trigger type
-        * configuration of the interrupt and sets the interrupt polarity
-        * of the device to high active (the default value).
-        */
-       int irq_type;
 };
 
 #endif
index 07ef9b82b66da9088f0a8811d21d14d8f0b66db2..4568a5cc9ab851c5c53e536cfbcd5e62003d6a47 100644 (file)
@@ -33,6 +33,7 @@ struct ssb_sprom {
        u8 et1phyaddr;          /* MII address for enet1 */
        u8 et0mdcport;          /* MDIO for enet0 */
        u8 et1mdcport;          /* MDIO for enet1 */
+       u16 dev_id;             /* Device ID overriding e.g. PCI ID */
        u16 board_rev;          /* Board revision number from SPROM. */
        u16 board_num;          /* Board number from SPROM. */
        u16 board_type;         /* Board type from SPROM. */
index 239946868142cec2893e89259555d3b86884d616..a0513210798fc9027af01dccccc5ff6c677d3d7e 100644 (file)
@@ -197,7 +197,8 @@ struct tcp_sock {
        u8      do_early_retrans:1,/* Enable RFC5827 early-retransmit  */
                syn_data:1,     /* SYN includes data */
                syn_fastopen:1, /* SYN includes Fast Open option */
-               syn_data_acked:1;/* data in SYN is acked by SYN-ACK */
+               syn_data_acked:1,/* data in SYN is acked by SYN-ACK */
+               is_cwnd_limited:1;/* forward progress limited by snd_cwnd? */
        u32     tlp_high_seq;   /* snd_nxt at the time of TLP retransmit. */
 
 /* RTT measurement */
@@ -209,6 +210,8 @@ struct tcp_sock {
 
        u32     packets_out;    /* Packets which are "in flight"        */
        u32     retrans_out;    /* Retransmitted packets out            */
+       u32     max_packets_out;  /* max packets_out in last window */
+       u32     max_packets_seq;  /* right edge of max_packets_out flight */
 
        u16     urg_data;       /* Saved octet of OOB data and control flags */
        u8      ecn_flags;      /* ECN status bits.                     */
@@ -365,11 +368,6 @@ static inline bool tcp_passive_fastopen(const struct sock *sk)
                tcp_sk(sk)->fastopen_rsk != NULL);
 }
 
-static inline bool fastopen_cookie_present(struct tcp_fastopen_cookie *foc)
-{
-       return foc->len != -1;
-}
-
 extern void tcp_sock_destruct(struct sock *sk);
 
 static inline int fastopen_init_queue(struct sock *sk, int backlog)
index 42278bbf7a882d90360d141db9c8cc1b1eca1719..247cfdcc4b08bbf377ff5819ebd02683806b0c83 100644 (file)
@@ -47,7 +47,9 @@ struct udp_sock {
 #define udp_portaddr_node      inet.sk.__sk_common.skc_portaddr_node
        int              pending;       /* Any pending frames ? */
        unsigned int     corkflag;      /* Cork is required */
-       __u16            encap_type;    /* Is this an Encapsulation socket? */
+       __u8             encap_type;    /* Is this an Encapsulation socket? */
+       unsigned char    no_check6_tx:1,/* Send zero UDP6 checksums on TX? */
+                        no_check6_rx:1;/* Allow zero UDP6 checksums on RX? */
        /*
         * Following member retains the information to create a UDP header
         * when the socket is uncorked.
@@ -76,6 +78,26 @@ static inline struct udp_sock *udp_sk(const struct sock *sk)
        return (struct udp_sock *)sk;
 }
 
+static inline void udp_set_no_check6_tx(struct sock *sk, bool val)
+{
+       udp_sk(sk)->no_check6_tx = val;
+}
+
+static inline void udp_set_no_check6_rx(struct sock *sk, bool val)
+{
+       udp_sk(sk)->no_check6_rx = val;
+}
+
+static inline bool udp_get_no_check6_tx(struct sock *sk)
+{
+       return udp_sk(sk)->no_check6_tx;
+}
+
+static inline bool udp_get_no_check6_rx(struct sock *sk)
+{
+       return udp_sk(sk)->no_check6_rx;
+}
+
 #define udp_portaddr_for_each_entry(__sk, node, list) \
        hlist_nulls_for_each_entry(__sk, node, list, __sk_common.skc_portaddr_node)
 
index c52f827ba6ce6449c3e6c62736d97d5c11192c39..4f844c6b03ee2c8c04bd4c745fd292e4229b7989 100644 (file)
@@ -103,6 +103,7 @@ extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, u
 extern bool __weak is_swbp_insn(uprobe_opcode_t *insn);
 extern bool __weak is_trap_insn(uprobe_opcode_t *insn);
 extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs);
+extern unsigned long uprobe_get_trap_addr(struct pt_regs *regs);
 extern int uprobe_write_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_t);
 extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc);
 extern int uprobe_apply(struct inode *inode, loff_t offset, struct uprobe_consumer *uc, bool);
@@ -133,6 +134,9 @@ extern void __weak arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
 #else /* !CONFIG_UPROBES */
 struct uprobes_state {
 };
+
+#define uprobe_get_trap_addr(regs)     instruction_pointer(regs)
+
 static inline int
 uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc)
 {
index 44b38b92236a5834f022185e99707ea9ac6edc39..7c9b484735c533ed4243be050d8ad2e8c67d581a 100644 (file)
 #define        CDC_NCM_NTB_MAX_SIZE_TX                 32768   /* bytes */
 #define        CDC_NCM_NTB_MAX_SIZE_RX                 32768   /* bytes */
 
+/* Initial NTB length */
+#define        CDC_NCM_NTB_DEF_SIZE_TX                 16384   /* bytes */
+#define        CDC_NCM_NTB_DEF_SIZE_RX                 16384   /* bytes */
+
 /* Minimum value for MaxDatagramSize, ch. 6.2.9 */
 #define        CDC_NCM_MIN_DATAGRAM_SIZE               1514    /* bytes */
 
 /* Restart the timer, if amount of datagrams is less than given value */
 #define        CDC_NCM_RESTART_TIMER_DATAGRAM_CNT      3
 #define        CDC_NCM_TIMER_PENDING_CNT               2
-#define CDC_NCM_TIMER_INTERVAL                 (400UL * NSEC_PER_USEC)
-
-/* The following macro defines the minimum header space */
-#define        CDC_NCM_MIN_HDR_SIZE \
-       (sizeof(struct usb_cdc_ncm_nth16) + sizeof(struct usb_cdc_ncm_ndp16) + \
-       (CDC_NCM_DPT_DATAGRAMS_MAX + 1) * sizeof(struct usb_cdc_ncm_dpe16))
-
-#define CDC_NCM_NDP_SIZE \
-       (sizeof(struct usb_cdc_ncm_ndp16) +                             \
-             (CDC_NCM_DPT_DATAGRAMS_MAX + 1) * sizeof(struct usb_cdc_ncm_dpe16))
+#define CDC_NCM_TIMER_INTERVAL_USEC            400UL
+#define CDC_NCM_TIMER_INTERVAL_MIN             5UL
+#define CDC_NCM_TIMER_INTERVAL_MAX             (U32_MAX / NSEC_PER_USEC)
 
 #define cdc_ncm_comm_intf_is_mbim(x)  ((x)->desc.bInterfaceSubClass == USB_CDC_SUBCLASS_MBIM && \
                                       (x)->desc.bInterfaceProtocol == USB_CDC_PROTO_NONE)
@@ -107,6 +104,9 @@ struct cdc_ncm_ctx {
        spinlock_t mtx;
        atomic_t stop;
 
+       u32 timer_interval;
+       u32 max_ndp_size;
+
        u32 tx_timer_pending;
        u32 tx_curr_frame_num;
        u32 rx_max;
@@ -118,10 +118,21 @@ struct cdc_ncm_ctx {
        u16 tx_ndp_modulus;
        u16 tx_seq;
        u16 rx_seq;
-       u16 connected;
+       u16 min_tx_pkt;
+
+       /* statistics */
+       u32 tx_curr_frame_payload;
+       u32 tx_reason_ntb_full;
+       u32 tx_reason_ndp_full;
+       u32 tx_reason_timeout;
+       u32 tx_reason_max_datagram;
+       u64 tx_overhead;
+       u64 tx_ntbs;
+       u64 rx_overhead;
+       u64 rx_ntbs;
 };
 
-u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf);
+u8 cdc_ncm_select_altsetting(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);
index 4195b97a3def6a84574519da4a6fe37b7e954406..de429d1f4357a89d2a6a9506611a93a6c201fdc6 100644 (file)
@@ -35,11 +35,23 @@ struct virtio_scsi_cmd_req {
        u8 lun[8];              /* Logical Unit Number */
        u64 tag;                /* Command identifier */
        u8 task_attr;           /* Task attribute */
-       u8 prio;
+       u8 prio;                /* SAM command priority field */
        u8 crn;
        u8 cdb[VIRTIO_SCSI_CDB_SIZE];
 } __packed;
 
+/* SCSI command request, followed by protection information */
+struct virtio_scsi_cmd_req_pi {
+       u8 lun[8];              /* Logical Unit Number */
+       u64 tag;                /* Command identifier */
+       u8 task_attr;           /* Task attribute */
+       u8 prio;                /* SAM command priority field */
+       u8 crn;
+       u32 pi_bytesout;        /* DataOUT PI Number of bytes */
+       u32 pi_bytesin;         /* DataIN PI Number of bytes */
+       u8 cdb[VIRTIO_SCSI_CDB_SIZE];
+} __packed;
+
 /* Response, followed by sense data and data-in */
 struct virtio_scsi_cmd_resp {
        u32 sense_len;          /* Sense data length */
@@ -97,6 +109,7 @@ struct virtio_scsi_config {
 #define VIRTIO_SCSI_F_INOUT                    0
 #define VIRTIO_SCSI_F_HOTPLUG                  1
 #define VIRTIO_SCSI_F_CHANGE                   2
+#define VIRTIO_SCSI_F_T10_PI                   3
 
 /* Response codes */
 #define VIRTIO_SCSI_S_OK                       0
index bca25dc53f9d204ef0773db521dbeba797705728..8fab6fa0dbfb08c7c8f272f7fa751db780a169a5 100644 (file)
@@ -432,6 +432,7 @@ void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no);
 void *vb2_plane_cookie(struct vb2_buffer *vb, unsigned int plane_no);
 
 void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state);
+void vb2_discard_done(struct vb2_queue *q);
 int vb2_wait_for_all_buffers(struct vb2_queue *q);
 
 int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b);
index f7d372b7d4ff672d79a6ce16c437e016449cba4e..79b530fb2c4dd40205595d73831846e5ee56c7ac 100644 (file)
@@ -54,6 +54,7 @@
 #define __6LOWPAN_H__
 
 #include <net/ipv6.h>
+#include <net/net_namespace.h>
 
 #define UIP_802154_SHORTADDR_LEN       2  /* compressed ipv6 address length */
 #define UIP_IPH_LEN                    40 /* ipv6 fixed header size */
index 933a9f22a05ff63abb4379f94cb907c95f6beac4..f679877bb6017dd4a6da7d1fe0e6ea217ba3b3e4 100644 (file)
@@ -306,11 +306,6 @@ static inline void addrconf_addr_solict_mult(const struct in6_addr *addr,
                      htonl(0xFF000000) | addr->s6_addr32[3]);
 }
 
-static inline bool ipv6_addr_is_multicast(const struct in6_addr *addr)
-{
-       return (addr->s6_addr32[0] & htonl(0xFF000000)) == htonl(0xFF000000);
-}
-
 static inline bool ipv6_addr_is_ll_all_nodes(const struct in6_addr *addr)
 {
 #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64
index f79ae2aa76d6a45fb9a1e452d53f82d05e101717..085940f7eeec0e56d5f95226034b32b906bc3380 100644 (file)
@@ -57,6 +57,14 @@ struct sockaddr_ieee802154 {
 /* get/setsockopt */
 #define SOL_IEEE802154 0
 
-#define WPAN_WANTACK   0
+#define WPAN_WANTACK           0
+#define WPAN_SECURITY          1
+#define WPAN_SECURITY_LEVEL    2
+
+#define WPAN_SECURITY_DEFAULT  0
+#define WPAN_SECURITY_OFF      1
+#define WPAN_SECURITY_ON       2
+
+#define WPAN_SECURITY_LEVEL_DEFAULT    (-1)
 
 #endif
index be150cf8cd432298d47860268a1d4566af74481d..16587dcd6a9181b74814f1a4d0865332aa1bd81d 100644 (file)
@@ -367,6 +367,7 @@ enum {
 #define HCI_ERROR_REMOTE_POWER_OFF     0x15
 #define HCI_ERROR_LOCAL_HOST_TERM      0x16
 #define HCI_ERROR_PAIRING_NOT_ALLOWED  0x18
+#define HCI_ERROR_ADVERTISING_TIMEOUT  0x3c
 
 /* Flow control modes */
 #define HCI_FLOW_CTL_MODE_PACKET_BASED 0x00
@@ -1053,6 +1054,17 @@ struct hci_cp_write_page_scan_activity {
        __le16   window;
 } __packed;
 
+#define HCI_OP_READ_TX_POWER           0x0c2d
+struct hci_cp_read_tx_power {
+       __le16   handle;
+       __u8     type;
+} __packed;
+struct hci_rp_read_tx_power {
+       __u8     status;
+       __le16   handle;
+       __s8     tx_power;
+} __packed;
+
 #define HCI_OP_READ_PAGE_SCAN_TYPE     0x0c46
 struct hci_rp_read_page_scan_type {
        __u8     status;
@@ -1063,6 +1075,16 @@ struct hci_rp_read_page_scan_type {
        #define PAGE_SCAN_TYPE_STANDARD         0x00
        #define PAGE_SCAN_TYPE_INTERLACED       0x01
 
+#define HCI_OP_READ_RSSI               0x1405
+struct hci_cp_read_rssi {
+       __le16   handle;
+} __packed;
+struct hci_rp_read_rssi {
+       __u8     status;
+       __le16   handle;
+       __s8     rssi;
+} __packed;
+
 #define HCI_OP_READ_LOCAL_AMP_INFO     0x1409
 struct hci_rp_read_local_amp_info {
        __u8     status;
index 5f8bc05694ac665159bb25f7a9eaa704c3784e91..b386bf17e6c2c10808c8306ad7cbb4bb42e4713d 100644 (file)
@@ -68,6 +68,11 @@ struct discovery_state {
        struct list_head        unknown;        /* Name state not known */
        struct list_head        resolve;        /* Name needs to be resolved */
        __u32                   timestamp;
+       bdaddr_t                last_adv_addr;
+       u8                      last_adv_addr_type;
+       s8                      last_adv_rssi;
+       u8                      last_adv_data[HCI_MAX_AD_LENGTH];
+       u8                      last_adv_data_len;
 };
 
 struct hci_conn_hash {
@@ -140,6 +145,10 @@ struct oob_data {
 /* Default LE RPA expiry time, 15 minutes */
 #define HCI_DEFAULT_RPA_TIMEOUT                (15 * 60)
 
+/* Default min/max age of connection information (1s/3s) */
+#define DEFAULT_CONN_INFO_MIN_AGE      1000
+#define DEFAULT_CONN_INFO_MAX_AGE      3000
+
 struct amp_assoc {
        __u16   len;
        __u16   offset;
@@ -194,6 +203,9 @@ struct hci_dev {
        __u16           le_scan_window;
        __u16           le_conn_min_interval;
        __u16           le_conn_max_interval;
+       __u16           discov_interleaved_timeout;
+       __u16           conn_info_min_age;
+       __u16           conn_info_max_age;
        __u8            ssp_debug_mode;
 
        __u16           devid_source;
@@ -368,8 +380,13 @@ struct hci_conn {
        __u16           setting;
        __u16           le_conn_min_interval;
        __u16           le_conn_max_interval;
+       __s8            rssi;
+       __s8            tx_power;
+       __s8            max_tx_power;
        unsigned long   flags;
 
+       unsigned long   conn_info_timestamp;
+
        __u8            remote_cap;
        __u8            remote_auth;
        __u8            remote_id;
@@ -1204,8 +1221,8 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event);
  */
 #define DISCOV_LE_SCAN_WIN             0x12
 #define DISCOV_LE_SCAN_INT             0x12
-#define DISCOV_LE_TIMEOUT              msecs_to_jiffies(10240)
-#define DISCOV_INTERLEAVED_TIMEOUT     msecs_to_jiffies(5120)
+#define DISCOV_LE_TIMEOUT              10240   /* msec */
+#define DISCOV_INTERLEAVED_TIMEOUT     5120    /* msec */
 #define DISCOV_INTERLEAVED_INQUIRY_LEN 0x04
 #define DISCOV_BREDR_INQUIRY_LEN       0x08
 
@@ -1265,7 +1282,8 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
                                       u8 *randomizer256, 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);
+                      u8 ssp, u8 *eir, u16 eir_len, u8 *scan_rsp,
+                      u8 scan_rsp_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);
index d4b571c2f9fd33b65ec95dbf9ca6b691f1567b61..bcffc9ae0c89bec0bd3237d708e76d0911abfa36 100644 (file)
@@ -181,6 +181,9 @@ struct mgmt_cp_load_link_keys {
 } __packed;
 #define MGMT_LOAD_LINK_KEYS_SIZE       3
 
+#define MGMT_LTK_UNAUTHENTICATED       0x00
+#define MGMT_LTK_AUTHENTICATED         0x01
+
 struct mgmt_ltk_info {
        struct mgmt_addr_info addr;
        __u8    type;
@@ -409,6 +412,18 @@ struct mgmt_cp_load_irks {
 } __packed;
 #define MGMT_LOAD_IRKS_SIZE            2
 
+#define MGMT_OP_GET_CONN_INFO          0x0031
+struct mgmt_cp_get_conn_info {
+       struct mgmt_addr_info addr;
+} __packed;
+#define MGMT_GET_CONN_INFO_SIZE                MGMT_ADDR_INFO_SIZE
+struct mgmt_rp_get_conn_info {
+       struct mgmt_addr_info addr;
+       __s8    rssi;
+       __s8    tx_power;
+       __s8    max_tx_power;
+} __packed;
+
 #define MGMT_EV_CMD_COMPLETE           0x0001
 struct mgmt_ev_cmd_complete {
        __le16  opcode;
index 2611cc389d7d65d03cf298061bf3e4ed32c304cd..578b83127af18545e51c26b859dfa6f80100c263 100644 (file)
@@ -173,7 +173,7 @@ struct rfcomm_dlc {
        struct sk_buff_head   tx_queue;
        struct timer_list     timer;
 
-       spinlock_t    lock;
+       struct mutex  lock;
        unsigned long state;
        unsigned long flags;
        atomic_t      refcnt;
@@ -244,8 +244,8 @@ int  rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig);
 void rfcomm_dlc_accept(struct rfcomm_dlc *d);
 struct rfcomm_dlc *rfcomm_dlc_exists(bdaddr_t *src, bdaddr_t *dst, u8 channel);
 
-#define rfcomm_dlc_lock(d)     spin_lock(&d->lock)
-#define rfcomm_dlc_unlock(d)   spin_unlock(&d->lock)
+#define rfcomm_dlc_lock(d)     mutex_lock(&d->lock)
+#define rfcomm_dlc_unlock(d)   mutex_unlock(&d->lock)
 
 static inline void rfcomm_dlc_hold(struct rfcomm_dlc *d)
 {
index f856e5a746fae66e2c91357cfe94fc89d0acae18..e46c437944f73e66cb275f4adc345a1f869923c8 100644 (file)
@@ -109,6 +109,13 @@ enum ieee80211_band {
  *     channel as the control or any of the secondary channels.
  *     This may be due to the driver or due to regulatory bandwidth
  *     restrictions.
+ * @IEEE80211_CHAN_INDOOR_ONLY: see %NL80211_FREQUENCY_ATTR_INDOOR_ONLY
+ * @IEEE80211_CHAN_GO_CONCURRENT: see %NL80211_FREQUENCY_ATTR_GO_CONCURRENT
+ * @IEEE80211_CHAN_NO_20MHZ: 20 MHz bandwidth is not permitted
+ *     on this channel.
+ * @IEEE80211_CHAN_NO_10MHZ: 10 MHz bandwidth is not permitted
+ *     on this channel.
+ *
  */
 enum ieee80211_channel_flags {
        IEEE80211_CHAN_DISABLED         = 1<<0,
@@ -120,6 +127,10 @@ enum ieee80211_channel_flags {
        IEEE80211_CHAN_NO_OFDM          = 1<<6,
        IEEE80211_CHAN_NO_80MHZ         = 1<<7,
        IEEE80211_CHAN_NO_160MHZ        = 1<<8,
+       IEEE80211_CHAN_INDOOR_ONLY      = 1<<9,
+       IEEE80211_CHAN_GO_CONCURRENT    = 1<<10,
+       IEEE80211_CHAN_NO_20MHZ         = 1<<11,
+       IEEE80211_CHAN_NO_10MHZ         = 1<<12,
 };
 
 #define IEEE80211_CHAN_NO_HT40 \
@@ -330,8 +341,8 @@ struct vif_params {
  * @seq_len: length of @seq.
  */
 struct key_params {
-       u8 *key;
-       u8 *seq;
+       const u8 *key;
+       const u8 *seq;
        int key_len;
        int seq_len;
        u32 cipher;
@@ -441,10 +452,13 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
  * 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
+ * @iftype: the interface type as specified in &enum nl80211_iftype
+ * Returns:
+ *     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);
+                                 const struct cfg80211_chan_def *chandef,
+                                 enum nl80211_iftype iftype);
 
 /**
  * ieee80211_chandef_rate_flags - returns rate flags for a channel
@@ -654,7 +668,6 @@ struct cfg80211_acl_data {
  * @p2p_opp_ps: P2P opportunistic PS
  * @acl: ACL configuration used by the drivers which has support for
  *     MAC address based access control
- * @radar_required: set if radar detection is required
  */
 struct cfg80211_ap_settings {
        struct cfg80211_chan_def chandef;
@@ -672,7 +685,6 @@ struct cfg80211_ap_settings {
        u8 p2p_ctwindow;
        bool p2p_opp_ps;
        const struct cfg80211_acl_data *acl;
-       bool radar_required;
 };
 
 /**
@@ -682,8 +694,10 @@ struct cfg80211_ap_settings {
  *
  * @chandef: defines the channel to use after the switch
  * @beacon_csa: beacon data while performing the switch
- * @counter_offset_beacon: offset for the counter within the beacon (tail)
- * @counter_offset_presp: offset for the counter within the probe response
+ * @counter_offsets_beacon: offsets of the counters within the beacon (tail)
+ * @counter_offsets_presp: offsets of the counters within the probe response
+ * @n_counter_offsets_beacon: number of csa counters the beacon (tail)
+ * @n_counter_offsets_presp: number of csa counters in the probe response
  * @beacon_after: beacon data to be used on the new channel
  * @radar_required: whether radar detection is required on the new channel
  * @block_tx: whether transmissions should be blocked while changing
@@ -692,7 +706,10 @@ struct cfg80211_ap_settings {
 struct cfg80211_csa_settings {
        struct cfg80211_chan_def chandef;
        struct cfg80211_beacon_data beacon_csa;
-       u16 counter_offset_beacon, counter_offset_presp;
+       const u16 *counter_offsets_beacon;
+       const u16 *counter_offsets_presp;
+       unsigned int n_counter_offsets_beacon;
+       unsigned int n_counter_offsets_presp;
        struct cfg80211_beacon_data beacon_after;
        bool radar_required;
        bool block_tx;
@@ -856,36 +873,38 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
  * @STATION_INFO_NONPEER_PM: @nonpeer_pm filled
  * @STATION_INFO_CHAIN_SIGNAL: @chain_signal filled
  * @STATION_INFO_CHAIN_SIGNAL_AVG: @chain_signal_avg filled
+ * @STATION_INFO_EXPECTED_THROUGHPUT: @expected_throughput filled
  */
 enum station_info_flags {
-       STATION_INFO_INACTIVE_TIME      = 1<<0,
-       STATION_INFO_RX_BYTES           = 1<<1,
-       STATION_INFO_TX_BYTES           = 1<<2,
-       STATION_INFO_LLID               = 1<<3,
-       STATION_INFO_PLID               = 1<<4,
-       STATION_INFO_PLINK_STATE        = 1<<5,
-       STATION_INFO_SIGNAL             = 1<<6,
-       STATION_INFO_TX_BITRATE         = 1<<7,
-       STATION_INFO_RX_PACKETS         = 1<<8,
-       STATION_INFO_TX_PACKETS         = 1<<9,
-       STATION_INFO_TX_RETRIES         = 1<<10,
-       STATION_INFO_TX_FAILED          = 1<<11,
-       STATION_INFO_RX_DROP_MISC       = 1<<12,
-       STATION_INFO_SIGNAL_AVG         = 1<<13,
-       STATION_INFO_RX_BITRATE         = 1<<14,
-       STATION_INFO_BSS_PARAM          = 1<<15,
-       STATION_INFO_CONNECTED_TIME     = 1<<16,
-       STATION_INFO_ASSOC_REQ_IES      = 1<<17,
-       STATION_INFO_STA_FLAGS          = 1<<18,
-       STATION_INFO_BEACON_LOSS_COUNT  = 1<<19,
-       STATION_INFO_T_OFFSET           = 1<<20,
-       STATION_INFO_LOCAL_PM           = 1<<21,
-       STATION_INFO_PEER_PM            = 1<<22,
-       STATION_INFO_NONPEER_PM         = 1<<23,
-       STATION_INFO_RX_BYTES64         = 1<<24,
-       STATION_INFO_TX_BYTES64         = 1<<25,
-       STATION_INFO_CHAIN_SIGNAL       = 1<<26,
-       STATION_INFO_CHAIN_SIGNAL_AVG   = 1<<27,
+       STATION_INFO_INACTIVE_TIME              = BIT(0),
+       STATION_INFO_RX_BYTES                   = BIT(1),
+       STATION_INFO_TX_BYTES                   = BIT(2),
+       STATION_INFO_LLID                       = BIT(3),
+       STATION_INFO_PLID                       = BIT(4),
+       STATION_INFO_PLINK_STATE                = BIT(5),
+       STATION_INFO_SIGNAL                     = BIT(6),
+       STATION_INFO_TX_BITRATE                 = BIT(7),
+       STATION_INFO_RX_PACKETS                 = BIT(8),
+       STATION_INFO_TX_PACKETS                 = BIT(9),
+       STATION_INFO_TX_RETRIES                 = BIT(10),
+       STATION_INFO_TX_FAILED                  = BIT(11),
+       STATION_INFO_RX_DROP_MISC               = BIT(12),
+       STATION_INFO_SIGNAL_AVG                 = BIT(13),
+       STATION_INFO_RX_BITRATE                 = BIT(14),
+       STATION_INFO_BSS_PARAM                  = BIT(15),
+       STATION_INFO_CONNECTED_TIME             = BIT(16),
+       STATION_INFO_ASSOC_REQ_IES              = BIT(17),
+       STATION_INFO_STA_FLAGS                  = BIT(18),
+       STATION_INFO_BEACON_LOSS_COUNT          = BIT(19),
+       STATION_INFO_T_OFFSET                   = BIT(20),
+       STATION_INFO_LOCAL_PM                   = BIT(21),
+       STATION_INFO_PEER_PM                    = BIT(22),
+       STATION_INFO_NONPEER_PM                 = BIT(23),
+       STATION_INFO_RX_BYTES64                 = BIT(24),
+       STATION_INFO_TX_BYTES64                 = BIT(25),
+       STATION_INFO_CHAIN_SIGNAL               = BIT(26),
+       STATION_INFO_CHAIN_SIGNAL_AVG           = BIT(27),
+       STATION_INFO_EXPECTED_THROUGHPUT        = BIT(28),
 };
 
 /**
@@ -1007,6 +1026,8 @@ struct sta_bss_parameters {
  * @local_pm: local mesh STA power save mode
  * @peer_pm: peer mesh STA power save mode
  * @nonpeer_pm: non-peer mesh STA power save mode
+ * @expected_throughput: expected throughput in kbps (including 802.11 headers)
+ *     towards this station.
  */
 struct station_info {
        u32 filled;
@@ -1045,12 +1066,27 @@ struct station_info {
        enum nl80211_mesh_power_mode peer_pm;
        enum nl80211_mesh_power_mode nonpeer_pm;
 
+       u32 expected_throughput;
+
        /*
         * Note: Add a new enum station_info_flags value for each new field and
         * use it to check which fields are initialized.
         */
 };
 
+/**
+ * cfg80211_get_station - retrieve information about a given station
+ * @dev: the device where the station is supposed to be connected to
+ * @mac_addr: the mac address of the station of interest
+ * @sinfo: pointer to the structure to fill with the information
+ *
+ * Returns 0 on success and sinfo is filled with the available information
+ * otherwise returns a negative error code and the content of sinfo has to be
+ * considered undefined.
+ */
+int cfg80211_get_station(struct net_device *dev, const u8 *mac_addr,
+                        struct station_info *sinfo);
+
 /**
  * enum monitor_flags - monitor flags
  *
@@ -1152,7 +1188,7 @@ struct bss_parameters {
        int use_cts_prot;
        int use_short_preamble;
        int use_short_slot_time;
-       u8 *basic_rates;
+       const u8 *basic_rates;
        u8 basic_rates_len;
        int ap_isolate;
        int ht_opmode;
@@ -1682,10 +1718,10 @@ struct cfg80211_disassoc_request {
  * @ht_capa_mask:  The bits of ht_capa which are to be used.
  */
 struct cfg80211_ibss_params {
-       u8 *ssid;
-       u8 *bssid;
+       const u8 *ssid;
+       const u8 *bssid;
        struct cfg80211_chan_def chandef;
-       u8 *ie;
+       const u8 *ie;
        u8 ssid_len, ie_len;
        u16 beacon_interval;
        u32 basic_rates;
@@ -1794,8 +1830,8 @@ struct cfg80211_bitrate_mask {
  * @pmkid: The PMK material itself.
  */
 struct cfg80211_pmksa {
-       u8 *bssid;
-       u8 *pmkid;
+       const u8 *bssid;
+       const u8 *pmkid;
 };
 
 /**
@@ -1810,7 +1846,7 @@ struct cfg80211_pmksa {
  * memory, free @mask only!
  */
 struct cfg80211_pkt_pattern {
-       u8 *mask, *pattern;
+       const u8 *mask, *pattern;
        int pattern_len;
        int pkt_offset;
 };
@@ -1974,6 +2010,8 @@ struct cfg80211_update_ft_ies_params {
  * @len: buffer length
  * @no_cck: don't use cck rates for this frame
  * @dont_wait_for_ack: tells the low level not to wait for an ack
+ * @n_csa_offsets: length of csa_offsets array
+ * @csa_offsets: array of all the csa offsets in the frame
  */
 struct cfg80211_mgmt_tx_params {
        struct ieee80211_channel *chan;
@@ -1983,6 +2021,8 @@ struct cfg80211_mgmt_tx_params {
        size_t len;
        bool no_cck;
        bool dont_wait_for_ack;
+       int n_csa_offsets;
+       const u16 *csa_offsets;
 };
 
 /**
@@ -2278,6 +2318,10 @@ struct cfg80211_qos_map {
  * @channel_switch: initiate channel-switch procedure (with CSA)
  *
  * @set_qos_map: Set QoS mapping information to the driver
+ *
+ * @set_ap_chanwidth: Set the AP (including P2P GO) mode channel width for the
+ *     given interface This is used e.g. for dynamic HT 20/40 MHz channel width
+ *     changes during the lifetime of the BSS.
  */
 struct cfg80211_ops {
        int     (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -2320,28 +2364,29 @@ struct cfg80211_ops {
 
 
        int     (*add_station)(struct wiphy *wiphy, struct net_device *dev,
-                              u8 *mac, struct station_parameters *params);
+                              const u8 *mac,
+                              struct station_parameters *params);
        int     (*del_station)(struct wiphy *wiphy, struct net_device *dev,
-                              u8 *mac);
+                              const u8 *mac);
        int     (*change_station)(struct wiphy *wiphy, struct net_device *dev,
-                                 u8 *mac, struct station_parameters *params);
+                                 const u8 *mac,
+                                 struct station_parameters *params);
        int     (*get_station)(struct wiphy *wiphy, struct net_device *dev,
-                              u8 *mac, struct station_info *sinfo);
+                              const u8 *mac, struct station_info *sinfo);
        int     (*dump_station)(struct wiphy *wiphy, struct net_device *dev,
-                              int idx, u8 *mac, struct station_info *sinfo);
+                               int idx, u8 *mac, struct station_info *sinfo);
 
        int     (*add_mpath)(struct wiphy *wiphy, struct net_device *dev,
-                              u8 *dst, u8 *next_hop);
+                              const u8 *dst, const u8 *next_hop);
        int     (*del_mpath)(struct wiphy *wiphy, struct net_device *dev,
-                              u8 *dst);
+                              const u8 *dst);
        int     (*change_mpath)(struct wiphy *wiphy, struct net_device *dev,
-                                 u8 *dst, u8 *next_hop);
+                                 const u8 *dst, const u8 *next_hop);
        int     (*get_mpath)(struct wiphy *wiphy, struct net_device *dev,
-                              u8 *dst, u8 *next_hop,
-                              struct mpath_info *pinfo);
+                            u8 *dst, u8 *next_hop, struct mpath_info *pinfo);
        int     (*dump_mpath)(struct wiphy *wiphy, struct net_device *dev,
-                              int idx, u8 *dst, u8 *next_hop,
-                              struct mpath_info *pinfo);
+                             int idx, u8 *dst, u8 *next_hop,
+                             struct mpath_info *pinfo);
        int     (*get_mesh_config)(struct wiphy *wiphy,
                                struct net_device *dev,
                                struct mesh_config *conf);
@@ -2471,11 +2516,11 @@ struct cfg80211_ops {
                                  struct cfg80211_gtk_rekey_data *data);
 
        int     (*tdls_mgmt)(struct wiphy *wiphy, struct net_device *dev,
-                            u8 *peer, u8 action_code,  u8 dialog_token,
+                            const u8 *peer, u8 action_code,  u8 dialog_token,
                             u16 status_code, u32 peer_capability,
                             const u8 *buf, size_t len);
        int     (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev,
-                            u8 *peer, enum nl80211_tdls_operation oper);
+                            const u8 *peer, enum nl80211_tdls_operation oper);
 
        int     (*probe_client)(struct wiphy *wiphy, struct net_device *dev,
                                const u8 *peer, u64 *cookie);
@@ -2521,9 +2566,13 @@ struct cfg80211_ops {
        int     (*channel_switch)(struct wiphy *wiphy,
                                  struct net_device *dev,
                                  struct cfg80211_csa_settings *params);
+
        int     (*set_qos_map)(struct wiphy *wiphy,
                               struct net_device *dev,
                               struct cfg80211_qos_map *qos_map);
+
+       int     (*set_ap_chanwidth)(struct wiphy *wiphy, struct net_device *dev,
+                                   struct cfg80211_chan_def *chandef);
 };
 
 /*
@@ -2618,6 +2667,7 @@ struct ieee80211_iface_limit {
  *     between infrastructure and AP types must match. This is required
  *     only in special cases.
  * @radar_detect_widths: bitmap of channel widths supported for radar detection
+ * @radar_detect_regions: bitmap of regions supported for radar detection
  *
  * With this structure the driver can describe which interface
  * combinations it supports concurrently.
@@ -2675,6 +2725,7 @@ struct ieee80211_iface_combination {
        u8 n_limits;
        bool beacon_int_infra_match;
        u8 radar_detect_widths;
+       u8 radar_detect_regions;
 };
 
 struct ieee80211_txrx_stypes {
@@ -2905,6 +2956,17 @@ struct wiphy_vendor_command {
  *     (including P2P GO) or 0 to indicate no such limit is advertised. The
  *     driver is allowed to advertise a theoretical limit that it can reach in
  *     some cases, but may not always reach.
+ *
+ * @max_num_csa_counters: Number of supported csa_counters in beacons
+ *     and probe responses.  This value should be set if the driver
+ *     wishes to limit the number of csa counters. Default (0) means
+ *     infinite.
+ * @max_adj_channel_rssi_comp: max offset of between the channel on which the
+ *     frame was sent and the channel on which the frame was heard for which
+ *     the reported rssi is still valid. If a driver is able to compensate the
+ *     low rssi when a frame is heard on different channel, then it should set
+ *     this variable to the maximal offset for which it can compensate.
+ *     This value should be set in MHz.
  */
 struct wiphy {
        /* assign these fields before you register the wiphy */
@@ -3022,6 +3084,9 @@ struct wiphy {
 
        u16 max_ap_assoc_sta;
 
+       u8 max_num_csa_counters;
+       u8 max_adj_channel_rssi_comp;
+
        char priv[0] __aligned(NETDEV_ALIGN);
 };
 
@@ -3194,6 +3259,7 @@ struct cfg80211_cached_keys;
  * @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
+ * @owner_nlportid: (private) owner socket port ID
  */
 struct wireless_dev {
        struct wiphy *wiphy;
@@ -3241,13 +3307,15 @@ struct wireless_dev {
        unsigned long cac_start_time;
        unsigned int cac_time_ms;
 
+       u32 owner_nlportid;
+
 #ifdef CONFIG_CFG80211_WEXT
        /* wext data */
        struct {
                struct cfg80211_ibss_params ibss;
                struct cfg80211_connect_params connect;
                struct cfg80211_cached_keys *keys;
-               u8 *ie;
+               const u8 *ie;
                size_t ie_len;
                u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
                u8 ssid[IEEE80211_MAX_SSID_LEN];
@@ -3488,7 +3556,8 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
  * Return: 0 on success, or a negative error code.
  */
 int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
-                            enum nl80211_iftype iftype, u8 *bssid, bool qos);
+                            enum nl80211_iftype iftype, const u8 *bssid,
+                            bool qos);
 
 /**
  * ieee80211_amsdu_to_8023s - decode an IEEE 802.11n A-MSDU frame
@@ -3600,7 +3669,7 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2);
  * default channel settings will be disregarded. If no rule is found for a
  * channel on the regulatory domain the channel will be disabled.
  * Drivers using this for a wiphy should also set the wiphy flag
- * WIPHY_FLAG_CUSTOM_REGULATORY or cfg80211 will set it for the wiphy
+ * REGULATORY_CUSTOM_REG or cfg80211 will set it for the wiphy
  * that called this helper.
  */
 void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
@@ -4289,7 +4358,7 @@ void cfg80211_roamed_bss(struct net_device *dev, struct cfg80211_bss *bss,
  * and not try to connect to any AP any more.
  */
 void cfg80211_disconnected(struct net_device *dev, u16 reason,
-                          u8 *ie, size_t ie_len, gfp_t gfp);
+                          const u8 *ie, size_t ie_len, gfp_t gfp);
 
 /**
  * cfg80211_ready_on_channel - notification of remain_on_channel start
@@ -4543,12 +4612,14 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy,
  * cfg80211_reg_can_beacon - check if beaconing is allowed
  * @wiphy: the wiphy
  * @chandef: the channel definition
+ * @iftype: interface type
  *
  * Return: %true if there is no secondary channel or the secondary channel(s)
  * can be used for beaconing (i.e. is not a radar channel etc.)
  */
 bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
-                            struct cfg80211_chan_def *chandef);
+                            struct cfg80211_chan_def *chandef,
+                            enum nl80211_iftype iftype);
 
 /*
  * cfg80211_ch_switch_notify - update wdev channel and notify userspace
@@ -4694,6 +4765,84 @@ void cfg80211_crit_proto_stopped(struct wireless_dev *wdev, gfp_t gfp);
  */
 unsigned int ieee80211_get_num_supported_channels(struct wiphy *wiphy);
 
+/**
+ * cfg80211_check_combinations - check interface combinations
+ *
+ * @wiphy: the wiphy
+ * @num_different_channels: the number of different channels we want
+ *     to use for verification
+ * @radar_detect: a bitmap where each bit corresponds to a channel
+ *     width where radar detection is needed, as in the definition of
+ *     &struct ieee80211_iface_combination.@radar_detect_widths
+ * @iftype_num: array with the numbers of interfaces of each interface
+ *     type.  The index is the interface type as specified in &enum
+ *     nl80211_iftype.
+ *
+ * This function can be called by the driver to check whether a
+ * combination of interfaces and their types are allowed according to
+ * the interface combinations.
+ */
+int cfg80211_check_combinations(struct wiphy *wiphy,
+                               const int num_different_channels,
+                               const u8 radar_detect,
+                               const int iftype_num[NUM_NL80211_IFTYPES]);
+
+/**
+ * cfg80211_iter_combinations - iterate over matching combinations
+ *
+ * @wiphy: the wiphy
+ * @num_different_channels: the number of different channels we want
+ *     to use for verification
+ * @radar_detect: a bitmap where each bit corresponds to a channel
+ *     width where radar detection is needed, as in the definition of
+ *     &struct ieee80211_iface_combination.@radar_detect_widths
+ * @iftype_num: array with the numbers of interfaces of each interface
+ *     type.  The index is the interface type as specified in &enum
+ *     nl80211_iftype.
+ * @iter: function to call for each matching combination
+ * @data: pointer to pass to iter function
+ *
+ * This function can be called by the driver to check what possible
+ * combinations it fits in at a given moment, e.g. for channel switching
+ * purposes.
+ */
+int cfg80211_iter_combinations(struct wiphy *wiphy,
+                              const int num_different_channels,
+                              const u8 radar_detect,
+                              const int iftype_num[NUM_NL80211_IFTYPES],
+                              void (*iter)(const struct ieee80211_iface_combination *c,
+                                           void *data),
+                              void *data);
+
+/*
+ * cfg80211_stop_iface - trigger interface disconnection
+ *
+ * @wiphy: the wiphy
+ * @wdev: wireless device
+ * @gfp: context flags
+ *
+ * Trigger interface to be stopped as if AP was stopped, IBSS/mesh left, STA
+ * disconnected.
+ *
+ * Note: This doesn't need any locks and is asynchronous.
+ */
+void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev,
+                        gfp_t gfp);
+
+/**
+ * cfg80211_shutdown_all_interfaces - shut down all interfaces for a wiphy
+ * @wiphy: the wiphy to shut down
+ *
+ * This function shuts down all interfaces belonging to this wiphy by
+ * calling dev_close() (and treating non-netdev interfaces as needed).
+ * It shouldn't really be used unless there are some fatal device errors
+ * that really can't be recovered in any other way.
+ *
+ * Callers must hold the RTNL and be able to deal with callbacks into
+ * the driver while the function is running.
+ */
+void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy);
+
 /* Logging, debugging and troubleshooting/diagnostic helpers. */
 
 /* wiphy_printk helpers, similar to dev_printk */
index a28f4e0f625193b0682932207a46578f094f52a9..87cb1903640d63ccfed890c957294af46a93cf24 100644 (file)
@@ -57,12 +57,14 @@ static __inline__ __wsum csum_and_copy_to_user
 }
 #endif
 
+#ifndef HAVE_ARCH_CSUM_ADD
 static inline __wsum csum_add(__wsum csum, __wsum addend)
 {
        u32 res = (__force u32)csum;
        res += (__force u32)addend;
        return (__force __wsum)(res + (res < (__force u32)addend));
 }
+#endif
 
 static inline __wsum csum_sub(__wsum csum, __wsum addend)
 {
index 7828ebf99ee132241b76e500681a43188143da99..6efce384451e56f16d8aab0847a6a7be4e94e0a0 100644 (file)
@@ -181,6 +181,11 @@ struct dsa_switch_driver {
 void register_switch_driver(struct dsa_switch_driver *type);
 void unregister_switch_driver(struct dsa_switch_driver *type);
 
+static inline void *ds_to_priv(struct dsa_switch *ds)
+{
+       return (void *)(ds + 1);
+}
+
 /*
  * The original DSA tag format and some other tag formats have no
  * ethertype, which means that we need to add a little hack to the
index 70046a0b0b89c13d0f71f6180ed64e7dacf869e6..b53182018743f4383e525c43f30fc25bd5b4f5a8 100644 (file)
@@ -37,9 +37,10 @@ void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
                      int hdr_len);
 
 static inline struct sk_buff *gre_handle_offloads(struct sk_buff *skb,
-                                                 bool gre_csum)
+                                                 bool csum)
 {
-       return iptunnel_handle_offloads(skb, gre_csum, SKB_GSO_GRE);
+       return iptunnel_handle_offloads(skb, csum,
+                                       csum ? SKB_GSO_GRE_CSUM : SKB_GSO_GRE);
 }
 
 
index c7ae0ac528dc1e5e1d3c2d5456c2d0e5221b6933..0aa7122e8f15390b4b6158429a245057fdd23e5d 100644 (file)
 #define IEEE802154_SCF_KEY_SHORT_INDEX         2
 #define IEEE802154_SCF_KEY_HW_INDEX            3
 
+#define IEEE802154_SCF_SECLEVEL_NONE           0
+#define IEEE802154_SCF_SECLEVEL_MIC32          1
+#define IEEE802154_SCF_SECLEVEL_MIC64          2
+#define IEEE802154_SCF_SECLEVEL_MIC128         3
+#define IEEE802154_SCF_SECLEVEL_ENC            4
+#define IEEE802154_SCF_SECLEVEL_ENC_MIC32      5
+#define IEEE802154_SCF_SECLEVEL_ENC_MIC64      6
+#define IEEE802154_SCF_SECLEVEL_ENC_MIC128     7
+
 /* MAC footer size */
 #define IEEE802154_MFR_SIZE    2 /* 2 octets */
 
index 5a719ca892f41c24eb45e849a64327cd6221d407..3b53c8e405e48143f667c20850db1666c402d8fa 100644 (file)
@@ -27,6 +27,7 @@
 #ifndef IEEE802154_NETDEVICE_H
 #define IEEE802154_NETDEVICE_H
 
+#include <net/ieee802154.h>
 #include <net/af_ieee802154.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
@@ -114,6 +115,34 @@ int ieee802154_hdr_pull(struct sk_buff *skb, struct ieee802154_hdr *hdr);
 int ieee802154_hdr_peek_addrs(const struct sk_buff *skb,
                              struct ieee802154_hdr *hdr);
 
+/* parses the full 802.15.4 header a given skb and stores them into hdr,
+ * performing pan id decompression and length checks to be suitable for use in
+ * header_ops.parse
+ */
+int ieee802154_hdr_peek(const struct sk_buff *skb, struct ieee802154_hdr *hdr);
+
+int ieee802154_max_payload(const struct ieee802154_hdr *hdr);
+
+static inline int
+ieee802154_sechdr_authtag_len(const struct ieee802154_sechdr *sec)
+{
+       switch (sec->level) {
+       case IEEE802154_SCF_SECLEVEL_MIC32:
+       case IEEE802154_SCF_SECLEVEL_ENC_MIC32:
+               return 4;
+       case IEEE802154_SCF_SECLEVEL_MIC64:
+       case IEEE802154_SCF_SECLEVEL_ENC_MIC64:
+               return 8;
+       case IEEE802154_SCF_SECLEVEL_MIC128:
+       case IEEE802154_SCF_SECLEVEL_ENC_MIC128:
+               return 16;
+       case IEEE802154_SCF_SECLEVEL_NONE:
+       case IEEE802154_SCF_SECLEVEL_ENC:
+       default:
+               return 0;
+       }
+}
+
 static inline int ieee802154_hdr_length(struct sk_buff *skb)
 {
        struct ieee802154_hdr hdr;
@@ -193,8 +222,12 @@ static inline void ieee802154_addr_to_sa(struct ieee802154_addr_sa *sa,
  */
 struct ieee802154_mac_cb {
        u8 lqi;
-       u8 flags;
-       u8 seq;
+       u8 type;
+       bool ackreq;
+       bool secen;
+       bool secen_override;
+       u8 seclevel;
+       bool seclevel_override;
        struct ieee802154_addr source;
        struct ieee802154_addr dest;
 };
@@ -204,25 +237,96 @@ static inline struct ieee802154_mac_cb *mac_cb(struct sk_buff *skb)
        return (struct ieee802154_mac_cb *)skb->cb;
 }
 
-#define MAC_CB_FLAG_TYPEMASK           ((1 << 3) - 1)
-
-#define MAC_CB_FLAG_ACKREQ             (1 << 3)
-#define MAC_CB_FLAG_SECEN              (1 << 4)
-
-static inline bool mac_cb_is_ackreq(struct sk_buff *skb)
+static inline struct ieee802154_mac_cb *mac_cb_init(struct sk_buff *skb)
 {
-       return mac_cb(skb)->flags & MAC_CB_FLAG_ACKREQ;
-}
+       BUILD_BUG_ON(sizeof(struct ieee802154_mac_cb) > sizeof(skb->cb));
 
-static inline bool mac_cb_is_secen(struct sk_buff *skb)
-{
-       return mac_cb(skb)->flags & MAC_CB_FLAG_SECEN;
+       memset(skb->cb, 0, sizeof(struct ieee802154_mac_cb));
+       return mac_cb(skb);
 }
 
-static inline int mac_cb_type(struct sk_buff *skb)
-{
-       return mac_cb(skb)->flags & MAC_CB_FLAG_TYPEMASK;
-}
+#define IEEE802154_LLSEC_KEY_SIZE 16
+
+struct ieee802154_llsec_key_id {
+       u8 mode;
+       u8 id;
+       union {
+               struct ieee802154_addr device_addr;
+               __le32 short_source;
+               __le64 extended_source;
+       };
+};
+
+struct ieee802154_llsec_key {
+       u8 frame_types;
+       u32 cmd_frame_ids;
+       u8 key[IEEE802154_LLSEC_KEY_SIZE];
+};
+
+struct ieee802154_llsec_key_entry {
+       struct list_head list;
+
+       struct ieee802154_llsec_key_id id;
+       struct ieee802154_llsec_key *key;
+};
+
+struct ieee802154_llsec_device_key {
+       struct list_head list;
+
+       struct ieee802154_llsec_key_id key_id;
+       u32 frame_counter;
+};
+
+enum {
+       IEEE802154_LLSEC_DEVKEY_IGNORE,
+       IEEE802154_LLSEC_DEVKEY_RESTRICT,
+       IEEE802154_LLSEC_DEVKEY_RECORD,
+
+       __IEEE802154_LLSEC_DEVKEY_MAX,
+};
+
+struct ieee802154_llsec_device {
+       struct list_head list;
+
+       __le16 pan_id;
+       __le16 short_addr;
+       __le64 hwaddr;
+       u32 frame_counter;
+       bool seclevel_exempt;
+
+       u8 key_mode;
+       struct list_head keys;
+};
+
+struct ieee802154_llsec_seclevel {
+       struct list_head list;
+
+       u8 frame_type;
+       u8 cmd_frame_id;
+       bool device_override;
+       u32 sec_levels;
+};
+
+struct ieee802154_llsec_params {
+       bool enabled;
+
+       __be32 frame_counter;
+       u8 out_level;
+       struct ieee802154_llsec_key_id out_key;
+
+       __le64 default_key_source;
+
+       __le16 pan_id;
+       __le64 hwaddr;
+       __le64 coord_hwaddr;
+       __le16 coord_shortaddr;
+};
+
+struct ieee802154_llsec_table {
+       struct list_head keys;
+       struct list_head devices;
+       struct list_head security_levels;
+};
 
 #define IEEE802154_MAC_SCAN_ED         0
 #define IEEE802154_MAC_SCAN_ACTIVE     1
@@ -242,6 +346,53 @@ struct ieee802154_mac_params {
 };
 
 struct wpan_phy;
+
+enum {
+       IEEE802154_LLSEC_PARAM_ENABLED = 1 << 0,
+       IEEE802154_LLSEC_PARAM_FRAME_COUNTER = 1 << 1,
+       IEEE802154_LLSEC_PARAM_OUT_LEVEL = 1 << 2,
+       IEEE802154_LLSEC_PARAM_OUT_KEY = 1 << 3,
+       IEEE802154_LLSEC_PARAM_KEY_SOURCE = 1 << 4,
+       IEEE802154_LLSEC_PARAM_PAN_ID = 1 << 5,
+       IEEE802154_LLSEC_PARAM_HWADDR = 1 << 6,
+       IEEE802154_LLSEC_PARAM_COORD_HWADDR = 1 << 7,
+       IEEE802154_LLSEC_PARAM_COORD_SHORTADDR = 1 << 8,
+};
+
+struct ieee802154_llsec_ops {
+       int (*get_params)(struct net_device *dev,
+                         struct ieee802154_llsec_params *params);
+       int (*set_params)(struct net_device *dev,
+                         const struct ieee802154_llsec_params *params,
+                         int changed);
+
+       int (*add_key)(struct net_device *dev,
+                      const struct ieee802154_llsec_key_id *id,
+                      const struct ieee802154_llsec_key *key);
+       int (*del_key)(struct net_device *dev,
+                      const struct ieee802154_llsec_key_id *id);
+
+       int (*add_dev)(struct net_device *dev,
+                      const struct ieee802154_llsec_device *llsec_dev);
+       int (*del_dev)(struct net_device *dev, __le64 dev_addr);
+
+       int (*add_devkey)(struct net_device *dev,
+                         __le64 device_addr,
+                         const struct ieee802154_llsec_device_key *key);
+       int (*del_devkey)(struct net_device *dev,
+                         __le64 device_addr,
+                         const struct ieee802154_llsec_device_key *key);
+
+       int (*add_seclevel)(struct net_device *dev,
+                           const struct ieee802154_llsec_seclevel *sl);
+       int (*del_seclevel)(struct net_device *dev,
+                           const struct ieee802154_llsec_seclevel *sl);
+
+       void (*lock_table)(struct net_device *dev);
+       void (*get_table)(struct net_device *dev,
+                         struct ieee802154_llsec_table **t);
+       void (*unlock_table)(struct net_device *dev);
+};
 /*
  * This should be located at net_device->ml_priv
  *
@@ -272,6 +423,8 @@ struct ieee802154_mlme_ops {
        void (*get_mac_params)(struct net_device *dev,
                               struct ieee802154_mac_params *params);
 
+       struct ieee802154_llsec_ops *llsec;
+
        /* The fields below are required. */
 
        struct wpan_phy *(*get_phy)(const struct net_device *dev);
index 3bd22795c3e259e1f1f55176c808c6fdcc994600..84b20835b736c53b55a19eac0bbe187e65626fec 100644 (file)
@@ -150,7 +150,7 @@ static inline int INET_ECN_set_ce(struct sk_buff *skb)
 }
 
 /*
- * RFC 6080 4.2
+ * RFC 6040 4.2
  *  To decapsulate the inner header at the tunnel egress, a compliant
  *  tunnel egress MUST set the outgoing ECN field to the codepoint at the
  *  intersection of the appropriate arriving inner header (row) and outer
index 1bdb47715def0e21496ae89a59d3d9bd5f1f2c81..dd1950a7e2730e0024f1c82fac8ab20f9b533fe5 100644 (file)
@@ -292,12 +292,12 @@ static inline struct sock *inet_lookup_listener(struct net *net,
 #define INET_ADDR_COOKIE(__name, __saddr, __daddr) \
        const __addrpair __name = (__force __addrpair) ( \
                                   (((__force __u64)(__be32)(__saddr)) << 32) | \
-                                  ((__force __u64)(__be32)(__daddr)));
+                                  ((__force __u64)(__be32)(__daddr)))
 #else /* __LITTLE_ENDIAN */
 #define INET_ADDR_COOKIE(__name, __saddr, __daddr) \
        const __addrpair __name = (__force __addrpair) ( \
                                   (((__force __u64)(__be32)(__daddr)) << 32) | \
-                                  ((__force __u64)(__be32)(__saddr)));
+                                  ((__force __u64)(__be32)(__saddr)))
 #endif /* __BIG_ENDIAN */
 #define INET_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif)    \
        (((__sk)->sk_portpair == (__ports))                     &&      \
@@ -306,7 +306,9 @@ static inline struct sock *inet_lookup_listener(struct net *net,
           ((__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_ADDR_COOKIE(__name, __saddr, __daddr) \
+       const int __name __deprecated __attribute__((unused))
+
 #define INET_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif) \
        (((__sk)->sk_portpair == (__ports))             &&              \
         ((__sk)->sk_daddr      == (__saddr))           &&              \
index 1833c3f389ee64a0c6b3862d4f2fbc6db0984b0a..b1edf17bec01130f9751747c4d092e5de50aaeac 100644 (file)
@@ -90,6 +90,7 @@ struct inet_request_sock {
        kmemcheck_bitfield_end(flags);
        struct ip_options_rcu   *opt;
        struct sk_buff          *pktopts;
+       u32                     ir_mark;
 };
 
 static inline struct inet_request_sock *inet_rsk(const struct request_sock *sk)
@@ -97,6 +98,15 @@ static inline struct inet_request_sock *inet_rsk(const struct request_sock *sk)
        return (struct inet_request_sock *)sk;
 }
 
+static inline u32 inet_request_mark(struct sock *sk, struct sk_buff *skb)
+{
+       if (!sk->sk_mark && sock_net(sk)->ipv4.sysctl_tcp_fwmark_accept) {
+               return skb->mark;
+       } else {
+               return sk->sk_mark;
+       }
+}
+
 struct inet_cork {
        unsigned int            flags;
        __be32                  addr;
index 058271bde27a52b59dbc1b495433eec8a63cf18e..01d590ee5e7eb5250eaef9e0880153c257b706bd 100644 (file)
@@ -41,14 +41,13 @@ struct inet_peer {
                struct rcu_head     gc_rcu;
        };
        /*
-        * Once inet_peer is queued for deletion (refcnt == -1), following fields
-        * are not available: rid, ip_id_count
+        * Once inet_peer is queued for deletion (refcnt == -1), following field
+        * is not available: rid
         * We can share memory with rcu_head to help keep inet_peer small.
         */
        union {
                struct {
                        atomic_t                        rid;            /* Frag reception counter */
-                       atomic_t                        ip_id_count;    /* IP ID for the next packet */
                };
                struct rcu_head         rcu;
                struct inet_peer        *gc_next;
@@ -165,21 +164,11 @@ bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout);
 void inetpeer_invalidate_tree(struct inet_peer_base *);
 
 /*
- * temporary check to make sure we dont access rid, ip_id_count, tcp_ts,
+ * temporary check to make sure we dont access rid, tcp_ts,
  * tcp_ts_stamp if no refcount is taken on inet_peer
  */
 static inline void inet_peer_refcheck(const struct inet_peer *p)
 {
        WARN_ON_ONCE(atomic_read(&p->refcnt) <= 0);
 }
-
-
-/* can be called with or without local BH being disabled */
-static inline int inet_getid(struct inet_peer *p, int more)
-{
-       more++;
-       inet_peer_refcheck(p);
-       return atomic_add_return(more, &p->ip_id_count) - more;
-}
-
 #endif /* _NET_INETPEER_H */
index 3ec2b0fb9d8395384373917691f49c433262a8db..0e795df05ec983d07d7acc52cddabd728ea6e0b1 100644 (file)
@@ -196,35 +196,31 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
 #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)
 
-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
-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)
+static inline u64 snmp_fold_field64(void __percpu *mib, int offt, size_t syncp_off)
 {
        return snmp_fold_field(mib, offt);
 }
 #endif
-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])
-{
-       int i;
-
-       BUG_ON(ptr == NULL);
-       for (i = 0; i < SNMP_ARRAY_SZ; i++) {
-               free_percpu(ptr[i]);
-               ptr[i] = NULL;
-       }
-}
 
 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)
+#ifdef CONFIG_SYSCTL
+static inline int inet_is_local_reserved_port(struct net *net, int port)
 {
-       return test_bit(port, sysctl_local_reserved_ports);
+       if (!net->ipv4.sysctl_local_reserved_ports)
+               return 0;
+       return test_bit(port, net->ipv4.sysctl_local_reserved_ports);
 }
+#else
+static inline int inet_is_local_reserved_port(struct net *net, int port)
+{
+       return 0;
+}
+#endif
 
 extern int sysctl_ip_nonlocal_bind;
 
@@ -243,6 +239,9 @@ void ipfrag_init(void);
 
 void ip_static_sysctl_init(void);
 
+#define IP4_REPLY_MARK(net, mark) \
+       ((net)->ipv4.sysctl_fwmark_reflect ? (mark) : 0)
+
 static inline bool ip_is_fragment(const struct iphdr *iph)
 {
        return (iph->frag_off & htons(IP_MF | IP_OFFSET)) != 0;
@@ -281,7 +280,7 @@ static inline bool ip_sk_use_pmtu(const struct sock *sk)
        return inet_sk(sk)->pmtudisc < IP_PMTUDISC_PROBE;
 }
 
-static inline bool ip_sk_local_df(const struct sock *sk)
+static inline bool ip_sk_ignore_df(const struct sock *sk)
 {
        return inet_sk(sk)->pmtudisc < IP_PMTUDISC_DO ||
               inet_sk(sk)->pmtudisc == IP_PMTUDISC_OMIT;
@@ -310,36 +309,48 @@ static inline unsigned int ip_skb_dst_mtu(const struct sk_buff *skb)
        }
 }
 
-void __ip_select_ident(struct iphdr *iph, struct dst_entry *dst, int more);
+#define IP_IDENTS_SZ 2048u
+extern atomic_t *ip_idents;
+
+static inline u32 ip_idents_reserve(u32 hash, int segs)
+{
+       atomic_t *id_ptr = ip_idents + hash % IP_IDENTS_SZ;
+
+       return atomic_add_return(segs, id_ptr) - segs;
+}
+
+void __ip_select_ident(struct iphdr *iph, int segs);
 
-static inline void ip_select_ident(struct sk_buff *skb, struct dst_entry *dst, struct sock *sk)
+static inline void ip_select_ident_segs(struct sk_buff *skb, struct sock *sk, int segs)
 {
        struct iphdr *iph = ip_hdr(skb);
 
-       if ((iph->frag_off & htons(IP_DF)) && !skb->local_df) {
+       if ((iph->frag_off & htons(IP_DF)) && !skb->ignore_df) {
                /* This is only to work around buggy Windows95/2000
                 * VJ compression implementations.  If the ID field
                 * does not change, they drop every other packet in
                 * a TCP stream using header compression.
                 */
-               iph->id = (sk && inet_sk(sk)->inet_daddr) ?
-                                       htons(inet_sk(sk)->inet_id++) : 0;
-       } else
-               __ip_select_ident(iph, dst, 0);
+               if (sk && inet_sk(sk)->inet_daddr) {
+                       iph->id = htons(inet_sk(sk)->inet_id);
+                       inet_sk(sk)->inet_id += segs;
+               } else {
+                       iph->id = 0;
+               }
+       } else {
+               __ip_select_ident(iph, segs);
+       }
 }
 
-static inline void ip_select_ident_more(struct sk_buff *skb, struct dst_entry *dst, struct sock *sk, int more)
+static inline void ip_select_ident(struct sk_buff *skb, struct sock *sk)
 {
-       struct iphdr *iph = ip_hdr(skb);
+       ip_select_ident_segs(skb, sk, 1);
+}
 
-       if ((iph->frag_off & htons(IP_DF)) && !skb->local_df) {
-               if (sk && inet_sk(sk)->inet_daddr) {
-                       iph->id = htons(inet_sk(sk)->inet_id);
-                       inet_sk(sk)->inet_id += 1 + more;
-               } else
-                       iph->id = 0;
-       } else
-               __ip_select_ident(iph, dst, more);
+static inline __wsum inet_compute_pseudo(struct sk_buff *skb, int proto)
+{
+       return csum_tcpudp_nofold(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr,
+                                 skb->len, proto, 0);
 }
 
 /*
index 9e3c540c1b110c71b65003a6aac22cc6c333be5a..55236cb711745fc76ef4c897bcf70e957b34611c 100644 (file)
@@ -41,6 +41,13 @@ __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
                        __wsum csum);
 #endif
 
+static inline __wsum ip6_compute_pseudo(struct sk_buff *skb, int proto)
+{
+       return ~csum_unfold(csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+                                           &ipv6_hdr(skb)->daddr,
+                                           skb->len, proto, 0));
+}
+
 static __inline__ __sum16 tcp_v6_check(int len,
                                   const struct in6_addr *saddr,
                                   const struct in6_addr *daddr,
@@ -75,5 +82,17 @@ static inline void tcp_v6_send_check(struct sock *sk, struct sk_buff *skb)
 }
 #endif
 
+static inline __sum16 udp_v6_check(int len,
+                                  const struct in6_addr *saddr,
+                                  const struct in6_addr *daddr,
+                                  __wsum base)
+{
+       return csum_ipv6_magic(saddr, daddr, len, IPPROTO_UDP, base);
+}
+
+void udp6_set_csum(bool nocheck, struct sk_buff *skb,
+                  const struct in6_addr *saddr,
+                  const struct in6_addr *daddr, int len);
+
 int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto);
 #endif
index 216cecce65e9e1200cd760de64c27ac852ffc85b..1d09b46c1e489325b95f9987327d95ca8affed08 100644 (file)
@@ -186,7 +186,7 @@ static inline bool ip6_sk_accept_pmtu(const struct sock *sk)
               inet6_sk(sk)->pmtudisc != IPV6_PMTUDISC_OMIT;
 }
 
-static inline bool ip6_sk_local_df(const struct sock *sk)
+static inline bool ip6_sk_ignore_df(const struct sock *sk)
 {
        return inet6_sk(sk)->pmtudisc < IPV6_PMTUDISC_DO ||
               inet6_sk(sk)->pmtudisc == IPV6_PMTUDISC_OMIT;
index d640925bc4543bdfb30bf15d164bececbda7e798..574337fe72ddbd3038c061dab5aef142ea0f068b 100644 (file)
@@ -113,6 +113,9 @@ struct frag_hdr {
 #define        IP6_MF          0x0001
 #define        IP6_OFFSET      0xFFF8
 
+#define IP6_REPLY_MARK(net, mark) \
+       ((net)->ipv6.sysctl.fwmark_reflect ? (mark) : 0)
+
 #include <net/sock.h>
 
 /* sysctls */
@@ -583,6 +586,11 @@ static inline bool ipv6_addr_orchid(const struct in6_addr *a)
        return (a->s6_addr32[0] & htonl(0xfffffff0)) == htonl(0x20010010);
 }
 
+static inline bool ipv6_addr_is_multicast(const struct in6_addr *addr)
+{
+       return (addr->s6_addr32[0] & htonl(0xFF000000)) == htonl(0xFF000000);
+}
+
 static inline void ipv6_addr_set_v4mapped(const __be32 addr,
                                          struct in6_addr *v4mapped)
 {
@@ -660,10 +668,22 @@ 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));
 }
 
-void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt);
-
 int ip6_dst_hoplimit(struct dst_entry *dst);
 
+static inline int ip6_sk_dst_hoplimit(struct ipv6_pinfo *np, struct flowi6 *fl6,
+                                     struct dst_entry *dst)
+{
+       int hlimit;
+
+       if (ipv6_addr_is_multicast(&fl6->daddr))
+               hlimit = np->mcast_hops;
+       else
+               hlimit = np->hop_limit;
+       if (hlimit < 0)
+               hlimit = ip6_dst_hoplimit(dst);
+       return hlimit;
+}
+
 /*
  *     Header manipulation
  */
index 8248e3909fdf7d8531890e15bc3f18c5b90ac95f..421b6ecb4b2cdee892379212f9d252f3f3ecffbc 100644 (file)
@@ -188,6 +188,43 @@ struct ieee80211_chanctx_conf {
        u8 drv_priv[0] __aligned(sizeof(void *));
 };
 
+/**
+ * enum ieee80211_chanctx_switch_mode - channel context switch mode
+ * @CHANCTX_SWMODE_REASSIGN_VIF: Both old and new contexts already
+ *     exist (and will continue to exist), but the virtual interface
+ *     needs to be switched from one to the other.
+ * @CHANCTX_SWMODE_SWAP_CONTEXTS: The old context exists but will stop
+ *      to exist with this call, the new context doesn't exist but
+ *      will be active after this call, the virtual interface switches
+ *      from the old to the new (note that the driver may of course
+ *      implement this as an on-the-fly chandef switch of the existing
+ *      hardware context, but the mac80211 pointer for the old context
+ *      will cease to exist and only the new one will later be used
+ *      for changes/removal.)
+ */
+enum ieee80211_chanctx_switch_mode {
+       CHANCTX_SWMODE_REASSIGN_VIF,
+       CHANCTX_SWMODE_SWAP_CONTEXTS,
+};
+
+/**
+ * struct ieee80211_vif_chanctx_switch - vif chanctx switch information
+ *
+ * This is structure is used to pass information about a vif that
+ * needs to switch from one chanctx to another.  The
+ * &ieee80211_chanctx_switch_mode defines how the switch should be
+ * done.
+ *
+ * @vif: the vif that should be switched from old_ctx to new_ctx
+ * @old_ctx: the old context to which the vif was assigned
+ * @new_ctx: the new context to which the vif must be assigned
+ */
+struct ieee80211_vif_chanctx_switch {
+       struct ieee80211_vif *vif;
+       struct ieee80211_chanctx_conf *old_ctx;
+       struct ieee80211_chanctx_conf *new_ctx;
+};
+
 /**
  * enum ieee80211_bss_change - BSS change notification flags
  *
@@ -1113,7 +1150,9 @@ enum ieee80211_vif_flags {
  * @addr: address of this interface
  * @p2p: indicates whether this AP or STA interface is a p2p
  *     interface, i.e. a GO or p2p-sta respectively
- * @csa_active: marks whether a channel switch is going on
+ * @csa_active: marks whether a channel switch is going on. Internally it is
+ *     write-protected by sdata_lock and local->mtx so holding either is fine
+ *     for read access.
  * @driver_flags: flags/capabilities the driver has for this interface,
  *     these need to be set (or cleared) when the interface is added
  *     or, if supported by the driver, the interface type is changed
@@ -1202,14 +1241,18 @@ struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev);
  *     fall back to software crypto. Note that this flag deals only with
  *     RX, if your crypto engine can't deal with TX you can also set the
  *     %IEEE80211_KEY_FLAG_SW_MGMT_TX flag to encrypt such frames in SW.
+ * @IEEE80211_KEY_FLAG_GENERATE_IV_MGMT: This flag should be set by the
+ *     driver for a CCMP key to indicate that is requires IV generation
+ *     only for managment frames (MFP).
  */
 enum ieee80211_key_flags {
-       IEEE80211_KEY_FLAG_GENERATE_IV  = 1<<1,
-       IEEE80211_KEY_FLAG_GENERATE_MMIC= 1<<2,
-       IEEE80211_KEY_FLAG_PAIRWISE     = 1<<3,
-       IEEE80211_KEY_FLAG_SW_MGMT_TX   = 1<<4,
-       IEEE80211_KEY_FLAG_PUT_IV_SPACE = 1<<5,
-       IEEE80211_KEY_FLAG_RX_MGMT      = 1<<6,
+       IEEE80211_KEY_FLAG_GENERATE_IV_MGMT     = BIT(0),
+       IEEE80211_KEY_FLAG_GENERATE_IV          = BIT(1),
+       IEEE80211_KEY_FLAG_GENERATE_MMIC        = BIT(2),
+       IEEE80211_KEY_FLAG_PAIRWISE             = BIT(3),
+       IEEE80211_KEY_FLAG_SW_MGMT_TX           = BIT(4),
+       IEEE80211_KEY_FLAG_PUT_IV_SPACE         = BIT(5),
+       IEEE80211_KEY_FLAG_RX_MGMT              = BIT(6),
 };
 
 /**
@@ -1370,6 +1413,7 @@ struct ieee80211_sta_rates {
  *     the station moves to associated state.
  * @smps_mode: current SMPS mode (off, static or dynamic)
  * @rates: rate control selection table
+ * @tdls: indicates whether the STA is a TDLS peer
  */
 struct ieee80211_sta {
        u32 supp_rates[IEEE80211_NUM_BANDS];
@@ -1384,6 +1428,7 @@ struct ieee80211_sta {
        enum ieee80211_sta_rx_bandwidth bandwidth;
        enum ieee80211_smps_mode smps_mode;
        struct ieee80211_sta_rates __rcu *rates;
+       bool tdls;
 
        /* must be last */
        u8 drv_priv[0] __aligned(sizeof(void *));
@@ -1555,6 +1600,12 @@ struct ieee80211_tx_control {
  *     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.
+ *
+ * @IEEE80211_HW_CHANGE_RUNNING_CHANCTX: The hardware can change a
+ *     channel context on-the-fly.  This is needed for channel switch
+ *     on single-channel hardware.  It can also be used as an
+ *     optimization in certain channel switch cases with
+ *     multi-channel.
  */
 enum ieee80211_hw_flags {
        IEEE80211_HW_HAS_RATE_CONTROL                   = 1<<0,
@@ -1586,6 +1637,7 @@ enum ieee80211_hw_flags {
        IEEE80211_HW_TIMING_BEACON_ONLY                 = 1<<26,
        IEEE80211_HW_SUPPORTS_HT_CCK_RATES              = 1<<27,
        IEEE80211_HW_CHANCTX_STA_CSA                    = 1<<28,
+       IEEE80211_HW_CHANGE_RUNNING_CHANCTX             = 1<<29,
 };
 
 /**
@@ -2609,6 +2661,7 @@ enum ieee80211_roc_type {
  *     of queues to flush, which is useful if different virtual interfaces
  *     use different hardware queues; it may also indicate all queues.
  *     If the parameter @drop is set to %true, pending frames may be dropped.
+ *     Note that vif can be NULL.
  *     The callback can sleep.
  *
  * @channel_switch: Drivers that need (or want) to offload the channel
@@ -2720,6 +2773,11 @@ enum ieee80211_roc_type {
  *     to vif. Possible use is for hw queue remapping.
  * @unassign_vif_chanctx: Notifies device driver about channel context being
  *     unbound from vif.
+ * @switch_vif_chanctx: switch a number of vifs from one chanctx to
+ *     another, as specified in the list of
+ *     @ieee80211_vif_chanctx_switch passed to the driver, according
+ *     to the mode defined in &ieee80211_chanctx_switch_mode.
+ *
  * @start_ap: Start operation on the AP interface, this is called after all the
  *     information in bss_conf is set and beacon can be retrieved. A channel
  *     context is bound before this is called. Note that if the driver uses
@@ -2753,6 +2811,10 @@ enum ieee80211_roc_type {
  *     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.
+ *
+ * @get_expected_throughput: extract the expected throughput towards the
+ *     specified station. The returned value is expressed in Kbps. It returns 0
+ *     if the RC algorithm does not have proper data to provide.
  */
 struct ieee80211_ops {
        void (*tx)(struct ieee80211_hw *hw,
@@ -2871,7 +2933,8 @@ struct ieee80211_ops {
                             struct netlink_callback *cb,
                             void *data, int len);
 #endif
-       void (*flush)(struct ieee80211_hw *hw, u32 queues, bool drop);
+       void (*flush)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                     u32 queues, bool drop);
        void (*channel_switch)(struct ieee80211_hw *hw,
                               struct ieee80211_channel_switch *ch_switch);
        int (*set_antenna)(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant);
@@ -2931,6 +2994,10 @@ struct ieee80211_ops {
        void (*unassign_vif_chanctx)(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif,
                                     struct ieee80211_chanctx_conf *ctx);
+       int (*switch_vif_chanctx)(struct ieee80211_hw *hw,
+                                 struct ieee80211_vif_chanctx_switch *vifs,
+                                 int n_vifs,
+                                 enum ieee80211_chanctx_switch_mode mode);
 
        void (*restart_complete)(struct ieee80211_hw *hw);
 
@@ -2945,6 +3012,7 @@ struct ieee80211_ops {
 
        int (*join_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
        void (*leave_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
+       u32 (*get_expected_throughput)(struct ieee80211_sta *sta);
 };
 
 /**
@@ -3394,6 +3462,47 @@ void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,
  */
 void ieee80211_report_low_ack(struct ieee80211_sta *sta, u32 num_packets);
 
+#define IEEE80211_MAX_CSA_COUNTERS_NUM 2
+
+/**
+ * struct ieee80211_mutable_offsets - mutable beacon offsets
+ * @tim_offset: position of TIM element
+ * @tim_length: size of TIM element
+ * @csa_counter_offs: array of IEEE80211_MAX_CSA_COUNTERS_NUM offsets
+ *     to CSA counters.  This array can contain zero values which
+ *     should be ignored.
+ */
+struct ieee80211_mutable_offsets {
+       u16 tim_offset;
+       u16 tim_length;
+
+       u16 csa_counter_offs[IEEE80211_MAX_CSA_COUNTERS_NUM];
+};
+
+/**
+ * ieee80211_beacon_get_template - beacon template generation function
+ * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @offs: &struct ieee80211_mutable_offsets pointer to struct that will
+ *     receive the offsets that may be updated by the driver.
+ *
+ * If the driver implements beaconing modes, it must use this function to
+ * obtain the beacon template.
+ *
+ * This function should be used if the beacon frames are generated by the
+ * device, and then the driver must use the returned beacon as the template
+ * The driver or the device are responsible to update the DTIM and, when
+ * applicable, the CSA count.
+ *
+ * The driver is responsible for freeing the returned skb.
+ *
+ * Return: The beacon template. %NULL on error.
+ */
+struct sk_buff *
+ieee80211_beacon_get_template(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif,
+                             struct ieee80211_mutable_offsets *offs);
+
 /**
  * ieee80211_beacon_get_tim - beacon generation function
  * @hw: pointer obtained from ieee80211_alloc_hw().
@@ -3405,16 +3514,12 @@ void ieee80211_report_low_ack(struct ieee80211_sta *sta, u32 num_packets);
  *     Set to 0 if invalid (in non-AP modes).
  *
  * If the driver implements beaconing modes, it must use this function to
- * obtain the beacon frame/template.
+ * obtain the beacon frame.
  *
  * If the beacon frames are generated by the host system (i.e., not in
  * hardware/firmware), the driver uses this function to get each beacon
- * frame from mac80211 -- it is responsible for calling this function
- * before the beacon is needed (e.g. based on hardware interrupt).
- *
- * If the beacon frames are generated by the device, then the driver
- * must use the returned beacon as the template and change the TIM IE
- * according to the current DTIM parameters/TIM bitmap.
+ * frame from mac80211 -- it is responsible for calling this function exactly
+ * once before the beacon is needed (e.g. based on hardware interrupt).
  *
  * The driver is responsible for freeing the returned skb.
  *
@@ -3439,6 +3544,20 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
        return ieee80211_beacon_get_tim(hw, vif, NULL, NULL);
 }
 
+/**
+ * ieee80211_csa_update_counter - request mac80211 to decrement the csa counter
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * The csa counter should be updated after each beacon transmission.
+ * This function is called implicitly when
+ * ieee80211_beacon_get/ieee80211_beacon_get_tim are called, however if the
+ * beacon frames are generated by the device, the driver should call this
+ * function after each beacon transmission to sync mac80211's csa counters.
+ *
+ * Return: new csa counter value
+ */
+u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif);
+
 /**
  * ieee80211_csa_finish - notify mac80211 about channel switch
  * @vif: &struct ieee80211_vif pointer from the add_interface callback.
@@ -4467,6 +4586,8 @@ struct rate_control_ops {
        void (*add_sta_debugfs)(void *priv, void *priv_sta,
                                struct dentry *dir);
        void (*remove_sta_debugfs)(void *priv, void *priv_sta);
+
+       u32 (*get_expected_throughput)(void *priv_sta);
 };
 
 static inline int rate_supported(struct ieee80211_sta *sta,
@@ -4576,7 +4697,9 @@ conf_is_ht40(struct ieee80211_conf *conf)
 static inline bool
 conf_is_ht(struct ieee80211_conf *conf)
 {
-       return conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT;
+       return (conf->chandef.width != NL80211_CHAN_WIDTH_5) &&
+               (conf->chandef.width != NL80211_CHAN_WIDTH_10) &&
+               (conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT);
 }
 
 static inline enum nl80211_iftype
index 5f9eb260990f6507d61f42d2463d45455346e812..361d26077196678af5bd7ce217715a2cc847f2af 100644 (file)
@@ -373,6 +373,14 @@ static inline void rt_genid_bump_ipv6(struct net *net)
 }
 #endif
 
+#if IS_ENABLED(CONFIG_IEEE802154_6LOWPAN)
+static inline struct netns_ieee802154_lowpan *
+net_ieee802154_lowpan(struct net *net)
+{
+       return &net->ieee802154_lowpan;
+}
+#endif
+
 /* For callers who don't really care about whether it's IPv4 or IPv6 */
 static inline void rt_genid_bump_all(struct net *net)
 {
index 07eaaf60409215198961cea9834c2d770a90f02e..a71dd333ac6869fdce096dce3a26be133e4d4aae 100644 (file)
@@ -48,6 +48,8 @@ unsigned int nf_nat_setup_info(struct nf_conn *ct,
 extern unsigned int nf_nat_alloc_null_binding(struct nf_conn *ct,
                                              unsigned int hooknum);
 
+struct nf_conn_nat *nf_ct_nat_ext_add(struct nf_conn *ct);
+
 /* Is this tuple already taken? (not by us)*/
 int nf_nat_used_tuple(const struct nf_conntrack_tuple *tuple,
                      const struct nf_conn *ignored_conntrack);
index e6bc14d8fa9a9a4b324fac9df5a47e64277f2aa8..7ee6ce6564aecc6b98eb6247e6d6c6263a3e130c 100644 (file)
@@ -72,21 +72,23 @@ static inline void nft_data_debug(const struct nft_data *data)
  *     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
+ *     @portid: netlink portID of the original message
+ *     @seq: netlink sequence number
+ *     @report: notify via unicast netlink message
  */
 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;
+       struct nft_af_info              *afi;
+       struct nft_table                *table;
+       struct nft_chain                *chain;
        const struct nlattr * const     *nla;
+       u32                             portid;
+       u32                             seq;
+       bool                            report;
 };
 
 struct nft_data_desc {
@@ -145,6 +147,44 @@ struct nft_set_iter {
                              const struct nft_set_elem *elem);
 };
 
+/**
+ *     struct nft_set_desc - description of set elements
+ *
+ *     @klen: key length
+ *     @dlen: data length
+ *     @size: number of set elements
+ */
+struct nft_set_desc {
+       unsigned int            klen;
+       unsigned int            dlen;
+       unsigned int            size;
+};
+
+/**
+ *     enum nft_set_class - performance class
+ *
+ *     @NFT_LOOKUP_O_1: constant, O(1)
+ *     @NFT_LOOKUP_O_LOG_N: logarithmic, O(log N)
+ *     @NFT_LOOKUP_O_N: linear, O(N)
+ */
+enum nft_set_class {
+       NFT_SET_CLASS_O_1,
+       NFT_SET_CLASS_O_LOG_N,
+       NFT_SET_CLASS_O_N,
+};
+
+/**
+ *     struct nft_set_estimate - estimation of memory and performance
+ *                               characteristics
+ *
+ *     @size: required memory
+ *     @class: lookup performance class
+ */
+struct nft_set_estimate {
+       unsigned int            size;
+       enum nft_set_class      class;
+};
+
 /**
  *     struct nft_set_ops - nf_tables set operations
  *
@@ -174,7 +214,11 @@ struct nft_set_ops {
                                                struct nft_set_iter *iter);
 
        unsigned int                    (*privsize)(const struct nlattr * const nla[]);
+       bool                            (*estimate)(const struct nft_set_desc *desc,
+                                                   u32 features,
+                                                   struct nft_set_estimate *est);
        int                             (*init)(const struct nft_set *set,
+                                               const struct nft_set_desc *desc,
                                                const struct nlattr * const nla[]);
        void                            (*destroy)(const struct nft_set *set);
 
@@ -194,6 +238,8 @@ void nft_unregister_set(struct nft_set_ops *ops);
  *     @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)
+ *     @size: maximum set size
+ *     @nelems: number of elements
  *     @ops: set ops
  *     @flags: set flags
  *     @klen: key length
@@ -206,6 +252,8 @@ struct nft_set {
        char                            name[IFNAMSIZ];
        u32                             ktype;
        u32                             dtype;
+       u32                             size;
+       u32                             nelems;
        /* runtime data below here */
        const struct nft_set_ops        *ops ____cacheline_aligned;
        u16                             flags;
@@ -222,6 +270,8 @@ static inline void *nft_set_priv(const struct nft_set *set)
 
 struct nft_set *nf_tables_set_lookup(const struct nft_table *table,
                                     const struct nlattr *nla);
+struct nft_set *nf_tables_set_lookup_byid(const struct net *net,
+                                         const struct nlattr *nla);
 
 /**
  *     struct nft_set_binding - nf_tables set binding
@@ -341,18 +391,75 @@ struct nft_rule {
 };
 
 /**
- *     struct nft_rule_trans - nf_tables rule update in transaction
+ *     struct nft_trans - nf_tables object update in transaction
  *
+ *     @rcu_head: rcu head to defer release of transaction data
  *     @list: used internally
- *     @ctx: rule context
- *     @rule: rule that needs to be updated
+ *     @msg_type: message type
+ *     @ctx: transaction context
+ *     @data: internal information related to the transaction
  */
-struct nft_rule_trans {
+struct nft_trans {
+       struct rcu_head                 rcu_head;
        struct list_head                list;
+       int                             msg_type;
        struct nft_ctx                  ctx;
+       char                            data[0];
+};
+
+struct nft_trans_rule {
        struct nft_rule                 *rule;
 };
 
+#define nft_trans_rule(trans)  \
+       (((struct nft_trans_rule *)trans->data)->rule)
+
+struct nft_trans_set {
+       struct nft_set  *set;
+       u32             set_id;
+};
+
+#define nft_trans_set(trans)   \
+       (((struct nft_trans_set *)trans->data)->set)
+#define nft_trans_set_id(trans)        \
+       (((struct nft_trans_set *)trans->data)->set_id)
+
+struct nft_trans_chain {
+       bool            update;
+       char            name[NFT_CHAIN_MAXNAMELEN];
+       struct nft_stats __percpu *stats;
+       u8              policy;
+};
+
+#define nft_trans_chain_update(trans)  \
+       (((struct nft_trans_chain *)trans->data)->update)
+#define nft_trans_chain_name(trans)    \
+       (((struct nft_trans_chain *)trans->data)->name)
+#define nft_trans_chain_stats(trans)   \
+       (((struct nft_trans_chain *)trans->data)->stats)
+#define nft_trans_chain_policy(trans)  \
+       (((struct nft_trans_chain *)trans->data)->policy)
+
+struct nft_trans_table {
+       bool            update;
+       bool            enable;
+};
+
+#define nft_trans_table_update(trans)  \
+       (((struct nft_trans_table *)trans->data)->update)
+#define nft_trans_table_enable(trans)  \
+       (((struct nft_trans_table *)trans->data)->enable)
+
+struct nft_trans_elem {
+       struct nft_set          *set;
+       struct nft_set_elem     elem;
+};
+
+#define nft_trans_elem_set(trans)      \
+       (((struct nft_trans_elem *)trans->data)->set)
+#define nft_trans_elem(trans)  \
+       (((struct nft_trans_elem *)trans->data)->elem)
+
 static inline struct nft_expr *nft_expr_first(const struct nft_rule *rule)
 {
        return (struct nft_expr *)&rule->data[0];
@@ -385,6 +492,7 @@ static inline void *nft_userdata(const struct nft_rule *rule)
 
 enum nft_chain_flags {
        NFT_BASE_CHAIN                  = 0x1,
+       NFT_CHAIN_INACTIVE              = 0x2,
 };
 
 /**
diff --git a/include/net/netfilter/nft_meta.h b/include/net/netfilter/nft_meta.h
new file mode 100644 (file)
index 0000000..0ee47c3
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef _NFT_META_H_
+#define _NFT_META_H_
+
+struct nft_meta {
+       enum nft_meta_keys      key:8;
+       union {
+               enum nft_registers      dreg:8;
+               enum nft_registers      sreg:8;
+       };
+};
+
+extern const struct nla_policy nft_meta_policy[];
+
+int nft_meta_get_init(const struct nft_ctx *ctx,
+                     const struct nft_expr *expr,
+                     const struct nlattr * const tb[]);
+
+int nft_meta_set_init(const struct nft_ctx *ctx,
+                     const struct nft_expr *expr,
+                     const struct nlattr * const tb[]);
+
+int nft_meta_get_dump(struct sk_buff *skb,
+                     const struct nft_expr *expr);
+
+int nft_meta_set_dump(struct sk_buff *skb,
+                     const struct nft_expr *expr);
+
+void nft_meta_get_eval(const struct nft_expr *expr,
+                      struct nft_data data[NFT_REG_MAX + 1],
+                      const struct nft_pktinfo *pkt);
+
+void nft_meta_set_eval(const struct nft_expr *expr,
+                      struct nft_data data[NFT_REG_MAX + 1],
+                      const struct nft_pktinfo *pkt);
+
+#endif
index b2704fd0ec80d714bc9e7dd9b87721da1853173c..aec5e12f9f19f1a6c506e47f60cc3056d7ce2a3d 100644 (file)
@@ -77,10 +77,17 @@ struct netns_ipv4 {
        int sysctl_ip_no_pmtu_disc;
        int sysctl_ip_fwd_use_pmtu;
 
+       int sysctl_fwmark_reflect;
+       int sysctl_tcp_fwmark_accept;
+
        struct ping_group_range ping_group_range;
 
        atomic_t dev_addr_genid;
 
+#ifdef CONFIG_SYSCTL
+       unsigned long *sysctl_local_reserved_ports;
+#endif
+
 #ifdef CONFIG_IP_MROUTE
 #ifndef CONFIG_IP_MROUTE_MULTIPLE_TABLES
        struct mr_table         *mrt;
index 21edaf1f79161535af7ae1ae3ae7535ff1a236e3..19d3446e59d2555639e9553b1958d7354792af1e 100644 (file)
@@ -30,6 +30,7 @@ struct netns_sysctl_ipv6 {
        int flowlabel_consistency;
        int icmpv6_time;
        int anycast_src_echo_reply;
+       int fwmark_reflect;
 };
 
 struct netns_ipv6 {
index 7655cfe27c3465f726dc0d1eed26040e61b6b366..bdf55c3b7a19ee1756c528ba067d79cef7e89d57 100644 (file)
@@ -36,6 +36,7 @@ enum {
        NFC_DIGITAL_RF_TECH_212F,
        NFC_DIGITAL_RF_TECH_424F,
        NFC_DIGITAL_RF_TECH_ISO15693,
+       NFC_DIGITAL_RF_TECH_106B,
 
        NFC_DIGITAL_RF_TECH_LAST,
 };
@@ -62,6 +63,9 @@ enum {
        NFC_DIGITAL_FRAMING_ISO15693_INVENTORY,
        NFC_DIGITAL_FRAMING_ISO15693_T5T,
 
+       NFC_DIGITAL_FRAMING_NFCB,
+       NFC_DIGITAL_FRAMING_NFCB_T4T,
+
        NFC_DIGITAL_FRAMING_LAST,
 };
 
index 03c4650b548ca7b01c24ba0f95317139949c7cdb..61286db54388b9d03b6a49b494d1492e7cfd8e5c 100644 (file)
@@ -27,6 +27,7 @@ struct nfc_hci_dev;
 struct nfc_hci_ops {
        int (*open) (struct nfc_hci_dev *hdev);
        void (*close) (struct nfc_hci_dev *hdev);
+       int (*load_session) (struct nfc_hci_dev *hdev);
        int (*hci_ready) (struct nfc_hci_dev *hdev);
        /*
         * xmit must always send the complete buffer before
index 2e8b40c16274f73d1ef98d6ee17fa17da172354e..6c583e244de2198d41effbf8a21086296a2c7e40 100644 (file)
@@ -264,4 +264,7 @@ 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);
 
+void nfc_send_to_raw_sock(struct nfc_dev *dev, struct sk_buff *skb,
+                         u8 payload_type, u8 direction);
+
 #endif /* __NET_NFC_H */
index a2441fb1428f3f2e181df63319ca2b3fdc15dc4e..6da46dcf1049789f492cefd9472d0df84d4db91d 100644 (file)
@@ -136,7 +136,7 @@ tcf_exts_exec(struct sk_buff *skb, struct tcf_exts *exts,
 
 int tcf_exts_validate(struct net *net, struct tcf_proto *tp,
                      struct nlattr **tb, struct nlattr *rate_tlv,
-                     struct tcf_exts *exts);
+                     struct tcf_exts *exts, bool ovr);
 void tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts);
 void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst,
                     struct tcf_exts *src);
index 891d80d2c4d2a98cfdad75deee1c28bdce9c9f9f..ec030cd7661693d3812035d350e242f9b271edc4 100644 (file)
@@ -96,7 +96,7 @@ struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r,
                                        struct nlattr *tab);
 void qdisc_put_rtab(struct qdisc_rate_table *tab);
 void qdisc_put_stab(struct qdisc_size_table *tab);
-void qdisc_warn_nonwc(char *txt, struct Qdisc *qdisc);
+void qdisc_warn_nonwc(const char *txt, struct Qdisc *qdisc);
 int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,
                    struct net_device *dev, struct netdev_queue *txq,
                    spinlock_t *root_lock);
index a7e986b081474a51da5011fc19b300d358b0b871..d6fcc1fcdb5b0928a0bd89279e11819a9cc3169d 100644 (file)
@@ -86,7 +86,6 @@ struct inet_protosw {
        struct proto     *prot;
        const struct proto_ops *ops;
   
-       char             no_check;   /* checksum on rcv/xmit/none? */
        unsigned char    flags;      /* See INET_PROTOSW_* below.  */
 };
 #define INET_PROTOSW_REUSE 0x01             /* Are ports automatically reusable? */
index 75fc1f5a948d685fcfff12e04cc6b85e194cd541..259992444e80ae0b88eaec4ff345d23fd8f81c75 100644 (file)
@@ -131,6 +131,11 @@ struct regulatory_request {
  *     all country IE information processed by the regulatory core. This will
  *     override %REGULATORY_COUNTRY_IE_FOLLOW_POWER as all country IEs will
  *     be ignored.
+ * @REGULATORY_ENABLE_RELAX_NO_IR: for devices that wish to allow the
+ *      NO_IR relaxation, which enables transmissions on channels on which
+ *      otherwise initiating radiation is not allowed. This will enable the
+ *      relaxations enabled under the CFG80211_REG_RELAX_NO_IR configuration
+ *      option
  */
 enum ieee80211_regulatory_flags {
        REGULATORY_CUSTOM_REG                   = BIT(0),
@@ -138,6 +143,7 @@ enum ieee80211_regulatory_flags {
        REGULATORY_DISABLE_BEACON_HINTS         = BIT(2),
        REGULATORY_COUNTRY_IE_FOLLOW_POWER      = BIT(3),
        REGULATORY_COUNTRY_IE_IGNORE            = BIT(4),
+       REGULATORY_ENABLE_RELAX_NO_IR           = BIT(5),
 };
 
 struct ieee80211_freq_range {
index d062f81c692f1ee3e61ba1a06bd27e3a9edb761a..624f9857c83e3d7f2987ef95ecc410ad6f8c744f 100644 (file)
@@ -199,7 +199,7 @@ struct tcf_proto_ops {
        int                     (*change)(struct net *net, struct sk_buff *,
                                        struct tcf_proto*, unsigned long,
                                        u32 handle, struct nlattr **,
-                                       unsigned long *);
+                                       unsigned long *, bool);
        int                     (*delete)(struct tcf_proto*, unsigned long);
        void                    (*walk)(struct tcf_proto*, struct tcf_walker *arg);
 
index 0dfcc92600e86cd3263a7378b613d8a3e81ff767..f38588bf3462d9e2374258bb6c383e38413507ed 100644 (file)
@@ -838,10 +838,10 @@ struct sctp_transport {
        unsigned long sackdelay;
        __u32 sackfreq;
 
-       /* When was the last time (in jiffies) that we heard from this
-        * transport?  We use this to pick new active and retran paths.
+       /* When was the last time that we heard from this transport? We use
+        * this to pick new active and retran paths.
         */
-       unsigned long last_time_heard;
+       ktime_t last_time_heard;
 
        /* Last time(in jiffies) when cwnd is reduced due to the congestion
         * indication based on ECNE chunk.
index f257486f17be4bed528826544359c569e6a4b2dd..3f36d45b714a4ba295fe253dbca56e54e0dd0b32 100644 (file)
@@ -3,8 +3,6 @@
 
 #include <linux/types.h>
 
-__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);
index 71596261fa997ec7014b77f0bbee9b47b6146493..f1f27fdbb0d5738d6f3f3fbb93a79d240a1129b5 100644 (file)
@@ -116,51 +116,49 @@ struct linux_xfrm_mib {
        unsigned long   mibs[LINUX_MIB_XFRMMAX];
 };
 
-#define SNMP_ARRAY_SZ 1
-
 #define DEFINE_SNMP_STAT(type, name)   \
-       __typeof__(type) __percpu *name[SNMP_ARRAY_SZ]
+       __typeof__(type) __percpu *name
 #define DEFINE_SNMP_STAT_ATOMIC(type, name)    \
        __typeof__(type) *name
 #define DECLARE_SNMP_STAT(type, name)  \
-       extern __typeof__(type) __percpu *name[SNMP_ARRAY_SZ]
+       extern __typeof__(type) __percpu *name
 
 #define SNMP_INC_STATS_BH(mib, field)  \
-                       __this_cpu_inc(mib[0]->mibs[field])
+                       __this_cpu_inc(mib->mibs[field])
 
 #define SNMP_INC_STATS_USER(mib, field)        \
-                       this_cpu_inc(mib[0]->mibs[field])
+                       this_cpu_inc(mib->mibs[field])
 
 #define SNMP_INC_STATS_ATOMIC_LONG(mib, field) \
                        atomic_long_inc(&mib->mibs[field])
 
 #define SNMP_INC_STATS(mib, field)     \
-                       this_cpu_inc(mib[0]->mibs[field])
+                       this_cpu_inc(mib->mibs[field])
 
 #define SNMP_DEC_STATS(mib, field)     \
-                       this_cpu_dec(mib[0]->mibs[field])
+                       this_cpu_dec(mib->mibs[field])
 
 #define SNMP_ADD_STATS_BH(mib, field, addend)  \
-                       __this_cpu_add(mib[0]->mibs[field], addend)
+                       __this_cpu_add(mib->mibs[field], addend)
 
 #define SNMP_ADD_STATS_USER(mib, field, addend)        \
-                       this_cpu_add(mib[0]->mibs[field], addend)
+                       this_cpu_add(mib->mibs[field], addend)
 
 #define SNMP_ADD_STATS(mib, field, addend)     \
-                       this_cpu_add(mib[0]->mibs[field], addend)
+                       this_cpu_add(mib->mibs[field], addend)
 /*
- * Use "__typeof__(*mib[0]) *ptr" instead of "__typeof__(mib[0]) ptr"
+ * Use "__typeof__(*mib) *ptr" instead of "__typeof__(mib) ptr"
  * to make @ptr a non-percpu pointer.
  */
 #define SNMP_UPD_PO_STATS(mib, basefield, addend)      \
        do { \
-               __typeof__(*mib[0]->mibs) *ptr = mib[0]->mibs;  \
+               __typeof__(*mib->mibs) *ptr = mib->mibs;        \
                this_cpu_inc(ptr[basefield##PKTS]);             \
                this_cpu_add(ptr[basefield##OCTETS], addend);   \
        } while (0)
 #define SNMP_UPD_PO_STATS_BH(mib, basefield, addend)   \
        do { \
-               __typeof__(*mib[0]->mibs) *ptr = mib[0]->mibs;  \
+               __typeof__(*mib->mibs) *ptr = mib->mibs;        \
                __this_cpu_inc(ptr[basefield##PKTS]);           \
                __this_cpu_add(ptr[basefield##OCTETS], addend); \
        } while (0)
@@ -170,7 +168,7 @@ struct linux_xfrm_mib {
 
 #define SNMP_ADD_STATS64_BH(mib, field, addend)                        \
        do {                                                            \
-               __typeof__(*mib[0]) *ptr = __this_cpu_ptr((mib)[0]);    \
+               __typeof__(*mib) *ptr = __this_cpu_ptr(mib);            \
                u64_stats_update_begin(&ptr->syncp);                    \
                ptr->mibs[field] += addend;                             \
                u64_stats_update_end(&ptr->syncp);                      \
@@ -191,8 +189,8 @@ struct linux_xfrm_mib {
 #define SNMP_INC_STATS64(mib, field) SNMP_ADD_STATS64(mib, field, 1)
 #define SNMP_UPD_PO_STATS64_BH(mib, basefield, addend)                 \
        do {                                                            \
-               __typeof__(*mib[0]) *ptr;                               \
-               ptr = __this_cpu_ptr((mib)[0]);                         \
+               __typeof__(*mib) *ptr;                                  \
+               ptr = __this_cpu_ptr(mib);                              \
                u64_stats_update_begin(&ptr->syncp);                    \
                ptr->mibs[basefield##PKTS]++;                           \
                ptr->mibs[basefield##OCTETS] += addend;                 \
index 21569cf456ed54459a537e5a6cf02349a2a8413c..07b7fcd60d808a33f9e6fff208c07fe412da8c7e 100644 (file)
@@ -243,7 +243,8 @@ struct cg_proto;
   *    @sk_sndbuf: size of send buffer in bytes
   *    @sk_flags: %SO_LINGER (l_onoff), %SO_BROADCAST, %SO_KEEPALIVE,
   *               %SO_OOBINLINE settings, %SO_TIMESTAMPING settings
-  *    @sk_no_check: %SO_NO_CHECK setting, whether or not checkup packets
+  *    @sk_no_check_tx: %SO_NO_CHECK setting, set checksum in TX packets
+  *    @sk_no_check_rx: allow zero checksum in RX packets
   *    @sk_route_caps: route capabilities (e.g. %NETIF_F_TSO)
   *    @sk_route_nocaps: forbidden route capabilities (e.g NETIF_F_GSO_MASK)
   *    @sk_gso_type: GSO type (e.g. %SKB_GSO_TCPV4)
@@ -371,7 +372,8 @@ struct sock {
        struct sk_buff_head     sk_write_queue;
        kmemcheck_bitfield_begin(flags);
        unsigned int            sk_shutdown  : 2,
-                               sk_no_check  : 2,
+                               sk_no_check_tx : 1,
+                               sk_no_check_rx : 1,
                                sk_userlocks : 4,
                                sk_protocol  : 8,
                                sk_type      : 16;
index 87d87740818867b8b50074c8a5634658be46199c..7286db80e8b8b6532cb4148a283f2f6d8eb3b530 100644 (file)
@@ -220,8 +220,6 @@ void tcp_time_wait(struct sock *sk, int state, int timeo);
 #define        TFO_SERVER_ENABLE       2
 #define        TFO_CLIENT_NO_COOKIE    4       /* Data in SYN w/o cookie option */
 
-/* Process SYN data but skip cookie validation */
-#define        TFO_SERVER_COOKIE_NOT_CHKED     0x100
 /* Accept SYN data w/o any cookie option */
 #define        TFO_SERVER_COOKIE_NOT_REQD      0x200
 
@@ -230,10 +228,6 @@ void tcp_time_wait(struct sock *sk, int state, int timeo);
  */
 #define        TFO_SERVER_WO_SOCKOPT1  0x400
 #define        TFO_SERVER_WO_SOCKOPT2  0x800
-/* Always create TFO child sockets on a TFO listener even when
- * cookie/data not present. (For testing purpose!)
- */
-#define        TFO_SERVER_ALWAYS       0x1000
 
 extern struct inet_timewait_death_row tcp_death_row;
 
@@ -541,7 +535,7 @@ 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);
+int tcp_fragment(struct sock *, struct sk_buff *, u32, unsigned int, gfp_t);
 
 void tcp_send_probe0(struct sock *);
 void tcp_send_partial(struct sock *);
@@ -558,7 +552,6 @@ void tcp_send_loss_probe(struct sock *sk);
 bool tcp_schedule_loss_probe(struct sock *sk);
 
 /* tcp_input.c */
-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);
@@ -797,7 +790,7 @@ struct tcp_congestion_ops {
        /* return slow start threshold (required) */
        u32 (*ssthresh)(struct sock *sk);
        /* do new cwnd calculation (required) */
-       void (*cong_avoid)(struct sock *sk, u32 ack, u32 acked, u32 in_flight);
+       void (*cong_avoid)(struct sock *sk, u32 ack, u32 acked);
        /* call before changing ca_state (optional) */
        void (*set_state)(struct sock *sk, u8 new_state);
        /* call when cwnd event occurs (optional) */
@@ -829,7 +822,7 @@ void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w);
 
 extern struct tcp_congestion_ops tcp_init_congestion_ops;
 u32 tcp_reno_ssthresh(struct sock *sk);
-void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight);
+void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked);
 extern struct tcp_congestion_ops tcp_reno;
 
 static inline void tcp_set_ca_state(struct sock *sk, const u8 ca_state)
@@ -975,7 +968,30 @@ static inline u32 tcp_wnd_end(const struct tcp_sock *tp)
 {
        return tp->snd_una + tp->snd_wnd;
 }
-bool tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight);
+
+/* We follow the spirit of RFC2861 to validate cwnd but implement a more
+ * flexible approach. The RFC suggests cwnd should not be raised unless
+ * it was fully used previously. And that's exactly what we do in
+ * congestion avoidance mode. But in slow start we allow cwnd to grow
+ * as long as the application has used half the cwnd.
+ * Example :
+ *    cwnd is 10 (IW10), but application sends 9 frames.
+ *    We allow cwnd to reach 18 when all frames are ACKed.
+ * This check is safe because it's as aggressive as slow start which already
+ * risks 100% overshoot. The advantage is that we discourage application to
+ * either send more filler packets or data to artificially blow up the cwnd
+ * usage, and allow application-limited process to probe bw more aggressively.
+ */
+static inline bool tcp_is_cwnd_limited(const struct sock *sk)
+{
+       const struct tcp_sock *tp = tcp_sk(sk);
+
+       /* If in slow start, ensure cwnd grows to twice what was ACKed. */
+       if (tp->snd_cwnd <= tp->snd_ssthresh)
+               return tp->snd_cwnd < 2 * tp->max_packets_out;
+
+       return tp->is_cwnd_limited;
+}
 
 static inline void tcp_check_probe_timer(struct sock *sk)
 {
@@ -1103,6 +1119,9 @@ static inline void tcp_openreq_init(struct request_sock *req,
        ireq->ir_num = ntohs(tcp_hdr(skb)->dest);
 }
 
+extern void tcp_openreq_init_rwin(struct request_sock *req,
+                                 struct sock *sk, struct dst_entry *dst);
+
 void tcp_enter_memory_pressure(struct sock *sk);
 
 static inline int keepalive_intvl_when(const struct tcp_sock *tp)
@@ -1312,8 +1331,10 @@ 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);
-void tcp_fastopen_cookie_gen(__be32 src, __be32 dst,
-                            struct tcp_fastopen_cookie *foc);
+bool tcp_try_fastopen(struct sock *sk, struct sk_buff *skb,
+                     struct request_sock *req,
+                     struct tcp_fastopen_cookie *foc,
+                     struct dst_entry *dst);
 void tcp_fastopen_init_key_once(bool publish);
 #define TCP_FASTOPEN_KEY_LENGTH 16
 
diff --git a/include/net/tso.h b/include/net/tso.h
new file mode 100644 (file)
index 0000000..47e5444
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef _TSO_H
+#define _TSO_H
+
+#include <net/ip.h>
+
+struct tso_t {
+       int next_frag_idx;
+       void *data;
+       size_t size;
+       u16 ip_id;
+       u32 tcp_seq;
+};
+
+int tso_count_descs(struct sk_buff *skb);
+void tso_build_hdr(struct sk_buff *skb, char *hdr, struct tso_t *tso,
+                  int size, bool is_last);
+void tso_build_data(struct sk_buff *skb, struct tso_t *tso, int size);
+void tso_start(struct sk_buff *skb, struct tso_t *tso);
+
+#endif /* _TSO_H */
index a24f0f3e107f67c71b256cd4fb85d04b2389fb7a..2ecfc6e156098dbd37b3763a3040109a19e85810 100644 (file)
@@ -95,15 +95,6 @@ static inline struct udp_hslot *udp_hashslot2(struct udp_table *table,
        return &table->hash2[hash & table->mask];
 }
 
-/* Note: this must match 'valbool' in sock_setsockopt */
-#define UDP_CSUM_NOXMIT                1
-
-/* Used by SunRPC/xprt layer. */
-#define UDP_CSUM_NORCV         2
-
-/* Default, as per the RFC, is to always do csums. */
-#define UDP_CSUM_DEFAULT       0
-
 extern struct proto udp_prot;
 
 extern atomic_long_t udp_memory_allocated;
@@ -156,6 +147,15 @@ static inline __wsum udp_csum(struct sk_buff *skb)
        return csum;
 }
 
+static inline __sum16 udp_v4_check(int len, __be32 saddr,
+                                  __be32 daddr, __wsum base)
+{
+       return csum_tcpudp_magic(saddr, daddr, len, IPPROTO_UDP, base);
+}
+
+void udp_set_csum(bool nocheck, struct sk_buff *skb,
+                 __be32 saddr, __be32 daddr, int len);
+
 /* hash routines shared between UDPv4/6 and UDP-Litev4/6 */
 static inline void udp_lib_hash(struct sock *sk)
 {
index 5deef1ae78c964608d629d29d5628dfef52fdf6e..12196ce661d9e288a3d3928ccd66cefff10920a9 100644 (file)
@@ -24,16 +24,26 @@ struct vxlan_sock {
        struct udp_offload udp_offloads;
 };
 
+#define VXLAN_F_LEARN                  0x01
+#define VXLAN_F_PROXY                  0x02
+#define VXLAN_F_RSC                    0x04
+#define VXLAN_F_L2MISS                 0x08
+#define VXLAN_F_L3MISS                 0x10
+#define VXLAN_F_IPV6                   0x20
+#define VXLAN_F_UDP_CSUM               0x40
+#define VXLAN_F_UDP_ZERO_CSUM6_TX      0x80
+#define VXLAN_F_UDP_ZERO_CSUM6_RX      0x100
+
 struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port,
                                  vxlan_rcv_t *rcv, void *data,
-                                 bool no_share, bool ipv6);
+                                 bool no_share, u32 flags);
 
 void vxlan_sock_release(struct vxlan_sock *vs);
 
 int vxlan_xmit_skb(struct vxlan_sock *vs,
                   struct rtable *rt, struct sk_buff *skb,
                   __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df,
-                  __be16 src_port, __be16 dst_port, __be32 vni);
+                  __be16 src_port, __be16 dst_port, __be32 vni, bool xnet);
 
 __be16 vxlan_src_port(__u16 port_min, __u16 port_max, struct sk_buff *skb);
 
index 116e9c7e19cbbe00272bbf4adc6de7681b0c27ee..721e9c3b11bddb208927852d223a36f548a3cade 100644 (file)
@@ -691,13 +691,6 @@ struct xfrm_spi_skb_cb {
 
 #define XFRM_SPI_SKB_CB(__skb) ((struct xfrm_spi_skb_cb *)&((__skb)->cb[0]))
 
-/* Audit Information */
-struct xfrm_audit {
-       u32     secid;
-       kuid_t  loginuid;
-       unsigned int sessionid;
-};
-
 #ifdef CONFIG_AUDITSYSCALL
 static inline struct audit_buffer *xfrm_audit_start(const char *op)
 {
@@ -713,30 +706,24 @@ static inline struct audit_buffer *xfrm_audit_start(const char *op)
        return audit_buf;
 }
 
-static inline void xfrm_audit_helper_usrinfo(kuid_t auid, unsigned int ses, u32 secid,
+static inline void xfrm_audit_helper_usrinfo(bool task_valid,
                                             struct audit_buffer *audit_buf)
 {
-       char *secctx;
-       u32 secctx_len;
-
-       audit_log_format(audit_buf, " auid=%u ses=%u",
-                        from_kuid(&init_user_ns, auid), ses);
-       if (secid != 0 &&
-           security_secid_to_secctx(secid, &secctx, &secctx_len) == 0) {
-               audit_log_format(audit_buf, " subj=%s", secctx);
-               security_release_secctx(secctx, secctx_len);
-       } else
-               audit_log_task_context(audit_buf);
-}
-
-void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, kuid_t auid,
-                          unsigned int ses, u32 secid);
-void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, kuid_t auid,
-                             unsigned int ses, u32 secid);
-void xfrm_audit_state_add(struct xfrm_state *x, int result, kuid_t auid,
-                         unsigned int ses, u32 secid);
-void xfrm_audit_state_delete(struct xfrm_state *x, int result, kuid_t auid,
-                            unsigned int ses, u32 secid);
+       const unsigned int auid = from_kuid(&init_user_ns, task_valid ?
+                                           audit_get_loginuid(current) :
+                                           INVALID_UID);
+       const unsigned int ses = task_valid ? audit_get_sessionid(current) :
+               (unsigned int) -1;
+
+       audit_log_format(audit_buf, " auid=%u ses=%u", auid, ses);
+       audit_log_task_context(audit_buf);
+}
+
+void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, bool task_valid);
+void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result,
+                             bool task_valid);
+void xfrm_audit_state_add(struct xfrm_state *x, int result, bool task_valid);
+void xfrm_audit_state_delete(struct xfrm_state *x, int result, bool task_valid);
 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,
@@ -749,22 +736,22 @@ void xfrm_audit_state_icvfail(struct xfrm_state *x, struct sk_buff *skb,
 #else
 
 static inline void xfrm_audit_policy_add(struct xfrm_policy *xp, int result,
-                                 kuid_t auid, unsigned int ses, u32 secid)
+                                        bool task_valid)
 {
 }
 
 static inline void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result,
-                                 kuid_t auid, unsigned int ses, u32 secid)
+                                           bool task_valid)
 {
 }
 
 static inline void xfrm_audit_state_add(struct xfrm_state *x, int result,
-                                kuid_t auid, unsigned int ses, u32 secid)
+                                       bool task_valid)
 {
 }
 
 static inline void xfrm_audit_state_delete(struct xfrm_state *x, int result,
-                                   kuid_t auid, unsigned int ses, u32 secid)
+                                          bool task_valid)
 {
 }
 
@@ -1508,7 +1495,7 @@ struct xfrmk_spdinfo {
 
 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);
+int xfrm_state_flush(struct net *net, u8 proto, bool task_valid);
 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);
@@ -1603,7 +1590,7 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark,
                                          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);
+int xfrm_policy_flush(struct net *net, u8 type, bool task_valid);
 u32 xfrm_get_acqseq(void);
 int verify_spi_info(u8 proto, u32 min, u32 max);
 int xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi);
index e016e2ac38df8f6c570980a4624e9ccaefd0dd5e..42ed789ebafcf9ab04c759d7ef167e981aab2bc6 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/types.h>
 #include <linux/timer.h>
 #include <linux/scatterlist.h>
+#include <scsi/scsi_device.h>
 
 struct Scsi_Host;
 struct scsi_device;
@@ -315,4 +316,20 @@ static inline void set_driver_byte(struct scsi_cmnd *cmd, char status)
        cmd->result = (cmd->result & 0x00ffffff) | (status << 24);
 }
 
+static inline unsigned scsi_transfer_length(struct scsi_cmnd *scmd)
+{
+       unsigned int xfer_len = blk_rq_bytes(scmd->request);
+       unsigned int prot_op = scsi_get_prot_op(scmd);
+       unsigned int sector_size = scmd->device->sector_size;
+
+       switch (prot_op) {
+       case SCSI_PROT_NORMAL:
+       case SCSI_PROT_WRITE_STRIP:
+       case SCSI_PROT_READ_INSERT:
+               return xfer_len;
+       }
+
+       return xfer_len + (xfer_len >> ilog2(sector_size)) * 8;
+}
+
 #endif /* _SCSI_SCSI_CMND_H */
index 33b487b5da92dc76f549df5d09a3852518c14159..daef9daa500c11f0ff7d92151fafbf9080a80e02 100644 (file)
@@ -70,7 +70,8 @@ extern void iscsit_build_nopin_rsp(struct iscsi_cmd *, struct iscsi_conn *,
 extern void iscsit_build_task_mgt_rsp(struct iscsi_cmd *, struct iscsi_conn *,
                                struct iscsi_tm_rsp *);
 extern int iscsit_build_text_rsp(struct iscsi_cmd *, struct iscsi_conn *,
-                               struct iscsi_text_rsp *);
+                               struct iscsi_text_rsp *,
+                               enum iscsit_transport_type);
 extern void iscsit_build_reject(struct iscsi_cmd *, struct iscsi_conn *,
                                struct iscsi_reject *);
 extern int iscsit_build_logout_rsp(struct iscsi_cmd *, struct iscsi_conn *,
index 3a1c1eea1fffcaee767d4bef1a8ea2700f1884e2..9adc1bca1178ba36482f63bf8cef1444045f7709 100644 (file)
@@ -59,6 +59,7 @@ int   transport_subsystem_register(struct se_subsystem_api *);
 void   transport_subsystem_release(struct se_subsystem_api *);
 
 void   target_complete_cmd(struct se_cmd *, u8);
+void   target_complete_cmd_with_length(struct se_cmd *, u8, int);
 
 sense_reason_t spc_parse_cdb(struct se_cmd *cmd, unsigned int *size);
 sense_reason_t spc_emulate_report_luns(struct se_cmd *cmd);
index 67e1bbf836954dbc486d1d7debb3a2bfa3cfd3d1..0a68d5ae584e9dfada372fdae49c5d0934176402 100644 (file)
@@ -530,6 +530,26 @@ TRACE_EVENT(sched_swap_numa,
                        __entry->dst_pid, __entry->dst_tgid, __entry->dst_ngid,
                        __entry->dst_cpu, __entry->dst_nid)
 );
+
+/*
+ * Tracepoint for waking a polling cpu without an IPI.
+ */
+TRACE_EVENT(sched_wake_idle_without_ipi,
+
+       TP_PROTO(int cpu),
+
+       TP_ARGS(cpu),
+
+       TP_STRUCT__entry(
+               __field(        int,    cpu     )
+       ),
+
+       TP_fast_assign(
+               __entry->cpu    = cpu;
+       ),
+
+       TP_printk("cpu=%d", __entry->cpu)
+);
 #endif /* _TRACE_SCHED_H */
 
 /* This part must be outside protection */
index 4c31a366be1639f08d2487309ff8602e4f7f4704..cf6714752b69ab12b5adc92461bb793aad39a431 100644 (file)
@@ -385,6 +385,14 @@ enum {
  */
 #define AUDIT_MESSAGE_TEXT_MAX 8560
 
+/* Multicast Netlink socket groups (default up to 32) */
+enum audit_nlgrps {
+       AUDIT_NLGRP_NONE,       /* Group 0 not used */
+       AUDIT_NLGRP_READLOG,    /* "best effort" read only socket */
+       __AUDIT_NLGRP_MAX
+};
+#define AUDIT_NLGRP_MAX                (__AUDIT_NLGRP_MAX - 1)
+
 struct audit_status {
        __u32           mask;           /* Bit mask for valid entries */
        __u32           enabled;        /* 1 = enabled, 0 = disabled */
index 5d9d1d1407180a9291c0f986945e3a34f2ccf51e..41892f720057df2cc23f96c7d44d6ab2808fdfdd 100644 (file)
@@ -42,8 +42,8 @@
  * DAMAGE.
  */
 
-#ifndef CAN_H
-#define CAN_H
+#ifndef _UAPI_CAN_H
+#define _UAPI_CAN_H
 
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -191,4 +191,4 @@ struct can_filter {
 
 #define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
 
-#endif /* CAN_H */
+#endif /* !_UAPI_CAN_H */
index 382251a1d21403acd817577d83c21f47d0389865..89ddb9dc9bdf7ca8bd191c9dedf7019f24573931 100644 (file)
@@ -41,8 +41,8 @@
  * DAMAGE.
  */
 
-#ifndef CAN_BCM_H
-#define CAN_BCM_H
+#ifndef _UAPI_CAN_BCM_H
+#define _UAPI_CAN_BCM_H
 
 #include <linux/types.h>
 #include <linux/can.h>
@@ -95,4 +95,4 @@ enum {
 #define TX_RESET_MULTI_IDX  0x0200
 #define RX_RTR_FRAME        0x0400
 
-#endif /* CAN_BCM_H */
+#endif /* !_UAPI_CAN_BCM_H */
index b632045453202074ada263866052bc2a806e85bc..c247446ab25a4e564a068ae97e154cf7972f0a1d 100644 (file)
@@ -41,8 +41,8 @@
  * DAMAGE.
  */
 
-#ifndef CAN_ERROR_H
-#define CAN_ERROR_H
+#ifndef _UAPI_CAN_ERROR_H
+#define _UAPI_CAN_ERROR_H
 
 #define CAN_ERR_DLC 8 /* dlc for error message frames */
 
 
 /* controller specific additional information / data[5..7] */
 
-#endif /* CAN_ERROR_H */
+#endif /* _UAPI_CAN_ERROR_H */
index 844c8964bdfee3a3f4a7308bf0fd832e82754a89..3e6184cf2f6dc5b2318f87f49db09ef119182683 100644 (file)
@@ -41,8 +41,8 @@
  * DAMAGE.
  */
 
-#ifndef CAN_GW_H
-#define CAN_GW_H
+#ifndef _UAPI_CAN_GW_H
+#define _UAPI_CAN_GW_H
 
 #include <linux/types.h>
 #include <linux/can.h>
@@ -200,4 +200,4 @@ enum {
  *         Beware of sending unpacked or aligned structs!
  */
 
-#endif
+#endif /* !_UAPI_CAN_GW_H */
index 7e2e1863db16e02fa15e1edc109adefda8236ba7..813d11f549774aadf5f3d87ba28be840e7f6e399 100644 (file)
@@ -15,8 +15,8 @@
  * GNU General Public License for more details.
  */
 
-#ifndef CAN_NETLINK_H
-#define CAN_NETLINK_H
+#ifndef _UAPI_CAN_NETLINK_H
+#define _UAPI_CAN_NETLINK_H
 
 #include <linux/types.h>
 
@@ -130,4 +130,4 @@ enum {
 
 #define IFLA_CAN_MAX   (__IFLA_CAN_MAX - 1)
 
-#endif /* CAN_NETLINK_H */
+#endif /* !_UAPI_CAN_NETLINK_H */
index c7d8c334e0ce26838c7cc611bd3ad1eb5a31a6c4..78ec76fd89a6ce4fe70161576ec0c01e5d6156d3 100644 (file)
@@ -42,8 +42,8 @@
  * DAMAGE.
  */
 
-#ifndef CAN_RAW_H
-#define CAN_RAW_H
+#ifndef _UAPI_CAN_RAW_H
+#define _UAPI_CAN_RAW_H
 
 #include <linux/can.h>
 
@@ -59,4 +59,4 @@ enum {
        CAN_RAW_FD_FRAMES,      /* allow CAN FD frames (default:off) */
 };
 
-#endif
+#endif /* !_UAPI_CAN_RAW_H */
index 154dd6d3c8fedaa54a04817567580b11727f2807..12c37a197d247ca980fef9c6e81ed0c067f27987 100644 (file)
@@ -347,7 +347,12 @@ struct vfs_cap_data {
 
 #define CAP_BLOCK_SUSPEND    36
 
-#define CAP_LAST_CAP         CAP_BLOCK_SUSPEND
+/* Allow reading the audit log via multicast netlink socket */
+
+#define CAP_AUDIT_READ         37
+
+
+#define CAP_LAST_CAP         CAP_AUDIT_READ
 
 #define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP)
 
index fd161e91b6d7e711270da030ed84b38f9b718f0c..e3c7a719c76b4a586c40da9506adaa095857ede4 100644 (file)
@@ -846,6 +846,38 @@ struct ethtool_rxfh_indir {
        __u32   ring_index[0];
 };
 
+/**
+ * struct ethtool_rxfh - command to get/set RX flow hash indir or/and hash key.
+ * @cmd: Specific command number - %ETHTOOL_GRSSH or %ETHTOOL_SRSSH
+ * @rss_context: RSS context identifier.
+ * @indir_size: On entry, the array size of the user buffer for the
+ *     indirection table, which may be zero, or (for %ETHTOOL_SRSSH),
+ *     %ETH_RXFH_INDIR_NO_CHANGE.  On return from %ETHTOOL_GRSSH,
+ *     the array size of the hardware indirection table.
+ * @key_size: On entry, the array size of the user buffer for the hash key,
+ *     which may be zero.  On return from %ETHTOOL_GRSSH, the size of the
+ *     hardware hash key.
+ * @rsvd:      Reserved for future extensions.
+ * @rss_config: RX ring/queue index for each hash value i.e., indirection table
+ *     of @indir_size __u32 elements, followed by hash key of @key_size
+ *     bytes.
+ *
+ * For %ETHTOOL_GRSSH, a @indir_size and key_size of zero means that only the
+ * size should be returned.  For %ETHTOOL_SRSSH, an @indir_size of
+ * %ETH_RXFH_INDIR_NO_CHANGE means that indir table setting is not requested
+ * and a @indir_size of zero means the indir table should be reset to default
+ * values.
+ */
+struct ethtool_rxfh {
+       __u32   cmd;
+       __u32   rss_context;
+       __u32   indir_size;
+       __u32   key_size;
+       __u32   rsvd[2];
+       __u32   rss_config[0];
+};
+#define ETH_RXFH_INDIR_NO_CHANGE       0xffffffff
+
 /**
  * struct ethtool_rx_ntuple_flow_spec - specification for RX flow filter
  * @flow_type: Type of match to perform, e.g. %TCP_V4_FLOW
@@ -1118,6 +1150,9 @@ enum ethtool_sfeatures_retval_bits {
 #define ETHTOOL_GEEE           0x00000044 /* Get EEE settings */
 #define ETHTOOL_SEEE           0x00000045 /* Set EEE settings */
 
+#define ETHTOOL_GRSSH          0x00000046 /* Get RX flow hash configuration */
+#define ETHTOOL_SRSSH          0x00000047 /* Set RX flow hash configuration */
+
 /* compatibility with older code */
 #define SPARC_ETH_GSET         ETHTOOL_GSET
 #define SPARC_ETH_SSET         ETHTOOL_SSET
index 8eb9ccaa5b48124b716e5abe7741aef4ca711d9f..253b4d42cf2bb31517a8a159f5e1c37f13c075f2 100644 (file)
@@ -130,7 +130,8 @@ struct sock_fprog { /* Required for SO_ATTACH_FILTER. */
 #define SKF_AD_VLAN_TAG        44
 #define SKF_AD_VLAN_TAG_PRESENT 48
 #define SKF_AD_PAY_OFFSET      52
-#define SKF_AD_MAX     56
+#define SKF_AD_RANDOM  56
+#define SKF_AD_MAX     60
 #define SKF_NET_OFF   (-0x100000)
 #define SKF_LL_OFF    (-0x200000)
 
index 0d36909c3aefa27f26aaf60d1d356d462ae94d0e..1086cd9f675473b21f2c316eac16d591743af777 100644 (file)
  *  Define max and min legal sizes.  The frame sizes do not include
  *  4 byte FCS/CRC (frame check sequence).
  */
-#define FDDI_K_ALEN                    6               /* Octets in one FDDI address */
-#define FDDI_K_8022_HLEN       16              /* Total octets in 802.2 header */
-#define FDDI_K_SNAP_HLEN       21              /* Total octets in 802.2 SNAP header */
-#define FDDI_K_8022_ZLEN       16              /* Min octets in 802.2 frame sans FCS */
-#define FDDI_K_SNAP_ZLEN       21              /* Min octets in 802.2 SNAP frame sans FCS */
+#define FDDI_K_ALEN            6       /* Octets in one FDDI address */
+#define FDDI_K_8022_HLEN       16      /* Total octets in 802.2 header */
+#define FDDI_K_SNAP_HLEN       21      /* Total octets in 802.2 SNAP header */
+#define FDDI_K_8022_ZLEN       16      /* Min octets in 802.2 frame sans
+                                          FCS */
+#define FDDI_K_SNAP_ZLEN       21      /* Min octets in 802.2 SNAP frame sans
+                                          FCS */
 #define FDDI_K_8022_DLEN       4475    /* Max octets in 802.2 payload */
 #define FDDI_K_SNAP_DLEN       4470    /* Max octets in 802.2 SNAP payload */
-#define FDDI_K_LLC_ZLEN                13              /* Min octets in LLC frame sans FCS */
+#define FDDI_K_LLC_ZLEN                13      /* Min octets in LLC frame sans FCS */
 #define FDDI_K_LLC_LEN         4491    /* Max octets in LLC frame sans FCS */
+#define FDDI_K_OUI_LEN         3       /* Octets in OUI in 802.2 SNAP
+                                          header */
 
 /* Define FDDI Frame Control (FC) Byte values */
-#define FDDI_FC_K_VOID                                 0x00    
-#define FDDI_FC_K_NON_RESTRICTED_TOKEN 0x80    
-#define FDDI_FC_K_RESTRICTED_TOKEN             0xC0    
-#define FDDI_FC_K_SMT_MIN                              0x41
-#define FDDI_FC_K_SMT_MAX                              0x4F
-#define FDDI_FC_K_MAC_MIN                              0xC1
-#define FDDI_FC_K_MAC_MAX                              0xCF    
-#define FDDI_FC_K_ASYNC_LLC_MIN                        0x50
-#define FDDI_FC_K_ASYNC_LLC_DEF                        0x54
-#define FDDI_FC_K_ASYNC_LLC_MAX                        0x5F
-#define FDDI_FC_K_SYNC_LLC_MIN                 0xD0
-#define FDDI_FC_K_SYNC_LLC_MAX                 0xD7
-#define FDDI_FC_K_IMPLEMENTOR_MIN              0x60
-#define FDDI_FC_K_IMPLEMENTOR_MAX              0x6F
-#define FDDI_FC_K_RESERVED_MIN                 0x70
-#define FDDI_FC_K_RESERVED_MAX                 0x7F
+#define FDDI_FC_K_VOID                 0x00
+#define FDDI_FC_K_NON_RESTRICTED_TOKEN 0x80
+#define FDDI_FC_K_RESTRICTED_TOKEN     0xC0
+#define FDDI_FC_K_SMT_MIN              0x41
+#define FDDI_FC_K_SMT_MAX              0x4F
+#define FDDI_FC_K_MAC_MIN              0xC1
+#define FDDI_FC_K_MAC_MAX              0xCF
+#define FDDI_FC_K_ASYNC_LLC_MIN                0x50
+#define FDDI_FC_K_ASYNC_LLC_DEF                0x54
+#define FDDI_FC_K_ASYNC_LLC_MAX                0x5F
+#define FDDI_FC_K_SYNC_LLC_MIN         0xD0
+#define FDDI_FC_K_SYNC_LLC_MAX         0xD7
+#define FDDI_FC_K_IMPLEMENTOR_MIN      0x60
+#define FDDI_FC_K_IMPLEMENTOR_MAX      0x6F
+#define FDDI_FC_K_RESERVED_MIN         0x70
+#define FDDI_FC_K_RESERVED_MAX         0x7F
 
 /* Define LLC and SNAP constants */
-#define FDDI_EXTENDED_SAP      0xAA
+#define FDDI_EXTENDED_SAP              0xAA
 #define FDDI_UI_CMD                    0x03
 
 /* Define 802.2 Type 1 header */
 struct fddi_8022_1_hdr {
-       __u8    dsap;                                   /* destination service access point */
-       __u8    ssap;                                   /* source service access point */
-       __u8    ctrl;                                   /* control byte #1 */
+       __u8    dsap;                   /* destination service access point */
+       __u8    ssap;                   /* source service access point */
+       __u8    ctrl;                   /* control byte #1 */
 } __attribute__((packed));
 
 /* Define 802.2 Type 2 header */
 struct fddi_8022_2_hdr {
-       __u8    dsap;                                   /* destination service access point */
-       __u8    ssap;                                   /* source service access point */
-       __u8    ctrl_1;                                 /* control byte #1 */
-       __u8    ctrl_2;                                 /* control byte #2 */
+       __u8    dsap;                   /* destination service access point */
+       __u8    ssap;                   /* source service access point */
+       __u8    ctrl_1;                 /* control byte #1 */
+       __u8    ctrl_2;                 /* control byte #2 */
 } __attribute__((packed));
 
 /* Define 802.2 SNAP header */
-#define FDDI_K_OUI_LEN 3
 struct fddi_snap_hdr {
-       __u8    dsap;                                   /* always 0xAA */
-       __u8    ssap;                                   /* always 0xAA */
-       __u8    ctrl;                                   /* always 0x03 */
+       __u8    dsap;                   /* always 0xAA */
+       __u8    ssap;                   /* always 0xAA */
+       __u8    ctrl;                   /* always 0x03 */
        __u8    oui[FDDI_K_OUI_LEN];    /* organizational universal id */
-       __be16  ethertype;                              /* packet type ID field */
+       __be16  ethertype;              /* packet type ID field */
 } __attribute__((packed));
 
 /* Define FDDI LLC frame header */
 struct fddihdr {
-       __u8    fc;                                             /* frame control */
-       __u8    daddr[FDDI_K_ALEN];             /* destination address */
-       __u8    saddr[FDDI_K_ALEN];             /* source address */
-       union
-               {
-               struct fddi_8022_1_hdr          llc_8022_1;
-               struct fddi_8022_2_hdr          llc_8022_2;
-               struct fddi_snap_hdr            llc_snap;
-               } hdr;
+       __u8    fc;                     /* frame control */
+       __u8    daddr[FDDI_K_ALEN];     /* destination address */
+       __u8    saddr[FDDI_K_ALEN];     /* source address */
+       union {
+               struct fddi_8022_1_hdr  llc_8022_1;
+               struct fddi_8022_2_hdr  llc_8022_2;
+               struct fddi_snap_hdr    llc_snap;
+       } hdr;
 } __attribute__((packed));
 
 
index 9a7f7ace66494e144c55c08d5bbba085de3a2b7c..b38534895db5608b9ddad5be74f50e989263277c 100644 (file)
@@ -319,6 +319,9 @@ enum {
        IFLA_VXLAN_PORT,        /* destination port */
        IFLA_VXLAN_GROUP6,
        IFLA_VXLAN_LOCAL6,
+       IFLA_VXLAN_UDP_CSUM,
+       IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
+       IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
        __IFLA_VXLAN_MAX
 };
 #define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1)
@@ -399,9 +402,10 @@ enum {
        IFLA_VF_UNSPEC,
        IFLA_VF_MAC,            /* Hardware queue specific attributes */
        IFLA_VF_VLAN,
-       IFLA_VF_TX_RATE,        /* TX Bandwidth Allocation */
+       IFLA_VF_TX_RATE,        /* Max TX Bandwidth Allocation */
        IFLA_VF_SPOOFCHK,       /* Spoof Checking on/off switch */
        IFLA_VF_LINK_STATE,     /* link state enable/disable/auto switch */
+       IFLA_VF_RATE,           /* Min and Max TX Bandwidth Allocation */
        __IFLA_VF_MAX,
 };
 
@@ -423,6 +427,12 @@ struct ifla_vf_tx_rate {
        __u32 rate; /* Max TX bandwidth in Mbps, 0 disables throttling */
 };
 
+struct ifla_vf_rate {
+       __u32 vf;
+       __u32 min_tx_rate; /* Min Bandwidth in Mbps */
+       __u32 max_tx_rate; /* Max Bandwidth in Mbps */
+};
+
 struct ifla_vf_spoofchk {
        __u32 vf;
        __u32 setting;
index aee73d0611fb9bbb72f3ab52a0b91c09ba861c12..3bce9e9d9f7c8d82c1e8064904098a73cdd4fb81 100644 (file)
@@ -100,7 +100,7 @@ enum {
 #define IFLA_GRE_MAX   (__IFLA_GRE_MAX - 1)
 
 /* VTI-mode i_flags */
-#define VTI_ISVTI 0x0001
+#define VTI_ISVTI ((__force __be16)0x0001)
 
 enum {
        IFLA_VTI_UNSPEC,
index 8adb681603273c287f25be055c0258c8a2a73c65..21caa2631c209fdc0f0c7e21591e0507fc3e0b9a 100644 (file)
@@ -124,6 +124,8 @@ enum {
        L2TP_ATTR_STATS,                /* nested */
        L2TP_ATTR_IP6_SADDR,            /* struct in6_addr */
        L2TP_ATTR_IP6_DADDR,            /* struct in6_addr */
+       L2TP_ATTR_UDP_ZERO_CSUM6_TX,    /* u8 */
+       L2TP_ATTR_UDP_ZERO_CSUM6_RX,    /* u8 */
        __L2TP_ATTR_MAX,
 };
 
index d3ef583104e0c905503a3911c33ca4018294e7de..4a1d7e96dfe3d7ccf8669428a943ed53af37828e 100644 (file)
@@ -24,6 +24,7 @@ enum {
        NDA_PORT,
        NDA_VNI,
        NDA_IFINDEX,
+       NDA_MASTER,
        __NDA_MAX
 };
 
index c88ccbfda5f1b111a5fa43e1d1803bcccf95b521..2a88f645a5d821c47d7a53a05dc7a0e083a72342 100644 (file)
@@ -211,6 +211,29 @@ enum nft_set_flags {
        NFT_SET_MAP                     = 0x8,
 };
 
+/**
+ * enum nft_set_policies - set selection policy
+ *
+ * @NFT_SET_POL_PERFORMANCE: prefer high performance over low memory use
+ * @NFT_SET_POL_MEMORY: prefer low memory use over high performance
+ */
+enum nft_set_policies {
+       NFT_SET_POL_PERFORMANCE,
+       NFT_SET_POL_MEMORY,
+};
+
+/**
+ * enum nft_set_desc_attributes - set element description
+ *
+ * @NFTA_SET_DESC_SIZE: number of elements in set (NLA_U32)
+ */
+enum nft_set_desc_attributes {
+       NFTA_SET_DESC_UNSPEC,
+       NFTA_SET_DESC_SIZE,
+       __NFTA_SET_DESC_MAX
+};
+#define NFTA_SET_DESC_MAX      (__NFTA_SET_DESC_MAX - 1)
+
 /**
  * enum nft_set_attributes - nf_tables set netlink attributes
  *
@@ -221,6 +244,9 @@ enum nft_set_flags {
  * @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)
+ * @NFTA_SET_POLICY: selection policy (NLA_U32)
+ * @NFTA_SET_DESC: set description (NLA_NESTED)
+ * @NFTA_SET_ID: uniquely identifies a set in a transaction (NLA_U32)
  */
 enum nft_set_attributes {
        NFTA_SET_UNSPEC,
@@ -231,6 +257,9 @@ enum nft_set_attributes {
        NFTA_SET_KEY_LEN,
        NFTA_SET_DATA_TYPE,
        NFTA_SET_DATA_LEN,
+       NFTA_SET_POLICY,
+       NFTA_SET_DESC,
+       NFTA_SET_ID,
        __NFTA_SET_MAX
 };
 #define NFTA_SET_MAX           (__NFTA_SET_MAX - 1)
@@ -266,12 +295,14 @@ enum nft_set_elem_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)
+ * @NFTA_SET_ELEM_LIST_SET_ID: uniquely identifies a set in a transaction (NLA_U32)
  */
 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_SET_ID,
        __NFTA_SET_ELEM_LIST_MAX
 };
 #define NFTA_SET_ELEM_LIST_MAX (__NFTA_SET_ELEM_LIST_MAX - 1)
@@ -457,12 +488,14 @@ enum nft_cmp_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)
+ * @NFTA_LOOKUP_SET_ID: uniquely identifies a set in a transaction (NLA_U32)
  */
 enum nft_lookup_attributes {
        NFTA_LOOKUP_UNSPEC,
        NFTA_LOOKUP_SET,
        NFTA_LOOKUP_SREG,
        NFTA_LOOKUP_DREG,
+       NFTA_LOOKUP_SET_ID,
        __NFTA_LOOKUP_MAX
 };
 #define NFTA_LOOKUP_MAX                (__NFTA_LOOKUP_MAX - 1)
@@ -536,6 +569,8 @@ enum nft_exthdr_attributes {
  * @NFT_META_SECMARK: packet secmark (skb->secmark)
  * @NFT_META_NFPROTO: netfilter protocol
  * @NFT_META_L4PROTO: layer 4 protocol number
+ * @NFT_META_BRI_IIFNAME: packet input bridge interface name
+ * @NFT_META_BRI_OIFNAME: packet output bridge interface name
  */
 enum nft_meta_keys {
        NFT_META_LEN,
@@ -555,6 +590,8 @@ enum nft_meta_keys {
        NFT_META_SECMARK,
        NFT_META_NFPROTO,
        NFT_META_L4PROTO,
+       NFT_META_BRI_IIFNAME,
+       NFT_META_BRI_OIFNAME,
 };
 
 /**
index 596ddd45253c02b1f611c0655b649e651109485a..354a7e5e50f22e1c079b86dd1977ad7584d7d7c0 100644 (file)
@@ -20,6 +20,8 @@ enum nfnetlink_groups {
 #define NFNLGRP_CONNTRACK_EXP_DESTROY  NFNLGRP_CONNTRACK_EXP_DESTROY
        NFNLGRP_NFTABLES,
 #define NFNLGRP_NFTABLES                NFNLGRP_NFTABLES
+       NFNLGRP_ACCT_QUOTA,
+#define NFNLGRP_ACCT_QUOTA             NFNLGRP_ACCT_QUOTA
        __NFNLGRP_MAX,
 };
 #define NFNLGRP_MAX    (__NFNLGRP_MAX - 1)
index c7b6269e760b83bfbf6c30849f7724c0c67e3673..51404ec190223a84f9f0c72393d435c674b8f11c 100644 (file)
@@ -10,15 +10,24 @@ enum nfnl_acct_msg_types {
        NFNL_MSG_ACCT_GET,
        NFNL_MSG_ACCT_GET_CTRZERO,
        NFNL_MSG_ACCT_DEL,
+       NFNL_MSG_ACCT_OVERQUOTA,
        NFNL_MSG_ACCT_MAX
 };
 
+enum nfnl_acct_flags {
+       NFACCT_F_QUOTA_PKTS     = (1 << 0),
+       NFACCT_F_QUOTA_BYTES    = (1 << 1),
+       NFACCT_F_OVERQUOTA      = (1 << 2), /* can't be set from userspace */
+};
+
 enum nfnl_acct_type {
        NFACCT_UNSPEC,
        NFACCT_NAME,
        NFACCT_PKTS,
        NFACCT_BYTES,
        NFACCT_USE,
+       NFACCT_FLAGS,
+       NFACCT_QUOTA,
        __NFACCT_MAX
 };
 #define NFACCT_MAX (__NFACCT_MAX - 1)
index 9789dc95b6a8fb775a16e4fff1612101cae879c7..9b19b44619286616b04f1aa9f04d026878730744 100644 (file)
@@ -273,11 +273,19 @@ struct sockaddr_nfc_llcp {
  * First byte is the adapter index
  * Second byte contains flags
  *  - 0x01 - Direction (0=RX, 1=TX)
- *  - 0x02-0x80 - Reserved
+ *  - 0x02-0x04 - Payload type (000=LLCP, 001=NCI, 010=HCI, 011=Digital,
+ *                              100=Proprietary)
+ *  - 0x05-0x80 - Reserved
  **/
-#define NFC_LLCP_RAW_HEADER_SIZE       2
-#define NFC_LLCP_DIRECTION_RX          0x00
-#define NFC_LLCP_DIRECTION_TX          0x01
+#define NFC_RAW_HEADER_SIZE    2
+#define NFC_DIRECTION_RX               0x00
+#define NFC_DIRECTION_TX               0x01
+
+#define RAW_PAYLOAD_LLCP 0
+#define RAW_PAYLOAD_NCI        1
+#define RAW_PAYLOAD_HCI        2
+#define RAW_PAYLOAD_DIGITAL    3
+#define RAW_PAYLOAD_PROPRIETARY        4
 
 /* socket option names */
 #define NFC_LLCP_RW            0
index 194c1eab04d8ad9cb37ae855d8c011d48722c829..be9519b52bb10edef5e5be12ddd3ff2065d706ac 100644 (file)
  *     TX status event pertaining to the TX request.
  *     %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the
  *     management frames at CCK rate or not in 2GHz band.
+ *     %NL80211_ATTR_CSA_C_OFFSETS_TX is an array of offsets to CSA
+ *     counters which will be updated to the current value. This attribute
+ *     is used during CSA period.
  * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this
  *     command may be used with the corresponding cookie to cancel the wait
  *     time if it is known that it is no longer necessary.
@@ -1525,10 +1528,10 @@ enum nl80211_commands {
  *     operation).
  * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information
  *     for the time while performing a channel switch.
- * @NL80211_ATTR_CSA_C_OFF_BEACON: Offset of the channel switch counter
- *     field in the beacons tail (%NL80211_ATTR_BEACON_TAIL).
- * @NL80211_ATTR_CSA_C_OFF_PRESP: Offset of the channel switch counter
- *     field in the probe response (%NL80211_ATTR_PROBE_RESP).
+ * @NL80211_ATTR_CSA_C_OFF_BEACON: An array of offsets (u16) to the channel
+ *     switch counters in the beacons tail (%NL80211_ATTR_BEACON_TAIL).
+ * @NL80211_ATTR_CSA_C_OFF_PRESP: An array of offsets (u16) to the channel
+ *     switch counters in the probe response (%NL80211_ATTR_PROBE_RESP).
  *
  * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32.
  *     As specified in the &enum nl80211_rxmgmt_flags.
@@ -1576,9 +1579,18 @@ enum nl80211_commands {
  *     advertise values that cannot always be met. In such cases, an attempt
  *     to add a new station entry with @NL80211_CMD_NEW_STATION may fail.
  *
+ * @NL80211_ATTR_CSA_C_OFFSETS_TX: An array of csa counter offsets (u16) which
+ *     should be updated when the frame is transmitted.
+ * @NL80211_ATTR_MAX_CSA_COUNTERS: U8 attribute used to advertise the maximum
+ *     supported number of csa counters.
+ *
  * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32.
  *     As specified in the &enum nl80211_tdls_peer_capability.
  *
+ * @NL80211_ATTR_IFACE_SOCKET_OWNER: flag attribute, if set during interface
+ *     creation then the new interface will be owned by the netlink socket
+ *     that created it and will be destroyed when the socket is closed
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1914,6 +1926,11 @@ enum nl80211_attrs {
 
        NL80211_ATTR_TDLS_PEER_CAPABILITY,
 
+       NL80211_ATTR_IFACE_SOCKET_OWNER,
+
+       NL80211_ATTR_CSA_C_OFFSETS_TX,
+       NL80211_ATTR_MAX_CSA_COUNTERS,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -2182,6 +2199,8 @@ enum nl80211_sta_bss_param {
  *     Contains a nested array of signal strength attributes (u8, dBm)
  * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average
  *     Same format as NL80211_STA_INFO_CHAIN_SIGNAL.
+ * @NL80211_STA_EXPECTED_THROUGHPUT: expected throughput considering also the
+ *     802.11 header (u32, kbps)
  * @__NL80211_STA_INFO_AFTER_LAST: internal
  * @NL80211_STA_INFO_MAX: highest possible station info attribute
  */
@@ -2213,6 +2232,7 @@ enum nl80211_sta_info {
        NL80211_STA_INFO_TX_BYTES64,
        NL80211_STA_INFO_CHAIN_SIGNAL,
        NL80211_STA_INFO_CHAIN_SIGNAL_AVG,
+       NL80211_STA_INFO_EXPECTED_THROUGHPUT,
 
        /* keep last */
        __NL80211_STA_INFO_AFTER_LAST,
@@ -2336,9 +2356,34 @@ enum nl80211_band_attr {
  *     using this channel as the primary or any of the secondary channels
  *     isn't possible
  * @NL80211_FREQUENCY_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
+ * @NL80211_FREQUENCY_ATTR_INDOOR_ONLY: Only indoor use is permitted on this
+ *     channel. A channel that has the INDOOR_ONLY attribute can only be
+ *     used when there is a clear assessment that the device is operating in
+ *     an indoor surroundings, i.e., it is connected to AC power (and not
+ *     through portable DC inverters) or is under the control of a master
+ *     that is acting as an AP and is connected to AC power.
+ * @NL80211_FREQUENCY_ATTR_GO_CONCURRENT: GO operation is allowed on this
+ *     channel if it's connected concurrently to a BSS on the same channel on
+ *     the 2 GHz band or to a channel in the same UNII band (on the 5 GHz
+ *     band), and IEEE80211_CHAN_RADAR is not set. Instantiating a GO on a
+ *     channel that has the GO_CONCURRENT attribute set can be done when there
+ *     is a clear assessment that the device is operating under the guidance of
+ *     an authorized master, i.e., setting up a GO while the device is also
+ *     connected to an AP with DFS and radar detection on the UNII band (it is
+ *     up to user-space, i.e., wpa_supplicant to perform the required
+ *     verifications)
+ * @NL80211_FREQUENCY_ATTR_NO_20MHZ: 20 MHz operation is not allowed
+ *     on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_NO_10MHZ: 10 MHz operation is not allowed
+ *     on this channel in current regulatory domain.
  * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
  *     currently defined
  * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
+ *
+ * See https://apps.fcc.gov/eas/comments/GetPublishedDocument.html?id=327&tn=528122
+ * for more information on the FCC description of the relaxations allowed
+ * by NL80211_FREQUENCY_ATTR_INDOOR_ONLY and
+ * NL80211_FREQUENCY_ATTR_GO_CONCURRENT.
  */
 enum nl80211_frequency_attr {
        __NL80211_FREQUENCY_ATTR_INVALID,
@@ -2355,6 +2400,10 @@ enum nl80211_frequency_attr {
        NL80211_FREQUENCY_ATTR_NO_80MHZ,
        NL80211_FREQUENCY_ATTR_NO_160MHZ,
        NL80211_FREQUENCY_ATTR_DFS_CAC_TIME,
+       NL80211_FREQUENCY_ATTR_INDOOR_ONLY,
+       NL80211_FREQUENCY_ATTR_GO_CONCURRENT,
+       NL80211_FREQUENCY_ATTR_NO_20MHZ,
+       NL80211_FREQUENCY_ATTR_NO_10MHZ,
 
        /* keep last */
        __NL80211_FREQUENCY_ATTR_AFTER_LAST,
@@ -2573,10 +2622,13 @@ enum nl80211_dfs_regions {
  *     present has been registered with the wireless core that
  *     has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a
  *     supported feature.
+ * @NL80211_USER_REG_HINT_INDOOR: a user sent an hint indicating that the
+ *     platform is operating in an indoor environment.
  */
 enum nl80211_user_reg_hint_type {
        NL80211_USER_REG_HINT_USER      = 0,
        NL80211_USER_REG_HINT_CELL_BASE = 1,
+       NL80211_USER_REG_HINT_INDOOR    = 2,
 };
 
 /**
@@ -3650,6 +3702,8 @@ enum nl80211_iface_limit_attrs {
  *     different channels may be used within this group.
  * @NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS: u32 attribute containing the bitmap
  *     of supported channel widths for radar detection.
+ * @NL80211_IFACE_COMB_RADAR_DETECT_REGIONS: u32 attribute containing the bitmap
+ *     of supported regulatory regions for radar detection.
  * @NUM_NL80211_IFACE_COMB: number of attributes
  * @MAX_NL80211_IFACE_COMB: highest attribute number
  *
@@ -3683,6 +3737,7 @@ enum nl80211_if_combination_attrs {
        NL80211_IFACE_COMB_STA_AP_BI_MATCH,
        NL80211_IFACE_COMB_NUM_CHANNELS,
        NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
+       NL80211_IFACE_COMB_RADAR_DETECT_REGIONS,
 
        /* keep last */
        NUM_NL80211_IFACE_COMB,
@@ -3893,6 +3948,9 @@ enum nl80211_ap_sme_features {
  *     interface. An active monitor interface behaves like a normal monitor
  *     interface, but gets added to the driver. It ensures that incoming
  *     unicast packets directed at the configured interface address get ACKed.
+ * @NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE: This driver supports dynamic
+ *     channel bandwidth change (e.g., HT 20 <-> 40 MHz channel) during the
+ *     lifetime of a BSS.
  */
 enum nl80211_feature_flags {
        NL80211_FEATURE_SK_TX_STATUS                    = 1 << 0,
@@ -3913,6 +3971,7 @@ enum nl80211_feature_flags {
        NL80211_FEATURE_FULL_AP_CLIENT_STATE            = 1 << 15,
        NL80211_FEATURE_USERSPACE_MPM                   = 1 << 16,
        NL80211_FEATURE_ACTIVE_MONITOR                  = 1 << 17,
+       NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE       = 1 << 18,
 };
 
 /**
index 970553cbbc8e9d69dca1167aedfbfee20947e257..0b979ee4bfc0dea7e7d3f2f743708c997b2138cb 100644 (file)
@@ -395,7 +395,9 @@ struct ovs_key_nd {
  * @OVS_FLOW_ATTR_ACTIONS: Nested %OVS_ACTION_ATTR_* attributes specifying
  * the actions to take for packets that match the key.  Always present in
  * notifications.  Required for %OVS_FLOW_CMD_NEW requests, optional for
- * %OVS_FLOW_CMD_SET requests.
+ * %OVS_FLOW_CMD_SET requests.  An %OVS_FLOW_CMD_SET without
+ * %OVS_FLOW_ATTR_ACTIONS will not modify the actions.  To clear the actions,
+ * an %OVS_FLOW_ATTR_ACTIONS without any nested attributes must be given.
  * @OVS_FLOW_ATTR_STATS: &struct ovs_flow_stats giving statistics for this
  * flow.  Present in notifications if the stats would be nonzero.  Ignored in
  * requests.
index e3fc8f09d110ce50bb02c255e3ec3a26f17c321c..5312fae472187c4c768787f1df4f799db1104455 100644 (file)
@@ -163,8 +163,9 @@ enum perf_branch_sample_type {
        PERF_SAMPLE_BRANCH_ABORT_TX     = 1U << 7, /* transaction aborts */
        PERF_SAMPLE_BRANCH_IN_TX        = 1U << 8, /* in transaction */
        PERF_SAMPLE_BRANCH_NO_TX        = 1U << 9, /* not in transaction */
+       PERF_SAMPLE_BRANCH_COND         = 1U << 10, /* conditional branches */
 
-       PERF_SAMPLE_BRANCH_MAX          = 1U << 10, /* non-ABI */
+       PERF_SAMPLE_BRANCH_MAX          = 1U << 11, /* non-ABI */
 };
 
 #define PERF_SAMPLE_BRANCH_PLM_ALL \
@@ -301,8 +302,8 @@ struct perf_event_attr {
                                exclude_callchain_kernel : 1, /* exclude kernel callchains */
                                exclude_callchain_user   : 1, /* exclude user callchains */
                                mmap2          :  1, /* include mmap with inode data     */
-
-                               __reserved_1   : 40;
+                               comm_exec      :  1, /* flag comm events that are due to an exec */
+                               __reserved_1   : 39;
 
        union {
                __u32           wakeup_events;    /* wakeup every n events */
@@ -501,7 +502,12 @@ struct perf_event_mmap_page {
 #define PERF_RECORD_MISC_GUEST_KERNEL          (4 << 0)
 #define PERF_RECORD_MISC_GUEST_USER            (5 << 0)
 
+/*
+ * PERF_RECORD_MISC_MMAP_DATA and PERF_RECORD_MISC_COMM_EXEC are used on
+ * different events so can reuse the same bit position.
+ */
 #define PERF_RECORD_MISC_MMAP_DATA             (1 << 13)
+#define PERF_RECORD_MISC_COMM_EXEC             (1 << 13)
 /*
  * Indicates that the content of PERF_SAMPLE_IP points to
  * the actual instruction that triggered the event. See also
index 852373d27dbb2bdd2016bf6a9d2ba00cd68e2954..6f71b9b4159581eac01241f9c443948584f30f6e 100644 (file)
@@ -38,6 +38,7 @@
 #define _LINUX_TIPC_H_
 
 #include <linux/types.h>
+#include <linux/sockios.h>
 
 /*
  * TIPC addressing primitives
@@ -87,6 +88,7 @@ static inline unsigned int tipc_node(__u32 addr)
 
 #define TIPC_CFG_SRV           0       /* configuration service name type */
 #define TIPC_TOP_SRV           1       /* topology service name type */
+#define TIPC_LINK_STATE                2       /* link state name type */
 #define TIPC_RESERVED_TYPES    64      /* lowest user-publishable name type */
 
 /*
@@ -206,4 +208,25 @@ struct sockaddr_tipc {
 #define TIPC_NODE_RECVQ_DEPTH  131     /* Default: none (read only) */
 #define TIPC_SOCK_RECVQ_DEPTH  132     /* Default: none (read only) */
 
+/*
+ * Maximum sizes of TIPC bearer-related names (including terminating NULL)
+ * The string formatting for each name element is:
+ * media: media
+ * interface: media:interface name
+ * link: Z.C.N:interface-Z.C.N:interface
+ *
+ */
+
+#define TIPC_MAX_MEDIA_NAME    16
+#define TIPC_MAX_IF_NAME       16
+#define TIPC_MAX_BEARER_NAME   32
+#define TIPC_MAX_LINK_NAME     60
+
+#define SIOCGETLINKNAME                SIOCPROTOPRIVATE
+
+struct tipc_sioc_ln_req {
+       __u32 peer;
+       __u32 bearer_id;
+       char linkname[TIPC_MAX_LINK_NAME];
+};
 #endif
index 6b0bff09b3a7ced5dc7cf2c1a07dd4f82112b088..41a76acbb305f85cb4cb0ec6dfab9cab1e20e1d4 100644 (file)
@@ -39,6 +39,7 @@
 
 #include <linux/types.h>
 #include <linux/string.h>
+#include <linux/tipc.h>
 #include <asm/byteorder.h>
 
 #ifndef __KERNEL__
 #define TIPC_TLV_NAME_TBL_QUERY        25      /* struct tipc_name_table_query */
 #define TIPC_TLV_PORT_REF      26      /* 32-bit port reference */
 
-/*
- * Maximum sizes of TIPC bearer-related names (including terminating NUL)
- */
-
-#define TIPC_MAX_MEDIA_NAME    16      /* format = media */
-#define TIPC_MAX_IF_NAME       16      /* format = interface */
-#define TIPC_MAX_BEARER_NAME   32      /* format = media:interface */
-#define TIPC_MAX_LINK_NAME     60      /* format = Z.C.N:interface-Z.C.N:interface */
-
 /*
  * Link priority limits (min, default, max, media default)
  */
index e2bcfd75a30d38d37475cef6e0382e1081d5a3b0..16574ea18f0cf62d743287779ffaed6205d120d0 100644 (file)
@@ -29,6 +29,8 @@ struct udphdr {
 /* UDP socket options */
 #define UDP_CORK       1       /* Never send partially complete segments */
 #define UDP_ENCAP      100     /* Set the socket to accept encapsulated packets */
+#define UDP_NO_CHECK6_TX 101   /* Disable sending checksum for UDP6X */
+#define UDP_NO_CHECK6_RX 102   /* Disable accpeting checksum for UDP6 */
 
 /* UDP encapsulation types */
 #define UDP_ENCAP_ESPINUDP_NON_IKE     1 /* draft-ietf-ipsec-nat-t-ike-00/01 */
index c50061db609893a924160556203f7dcf73e24700..70054cc0708d0aed1bc4fa23ce6bec09d65435b8 100644 (file)
  * node as before.
  */
 
+/*
+ * Multiple transmit and receive queues:
+ * If supported, the backend will write the key "multi-queue-max-queues" to
+ * the directory for that vif, and set its value to the maximum supported
+ * number of queues.
+ * Frontends that are aware of this feature and wish to use it can write the
+ * key "multi-queue-num-queues", set to the number they wish to use, which
+ * must be greater than zero, and no more than the value reported by the backend
+ * in "multi-queue-max-queues".
+ *
+ * Queues replicate the shared rings and event channels.
+ * "feature-split-event-channels" may optionally be used when using
+ * multiple queues, but is not mandatory.
+ *
+ * Each queue consists of one shared ring pair, i.e. there must be the same
+ * number of tx and rx rings.
+ *
+ * For frontends requesting just one queue, the usual event-channel and
+ * ring-ref keys are written as before, simplifying the backend processing
+ * to avoid distinguishing between a frontend that doesn't understand the
+ * multi-queue feature, and one that does, but requested only one queue.
+ *
+ * Frontends requesting two or more queues must not write the toplevel
+ * event-channel (or event-channel-{tx,rx}) and {tx,rx}-ring-ref keys,
+ * instead writing those keys under sub-keys having the name "queue-N" where
+ * N is the integer ID of the queue for which those keys belong. Queues
+ * are indexed from zero. For example, a frontend with two queues and split
+ * event channels must write the following set of queue-related keys:
+ *
+ * /local/domain/1/device/vif/0/multi-queue-num-queues = "2"
+ * /local/domain/1/device/vif/0/queue-0 = ""
+ * /local/domain/1/device/vif/0/queue-0/tx-ring-ref = "<ring-ref-tx0>"
+ * /local/domain/1/device/vif/0/queue-0/rx-ring-ref = "<ring-ref-rx0>"
+ * /local/domain/1/device/vif/0/queue-0/event-channel-tx = "<evtchn-tx0>"
+ * /local/domain/1/device/vif/0/queue-0/event-channel-rx = "<evtchn-rx0>"
+ * /local/domain/1/device/vif/0/queue-1 = ""
+ * /local/domain/1/device/vif/0/queue-1/tx-ring-ref = "<ring-ref-tx1>"
+ * /local/domain/1/device/vif/0/queue-1/rx-ring-ref = "<ring-ref-rx1"
+ * /local/domain/1/device/vif/0/queue-1/event-channel-tx = "<evtchn-tx1>"
+ * /local/domain/1/device/vif/0/queue-1/event-channel-rx = "<evtchn-rx1>"
+ *
+ * If there is any inconsistency in the XenStore data, the backend may
+ * choose not to connect any queues, instead treating the request as an
+ * error. This includes scenarios where more (or fewer) queues were
+ * requested than the frontend provided details for.
+ *
+ * Mapping of packets to queues is considered to be a function of the
+ * transmitting system (backend or frontend) and is not negotiated
+ * between the two. Guests are free to transmit packets on any queue
+ * they choose, provided it has been set up correctly. Guests must be
+ * prepared to receive packets on any queue they have requested be set up.
+ */
+
 /*
  * "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.
index d2b32ac27a394095e87229030e010eb428ae7174..35536d9c096420f3718b93e583c10d5fdfc07cec 100644 (file)
@@ -223,3 +223,10 @@ endif
 config MUTEX_SPIN_ON_OWNER
        def_bool y
        depends on SMP && !DEBUG_MUTEXES
+
+config ARCH_USE_QUEUE_RWLOCK
+       bool
+
+config QUEUE_RWLOCK
+       def_bool y if ARCH_USE_QUEUE_RWLOCK
+       depends on SMP
index f30106459a3243945d75038d8c069b06906efa13..3ef2e0e797e8e576cc6c922d65ef2dc6e40e2d86 100644 (file)
@@ -423,6 +423,38 @@ static void kauditd_send_skb(struct sk_buff *skb)
                consume_skb(skb);
 }
 
+/*
+ * kauditd_send_multicast_skb - send the skb to multicast userspace listeners
+ *
+ * This function doesn't consume an skb as might be expected since it has to
+ * copy it anyways.
+ */
+static void kauditd_send_multicast_skb(struct sk_buff *skb)
+{
+       struct sk_buff          *copy;
+       struct audit_net        *aunet = net_generic(&init_net, audit_net_id);
+       struct sock             *sock = aunet->nlsk;
+
+       if (!netlink_has_listeners(sock, AUDIT_NLGRP_READLOG))
+               return;
+
+       /*
+        * The seemingly wasteful skb_copy() rather than bumping the refcount
+        * using skb_get() is necessary because non-standard mods are made to
+        * the skb by the original kaudit unicast socket send routine.  The
+        * existing auditd daemon assumes this breakage.  Fixing this would
+        * require co-ordinating a change in the established protocol between
+        * the kaudit kernel subsystem and the auditd userspace code.  There is
+        * no reason for new multicast clients to continue with this
+        * non-compliance.
+        */
+       copy = skb_copy(skb, GFP_KERNEL);
+       if (!copy)
+               return;
+
+       nlmsg_multicast(sock, copy, 0, AUDIT_NLGRP_READLOG, GFP_KERNEL);
+}
+
 /*
  * flush_hold_queue - empty the hold queue if auditd appears
  *
@@ -1076,10 +1108,22 @@ static void audit_receive(struct sk_buff  *skb)
        mutex_unlock(&audit_cmd_mutex);
 }
 
+/* Run custom bind function on netlink socket group connect or bind requests. */
+static int audit_bind(int group)
+{
+       if (!capable(CAP_AUDIT_READ))
+               return -EPERM;
+
+       return 0;
+}
+
 static int __net_init audit_net_init(struct net *net)
 {
        struct netlink_kernel_cfg cfg = {
                .input  = audit_receive,
+               .bind   = audit_bind,
+               .flags  = NL_CFG_F_NONROOT_RECV,
+               .groups = AUDIT_NLGRP_MAX,
        };
 
        struct audit_net *aunet = net_generic(net, audit_net_id);
@@ -1901,10 +1945,10 @@ void audit_log_link_denied(const char *operation, struct path *link)
  * audit_log_end - end one audit record
  * @ab: the audit_buffer
  *
- * The netlink_* functions cannot be called inside an irq context, so
- * the audit buffer is placed on a queue and a tasklet is scheduled to
- * remove them from the queue outside the irq context.  May be called in
- * any context.
+ * netlink_unicast() cannot be called inside an irq context because it blocks
+ * (last arg, flags, is not set to MSG_DONTWAIT), so the audit buffer is placed
+ * on a queue and a tasklet is scheduled to remove them from the queue outside
+ * the irq context.  May be called in any context.
  */
 void audit_log_end(struct audit_buffer *ab)
 {
@@ -1914,6 +1958,18 @@ void audit_log_end(struct audit_buffer *ab)
                audit_log_lost("rate limit exceeded");
        } else {
                struct nlmsghdr *nlh = nlmsg_hdr(ab->skb);
+
+               kauditd_send_multicast_skb(ab->skb);
+
+               /*
+                * The original kaudit unicast socket sends up messages with
+                * nlmsg_len set to the payload length rather than the entire
+                * message length.  This breaks the standard set by netlink.
+                * The existing auditd daemon assumes this breakage.  Fixing
+                * this would require co-ordinating a change in the established
+                * protocol between the kaudit kernel subsystem and the auditd
+                * userspace code.
+                */
                nlh->nlmsg_len = ab->skb->len - NLMSG_HDRLEN;
 
                if (audit_pid) {
index 24d35cc38e42ab080400e3ae82f93d475c19e177..5fa58e4cffac3a7f2c7144fda8aeb72e6fc05a98 100644 (file)
@@ -2974,6 +2974,22 @@ static void perf_event_enable_on_exec(struct perf_event_context *ctx)
        local_irq_restore(flags);
 }
 
+void perf_event_exec(void)
+{
+       struct perf_event_context *ctx;
+       int ctxn;
+
+       rcu_read_lock();
+       for_each_task_context_nr(ctxn) {
+               ctx = current->perf_event_ctxp[ctxn];
+               if (!ctx)
+                       continue;
+
+               perf_event_enable_on_exec(ctx);
+       }
+       rcu_read_unlock();
+}
+
 /*
  * Cross CPU call to read the hardware event
  */
@@ -5075,21 +5091,9 @@ static void perf_event_comm_event(struct perf_comm_event *comm_event)
                       NULL);
 }
 
-void perf_event_comm(struct task_struct *task)
+void perf_event_comm(struct task_struct *task, bool exec)
 {
        struct perf_comm_event comm_event;
-       struct perf_event_context *ctx;
-       int ctxn;
-
-       rcu_read_lock();
-       for_each_task_context_nr(ctxn) {
-               ctx = task->perf_event_ctxp[ctxn];
-               if (!ctx)
-                       continue;
-
-               perf_event_enable_on_exec(ctx);
-       }
-       rcu_read_unlock();
 
        if (!atomic_read(&nr_comm_events))
                return;
@@ -5101,7 +5105,7 @@ void perf_event_comm(struct task_struct *task)
                .event_id  = {
                        .header = {
                                .type = PERF_RECORD_COMM,
-                               .misc = 0,
+                               .misc = exec ? PERF_RECORD_MISC_COMM_EXEC : 0,
                                /* .size */
                        },
                        /* .pid */
@@ -7122,6 +7126,13 @@ SYSCALL_DEFINE5(perf_event_open,
                }
        }
 
+       if (is_sampling_event(event)) {
+               if (event->pmu->capabilities & PERF_PMU_CAP_NO_INTERRUPT) {
+                       err = -ENOTSUPP;
+                       goto err_alloc;
+               }
+       }
+
        account_event(event);
 
        /*
@@ -7433,7 +7444,7 @@ __perf_event_exit_task(struct perf_event *child_event,
 
 static void perf_event_exit_task_context(struct task_struct *child, int ctxn)
 {
-       struct perf_event *child_event;
+       struct perf_event *child_event, *next;
        struct perf_event_context *child_ctx;
        unsigned long flags;
 
@@ -7487,7 +7498,7 @@ static void perf_event_exit_task_context(struct task_struct *child, int ctxn)
         */
        mutex_lock(&child_ctx->mutex);
 
-       list_for_each_entry_rcu(child_event, &child_ctx->event_list, event_entry)
+       list_for_each_entry_safe(child_event, next, &child_ctx->event_list, event_entry)
                __perf_event_exit_task(child_event, child_ctx, child);
 
        mutex_unlock(&child_ctx->mutex);
index adcd76a968397a2a2c6f6226dfd10224d6a24db5..c445e392e93ff1a977f2f61ca25dc69de7e61bcc 100644 (file)
@@ -36,6 +36,7 @@
 #include "../../mm/internal.h" /* munlock_vma_page */
 #include <linux/percpu-rwsem.h>
 #include <linux/task_work.h>
+#include <linux/shmem_fs.h>
 
 #include <linux/uprobes.h>
 
@@ -127,7 +128,7 @@ struct xol_area {
  */
 static bool valid_vma(struct vm_area_struct *vma, bool is_register)
 {
-       vm_flags_t flags = VM_HUGETLB | VM_MAYEXEC | VM_SHARED;
+       vm_flags_t flags = VM_HUGETLB | VM_MAYEXEC | VM_MAYSHARE;
 
        if (is_register)
                flags |= VM_WRITE;
@@ -279,18 +280,13 @@ static int verify_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t
  * supported by that architecture then we need to modify is_trap_at_addr and
  * uprobe_write_opcode accordingly. This would never be a problem for archs
  * that have fixed length instructions.
- */
-
-/*
+ *
  * uprobe_write_opcode - write the opcode at a given virtual address.
  * @mm: the probed process address space.
  * @vaddr: the virtual address to store the opcode.
  * @opcode: opcode to be written at @vaddr.
  *
- * Called with mm->mmap_sem held (for read and with a reference to
- * mm).
- *
- * For mm @mm, write the opcode at @vaddr.
+ * Called with mm->mmap_sem held for write.
  * Return 0 (success) or a negative errno.
  */
 int uprobe_write_opcode(struct mm_struct *mm, unsigned long vaddr,
@@ -310,21 +306,25 @@ int uprobe_write_opcode(struct mm_struct *mm, unsigned long vaddr,
        if (ret <= 0)
                goto put_old;
 
+       ret = anon_vma_prepare(vma);
+       if (ret)
+               goto put_old;
+
        ret = -ENOMEM;
        new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr);
        if (!new_page)
                goto put_old;
 
-       __SetPageUptodate(new_page);
+       if (mem_cgroup_charge_anon(new_page, mm, GFP_KERNEL))
+               goto put_new;
 
+       __SetPageUptodate(new_page);
        copy_highpage(new_page, old_page);
        copy_to_page(new_page, vaddr, &opcode, UPROBE_SWBP_INSN_SIZE);
 
-       ret = anon_vma_prepare(vma);
-       if (ret)
-               goto put_new;
-
        ret = __replace_page(vma, vaddr, old_page, new_page);
+       if (ret)
+               mem_cgroup_uncharge_page(new_page);
 
 put_new:
        page_cache_release(new_page);
@@ -537,14 +537,15 @@ static int __copy_insn(struct address_space *mapping, struct file *filp,
                        void *insn, int nbytes, loff_t offset)
 {
        struct page *page;
-
-       if (!mapping->a_ops->readpage)
-               return -EIO;
        /*
-        * Ensure that the page that has the original instruction is
-        * populated and in page-cache.
+        * Ensure that the page that has the original instruction is populated
+        * and in page-cache. If ->readpage == NULL it must be shmem_mapping(),
+        * see uprobe_register().
         */
-       page = read_mapping_page(mapping, offset >> PAGE_CACHE_SHIFT, filp);
+       if (mapping->a_ops->readpage)
+               page = read_mapping_page(mapping, offset >> PAGE_CACHE_SHIFT, filp);
+       else
+               page = shmem_read_mapping_page(mapping, offset >> PAGE_CACHE_SHIFT);
        if (IS_ERR(page))
                return PTR_ERR(page);
 
@@ -880,6 +881,9 @@ int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *
        if (!uc->handler && !uc->ret_handler)
                return -EINVAL;
 
+       /* copy_insn() uses read_mapping_page() or shmem_read_mapping_page() */
+       if (!inode->i_mapping->a_ops->readpage && !shmem_mapping(inode->i_mapping))
+               return -EIO;
        /* Racy, just to catch the obvious mistakes */
        if (offset > i_size_read(inode))
                return -EINVAL;
@@ -1361,6 +1365,16 @@ unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs)
        return instruction_pointer(regs) - UPROBE_SWBP_INSN_SIZE;
 }
 
+unsigned long uprobe_get_trap_addr(struct pt_regs *regs)
+{
+       struct uprobe_task *utask = current->utask;
+
+       if (unlikely(utask && utask->active_uprobe))
+               return utask->vaddr;
+
+       return instruction_pointer(regs);
+}
+
 /*
  * Called with no locks held.
  * Called in context of a exiting or a exec-ing thread.
index ceeadfcabb7611defdbb10f66f3a6b23e60fb0ed..3214289df5a7a8f6917718a9a00f418794efeab1 100644 (file)
@@ -86,21 +86,8 @@ static raw_spinlock_t *kretprobe_table_lock_ptr(unsigned long hash)
        return &(kretprobe_table_locks[hash].lock);
 }
 
-/*
- * Normally, functions that we'd want to prohibit kprobes in, are marked
- * __kprobes. But, there are cases where such functions already belong to
- * a different section (__sched for preempt_schedule)
- *
- * For such cases, we now have a blacklist
- */
-static struct kprobe_blackpoint kprobe_blacklist[] = {
-       {"preempt_schedule",},
-       {"native_get_debugreg",},
-       {"irq_entries_start",},
-       {"common_interrupt",},
-       {"mcount",},    /* mcount can be called from everywhere */
-       {NULL}    /* Terminator */
-};
+/* Blacklist -- list of struct kprobe_blacklist_entry */
+static LIST_HEAD(kprobe_blacklist);
 
 #ifdef __ARCH_WANT_KPROBES_INSN_SLOT
 /*
@@ -151,13 +138,13 @@ struct kprobe_insn_cache kprobe_insn_slots = {
        .insn_size = MAX_INSN_SIZE,
        .nr_garbage = 0,
 };
-static int __kprobes collect_garbage_slots(struct kprobe_insn_cache *c);
+static int collect_garbage_slots(struct kprobe_insn_cache *c);
 
 /**
  * __get_insn_slot() - Find a slot on an executable page for an instruction.
  * We allocate an executable page if there's no room on existing ones.
  */
-kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_cache *c)
+kprobe_opcode_t *__get_insn_slot(struct kprobe_insn_cache *c)
 {
        struct kprobe_insn_page *kip;
        kprobe_opcode_t *slot = NULL;
@@ -214,7 +201,7 @@ kprobe_opcode_t __kprobes *__get_insn_slot(struct kprobe_insn_cache *c)
 }
 
 /* Return 1 if all garbages are collected, otherwise 0. */
-static int __kprobes collect_one_slot(struct kprobe_insn_page *kip, int idx)
+static int collect_one_slot(struct kprobe_insn_page *kip, int idx)
 {
        kip->slot_used[idx] = SLOT_CLEAN;
        kip->nused--;
@@ -235,7 +222,7 @@ static int __kprobes collect_one_slot(struct kprobe_insn_page *kip, int idx)
        return 0;
 }
 
-static int __kprobes collect_garbage_slots(struct kprobe_insn_cache *c)
+static int collect_garbage_slots(struct kprobe_insn_cache *c)
 {
        struct kprobe_insn_page *kip, *next;
 
@@ -257,8 +244,8 @@ static int __kprobes collect_garbage_slots(struct kprobe_insn_cache *c)
        return 0;
 }
 
-void __kprobes __free_insn_slot(struct kprobe_insn_cache *c,
-                               kprobe_opcode_t *slot, int dirty)
+void __free_insn_slot(struct kprobe_insn_cache *c,
+                     kprobe_opcode_t *slot, int dirty)
 {
        struct kprobe_insn_page *kip;
 
@@ -314,7 +301,7 @@ static inline void reset_kprobe_instance(void)
  *                             OR
  *     - with preemption disabled - from arch/xxx/kernel/kprobes.c
  */
-struct kprobe __kprobes *get_kprobe(void *addr)
+struct kprobe *get_kprobe(void *addr)
 {
        struct hlist_head *head;
        struct kprobe *p;
@@ -327,8 +314,9 @@ struct kprobe __kprobes *get_kprobe(void *addr)
 
        return NULL;
 }
+NOKPROBE_SYMBOL(get_kprobe);
 
-static int __kprobes aggr_pre_handler(struct kprobe *p, struct pt_regs *regs);
+static int aggr_pre_handler(struct kprobe *p, struct pt_regs *regs);
 
 /* Return true if the kprobe is an aggregator */
 static inline int kprobe_aggrprobe(struct kprobe *p)
@@ -360,7 +348,7 @@ static bool kprobes_allow_optimization;
  * Call all pre_handler on the list, but ignores its return value.
  * This must be called from arch-dep optimized caller.
  */
-void __kprobes opt_pre_handler(struct kprobe *p, struct pt_regs *regs)
+void opt_pre_handler(struct kprobe *p, struct pt_regs *regs)
 {
        struct kprobe *kp;
 
@@ -372,9 +360,10 @@ void __kprobes opt_pre_handler(struct kprobe *p, struct pt_regs *regs)
                reset_kprobe_instance();
        }
 }
+NOKPROBE_SYMBOL(opt_pre_handler);
 
 /* Free optimized instructions and optimized_kprobe */
-static __kprobes void free_aggr_kprobe(struct kprobe *p)
+static void free_aggr_kprobe(struct kprobe *p)
 {
        struct optimized_kprobe *op;
 
@@ -412,7 +401,7 @@ static inline int kprobe_disarmed(struct kprobe *p)
 }
 
 /* Return true(!0) if the probe is queued on (un)optimizing lists */
-static int __kprobes kprobe_queued(struct kprobe *p)
+static int kprobe_queued(struct kprobe *p)
 {
        struct optimized_kprobe *op;
 
@@ -428,7 +417,7 @@ static int __kprobes kprobe_queued(struct kprobe *p)
  * Return an optimized kprobe whose optimizing code replaces
  * instructions including addr (exclude breakpoint).
  */
-static struct kprobe *__kprobes get_optimized_kprobe(unsigned long addr)
+static struct kprobe *get_optimized_kprobe(unsigned long addr)
 {
        int i;
        struct kprobe *p = NULL;
@@ -460,7 +449,7 @@ static DECLARE_DELAYED_WORK(optimizing_work, kprobe_optimizer);
  * Optimize (replace a breakpoint with a jump) kprobes listed on
  * optimizing_list.
  */
-static __kprobes void do_optimize_kprobes(void)
+static void do_optimize_kprobes(void)
 {
        /* Optimization never be done when disarmed */
        if (kprobes_all_disarmed || !kprobes_allow_optimization ||
@@ -488,7 +477,7 @@ static __kprobes void do_optimize_kprobes(void)
  * Unoptimize (replace a jump with a breakpoint and remove the breakpoint
  * if need) kprobes listed on unoptimizing_list.
  */
-static __kprobes void do_unoptimize_kprobes(void)
+static void do_unoptimize_kprobes(void)
 {
        struct optimized_kprobe *op, *tmp;
 
@@ -520,7 +509,7 @@ static __kprobes void do_unoptimize_kprobes(void)
 }
 
 /* Reclaim all kprobes on the free_list */
-static __kprobes void do_free_cleaned_kprobes(void)
+static void do_free_cleaned_kprobes(void)
 {
        struct optimized_kprobe *op, *tmp;
 
@@ -532,13 +521,13 @@ static __kprobes void do_free_cleaned_kprobes(void)
 }
 
 /* Start optimizer after OPTIMIZE_DELAY passed */
-static __kprobes void kick_kprobe_optimizer(void)
+static void kick_kprobe_optimizer(void)
 {
        schedule_delayed_work(&optimizing_work, OPTIMIZE_DELAY);
 }
 
 /* Kprobe jump optimizer */
-static __kprobes void kprobe_optimizer(struct work_struct *work)
+static void kprobe_optimizer(struct work_struct *work)
 {
        mutex_lock(&kprobe_mutex);
        /* Lock modules while optimizing kprobes */
@@ -574,7 +563,7 @@ static __kprobes void kprobe_optimizer(struct work_struct *work)
 }
 
 /* Wait for completing optimization and unoptimization */
-static __kprobes void wait_for_kprobe_optimizer(void)
+static void wait_for_kprobe_optimizer(void)
 {
        mutex_lock(&kprobe_mutex);
 
@@ -593,7 +582,7 @@ static __kprobes void wait_for_kprobe_optimizer(void)
 }
 
 /* Optimize kprobe if p is ready to be optimized */
-static __kprobes void optimize_kprobe(struct kprobe *p)
+static void optimize_kprobe(struct kprobe *p)
 {
        struct optimized_kprobe *op;
 
@@ -627,7 +616,7 @@ static __kprobes void optimize_kprobe(struct kprobe *p)
 }
 
 /* Short cut to direct unoptimizing */
-static __kprobes void force_unoptimize_kprobe(struct optimized_kprobe *op)
+static void force_unoptimize_kprobe(struct optimized_kprobe *op)
 {
        get_online_cpus();
        arch_unoptimize_kprobe(op);
@@ -637,7 +626,7 @@ static __kprobes void force_unoptimize_kprobe(struct optimized_kprobe *op)
 }
 
 /* Unoptimize a kprobe if p is optimized */
-static __kprobes void unoptimize_kprobe(struct kprobe *p, bool force)
+static void unoptimize_kprobe(struct kprobe *p, bool force)
 {
        struct optimized_kprobe *op;
 
@@ -697,7 +686,7 @@ static void reuse_unused_kprobe(struct kprobe *ap)
 }
 
 /* Remove optimized instructions */
-static void __kprobes kill_optimized_kprobe(struct kprobe *p)
+static void kill_optimized_kprobe(struct kprobe *p)
 {
        struct optimized_kprobe *op;
 
@@ -723,7 +712,7 @@ static void __kprobes kill_optimized_kprobe(struct kprobe *p)
 }
 
 /* Try to prepare optimized instructions */
-static __kprobes void prepare_optimized_kprobe(struct kprobe *p)
+static void prepare_optimized_kprobe(struct kprobe *p)
 {
        struct optimized_kprobe *op;
 
@@ -732,7 +721,7 @@ static __kprobes void prepare_optimized_kprobe(struct kprobe *p)
 }
 
 /* Allocate new optimized_kprobe and try to prepare optimized instructions */
-static __kprobes struct kprobe *alloc_aggr_kprobe(struct kprobe *p)
+static struct kprobe *alloc_aggr_kprobe(struct kprobe *p)
 {
        struct optimized_kprobe *op;
 
@@ -747,13 +736,13 @@ static __kprobes struct kprobe *alloc_aggr_kprobe(struct kprobe *p)
        return &op->kp;
 }
 
-static void __kprobes init_aggr_kprobe(struct kprobe *ap, struct kprobe *p);
+static void init_aggr_kprobe(struct kprobe *ap, struct kprobe *p);
 
 /*
  * Prepare an optimized_kprobe and optimize it
  * NOTE: p must be a normal registered kprobe
  */
-static __kprobes void try_to_optimize_kprobe(struct kprobe *p)
+static void try_to_optimize_kprobe(struct kprobe *p)
 {
        struct kprobe *ap;
        struct optimized_kprobe *op;
@@ -787,7 +776,7 @@ static __kprobes void try_to_optimize_kprobe(struct kprobe *p)
 }
 
 #ifdef CONFIG_SYSCTL
-static void __kprobes optimize_all_kprobes(void)
+static void optimize_all_kprobes(void)
 {
        struct hlist_head *head;
        struct kprobe *p;
@@ -810,7 +799,7 @@ static void __kprobes optimize_all_kprobes(void)
        mutex_unlock(&kprobe_mutex);
 }
 
-static void __kprobes unoptimize_all_kprobes(void)
+static void unoptimize_all_kprobes(void)
 {
        struct hlist_head *head;
        struct kprobe *p;
@@ -861,7 +850,7 @@ int proc_kprobes_optimization_handler(struct ctl_table *table, int write,
 #endif /* CONFIG_SYSCTL */
 
 /* Put a breakpoint for a probe. Must be called with text_mutex locked */
-static void __kprobes __arm_kprobe(struct kprobe *p)
+static void __arm_kprobe(struct kprobe *p)
 {
        struct kprobe *_p;
 
@@ -876,7 +865,7 @@ static void __kprobes __arm_kprobe(struct kprobe *p)
 }
 
 /* Remove the breakpoint of a probe. Must be called with text_mutex locked */
-static void __kprobes __disarm_kprobe(struct kprobe *p, bool reopt)
+static void __disarm_kprobe(struct kprobe *p, bool reopt)
 {
        struct kprobe *_p;
 
@@ -911,13 +900,13 @@ static void reuse_unused_kprobe(struct kprobe *ap)
        BUG_ON(kprobe_unused(ap));
 }
 
-static __kprobes void free_aggr_kprobe(struct kprobe *p)
+static void free_aggr_kprobe(struct kprobe *p)
 {
        arch_remove_kprobe(p);
        kfree(p);
 }
 
-static __kprobes struct kprobe *alloc_aggr_kprobe(struct kprobe *p)
+static struct kprobe *alloc_aggr_kprobe(struct kprobe *p)
 {
        return kzalloc(sizeof(struct kprobe), GFP_KERNEL);
 }
@@ -931,7 +920,7 @@ static struct ftrace_ops kprobe_ftrace_ops __read_mostly = {
 static int kprobe_ftrace_enabled;
 
 /* Must ensure p->addr is really on ftrace */
-static int __kprobes prepare_kprobe(struct kprobe *p)
+static int prepare_kprobe(struct kprobe *p)
 {
        if (!kprobe_ftrace(p))
                return arch_prepare_kprobe(p);
@@ -940,7 +929,7 @@ static int __kprobes prepare_kprobe(struct kprobe *p)
 }
 
 /* Caller must lock kprobe_mutex */
-static void __kprobes arm_kprobe_ftrace(struct kprobe *p)
+static void arm_kprobe_ftrace(struct kprobe *p)
 {
        int ret;
 
@@ -955,7 +944,7 @@ static void __kprobes arm_kprobe_ftrace(struct kprobe *p)
 }
 
 /* Caller must lock kprobe_mutex */
-static void __kprobes disarm_kprobe_ftrace(struct kprobe *p)
+static void disarm_kprobe_ftrace(struct kprobe *p)
 {
        int ret;
 
@@ -975,7 +964,7 @@ static void __kprobes disarm_kprobe_ftrace(struct kprobe *p)
 #endif
 
 /* Arm a kprobe with text_mutex */
-static void __kprobes arm_kprobe(struct kprobe *kp)
+static void arm_kprobe(struct kprobe *kp)
 {
        if (unlikely(kprobe_ftrace(kp))) {
                arm_kprobe_ftrace(kp);
@@ -992,7 +981,7 @@ static void __kprobes arm_kprobe(struct kprobe *kp)
 }
 
 /* Disarm a kprobe with text_mutex */
-static void __kprobes disarm_kprobe(struct kprobe *kp, bool reopt)
+static void disarm_kprobe(struct kprobe *kp, bool reopt)
 {
        if (unlikely(kprobe_ftrace(kp))) {
                disarm_kprobe_ftrace(kp);
@@ -1008,7 +997,7 @@ static void __kprobes disarm_kprobe(struct kprobe *kp, bool reopt)
  * Aggregate handlers for multiple kprobes support - these handlers
  * take care of invoking the individual kprobe handlers on p->list
  */
-static int __kprobes aggr_pre_handler(struct kprobe *p, struct pt_regs *regs)
+static int aggr_pre_handler(struct kprobe *p, struct pt_regs *regs)
 {
        struct kprobe *kp;
 
@@ -1022,9 +1011,10 @@ static int __kprobes aggr_pre_handler(struct kprobe *p, struct pt_regs *regs)
        }
        return 0;
 }
+NOKPROBE_SYMBOL(aggr_pre_handler);
 
-static void __kprobes aggr_post_handler(struct kprobe *p, struct pt_regs *regs,
-                                       unsigned long flags)
+static void aggr_post_handler(struct kprobe *p, struct pt_regs *regs,
+                             unsigned long flags)
 {
        struct kprobe *kp;
 
@@ -1036,9 +1026,10 @@ static void __kprobes aggr_post_handler(struct kprobe *p, struct pt_regs *regs,
                }
        }
 }
+NOKPROBE_SYMBOL(aggr_post_handler);
 
-static int __kprobes aggr_fault_handler(struct kprobe *p, struct pt_regs *regs,
-                                       int trapnr)
+static int aggr_fault_handler(struct kprobe *p, struct pt_regs *regs,
+                             int trapnr)
 {
        struct kprobe *cur = __this_cpu_read(kprobe_instance);
 
@@ -1052,8 +1043,9 @@ static int __kprobes aggr_fault_handler(struct kprobe *p, struct pt_regs *regs,
        }
        return 0;
 }
+NOKPROBE_SYMBOL(aggr_fault_handler);
 
-static int __kprobes aggr_break_handler(struct kprobe *p, struct pt_regs *regs)
+static int aggr_break_handler(struct kprobe *p, struct pt_regs *regs)
 {
        struct kprobe *cur = __this_cpu_read(kprobe_instance);
        int ret = 0;
@@ -1065,9 +1057,10 @@ static int __kprobes aggr_break_handler(struct kprobe *p, struct pt_regs *regs)
        reset_kprobe_instance();
        return ret;
 }
+NOKPROBE_SYMBOL(aggr_break_handler);
 
 /* Walks the list and increments nmissed count for multiprobe case */
-void __kprobes kprobes_inc_nmissed_count(struct kprobe *p)
+void kprobes_inc_nmissed_count(struct kprobe *p)
 {
        struct kprobe *kp;
        if (!kprobe_aggrprobe(p)) {
@@ -1078,9 +1071,10 @@ void __kprobes kprobes_inc_nmissed_count(struct kprobe *p)
        }
        return;
 }
+NOKPROBE_SYMBOL(kprobes_inc_nmissed_count);
 
-void __kprobes recycle_rp_inst(struct kretprobe_instance *ri,
-                               struct hlist_head *head)
+void recycle_rp_inst(struct kretprobe_instance *ri,
+                    struct hlist_head *head)
 {
        struct kretprobe *rp = ri->rp;
 
@@ -1095,8 +1089,9 @@ void __kprobes recycle_rp_inst(struct kretprobe_instance *ri,
                /* Unregistering */
                hlist_add_head(&ri->hlist, head);
 }
+NOKPROBE_SYMBOL(recycle_rp_inst);
 
-void __kprobes kretprobe_hash_lock(struct task_struct *tsk,
+void kretprobe_hash_lock(struct task_struct *tsk,
                         struct hlist_head **head, unsigned long *flags)
 __acquires(hlist_lock)
 {
@@ -1107,17 +1102,19 @@ __acquires(hlist_lock)
        hlist_lock = kretprobe_table_lock_ptr(hash);
        raw_spin_lock_irqsave(hlist_lock, *flags);
 }
+NOKPROBE_SYMBOL(kretprobe_hash_lock);
 
-static void __kprobes kretprobe_table_lock(unsigned long hash,
-       unsigned long *flags)
+static void kretprobe_table_lock(unsigned long hash,
+                                unsigned long *flags)
 __acquires(hlist_lock)
 {
        raw_spinlock_t *hlist_lock = kretprobe_table_lock_ptr(hash);
        raw_spin_lock_irqsave(hlist_lock, *flags);
 }
+NOKPROBE_SYMBOL(kretprobe_table_lock);
 
-void __kprobes kretprobe_hash_unlock(struct task_struct *tsk,
-       unsigned long *flags)
+void kretprobe_hash_unlock(struct task_struct *tsk,
+                          unsigned long *flags)
 __releases(hlist_lock)
 {
        unsigned long hash = hash_ptr(tsk, KPROBE_HASH_BITS);
@@ -1126,14 +1123,16 @@ __releases(hlist_lock)
        hlist_lock = kretprobe_table_lock_ptr(hash);
        raw_spin_unlock_irqrestore(hlist_lock, *flags);
 }
+NOKPROBE_SYMBOL(kretprobe_hash_unlock);
 
-static void __kprobes kretprobe_table_unlock(unsigned long hash,
-       unsigned long *flags)
+static void kretprobe_table_unlock(unsigned long hash,
+                                  unsigned long *flags)
 __releases(hlist_lock)
 {
        raw_spinlock_t *hlist_lock = kretprobe_table_lock_ptr(hash);
        raw_spin_unlock_irqrestore(hlist_lock, *flags);
 }
+NOKPROBE_SYMBOL(kretprobe_table_unlock);
 
 /*
  * This function is called from finish_task_switch when task tk becomes dead,
@@ -1141,7 +1140,7 @@ __releases(hlist_lock)
  * with this task. These left over instances represent probed functions
  * that have been called but will never return.
  */
-void __kprobes kprobe_flush_task(struct task_struct *tk)
+void kprobe_flush_task(struct task_struct *tk)
 {
        struct kretprobe_instance *ri;
        struct hlist_head *head, empty_rp;
@@ -1166,6 +1165,7 @@ void __kprobes kprobe_flush_task(struct task_struct *tk)
                kfree(ri);
        }
 }
+NOKPROBE_SYMBOL(kprobe_flush_task);
 
 static inline void free_rp_inst(struct kretprobe *rp)
 {
@@ -1178,7 +1178,7 @@ static inline void free_rp_inst(struct kretprobe *rp)
        }
 }
 
-static void __kprobes cleanup_rp_inst(struct kretprobe *rp)
+static void cleanup_rp_inst(struct kretprobe *rp)
 {
        unsigned long flags, hash;
        struct kretprobe_instance *ri;
@@ -1197,12 +1197,13 @@ static void __kprobes cleanup_rp_inst(struct kretprobe *rp)
        }
        free_rp_inst(rp);
 }
+NOKPROBE_SYMBOL(cleanup_rp_inst);
 
 /*
 * Add the new probe to ap->list. Fail if this is the
 * second jprobe at the address - two jprobes can't coexist
 */
-static int __kprobes add_new_kprobe(struct kprobe *ap, struct kprobe *p)
+static int add_new_kprobe(struct kprobe *ap, struct kprobe *p)
 {
        BUG_ON(kprobe_gone(ap) || kprobe_gone(p));
 
@@ -1226,7 +1227,7 @@ static int __kprobes add_new_kprobe(struct kprobe *ap, struct kprobe *p)
  * Fill in the required fields of the "manager kprobe". Replace the
  * earlier kprobe in the hlist with the manager kprobe
  */
-static void __kprobes init_aggr_kprobe(struct kprobe *ap, struct kprobe *p)
+static void init_aggr_kprobe(struct kprobe *ap, struct kprobe *p)
 {
        /* Copy p's insn slot to ap */
        copy_kprobe(p, ap);
@@ -1252,8 +1253,7 @@ static void __kprobes init_aggr_kprobe(struct kprobe *ap, struct kprobe *p)
  * This is the second or subsequent kprobe at the address - handle
  * the intricacies
  */
-static int __kprobes register_aggr_kprobe(struct kprobe *orig_p,
-                                         struct kprobe *p)
+static int register_aggr_kprobe(struct kprobe *orig_p, struct kprobe *p)
 {
        int ret = 0;
        struct kprobe *ap = orig_p;
@@ -1324,25 +1324,29 @@ static int __kprobes register_aggr_kprobe(struct kprobe *orig_p,
        return ret;
 }
 
-static int __kprobes in_kprobes_functions(unsigned long addr)
+bool __weak arch_within_kprobe_blacklist(unsigned long addr)
 {
-       struct kprobe_blackpoint *kb;
+       /* The __kprobes marked functions and entry code must not be probed */
+       return addr >= (unsigned long)__kprobes_text_start &&
+              addr < (unsigned long)__kprobes_text_end;
+}
 
-       if (addr >= (unsigned long)__kprobes_text_start &&
-           addr < (unsigned long)__kprobes_text_end)
-               return -EINVAL;
+static bool within_kprobe_blacklist(unsigned long addr)
+{
+       struct kprobe_blacklist_entry *ent;
+
+       if (arch_within_kprobe_blacklist(addr))
+               return true;
        /*
         * If there exists a kprobe_blacklist, verify and
         * fail any probe registration in the prohibited area
         */
-       for (kb = kprobe_blacklist; kb->name != NULL; kb++) {
-               if (kb->start_addr) {
-                       if (addr >= kb->start_addr &&
-                           addr < (kb->start_addr + kb->range))
-                               return -EINVAL;
-               }
+       list_for_each_entry(ent, &kprobe_blacklist, list) {
+               if (addr >= ent->start_addr && addr < ent->end_addr)
+                       return true;
        }
-       return 0;
+
+       return false;
 }
 
 /*
@@ -1351,7 +1355,7 @@ static int __kprobes in_kprobes_functions(unsigned long addr)
  * This returns encoded errors if it fails to look up symbol or invalid
  * combination of parameters.
  */
-static kprobe_opcode_t __kprobes *kprobe_addr(struct kprobe *p)
+static kprobe_opcode_t *kprobe_addr(struct kprobe *p)
 {
        kprobe_opcode_t *addr = p->addr;
 
@@ -1374,7 +1378,7 @@ static kprobe_opcode_t __kprobes *kprobe_addr(struct kprobe *p)
 }
 
 /* Check passed kprobe is valid and return kprobe in kprobe_table. */
-static struct kprobe * __kprobes __get_valid_kprobe(struct kprobe *p)
+static struct kprobe *__get_valid_kprobe(struct kprobe *p)
 {
        struct kprobe *ap, *list_p;
 
@@ -1406,8 +1410,8 @@ static inline int check_kprobe_rereg(struct kprobe *p)
        return ret;
 }
 
-static __kprobes int check_kprobe_address_safe(struct kprobe *p,
-                                              struct module **probed_mod)
+static int check_kprobe_address_safe(struct kprobe *p,
+                                    struct module **probed_mod)
 {
        int ret = 0;
        unsigned long ftrace_addr;
@@ -1433,7 +1437,7 @@ static __kprobes int check_kprobe_address_safe(struct kprobe *p,
 
        /* Ensure it is not in reserved area nor out of text */
        if (!kernel_text_address((unsigned long) p->addr) ||
-           in_kprobes_functions((unsigned long) p->addr) ||
+           within_kprobe_blacklist((unsigned long) p->addr) ||
            jump_label_text_reserved(p->addr, p->addr)) {
                ret = -EINVAL;
                goto out;
@@ -1469,7 +1473,7 @@ static __kprobes int check_kprobe_address_safe(struct kprobe *p,
        return ret;
 }
 
-int __kprobes register_kprobe(struct kprobe *p)
+int register_kprobe(struct kprobe *p)
 {
        int ret;
        struct kprobe *old_p;
@@ -1531,7 +1535,7 @@ int __kprobes register_kprobe(struct kprobe *p)
 EXPORT_SYMBOL_GPL(register_kprobe);
 
 /* Check if all probes on the aggrprobe are disabled */
-static int __kprobes aggr_kprobe_disabled(struct kprobe *ap)
+static int aggr_kprobe_disabled(struct kprobe *ap)
 {
        struct kprobe *kp;
 
@@ -1547,7 +1551,7 @@ static int __kprobes aggr_kprobe_disabled(struct kprobe *ap)
 }
 
 /* Disable one kprobe: Make sure called under kprobe_mutex is locked */
-static struct kprobe *__kprobes __disable_kprobe(struct kprobe *p)
+static struct kprobe *__disable_kprobe(struct kprobe *p)
 {
        struct kprobe *orig_p;
 
@@ -1574,7 +1578,7 @@ static struct kprobe *__kprobes __disable_kprobe(struct kprobe *p)
 /*
  * Unregister a kprobe without a scheduler synchronization.
  */
-static int __kprobes __unregister_kprobe_top(struct kprobe *p)
+static int __unregister_kprobe_top(struct kprobe *p)
 {
        struct kprobe *ap, *list_p;
 
@@ -1631,7 +1635,7 @@ static int __kprobes __unregister_kprobe_top(struct kprobe *p)
        return 0;
 }
 
-static void __kprobes __unregister_kprobe_bottom(struct kprobe *p)
+static void __unregister_kprobe_bottom(struct kprobe *p)
 {
        struct kprobe *ap;
 
@@ -1647,7 +1651,7 @@ static void __kprobes __unregister_kprobe_bottom(struct kprobe *p)
        /* Otherwise, do nothing. */
 }
 
-int __kprobes register_kprobes(struct kprobe **kps, int num)
+int register_kprobes(struct kprobe **kps, int num)
 {
        int i, ret = 0;
 
@@ -1665,13 +1669,13 @@ int __kprobes register_kprobes(struct kprobe **kps, int num)
 }
 EXPORT_SYMBOL_GPL(register_kprobes);
 
-void __kprobes unregister_kprobe(struct kprobe *p)
+void unregister_kprobe(struct kprobe *p)
 {
        unregister_kprobes(&p, 1);
 }
 EXPORT_SYMBOL_GPL(unregister_kprobe);
 
-void __kprobes unregister_kprobes(struct kprobe **kps, int num)
+void unregister_kprobes(struct kprobe **kps, int num)
 {
        int i;
 
@@ -1700,7 +1704,7 @@ unsigned long __weak arch_deref_entry_point(void *entry)
        return (unsigned long)entry;
 }
 
-int __kprobes register_jprobes(struct jprobe **jps, int num)
+int register_jprobes(struct jprobe **jps, int num)
 {
        struct jprobe *jp;
        int ret = 0, i;
@@ -1731,19 +1735,19 @@ int __kprobes register_jprobes(struct jprobe **jps, int num)
 }
 EXPORT_SYMBOL_GPL(register_jprobes);
 
-int __kprobes register_jprobe(struct jprobe *jp)
+int register_jprobe(struct jprobe *jp)
 {
        return register_jprobes(&jp, 1);
 }
 EXPORT_SYMBOL_GPL(register_jprobe);
 
-void __kprobes unregister_jprobe(struct jprobe *jp)
+void unregister_jprobe(struct jprobe *jp)
 {
        unregister_jprobes(&jp, 1);
 }
 EXPORT_SYMBOL_GPL(unregister_jprobe);
 
-void __kprobes unregister_jprobes(struct jprobe **jps, int num)
+void unregister_jprobes(struct jprobe **jps, int num)
 {
        int i;
 
@@ -1768,8 +1772,7 @@ EXPORT_SYMBOL_GPL(unregister_jprobes);
  * This kprobe pre_handler is registered with every kretprobe. When probe
  * hits it will set up the return probe.
  */
-static int __kprobes pre_handler_kretprobe(struct kprobe *p,
-                                          struct pt_regs *regs)
+static int pre_handler_kretprobe(struct kprobe *p, struct pt_regs *regs)
 {
        struct kretprobe *rp = container_of(p, struct kretprobe, kp);
        unsigned long hash, flags = 0;
@@ -1807,8 +1810,9 @@ static int __kprobes pre_handler_kretprobe(struct kprobe *p,
        }
        return 0;
 }
+NOKPROBE_SYMBOL(pre_handler_kretprobe);
 
-int __kprobes register_kretprobe(struct kretprobe *rp)
+int register_kretprobe(struct kretprobe *rp)
 {
        int ret = 0;
        struct kretprobe_instance *inst;
@@ -1861,7 +1865,7 @@ int __kprobes register_kretprobe(struct kretprobe *rp)
 }
 EXPORT_SYMBOL_GPL(register_kretprobe);
 
-int __kprobes register_kretprobes(struct kretprobe **rps, int num)
+int register_kretprobes(struct kretprobe **rps, int num)
 {
        int ret = 0, i;
 
@@ -1879,13 +1883,13 @@ int __kprobes register_kretprobes(struct kretprobe **rps, int num)
 }
 EXPORT_SYMBOL_GPL(register_kretprobes);
 
-void __kprobes unregister_kretprobe(struct kretprobe *rp)
+void unregister_kretprobe(struct kretprobe *rp)
 {
        unregister_kretprobes(&rp, 1);
 }
 EXPORT_SYMBOL_GPL(unregister_kretprobe);
 
-void __kprobes unregister_kretprobes(struct kretprobe **rps, int num)
+void unregister_kretprobes(struct kretprobe **rps, int num)
 {
        int i;
 
@@ -1908,38 +1912,38 @@ void __kprobes unregister_kretprobes(struct kretprobe **rps, int num)
 EXPORT_SYMBOL_GPL(unregister_kretprobes);
 
 #else /* CONFIG_KRETPROBES */
-int __kprobes register_kretprobe(struct kretprobe *rp)
+int register_kretprobe(struct kretprobe *rp)
 {
        return -ENOSYS;
 }
 EXPORT_SYMBOL_GPL(register_kretprobe);
 
-int __kprobes register_kretprobes(struct kretprobe **rps, int num)
+int register_kretprobes(struct kretprobe **rps, int num)
 {
        return -ENOSYS;
 }
 EXPORT_SYMBOL_GPL(register_kretprobes);
 
-void __kprobes unregister_kretprobe(struct kretprobe *rp)
+void unregister_kretprobe(struct kretprobe *rp)
 {
 }
 EXPORT_SYMBOL_GPL(unregister_kretprobe);
 
-void __kprobes unregister_kretprobes(struct kretprobe **rps, int num)
+void unregister_kretprobes(struct kretprobe **rps, int num)
 {
 }
 EXPORT_SYMBOL_GPL(unregister_kretprobes);
 
-static int __kprobes pre_handler_kretprobe(struct kprobe *p,
-                                          struct pt_regs *regs)
+static int pre_handler_kretprobe(struct kprobe *p, struct pt_regs *regs)
 {
        return 0;
 }
+NOKPROBE_SYMBOL(pre_handler_kretprobe);
 
 #endif /* CONFIG_KRETPROBES */
 
 /* Set the kprobe gone and remove its instruction buffer. */
-static void __kprobes kill_kprobe(struct kprobe *p)
+static void kill_kprobe(struct kprobe *p)
 {
        struct kprobe *kp;
 
@@ -1963,7 +1967,7 @@ static void __kprobes kill_kprobe(struct kprobe *p)
 }
 
 /* Disable one kprobe */
-int __kprobes disable_kprobe(struct kprobe *kp)
+int disable_kprobe(struct kprobe *kp)
 {
        int ret = 0;
 
@@ -1979,7 +1983,7 @@ int __kprobes disable_kprobe(struct kprobe *kp)
 EXPORT_SYMBOL_GPL(disable_kprobe);
 
 /* Enable one kprobe */
-int __kprobes enable_kprobe(struct kprobe *kp)
+int enable_kprobe(struct kprobe *kp)
 {
        int ret = 0;
        struct kprobe *p;
@@ -2012,16 +2016,49 @@ int __kprobes enable_kprobe(struct kprobe *kp)
 }
 EXPORT_SYMBOL_GPL(enable_kprobe);
 
-void __kprobes dump_kprobe(struct kprobe *kp)
+void dump_kprobe(struct kprobe *kp)
 {
        printk(KERN_WARNING "Dumping kprobe:\n");
        printk(KERN_WARNING "Name: %s\nAddress: %p\nOffset: %x\n",
               kp->symbol_name, kp->addr, kp->offset);
 }
+NOKPROBE_SYMBOL(dump_kprobe);
+
+/*
+ * Lookup and populate the kprobe_blacklist.
+ *
+ * Unlike the kretprobe blacklist, we'll need to determine
+ * the range of addresses that belong to the said functions,
+ * since a kprobe need not necessarily be at the beginning
+ * of a function.
+ */
+static int __init populate_kprobe_blacklist(unsigned long *start,
+                                            unsigned long *end)
+{
+       unsigned long *iter;
+       struct kprobe_blacklist_entry *ent;
+       unsigned long offset = 0, size = 0;
+
+       for (iter = start; iter < end; iter++) {
+               if (!kallsyms_lookup_size_offset(*iter, &size, &offset)) {
+                       pr_err("Failed to find blacklist %p\n", (void *)*iter);
+                       continue;
+               }
+
+               ent = kmalloc(sizeof(*ent), GFP_KERNEL);
+               if (!ent)
+                       return -ENOMEM;
+               ent->start_addr = *iter;
+               ent->end_addr = *iter + size;
+               INIT_LIST_HEAD(&ent->list);
+               list_add_tail(&ent->list, &kprobe_blacklist);
+       }
+       return 0;
+}
 
 /* Module notifier call back, checking kprobes on the module */
-static int __kprobes kprobes_module_callback(struct notifier_block *nb,
-                                            unsigned long val, void *data)
+static int kprobes_module_callback(struct notifier_block *nb,
+                                  unsigned long val, void *data)
 {
        struct module *mod = data;
        struct hlist_head *head;
@@ -2062,14 +2099,13 @@ static struct notifier_block kprobe_module_nb = {
        .priority = 0
 };
 
+/* Markers of _kprobe_blacklist section */
+extern unsigned long __start_kprobe_blacklist[];
+extern unsigned long __stop_kprobe_blacklist[];
+
 static int __init init_kprobes(void)
 {
        int i, err = 0;
-       unsigned long offset = 0, size = 0;
-       char *modname, namebuf[KSYM_NAME_LEN];
-       const char *symbol_name;
-       void *addr;
-       struct kprobe_blackpoint *kb;
 
        /* FIXME allocate the probe table, currently defined statically */
        /* initialize all list heads */
@@ -2079,26 +2115,11 @@ static int __init init_kprobes(void)
                raw_spin_lock_init(&(kretprobe_table_locks[i].lock));
        }
 
-       /*
-        * Lookup and populate the kprobe_blacklist.
-        *
-        * Unlike the kretprobe blacklist, we'll need to determine
-        * the range of addresses that belong to the said functions,
-        * since a kprobe need not necessarily be at the beginning
-        * of a function.
-        */
-       for (kb = kprobe_blacklist; kb->name != NULL; kb++) {
-               kprobe_lookup_name(kb->name, addr);
-               if (!addr)
-                       continue;
-
-               kb->start_addr = (unsigned long)addr;
-               symbol_name = kallsyms_lookup(kb->start_addr,
-                               &size, &offset, &modname, namebuf);
-               if (!symbol_name)
-                       kb->range = 0;
-               else
-                       kb->range = size;
+       err = populate_kprobe_blacklist(__start_kprobe_blacklist,
+                                       __stop_kprobe_blacklist);
+       if (err) {
+               pr_err("kprobes: failed to populate blacklist: %d\n", err);
+               pr_err("Please take care of using kprobes.\n");
        }
 
        if (kretprobe_blacklist_size) {
@@ -2138,7 +2159,7 @@ static int __init init_kprobes(void)
 }
 
 #ifdef CONFIG_DEBUG_FS
-static void __kprobes report_probe(struct seq_file *pi, struct kprobe *p,
+static void report_probe(struct seq_file *pi, struct kprobe *p,
                const char *sym, int offset, char *modname, struct kprobe *pp)
 {
        char *kprobe_type;
@@ -2167,12 +2188,12 @@ static void __kprobes report_probe(struct seq_file *pi, struct kprobe *p,
                (kprobe_ftrace(pp) ? "[FTRACE]" : ""));
 }
 
-static void __kprobes *kprobe_seq_start(struct seq_file *f, loff_t *pos)
+static void *kprobe_seq_start(struct seq_file *f, loff_t *pos)
 {
        return (*pos < KPROBE_TABLE_SIZE) ? pos : NULL;
 }
 
-static void __kprobes *kprobe_seq_next(struct seq_file *f, void *v, loff_t *pos)
+static void *kprobe_seq_next(struct seq_file *f, void *v, loff_t *pos)
 {
        (*pos)++;
        if (*pos >= KPROBE_TABLE_SIZE)
@@ -2180,12 +2201,12 @@ static void __kprobes *kprobe_seq_next(struct seq_file *f, void *v, loff_t *pos)
        return pos;
 }
 
-static void __kprobes kprobe_seq_stop(struct seq_file *f, void *v)
+static void kprobe_seq_stop(struct seq_file *f, void *v)
 {
        /* Nothing to do */
 }
 
-static int __kprobes show_kprobe_addr(struct seq_file *pi, void *v)
+static int show_kprobe_addr(struct seq_file *pi, void *v)
 {
        struct hlist_head *head;
        struct kprobe *p, *kp;
@@ -2216,7 +2237,7 @@ static const struct seq_operations kprobes_seq_ops = {
        .show  = show_kprobe_addr
 };
 
-static int __kprobes kprobes_open(struct inode *inode, struct file *filp)
+static int kprobes_open(struct inode *inode, struct file *filp)
 {
        return seq_open(filp, &kprobes_seq_ops);
 }
@@ -2228,7 +2249,47 @@ static const struct file_operations debugfs_kprobes_operations = {
        .release        = seq_release,
 };
 
-static void __kprobes arm_all_kprobes(void)
+/* kprobes/blacklist -- shows which functions can not be probed */
+static void *kprobe_blacklist_seq_start(struct seq_file *m, loff_t *pos)
+{
+       return seq_list_start(&kprobe_blacklist, *pos);
+}
+
+static void *kprobe_blacklist_seq_next(struct seq_file *m, void *v, loff_t *pos)
+{
+       return seq_list_next(v, &kprobe_blacklist, pos);
+}
+
+static int kprobe_blacklist_seq_show(struct seq_file *m, void *v)
+{
+       struct kprobe_blacklist_entry *ent =
+               list_entry(v, struct kprobe_blacklist_entry, list);
+
+       seq_printf(m, "0x%p-0x%p\t%ps\n", (void *)ent->start_addr,
+                  (void *)ent->end_addr, (void *)ent->start_addr);
+       return 0;
+}
+
+static const struct seq_operations kprobe_blacklist_seq_ops = {
+       .start = kprobe_blacklist_seq_start,
+       .next  = kprobe_blacklist_seq_next,
+       .stop  = kprobe_seq_stop,       /* Reuse void function */
+       .show  = kprobe_blacklist_seq_show,
+};
+
+static int kprobe_blacklist_open(struct inode *inode, struct file *filp)
+{
+       return seq_open(filp, &kprobe_blacklist_seq_ops);
+}
+
+static const struct file_operations debugfs_kprobe_blacklist_ops = {
+       .open           = kprobe_blacklist_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = seq_release,
+};
+
+static void arm_all_kprobes(void)
 {
        struct hlist_head *head;
        struct kprobe *p;
@@ -2256,7 +2317,7 @@ static void __kprobes arm_all_kprobes(void)
        return;
 }
 
-static void __kprobes disarm_all_kprobes(void)
+static void disarm_all_kprobes(void)
 {
        struct hlist_head *head;
        struct kprobe *p;
@@ -2340,7 +2401,7 @@ static const struct file_operations fops_kp = {
        .llseek =       default_llseek,
 };
 
-static int __kprobes debugfs_kprobe_init(void)
+static int __init debugfs_kprobe_init(void)
 {
        struct dentry *dir, *file;
        unsigned int value = 1;
@@ -2351,19 +2412,24 @@ static int __kprobes debugfs_kprobe_init(void)
 
        file = debugfs_create_file("list", 0444, dir, NULL,
                                &debugfs_kprobes_operations);
-       if (!file) {
-               debugfs_remove(dir);
-               return -ENOMEM;
-       }
+       if (!file)
+               goto error;
 
        file = debugfs_create_file("enabled", 0600, dir,
                                        &value, &fops_kp);
-       if (!file) {
-               debugfs_remove(dir);
-               return -ENOMEM;
-       }
+       if (!file)
+               goto error;
+
+       file = debugfs_create_file("blacklist", 0444, dir, NULL,
+                               &debugfs_kprobe_blacklist_ops);
+       if (!file)
+               goto error;
 
        return 0;
+
+error:
+       debugfs_remove(dir);
+       return -ENOMEM;
 }
 
 late_initcall(debugfs_kprobe_init);
index b8bdcd4785b767d5fc1ee732d778b07ee74cf102..8541bfdfd232bb4213629f265cbb68a6bfb50c72 100644 (file)
@@ -24,4 +24,5 @@ obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock_debug.o
 obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
 obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem-xadd.o
 obj-$(CONFIG_PERCPU_RWSEM) += percpu-rwsem.o
+obj-$(CONFIG_QUEUE_RWLOCK) += qrwlock.o
 obj-$(CONFIG_LOCK_TORTURE_TEST) += locktorture.o
diff --git a/kernel/locking/qrwlock.c b/kernel/locking/qrwlock.c
new file mode 100644 (file)
index 0000000..fb5b8ac
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Queue read/write lock
+ *
+ * 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.
+ *
+ * (C) Copyright 2013-2014 Hewlett-Packard Development Company, L.P.
+ *
+ * Authors: Waiman Long <waiman.long@hp.com>
+ */
+#include <linux/smp.h>
+#include <linux/bug.h>
+#include <linux/cpumask.h>
+#include <linux/percpu.h>
+#include <linux/hardirq.h>
+#include <linux/mutex.h>
+#include <asm/qrwlock.h>
+
+/**
+ * rspin_until_writer_unlock - inc reader count & spin until writer is gone
+ * @lock  : Pointer to queue rwlock structure
+ * @writer: Current queue rwlock writer status byte
+ *
+ * In interrupt context or at the head of the queue, the reader will just
+ * increment the reader count & wait until the writer releases the lock.
+ */
+static __always_inline void
+rspin_until_writer_unlock(struct qrwlock *lock, u32 cnts)
+{
+       while ((cnts & _QW_WMASK) == _QW_LOCKED) {
+               arch_mutex_cpu_relax();
+               cnts = smp_load_acquire((u32 *)&lock->cnts);
+       }
+}
+
+/**
+ * queue_read_lock_slowpath - acquire read lock of a queue rwlock
+ * @lock: Pointer to queue rwlock structure
+ */
+void queue_read_lock_slowpath(struct qrwlock *lock)
+{
+       u32 cnts;
+
+       /*
+        * Readers come here when they cannot get the lock without waiting
+        */
+       if (unlikely(in_interrupt())) {
+               /*
+                * Readers in interrupt context will spin until the lock is
+                * available without waiting in the queue.
+                */
+               cnts = smp_load_acquire((u32 *)&lock->cnts);
+               rspin_until_writer_unlock(lock, cnts);
+               return;
+       }
+       atomic_sub(_QR_BIAS, &lock->cnts);
+
+       /*
+        * Put the reader into the wait queue
+        */
+       arch_spin_lock(&lock->lock);
+
+       /*
+        * At the head of the wait queue now, wait until the writer state
+        * goes to 0 and then try to increment the reader count and get
+        * the lock. It is possible that an incoming writer may steal the
+        * lock in the interim, so it is necessary to check the writer byte
+        * to make sure that the write lock isn't taken.
+        */
+       while (atomic_read(&lock->cnts) & _QW_WMASK)
+               arch_mutex_cpu_relax();
+
+       cnts = atomic_add_return(_QR_BIAS, &lock->cnts) - _QR_BIAS;
+       rspin_until_writer_unlock(lock, cnts);
+
+       /*
+        * Signal the next one in queue to become queue head
+        */
+       arch_spin_unlock(&lock->lock);
+}
+EXPORT_SYMBOL(queue_read_lock_slowpath);
+
+/**
+ * queue_write_lock_slowpath - acquire write lock of a queue rwlock
+ * @lock : Pointer to queue rwlock structure
+ */
+void queue_write_lock_slowpath(struct qrwlock *lock)
+{
+       u32 cnts;
+
+       /* Put the writer into the wait queue */
+       arch_spin_lock(&lock->lock);
+
+       /* Try to acquire the lock directly if no reader is present */
+       if (!atomic_read(&lock->cnts) &&
+           (atomic_cmpxchg(&lock->cnts, 0, _QW_LOCKED) == 0))
+               goto unlock;
+
+       /*
+        * Set the waiting flag to notify readers that a writer is pending,
+        * or wait for a previous writer to go away.
+        */
+       for (;;) {
+               cnts = atomic_read(&lock->cnts);
+               if (!(cnts & _QW_WMASK) &&
+                   (atomic_cmpxchg(&lock->cnts, cnts,
+                                   cnts | _QW_WAITING) == cnts))
+                       break;
+
+               arch_mutex_cpu_relax();
+       }
+
+       /* When no more readers, set the locked flag */
+       for (;;) {
+               cnts = atomic_read(&lock->cnts);
+               if ((cnts == _QW_WAITING) &&
+                   (atomic_cmpxchg(&lock->cnts, _QW_WAITING,
+                                   _QW_LOCKED) == _QW_WAITING))
+                       break;
+
+               arch_mutex_cpu_relax();
+       }
+unlock:
+       arch_spin_unlock(&lock->lock);
+}
+EXPORT_SYMBOL(queue_write_lock_slowpath);
index b4219ff87b8c6dc95ae85bb2a4f38118681c0df6..dacc32142fccaec5222ba82c01801ed7768a819f 100644 (file)
@@ -5,11 +5,17 @@
  *
  * Writer lock-stealing by Alex Shi <alex.shi@intel.com>
  * and Michel Lespinasse <walken@google.com>
+ *
+ * Optimistic spinning by Tim Chen <tim.c.chen@intel.com>
+ * and Davidlohr Bueso <davidlohr@hp.com>. Based on mutexes.
  */
 #include <linux/rwsem.h>
 #include <linux/sched.h>
 #include <linux/init.h>
 #include <linux/export.h>
+#include <linux/sched/rt.h>
+
+#include "mcs_spinlock.h"
 
 /*
  * Guide to the rw_semaphore's count field for common values.
@@ -76,6 +82,10 @@ void __init_rwsem(struct rw_semaphore *sem, const char *name,
        sem->count = RWSEM_UNLOCKED_VALUE;
        raw_spin_lock_init(&sem->wait_lock);
        INIT_LIST_HEAD(&sem->wait_list);
+#ifdef CONFIG_SMP
+       sem->owner = NULL;
+       sem->osq = NULL;
+#endif
 }
 
 EXPORT_SYMBOL(__init_rwsem);
@@ -190,7 +200,7 @@ __rwsem_do_wake(struct rw_semaphore *sem, enum rwsem_wake_type wake_type)
 }
 
 /*
- * wait for the read lock to be granted
+ * Wait for the read lock to be granted
  */
 __visible
 struct rw_semaphore __sched *rwsem_down_read_failed(struct rw_semaphore *sem)
@@ -237,64 +247,221 @@ struct rw_semaphore __sched *rwsem_down_read_failed(struct rw_semaphore *sem)
        return sem;
 }
 
+static inline bool rwsem_try_write_lock(long count, struct rw_semaphore *sem)
+{
+       if (!(count & RWSEM_ACTIVE_MASK)) {
+               /* try acquiring the write lock */
+               if (sem->count == RWSEM_WAITING_BIAS &&
+                   cmpxchg(&sem->count, RWSEM_WAITING_BIAS,
+                           RWSEM_ACTIVE_WRITE_BIAS) == RWSEM_WAITING_BIAS) {
+                       if (!list_is_singular(&sem->wait_list))
+                               rwsem_atomic_update(RWSEM_WAITING_BIAS, sem);
+                       return true;
+               }
+       }
+       return false;
+}
+
+#ifdef CONFIG_SMP
 /*
- * wait until we successfully acquire the write lock
+ * Try to acquire write lock before the writer has been put on wait queue.
+ */
+static inline bool rwsem_try_write_lock_unqueued(struct rw_semaphore *sem)
+{
+       long old, count = ACCESS_ONCE(sem->count);
+
+       while (true) {
+               if (!(count == 0 || count == RWSEM_WAITING_BIAS))
+                       return false;
+
+               old = cmpxchg(&sem->count, count, count + RWSEM_ACTIVE_WRITE_BIAS);
+               if (old == count)
+                       return true;
+
+               count = old;
+       }
+}
+
+static inline bool rwsem_can_spin_on_owner(struct rw_semaphore *sem)
+{
+       struct task_struct *owner;
+       bool on_cpu = true;
+
+       if (need_resched())
+               return 0;
+
+       rcu_read_lock();
+       owner = ACCESS_ONCE(sem->owner);
+       if (owner)
+               on_cpu = owner->on_cpu;
+       rcu_read_unlock();
+
+       /*
+        * If sem->owner is not set, the rwsem owner may have
+        * just acquired it and not set the owner yet or the rwsem
+        * has been released.
+        */
+       return on_cpu;
+}
+
+static inline bool owner_running(struct rw_semaphore *sem,
+                                struct task_struct *owner)
+{
+       if (sem->owner != owner)
+               return false;
+
+       /*
+        * Ensure we emit the owner->on_cpu, dereference _after_ checking
+        * sem->owner still matches owner, if that fails, owner might
+        * point to free()d memory, if it still matches, the rcu_read_lock()
+        * ensures the memory stays valid.
+        */
+       barrier();
+
+       return owner->on_cpu;
+}
+
+static noinline
+bool rwsem_spin_on_owner(struct rw_semaphore *sem, struct task_struct *owner)
+{
+       rcu_read_lock();
+       while (owner_running(sem, owner)) {
+               if (need_resched())
+                       break;
+
+               arch_mutex_cpu_relax();
+       }
+       rcu_read_unlock();
+
+       /*
+        * We break out the loop above on need_resched() or when the
+        * owner changed, which is a sign for heavy contention. Return
+        * success only when sem->owner is NULL.
+        */
+       return sem->owner == NULL;
+}
+
+static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
+{
+       struct task_struct *owner;
+       bool taken = false;
+
+       preempt_disable();
+
+       /* sem->wait_lock should not be held when doing optimistic spinning */
+       if (!rwsem_can_spin_on_owner(sem))
+               goto done;
+
+       if (!osq_lock(&sem->osq))
+               goto done;
+
+       while (true) {
+               owner = ACCESS_ONCE(sem->owner);
+               if (owner && !rwsem_spin_on_owner(sem, owner))
+                       break;
+
+               /* wait_lock will be acquired if write_lock is obtained */
+               if (rwsem_try_write_lock_unqueued(sem)) {
+                       taken = true;
+                       break;
+               }
+
+               /*
+                * When there's no owner, we might have preempted between the
+                * owner acquiring the lock and setting the owner field. If
+                * we're an RT task that will live-lock because we won't let
+                * the owner complete.
+                */
+               if (!owner && (need_resched() || rt_task(current)))
+                       break;
+
+               /*
+                * The cpu_relax() call is a compiler barrier which forces
+                * everything in this loop to be re-loaded. We don't need
+                * memory barriers as we'll eventually observe the right
+                * values at the cost of a few extra spins.
+                */
+               arch_mutex_cpu_relax();
+       }
+       osq_unlock(&sem->osq);
+done:
+       preempt_enable();
+       return taken;
+}
+
+#else
+static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
+{
+       return false;
+}
+#endif
+
+/*
+ * Wait until we successfully acquire the write lock
  */
 __visible
 struct rw_semaphore __sched *rwsem_down_write_failed(struct rw_semaphore *sem)
 {
-       long count, adjustment = -RWSEM_ACTIVE_WRITE_BIAS;
+       long count;
+       bool waiting = true; /* any queued threads before us */
        struct rwsem_waiter waiter;
-       struct task_struct *tsk = current;
 
-       /* set up my own style of waitqueue */
-       waiter.task = tsk;
+       /* undo write bias from down_write operation, stop active locking */
+       count = rwsem_atomic_update(-RWSEM_ACTIVE_WRITE_BIAS, sem);
+
+       /* do optimistic spinning and steal lock if possible */
+       if (rwsem_optimistic_spin(sem))
+               return sem;
+
+       /*
+        * Optimistic spinning failed, proceed to the slowpath
+        * and block until we can acquire the sem.
+        */
+       waiter.task = current;
        waiter.type = RWSEM_WAITING_FOR_WRITE;
 
        raw_spin_lock_irq(&sem->wait_lock);
+
+       /* account for this before adding a new element to the list */
        if (list_empty(&sem->wait_list))
-               adjustment += RWSEM_WAITING_BIAS;
+               waiting = false;
+
        list_add_tail(&waiter.list, &sem->wait_list);
 
        /* we're now waiting on the lock, but no longer actively locking */
-       count = rwsem_atomic_update(adjustment, sem);
+       if (waiting) {
+               count = ACCESS_ONCE(sem->count);
 
-       /* If there were already threads queued before us and there are no
-        * active writers, the lock must be read owned; so we try to wake
-        * any read locks that were queued ahead of us. */
-       if (count > RWSEM_WAITING_BIAS &&
-           adjustment == -RWSEM_ACTIVE_WRITE_BIAS)
-               sem = __rwsem_do_wake(sem, RWSEM_WAKE_READERS);
+               /*
+                * If there were already threads queued before us and there are
+                * no active writers, the lock must be read owned; so we try to
+                * wake any read locks that were queued ahead of us.
+                */
+               if (count > RWSEM_WAITING_BIAS)
+                       sem = __rwsem_do_wake(sem, RWSEM_WAKE_READERS);
+
+       } else
+               count = rwsem_atomic_update(RWSEM_WAITING_BIAS, sem);
 
        /* wait until we successfully acquire the lock */
-       set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+       set_current_state(TASK_UNINTERRUPTIBLE);
        while (true) {
-               if (!(count & RWSEM_ACTIVE_MASK)) {
-                       /* Try acquiring the write lock. */
-                       count = RWSEM_ACTIVE_WRITE_BIAS;
-                       if (!list_is_singular(&sem->wait_list))
-                               count += RWSEM_WAITING_BIAS;
-
-                       if (sem->count == RWSEM_WAITING_BIAS &&
-                           cmpxchg(&sem->count, RWSEM_WAITING_BIAS, count) ==
-                                                       RWSEM_WAITING_BIAS)
-                               break;
-               }
-
+               if (rwsem_try_write_lock(count, sem))
+                       break;
                raw_spin_unlock_irq(&sem->wait_lock);
 
                /* Block until there are no active lockers. */
                do {
                        schedule();
-                       set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+                       set_current_state(TASK_UNINTERRUPTIBLE);
                } while ((count = sem->count) & RWSEM_ACTIVE_MASK);
 
                raw_spin_lock_irq(&sem->wait_lock);
        }
+       __set_current_state(TASK_RUNNING);
 
        list_del(&waiter.list);
        raw_spin_unlock_irq(&sem->wait_lock);
-       tsk->state = TASK_RUNNING;
 
        return sem;
 }
index cfff1435bdfb2f1e6d8a88d797c2a205f9145daf..42f806de49d421092a7bd077c8efb4df9546cb94 100644 (file)
 
 #include <linux/atomic.h>
 
+#if defined(CONFIG_SMP) && defined(CONFIG_RWSEM_XCHGADD_ALGORITHM)
+static inline void rwsem_set_owner(struct rw_semaphore *sem)
+{
+       sem->owner = current;
+}
+
+static inline void rwsem_clear_owner(struct rw_semaphore *sem)
+{
+       sem->owner = NULL;
+}
+
+#else
+static inline void rwsem_set_owner(struct rw_semaphore *sem)
+{
+}
+
+static inline void rwsem_clear_owner(struct rw_semaphore *sem)
+{
+}
+#endif
+
 /*
  * lock for reading
  */
@@ -48,6 +69,7 @@ void __sched down_write(struct rw_semaphore *sem)
        rwsem_acquire(&sem->dep_map, 0, 0, _RET_IP_);
 
        LOCK_CONTENDED(sem, __down_write_trylock, __down_write);
+       rwsem_set_owner(sem);
 }
 
 EXPORT_SYMBOL(down_write);
@@ -59,8 +81,11 @@ int down_write_trylock(struct rw_semaphore *sem)
 {
        int ret = __down_write_trylock(sem);
 
-       if (ret == 1)
+       if (ret == 1) {
                rwsem_acquire(&sem->dep_map, 0, 1, _RET_IP_);
+               rwsem_set_owner(sem);
+       }
+
        return ret;
 }
 
@@ -85,6 +110,7 @@ void up_write(struct rw_semaphore *sem)
 {
        rwsem_release(&sem->dep_map, 1, _RET_IP_);
 
+       rwsem_clear_owner(sem);
        __up_write(sem);
 }
 
@@ -99,6 +125,7 @@ void downgrade_write(struct rw_semaphore *sem)
         * lockdep: a downgraded write will live on as a write
         * dependency.
         */
+       rwsem_clear_owner(sem);
        __downgrade_write(sem);
 }
 
@@ -122,6 +149,7 @@ void _down_write_nest_lock(struct rw_semaphore *sem, struct lockdep_map *nest)
        rwsem_acquire_nest(&sem->dep_map, 0, 0, nest, _RET_IP_);
 
        LOCK_CONTENDED(sem, __down_write_trylock, __down_write);
+       rwsem_set_owner(sem);
 }
 
 EXPORT_SYMBOL(_down_write_nest_lock);
@@ -141,6 +169,7 @@ void down_write_nested(struct rw_semaphore *sem, int subclass)
        rwsem_acquire(&sem->dep_map, subclass, 0, _RET_IP_);
 
        LOCK_CONTENDED(sem, __down_write_trylock, __down_write);
+       rwsem_set_owner(sem);
 }
 
 EXPORT_SYMBOL(down_write_nested);
index db4c8b08a50cef986f48f0e537f1b622d4e8b19a..4803da6eab62f182354707c10f48be35a8b54fb5 100644 (file)
@@ -71,9 +71,9 @@ static int notifier_chain_unregister(struct notifier_block **nl,
  *     @returns:       notifier_call_chain returns the value returned by the
  *                     last notifier function called.
  */
-static int __kprobes notifier_call_chain(struct notifier_block **nl,
-                                       unsigned long val, void *v,
-                                       int nr_to_call, int *nr_calls)
+static int notifier_call_chain(struct notifier_block **nl,
+                              unsigned long val, void *v,
+                              int nr_to_call, int *nr_calls)
 {
        int ret = NOTIFY_DONE;
        struct notifier_block *nb, *next_nb;
@@ -102,6 +102,7 @@ static int __kprobes notifier_call_chain(struct notifier_block **nl,
        }
        return ret;
 }
+NOKPROBE_SYMBOL(notifier_call_chain);
 
 /*
  *     Atomic notifier chain routines.  Registration and unregistration
@@ -172,9 +173,9 @@ EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister);
  *     Otherwise the return value is the return value
  *     of the last notifier function called.
  */
-int __kprobes __atomic_notifier_call_chain(struct atomic_notifier_head *nh,
-                                       unsigned long val, void *v,
-                                       int nr_to_call, int *nr_calls)
+int __atomic_notifier_call_chain(struct atomic_notifier_head *nh,
+                                unsigned long val, void *v,
+                                int nr_to_call, int *nr_calls)
 {
        int ret;
 
@@ -184,13 +185,15 @@ int __kprobes __atomic_notifier_call_chain(struct atomic_notifier_head *nh,
        return ret;
 }
 EXPORT_SYMBOL_GPL(__atomic_notifier_call_chain);
+NOKPROBE_SYMBOL(__atomic_notifier_call_chain);
 
-int __kprobes atomic_notifier_call_chain(struct atomic_notifier_head *nh,
-               unsigned long val, void *v)
+int atomic_notifier_call_chain(struct atomic_notifier_head *nh,
+                              unsigned long val, void *v)
 {
        return __atomic_notifier_call_chain(nh, val, v, -1, NULL);
 }
 EXPORT_SYMBOL_GPL(atomic_notifier_call_chain);
+NOKPROBE_SYMBOL(atomic_notifier_call_chain);
 
 /*
  *     Blocking notifier chain routines.  All access to the chain is
@@ -527,7 +530,7 @@ EXPORT_SYMBOL_GPL(srcu_init_notifier_head);
 
 static ATOMIC_NOTIFIER_HEAD(die_chain);
 
-int notrace __kprobes notify_die(enum die_val val, const char *str,
+int notrace notify_die(enum die_val val, const char *str,
               struct pt_regs *regs, long err, int trap, int sig)
 {
        struct die_args args = {
@@ -540,6 +543,7 @@ int notrace __kprobes notify_die(enum die_val val, const char *str,
        };
        return atomic_notifier_call_chain(&die_chain, val, &args);
 }
+NOKPROBE_SYMBOL(notify_die);
 
 int register_die_notifier(struct notifier_block *nb)
 {
index c6b98793d6477fd60fe8122745c9db75007d3080..3bdf01b494fe29c267a0abe73828b02a799a737d 100644 (file)
@@ -535,7 +535,7 @@ static inline void init_hrtick(void)
        __old;                                                          \
 })
 
-#ifdef TIF_POLLING_NRFLAG
+#if defined(CONFIG_SMP) && defined(TIF_POLLING_NRFLAG)
 /*
  * Atomically set TIF_NEED_RESCHED and test for TIF_POLLING_NRFLAG,
  * this avoids any races wrt polling state changes and thereby avoids
@@ -546,12 +546,44 @@ static bool set_nr_and_not_polling(struct task_struct *p)
        struct thread_info *ti = task_thread_info(p);
        return !(fetch_or(&ti->flags, _TIF_NEED_RESCHED) & _TIF_POLLING_NRFLAG);
 }
+
+/*
+ * Atomically set TIF_NEED_RESCHED if TIF_POLLING_NRFLAG is set.
+ *
+ * If this returns true, then the idle task promises to call
+ * sched_ttwu_pending() and reschedule soon.
+ */
+static bool set_nr_if_polling(struct task_struct *p)
+{
+       struct thread_info *ti = task_thread_info(p);
+       typeof(ti->flags) old, val = ACCESS_ONCE(ti->flags);
+
+       for (;;) {
+               if (!(val & _TIF_POLLING_NRFLAG))
+                       return false;
+               if (val & _TIF_NEED_RESCHED)
+                       return true;
+               old = cmpxchg(&ti->flags, val, val | _TIF_NEED_RESCHED);
+               if (old == val)
+                       break;
+               val = old;
+       }
+       return true;
+}
+
 #else
 static bool set_nr_and_not_polling(struct task_struct *p)
 {
        set_tsk_need_resched(p);
        return true;
 }
+
+#ifdef CONFIG_SMP
+static bool set_nr_if_polling(struct task_struct *p)
+{
+       return false;
+}
+#endif
 #endif
 
 /*
@@ -580,6 +612,8 @@ void resched_task(struct task_struct *p)
 
        if (set_nr_and_not_polling(p))
                smp_send_reschedule(cpu);
+       else
+               trace_sched_wake_idle_without_ipi(cpu);
 }
 
 void resched_cpu(int cpu)
@@ -642,27 +676,10 @@ static void wake_up_idle_cpu(int cpu)
        if (cpu == smp_processor_id())
                return;
 
-       /*
-        * This is safe, as this function is called with the timer
-        * wheel base lock of (cpu) held. When the CPU is on the way
-        * to idle and has not yet set rq->curr to idle then it will
-        * be serialized on the timer wheel base lock and take the new
-        * timer into account automatically.
-        */
-       if (rq->curr != rq->idle)
-               return;
-
-       /*
-        * We can set TIF_RESCHED on the idle task of the other CPU
-        * lockless. The worst case is that the other CPU runs the
-        * idle task through an additional NOOP schedule()
-        */
-       set_tsk_need_resched(rq->idle);
-
-       /* NEED_RESCHED must be visible before we test polling */
-       smp_mb();
-       if (!tsk_is_polling(rq->idle))
+       if (set_nr_and_not_polling(rq->idle))
                smp_send_reschedule(cpu);
+       else
+               trace_sched_wake_idle_without_ipi(cpu);
 }
 
 static bool wake_up_full_nohz_cpu(int cpu)
@@ -888,7 +905,7 @@ static void update_rq_clock_task(struct rq *rq, s64 delta)
        rq->clock_task += delta;
 
 #if defined(CONFIG_IRQ_TIME_ACCOUNTING) || defined(CONFIG_PARAVIRT_TIME_ACCOUNTING)
-       if ((irq_delta + steal) && sched_feat(NONTASK_POWER))
+       if ((irq_delta + steal) && sched_feat(NONTASK_CAPACITY))
                sched_rt_avg_update(rq, irq_delta + steal);
 #endif
 }
@@ -1521,13 +1538,17 @@ static int ttwu_remote(struct task_struct *p, int wake_flags)
 }
 
 #ifdef CONFIG_SMP
-static void sched_ttwu_pending(void)
+void sched_ttwu_pending(void)
 {
        struct rq *rq = this_rq();
        struct llist_node *llist = llist_del_all(&rq->wake_list);
        struct task_struct *p;
+       unsigned long flags;
 
-       raw_spin_lock(&rq->lock);
+       if (!llist)
+               return;
+
+       raw_spin_lock_irqsave(&rq->lock, flags);
 
        while (llist) {
                p = llist_entry(llist, struct task_struct, wake_entry);
@@ -1535,7 +1556,7 @@ static void sched_ttwu_pending(void)
                ttwu_do_activate(rq, p, 0);
        }
 
-       raw_spin_unlock(&rq->lock);
+       raw_spin_unlock_irqrestore(&rq->lock, flags);
 }
 
 void scheduler_ipi(void)
@@ -1581,8 +1602,14 @@ void scheduler_ipi(void)
 
 static void ttwu_queue_remote(struct task_struct *p, int cpu)
 {
-       if (llist_add(&p->wake_entry, &cpu_rq(cpu)->wake_list))
-               smp_send_reschedule(cpu);
+       struct rq *rq = cpu_rq(cpu);
+
+       if (llist_add(&p->wake_entry, &cpu_rq(cpu)->wake_list)) {
+               if (!set_nr_if_polling(rq->idle))
+                       smp_send_reschedule(cpu);
+               else
+                       trace_sched_wake_idle_without_ipi(cpu);
+       }
 }
 
 bool cpus_share_cache(int this_cpu, int that_cpu)
@@ -2527,7 +2554,7 @@ notrace unsigned long get_parent_ip(unsigned long addr)
 #if defined(CONFIG_PREEMPT) && (defined(CONFIG_DEBUG_PREEMPT) || \
                                defined(CONFIG_PREEMPT_TRACER))
 
-void __kprobes preempt_count_add(int val)
+void preempt_count_add(int val)
 {
 #ifdef CONFIG_DEBUG_PREEMPT
        /*
@@ -2553,8 +2580,9 @@ void __kprobes preempt_count_add(int val)
        }
 }
 EXPORT_SYMBOL(preempt_count_add);
+NOKPROBE_SYMBOL(preempt_count_add);
 
-void __kprobes preempt_count_sub(int val)
+void preempt_count_sub(int val)
 {
 #ifdef CONFIG_DEBUG_PREEMPT
        /*
@@ -2575,6 +2603,7 @@ void __kprobes preempt_count_sub(int val)
        __preempt_count_sub(val);
 }
 EXPORT_SYMBOL(preempt_count_sub);
+NOKPROBE_SYMBOL(preempt_count_sub);
 
 #endif
 
@@ -2857,6 +2886,7 @@ asmlinkage __visible void __sched notrace preempt_schedule(void)
                barrier();
        } while (need_resched());
 }
+NOKPROBE_SYMBOL(preempt_schedule);
 EXPORT_SYMBOL(preempt_schedule);
 #endif /* CONFIG_PREEMPT */
 
@@ -4216,7 +4246,7 @@ EXPORT_SYMBOL(yield);
  *     false (0) if we failed to boost the target.
  *     -ESRCH if there's no task to yield to.
  */
-bool __sched yield_to(struct task_struct *p, bool preempt)
+int __sched yield_to(struct task_struct *p, bool preempt)
 {
        struct task_struct *curr = current;
        struct rq *rq, *p_rq;
@@ -5242,14 +5272,13 @@ static int sched_domain_debug_one(struct sched_domain *sd, int cpu, int level,
                }
 
                /*
-                * Even though we initialize ->power to something semi-sane,
-                * we leave power_orig unset. This allows us to detect if
+                * Even though we initialize ->capacity to something semi-sane,
+                * we leave capacity_orig unset. This allows us to detect if
                 * domain iteration is still funny without causing /0 traps.
                 */
-               if (!group->sgp->power_orig) {
+               if (!group->sgc->capacity_orig) {
                        printk(KERN_CONT "\n");
-                       printk(KERN_ERR "ERROR: domain->cpu_power not "
-                                       "set\n");
+                       printk(KERN_ERR "ERROR: domain->cpu_capacity not set\n");
                        break;
                }
 
@@ -5271,9 +5300,9 @@ static int sched_domain_debug_one(struct sched_domain *sd, int cpu, int level,
                cpulist_scnprintf(str, sizeof(str), sched_group_cpus(group));
 
                printk(KERN_CONT " %s", str);
-               if (group->sgp->power != SCHED_POWER_SCALE) {
-                       printk(KERN_CONT " (cpu_power = %d)",
-                               group->sgp->power);
+               if (group->sgc->capacity != SCHED_CAPACITY_SCALE) {
+                       printk(KERN_CONT " (cpu_capacity = %d)",
+                               group->sgc->capacity);
                }
 
                group = group->next;
@@ -5331,7 +5360,7 @@ static int sd_degenerate(struct sched_domain *sd)
                         SD_BALANCE_NEWIDLE |
                         SD_BALANCE_FORK |
                         SD_BALANCE_EXEC |
-                        SD_SHARE_CPUPOWER |
+                        SD_SHARE_CPUCAPACITY |
                         SD_SHARE_PKG_RESOURCES |
                         SD_SHARE_POWERDOMAIN)) {
                if (sd->groups != sd->groups->next)
@@ -5362,7 +5391,7 @@ sd_parent_degenerate(struct sched_domain *sd, struct sched_domain *parent)
                                SD_BALANCE_NEWIDLE |
                                SD_BALANCE_FORK |
                                SD_BALANCE_EXEC |
-                               SD_SHARE_CPUPOWER |
+                               SD_SHARE_CPUCAPACITY |
                                SD_SHARE_PKG_RESOURCES |
                                SD_PREFER_SIBLING |
                                SD_SHARE_POWERDOMAIN);
@@ -5487,7 +5516,7 @@ static struct root_domain *alloc_rootdomain(void)
        return rd;
 }
 
-static void free_sched_groups(struct sched_group *sg, int free_sgp)
+static void free_sched_groups(struct sched_group *sg, int free_sgc)
 {
        struct sched_group *tmp, *first;
 
@@ -5498,8 +5527,8 @@ static void free_sched_groups(struct sched_group *sg, int free_sgp)
        do {
                tmp = sg->next;
 
-               if (free_sgp && atomic_dec_and_test(&sg->sgp->ref))
-                       kfree(sg->sgp);
+               if (free_sgc && atomic_dec_and_test(&sg->sgc->ref))
+                       kfree(sg->sgc);
 
                kfree(sg);
                sg = tmp;
@@ -5517,7 +5546,7 @@ static void free_sched_domain(struct rcu_head *rcu)
        if (sd->flags & SD_OVERLAP) {
                free_sched_groups(sd->groups, 1);
        } else if (atomic_dec_and_test(&sd->groups->ref)) {
-               kfree(sd->groups->sgp);
+               kfree(sd->groups->sgc);
                kfree(sd->groups);
        }
        kfree(sd);
@@ -5728,17 +5757,17 @@ build_overlap_sched_groups(struct sched_domain *sd, int cpu)
 
                cpumask_or(covered, covered, sg_span);
 
-               sg->sgp = *per_cpu_ptr(sdd->sgp, i);
-               if (atomic_inc_return(&sg->sgp->ref) == 1)
+               sg->sgc = *per_cpu_ptr(sdd->sgc, i);
+               if (atomic_inc_return(&sg->sgc->ref) == 1)
                        build_group_mask(sd, sg);
 
                /*
-                * Initialize sgp->power such that even if we mess up the
+                * Initialize sgc->capacity such that even if we mess up the
                 * domains and no possible iteration will get us here, we won't
                 * die on a /0 trap.
                 */
-               sg->sgp->power = SCHED_POWER_SCALE * cpumask_weight(sg_span);
-               sg->sgp->power_orig = sg->sgp->power;
+               sg->sgc->capacity = SCHED_CAPACITY_SCALE * cpumask_weight(sg_span);
+               sg->sgc->capacity_orig = sg->sgc->capacity;
 
                /*
                 * Make sure the first group of this domain contains the
@@ -5776,8 +5805,8 @@ static int get_group(int cpu, struct sd_data *sdd, struct sched_group **sg)
 
        if (sg) {
                *sg = *per_cpu_ptr(sdd->sg, cpu);
-               (*sg)->sgp = *per_cpu_ptr(sdd->sgp, cpu);
-               atomic_set(&(*sg)->sgp->ref, 1); /* for claim_allocations */
+               (*sg)->sgc = *per_cpu_ptr(sdd->sgc, cpu);
+               atomic_set(&(*sg)->sgc->ref, 1); /* for claim_allocations */
        }
 
        return cpu;
@@ -5786,7 +5815,7 @@ static int get_group(int cpu, struct sd_data *sdd, struct sched_group **sg)
 /*
  * build_sched_groups will build a circular linked list of the groups
  * covered by the given span, and will set each group's ->cpumask correctly,
- * and ->cpu_power to 0.
+ * and ->cpu_capacity to 0.
  *
  * Assumes the sched_domain tree is fully constructed
  */
@@ -5840,16 +5869,16 @@ build_sched_groups(struct sched_domain *sd, int cpu)
 }
 
 /*
- * Initialize sched groups cpu_power.
+ * Initialize sched groups cpu_capacity.
  *
- * cpu_power indicates the capacity of sched group, which is used while
+ * cpu_capacity indicates the capacity of sched group, which is used while
  * distributing the load between different sched groups in a sched domain.
- * Typically cpu_power for all the groups in a sched domain will be same unless
- * there are asymmetries in the topology. If there are asymmetries, group
- * having more cpu_power will pickup more load compared to the group having
- * less cpu_power.
+ * Typically cpu_capacity for all the groups in a sched domain will be same
+ * unless there are asymmetries in the topology. If there are asymmetries,
+ * group having more cpu_capacity will pickup more load compared to the
+ * group having less cpu_capacity.
  */
-static void init_sched_groups_power(int cpu, struct sched_domain *sd)
+static void init_sched_groups_capacity(int cpu, struct sched_domain *sd)
 {
        struct sched_group *sg = sd->groups;
 
@@ -5863,8 +5892,8 @@ static void init_sched_groups_power(int cpu, struct sched_domain *sd)
        if (cpu != group_balance_cpu(sg))
                return;
 
-       update_group_power(sd, cpu);
-       atomic_set(&sg->sgp->nr_busy_cpus, sg->group_weight);
+       update_group_capacity(sd, cpu);
+       atomic_set(&sg->sgc->nr_busy_cpus, sg->group_weight);
 }
 
 /*
@@ -5955,8 +5984,8 @@ static void claim_allocations(int cpu, struct sched_domain *sd)
        if (atomic_read(&(*per_cpu_ptr(sdd->sg, cpu))->ref))
                *per_cpu_ptr(sdd->sg, cpu) = NULL;
 
-       if (atomic_read(&(*per_cpu_ptr(sdd->sgp, cpu))->ref))
-               *per_cpu_ptr(sdd->sgp, cpu) = NULL;
+       if (atomic_read(&(*per_cpu_ptr(sdd->sgc, cpu))->ref))
+               *per_cpu_ptr(sdd->sgc, cpu) = NULL;
 }
 
 #ifdef CONFIG_NUMA
@@ -5969,7 +5998,7 @@ static int sched_domains_curr_level;
 /*
  * SD_flags allowed in topology descriptions.
  *
- * SD_SHARE_CPUPOWER      - describes SMT topologies
+ * SD_SHARE_CPUCAPACITY      - describes SMT topologies
  * SD_SHARE_PKG_RESOURCES - describes shared caches
  * SD_NUMA                - describes NUMA topologies
  * SD_SHARE_POWERDOMAIN   - describes shared power domain
@@ -5978,7 +6007,7 @@ static int sched_domains_curr_level;
  * SD_ASYM_PACKING        - describes SMT quirks
  */
 #define TOPOLOGY_SD_FLAGS              \
-       (SD_SHARE_CPUPOWER |            \
+       (SD_SHARE_CPUCAPACITY |         \
         SD_SHARE_PKG_RESOURCES |       \
         SD_NUMA |                      \
         SD_ASYM_PACKING |              \
@@ -6024,7 +6053,7 @@ sd_init(struct sched_domain_topology_level *tl, int cpu)
                                        | 1*SD_BALANCE_FORK
                                        | 0*SD_BALANCE_WAKE
                                        | 1*SD_WAKE_AFFINE
-                                       | 0*SD_SHARE_CPUPOWER
+                                       | 0*SD_SHARE_CPUCAPACITY
                                        | 0*SD_SHARE_PKG_RESOURCES
                                        | 0*SD_SERIALIZE
                                        | 0*SD_PREFER_SIBLING
@@ -6046,7 +6075,7 @@ sd_init(struct sched_domain_topology_level *tl, int cpu)
         * Convert topological properties into behaviour.
         */
 
-       if (sd->flags & SD_SHARE_CPUPOWER) {
+       if (sd->flags & SD_SHARE_CPUCAPACITY) {
                sd->imbalance_pct = 110;
                sd->smt_gain = 1178; /* ~15% */
 
@@ -6358,14 +6387,14 @@ static int __sdt_alloc(const struct cpumask *cpu_map)
                if (!sdd->sg)
                        return -ENOMEM;
 
-               sdd->sgp = alloc_percpu(struct sched_group_power *);
-               if (!sdd->sgp)
+               sdd->sgc = alloc_percpu(struct sched_group_capacity *);
+               if (!sdd->sgc)
                        return -ENOMEM;
 
                for_each_cpu(j, cpu_map) {
                        struct sched_domain *sd;
                        struct sched_group *sg;
-                       struct sched_group_power *sgp;
+                       struct sched_group_capacity *sgc;
 
                        sd = kzalloc_node(sizeof(struct sched_domain) + cpumask_size(),
                                        GFP_KERNEL, cpu_to_node(j));
@@ -6383,12 +6412,12 @@ static int __sdt_alloc(const struct cpumask *cpu_map)
 
                        *per_cpu_ptr(sdd->sg, j) = sg;
 
-                       sgp = kzalloc_node(sizeof(struct sched_group_power) + cpumask_size(),
+                       sgc = kzalloc_node(sizeof(struct sched_group_capacity) + cpumask_size(),
                                        GFP_KERNEL, cpu_to_node(j));
-                       if (!sgp)
+                       if (!sgc)
                                return -ENOMEM;
 
-                       *per_cpu_ptr(sdd->sgp, j) = sgp;
+                       *per_cpu_ptr(sdd->sgc, j) = sgc;
                }
        }
 
@@ -6415,15 +6444,15 @@ static void __sdt_free(const struct cpumask *cpu_map)
 
                        if (sdd->sg)
                                kfree(*per_cpu_ptr(sdd->sg, j));
-                       if (sdd->sgp)
-                               kfree(*per_cpu_ptr(sdd->sgp, j));
+                       if (sdd->sgc)
+                               kfree(*per_cpu_ptr(sdd->sgc, j));
                }
                free_percpu(sdd->sd);
                sdd->sd = NULL;
                free_percpu(sdd->sg);
                sdd->sg = NULL;
-               free_percpu(sdd->sgp);
-               sdd->sgp = NULL;
+               free_percpu(sdd->sgc);
+               sdd->sgc = NULL;
        }
 }
 
@@ -6493,14 +6522,14 @@ static int build_sched_domains(const struct cpumask *cpu_map,
                }
        }
 
-       /* Calculate CPU power for physical packages and nodes */
+       /* Calculate CPU capacity for physical packages and nodes */
        for (i = nr_cpumask_bits-1; i >= 0; i--) {
                if (!cpumask_test_cpu(i, cpu_map))
                        continue;
 
                for (sd = *per_cpu_ptr(d.sd, i); sd; sd = sd->parent) {
                        claim_allocations(i, sd);
-                       init_sched_groups_power(i, sd);
+                       init_sched_groups_capacity(i, sd);
                }
        }
 
@@ -6943,7 +6972,7 @@ void __init sched_init(void)
 #ifdef CONFIG_SMP
                rq->sd = NULL;
                rq->rd = NULL;
-               rq->cpu_power = SCHED_POWER_SCALE;
+               rq->cpu_capacity = SCHED_CAPACITY_SCALE;
                rq->post_schedule = 0;
                rq->active_balance = 0;
                rq->next_balance = jiffies;
index 2b8cbf09d1a4add6fe837be0080a71a35f145164..fc4f98b1258f66cbbf3cf1fc1082cb909c0f1144 100644 (file)
@@ -57,8 +57,6 @@ void init_dl_bandwidth(struct dl_bandwidth *dl_b, u64 period, u64 runtime)
        dl_b->dl_runtime = runtime;
 }
 
-extern unsigned long to_ratio(u64 period, u64 runtime);
-
 void init_dl_bw(struct dl_bw *dl_b)
 {
        raw_spin_lock_init(&dl_b->lock);
index 9855e87d671a54982238d325f014162327a974e8..fea7d3335e1fdf3502fc72f5d64b9181bc7e4243 100644 (file)
@@ -1017,7 +1017,7 @@ bool should_numa_migrate_memory(struct task_struct *p, struct page * page,
 static unsigned long weighted_cpuload(const int cpu);
 static unsigned long source_load(int cpu, int type);
 static unsigned long target_load(int cpu, int type);
-static unsigned long power_of(int cpu);
+static unsigned long capacity_of(int cpu);
 static long effective_load(struct task_group *tg, int cpu, long wl, long wg);
 
 /* Cached statistics for all CPUs within a node */
@@ -1026,11 +1026,11 @@ struct numa_stats {
        unsigned long load;
 
        /* Total compute capacity of CPUs on a node */
-       unsigned long power;
+       unsigned long compute_capacity;
 
        /* Approximate capacity in terms of runnable tasks on a node */
-       unsigned long capacity;
-       int has_capacity;
+       unsigned long task_capacity;
+       int has_free_capacity;
 };
 
 /*
@@ -1046,7 +1046,7 @@ static void update_numa_stats(struct numa_stats *ns, int nid)
 
                ns->nr_running += rq->nr_running;
                ns->load += weighted_cpuload(cpu);
-               ns->power += power_of(cpu);
+               ns->compute_capacity += capacity_of(cpu);
 
                cpus++;
        }
@@ -1056,15 +1056,16 @@ static void update_numa_stats(struct numa_stats *ns, int nid)
         * the @ns structure is NULL'ed and task_numa_compare() will
         * not find this node attractive.
         *
-        * We'll either bail at !has_capacity, or we'll detect a huge imbalance
-        * and bail there.
+        * We'll either bail at !has_free_capacity, or we'll detect a huge
+        * imbalance and bail there.
         */
        if (!cpus)
                return;
 
-       ns->load = (ns->load * SCHED_POWER_SCALE) / ns->power;
-       ns->capacity = DIV_ROUND_CLOSEST(ns->power, SCHED_POWER_SCALE);
-       ns->has_capacity = (ns->nr_running < ns->capacity);
+       ns->load = (ns->load * SCHED_CAPACITY_SCALE) / ns->compute_capacity;
+       ns->task_capacity =
+               DIV_ROUND_CLOSEST(ns->compute_capacity, SCHED_CAPACITY_SCALE);
+       ns->has_free_capacity = (ns->nr_running < ns->task_capacity);
 }
 
 struct task_numa_env {
@@ -1195,8 +1196,8 @@ static void task_numa_compare(struct task_numa_env *env,
 
        if (!cur) {
                /* Is there capacity at our destination? */
-               if (env->src_stats.has_capacity &&
-                   !env->dst_stats.has_capacity)
+               if (env->src_stats.has_free_capacity &&
+                   !env->dst_stats.has_free_capacity)
                        goto unlock;
 
                goto balance;
@@ -1213,7 +1214,7 @@ static void task_numa_compare(struct task_numa_env *env,
        orig_dst_load = env->dst_stats.load;
        orig_src_load = env->src_stats.load;
 
-       /* XXX missing power terms */
+       /* XXX missing capacity terms */
        load = task_h_load(env->p);
        dst_load = orig_dst_load + load;
        src_load = orig_src_load - load;
@@ -1301,8 +1302,8 @@ static int task_numa_migrate(struct task_struct *p)
        groupimp = group_weight(p, env.dst_nid) - groupweight;
        update_numa_stats(&env.dst_stats, env.dst_nid);
 
-       /* If the preferred nid has capacity, try to use it. */
-       if (env.dst_stats.has_capacity)
+       /* If the preferred nid has free capacity, try to use it. */
+       if (env.dst_stats.has_free_capacity)
                task_numa_find_cpu(&env, taskimp, groupimp);
 
        /* No space available on the preferred nid. Look elsewhere. */
@@ -3225,10 +3226,12 @@ static void expire_cfs_rq_runtime(struct cfs_rq *cfs_rq)
         * has not truly expired.
         *
         * Fortunately we can check determine whether this the case by checking
-        * whether the global deadline has advanced.
+        * whether the global deadline has advanced. It is valid to compare
+        * cfs_b->runtime_expires without any locks since we only care about
+        * exact equality, so a partial write will still work.
         */
 
-       if ((s64)(cfs_rq->runtime_expires - cfs_b->runtime_expires) >= 0) {
+       if (cfs_rq->runtime_expires != cfs_b->runtime_expires) {
                /* extend local deadline, drift is bounded above by 2 ticks */
                cfs_rq->runtime_expires += TICK_NSEC;
        } else {
@@ -3457,21 +3460,21 @@ static u64 distribute_cfs_runtime(struct cfs_bandwidth *cfs_b,
 static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun)
 {
        u64 runtime, runtime_expires;
-       int idle = 1, throttled;
+       int throttled;
 
-       raw_spin_lock(&cfs_b->lock);
        /* no need to continue the timer with no bandwidth constraint */
        if (cfs_b->quota == RUNTIME_INF)
-               goto out_unlock;
+               goto out_deactivate;
 
        throttled = !list_empty(&cfs_b->throttled_cfs_rq);
-       /* idle depends on !throttled (for the case of a large deficit) */
-       idle = cfs_b->idle && !throttled;
        cfs_b->nr_periods += overrun;
 
-       /* if we're going inactive then everything else can be deferred */
-       if (idle)
-               goto out_unlock;
+       /*
+        * idle depends on !throttled (for the case of a large deficit), and if
+        * we're going inactive then everything else can be deferred
+        */
+       if (cfs_b->idle && !throttled)
+               goto out_deactivate;
 
        /*
         * if we have relooped after returning idle once, we need to update our
@@ -3485,7 +3488,7 @@ static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun)
        if (!throttled) {
                /* mark as potentially idle for the upcoming period */
                cfs_b->idle = 1;
-               goto out_unlock;
+               return 0;
        }
 
        /* account preceding periods in which throttling occurred */
@@ -3525,12 +3528,12 @@ static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun)
         * timer to remain active while there are any throttled entities.)
         */
        cfs_b->idle = 0;
-out_unlock:
-       if (idle)
-               cfs_b->timer_active = 0;
-       raw_spin_unlock(&cfs_b->lock);
 
-       return idle;
+       return 0;
+
+out_deactivate:
+       cfs_b->timer_active = 0;
+       return 1;
 }
 
 /* a cfs_rq won't donate quota below this amount */
@@ -3707,6 +3710,7 @@ static enum hrtimer_restart sched_cfs_period_timer(struct hrtimer *timer)
        int overrun;
        int idle = 0;
 
+       raw_spin_lock(&cfs_b->lock);
        for (;;) {
                now = hrtimer_cb_get_time(timer);
                overrun = hrtimer_forward(timer, now, cfs_b->period);
@@ -3716,6 +3720,7 @@ static enum hrtimer_restart sched_cfs_period_timer(struct hrtimer *timer)
 
                idle = do_sched_cfs_period_timer(cfs_b, overrun);
        }
+       raw_spin_unlock(&cfs_b->lock);
 
        return idle ? HRTIMER_NORESTART : HRTIMER_RESTART;
 }
@@ -3775,8 +3780,6 @@ static void __maybe_unused unthrottle_offline_cfs_rqs(struct rq *rq)
        struct cfs_rq *cfs_rq;
 
        for_each_leaf_cfs_rq(rq, cfs_rq) {
-               struct cfs_bandwidth *cfs_b = tg_cfs_bandwidth(cfs_rq->tg);
-
                if (!cfs_rq->runtime_enabled)
                        continue;
 
@@ -3784,7 +3787,7 @@ static void __maybe_unused unthrottle_offline_cfs_rqs(struct rq *rq)
                 * clock_task is not advancing so we just need to make sure
                 * there's some valid quota amount
                 */
-               cfs_rq->runtime_remaining = cfs_b->quota;
+               cfs_rq->runtime_remaining = 1;
                if (cfs_rq_throttled(cfs_rq))
                        unthrottle_cfs_rq(cfs_rq);
        }
@@ -4041,9 +4044,9 @@ static unsigned long target_load(int cpu, int type)
        return max(rq->cpu_load[type-1], total);
 }
 
-static unsigned long power_of(int cpu)
+static unsigned long capacity_of(int cpu)
 {
-       return cpu_rq(cpu)->cpu_power;
+       return cpu_rq(cpu)->cpu_capacity;
 }
 
 static unsigned long cpu_avg_load_per_task(int cpu)
@@ -4065,7 +4068,7 @@ static void record_wakee(struct task_struct *p)
         * about the boundary, really active task won't care
         * about the loss.
         */
-       if (jiffies > current->wakee_flip_decay_ts + HZ) {
+       if (time_after(jiffies, current->wakee_flip_decay_ts + HZ)) {
                current->wakee_flips >>= 1;
                current->wakee_flip_decay_ts = jiffies;
        }
@@ -4286,12 +4289,12 @@ static int wake_affine(struct sched_domain *sd, struct task_struct *p, int sync)
                s64 this_eff_load, prev_eff_load;
 
                this_eff_load = 100;
-               this_eff_load *= power_of(prev_cpu);
+               this_eff_load *= capacity_of(prev_cpu);
                this_eff_load *= this_load +
                        effective_load(tg, this_cpu, weight, weight);
 
                prev_eff_load = 100 + (sd->imbalance_pct - 100) / 2;
-               prev_eff_load *= power_of(this_cpu);
+               prev_eff_load *= capacity_of(this_cpu);
                prev_eff_load *= load + effective_load(tg, prev_cpu, 0, weight);
 
                balanced = this_eff_load <= prev_eff_load;
@@ -4367,8 +4370,8 @@ find_idlest_group(struct sched_domain *sd, struct task_struct *p,
                        avg_load += load;
                }
 
-               /* Adjust by relative CPU power of the group */
-               avg_load = (avg_load * SCHED_POWER_SCALE) / group->sgp->power;
+               /* Adjust by relative CPU capacity of the group */
+               avg_load = (avg_load * SCHED_CAPACITY_SCALE) / group->sgc->capacity;
 
                if (local_group) {
                        this_load = avg_load;
@@ -4948,14 +4951,14 @@ static bool yield_to_task_fair(struct rq *rq, struct task_struct *p, bool preemp
  *
  *   W'_i,n = (2^n - 1) / 2^n * W_i,n + 1 / 2^n * W_i,0               (3)
  *
- * P_i is the cpu power (or compute capacity) of cpu i, typically it is the
+ * C_i is the compute capacity of cpu i, typically it is the
  * fraction of 'recent' time available for SCHED_OTHER task execution. But it
  * can also include other factors [XXX].
  *
  * To achieve this balance we define a measure of imbalance which follows
  * directly from (1):
  *
- *   imb_i,j = max{ avg(W/P), W_i/P_i } - min{ avg(W/P), W_j/P_j }    (4)
+ *   imb_i,j = max{ avg(W/C), W_i/C_i } - min{ avg(W/C), W_j/C_j }    (4)
  *
  * We them move tasks around to minimize the imbalance. In the continuous
  * function space it is obvious this converges, in the discrete case we get
@@ -5530,13 +5533,13 @@ struct sg_lb_stats {
        unsigned long group_load; /* Total load over the CPUs of the group */
        unsigned long sum_weighted_load; /* Weighted load of group's tasks */
        unsigned long load_per_task;
-       unsigned long group_power;
+       unsigned long group_capacity;
        unsigned int sum_nr_running; /* Nr tasks running in the group */
-       unsigned int group_capacity;
+       unsigned int group_capacity_factor;
        unsigned int idle_cpus;
        unsigned int group_weight;
        int group_imb; /* Is there an imbalance in the group ? */
-       int group_has_capacity; /* Is there extra capacity in the group? */
+       int group_has_free_capacity;
 #ifdef CONFIG_NUMA_BALANCING
        unsigned int nr_numa_running;
        unsigned int nr_preferred_running;
@@ -5551,7 +5554,7 @@ struct sd_lb_stats {
        struct sched_group *busiest;    /* Busiest group in this sd */
        struct sched_group *local;      /* Local group in this sd */
        unsigned long total_load;       /* Total load of all groups in sd */
-       unsigned long total_pwr;        /* Total power of all groups in sd */
+       unsigned long total_capacity;   /* Total capacity of all groups in sd */
        unsigned long avg_load; /* Average load across all groups in sd */
 
        struct sg_lb_stats busiest_stat;/* Statistics of the busiest group */
@@ -5570,7 +5573,7 @@ static inline void init_sd_lb_stats(struct sd_lb_stats *sds)
                .busiest = NULL,
                .local = NULL,
                .total_load = 0UL,
-               .total_pwr = 0UL,
+               .total_capacity = 0UL,
                .busiest_stat = {
                        .avg_load = 0UL,
                },
@@ -5605,17 +5608,17 @@ static inline int get_sd_load_idx(struct sched_domain *sd,
        return load_idx;
 }
 
-static unsigned long default_scale_freq_power(struct sched_domain *sd, int cpu)
+static unsigned long default_scale_capacity(struct sched_domain *sd, int cpu)
 {
-       return SCHED_POWER_SCALE;
+       return SCHED_CAPACITY_SCALE;
 }
 
-unsigned long __weak arch_scale_freq_power(struct sched_domain *sd, int cpu)
+unsigned long __weak arch_scale_freq_capacity(struct sched_domain *sd, int cpu)
 {
-       return default_scale_freq_power(sd, cpu);
+       return default_scale_capacity(sd, cpu);
 }
 
-static unsigned long default_scale_smt_power(struct sched_domain *sd, int cpu)
+static unsigned long default_scale_smt_capacity(struct sched_domain *sd, int cpu)
 {
        unsigned long weight = sd->span_weight;
        unsigned long smt_gain = sd->smt_gain;
@@ -5625,12 +5628,12 @@ static unsigned long default_scale_smt_power(struct sched_domain *sd, int cpu)
        return smt_gain;
 }
 
-unsigned long __weak arch_scale_smt_power(struct sched_domain *sd, int cpu)
+unsigned long __weak arch_scale_smt_capacity(struct sched_domain *sd, int cpu)
 {
-       return default_scale_smt_power(sd, cpu);
+       return default_scale_smt_capacity(sd, cpu);
 }
 
-static unsigned long scale_rt_power(int cpu)
+static unsigned long scale_rt_capacity(int cpu)
 {
        struct rq *rq = cpu_rq(cpu);
        u64 total, available, age_stamp, avg;
@@ -5650,71 +5653,71 @@ static unsigned long scale_rt_power(int cpu)
        total = sched_avg_period() + delta;
 
        if (unlikely(total < avg)) {
-               /* Ensures that power won't end up being negative */
+               /* Ensures that capacity won't end up being negative */
                available = 0;
        } else {
                available = total - avg;
        }
 
-       if (unlikely((s64)total < SCHED_POWER_SCALE))
-               total = SCHED_POWER_SCALE;
+       if (unlikely((s64)total < SCHED_CAPACITY_SCALE))
+               total = SCHED_CAPACITY_SCALE;
 
-       total >>= SCHED_POWER_SHIFT;
+       total >>= SCHED_CAPACITY_SHIFT;
 
        return div_u64(available, total);
 }
 
-static void update_cpu_power(struct sched_domain *sd, int cpu)
+static void update_cpu_capacity(struct sched_domain *sd, int cpu)
 {
        unsigned long weight = sd->span_weight;
-       unsigned long power = SCHED_POWER_SCALE;
+       unsigned long capacity = SCHED_CAPACITY_SCALE;
        struct sched_group *sdg = sd->groups;
 
-       if ((sd->flags & SD_SHARE_CPUPOWER) && weight > 1) {
-               if (sched_feat(ARCH_POWER))
-                       power *= arch_scale_smt_power(sd, cpu);
+       if ((sd->flags & SD_SHARE_CPUCAPACITY) && weight > 1) {
+               if (sched_feat(ARCH_CAPACITY))
+                       capacity *= arch_scale_smt_capacity(sd, cpu);
                else
-                       power *= default_scale_smt_power(sd, cpu);
+                       capacity *= default_scale_smt_capacity(sd, cpu);
 
-               power >>= SCHED_POWER_SHIFT;
+               capacity >>= SCHED_CAPACITY_SHIFT;
        }
 
-       sdg->sgp->power_orig = power;
+       sdg->sgc->capacity_orig = capacity;
 
-       if (sched_feat(ARCH_POWER))
-               power *= arch_scale_freq_power(sd, cpu);
+       if (sched_feat(ARCH_CAPACITY))
+               capacity *= arch_scale_freq_capacity(sd, cpu);
        else
-               power *= default_scale_freq_power(sd, cpu);
+               capacity *= default_scale_capacity(sd, cpu);
 
-       power >>= SCHED_POWER_SHIFT;
+       capacity >>= SCHED_CAPACITY_SHIFT;
 
-       power *= scale_rt_power(cpu);
-       power >>= SCHED_POWER_SHIFT;
+       capacity *= scale_rt_capacity(cpu);
+       capacity >>= SCHED_CAPACITY_SHIFT;
 
-       if (!power)
-               power = 1;
+       if (!capacity)
+               capacity = 1;
 
-       cpu_rq(cpu)->cpu_power = power;
-       sdg->sgp->power = power;
+       cpu_rq(cpu)->cpu_capacity = capacity;
+       sdg->sgc->capacity = capacity;
 }
 
-void update_group_power(struct sched_domain *sd, int cpu)
+void update_group_capacity(struct sched_domain *sd, int cpu)
 {
        struct sched_domain *child = sd->child;
        struct sched_group *group, *sdg = sd->groups;
-       unsigned long power, power_orig;
+       unsigned long capacity, capacity_orig;
        unsigned long interval;
 
        interval = msecs_to_jiffies(sd->balance_interval);
        interval = clamp(interval, 1UL, max_load_balance_interval);
-       sdg->sgp->next_update = jiffies + interval;
+       sdg->sgc->next_update = jiffies + interval;
 
        if (!child) {
-               update_cpu_power(sd, cpu);
+               update_cpu_capacity(sd, cpu);
                return;
        }
 
-       power_orig = power = 0;
+       capacity_orig = capacity = 0;
 
        if (child->flags & SD_OVERLAP) {
                /*
@@ -5723,31 +5726,31 @@ void update_group_power(struct sched_domain *sd, int cpu)
                 */
 
                for_each_cpu(cpu, sched_group_cpus(sdg)) {
-                       struct sched_group_power *sgp;
+                       struct sched_group_capacity *sgc;
                        struct rq *rq = cpu_rq(cpu);
 
                        /*
-                        * build_sched_domains() -> init_sched_groups_power()
+                        * build_sched_domains() -> init_sched_groups_capacity()
                         * gets here before we've attached the domains to the
                         * runqueues.
                         *
-                        * Use power_of(), which is set irrespective of domains
-                        * in update_cpu_power().
+                        * Use capacity_of(), which is set irrespective of domains
+                        * in update_cpu_capacity().
                         *
-                        * This avoids power/power_orig from being 0 and
+                        * This avoids capacity/capacity_orig from being 0 and
                         * causing divide-by-zero issues on boot.
                         *
-                        * Runtime updates will correct power_orig.
+                        * Runtime updates will correct capacity_orig.
                         */
                        if (unlikely(!rq->sd)) {
-                               power_orig += power_of(cpu);
-                               power += power_of(cpu);
+                               capacity_orig += capacity_of(cpu);
+                               capacity += capacity_of(cpu);
                                continue;
                        }
 
-                       sgp = rq->sd->groups->sgp;
-                       power_orig += sgp->power_orig;
-                       power += sgp->power;
+                       sgc = rq->sd->groups->sgc;
+                       capacity_orig += sgc->capacity_orig;
+                       capacity += sgc->capacity;
                }
        } else  {
                /*
@@ -5757,14 +5760,14 @@ void update_group_power(struct sched_domain *sd, int cpu)
 
                group = child->groups;
                do {
-                       power_orig += group->sgp->power_orig;
-                       power += group->sgp->power;
+                       capacity_orig += group->sgc->capacity_orig;
+                       capacity += group->sgc->capacity;
                        group = group->next;
                } while (group != child->groups);
        }
 
-       sdg->sgp->power_orig = power_orig;
-       sdg->sgp->power = power;
+       sdg->sgc->capacity_orig = capacity_orig;
+       sdg->sgc->capacity = capacity;
 }
 
 /*
@@ -5778,15 +5781,15 @@ static inline int
 fix_small_capacity(struct sched_domain *sd, struct sched_group *group)
 {
        /*
-        * Only siblings can have significantly less than SCHED_POWER_SCALE
+        * Only siblings can have significantly less than SCHED_CAPACITY_SCALE
         */
-       if (!(sd->flags & SD_SHARE_CPUPOWER))
+       if (!(sd->flags & SD_SHARE_CPUCAPACITY))
                return 0;
 
        /*
-        * If ~90% of the cpu_power is still there, we're good.
+        * If ~90% of the cpu_capacity is still there, we're good.
         */
-       if (group->sgp->power * 32 > group->sgp->power_orig * 29)
+       if (group->sgc->capacity * 32 > group->sgc->capacity_orig * 29)
                return 1;
 
        return 0;
@@ -5823,34 +5826,35 @@ fix_small_capacity(struct sched_domain *sd, struct sched_group *group)
 
 static inline int sg_imbalanced(struct sched_group *group)
 {
-       return group->sgp->imbalance;
+       return group->sgc->imbalance;
 }
 
 /*
- * Compute the group capacity.
+ * Compute the group capacity factor.
  *
- * Avoid the issue where N*frac(smt_power) >= 1 creates 'phantom' cores by
+ * Avoid the issue where N*frac(smt_capacity) >= 1 creates 'phantom' cores by
  * first dividing out the smt factor and computing the actual number of cores
- * and limit power unit capacity with that.
+ * and limit unit capacity with that.
  */
-static inline int sg_capacity(struct lb_env *env, struct sched_group *group)
+static inline int sg_capacity_factor(struct lb_env *env, struct sched_group *group)
 {
-       unsigned int capacity, smt, cpus;
-       unsigned int power, power_orig;
+       unsigned int capacity_factor, smt, cpus;
+       unsigned int capacity, capacity_orig;
 
-       power = group->sgp->power;
-       power_orig = group->sgp->power_orig;
+       capacity = group->sgc->capacity;
+       capacity_orig = group->sgc->capacity_orig;
        cpus = group->group_weight;
 
-       /* smt := ceil(cpus / power), assumes: 1 < smt_power < 2 */
-       smt = DIV_ROUND_UP(SCHED_POWER_SCALE * cpus, power_orig);
-       capacity = cpus / smt; /* cores */
+       /* smt := ceil(cpus / capacity), assumes: 1 < smt_capacity < 2 */
+       smt = DIV_ROUND_UP(SCHED_CAPACITY_SCALE * cpus, capacity_orig);
+       capacity_factor = cpus / smt; /* cores */
 
-       capacity = min_t(unsigned, capacity, DIV_ROUND_CLOSEST(power, SCHED_POWER_SCALE));
-       if (!capacity)
-               capacity = fix_small_capacity(env->sd, group);
+       capacity_factor = min_t(unsigned,
+               capacity_factor, DIV_ROUND_CLOSEST(capacity, SCHED_CAPACITY_SCALE));
+       if (!capacity_factor)
+               capacity_factor = fix_small_capacity(env->sd, group);
 
-       return capacity;
+       return capacity_factor;
 }
 
 /**
@@ -5890,9 +5894,9 @@ static inline void update_sg_lb_stats(struct lb_env *env,
                        sgs->idle_cpus++;
        }
 
-       /* Adjust by relative CPU power of the group */
-       sgs->group_power = group->sgp->power;
-       sgs->avg_load = (sgs->group_load*SCHED_POWER_SCALE) / sgs->group_power;
+       /* Adjust by relative CPU capacity of the group */
+       sgs->group_capacity = group->sgc->capacity;
+       sgs->avg_load = (sgs->group_load*SCHED_CAPACITY_SCALE) / sgs->group_capacity;
 
        if (sgs->sum_nr_running)
                sgs->load_per_task = sgs->sum_weighted_load / sgs->sum_nr_running;
@@ -5900,10 +5904,10 @@ static inline void update_sg_lb_stats(struct lb_env *env,
        sgs->group_weight = group->group_weight;
 
        sgs->group_imb = sg_imbalanced(group);
-       sgs->group_capacity = sg_capacity(env, group);
+       sgs->group_capacity_factor = sg_capacity_factor(env, group);
 
-       if (sgs->group_capacity > sgs->sum_nr_running)
-               sgs->group_has_capacity = 1;
+       if (sgs->group_capacity_factor > sgs->sum_nr_running)
+               sgs->group_has_free_capacity = 1;
 }
 
 /**
@@ -5927,7 +5931,7 @@ static bool update_sd_pick_busiest(struct lb_env *env,
        if (sgs->avg_load <= sds->busiest_stat.avg_load)
                return false;
 
-       if (sgs->sum_nr_running > sgs->group_capacity)
+       if (sgs->sum_nr_running > sgs->group_capacity_factor)
                return true;
 
        if (sgs->group_imb)
@@ -6007,8 +6011,8 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd
                        sgs = &sds->local_stat;
 
                        if (env->idle != CPU_NEWLY_IDLE ||
-                           time_after_eq(jiffies, sg->sgp->next_update))
-                               update_group_power(env->sd, env->dst_cpu);
+                           time_after_eq(jiffies, sg->sgc->next_update))
+                               update_group_capacity(env->sd, env->dst_cpu);
                }
 
                update_sg_lb_stats(env, sg, load_idx, local_group, sgs);
@@ -6018,17 +6022,17 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd
 
                /*
                 * In case the child domain prefers tasks go to siblings
-                * first, lower the sg capacity to one so that we'll try
+                * first, lower the sg capacity factor to one so that we'll try
                 * and move all the excess tasks away. We lower the capacity
                 * of a group only if the local group has the capacity to fit
-                * these excess tasks, i.e. nr_running < group_capacity. The
+                * these excess tasks, i.e. nr_running < group_capacity_factor. The
                 * extra check prevents the case where you always pull from the
                 * heaviest group when it is already under-utilized (possible
                 * with a large weight task outweighs the tasks on the system).
                 */
                if (prefer_sibling && sds->local &&
-                   sds->local_stat.group_has_capacity)
-                       sgs->group_capacity = min(sgs->group_capacity, 1U);
+                   sds->local_stat.group_has_free_capacity)
+                       sgs->group_capacity_factor = min(sgs->group_capacity_factor, 1U);
 
                if (update_sd_pick_busiest(env, sds, sg, sgs)) {
                        sds->busiest = sg;
@@ -6038,7 +6042,7 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd
 next_group:
                /* Now, start updating sd_lb_stats */
                sds->total_load += sgs->group_load;
-               sds->total_pwr += sgs->group_power;
+               sds->total_capacity += sgs->group_capacity;
 
                sg = sg->next;
        } while (sg != env->sd->groups);
@@ -6085,8 +6089,8 @@ static int check_asym_packing(struct lb_env *env, struct sd_lb_stats *sds)
                return 0;
 
        env->imbalance = DIV_ROUND_CLOSEST(
-               sds->busiest_stat.avg_load * sds->busiest_stat.group_power,
-               SCHED_POWER_SCALE);
+               sds->busiest_stat.avg_load * sds->busiest_stat.group_capacity,
+               SCHED_CAPACITY_SCALE);
 
        return 1;
 }
@@ -6101,7 +6105,7 @@ static int check_asym_packing(struct lb_env *env, struct sd_lb_stats *sds)
 static inline
 void fix_small_imbalance(struct lb_env *env, struct sd_lb_stats *sds)
 {
-       unsigned long tmp, pwr_now = 0, pwr_move = 0;
+       unsigned long tmp, capa_now = 0, capa_move = 0;
        unsigned int imbn = 2;
        unsigned long scaled_busy_load_per_task;
        struct sg_lb_stats *local, *busiest;
@@ -6115,8 +6119,8 @@ void fix_small_imbalance(struct lb_env *env, struct sd_lb_stats *sds)
                imbn = 1;
 
        scaled_busy_load_per_task =
-               (busiest->load_per_task * SCHED_POWER_SCALE) /
-               busiest->group_power;
+               (busiest->load_per_task * SCHED_CAPACITY_SCALE) /
+               busiest->group_capacity;
 
        if (busiest->avg_load + scaled_busy_load_per_task >=
            local->avg_load + (scaled_busy_load_per_task * imbn)) {
@@ -6126,38 +6130,38 @@ void fix_small_imbalance(struct lb_env *env, struct sd_lb_stats *sds)
 
        /*
         * OK, we don't have enough imbalance to justify moving tasks,
-        * however we may be able to increase total CPU power used by
+        * however we may be able to increase total CPU capacity used by
         * moving them.
         */
 
-       pwr_now += busiest->group_power *
+       capa_now += busiest->group_capacity *
                        min(busiest->load_per_task, busiest->avg_load);
-       pwr_now += local->group_power *
+       capa_now += local->group_capacity *
                        min(local->load_per_task, local->avg_load);
-       pwr_now /= SCHED_POWER_SCALE;
+       capa_now /= SCHED_CAPACITY_SCALE;
 
        /* Amount of load we'd subtract */
        if (busiest->avg_load > scaled_busy_load_per_task) {
-               pwr_move += busiest->group_power *
+               capa_move += busiest->group_capacity *
                            min(busiest->load_per_task,
                                busiest->avg_load - scaled_busy_load_per_task);
        }
 
        /* Amount of load we'd add */
-       if (busiest->avg_load * busiest->group_power <
-           busiest->load_per_task * SCHED_POWER_SCALE) {
-               tmp = (busiest->avg_load * busiest->group_power) /
-                     local->group_power;
+       if (busiest->avg_load * busiest->group_capacity <
+           busiest->load_per_task * SCHED_CAPACITY_SCALE) {
+               tmp = (busiest->avg_load * busiest->group_capacity) /
+                     local->group_capacity;
        } else {
-               tmp = (busiest->load_per_task * SCHED_POWER_SCALE) /
-                     local->group_power;
+               tmp = (busiest->load_per_task * SCHED_CAPACITY_SCALE) /
+                     local->group_capacity;
        }
-       pwr_move += local->group_power *
+       capa_move += local->group_capacity *
                    min(local->load_per_task, local->avg_load + tmp);
-       pwr_move /= SCHED_POWER_SCALE;
+       capa_move /= SCHED_CAPACITY_SCALE;
 
        /* Move if we gain throughput */
-       if (pwr_move > pwr_now)
+       if (capa_move > capa_now)
                env->imbalance = busiest->load_per_task;
 }
 
@@ -6187,7 +6191,7 @@ static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *s
        /*
         * In the presence of smp nice balancing, certain scenarios can have
         * max load less than avg load(as we skip the groups at or below
-        * its cpu_power, while calculating max_load..)
+        * its cpu_capacity, while calculating max_load..)
         */
        if (busiest->avg_load <= sds->avg_load ||
            local->avg_load >= sds->avg_load) {
@@ -6202,10 +6206,10 @@ static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *s
                 * have to drop below capacity to reach cpu-load equilibrium.
                 */
                load_above_capacity =
-                       (busiest->sum_nr_running - busiest->group_capacity);
+                       (busiest->sum_nr_running - busiest->group_capacity_factor);
 
-               load_above_capacity *= (SCHED_LOAD_SCALE * SCHED_POWER_SCALE);
-               load_above_capacity /= busiest->group_power;
+               load_above_capacity *= (SCHED_LOAD_SCALE * SCHED_CAPACITY_SCALE);
+               load_above_capacity /= busiest->group_capacity;
        }
 
        /*
@@ -6220,9 +6224,9 @@ static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *s
 
        /* How much load to actually move to equalise the imbalance */
        env->imbalance = min(
-               max_pull * busiest->group_power,
-               (sds->avg_load - local->avg_load) * local->group_power
-       ) / SCHED_POWER_SCALE;
+               max_pull * busiest->group_capacity,
+               (sds->avg_load - local->avg_load) * local->group_capacity
+       ) / SCHED_CAPACITY_SCALE;
 
        /*
         * if *imbalance is less than the average load per runnable task
@@ -6276,7 +6280,8 @@ static struct sched_group *find_busiest_group(struct lb_env *env)
        if (!sds.busiest || busiest->sum_nr_running == 0)
                goto out_balanced;
 
-       sds.avg_load = (SCHED_POWER_SCALE * sds.total_load) / sds.total_pwr;
+       sds.avg_load = (SCHED_CAPACITY_SCALE * sds.total_load)
+                                               / sds.total_capacity;
 
        /*
         * If the busiest group is imbalanced the below checks don't
@@ -6287,8 +6292,8 @@ static struct sched_group *find_busiest_group(struct lb_env *env)
                goto force_balance;
 
        /* SD_BALANCE_NEWIDLE trumps SMP nice when underutilized */
-       if (env->idle == CPU_NEWLY_IDLE && local->group_has_capacity &&
-           !busiest->group_has_capacity)
+       if (env->idle == CPU_NEWLY_IDLE && local->group_has_free_capacity &&
+           !busiest->group_has_free_capacity)
                goto force_balance;
 
        /*
@@ -6342,11 +6347,11 @@ static struct rq *find_busiest_queue(struct lb_env *env,
                                     struct sched_group *group)
 {
        struct rq *busiest = NULL, *rq;
-       unsigned long busiest_load = 0, busiest_power = 1;
+       unsigned long busiest_load = 0, busiest_capacity = 1;
        int i;
 
        for_each_cpu_and(i, sched_group_cpus(group), env->cpus) {
-               unsigned long power, capacity, wl;
+               unsigned long capacity, capacity_factor, wl;
                enum fbq_type rt;
 
                rq = cpu_rq(i);
@@ -6374,34 +6379,34 @@ static struct rq *find_busiest_queue(struct lb_env *env,
                if (rt > env->fbq_type)
                        continue;
 
-               power = power_of(i);
-               capacity = DIV_ROUND_CLOSEST(power, SCHED_POWER_SCALE);
-               if (!capacity)
-                       capacity = fix_small_capacity(env->sd, group);
+               capacity = capacity_of(i);
+               capacity_factor = DIV_ROUND_CLOSEST(capacity, SCHED_CAPACITY_SCALE);
+               if (!capacity_factor)
+                       capacity_factor = fix_small_capacity(env->sd, group);
 
                wl = weighted_cpuload(i);
 
                /*
                 * When comparing with imbalance, use weighted_cpuload()
-                * which is not scaled with the cpu power.
+                * which is not scaled with the cpu capacity.
                 */
-               if (capacity && rq->nr_running == 1 && wl > env->imbalance)
+               if (capacity_factor && rq->nr_running == 1 && wl > env->imbalance)
                        continue;
 
                /*
                 * For the load comparisons with the other cpu's, consider
-                * the weighted_cpuload() scaled with the cpu power, so that
-                * the load can be moved away from the cpu that is potentially
-                * running at a lower capacity.
+                * the weighted_cpuload() scaled with the cpu capacity, so
+                * that the load can be moved away from the cpu that is
+                * potentially running at a lower capacity.
                 *
-                * Thus we're looking for max(wl_i / power_i), crosswise
+                * Thus we're looking for max(wl_i / capacity_i), crosswise
                 * multiplication to rid ourselves of the division works out
-                * to: wl_i * power_j > wl_j * power_i;  where j is our
-                * previous maximum.
+                * to: wl_i * capacity_j > wl_j * capacity_i;  where j is
+                * our previous maximum.
                 */
-               if (wl * busiest_power > busiest_load * power) {
+               if (wl * busiest_capacity > busiest_load * capacity) {
                        busiest_load = wl;
-                       busiest_power = power;
+                       busiest_capacity = capacity;
                        busiest = rq;
                }
        }
@@ -6609,7 +6614,7 @@ static int load_balance(int this_cpu, struct rq *this_rq,
                 * We failed to reach balance because of affinity.
                 */
                if (sd_parent) {
-                       int *group_imbalance = &sd_parent->groups->sgp->imbalance;
+                       int *group_imbalance = &sd_parent->groups->sgc->imbalance;
 
                        if ((env.flags & LBF_SOME_PINNED) && env.imbalance > 0) {
                                *group_imbalance = 1;
@@ -6996,7 +7001,7 @@ static inline void set_cpu_sd_state_busy(void)
                goto unlock;
        sd->nohz_idle = 0;
 
-       atomic_inc(&sd->groups->sgp->nr_busy_cpus);
+       atomic_inc(&sd->groups->sgc->nr_busy_cpus);
 unlock:
        rcu_read_unlock();
 }
@@ -7013,7 +7018,7 @@ void set_cpu_sd_state_idle(void)
                goto unlock;
        sd->nohz_idle = 1;
 
-       atomic_dec(&sd->groups->sgp->nr_busy_cpus);
+       atomic_dec(&sd->groups->sgc->nr_busy_cpus);
 unlock:
        rcu_read_unlock();
 }
@@ -7192,12 +7197,17 @@ static void nohz_idle_balance(struct rq *this_rq, enum cpu_idle_type idle)
 
                rq = cpu_rq(balance_cpu);
 
-               raw_spin_lock_irq(&rq->lock);
-               update_rq_clock(rq);
-               update_idle_cpu_load(rq);
-               raw_spin_unlock_irq(&rq->lock);
-
-               rebalance_domains(rq, CPU_IDLE);
+               /*
+                * If time for next balance is due,
+                * do the balance.
+                */
+               if (time_after_eq(jiffies, rq->next_balance)) {
+                       raw_spin_lock_irq(&rq->lock);
+                       update_rq_clock(rq);
+                       update_idle_cpu_load(rq);
+                       raw_spin_unlock_irq(&rq->lock);
+                       rebalance_domains(rq, CPU_IDLE);
+               }
 
                if (time_after(this_rq->next_balance, rq->next_balance))
                        this_rq->next_balance = rq->next_balance;
@@ -7212,7 +7222,7 @@ static void nohz_idle_balance(struct rq *this_rq, enum cpu_idle_type idle)
  * of an idle cpu is the system.
  *   - This rq has more than one task.
  *   - At any scheduler domain level, this cpu's scheduler group has multiple
- *     busy cpu's exceeding the group's power.
+ *     busy cpu's exceeding the group's capacity.
  *   - For SD_ASYM_PACKING, if the lower numbered cpu's in the scheduler
  *     domain span are idle.
  */
@@ -7220,7 +7230,7 @@ static inline int nohz_kick_needed(struct rq *rq)
 {
        unsigned long now = jiffies;
        struct sched_domain *sd;
-       struct sched_group_power *sgp;
+       struct sched_group_capacity *sgc;
        int nr_busy, cpu = rq->cpu;
 
        if (unlikely(rq->idle_balance))
@@ -7250,8 +7260,8 @@ static inline int nohz_kick_needed(struct rq *rq)
        sd = rcu_dereference(per_cpu(sd_busy, cpu));
 
        if (sd) {
-               sgp = sd->groups->sgp;
-               nr_busy = atomic_read(&sgp->nr_busy_cpus);
+               sgc = sd->groups->sgc;
+               nr_busy = atomic_read(&sgc->nr_busy_cpus);
 
                if (nr_busy > 1)
                        goto need_kick_unlock;
index 5716929a2e3a4ad2d492ad2de0c3431291ea81a1..90284d117fe65ffc7ee1de7127995a750c84df92 100644 (file)
@@ -37,18 +37,18 @@ SCHED_FEAT(CACHE_HOT_BUDDY, true)
 SCHED_FEAT(WAKEUP_PREEMPTION, true)
 
 /*
- * Use arch dependent cpu power functions
+ * Use arch dependent cpu capacity functions
  */
-SCHED_FEAT(ARCH_POWER, true)
+SCHED_FEAT(ARCH_CAPACITY, true)
 
 SCHED_FEAT(HRTICK, false)
 SCHED_FEAT(DOUBLE_TICK, false)
 SCHED_FEAT(LB_BIAS, true)
 
 /*
- * Decrement CPU power based on time not spent running tasks
+ * Decrement CPU capacity based on time not spent running tasks
  */
-SCHED_FEAT(NONTASK_POWER, true)
+SCHED_FEAT(NONTASK_CAPACITY, true)
 
 /*
  * Queue remote wakeups on the target CPU and process them
index 25b9423abce9fa54052abb49946ad6561f1dfed7..cf009fb0bc25b1427683d614a3a40ad98ad0c958 100644 (file)
@@ -12,6 +12,8 @@
 
 #include <trace/events/power.h>
 
+#include "sched.h"
+
 static int __read_mostly cpu_idle_force_poll;
 
 void cpu_idle_poll_ctrl(bool enable)
@@ -67,6 +69,10 @@ void __weak arch_cpu_idle(void)
  * cpuidle_idle_call - the main idle function
  *
  * NOTE: no locks or semaphores should be used here
+ *
+ * On archs that support TIF_POLLING_NRFLAG, is called with polling
+ * set, and it returns with polling set.  If it ever stops polling, it
+ * must clear the polling bit.
  */
 static void cpuidle_idle_call(void)
 {
@@ -175,10 +181,22 @@ static void cpuidle_idle_call(void)
 
 /*
  * Generic idle loop implementation
+ *
+ * Called with polling cleared.
  */
 static void cpu_idle_loop(void)
 {
        while (1) {
+               /*
+                * If the arch has a polling bit, we maintain an invariant:
+                *
+                * Our polling bit is clear if we're not scheduled (i.e. if
+                * rq->curr != rq->idle).  This means that, if rq->idle has
+                * the polling bit set, then setting need_resched is
+                * guaranteed to cause the cpu to reschedule.
+                */
+
+               __current_set_polling();
                tick_nohz_idle_enter();
 
                while (!need_resched()) {
@@ -218,6 +236,17 @@ static void cpu_idle_loop(void)
                 */
                preempt_set_need_resched();
                tick_nohz_idle_exit();
+               __current_clr_polling();
+
+               /*
+                * We promise to call sched_ttwu_pending and reschedule
+                * if need_resched is set while polling is set.  That
+                * means that clearing polling needs to be visible
+                * before doing these things.
+                */
+               smp_mb__after_atomic();
+
+               sched_ttwu_pending();
                schedule_preempt_disabled();
        }
 }
@@ -239,7 +268,6 @@ void cpu_startup_entry(enum cpuhp_state state)
         */
        boot_init_stack_canary();
 #endif
-       __current_set_polling();
        arch_cpu_idle_prepare();
        cpu_idle_loop();
 }
index b3512f1afce9361588a8ccb50f7ad75540122426..a49083192c64c306952c752ec6b3a05a0df7205d 100644 (file)
@@ -918,7 +918,6 @@ static void update_curr_rt(struct rq *rq)
 {
        struct task_struct *curr = rq->curr;
        struct sched_rt_entity *rt_se = &curr->rt;
-       struct rt_rq *rt_rq = rt_rq_of_se(rt_se);
        u64 delta_exec;
 
        if (curr->sched_class != &rt_sched_class)
@@ -943,7 +942,7 @@ static void update_curr_rt(struct rq *rq)
                return;
 
        for_each_sched_rt_entity(rt_se) {
-               rt_rq = rt_rq_of_se(rt_se);
+               struct rt_rq *rt_rq = rt_rq_of_se(rt_se);
 
                if (sched_rt_runtime(rt_rq) != RUNTIME_INF) {
                        raw_spin_lock(&rt_rq->rt_runtime_lock);
index e47679b04d167b8cdc2abf91326f7d52243b3996..31cc02ebc54ed82f5bf3f62fae879a1c0343a97d 100644 (file)
@@ -567,7 +567,7 @@ struct rq {
        struct root_domain *rd;
        struct sched_domain *sd;
 
-       unsigned long cpu_power;
+       unsigned long cpu_capacity;
 
        unsigned char idle_balance;
        /* For active balancing */
@@ -670,6 +670,8 @@ extern int migrate_swap(struct task_struct *, struct task_struct *);
 
 #ifdef CONFIG_SMP
 
+extern void sched_ttwu_pending(void);
+
 #define rcu_dereference_check_sched_domain(p) \
        rcu_dereference_check((p), \
                              lockdep_is_held(&sched_domains_mutex))
@@ -728,15 +730,15 @@ DECLARE_PER_CPU(struct sched_domain *, sd_numa);
 DECLARE_PER_CPU(struct sched_domain *, sd_busy);
 DECLARE_PER_CPU(struct sched_domain *, sd_asym);
 
-struct sched_group_power {
+struct sched_group_capacity {
        atomic_t ref;
        /*
-        * CPU power of this group, SCHED_LOAD_SCALE being max power for a
-        * single CPU.
+        * CPU capacity of this group, SCHED_LOAD_SCALE being max capacity
+        * for a single CPU.
         */
-       unsigned int power, power_orig;
+       unsigned int capacity, capacity_orig;
        unsigned long next_update;
-       int imbalance; /* XXX unrelated to power but shared group state */
+       int imbalance; /* XXX unrelated to capacity but shared group state */
        /*
         * Number of busy cpus in this group.
         */
@@ -750,7 +752,7 @@ struct sched_group {
        atomic_t ref;
 
        unsigned int group_weight;
-       struct sched_group_power *sgp;
+       struct sched_group_capacity *sgc;
 
        /*
         * The CPUs this group covers.
@@ -773,7 +775,7 @@ static inline struct cpumask *sched_group_cpus(struct sched_group *sg)
  */
 static inline struct cpumask *sched_group_mask(struct sched_group *sg)
 {
-       return to_cpumask(sg->sgp->cpumask);
+       return to_cpumask(sg->sgc->cpumask);
 }
 
 /**
@@ -787,6 +789,10 @@ static inline unsigned int group_first_cpu(struct sched_group *group)
 
 extern int group_balance_cpu(struct sched_group *sg);
 
+#else
+
+static inline void sched_ttwu_pending(void) { }
+
 #endif /* CONFIG_SMP */
 
 #include "stats.h"
@@ -1167,7 +1173,7 @@ extern const struct sched_class idle_sched_class;
 
 #ifdef CONFIG_SMP
 
-extern void update_group_power(struct sched_domain *sd, int cpu);
+extern void update_group_capacity(struct sched_domain *sd, int cpu);
 
 extern void trigger_load_balance(struct rq *rq);
 
index f6d76bebe69f0f1adae6507d6c0d0ed0e034cb61..301bbc24739c9ff8fd58fe95149ad242da8e50b7 100644 (file)
@@ -54,8 +54,7 @@
 struct seccomp_filter {
        atomic_t usage;
        struct seccomp_filter *prev;
-       unsigned short len;  /* Instruction count */
-       struct sock_filter_int insnsi[];
+       struct sk_filter *prog;
 };
 
 /* Limit any path through the tree to 256KB worth of instructions. */
@@ -104,60 +103,59 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen)
                u32 k = ftest->k;
 
                switch (code) {
-               case BPF_S_LD_W_ABS:
+               case BPF_LD | BPF_W | BPF_ABS:
                        ftest->code = BPF_LDX | BPF_W | BPF_ABS;
                        /* 32-bit aligned and not out of bounds. */
                        if (k >= sizeof(struct seccomp_data) || k & 3)
                                return -EINVAL;
                        continue;
-               case BPF_S_LD_W_LEN:
+               case BPF_LD | BPF_W | BPF_LEN:
                        ftest->code = BPF_LD | BPF_IMM;
                        ftest->k = sizeof(struct seccomp_data);
                        continue;
-               case BPF_S_LDX_W_LEN:
+               case BPF_LDX | BPF_W | BPF_LEN:
                        ftest->code = BPF_LDX | BPF_IMM;
                        ftest->k = sizeof(struct seccomp_data);
                        continue;
                /* Explicitly include allowed calls. */
-               case BPF_S_RET_K:
-               case BPF_S_RET_A:
-               case BPF_S_ALU_ADD_K:
-               case BPF_S_ALU_ADD_X:
-               case BPF_S_ALU_SUB_K:
-               case BPF_S_ALU_SUB_X:
-               case BPF_S_ALU_MUL_K:
-               case BPF_S_ALU_MUL_X:
-               case BPF_S_ALU_DIV_X:
-               case BPF_S_ALU_AND_K:
-               case BPF_S_ALU_AND_X:
-               case BPF_S_ALU_OR_K:
-               case BPF_S_ALU_OR_X:
-               case BPF_S_ALU_XOR_K:
-               case BPF_S_ALU_XOR_X:
-               case BPF_S_ALU_LSH_K:
-               case BPF_S_ALU_LSH_X:
-               case BPF_S_ALU_RSH_K:
-               case BPF_S_ALU_RSH_X:
-               case BPF_S_ALU_NEG:
-               case BPF_S_LD_IMM:
-               case BPF_S_LDX_IMM:
-               case BPF_S_MISC_TAX:
-               case BPF_S_MISC_TXA:
-               case BPF_S_ALU_DIV_K:
-               case BPF_S_LD_MEM:
-               case BPF_S_LDX_MEM:
-               case BPF_S_ST:
-               case BPF_S_STX:
-               case BPF_S_JMP_JA:
-               case BPF_S_JMP_JEQ_K:
-               case BPF_S_JMP_JEQ_X:
-               case BPF_S_JMP_JGE_K:
-               case BPF_S_JMP_JGE_X:
-               case BPF_S_JMP_JGT_K:
-               case BPF_S_JMP_JGT_X:
-               case BPF_S_JMP_JSET_K:
-               case BPF_S_JMP_JSET_X:
-                       sk_decode_filter(ftest, ftest);
+               case BPF_RET | BPF_K:
+               case BPF_RET | BPF_A:
+               case BPF_ALU | BPF_ADD | BPF_K:
+               case BPF_ALU | BPF_ADD | BPF_X:
+               case BPF_ALU | BPF_SUB | BPF_K:
+               case BPF_ALU | BPF_SUB | BPF_X:
+               case BPF_ALU | BPF_MUL | BPF_K:
+               case BPF_ALU | BPF_MUL | BPF_X:
+               case BPF_ALU | BPF_DIV | BPF_K:
+               case BPF_ALU | BPF_DIV | BPF_X:
+               case BPF_ALU | BPF_AND | BPF_K:
+               case BPF_ALU | BPF_AND | BPF_X:
+               case BPF_ALU | BPF_OR | BPF_K:
+               case BPF_ALU | BPF_OR | BPF_X:
+               case BPF_ALU | BPF_XOR | BPF_K:
+               case BPF_ALU | BPF_XOR | BPF_X:
+               case BPF_ALU | BPF_LSH | BPF_K:
+               case BPF_ALU | BPF_LSH | BPF_X:
+               case BPF_ALU | BPF_RSH | BPF_K:
+               case BPF_ALU | BPF_RSH | BPF_X:
+               case BPF_ALU | BPF_NEG:
+               case BPF_LD | BPF_IMM:
+               case BPF_LDX | BPF_IMM:
+               case BPF_MISC | BPF_TAX:
+               case BPF_MISC | BPF_TXA:
+               case BPF_LD | BPF_MEM:
+               case BPF_LDX | BPF_MEM:
+               case BPF_ST:
+               case BPF_STX:
+               case BPF_JMP | BPF_JA:
+               case BPF_JMP | BPF_JEQ | BPF_K:
+               case BPF_JMP | BPF_JEQ | BPF_X:
+               case BPF_JMP | BPF_JGE | BPF_K:
+               case BPF_JMP | BPF_JGE | BPF_X:
+               case BPF_JMP | BPF_JGT | BPF_K:
+               case BPF_JMP | BPF_JGT | BPF_X:
+               case BPF_JMP | BPF_JSET | BPF_K:
+               case BPF_JMP | BPF_JSET | BPF_X:
                        continue;
                default:
                        return -EINVAL;
@@ -189,7 +187,8 @@ static u32 seccomp_run_filters(int syscall)
         * value always takes priority (ignoring the DATA).
         */
        for (f = current->seccomp.filter; f; f = f->prev) {
-               u32 cur_ret = sk_run_filter_int_seccomp(&sd, f->insnsi);
+               u32 cur_ret = SK_RUN_FILTER(f->prog, (void *)&sd);
+
                if ((cur_ret & SECCOMP_RET_ACTION) < (ret & SECCOMP_RET_ACTION))
                        ret = cur_ret;
        }
@@ -215,7 +214,7 @@ static long seccomp_attach_filter(struct sock_fprog *fprog)
                return -EINVAL;
 
        for (filter = current->seccomp.filter; filter; filter = filter->prev)
-               total_insns += filter->len + 4;  /* include a 4 instr penalty */
+               total_insns += filter->prog->len + 4;  /* include a 4 instr penalty */
        if (total_insns > MAX_INSNS_PER_PATH)
                return -ENOMEM;
 
@@ -256,19 +255,25 @@ static long seccomp_attach_filter(struct sock_fprog *fprog)
 
        /* Allocate a new seccomp_filter */
        ret = -ENOMEM;
-       filter = kzalloc(sizeof(struct seccomp_filter) +
-                        sizeof(struct sock_filter_int) * new_len,
+       filter = kzalloc(sizeof(struct seccomp_filter),
                         GFP_KERNEL|__GFP_NOWARN);
        if (!filter)
                goto free_prog;
 
-       ret = sk_convert_filter(fp, fprog->len, filter->insnsi, &new_len);
-       if (ret)
+       filter->prog = kzalloc(sk_filter_size(new_len),
+                              GFP_KERNEL|__GFP_NOWARN);
+       if (!filter->prog)
                goto free_filter;
+
+       ret = sk_convert_filter(fp, fprog->len, filter->prog->insnsi, &new_len);
+       if (ret)
+               goto free_filter_prog;
        kfree(fp);
 
        atomic_set(&filter->usage, 1);
-       filter->len = new_len;
+       filter->prog->len = new_len;
+
+       sk_filter_select_runtime(filter->prog);
 
        /*
         * If there is an existing filter, make it the prev and don't drop its
@@ -278,6 +283,8 @@ static long seccomp_attach_filter(struct sock_fprog *fprog)
        current->seccomp.filter = filter;
        return 0;
 
+free_filter_prog:
+       kfree(filter->prog);
 free_filter:
        kfree(filter);
 free_prog:
@@ -330,6 +337,7 @@ void put_seccomp_filter(struct task_struct *tsk)
        while (orig && atomic_dec_and_test(&orig->usage)) {
                struct seccomp_filter *freeme = orig;
                orig = orig->prev;
+               sk_filter_free(freeme->prog);
                kfree(freeme);
        }
 }
index db19e3e2aa4bd000d96af8d3380af60148a01b10..ba9ed453c4ed497d62537b019b153fbf47171084 100644 (file)
@@ -2568,11 +2568,11 @@ int proc_do_large_bitmap(struct ctl_table *table, int write,
        bool first = 1;
        size_t left = *lenp;
        unsigned long bitmap_len = table->maxlen;
-       unsigned long *bitmap = (unsigned long *) table->data;
+       unsigned long *bitmap = *(unsigned long **) table->data;
        unsigned long *tmp_bitmap = NULL;
        char tr_a[] = { '-', ',', '\n' }, tr_b[] = { ',', '\n', 0 }, c;
 
-       if (!bitmap_len || !left || (*ppos && !write)) {
+       if (!bitmap || !bitmap_len || !left || (*ppos && !write)) {
                *lenp = 0;
                return 0;
        }
index c634868c2921ca39837c3a36773c36fa4a3273d5..7c56c3d06943060d13ed611da638a09ccfa70ef4 100644 (file)
@@ -543,7 +543,7 @@ static void rb_wake_up_waiters(struct irq_work *work)
  * as data is added to any of the @buffer's cpu buffers. Otherwise
  * it will wait for data to be added to a specific cpu buffer.
  */
-void ring_buffer_wait(struct ring_buffer *buffer, int cpu)
+int ring_buffer_wait(struct ring_buffer *buffer, int cpu)
 {
        struct ring_buffer_per_cpu *cpu_buffer;
        DEFINE_WAIT(wait);
@@ -557,6 +557,8 @@ void ring_buffer_wait(struct ring_buffer *buffer, int cpu)
        if (cpu == RING_BUFFER_ALL_CPUS)
                work = &buffer->irq_work;
        else {
+               if (!cpumask_test_cpu(cpu, buffer->cpumask))
+                       return -ENODEV;
                cpu_buffer = buffer->buffers[cpu];
                work = &cpu_buffer->irq_work;
        }
@@ -591,6 +593,7 @@ void ring_buffer_wait(struct ring_buffer *buffer, int cpu)
                schedule();
 
        finish_wait(&work->waiters, &wait);
+       return 0;
 }
 
 /**
index 16f7038d1f4d7a245a3350cf9b647992072e94e0..384ede3117172fa9e6582ead5c479f42c8f98590 100644 (file)
@@ -1085,13 +1085,13 @@ update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu)
 }
 #endif /* CONFIG_TRACER_MAX_TRACE */
 
-static void wait_on_pipe(struct trace_iterator *iter)
+static int wait_on_pipe(struct trace_iterator *iter)
 {
        /* Iterators are static, they should be filled or empty */
        if (trace_buffer_iter(iter, iter->cpu_file))
-               return;
+               return 0;
 
-       ring_buffer_wait(iter->trace_buffer->buffer, iter->cpu_file);
+       return ring_buffer_wait(iter->trace_buffer->buffer, iter->cpu_file);
 }
 
 #ifdef CONFIG_FTRACE_STARTUP_TEST
@@ -1338,7 +1338,7 @@ static int trace_create_savedcmd(void)
 {
        int ret;
 
-       savedcmd = kmalloc(sizeof(struct saved_cmdlines_buffer), GFP_KERNEL);
+       savedcmd = kmalloc(sizeof(*savedcmd), GFP_KERNEL);
        if (!savedcmd)
                return -ENOMEM;
 
@@ -3840,7 +3840,7 @@ tracing_saved_cmdlines_size_read(struct file *filp, char __user *ubuf,
        int r;
 
        arch_spin_lock(&trace_cmdline_lock);
-       r = sprintf(buf, "%u\n", savedcmd->cmdline_num);
+       r = scnprintf(buf, sizeof(buf), "%u\n", savedcmd->cmdline_num);
        arch_spin_unlock(&trace_cmdline_lock);
 
        return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
@@ -3857,7 +3857,7 @@ static int tracing_resize_saved_cmdlines(unsigned int val)
 {
        struct saved_cmdlines_buffer *s, *savedcmd_temp;
 
-       s = kmalloc(sizeof(struct saved_cmdlines_buffer), GFP_KERNEL);
+       s = kmalloc(sizeof(*s), GFP_KERNEL);
        if (!s)
                return -ENOMEM;
 
@@ -4378,6 +4378,7 @@ tracing_poll_pipe(struct file *filp, poll_table *poll_table)
 static int tracing_wait_pipe(struct file *filp)
 {
        struct trace_iterator *iter = filp->private_data;
+       int ret;
 
        while (trace_empty(iter)) {
 
@@ -4399,10 +4400,13 @@ static int tracing_wait_pipe(struct file *filp)
 
                mutex_unlock(&iter->mutex);
 
-               wait_on_pipe(iter);
+               ret = wait_on_pipe(iter);
 
                mutex_lock(&iter->mutex);
 
+               if (ret)
+                       return ret;
+
                if (signal_pending(current))
                        return -EINTR;
        }
@@ -5327,8 +5331,12 @@ tracing_buffers_read(struct file *filp, char __user *ubuf,
                                goto out_unlock;
                        }
                        mutex_unlock(&trace_types_lock);
-                       wait_on_pipe(iter);
+                       ret = wait_on_pipe(iter);
                        mutex_lock(&trace_types_lock);
+                       if (ret) {
+                               size = ret;
+                               goto out_unlock;
+                       }
                        if (signal_pending(current)) {
                                size = -EINTR;
                                goto out_unlock;
@@ -5538,8 +5546,10 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos,
                        goto out;
                }
                mutex_unlock(&trace_types_lock);
-               wait_on_pipe(iter);
+               ret = wait_on_pipe(iter);
                mutex_lock(&trace_types_lock);
+               if (ret)
+                       goto out;
                if (signal_pending(current)) {
                        ret = -EINTR;
                        goto out;
@@ -6232,22 +6242,25 @@ static int allocate_trace_buffers(struct trace_array *tr, int size)
        return 0;
 }
 
+static void free_trace_buffer(struct trace_buffer *buf)
+{
+       if (buf->buffer) {
+               ring_buffer_free(buf->buffer);
+               buf->buffer = NULL;
+               free_percpu(buf->data);
+               buf->data = NULL;
+       }
+}
+
 static void free_trace_buffers(struct trace_array *tr)
 {
        if (!tr)
                return;
 
-       if (tr->trace_buffer.buffer) {
-               ring_buffer_free(tr->trace_buffer.buffer);
-               tr->trace_buffer.buffer = NULL;
-               free_percpu(tr->trace_buffer.data);
-       }
+       free_trace_buffer(&tr->trace_buffer);
 
 #ifdef CONFIG_TRACER_MAX_TRACE
-       if (tr->max_buffer.buffer) {
-               ring_buffer_free(tr->max_buffer.buffer);
-               tr->max_buffer.buffer = NULL;
-       }
+       free_trace_buffer(&tr->max_buffer);
 #endif
 }
 
index 9e82551dd566057c61a862e7b27b395939d270e3..9258f5a815db4d4fe4a8d314a2593218b3c4e5ce 100644 (file)
@@ -252,7 +252,7 @@ static inline struct trace_array *top_trace_array(void)
 {
        struct trace_array *tr;
 
-       if (list_empty(ftrace_trace_arrays.prev))
+       if (list_empty(&ftrace_trace_arrays))
                return NULL;
 
        tr = list_entry(ftrace_trace_arrays.prev,
index c894614de14d8efdbbc145141d19f9a56af9a984..5d12bb407b44290fb6a8abad49ca5deef36691a0 100644 (file)
@@ -248,8 +248,8 @@ void perf_trace_del(struct perf_event *p_event, int flags)
        tp_event->class->reg(tp_event, TRACE_REG_PERF_DEL, p_event);
 }
 
-__kprobes void *perf_trace_buf_prepare(int size, unsigned short type,
-                                      struct pt_regs *regs, int *rctxp)
+void *perf_trace_buf_prepare(int size, unsigned short type,
+                            struct pt_regs *regs, int *rctxp)
 {
        struct trace_entry *entry;
        unsigned long flags;
@@ -281,6 +281,7 @@ __kprobes void *perf_trace_buf_prepare(int size, unsigned short type,
        return raw_data;
 }
 EXPORT_SYMBOL_GPL(perf_trace_buf_prepare);
+NOKPROBE_SYMBOL(perf_trace_buf_prepare);
 
 #ifdef CONFIG_FUNCTION_TRACER
 static void
index ef2fba1f46b598eae5c7462601a96d98f65ef322..282f6e4e553988bb6b75a974bb62742d0a80a7e2 100644 (file)
@@ -40,27 +40,27 @@ struct trace_kprobe {
        (sizeof(struct probe_arg) * (n)))
 
 
-static __kprobes bool trace_kprobe_is_return(struct trace_kprobe *tk)
+static nokprobe_inline bool trace_kprobe_is_return(struct trace_kprobe *tk)
 {
        return tk->rp.handler != NULL;
 }
 
-static __kprobes const char *trace_kprobe_symbol(struct trace_kprobe *tk)
+static nokprobe_inline const char *trace_kprobe_symbol(struct trace_kprobe *tk)
 {
        return tk->symbol ? tk->symbol : "unknown";
 }
 
-static __kprobes unsigned long trace_kprobe_offset(struct trace_kprobe *tk)
+static nokprobe_inline unsigned long trace_kprobe_offset(struct trace_kprobe *tk)
 {
        return tk->rp.kp.offset;
 }
 
-static __kprobes bool trace_kprobe_has_gone(struct trace_kprobe *tk)
+static nokprobe_inline bool trace_kprobe_has_gone(struct trace_kprobe *tk)
 {
        return !!(kprobe_gone(&tk->rp.kp));
 }
 
-static __kprobes bool trace_kprobe_within_module(struct trace_kprobe *tk,
+static nokprobe_inline bool trace_kprobe_within_module(struct trace_kprobe *tk,
                                                 struct module *mod)
 {
        int len = strlen(mod->name);
@@ -68,7 +68,7 @@ static __kprobes bool trace_kprobe_within_module(struct trace_kprobe *tk,
        return strncmp(mod->name, name, len) == 0 && name[len] == ':';
 }
 
-static __kprobes bool trace_kprobe_is_on_module(struct trace_kprobe *tk)
+static nokprobe_inline bool trace_kprobe_is_on_module(struct trace_kprobe *tk)
 {
        return !!strchr(trace_kprobe_symbol(tk), ':');
 }
@@ -132,19 +132,21 @@ struct symbol_cache *alloc_symbol_cache(const char *sym, long offset)
  * Kprobes-specific fetch functions
  */
 #define DEFINE_FETCH_stack(type)                                       \
-static __kprobes void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,\
+static void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,         \
                                          void *offset, void *dest)     \
 {                                                                      \
        *(type *)dest = (type)regs_get_kernel_stack_nth(regs,           \
                                (unsigned int)((unsigned long)offset)); \
-}
+}                                                                      \
+NOKPROBE_SYMBOL(FETCH_FUNC_NAME(stack, type));
+
 DEFINE_BASIC_FETCH_FUNCS(stack)
 /* No string on the stack entry */
 #define fetch_stack_string     NULL
 #define fetch_stack_string_size        NULL
 
 #define DEFINE_FETCH_memory(type)                                      \
-static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\
+static void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,                \
                                          void *addr, void *dest)       \
 {                                                                      \
        type retval;                                                    \
@@ -152,14 +154,16 @@ static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\
                *(type *)dest = 0;                                      \
        else                                                            \
                *(type *)dest = retval;                                 \
-}
+}                                                                      \
+NOKPROBE_SYMBOL(FETCH_FUNC_NAME(memory, type));
+
 DEFINE_BASIC_FETCH_FUNCS(memory)
 /*
  * Fetch a null-terminated string. Caller MUST set *(u32 *)dest with max
  * length and relative data location.
  */
-static __kprobes void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs,
-                                                     void *addr, void *dest)
+static void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs,
+                                           void *addr, void *dest)
 {
        long ret;
        int maxlen = get_rloc_len(*(u32 *)dest);
@@ -193,10 +197,11 @@ static __kprobes void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs,
                                              get_rloc_offs(*(u32 *)dest));
        }
 }
+NOKPROBE_SYMBOL(FETCH_FUNC_NAME(memory, string));
 
 /* Return the length of string -- including null terminal byte */
-static __kprobes void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs,
-                                                       void *addr, void *dest)
+static void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs,
+                                                void *addr, void *dest)
 {
        mm_segment_t old_fs;
        int ret, len = 0;
@@ -219,17 +224,19 @@ static __kprobes void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs,
        else
                *(u32 *)dest = len;
 }
+NOKPROBE_SYMBOL(FETCH_FUNC_NAME(memory, string_size));
 
 #define DEFINE_FETCH_symbol(type)                                      \
-__kprobes void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs,     \
-                                         void *data, void *dest)       \
+void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs, void *data, void *dest)\
 {                                                                      \
        struct symbol_cache *sc = data;                                 \
        if (sc->addr)                                                   \
                fetch_memory_##type(regs, (void *)sc->addr, dest);      \
        else                                                            \
                *(type *)dest = 0;                                      \
-}
+}                                                                      \
+NOKPROBE_SYMBOL(FETCH_FUNC_NAME(symbol, type));
+
 DEFINE_BASIC_FETCH_FUNCS(symbol)
 DEFINE_FETCH_symbol(string)
 DEFINE_FETCH_symbol(string_size)
@@ -907,7 +914,7 @@ static const struct file_operations kprobe_profile_ops = {
 };
 
 /* Kprobe handler */
-static __kprobes void
+static nokprobe_inline void
 __kprobe_trace_func(struct trace_kprobe *tk, struct pt_regs *regs,
                    struct ftrace_event_file *ftrace_file)
 {
@@ -943,7 +950,7 @@ __kprobe_trace_func(struct trace_kprobe *tk, struct pt_regs *regs,
                                         entry, irq_flags, pc, regs);
 }
 
-static __kprobes void
+static void
 kprobe_trace_func(struct trace_kprobe *tk, struct pt_regs *regs)
 {
        struct event_file_link *link;
@@ -951,9 +958,10 @@ kprobe_trace_func(struct trace_kprobe *tk, struct pt_regs *regs)
        list_for_each_entry_rcu(link, &tk->tp.files, list)
                __kprobe_trace_func(tk, regs, link->file);
 }
+NOKPROBE_SYMBOL(kprobe_trace_func);
 
 /* Kretprobe handler */
-static __kprobes void
+static nokprobe_inline void
 __kretprobe_trace_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
                       struct pt_regs *regs,
                       struct ftrace_event_file *ftrace_file)
@@ -991,7 +999,7 @@ __kretprobe_trace_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
                                         entry, irq_flags, pc, regs);
 }
 
-static __kprobes void
+static void
 kretprobe_trace_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
                     struct pt_regs *regs)
 {
@@ -1000,6 +1008,7 @@ kretprobe_trace_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
        list_for_each_entry_rcu(link, &tk->tp.files, list)
                __kretprobe_trace_func(tk, ri, regs, link->file);
 }
+NOKPROBE_SYMBOL(kretprobe_trace_func);
 
 /* Event entry printers */
 static enum print_line_t
@@ -1131,7 +1140,7 @@ static int kretprobe_event_define_fields(struct ftrace_event_call *event_call)
 #ifdef CONFIG_PERF_EVENTS
 
 /* Kprobe profile handler */
-static __kprobes void
+static void
 kprobe_perf_func(struct trace_kprobe *tk, struct pt_regs *regs)
 {
        struct ftrace_event_call *call = &tk->tp.call;
@@ -1158,9 +1167,10 @@ kprobe_perf_func(struct trace_kprobe *tk, struct pt_regs *regs)
        store_trace_args(sizeof(*entry), &tk->tp, regs, (u8 *)&entry[1], dsize);
        perf_trace_buf_submit(entry, size, rctx, 0, 1, regs, head, NULL);
 }
+NOKPROBE_SYMBOL(kprobe_perf_func);
 
 /* Kretprobe profile handler */
-static __kprobes void
+static void
 kretprobe_perf_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
                    struct pt_regs *regs)
 {
@@ -1188,6 +1198,7 @@ kretprobe_perf_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
        store_trace_args(sizeof(*entry), &tk->tp, regs, (u8 *)&entry[1], dsize);
        perf_trace_buf_submit(entry, size, rctx, 0, 1, regs, head, NULL);
 }
+NOKPROBE_SYMBOL(kretprobe_perf_func);
 #endif /* CONFIG_PERF_EVENTS */
 
 /*
@@ -1196,9 +1207,8 @@ kretprobe_perf_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
  * kprobe_trace_self_tests_init() does enable_trace_probe/disable_trace_probe
  * lockless, but we can't race with this __init function.
  */
-static __kprobes
-int kprobe_register(struct ftrace_event_call *event,
-                   enum trace_reg type, void *data)
+static int kprobe_register(struct ftrace_event_call *event,
+                          enum trace_reg type, void *data)
 {
        struct trace_kprobe *tk = (struct trace_kprobe *)event->data;
        struct ftrace_event_file *file = data;
@@ -1224,8 +1234,7 @@ int kprobe_register(struct ftrace_event_call *event,
        return 0;
 }
 
-static __kprobes
-int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs)
+static int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs)
 {
        struct trace_kprobe *tk = container_of(kp, struct trace_kprobe, rp.kp);
 
@@ -1239,9 +1248,10 @@ int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs)
 #endif
        return 0;       /* We don't tweek kernel, so just return 0 */
 }
+NOKPROBE_SYMBOL(kprobe_dispatcher);
 
-static __kprobes
-int kretprobe_dispatcher(struct kretprobe_instance *ri, struct pt_regs *regs)
+static int
+kretprobe_dispatcher(struct kretprobe_instance *ri, struct pt_regs *regs)
 {
        struct trace_kprobe *tk = container_of(ri->rp, struct trace_kprobe, rp);
 
@@ -1255,6 +1265,7 @@ int kretprobe_dispatcher(struct kretprobe_instance *ri, struct pt_regs *regs)
 #endif
        return 0;       /* We don't tweek kernel, so just return 0 */
 }
+NOKPROBE_SYMBOL(kretprobe_dispatcher);
 
 static struct trace_event_functions kretprobe_funcs = {
        .trace          = print_kretprobe_event
index 8364a421b4dfc7bb8935cf4e9c128aa99de0776f..d4b9fc22cd27fb0a87029424afde729a2b843a4d 100644 (file)
@@ -37,13 +37,13 @@ const char *reserved_field_names[] = {
 
 /* Printing  in basic type function template */
 #define DEFINE_BASIC_PRINT_TYPE_FUNC(type, fmt)                                \
-__kprobes int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s,  \
-                                               const char *name,       \
-                                               void *data, void *ent)  \
+int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s, const char *name,  \
+                               void *data, void *ent)                  \
 {                                                                      \
        return trace_seq_printf(s, " %s=" fmt, name, *(type *)data);    \
 }                                                                      \
-const char PRINT_TYPE_FMT_NAME(type)[] = fmt;
+const char PRINT_TYPE_FMT_NAME(type)[] = fmt;                          \
+NOKPROBE_SYMBOL(PRINT_TYPE_FUNC_NAME(type));
 
 DEFINE_BASIC_PRINT_TYPE_FUNC(u8 , "0x%x")
 DEFINE_BASIC_PRINT_TYPE_FUNC(u16, "0x%x")
@@ -55,9 +55,8 @@ DEFINE_BASIC_PRINT_TYPE_FUNC(s32, "%d")
 DEFINE_BASIC_PRINT_TYPE_FUNC(s64, "%Ld")
 
 /* Print type function for string type */
-__kprobes int PRINT_TYPE_FUNC_NAME(string)(struct trace_seq *s,
-                                                 const char *name,
-                                                 void *data, void *ent)
+int PRINT_TYPE_FUNC_NAME(string)(struct trace_seq *s, const char *name,
+                                void *data, void *ent)
 {
        int len = *(u32 *)data >> 16;
 
@@ -67,6 +66,7 @@ __kprobes int PRINT_TYPE_FUNC_NAME(string)(struct trace_seq *s,
                return trace_seq_printf(s, " %s=\"%s\"", name,
                                        (const char *)get_loc_data(data, ent));
 }
+NOKPROBE_SYMBOL(PRINT_TYPE_FUNC_NAME(string));
 
 const char PRINT_TYPE_FMT_NAME(string)[] = "\\\"%s\\\"";
 
@@ -81,23 +81,24 @@ const char PRINT_TYPE_FMT_NAME(string)[] = "\\\"%s\\\"";
 
 /* Data fetch function templates */
 #define DEFINE_FETCH_reg(type)                                         \
-__kprobes void FETCH_FUNC_NAME(reg, type)(struct pt_regs *regs,                \
-                                       void *offset, void *dest)       \
+void FETCH_FUNC_NAME(reg, type)(struct pt_regs *regs, void *offset, void *dest)        \
 {                                                                      \
        *(type *)dest = (type)regs_get_register(regs,                   \
                                (unsigned int)((unsigned long)offset)); \
-}
+}                                                                      \
+NOKPROBE_SYMBOL(FETCH_FUNC_NAME(reg, type));
 DEFINE_BASIC_FETCH_FUNCS(reg)
 /* No string on the register */
 #define fetch_reg_string       NULL
 #define fetch_reg_string_size  NULL
 
 #define DEFINE_FETCH_retval(type)                                      \
-__kprobes void FETCH_FUNC_NAME(retval, type)(struct pt_regs *regs,     \
-                                         void *dummy, void *dest)      \
+void FETCH_FUNC_NAME(retval, type)(struct pt_regs *regs,               \
+                                  void *dummy, void *dest)             \
 {                                                                      \
        *(type *)dest = (type)regs_return_value(regs);                  \
-}
+}                                                                      \
+NOKPROBE_SYMBOL(FETCH_FUNC_NAME(retval, type));
 DEFINE_BASIC_FETCH_FUNCS(retval)
 /* No string on the retval */
 #define fetch_retval_string            NULL
@@ -112,8 +113,8 @@ struct deref_fetch_param {
 };
 
 #define DEFINE_FETCH_deref(type)                                       \
-__kprobes void FETCH_FUNC_NAME(deref, type)(struct pt_regs *regs,      \
-                                           void *data, void *dest)     \
+void FETCH_FUNC_NAME(deref, type)(struct pt_regs *regs,                        \
+                                 void *data, void *dest)               \
 {                                                                      \
        struct deref_fetch_param *dprm = data;                          \
        unsigned long addr;                                             \
@@ -123,12 +124,13 @@ __kprobes void FETCH_FUNC_NAME(deref, type)(struct pt_regs *regs, \
                dprm->fetch(regs, (void *)addr, dest);                  \
        } else                                                          \
                *(type *)dest = 0;                                      \
-}
+}                                                                      \
+NOKPROBE_SYMBOL(FETCH_FUNC_NAME(deref, type));
 DEFINE_BASIC_FETCH_FUNCS(deref)
 DEFINE_FETCH_deref(string)
 
-__kprobes void FETCH_FUNC_NAME(deref, string_size)(struct pt_regs *regs,
-                                                  void *data, void *dest)
+void FETCH_FUNC_NAME(deref, string_size)(struct pt_regs *regs,
+                                        void *data, void *dest)
 {
        struct deref_fetch_param *dprm = data;
        unsigned long addr;
@@ -140,16 +142,18 @@ __kprobes void FETCH_FUNC_NAME(deref, string_size)(struct pt_regs *regs,
        } else
                *(string_size *)dest = 0;
 }
+NOKPROBE_SYMBOL(FETCH_FUNC_NAME(deref, string_size));
 
-static __kprobes void update_deref_fetch_param(struct deref_fetch_param *data)
+static void update_deref_fetch_param(struct deref_fetch_param *data)
 {
        if (CHECK_FETCH_FUNCS(deref, data->orig.fn))
                update_deref_fetch_param(data->orig.data);
        else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn))
                update_symbol_cache(data->orig.data);
 }
+NOKPROBE_SYMBOL(update_deref_fetch_param);
 
-static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data)
+static void free_deref_fetch_param(struct deref_fetch_param *data)
 {
        if (CHECK_FETCH_FUNCS(deref, data->orig.fn))
                free_deref_fetch_param(data->orig.data);
@@ -157,6 +161,7 @@ static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data)
                free_symbol_cache(data->orig.data);
        kfree(data);
 }
+NOKPROBE_SYMBOL(free_deref_fetch_param);
 
 /* Bitfield fetch function */
 struct bitfield_fetch_param {
@@ -166,8 +171,8 @@ struct bitfield_fetch_param {
 };
 
 #define DEFINE_FETCH_bitfield(type)                                    \
-__kprobes void FETCH_FUNC_NAME(bitfield, type)(struct pt_regs *regs,   \
-                                           void *data, void *dest)     \
+void FETCH_FUNC_NAME(bitfield, type)(struct pt_regs *regs,             \
+                                    void *data, void *dest)            \
 {                                                                      \
        struct bitfield_fetch_param *bprm = data;                       \
        type buf = 0;                                                   \
@@ -177,13 +182,13 @@ __kprobes void FETCH_FUNC_NAME(bitfield, type)(struct pt_regs *regs,      \
                buf >>= bprm->low_shift;                                \
        }                                                               \
        *(type *)dest = buf;                                            \
-}
-
+}                                                                      \
+NOKPROBE_SYMBOL(FETCH_FUNC_NAME(bitfield, type));
 DEFINE_BASIC_FETCH_FUNCS(bitfield)
 #define fetch_bitfield_string          NULL
 #define fetch_bitfield_string_size     NULL
 
-static __kprobes void
+static void
 update_bitfield_fetch_param(struct bitfield_fetch_param *data)
 {
        /*
@@ -196,7 +201,7 @@ update_bitfield_fetch_param(struct bitfield_fetch_param *data)
                update_symbol_cache(data->orig.data);
 }
 
-static __kprobes void
+static void
 free_bitfield_fetch_param(struct bitfield_fetch_param *data)
 {
        /*
@@ -255,17 +260,17 @@ static const struct fetch_type *find_fetch_type(const char *type,
 }
 
 /* Special function : only accept unsigned long */
-static __kprobes void fetch_kernel_stack_address(struct pt_regs *regs,
-                                                void *dummy, void *dest)
+static void fetch_kernel_stack_address(struct pt_regs *regs, void *dummy, void *dest)
 {
        *(unsigned long *)dest = kernel_stack_pointer(regs);
 }
+NOKPROBE_SYMBOL(fetch_kernel_stack_address);
 
-static __kprobes void fetch_user_stack_address(struct pt_regs *regs,
-                                              void *dummy, void *dest)
+static void fetch_user_stack_address(struct pt_regs *regs, void *dummy, void *dest)
 {
        *(unsigned long *)dest = user_stack_pointer(regs);
 }
+NOKPROBE_SYMBOL(fetch_user_stack_address);
 
 static fetch_func_t get_fetch_size_function(const struct fetch_type *type,
                                            fetch_func_t orig_fn,
index fb1ab5dfbd42f67c3125ebc0688f26eb60f8d529..4f815fbce16d26eefbc8e3b572ec9f2fdca7879b 100644 (file)
  */
 #define convert_rloc_to_loc(dl, offs)  ((u32)(dl) + (offs))
 
-static inline void *get_rloc_data(u32 *dl)
+static nokprobe_inline void *get_rloc_data(u32 *dl)
 {
        return (u8 *)dl + get_rloc_offs(*dl);
 }
 
 /* For data_loc conversion */
-static inline void *get_loc_data(u32 *dl, void *ent)
+static nokprobe_inline void *get_loc_data(u32 *dl, void *ent)
 {
        return (u8 *)ent + get_rloc_offs(*dl);
 }
@@ -136,9 +136,8 @@ typedef u32 string_size;
 
 /* Printing  in basic type function template */
 #define DECLARE_BASIC_PRINT_TYPE_FUNC(type)                            \
-__kprobes int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s,          \
-                                        const char *name,              \
-                                        void *data, void *ent);        \
+int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s, const char *name,  \
+                               void *data, void *ent);                 \
 extern const char PRINT_TYPE_FMT_NAME(type)[]
 
 DECLARE_BASIC_PRINT_TYPE_FUNC(u8);
@@ -303,7 +302,7 @@ static inline bool trace_probe_is_registered(struct trace_probe *tp)
        return !!(tp->flags & TP_FLAG_REGISTERED);
 }
 
-static inline __kprobes void call_fetch(struct fetch_param *fprm,
+static nokprobe_inline void call_fetch(struct fetch_param *fprm,
                                 struct pt_regs *regs, void *dest)
 {
        return fprm->fn(regs, fprm->data, dest);
@@ -351,7 +350,7 @@ extern ssize_t traceprobe_probes_write(struct file *file,
 extern int traceprobe_command(const char *buf, int (*createfn)(int, char**));
 
 /* Sum up total data length for dynamic arraies (strings) */
-static inline __kprobes int
+static nokprobe_inline int
 __get_data_size(struct trace_probe *tp, struct pt_regs *regs)
 {
        int i, ret = 0;
@@ -367,7 +366,7 @@ __get_data_size(struct trace_probe *tp, struct pt_regs *regs)
 }
 
 /* Store the value of each argument */
-static inline __kprobes void
+static nokprobe_inline void
 store_trace_args(int ent_size, struct trace_probe *tp, struct pt_regs *regs,
                 u8 *data, int maxlen)
 {
index c082a74413455da972d2c9c115001b58fe55e5a7..04fdb5de823c5db150aa8d15f82e2b70acb34060 100644 (file)
@@ -108,8 +108,8 @@ static unsigned long get_user_stack_nth(struct pt_regs *regs, unsigned int n)
  * Uprobes-specific fetch functions
  */
 #define DEFINE_FETCH_stack(type)                                       \
-static __kprobes void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,\
-                                         void *offset, void *dest)     \
+static void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,         \
+                                        void *offset, void *dest)      \
 {                                                                      \
        *(type *)dest = (type)get_user_stack_nth(regs,                  \
                                              ((unsigned long)offset)); \
@@ -120,8 +120,8 @@ DEFINE_BASIC_FETCH_FUNCS(stack)
 #define fetch_stack_string_size        NULL
 
 #define DEFINE_FETCH_memory(type)                                      \
-static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\
-                                               void *addr, void *dest) \
+static void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,                \
+                                         void *addr, void *dest)       \
 {                                                                      \
        type retval;                                                    \
        void __user *vaddr = (void __force __user *) addr;              \
@@ -136,8 +136,8 @@ DEFINE_BASIC_FETCH_FUNCS(memory)
  * Fetch a null-terminated string. Caller MUST set *(u32 *)dest with max
  * length and relative data location.
  */
-static __kprobes void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs,
-                                                     void *addr, void *dest)
+static void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs,
+                                           void *addr, void *dest)
 {
        long ret;
        u32 rloc = *(u32 *)dest;
@@ -158,8 +158,8 @@ static __kprobes void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs,
        }
 }
 
-static __kprobes void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs,
-                                                     void *addr, void *dest)
+static void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs,
+                                                void *addr, void *dest)
 {
        int len;
        void __user *vaddr = (void __force __user *) addr;
@@ -184,8 +184,8 @@ static unsigned long translate_user_vaddr(void *file_offset)
 }
 
 #define DEFINE_FETCH_file_offset(type)                                 \
-static __kprobes void FETCH_FUNC_NAME(file_offset, type)(struct pt_regs *regs,\
-                                       void *offset, void *dest)       \
+static void FETCH_FUNC_NAME(file_offset, type)(struct pt_regs *regs,   \
+                                              void *offset, void *dest)\
 {                                                                      \
        void *vaddr = (void *)translate_user_vaddr(offset);             \
                                                                        \
@@ -1009,56 +1009,60 @@ uprobe_filter_event(struct trace_uprobe *tu, struct perf_event *event)
        return __uprobe_perf_filter(&tu->filter, event->hw.tp_target->mm);
 }
 
-static int uprobe_perf_open(struct trace_uprobe *tu, struct perf_event *event)
+static int uprobe_perf_close(struct trace_uprobe *tu, struct perf_event *event)
 {
        bool done;
 
        write_lock(&tu->filter.rwlock);
        if (event->hw.tp_target) {
-               /*
-                * event->parent != NULL means copy_process(), we can avoid
-                * uprobe_apply(). current->mm must be probed and we can rely
-                * on dup_mmap() which preserves the already installed bp's.
-                *
-                * attr.enable_on_exec means that exec/mmap will install the
-                * breakpoints we need.
-                */
+               list_del(&event->hw.tp_list);
                done = tu->filter.nr_systemwide ||
-                       event->parent || event->attr.enable_on_exec ||
+                       (event->hw.tp_target->flags & PF_EXITING) ||
                        uprobe_filter_event(tu, event);
-               list_add(&event->hw.tp_list, &tu->filter.perf_events);
        } else {
+               tu->filter.nr_systemwide--;
                done = tu->filter.nr_systemwide;
-               tu->filter.nr_systemwide++;
        }
        write_unlock(&tu->filter.rwlock);
 
        if (!done)
-               uprobe_apply(tu->inode, tu->offset, &tu->consumer, true);
+               return uprobe_apply(tu->inode, tu->offset, &tu->consumer, false);
 
        return 0;
 }
 
-static int uprobe_perf_close(struct trace_uprobe *tu, struct perf_event *event)
+static int uprobe_perf_open(struct trace_uprobe *tu, struct perf_event *event)
 {
        bool done;
+       int err;
 
        write_lock(&tu->filter.rwlock);
        if (event->hw.tp_target) {
-               list_del(&event->hw.tp_list);
+               /*
+                * event->parent != NULL means copy_process(), we can avoid
+                * uprobe_apply(). current->mm must be probed and we can rely
+                * on dup_mmap() which preserves the already installed bp's.
+                *
+                * attr.enable_on_exec means that exec/mmap will install the
+                * breakpoints we need.
+                */
                done = tu->filter.nr_systemwide ||
-                       (event->hw.tp_target->flags & PF_EXITING) ||
+                       event->parent || event->attr.enable_on_exec ||
                        uprobe_filter_event(tu, event);
+               list_add(&event->hw.tp_list, &tu->filter.perf_events);
        } else {
-               tu->filter.nr_systemwide--;
                done = tu->filter.nr_systemwide;
+               tu->filter.nr_systemwide++;
        }
        write_unlock(&tu->filter.rwlock);
 
-       if (!done)
-               uprobe_apply(tu->inode, tu->offset, &tu->consumer, false);
-
-       return 0;
+       err = 0;
+       if (!done) {
+               err = uprobe_apply(tu->inode, tu->offset, &tu->consumer, true);
+               if (err)
+                       uprobe_perf_close(tu, event);
+       }
+       return err;
 }
 
 static bool uprobe_perf_filter(struct uprobe_consumer *uc,
index e34d11d70bbfb27be66b42b701b698994c4d8888..7cfcc1b8e1017006f1d1868cc26d63350c2df3b9 100644 (file)
@@ -1636,6 +1636,19 @@ config TEST_USER_COPY
 
          If unsure, say N.
 
+config TEST_BPF
+       tristate "Test BPF filter functionality"
+       default n
+       depends on m && NET
+       help
+         This builds the "test_bpf" module that runs various test vectors
+         against the BPF interpreter or BPF JIT compiler depending on the
+         current setting. This is in particular useful for BPF JIT compiler
+         development, but also to run regression tests against changes in
+         the interpreter code.
+
+         If unsure, say N.
+
 source "samples/Kconfig"
 
 source "lib/Kconfig.kgdb"
index 4a4078987a4ce54cf3b0eccbe5ead45f052986b8..ba967a19edbae1d97da6d301f65d04c8bf44f6a5 100644 (file)
@@ -33,6 +33,7 @@ obj-y += kstrtox.o
 obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o
 obj-$(CONFIG_TEST_MODULE) += test_module.o
 obj-$(CONFIG_TEST_USER_COPY) += test_user_copy.o
+obj-$(CONFIG_TEST_BPF) += test_bpf.o
 
 ifeq ($(CONFIG_DEBUG_KOBJECT),y)
 CFLAGS_kobject.o += -DDEBUG
index b810b753c607500a9aab3aedd3060cb6c35189fd..c101230658ebc9af1f7daf35252e293e0ce8d3f0 100644 (file)
@@ -164,3 +164,66 @@ void __init free_bootmem_cpumask_var(cpumask_var_t mask)
        memblock_free_early(__pa(mask), cpumask_size());
 }
 #endif
+
+/**
+ * cpumask_set_cpu_local_first - set i'th cpu with local numa cpu's first
+ *
+ * @i: index number
+ * @numa_node: local numa_node
+ * @dstp: cpumask with the relevant cpu bit set according to the policy
+ *
+ * This function sets the cpumask according to a numa aware policy.
+ * cpumask could be used as an affinity hint for the IRQ related to a
+ * queue. When the policy is to spread queues across cores - local cores
+ * first.
+ *
+ * Returns 0 on success, -ENOMEM for no memory, and -EAGAIN when failed to set
+ * the cpu bit and need to re-call the function.
+ */
+int cpumask_set_cpu_local_first(int i, int numa_node, cpumask_t *dstp)
+{
+       cpumask_var_t mask;
+       int cpu;
+       int ret = 0;
+
+       if (!zalloc_cpumask_var(&mask, GFP_KERNEL))
+               return -ENOMEM;
+
+       i %= num_online_cpus();
+
+       if (!cpumask_of_node(numa_node)) {
+               /* Use all online cpu's for non numa aware system */
+               cpumask_copy(mask, cpu_online_mask);
+       } else {
+               int n;
+
+               cpumask_and(mask,
+                           cpumask_of_node(numa_node), cpu_online_mask);
+
+               n = cpumask_weight(mask);
+               if (i >= n) {
+                       i -= n;
+
+                       /* If index > number of local cpu's, mask out local
+                        * cpu's
+                        */
+                       cpumask_andnot(mask, cpu_online_mask, mask);
+               }
+       }
+
+       for_each_cpu(cpu, mask) {
+               if (--i < 0)
+                       goto out;
+       }
+
+       ret = -EAGAIN;
+
+out:
+       free_cpumask_var(mask);
+
+       if (!ret)
+               cpumask_set_cpu(cpu, dstp);
+
+       return ret;
+}
+EXPORT_SYMBOL(cpumask_set_cpu_local_first);
index f1c3a144cec123d73bee9897922bca0fa4dace48..bf6255e239193159afb1fc6fef402b6b011f1b22 100644 (file)
 #include <linux/crc7.h>
 
 
-/* Table for CRC-7 (polynomial x^7 + x^3 + 1) */
-const u8 crc7_syndrome_table[256] = {
-       0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f,
-       0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
-       0x19, 0x10, 0x0b, 0x02, 0x3d, 0x34, 0x2f, 0x26,
-       0x51, 0x58, 0x43, 0x4a, 0x75, 0x7c, 0x67, 0x6e,
-       0x32, 0x3b, 0x20, 0x29, 0x16, 0x1f, 0x04, 0x0d,
-       0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45,
-       0x2b, 0x22, 0x39, 0x30, 0x0f, 0x06, 0x1d, 0x14,
-       0x63, 0x6a, 0x71, 0x78, 0x47, 0x4e, 0x55, 0x5c,
-       0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b,
-       0x2c, 0x25, 0x3e, 0x37, 0x08, 0x01, 0x1a, 0x13,
-       0x7d, 0x74, 0x6f, 0x66, 0x59, 0x50, 0x4b, 0x42,
-       0x35, 0x3c, 0x27, 0x2e, 0x11, 0x18, 0x03, 0x0a,
-       0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69,
-       0x1e, 0x17, 0x0c, 0x05, 0x3a, 0x33, 0x28, 0x21,
-       0x4f, 0x46, 0x5d, 0x54, 0x6b, 0x62, 0x79, 0x70,
-       0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38,
-       0x41, 0x48, 0x53, 0x5a, 0x65, 0x6c, 0x77, 0x7e,
-       0x09, 0x00, 0x1b, 0x12, 0x2d, 0x24, 0x3f, 0x36,
-       0x58, 0x51, 0x4a, 0x43, 0x7c, 0x75, 0x6e, 0x67,
-       0x10, 0x19, 0x02, 0x0b, 0x34, 0x3d, 0x26, 0x2f,
-       0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
-       0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04,
-       0x6a, 0x63, 0x78, 0x71, 0x4e, 0x47, 0x5c, 0x55,
-       0x22, 0x2b, 0x30, 0x39, 0x06, 0x0f, 0x14, 0x1d,
-       0x25, 0x2c, 0x37, 0x3e, 0x01, 0x08, 0x13, 0x1a,
-       0x6d, 0x64, 0x7f, 0x76, 0x49, 0x40, 0x5b, 0x52,
-       0x3c, 0x35, 0x2e, 0x27, 0x18, 0x11, 0x0a, 0x03,
-       0x74, 0x7d, 0x66, 0x6f, 0x50, 0x59, 0x42, 0x4b,
-       0x17, 0x1e, 0x05, 0x0c, 0x33, 0x3a, 0x21, 0x28,
-       0x5f, 0x56, 0x4d, 0x44, 0x7b, 0x72, 0x69, 0x60,
-       0x0e, 0x07, 0x1c, 0x15, 0x2a, 0x23, 0x38, 0x31,
-       0x46, 0x4f, 0x54, 0x5d, 0x62, 0x6b, 0x70, 0x79
+/*
+ * Table for CRC-7 (polynomial x^7 + x^3 + 1).
+ * This is a big-endian CRC (msbit is highest power of x),
+ * aligned so the msbit of the byte is the x^6 coefficient
+ * and the lsbit is not used.
+ */
+const u8 crc7_be_syndrome_table[256] = {
+       0x00, 0x12, 0x24, 0x36, 0x48, 0x5a, 0x6c, 0x7e,
+       0x90, 0x82, 0xb4, 0xa6, 0xd8, 0xca, 0xfc, 0xee,
+       0x32, 0x20, 0x16, 0x04, 0x7a, 0x68, 0x5e, 0x4c,
+       0xa2, 0xb0, 0x86, 0x94, 0xea, 0xf8, 0xce, 0xdc,
+       0x64, 0x76, 0x40, 0x52, 0x2c, 0x3e, 0x08, 0x1a,
+       0xf4, 0xe6, 0xd0, 0xc2, 0xbc, 0xae, 0x98, 0x8a,
+       0x56, 0x44, 0x72, 0x60, 0x1e, 0x0c, 0x3a, 0x28,
+       0xc6, 0xd4, 0xe2, 0xf0, 0x8e, 0x9c, 0xaa, 0xb8,
+       0xc8, 0xda, 0xec, 0xfe, 0x80, 0x92, 0xa4, 0xb6,
+       0x58, 0x4a, 0x7c, 0x6e, 0x10, 0x02, 0x34, 0x26,
+       0xfa, 0xe8, 0xde, 0xcc, 0xb2, 0xa0, 0x96, 0x84,
+       0x6a, 0x78, 0x4e, 0x5c, 0x22, 0x30, 0x06, 0x14,
+       0xac, 0xbe, 0x88, 0x9a, 0xe4, 0xf6, 0xc0, 0xd2,
+       0x3c, 0x2e, 0x18, 0x0a, 0x74, 0x66, 0x50, 0x42,
+       0x9e, 0x8c, 0xba, 0xa8, 0xd6, 0xc4, 0xf2, 0xe0,
+       0x0e, 0x1c, 0x2a, 0x38, 0x46, 0x54, 0x62, 0x70,
+       0x82, 0x90, 0xa6, 0xb4, 0xca, 0xd8, 0xee, 0xfc,
+       0x12, 0x00, 0x36, 0x24, 0x5a, 0x48, 0x7e, 0x6c,
+       0xb0, 0xa2, 0x94, 0x86, 0xf8, 0xea, 0xdc, 0xce,
+       0x20, 0x32, 0x04, 0x16, 0x68, 0x7a, 0x4c, 0x5e,
+       0xe6, 0xf4, 0xc2, 0xd0, 0xae, 0xbc, 0x8a, 0x98,
+       0x76, 0x64, 0x52, 0x40, 0x3e, 0x2c, 0x1a, 0x08,
+       0xd4, 0xc6, 0xf0, 0xe2, 0x9c, 0x8e, 0xb8, 0xaa,
+       0x44, 0x56, 0x60, 0x72, 0x0c, 0x1e, 0x28, 0x3a,
+       0x4a, 0x58, 0x6e, 0x7c, 0x02, 0x10, 0x26, 0x34,
+       0xda, 0xc8, 0xfe, 0xec, 0x92, 0x80, 0xb6, 0xa4,
+       0x78, 0x6a, 0x5c, 0x4e, 0x30, 0x22, 0x14, 0x06,
+       0xe8, 0xfa, 0xcc, 0xde, 0xa0, 0xb2, 0x84, 0x96,
+       0x2e, 0x3c, 0x0a, 0x18, 0x66, 0x74, 0x42, 0x50,
+       0xbe, 0xac, 0x9a, 0x88, 0xf6, 0xe4, 0xd2, 0xc0,
+       0x1c, 0x0e, 0x38, 0x2a, 0x54, 0x46, 0x70, 0x62,
+       0x8c, 0x9e, 0xa8, 0xba, 0xc4, 0xd6, 0xe0, 0xf2
 };
-EXPORT_SYMBOL(crc7_syndrome_table);
+EXPORT_SYMBOL(crc7_be_syndrome_table);
 
 /**
  * crc7 - update the CRC7 for the data buffer
@@ -55,14 +60,17 @@ EXPORT_SYMBOL(crc7_syndrome_table);
  * Context: any
  *
  * Returns the updated CRC7 value.
+ * The CRC7 is left-aligned in the byte (the lsbit is always 0), as that
+ * makes the computation easier, and all callers want it in that form.
+ *
  */
-u8 crc7(u8 crc, const u8 *buffer, size_t len)
+u8 crc7_be(u8 crc, const u8 *buffer, size_t len)
 {
        while (len--)
-               crc = crc7_byte(crc, *buffer++);
+               crc = crc7_be_byte(crc, *buffer++);
        return crc;
 }
-EXPORT_SYMBOL(crc7);
+EXPORT_SYMBOL(crc7_be);
 
 MODULE_DESCRIPTION("CRC7 calculations");
 MODULE_LICENSE("GPL");
diff --git a/lib/test_bpf.c b/lib/test_bpf.c
new file mode 100644 (file)
index 0000000..c579e0f
--- /dev/null
@@ -0,0 +1,1929 @@
+/*
+ * Testsuite for BPF interpreter and BPF JIT compiler
+ *
+ * Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/filter.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/if_vlan.h>
+
+/* General test specific settings */
+#define MAX_SUBTESTS   3
+#define MAX_TESTRUNS   10000
+#define MAX_DATA       128
+#define MAX_INSNS      512
+#define MAX_K          0xffffFFFF
+
+/* Few constants used to init test 'skb' */
+#define SKB_TYPE       3
+#define SKB_MARK       0x1234aaaa
+#define SKB_HASH       0x1234aaab
+#define SKB_QUEUE_MAP  123
+#define SKB_VLAN_TCI   0xffff
+#define SKB_DEV_IFINDEX        577
+#define SKB_DEV_TYPE   588
+
+/* Redefine REGs to make tests less verbose */
+#define R0             BPF_REG_0
+#define R1             BPF_REG_1
+#define R2             BPF_REG_2
+#define R3             BPF_REG_3
+#define R4             BPF_REG_4
+#define R5             BPF_REG_5
+#define R6             BPF_REG_6
+#define R7             BPF_REG_7
+#define R8             BPF_REG_8
+#define R9             BPF_REG_9
+#define R10            BPF_REG_10
+
+/* Flags that can be passed to test cases */
+#define FLAG_NO_DATA           BIT(0)
+#define FLAG_EXPECTED_FAIL     BIT(1)
+
+enum {
+       CLASSIC  = BIT(6),      /* Old BPF instructions only. */
+       INTERNAL = BIT(7),      /* Extended instruction set.  */
+};
+
+#define TEST_TYPE_MASK         (CLASSIC | INTERNAL)
+
+struct bpf_test {
+       const char *descr;
+       union {
+               struct sock_filter insns[MAX_INSNS];
+               struct sock_filter_int insns_int[MAX_INSNS];
+       } u;
+       __u8 aux;
+       __u8 data[MAX_DATA];
+       struct {
+               int data_size;
+               __u32 result;
+       } test[MAX_SUBTESTS];
+};
+
+static struct bpf_test tests[] = {
+       {
+               "TAX",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_IMM, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_IMM, 2),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_ALU | BPF_NEG, 0), /* A == -3 */
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_LEN, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0), /* X == len - 3 */
+                       BPF_STMT(BPF_LD | BPF_B | BPF_IND, 1),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { 10, 20, 30, 40, 50 },
+               { { 2, 10 }, { 3, 20 }, { 4, 30 } },
+       },
+       {
+               "TXA",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_LEN, 0),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0) /* A == len * 2 */
+               },
+               CLASSIC,
+               { 10, 20, 30, 40, 50 },
+               { { 1, 2 }, { 3, 6 }, { 4, 8 } },
+       },
+       {
+               "ADD_SUB_MUL_K",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_IMM, 1),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 2),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 3),
+                       BPF_STMT(BPF_ALU | BPF_SUB | BPF_X, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 0xffffffff),
+                       BPF_STMT(BPF_ALU | BPF_MUL | BPF_K, 3),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC | FLAG_NO_DATA,
+               { },
+               { { 0, 0xfffffffd } }
+       },
+       {
+               "DIV_KX",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_IMM, 8),
+                       BPF_STMT(BPF_ALU | BPF_DIV | BPF_K, 2),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_IMM, 0xffffffff),
+                       BPF_STMT(BPF_ALU | BPF_DIV | BPF_X, 0),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_IMM, 0xffffffff),
+                       BPF_STMT(BPF_ALU | BPF_DIV | BPF_K, 0x70000000),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC | FLAG_NO_DATA,
+               { },
+               { { 0, 0x40000001 } }
+       },
+       {
+               "AND_OR_LSH_K",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_IMM, 0xff),
+                       BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xf0),
+                       BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 27),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_IMM, 0xf),
+                       BPF_STMT(BPF_ALU | BPF_OR | BPF_K, 0xf0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC | FLAG_NO_DATA,
+               { },
+               { { 0, 0x800000ff }, { 1, 0x800000ff } },
+       },
+       {
+               "LD_IMM_0",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_IMM, 0), /* ld #0 */
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 1),
+               },
+               CLASSIC,
+               { },
+               { { 1, 1 } },
+       },
+       {
+               "LD_IND",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_LEN, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_IND, MAX_K),
+                       BPF_STMT(BPF_RET | BPF_K, 1)
+               },
+               CLASSIC,
+               { },
+               { { 1, 0 }, { 10, 0 }, { 60, 0 } },
+       },
+       {
+               "LD_ABS",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 1000),
+                       BPF_STMT(BPF_RET | BPF_K, 1)
+               },
+               CLASSIC,
+               { },
+               { { 1, 0 }, { 10, 0 }, { 60, 0 } },
+       },
+       {
+               "LD_ABS_LL",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, SKF_LL_OFF),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, SKF_LL_OFF + 1),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { 1, 2, 3 },
+               { { 1, 0 }, { 2, 3 } },
+       },
+       {
+               "LD_IND_LL",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_IMM, SKF_LL_OFF - 1),
+                       BPF_STMT(BPF_LDX | BPF_LEN, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { 1, 2, 3, 0xff },
+               { { 1, 1 }, { 3, 3 }, { 4, 0xff } },
+       },
+       {
+               "LD_ABS_NET",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, SKF_NET_OFF),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, SKF_NET_OFF + 1),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3 },
+               { { 15, 0 }, { 16, 3 } },
+       },
+       {
+               "LD_IND_NET",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_IMM, SKF_NET_OFF - 15),
+                       BPF_STMT(BPF_LDX | BPF_LEN, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3 },
+               { { 14, 0 }, { 15, 1 }, { 17, 3 } },
+       },
+       {
+               "LD_PKTTYPE",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_PKTTYPE),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SKB_TYPE, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 1),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_PKTTYPE),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SKB_TYPE, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 1),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_PKTTYPE),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SKB_TYPE, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 1),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               { { 1, 3 }, { 10, 3 } },
+       },
+       {
+               "LD_MARK",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_MARK),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               { { 1, SKB_MARK}, { 10, SKB_MARK} },
+       },
+       {
+               "LD_RXHASH",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_RXHASH),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               { { 1, SKB_HASH}, { 10, SKB_HASH} },
+       },
+       {
+               "LD_QUEUE",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_QUEUE),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               { { 1, SKB_QUEUE_MAP }, { 10, SKB_QUEUE_MAP } },
+       },
+       {
+               "LD_PROTOCOL",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 1),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 20, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 0),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_PROTOCOL),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 2),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 30, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 0),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { 10, 20, 30 },
+               { { 10, ETH_P_IP }, { 100, ETH_P_IP } },
+       },
+       {
+               "LD_VLAN_TAG",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_VLAN_TAG),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               {
+                       { 1, SKB_VLAN_TCI & ~VLAN_TAG_PRESENT },
+                       { 10, SKB_VLAN_TCI & ~VLAN_TAG_PRESENT }
+               },
+       },
+       {
+               "LD_VLAN_TAG_PRESENT",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               {
+                       { 1, !!(SKB_VLAN_TCI & VLAN_TAG_PRESENT) },
+                       { 10, !!(SKB_VLAN_TCI & VLAN_TAG_PRESENT) }
+               },
+       },
+       {
+               "LD_IFINDEX",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_IFINDEX),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               { { 1, SKB_DEV_IFINDEX }, { 10, SKB_DEV_IFINDEX } },
+       },
+       {
+               "LD_HATYPE",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_HATYPE),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               { { 1, SKB_DEV_TYPE }, { 10, SKB_DEV_TYPE } },
+       },
+       {
+               "LD_CPU",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_CPU),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_CPU),
+                       BPF_STMT(BPF_ALU | BPF_SUB | BPF_X, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               { { 1, 0 }, { 10, 0 } },
+       },
+       {
+               "LD_NLATTR",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_IMM, 2),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 3),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_NLATTR),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+#ifdef __BIG_ENDIAN
+               { 0xff, 0xff, 0, 4, 0, 2, 0, 4, 0, 3 },
+#else
+               { 0xff, 0xff, 4, 0, 2, 0, 4, 0, 3, 0 },
+#endif
+               { { 4, 0 }, { 20, 6 } },
+       },
+       {
+               "LD_NLATTR_NEST",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_IMM, 2),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 3),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_NLATTR_NEST),
+                       BPF_STMT(BPF_LD | BPF_IMM, 2),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_NLATTR_NEST),
+                       BPF_STMT(BPF_LD | BPF_IMM, 2),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_NLATTR_NEST),
+                       BPF_STMT(BPF_LD | BPF_IMM, 2),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_NLATTR_NEST),
+                       BPF_STMT(BPF_LD | BPF_IMM, 2),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_NLATTR_NEST),
+                       BPF_STMT(BPF_LD | BPF_IMM, 2),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_NLATTR_NEST),
+                       BPF_STMT(BPF_LD | BPF_IMM, 2),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_NLATTR_NEST),
+                       BPF_STMT(BPF_LD | BPF_IMM, 2),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_NLATTR_NEST),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+#ifdef __BIG_ENDIAN
+               { 0xff, 0xff, 0, 12, 0, 1, 0, 4, 0, 2, 0, 4, 0, 3 },
+#else
+               { 0xff, 0xff, 12, 0, 1, 0, 4, 0, 2, 0, 4, 0, 3, 0 },
+#endif
+               { { 4, 0 }, { 20, 10 } },
+       },
+       {
+               "LD_PAYLOAD_OFF",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_PAY_OFFSET),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_PAY_OFFSET),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_PAY_OFFSET),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_PAY_OFFSET),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_PAY_OFFSET),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               /* 00:00:00:00:00:00 > 00:00:00:00:00:00, ethtype IPv4 (0x0800),
+                * length 98: 127.0.0.1 > 127.0.0.1: ICMP echo request,
+                * id 9737, seq 1, length 64
+                */
+               { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                 0x08, 0x00,
+                 0x45, 0x00, 0x00, 0x54, 0xac, 0x8b, 0x40, 0x00, 0x40,
+                 0x01, 0x90, 0x1b, 0x7f, 0x00, 0x00, 0x01 },
+               { { 30, 0 }, { 100, 42 } },
+       },
+       {
+               "LD_ANC_XOR",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_IMM, 10),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 300),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_ALU_XOR_X),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               { { 4, 10 ^ 300 }, { 20, 10 ^ 300 } },
+       },
+       {
+               "SPILL_FILL",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_LEN, 0),
+                       BPF_STMT(BPF_LD | BPF_IMM, 2),
+                       BPF_STMT(BPF_ALU | BPF_RSH, 1),
+                       BPF_STMT(BPF_ALU | BPF_XOR | BPF_X, 0),
+                       BPF_STMT(BPF_ST, 1), /* M1 = 1 ^ len */
+                       BPF_STMT(BPF_ALU | BPF_XOR | BPF_K, 0x80000000),
+                       BPF_STMT(BPF_ST, 2), /* M2 = 1 ^ len ^ 0x80000000 */
+                       BPF_STMT(BPF_STX, 15), /* M3 = len */
+                       BPF_STMT(BPF_LDX | BPF_MEM, 1),
+                       BPF_STMT(BPF_LD | BPF_MEM, 2),
+                       BPF_STMT(BPF_ALU | BPF_XOR | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 15),
+                       BPF_STMT(BPF_ALU | BPF_XOR | BPF_X, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               { { 1, 0x80000001 }, { 2, 0x80000002 }, { 60, 0x80000000 ^ 60 } }
+       },
+       {
+               "JEQ",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_LEN, 0),
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 2),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, 0, 1),
+                       BPF_STMT(BPF_RET | BPF_K, 1),
+                       BPF_STMT(BPF_RET | BPF_K, MAX_K)
+               },
+               CLASSIC,
+               { 3, 3, 3, 3, 3 },
+               { { 1, 0 }, { 3, 1 }, { 4, MAX_K } },
+       },
+       {
+               "JGT",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_LEN, 0),
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 2),
+                       BPF_JUMP(BPF_JMP | BPF_JGT | BPF_X, 0, 0, 1),
+                       BPF_STMT(BPF_RET | BPF_K, 1),
+                       BPF_STMT(BPF_RET | BPF_K, MAX_K)
+               },
+               CLASSIC,
+               { 4, 4, 4, 3, 3 },
+               { { 2, 0 }, { 3, 1 }, { 4, MAX_K } },
+       },
+       {
+               "JGE",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_LEN, 0),
+                       BPF_STMT(BPF_LD | BPF_B | BPF_IND, MAX_K),
+                       BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 1, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 10),
+                       BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 2, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 20),
+                       BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 3, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 30),
+                       BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 4, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 40),
+                       BPF_STMT(BPF_RET | BPF_K, MAX_K)
+               },
+               CLASSIC,
+               { 1, 2, 3, 4, 5 },
+               { { 1, 20 }, { 3, 40 }, { 5, MAX_K } },
+       },
+       {
+               "JSET",
+               .u.insns = {
+                       BPF_JUMP(BPF_JMP | BPF_JA, 0, 0, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JA, 1, 1, 1),
+                       BPF_JUMP(BPF_JMP | BPF_JA, 0, 0, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JA, 0, 0, 0),
+                       BPF_STMT(BPF_LDX | BPF_LEN, 0),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_SUB | BPF_K, 4),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_LD | BPF_W | BPF_IND, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 1, 0, 1),
+                       BPF_STMT(BPF_RET | BPF_K, 10),
+                       BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0x80000000, 0, 1),
+                       BPF_STMT(BPF_RET | BPF_K, 20),
+                       BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0xffffff, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 30),
+                       BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0xffffff, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 30),
+                       BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0xffffff, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 30),
+                       BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0xffffff, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 30),
+                       BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0xffffff, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 30),
+                       BPF_STMT(BPF_RET | BPF_K, MAX_K)
+               },
+               CLASSIC,
+               { 0, 0xAA, 0x55, 1 },
+               { { 4, 10 }, { 5, 20 }, { 6, MAX_K } },
+       },
+       {
+               "tcpdump port 22",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 12),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x86dd, 0, 8), /* IPv6 */
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 20),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x84, 2, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x6, 1, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x11, 0, 17),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 54),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 22, 14, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 56),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 22, 12, 13),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0800, 0, 12), /* IPv4 */
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 23),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x84, 2, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x6, 1, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x11, 0, 8),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 20),
+                       BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0x1fff, 6, 0),
+                       BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, 14),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_IND, 14),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 22, 2, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_IND, 16),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 22, 0, 1),
+                       BPF_STMT(BPF_RET | BPF_K, 0xffff),
+                       BPF_STMT(BPF_RET | BPF_K, 0),
+               },
+               CLASSIC,
+               /* 3c:07:54:43:e5:76 > 10:bf:48:d6:43:d6, ethertype IPv4(0x0800)
+                * length 114: 10.1.1.149.49700 > 10.1.2.10.22: Flags [P.],
+                * seq 1305692979:1305693027, ack 3650467037, win 65535,
+                * options [nop,nop,TS val 2502645400 ecr 3971138], length 48
+                */
+               { 0x10, 0xbf, 0x48, 0xd6, 0x43, 0xd6,
+                 0x3c, 0x07, 0x54, 0x43, 0xe5, 0x76,
+                 0x08, 0x00,
+                 0x45, 0x10, 0x00, 0x64, 0x75, 0xb5,
+                 0x40, 0x00, 0x40, 0x06, 0xad, 0x2e, /* IP header */
+                 0x0a, 0x01, 0x01, 0x95, /* ip src */
+                 0x0a, 0x01, 0x02, 0x0a, /* ip dst */
+                 0xc2, 0x24,
+                 0x00, 0x16 /* dst port */ },
+               { { 10, 0 }, { 30, 0 }, { 100, 65535 } },
+       },
+       {
+               "tcpdump complex",
+               .u.insns = {
+                       /* tcpdump -nei eth0 'tcp port 22 and (((ip[2:2] -
+                        * ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0) and
+                        * (len > 115 or len < 30000000000)' -d
+                        */
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 12),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x86dd, 30, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x800, 0, 29),
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 23),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x6, 0, 27),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 20),
+                       BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0x1fff, 25, 0),
+                       BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, 14),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_IND, 14),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 22, 2, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_IND, 16),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 22, 0, 20),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 16),
+                       BPF_STMT(BPF_ST, 1),
+                       BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 14),
+                       BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xf),
+                       BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 2),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0x5), /* libpcap emits K on TAX */
+                       BPF_STMT(BPF_LD | BPF_MEM, 1),
+                       BPF_STMT(BPF_ALU | BPF_SUB | BPF_X, 0),
+                       BPF_STMT(BPF_ST, 5),
+                       BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, 14),
+                       BPF_STMT(BPF_LD | BPF_B | BPF_IND, 26),
+                       BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xf0),
+                       BPF_STMT(BPF_ALU | BPF_RSH | BPF_K, 2),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0x9), /* libpcap emits K on TAX */
+                       BPF_STMT(BPF_LD | BPF_MEM, 5),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, 4, 0),
+                       BPF_STMT(BPF_LD | BPF_LEN, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K, 0x73, 1, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 0xfc23ac00, 1, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 0xffff),
+                       BPF_STMT(BPF_RET | BPF_K, 0),
+               },
+               CLASSIC,
+               { 0x10, 0xbf, 0x48, 0xd6, 0x43, 0xd6,
+                 0x3c, 0x07, 0x54, 0x43, 0xe5, 0x76,
+                 0x08, 0x00,
+                 0x45, 0x10, 0x00, 0x64, 0x75, 0xb5,
+                 0x40, 0x00, 0x40, 0x06, 0xad, 0x2e, /* IP header */
+                 0x0a, 0x01, 0x01, 0x95, /* ip src */
+                 0x0a, 0x01, 0x02, 0x0a, /* ip dst */
+                 0xc2, 0x24,
+                 0x00, 0x16 /* dst port */ },
+               { { 10, 0 }, { 30, 0 }, { 100, 65535 } },
+       },
+       {
+               "RET_A",
+               .u.insns = {
+                       /* check that unitialized X and A contain zeros */
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0)
+               },
+               CLASSIC,
+               { },
+               { {1, 0}, {2, 0} },
+       },
+       {
+               "INT: ADD trivial",
+               .u.insns_int = {
+                       BPF_ALU64_IMM(BPF_MOV, R1, 1),
+                       BPF_ALU64_IMM(BPF_ADD, R1, 2),
+                       BPF_ALU64_IMM(BPF_MOV, R2, 3),
+                       BPF_ALU64_REG(BPF_SUB, R1, R2),
+                       BPF_ALU64_IMM(BPF_ADD, R1, -1),
+                       BPF_ALU64_IMM(BPF_MUL, R1, 3),
+                       BPF_ALU64_REG(BPF_MOV, R0, R1),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { },
+               { { 0, 0xfffffffd } }
+       },
+       {
+               "INT: MUL_X",
+               .u.insns_int = {
+                       BPF_ALU64_IMM(BPF_MOV, R0, -1),
+                       BPF_ALU64_IMM(BPF_MOV, R1, -1),
+                       BPF_ALU64_IMM(BPF_MOV, R2, 3),
+                       BPF_ALU64_REG(BPF_MUL, R1, R2),
+                       BPF_JMP_IMM(BPF_JEQ, R1, 0xfffffffd, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_IMM(BPF_MOV, R0, 1),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { },
+               { { 0, 1 } }
+       },
+       {
+               "INT: MUL_X2",
+               .u.insns_int = {
+                       BPF_ALU32_IMM(BPF_MOV, R0, -1),
+                       BPF_ALU32_IMM(BPF_MOV, R1, -1),
+                       BPF_ALU32_IMM(BPF_MOV, R2, 3),
+                       BPF_ALU64_REG(BPF_MUL, R1, R2),
+                       BPF_ALU64_IMM(BPF_RSH, R1, 8),
+                       BPF_JMP_IMM(BPF_JEQ, R1, 0x2ffffff, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU32_IMM(BPF_MOV, R0, 1),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { },
+               { { 0, 1 } }
+       },
+       {
+               "INT: MUL32_X",
+               .u.insns_int = {
+                       BPF_ALU32_IMM(BPF_MOV, R0, -1),
+                       BPF_ALU64_IMM(BPF_MOV, R1, -1),
+                       BPF_ALU32_IMM(BPF_MOV, R2, 3),
+                       BPF_ALU32_REG(BPF_MUL, R1, R2),
+                       BPF_ALU64_IMM(BPF_RSH, R1, 8),
+                       BPF_JMP_IMM(BPF_JEQ, R1, 0xffffff, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU32_IMM(BPF_MOV, R0, 1),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { },
+               { { 0, 1 } }
+       },
+       {
+               /* Have to test all register combinations, since
+                * JITing of different registers will produce
+                * different asm code.
+                */
+               "INT: ADD 64-bit",
+               .u.insns_int = {
+                       BPF_ALU64_IMM(BPF_MOV, R0, 0),
+                       BPF_ALU64_IMM(BPF_MOV, R1, 1),
+                       BPF_ALU64_IMM(BPF_MOV, R2, 2),
+                       BPF_ALU64_IMM(BPF_MOV, R3, 3),
+                       BPF_ALU64_IMM(BPF_MOV, R4, 4),
+                       BPF_ALU64_IMM(BPF_MOV, R5, 5),
+                       BPF_ALU64_IMM(BPF_MOV, R6, 6),
+                       BPF_ALU64_IMM(BPF_MOV, R7, 7),
+                       BPF_ALU64_IMM(BPF_MOV, R8, 8),
+                       BPF_ALU64_IMM(BPF_MOV, R9, 9),
+                       BPF_ALU64_IMM(BPF_ADD, R0, 20),
+                       BPF_ALU64_IMM(BPF_ADD, R1, 20),
+                       BPF_ALU64_IMM(BPF_ADD, R2, 20),
+                       BPF_ALU64_IMM(BPF_ADD, R3, 20),
+                       BPF_ALU64_IMM(BPF_ADD, R4, 20),
+                       BPF_ALU64_IMM(BPF_ADD, R5, 20),
+                       BPF_ALU64_IMM(BPF_ADD, R6, 20),
+                       BPF_ALU64_IMM(BPF_ADD, R7, 20),
+                       BPF_ALU64_IMM(BPF_ADD, R8, 20),
+                       BPF_ALU64_IMM(BPF_ADD, R9, 20),
+                       BPF_ALU64_IMM(BPF_SUB, R0, 10),
+                       BPF_ALU64_IMM(BPF_SUB, R1, 10),
+                       BPF_ALU64_IMM(BPF_SUB, R2, 10),
+                       BPF_ALU64_IMM(BPF_SUB, R3, 10),
+                       BPF_ALU64_IMM(BPF_SUB, R4, 10),
+                       BPF_ALU64_IMM(BPF_SUB, R5, 10),
+                       BPF_ALU64_IMM(BPF_SUB, R6, 10),
+                       BPF_ALU64_IMM(BPF_SUB, R7, 10),
+                       BPF_ALU64_IMM(BPF_SUB, R8, 10),
+                       BPF_ALU64_IMM(BPF_SUB, R9, 10),
+                       BPF_ALU64_REG(BPF_ADD, R0, R0),
+                       BPF_ALU64_REG(BPF_ADD, R0, R1),
+                       BPF_ALU64_REG(BPF_ADD, R0, R2),
+                       BPF_ALU64_REG(BPF_ADD, R0, R3),
+                       BPF_ALU64_REG(BPF_ADD, R0, R4),
+                       BPF_ALU64_REG(BPF_ADD, R0, R5),
+                       BPF_ALU64_REG(BPF_ADD, R0, R6),
+                       BPF_ALU64_REG(BPF_ADD, R0, R7),
+                       BPF_ALU64_REG(BPF_ADD, R0, R8),
+                       BPF_ALU64_REG(BPF_ADD, R0, R9), /* R0 == 155 */
+                       BPF_JMP_IMM(BPF_JEQ, R0, 155, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_ADD, R1, R0),
+                       BPF_ALU64_REG(BPF_ADD, R1, R1),
+                       BPF_ALU64_REG(BPF_ADD, R1, R2),
+                       BPF_ALU64_REG(BPF_ADD, R1, R3),
+                       BPF_ALU64_REG(BPF_ADD, R1, R4),
+                       BPF_ALU64_REG(BPF_ADD, R1, R5),
+                       BPF_ALU64_REG(BPF_ADD, R1, R6),
+                       BPF_ALU64_REG(BPF_ADD, R1, R7),
+                       BPF_ALU64_REG(BPF_ADD, R1, R8),
+                       BPF_ALU64_REG(BPF_ADD, R1, R9), /* R1 == 456 */
+                       BPF_JMP_IMM(BPF_JEQ, R1, 456, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_ADD, R2, R0),
+                       BPF_ALU64_REG(BPF_ADD, R2, R1),
+                       BPF_ALU64_REG(BPF_ADD, R2, R2),
+                       BPF_ALU64_REG(BPF_ADD, R2, R3),
+                       BPF_ALU64_REG(BPF_ADD, R2, R4),
+                       BPF_ALU64_REG(BPF_ADD, R2, R5),
+                       BPF_ALU64_REG(BPF_ADD, R2, R6),
+                       BPF_ALU64_REG(BPF_ADD, R2, R7),
+                       BPF_ALU64_REG(BPF_ADD, R2, R8),
+                       BPF_ALU64_REG(BPF_ADD, R2, R9), /* R2 == 1358 */
+                       BPF_JMP_IMM(BPF_JEQ, R2, 1358, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_ADD, R3, R0),
+                       BPF_ALU64_REG(BPF_ADD, R3, R1),
+                       BPF_ALU64_REG(BPF_ADD, R3, R2),
+                       BPF_ALU64_REG(BPF_ADD, R3, R3),
+                       BPF_ALU64_REG(BPF_ADD, R3, R4),
+                       BPF_ALU64_REG(BPF_ADD, R3, R5),
+                       BPF_ALU64_REG(BPF_ADD, R3, R6),
+                       BPF_ALU64_REG(BPF_ADD, R3, R7),
+                       BPF_ALU64_REG(BPF_ADD, R3, R8),
+                       BPF_ALU64_REG(BPF_ADD, R3, R9), /* R3 == 4063 */
+                       BPF_JMP_IMM(BPF_JEQ, R3, 4063, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_ADD, R4, R0),
+                       BPF_ALU64_REG(BPF_ADD, R4, R1),
+                       BPF_ALU64_REG(BPF_ADD, R4, R2),
+                       BPF_ALU64_REG(BPF_ADD, R4, R3),
+                       BPF_ALU64_REG(BPF_ADD, R4, R4),
+                       BPF_ALU64_REG(BPF_ADD, R4, R5),
+                       BPF_ALU64_REG(BPF_ADD, R4, R6),
+                       BPF_ALU64_REG(BPF_ADD, R4, R7),
+                       BPF_ALU64_REG(BPF_ADD, R4, R8),
+                       BPF_ALU64_REG(BPF_ADD, R4, R9), /* R4 == 12177 */
+                       BPF_JMP_IMM(BPF_JEQ, R4, 12177, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_ADD, R5, R0),
+                       BPF_ALU64_REG(BPF_ADD, R5, R1),
+                       BPF_ALU64_REG(BPF_ADD, R5, R2),
+                       BPF_ALU64_REG(BPF_ADD, R5, R3),
+                       BPF_ALU64_REG(BPF_ADD, R5, R4),
+                       BPF_ALU64_REG(BPF_ADD, R5, R5),
+                       BPF_ALU64_REG(BPF_ADD, R5, R6),
+                       BPF_ALU64_REG(BPF_ADD, R5, R7),
+                       BPF_ALU64_REG(BPF_ADD, R5, R8),
+                       BPF_ALU64_REG(BPF_ADD, R5, R9), /* R5 == 36518 */
+                       BPF_JMP_IMM(BPF_JEQ, R5, 36518, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_ADD, R6, R0),
+                       BPF_ALU64_REG(BPF_ADD, R6, R1),
+                       BPF_ALU64_REG(BPF_ADD, R6, R2),
+                       BPF_ALU64_REG(BPF_ADD, R6, R3),
+                       BPF_ALU64_REG(BPF_ADD, R6, R4),
+                       BPF_ALU64_REG(BPF_ADD, R6, R5),
+                       BPF_ALU64_REG(BPF_ADD, R6, R6),
+                       BPF_ALU64_REG(BPF_ADD, R6, R7),
+                       BPF_ALU64_REG(BPF_ADD, R6, R8),
+                       BPF_ALU64_REG(BPF_ADD, R6, R9), /* R6 == 109540 */
+                       BPF_JMP_IMM(BPF_JEQ, R6, 109540, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_ADD, R7, R0),
+                       BPF_ALU64_REG(BPF_ADD, R7, R1),
+                       BPF_ALU64_REG(BPF_ADD, R7, R2),
+                       BPF_ALU64_REG(BPF_ADD, R7, R3),
+                       BPF_ALU64_REG(BPF_ADD, R7, R4),
+                       BPF_ALU64_REG(BPF_ADD, R7, R5),
+                       BPF_ALU64_REG(BPF_ADD, R7, R6),
+                       BPF_ALU64_REG(BPF_ADD, R7, R7),
+                       BPF_ALU64_REG(BPF_ADD, R7, R8),
+                       BPF_ALU64_REG(BPF_ADD, R7, R9), /* R7 == 328605 */
+                       BPF_JMP_IMM(BPF_JEQ, R7, 328605, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_ADD, R8, R0),
+                       BPF_ALU64_REG(BPF_ADD, R8, R1),
+                       BPF_ALU64_REG(BPF_ADD, R8, R2),
+                       BPF_ALU64_REG(BPF_ADD, R8, R3),
+                       BPF_ALU64_REG(BPF_ADD, R8, R4),
+                       BPF_ALU64_REG(BPF_ADD, R8, R5),
+                       BPF_ALU64_REG(BPF_ADD, R8, R6),
+                       BPF_ALU64_REG(BPF_ADD, R8, R7),
+                       BPF_ALU64_REG(BPF_ADD, R8, R8),
+                       BPF_ALU64_REG(BPF_ADD, R8, R9), /* R8 == 985799 */
+                       BPF_JMP_IMM(BPF_JEQ, R8, 985799, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_ADD, R9, R0),
+                       BPF_ALU64_REG(BPF_ADD, R9, R1),
+                       BPF_ALU64_REG(BPF_ADD, R9, R2),
+                       BPF_ALU64_REG(BPF_ADD, R9, R3),
+                       BPF_ALU64_REG(BPF_ADD, R9, R4),
+                       BPF_ALU64_REG(BPF_ADD, R9, R5),
+                       BPF_ALU64_REG(BPF_ADD, R9, R6),
+                       BPF_ALU64_REG(BPF_ADD, R9, R7),
+                       BPF_ALU64_REG(BPF_ADD, R9, R8),
+                       BPF_ALU64_REG(BPF_ADD, R9, R9), /* R9 == 2957380 */
+                       BPF_ALU64_REG(BPF_MOV, R0, R9),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { },
+               { { 0, 2957380 } }
+       },
+       {
+               "INT: ADD 32-bit",
+               .u.insns_int = {
+                       BPF_ALU32_IMM(BPF_MOV, R0, 20),
+                       BPF_ALU32_IMM(BPF_MOV, R1, 1),
+                       BPF_ALU32_IMM(BPF_MOV, R2, 2),
+                       BPF_ALU32_IMM(BPF_MOV, R3, 3),
+                       BPF_ALU32_IMM(BPF_MOV, R4, 4),
+                       BPF_ALU32_IMM(BPF_MOV, R5, 5),
+                       BPF_ALU32_IMM(BPF_MOV, R6, 6),
+                       BPF_ALU32_IMM(BPF_MOV, R7, 7),
+                       BPF_ALU32_IMM(BPF_MOV, R8, 8),
+                       BPF_ALU32_IMM(BPF_MOV, R9, 9),
+                       BPF_ALU64_IMM(BPF_ADD, R1, 10),
+                       BPF_ALU64_IMM(BPF_ADD, R2, 10),
+                       BPF_ALU64_IMM(BPF_ADD, R3, 10),
+                       BPF_ALU64_IMM(BPF_ADD, R4, 10),
+                       BPF_ALU64_IMM(BPF_ADD, R5, 10),
+                       BPF_ALU64_IMM(BPF_ADD, R6, 10),
+                       BPF_ALU64_IMM(BPF_ADD, R7, 10),
+                       BPF_ALU64_IMM(BPF_ADD, R8, 10),
+                       BPF_ALU64_IMM(BPF_ADD, R9, 10),
+                       BPF_ALU32_REG(BPF_ADD, R0, R1),
+                       BPF_ALU32_REG(BPF_ADD, R0, R2),
+                       BPF_ALU32_REG(BPF_ADD, R0, R3),
+                       BPF_ALU32_REG(BPF_ADD, R0, R4),
+                       BPF_ALU32_REG(BPF_ADD, R0, R5),
+                       BPF_ALU32_REG(BPF_ADD, R0, R6),
+                       BPF_ALU32_REG(BPF_ADD, R0, R7),
+                       BPF_ALU32_REG(BPF_ADD, R0, R8),
+                       BPF_ALU32_REG(BPF_ADD, R0, R9), /* R0 == 155 */
+                       BPF_JMP_IMM(BPF_JEQ, R0, 155, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU32_REG(BPF_ADD, R1, R0),
+                       BPF_ALU32_REG(BPF_ADD, R1, R1),
+                       BPF_ALU32_REG(BPF_ADD, R1, R2),
+                       BPF_ALU32_REG(BPF_ADD, R1, R3),
+                       BPF_ALU32_REG(BPF_ADD, R1, R4),
+                       BPF_ALU32_REG(BPF_ADD, R1, R5),
+                       BPF_ALU32_REG(BPF_ADD, R1, R6),
+                       BPF_ALU32_REG(BPF_ADD, R1, R7),
+                       BPF_ALU32_REG(BPF_ADD, R1, R8),
+                       BPF_ALU32_REG(BPF_ADD, R1, R9), /* R1 == 456 */
+                       BPF_JMP_IMM(BPF_JEQ, R1, 456, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU32_REG(BPF_ADD, R2, R0),
+                       BPF_ALU32_REG(BPF_ADD, R2, R1),
+                       BPF_ALU32_REG(BPF_ADD, R2, R2),
+                       BPF_ALU32_REG(BPF_ADD, R2, R3),
+                       BPF_ALU32_REG(BPF_ADD, R2, R4),
+                       BPF_ALU32_REG(BPF_ADD, R2, R5),
+                       BPF_ALU32_REG(BPF_ADD, R2, R6),
+                       BPF_ALU32_REG(BPF_ADD, R2, R7),
+                       BPF_ALU32_REG(BPF_ADD, R2, R8),
+                       BPF_ALU32_REG(BPF_ADD, R2, R9), /* R2 == 1358 */
+                       BPF_JMP_IMM(BPF_JEQ, R2, 1358, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU32_REG(BPF_ADD, R3, R0),
+                       BPF_ALU32_REG(BPF_ADD, R3, R1),
+                       BPF_ALU32_REG(BPF_ADD, R3, R2),
+                       BPF_ALU32_REG(BPF_ADD, R3, R3),
+                       BPF_ALU32_REG(BPF_ADD, R3, R4),
+                       BPF_ALU32_REG(BPF_ADD, R3, R5),
+                       BPF_ALU32_REG(BPF_ADD, R3, R6),
+                       BPF_ALU32_REG(BPF_ADD, R3, R7),
+                       BPF_ALU32_REG(BPF_ADD, R3, R8),
+                       BPF_ALU32_REG(BPF_ADD, R3, R9), /* R3 == 4063 */
+                       BPF_JMP_IMM(BPF_JEQ, R3, 4063, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU32_REG(BPF_ADD, R4, R0),
+                       BPF_ALU32_REG(BPF_ADD, R4, R1),
+                       BPF_ALU32_REG(BPF_ADD, R4, R2),
+                       BPF_ALU32_REG(BPF_ADD, R4, R3),
+                       BPF_ALU32_REG(BPF_ADD, R4, R4),
+                       BPF_ALU32_REG(BPF_ADD, R4, R5),
+                       BPF_ALU32_REG(BPF_ADD, R4, R6),
+                       BPF_ALU32_REG(BPF_ADD, R4, R7),
+                       BPF_ALU32_REG(BPF_ADD, R4, R8),
+                       BPF_ALU32_REG(BPF_ADD, R4, R9), /* R4 == 12177 */
+                       BPF_JMP_IMM(BPF_JEQ, R4, 12177, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU32_REG(BPF_ADD, R5, R0),
+                       BPF_ALU32_REG(BPF_ADD, R5, R1),
+                       BPF_ALU32_REG(BPF_ADD, R5, R2),
+                       BPF_ALU32_REG(BPF_ADD, R5, R3),
+                       BPF_ALU32_REG(BPF_ADD, R5, R4),
+                       BPF_ALU32_REG(BPF_ADD, R5, R5),
+                       BPF_ALU32_REG(BPF_ADD, R5, R6),
+                       BPF_ALU32_REG(BPF_ADD, R5, R7),
+                       BPF_ALU32_REG(BPF_ADD, R5, R8),
+                       BPF_ALU32_REG(BPF_ADD, R5, R9), /* R5 == 36518 */
+                       BPF_JMP_IMM(BPF_JEQ, R5, 36518, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU32_REG(BPF_ADD, R6, R0),
+                       BPF_ALU32_REG(BPF_ADD, R6, R1),
+                       BPF_ALU32_REG(BPF_ADD, R6, R2),
+                       BPF_ALU32_REG(BPF_ADD, R6, R3),
+                       BPF_ALU32_REG(BPF_ADD, R6, R4),
+                       BPF_ALU32_REG(BPF_ADD, R6, R5),
+                       BPF_ALU32_REG(BPF_ADD, R6, R6),
+                       BPF_ALU32_REG(BPF_ADD, R6, R7),
+                       BPF_ALU32_REG(BPF_ADD, R6, R8),
+                       BPF_ALU32_REG(BPF_ADD, R6, R9), /* R6 == 109540 */
+                       BPF_JMP_IMM(BPF_JEQ, R6, 109540, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU32_REG(BPF_ADD, R7, R0),
+                       BPF_ALU32_REG(BPF_ADD, R7, R1),
+                       BPF_ALU32_REG(BPF_ADD, R7, R2),
+                       BPF_ALU32_REG(BPF_ADD, R7, R3),
+                       BPF_ALU32_REG(BPF_ADD, R7, R4),
+                       BPF_ALU32_REG(BPF_ADD, R7, R5),
+                       BPF_ALU32_REG(BPF_ADD, R7, R6),
+                       BPF_ALU32_REG(BPF_ADD, R7, R7),
+                       BPF_ALU32_REG(BPF_ADD, R7, R8),
+                       BPF_ALU32_REG(BPF_ADD, R7, R9), /* R7 == 328605 */
+                       BPF_JMP_IMM(BPF_JEQ, R7, 328605, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU32_REG(BPF_ADD, R8, R0),
+                       BPF_ALU32_REG(BPF_ADD, R8, R1),
+                       BPF_ALU32_REG(BPF_ADD, R8, R2),
+                       BPF_ALU32_REG(BPF_ADD, R8, R3),
+                       BPF_ALU32_REG(BPF_ADD, R8, R4),
+                       BPF_ALU32_REG(BPF_ADD, R8, R5),
+                       BPF_ALU32_REG(BPF_ADD, R8, R6),
+                       BPF_ALU32_REG(BPF_ADD, R8, R7),
+                       BPF_ALU32_REG(BPF_ADD, R8, R8),
+                       BPF_ALU32_REG(BPF_ADD, R8, R9), /* R8 == 985799 */
+                       BPF_JMP_IMM(BPF_JEQ, R8, 985799, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU32_REG(BPF_ADD, R9, R0),
+                       BPF_ALU32_REG(BPF_ADD, R9, R1),
+                       BPF_ALU32_REG(BPF_ADD, R9, R2),
+                       BPF_ALU32_REG(BPF_ADD, R9, R3),
+                       BPF_ALU32_REG(BPF_ADD, R9, R4),
+                       BPF_ALU32_REG(BPF_ADD, R9, R5),
+                       BPF_ALU32_REG(BPF_ADD, R9, R6),
+                       BPF_ALU32_REG(BPF_ADD, R9, R7),
+                       BPF_ALU32_REG(BPF_ADD, R9, R8),
+                       BPF_ALU32_REG(BPF_ADD, R9, R9), /* R9 == 2957380 */
+                       BPF_ALU32_REG(BPF_MOV, R0, R9),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { },
+               { { 0, 2957380 } }
+       },
+       {       /* Mainly checking JIT here. */
+               "INT: SUB",
+               .u.insns_int = {
+                       BPF_ALU64_IMM(BPF_MOV, R0, 0),
+                       BPF_ALU64_IMM(BPF_MOV, R1, 1),
+                       BPF_ALU64_IMM(BPF_MOV, R2, 2),
+                       BPF_ALU64_IMM(BPF_MOV, R3, 3),
+                       BPF_ALU64_IMM(BPF_MOV, R4, 4),
+                       BPF_ALU64_IMM(BPF_MOV, R5, 5),
+                       BPF_ALU64_IMM(BPF_MOV, R6, 6),
+                       BPF_ALU64_IMM(BPF_MOV, R7, 7),
+                       BPF_ALU64_IMM(BPF_MOV, R8, 8),
+                       BPF_ALU64_IMM(BPF_MOV, R9, 9),
+                       BPF_ALU64_REG(BPF_SUB, R0, R0),
+                       BPF_ALU64_REG(BPF_SUB, R0, R1),
+                       BPF_ALU64_REG(BPF_SUB, R0, R2),
+                       BPF_ALU64_REG(BPF_SUB, R0, R3),
+                       BPF_ALU64_REG(BPF_SUB, R0, R4),
+                       BPF_ALU64_REG(BPF_SUB, R0, R5),
+                       BPF_ALU64_REG(BPF_SUB, R0, R6),
+                       BPF_ALU64_REG(BPF_SUB, R0, R7),
+                       BPF_ALU64_REG(BPF_SUB, R0, R8),
+                       BPF_ALU64_REG(BPF_SUB, R0, R9),
+                       BPF_ALU64_IMM(BPF_SUB, R0, 10),
+                       BPF_JMP_IMM(BPF_JEQ, R0, -55, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_SUB, R1, R0),
+                       BPF_ALU64_REG(BPF_SUB, R1, R2),
+                       BPF_ALU64_REG(BPF_SUB, R1, R3),
+                       BPF_ALU64_REG(BPF_SUB, R1, R4),
+                       BPF_ALU64_REG(BPF_SUB, R1, R5),
+                       BPF_ALU64_REG(BPF_SUB, R1, R6),
+                       BPF_ALU64_REG(BPF_SUB, R1, R7),
+                       BPF_ALU64_REG(BPF_SUB, R1, R8),
+                       BPF_ALU64_REG(BPF_SUB, R1, R9),
+                       BPF_ALU64_IMM(BPF_SUB, R1, 10),
+                       BPF_ALU64_REG(BPF_SUB, R2, R0),
+                       BPF_ALU64_REG(BPF_SUB, R2, R1),
+                       BPF_ALU64_REG(BPF_SUB, R2, R3),
+                       BPF_ALU64_REG(BPF_SUB, R2, R4),
+                       BPF_ALU64_REG(BPF_SUB, R2, R5),
+                       BPF_ALU64_REG(BPF_SUB, R2, R6),
+                       BPF_ALU64_REG(BPF_SUB, R2, R7),
+                       BPF_ALU64_REG(BPF_SUB, R2, R8),
+                       BPF_ALU64_REG(BPF_SUB, R2, R9),
+                       BPF_ALU64_IMM(BPF_SUB, R2, 10),
+                       BPF_ALU64_REG(BPF_SUB, R3, R0),
+                       BPF_ALU64_REG(BPF_SUB, R3, R1),
+                       BPF_ALU64_REG(BPF_SUB, R3, R2),
+                       BPF_ALU64_REG(BPF_SUB, R3, R4),
+                       BPF_ALU64_REG(BPF_SUB, R3, R5),
+                       BPF_ALU64_REG(BPF_SUB, R3, R6),
+                       BPF_ALU64_REG(BPF_SUB, R3, R7),
+                       BPF_ALU64_REG(BPF_SUB, R3, R8),
+                       BPF_ALU64_REG(BPF_SUB, R3, R9),
+                       BPF_ALU64_IMM(BPF_SUB, R3, 10),
+                       BPF_ALU64_REG(BPF_SUB, R4, R0),
+                       BPF_ALU64_REG(BPF_SUB, R4, R1),
+                       BPF_ALU64_REG(BPF_SUB, R4, R2),
+                       BPF_ALU64_REG(BPF_SUB, R4, R3),
+                       BPF_ALU64_REG(BPF_SUB, R4, R5),
+                       BPF_ALU64_REG(BPF_SUB, R4, R6),
+                       BPF_ALU64_REG(BPF_SUB, R4, R7),
+                       BPF_ALU64_REG(BPF_SUB, R4, R8),
+                       BPF_ALU64_REG(BPF_SUB, R4, R9),
+                       BPF_ALU64_IMM(BPF_SUB, R4, 10),
+                       BPF_ALU64_REG(BPF_SUB, R5, R0),
+                       BPF_ALU64_REG(BPF_SUB, R5, R1),
+                       BPF_ALU64_REG(BPF_SUB, R5, R2),
+                       BPF_ALU64_REG(BPF_SUB, R5, R3),
+                       BPF_ALU64_REG(BPF_SUB, R5, R4),
+                       BPF_ALU64_REG(BPF_SUB, R5, R6),
+                       BPF_ALU64_REG(BPF_SUB, R5, R7),
+                       BPF_ALU64_REG(BPF_SUB, R5, R8),
+                       BPF_ALU64_REG(BPF_SUB, R5, R9),
+                       BPF_ALU64_IMM(BPF_SUB, R5, 10),
+                       BPF_ALU64_REG(BPF_SUB, R6, R0),
+                       BPF_ALU64_REG(BPF_SUB, R6, R1),
+                       BPF_ALU64_REG(BPF_SUB, R6, R2),
+                       BPF_ALU64_REG(BPF_SUB, R6, R3),
+                       BPF_ALU64_REG(BPF_SUB, R6, R4),
+                       BPF_ALU64_REG(BPF_SUB, R6, R5),
+                       BPF_ALU64_REG(BPF_SUB, R6, R7),
+                       BPF_ALU64_REG(BPF_SUB, R6, R8),
+                       BPF_ALU64_REG(BPF_SUB, R6, R9),
+                       BPF_ALU64_IMM(BPF_SUB, R6, 10),
+                       BPF_ALU64_REG(BPF_SUB, R7, R0),
+                       BPF_ALU64_REG(BPF_SUB, R7, R1),
+                       BPF_ALU64_REG(BPF_SUB, R7, R2),
+                       BPF_ALU64_REG(BPF_SUB, R7, R3),
+                       BPF_ALU64_REG(BPF_SUB, R7, R4),
+                       BPF_ALU64_REG(BPF_SUB, R7, R5),
+                       BPF_ALU64_REG(BPF_SUB, R7, R6),
+                       BPF_ALU64_REG(BPF_SUB, R7, R8),
+                       BPF_ALU64_REG(BPF_SUB, R7, R9),
+                       BPF_ALU64_IMM(BPF_SUB, R7, 10),
+                       BPF_ALU64_REG(BPF_SUB, R8, R0),
+                       BPF_ALU64_REG(BPF_SUB, R8, R1),
+                       BPF_ALU64_REG(BPF_SUB, R8, R2),
+                       BPF_ALU64_REG(BPF_SUB, R8, R3),
+                       BPF_ALU64_REG(BPF_SUB, R8, R4),
+                       BPF_ALU64_REG(BPF_SUB, R8, R5),
+                       BPF_ALU64_REG(BPF_SUB, R8, R6),
+                       BPF_ALU64_REG(BPF_SUB, R8, R7),
+                       BPF_ALU64_REG(BPF_SUB, R8, R9),
+                       BPF_ALU64_IMM(BPF_SUB, R8, 10),
+                       BPF_ALU64_REG(BPF_SUB, R9, R0),
+                       BPF_ALU64_REG(BPF_SUB, R9, R1),
+                       BPF_ALU64_REG(BPF_SUB, R9, R2),
+                       BPF_ALU64_REG(BPF_SUB, R9, R3),
+                       BPF_ALU64_REG(BPF_SUB, R9, R4),
+                       BPF_ALU64_REG(BPF_SUB, R9, R5),
+                       BPF_ALU64_REG(BPF_SUB, R9, R6),
+                       BPF_ALU64_REG(BPF_SUB, R9, R7),
+                       BPF_ALU64_REG(BPF_SUB, R9, R8),
+                       BPF_ALU64_IMM(BPF_SUB, R9, 10),
+                       BPF_ALU64_IMM(BPF_SUB, R0, 10),
+                       BPF_ALU64_IMM(BPF_NEG, R0, 0),
+                       BPF_ALU64_REG(BPF_SUB, R0, R1),
+                       BPF_ALU64_REG(BPF_SUB, R0, R2),
+                       BPF_ALU64_REG(BPF_SUB, R0, R3),
+                       BPF_ALU64_REG(BPF_SUB, R0, R4),
+                       BPF_ALU64_REG(BPF_SUB, R0, R5),
+                       BPF_ALU64_REG(BPF_SUB, R0, R6),
+                       BPF_ALU64_REG(BPF_SUB, R0, R7),
+                       BPF_ALU64_REG(BPF_SUB, R0, R8),
+                       BPF_ALU64_REG(BPF_SUB, R0, R9),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { },
+               { { 0, 11 } }
+       },
+       {       /* Mainly checking JIT here. */
+               "INT: XOR",
+               .u.insns_int = {
+                       BPF_ALU64_REG(BPF_SUB, R0, R0),
+                       BPF_ALU64_REG(BPF_XOR, R1, R1),
+                       BPF_JMP_REG(BPF_JEQ, R0, R1, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_IMM(BPF_MOV, R0, 10),
+                       BPF_ALU64_IMM(BPF_MOV, R1, -1),
+                       BPF_ALU64_REG(BPF_SUB, R1, R1),
+                       BPF_ALU64_REG(BPF_XOR, R2, R2),
+                       BPF_JMP_REG(BPF_JEQ, R1, R2, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_SUB, R2, R2),
+                       BPF_ALU64_REG(BPF_XOR, R3, R3),
+                       BPF_ALU64_IMM(BPF_MOV, R0, 10),
+                       BPF_ALU64_IMM(BPF_MOV, R1, -1),
+                       BPF_JMP_REG(BPF_JEQ, R2, R3, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_SUB, R3, R3),
+                       BPF_ALU64_REG(BPF_XOR, R4, R4),
+                       BPF_ALU64_IMM(BPF_MOV, R2, 1),
+                       BPF_ALU64_IMM(BPF_MOV, R5, -1),
+                       BPF_JMP_REG(BPF_JEQ, R3, R4, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_SUB, R4, R4),
+                       BPF_ALU64_REG(BPF_XOR, R5, R5),
+                       BPF_ALU64_IMM(BPF_MOV, R3, 1),
+                       BPF_ALU64_IMM(BPF_MOV, R7, -1),
+                       BPF_JMP_REG(BPF_JEQ, R5, R4, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_IMM(BPF_MOV, R5, 1),
+                       BPF_ALU64_REG(BPF_SUB, R5, R5),
+                       BPF_ALU64_REG(BPF_XOR, R6, R6),
+                       BPF_ALU64_IMM(BPF_MOV, R1, 1),
+                       BPF_ALU64_IMM(BPF_MOV, R8, -1),
+                       BPF_JMP_REG(BPF_JEQ, R5, R6, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_SUB, R6, R6),
+                       BPF_ALU64_REG(BPF_XOR, R7, R7),
+                       BPF_JMP_REG(BPF_JEQ, R7, R6, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_SUB, R7, R7),
+                       BPF_ALU64_REG(BPF_XOR, R8, R8),
+                       BPF_JMP_REG(BPF_JEQ, R7, R8, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_SUB, R8, R8),
+                       BPF_ALU64_REG(BPF_XOR, R9, R9),
+                       BPF_JMP_REG(BPF_JEQ, R9, R8, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_SUB, R9, R9),
+                       BPF_ALU64_REG(BPF_XOR, R0, R0),
+                       BPF_JMP_REG(BPF_JEQ, R9, R0, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_SUB, R1, R1),
+                       BPF_ALU64_REG(BPF_XOR, R0, R0),
+                       BPF_JMP_REG(BPF_JEQ, R9, R0, 2),
+                       BPF_ALU64_IMM(BPF_MOV, R0, 0),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_IMM(BPF_MOV, R0, 1),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { },
+               { { 0, 1 } }
+       },
+       {       /* Mainly checking JIT here. */
+               "INT: MUL",
+               .u.insns_int = {
+                       BPF_ALU64_IMM(BPF_MOV, R0, 11),
+                       BPF_ALU64_IMM(BPF_MOV, R1, 1),
+                       BPF_ALU64_IMM(BPF_MOV, R2, 2),
+                       BPF_ALU64_IMM(BPF_MOV, R3, 3),
+                       BPF_ALU64_IMM(BPF_MOV, R4, 4),
+                       BPF_ALU64_IMM(BPF_MOV, R5, 5),
+                       BPF_ALU64_IMM(BPF_MOV, R6, 6),
+                       BPF_ALU64_IMM(BPF_MOV, R7, 7),
+                       BPF_ALU64_IMM(BPF_MOV, R8, 8),
+                       BPF_ALU64_IMM(BPF_MOV, R9, 9),
+                       BPF_ALU64_REG(BPF_MUL, R0, R0),
+                       BPF_ALU64_REG(BPF_MUL, R0, R1),
+                       BPF_ALU64_REG(BPF_MUL, R0, R2),
+                       BPF_ALU64_REG(BPF_MUL, R0, R3),
+                       BPF_ALU64_REG(BPF_MUL, R0, R4),
+                       BPF_ALU64_REG(BPF_MUL, R0, R5),
+                       BPF_ALU64_REG(BPF_MUL, R0, R6),
+                       BPF_ALU64_REG(BPF_MUL, R0, R7),
+                       BPF_ALU64_REG(BPF_MUL, R0, R8),
+                       BPF_ALU64_REG(BPF_MUL, R0, R9),
+                       BPF_ALU64_IMM(BPF_MUL, R0, 10),
+                       BPF_JMP_IMM(BPF_JEQ, R0, 439084800, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_MUL, R1, R0),
+                       BPF_ALU64_REG(BPF_MUL, R1, R2),
+                       BPF_ALU64_REG(BPF_MUL, R1, R3),
+                       BPF_ALU64_REG(BPF_MUL, R1, R4),
+                       BPF_ALU64_REG(BPF_MUL, R1, R5),
+                       BPF_ALU64_REG(BPF_MUL, R1, R6),
+                       BPF_ALU64_REG(BPF_MUL, R1, R7),
+                       BPF_ALU64_REG(BPF_MUL, R1, R8),
+                       BPF_ALU64_REG(BPF_MUL, R1, R9),
+                       BPF_ALU64_IMM(BPF_MUL, R1, 10),
+                       BPF_ALU64_REG(BPF_MOV, R2, R1),
+                       BPF_ALU64_IMM(BPF_RSH, R2, 32),
+                       BPF_JMP_IMM(BPF_JEQ, R2, 0x5a924, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_IMM(BPF_LSH, R1, 32),
+                       BPF_ALU64_IMM(BPF_ARSH, R1, 32),
+                       BPF_JMP_IMM(BPF_JEQ, R1, 0xebb90000, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_REG(BPF_MUL, R2, R0),
+                       BPF_ALU64_REG(BPF_MUL, R2, R1),
+                       BPF_ALU64_REG(BPF_MUL, R2, R3),
+                       BPF_ALU64_REG(BPF_MUL, R2, R4),
+                       BPF_ALU64_REG(BPF_MUL, R2, R5),
+                       BPF_ALU64_REG(BPF_MUL, R2, R6),
+                       BPF_ALU64_REG(BPF_MUL, R2, R7),
+                       BPF_ALU64_REG(BPF_MUL, R2, R8),
+                       BPF_ALU64_REG(BPF_MUL, R2, R9),
+                       BPF_ALU64_IMM(BPF_MUL, R2, 10),
+                       BPF_ALU64_IMM(BPF_RSH, R2, 32),
+                       BPF_ALU64_REG(BPF_MOV, R0, R2),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { },
+               { { 0, 0x35d97ef2 } }
+       },
+       {
+               "INT: ALU MIX",
+               .u.insns_int = {
+                       BPF_ALU64_IMM(BPF_MOV, R0, 11),
+                       BPF_ALU64_IMM(BPF_ADD, R0, -1),
+                       BPF_ALU64_IMM(BPF_MOV, R2, 2),
+                       BPF_ALU64_IMM(BPF_XOR, R2, 3),
+                       BPF_ALU64_REG(BPF_DIV, R0, R2),
+                       BPF_JMP_IMM(BPF_JEQ, R0, 10, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_IMM(BPF_MOD, R0, 3),
+                       BPF_JMP_IMM(BPF_JEQ, R0, 1, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_ALU64_IMM(BPF_MOV, R0, -1),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { },
+               { { 0, -1 } }
+       },
+       {
+               "INT: DIV + ABS",
+               .u.insns_int = {
+                       BPF_ALU64_REG(BPF_MOV, R6, R1),
+                       BPF_LD_ABS(BPF_B, 3),
+                       BPF_ALU64_IMM(BPF_MOV, R2, 2),
+                       BPF_ALU32_REG(BPF_DIV, R0, R2),
+                       BPF_ALU64_REG(BPF_MOV, R8, R0),
+                       BPF_LD_ABS(BPF_B, 4),
+                       BPF_ALU64_REG(BPF_ADD, R8, R0),
+                       BPF_LD_IND(BPF_B, R8, -70),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { 10, 20, 30, 40, 50 },
+               { { 4, 0 }, { 5, 10 } }
+       },
+       {
+               "INT: DIV by zero",
+               .u.insns_int = {
+                       BPF_ALU64_REG(BPF_MOV, R6, R1),
+                       BPF_ALU64_IMM(BPF_MOV, R7, 0),
+                       BPF_LD_ABS(BPF_B, 3),
+                       BPF_ALU32_REG(BPF_DIV, R0, R7),
+                       BPF_EXIT_INSN(),
+               },
+               INTERNAL,
+               { 10, 20, 30, 40, 50 },
+               { { 3, 0 }, { 4, 0 } }
+       },
+       {
+               "check: missing ret",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_IMM, 1),
+               },
+               CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL,
+               { },
+               { }
+       },
+       {
+               "check: div_k_0",
+               .u.insns = {
+                       BPF_STMT(BPF_ALU | BPF_DIV | BPF_K, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 0)
+               },
+               CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL,
+               { },
+               { }
+       },
+       {
+               "check: unknown insn",
+               .u.insns = {
+                       /* seccomp insn, rejected in socket filter */
+                       BPF_STMT(BPF_LDX | BPF_W | BPF_ABS, 0),
+                       BPF_STMT(BPF_RET | BPF_K, 0)
+               },
+               CLASSIC | FLAG_EXPECTED_FAIL,
+               { },
+               { }
+       },
+       {
+               "check: out of range spill/fill",
+               .u.insns = {
+                       BPF_STMT(BPF_STX, 16),
+                       BPF_STMT(BPF_RET | BPF_K, 0)
+               },
+               CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL,
+               { },
+               { }
+       },
+       {
+               "JUMPS + HOLES",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JGE, 0, 13, 15),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ, 0x90c2894d, 3, 4),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ, 0x90c2894d, 1, 2),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JGE, 0, 14, 15),
+                       BPF_JUMP(BPF_JMP | BPF_JGE, 0, 13, 14),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ, 0x2ac28349, 2, 3),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ, 0x2ac28349, 1, 2),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JGE, 0, 14, 15),
+                       BPF_JUMP(BPF_JMP | BPF_JGE, 0, 13, 14),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ, 0x90d2ff41, 2, 3),
+                       BPF_JUMP(BPF_JMP | BPF_JEQ, 0x90d2ff41, 1, 2),
+                       BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0),
+               },
+               CLASSIC,
+               { 0x00, 0x1b, 0x21, 0x3c, 0x9d, 0xf8,
+                 0x90, 0xe2, 0xba, 0x0a, 0x56, 0xb4,
+                 0x08, 0x00,
+                 0x45, 0x00, 0x00, 0x28, 0x00, 0x00,
+                 0x20, 0x00, 0x40, 0x11, 0x00, 0x00, /* IP header */
+                 0xc0, 0xa8, 0x33, 0x01,
+                 0xc0, 0xa8, 0x33, 0x02,
+                 0xbb, 0xb6,
+                 0xa9, 0xfa,
+                 0x00, 0x14, 0x00, 0x00,
+                 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+                 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+                 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+                 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+                 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+                 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+                 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+                 0xcc, 0xcc, 0xcc, 0xcc },
+               { { 88, 0x001b } }
+       },
+       {
+               "check: RET X",
+               .u.insns = {
+                       BPF_STMT(BPF_RET | BPF_X, 0),
+               },
+               CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL,
+               { },
+               { },
+       },
+       {
+               "check: LDX + RET X",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_IMM, 42),
+                       BPF_STMT(BPF_RET | BPF_X, 0),
+               },
+               CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL,
+               { },
+               { },
+       },
+       {       /* Mainly checking JIT here. */
+               "M[]: alt STX + LDX",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_IMM, 100),
+                       BPF_STMT(BPF_STX, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 0),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 1),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 1),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 2),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 2),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 3),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 3),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 4),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 4),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 5),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 5),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 6),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 6),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 7),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 7),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 8),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 8),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 9),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 9),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 10),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 10),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 11),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 11),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 12),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 12),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 13),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 13),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 14),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 14),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_STX, 15),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 15),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 1),
+                       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0),
+               },
+               CLASSIC | FLAG_NO_DATA,
+               { },
+               { { 0, 116 } },
+       },
+       {       /* Mainly checking JIT here. */
+               "M[]: full STX + full LDX",
+               .u.insns = {
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xbadfeedb),
+                       BPF_STMT(BPF_STX, 0),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xecabedae),
+                       BPF_STMT(BPF_STX, 1),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xafccfeaf),
+                       BPF_STMT(BPF_STX, 2),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xbffdcedc),
+                       BPF_STMT(BPF_STX, 3),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xfbbbdccb),
+                       BPF_STMT(BPF_STX, 4),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xfbabcbda),
+                       BPF_STMT(BPF_STX, 5),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xaedecbdb),
+                       BPF_STMT(BPF_STX, 6),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xadebbade),
+                       BPF_STMT(BPF_STX, 7),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xfcfcfaec),
+                       BPF_STMT(BPF_STX, 8),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xbcdddbdc),
+                       BPF_STMT(BPF_STX, 9),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xfeefdfac),
+                       BPF_STMT(BPF_STX, 10),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xcddcdeea),
+                       BPF_STMT(BPF_STX, 11),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xaccfaebb),
+                       BPF_STMT(BPF_STX, 12),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xbdcccdcf),
+                       BPF_STMT(BPF_STX, 13),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xaaedecde),
+                       BPF_STMT(BPF_STX, 14),
+                       BPF_STMT(BPF_LDX | BPF_IMM, 0xfaeacdad),
+                       BPF_STMT(BPF_STX, 15),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 0),
+                       BPF_STMT(BPF_MISC | BPF_TXA, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 1),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 2),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 3),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 4),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 5),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 6),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 7),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 8),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 9),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 10),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 11),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 12),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 13),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 14),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_LDX | BPF_MEM, 15),
+                       BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
+                       BPF_STMT(BPF_RET | BPF_A, 0),
+               },
+               CLASSIC | FLAG_NO_DATA,
+               { },
+               { { 0, 0x2a5a5e5 } },
+       },
+       {
+               "check: SKF_AD_MAX",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF + SKF_AD_MAX),
+                       BPF_STMT(BPF_RET | BPF_A, 0),
+               },
+               CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL,
+               { },
+               { },
+       },
+       {       /* Passes checker but fails during runtime. */
+               "LD [SKF_AD_OFF-1]",
+               .u.insns = {
+                       BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
+                                SKF_AD_OFF - 1),
+                       BPF_STMT(BPF_RET | BPF_K, 1),
+               },
+               CLASSIC,
+               { },
+               { { 1, 0 } },
+       },
+};
+
+static struct net_device dev;
+
+static struct sk_buff *populate_skb(char *buf, int size)
+{
+       struct sk_buff *skb;
+
+       if (size >= MAX_DATA)
+               return NULL;
+
+       skb = alloc_skb(MAX_DATA, GFP_KERNEL);
+       if (!skb)
+               return NULL;
+
+       memcpy(__skb_put(skb, size), buf, size);
+
+       /* Initialize a fake skb with test pattern. */
+       skb_reset_mac_header(skb);
+       skb->protocol = htons(ETH_P_IP);
+       skb->pkt_type = SKB_TYPE;
+       skb->mark = SKB_MARK;
+       skb->hash = SKB_HASH;
+       skb->queue_mapping = SKB_QUEUE_MAP;
+       skb->vlan_tci = SKB_VLAN_TCI;
+       skb->dev = &dev;
+       skb->dev->ifindex = SKB_DEV_IFINDEX;
+       skb->dev->type = SKB_DEV_TYPE;
+       skb_set_network_header(skb, min(size, ETH_HLEN));
+
+       return skb;
+}
+
+static void *generate_test_data(struct bpf_test *test, int sub)
+{
+       if (test->aux & FLAG_NO_DATA)
+               return NULL;
+
+       /* Test case expects an skb, so populate one. Various
+        * subtests generate skbs of different sizes based on
+        * the same data.
+        */
+       return populate_skb(test->data, test->test[sub].data_size);
+}
+
+static void release_test_data(const struct bpf_test *test, void *data)
+{
+       if (test->aux & FLAG_NO_DATA)
+               return;
+
+       kfree_skb(data);
+}
+
+static int probe_filter_length(struct sock_filter *fp)
+{
+       int len = 0;
+
+       for (len = MAX_INSNS - 1; len > 0; --len)
+               if (fp[len].code != 0 || fp[len].k != 0)
+                       break;
+
+       return len + 1;
+}
+
+static struct sk_filter *generate_filter(int which, int *err)
+{
+       struct sk_filter *fp;
+       struct sock_fprog_kern fprog;
+       unsigned int flen = probe_filter_length(tests[which].u.insns);
+       __u8 test_type = tests[which].aux & TEST_TYPE_MASK;
+
+       switch (test_type) {
+       case CLASSIC:
+               fprog.filter = tests[which].u.insns;
+               fprog.len = flen;
+
+               *err = sk_unattached_filter_create(&fp, &fprog);
+               if (tests[which].aux & FLAG_EXPECTED_FAIL) {
+                       if (*err == -EINVAL) {
+                               pr_cont("PASS\n");
+                               /* Verifier rejected filter as expected. */
+                               *err = 0;
+                               return NULL;
+                       } else {
+                               pr_cont("UNEXPECTED_PASS\n");
+                               /* Verifier didn't reject the test that's
+                                * bad enough, just return!
+                                */
+                               *err = -EINVAL;
+                               return NULL;
+                       }
+               }
+               /* We don't expect to fail. */
+               if (*err) {
+                       pr_cont("FAIL to attach err=%d len=%d\n",
+                               *err, fprog.len);
+                       return NULL;
+               }
+               break;
+
+       case INTERNAL:
+               fp = kzalloc(sk_filter_size(flen), GFP_KERNEL);
+               if (fp == NULL) {
+                       pr_cont("UNEXPECTED_FAIL no memory left\n");
+                       *err = -ENOMEM;
+                       return NULL;
+               }
+
+               fp->len = flen;
+               memcpy(fp->insnsi, tests[which].u.insns_int,
+                      fp->len * sizeof(struct sock_filter_int));
+
+               sk_filter_select_runtime(fp);
+               break;
+       }
+
+       *err = 0;
+       return fp;
+}
+
+static void release_filter(struct sk_filter *fp, int which)
+{
+       __u8 test_type = tests[which].aux & TEST_TYPE_MASK;
+
+       switch (test_type) {
+       case CLASSIC:
+               sk_unattached_filter_destroy(fp);
+               break;
+       case INTERNAL:
+               sk_filter_free(fp);
+               break;
+       }
+}
+
+static int __run_one(const struct sk_filter *fp, const void *data,
+                    int runs, u64 *duration)
+{
+       u64 start, finish;
+       int ret, i;
+
+       start = ktime_to_us(ktime_get());
+
+       for (i = 0; i < runs; i++)
+               ret = SK_RUN_FILTER(fp, data);
+
+       finish = ktime_to_us(ktime_get());
+
+       *duration = (finish - start) * 1000ULL;
+       do_div(*duration, runs);
+
+       return ret;
+}
+
+static int run_one(const struct sk_filter *fp, struct bpf_test *test)
+{
+       int err_cnt = 0, i, runs = MAX_TESTRUNS;
+
+       for (i = 0; i < MAX_SUBTESTS; i++) {
+               void *data;
+               u64 duration;
+               u32 ret;
+
+               if (test->test[i].data_size == 0 &&
+                   test->test[i].result == 0)
+                       break;
+
+               data = generate_test_data(test, i);
+               ret = __run_one(fp, data, runs, &duration);
+               release_test_data(test, data);
+
+               if (ret == test->test[i].result) {
+                       pr_cont("%lld ", duration);
+               } else {
+                       pr_cont("ret %d != %d ", ret,
+                               test->test[i].result);
+                       err_cnt++;
+               }
+       }
+
+       return err_cnt;
+}
+
+static __init int test_bpf(void)
+{
+       int i, err_cnt = 0, pass_cnt = 0;
+
+       for (i = 0; i < ARRAY_SIZE(tests); i++) {
+               struct sk_filter *fp;
+               int err;
+
+               pr_info("#%d %s ", i, tests[i].descr);
+
+               fp = generate_filter(i, &err);
+               if (fp == NULL) {
+                       if (err == 0) {
+                               pass_cnt++;
+                               continue;
+                       }
+
+                       return err;
+               }
+               err = run_one(fp, &tests[i]);
+               release_filter(fp, i);
+
+               if (err) {
+                       pr_cont("FAIL (%d times)\n", err);
+                       err_cnt++;
+               } else {
+                       pr_cont("PASS\n");
+                       pass_cnt++;
+               }
+       }
+
+       pr_info("Summary: %d PASSED, %d FAILED\n", pass_cnt, err_cnt);
+       return err_cnt ? -EINVAL : 0;
+}
+
+static int __init test_bpf_init(void)
+{
+       return test_bpf();
+}
+
+static void __exit test_bpf_exit(void)
+{
+}
+
+module_init(test_bpf_init);
+module_exit(test_bpf_exit);
+
+MODULE_LICENSE("GPL");
index 3c32bd257b73975a33ba104c1c3b3797d9f29843..9012b1c922b61acd28fffb7f50b4968da9293b2f 100644 (file)
@@ -63,7 +63,7 @@ bool vlan_do_receive(struct sk_buff **skbp)
 }
 
 /* Must be invoked with rcu_read_lock. */
-struct net_device *__vlan_find_dev_deep(struct net_device *dev,
+struct net_device *__vlan_find_dev_deep_rcu(struct net_device *dev,
                                        __be16 vlan_proto, u16 vlan_id)
 {
        struct vlan_info *vlan_info = rcu_dereference(dev->vlan_info);
@@ -81,13 +81,13 @@ struct net_device *__vlan_find_dev_deep(struct net_device *dev,
 
                upper_dev = netdev_master_upper_dev_get_rcu(dev);
                if (upper_dev)
-                       return __vlan_find_dev_deep(upper_dev,
+                       return __vlan_find_dev_deep_rcu(upper_dev,
                                                    vlan_proto, vlan_id);
        }
 
        return NULL;
 }
-EXPORT_SYMBOL(__vlan_find_dev_deep);
+EXPORT_SYMBOL(__vlan_find_dev_deep_rcu);
 
 struct net_device *vlan_dev_real_dev(const struct net_device *dev)
 {
index 019efb79708f81976bc6484cc371a8fa6c0c080e..ad2ac3c003988741c066c2bb467c2abc4a523c5a 100644 (file)
@@ -643,9 +643,9 @@ static netdev_features_t vlan_dev_fix_features(struct net_device *dev,
        struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;
        netdev_features_t old_features = features;
 
-       features &= real_dev->vlan_features;
+       features = netdev_intersect_features(features, real_dev->vlan_features);
        features |= NETIF_F_RXCSUM;
-       features &= real_dev->features;
+       features = netdev_intersect_features(features, real_dev->features);
 
        features |= old_features & NETIF_F_SOFT_FEATURES;
        features |= NETIF_F_LLTX;
@@ -671,38 +671,36 @@ static void vlan_ethtool_get_drvinfo(struct net_device *dev,
 
 static struct rtnl_link_stats64 *vlan_dev_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 {
+       struct vlan_pcpu_stats *p;
+       u32 rx_errors = 0, tx_dropped = 0;
+       int i;
 
-       if (vlan_dev_priv(dev)->vlan_pcpu_stats) {
-               struct vlan_pcpu_stats *p;
-               u32 rx_errors = 0, tx_dropped = 0;
-               int i;
-
-               for_each_possible_cpu(i) {
-                       u64 rxpackets, rxbytes, rxmulticast, txpackets, txbytes;
-                       unsigned int start;
-
-                       p = per_cpu_ptr(vlan_dev_priv(dev)->vlan_pcpu_stats, i);
-                       do {
-                               start = u64_stats_fetch_begin_irq(&p->syncp);
-                               rxpackets       = p->rx_packets;
-                               rxbytes         = p->rx_bytes;
-                               rxmulticast     = p->rx_multicast;
-                               txpackets       = p->tx_packets;
-                               txbytes         = p->tx_bytes;
-                       } while (u64_stats_fetch_retry_irq(&p->syncp, start));
-
-                       stats->rx_packets       += rxpackets;
-                       stats->rx_bytes         += rxbytes;
-                       stats->multicast        += rxmulticast;
-                       stats->tx_packets       += txpackets;
-                       stats->tx_bytes         += txbytes;
-                       /* rx_errors & tx_dropped are u32 */
-                       rx_errors       += p->rx_errors;
-                       tx_dropped      += p->tx_dropped;
-               }
-               stats->rx_errors  = rx_errors;
-               stats->tx_dropped = tx_dropped;
+       for_each_possible_cpu(i) {
+               u64 rxpackets, rxbytes, rxmulticast, txpackets, txbytes;
+               unsigned int start;
+
+               p = per_cpu_ptr(vlan_dev_priv(dev)->vlan_pcpu_stats, i);
+               do {
+                       start = u64_stats_fetch_begin_irq(&p->syncp);
+                       rxpackets       = p->rx_packets;
+                       rxbytes         = p->rx_bytes;
+                       rxmulticast     = p->rx_multicast;
+                       txpackets       = p->tx_packets;
+                       txbytes         = p->tx_bytes;
+               } while (u64_stats_fetch_retry_irq(&p->syncp, start));
+
+               stats->rx_packets       += rxpackets;
+               stats->rx_bytes         += rxbytes;
+               stats->multicast        += rxmulticast;
+               stats->tx_packets       += txpackets;
+               stats->tx_bytes         += txbytes;
+               /* rx_errors & tx_dropped are u32 */
+               rx_errors       += p->rx_errors;
+               tx_dropped      += p->tx_dropped;
        }
+       stats->rx_errors  = rx_errors;
+       stats->tx_dropped = tx_dropped;
+
        return stats;
 }
 
index 786ee2f83d5fea1dbfd6bb2660544d7f88e1eff2..01a1082e02b3157b3abc84e6b23844ed3a26f2f2 100644 (file)
@@ -1669,7 +1669,7 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
                goto out;
        }
 
-       if (sk->sk_no_check == 1)
+       if (sk->sk_no_check_tx)
                ddp->deh_sum = 0;
        else
                ddp->deh_sum = atalk_checksum(skb, len + sizeof(*ddp));
index 1281049c135f269f9ab0553eb51a681cd97b2079..d8e5d0c2ebbc2acb9a084581f5fa9f99fbd904da 100644 (file)
@@ -263,17 +263,11 @@ static int svc_connect(struct socket *sock, struct sockaddr *sockaddr,
                        goto out;
                }
        }
-/*
- * Not supported yet
- *
- * #ifndef CONFIG_SINGLE_SIGITF
- */
+
        vcc->qos.txtp.max_pcr = SELECT_TOP_PCR(vcc->qos.txtp);
        vcc->qos.txtp.pcr = 0;
        vcc->qos.txtp.min_pcr = 0;
-/*
- * #endif
- */
+
        error = vcc_connect(sock, vcc->itf, vcc->vpi, vcc->vci);
        if (!error)
                sock->state = SS_CONNECTED;
index b758881be108c84bfa059cf61f15d44a9008c1c8..a12e25efaf6ff055094f843c7c5536ce861f593a 100644 (file)
@@ -245,6 +245,7 @@ static int batadv_algorithms_open(struct inode *inode, struct file *file)
 static int batadv_originators_open(struct inode *inode, struct file *file)
 {
        struct net_device *net_dev = (struct net_device *)inode->i_private;
+
        return single_open(file, batadv_orig_seq_print_text, net_dev);
 }
 
@@ -258,18 +259,21 @@ static int batadv_originators_hardif_open(struct inode *inode,
                                          struct file *file)
 {
        struct net_device *net_dev = (struct net_device *)inode->i_private;
+
        return single_open(file, batadv_orig_hardif_seq_print_text, net_dev);
 }
 
 static int batadv_gateways_open(struct inode *inode, struct file *file)
 {
        struct net_device *net_dev = (struct net_device *)inode->i_private;
+
        return single_open(file, batadv_gw_client_seq_print_text, net_dev);
 }
 
 static int batadv_transtable_global_open(struct inode *inode, struct file *file)
 {
        struct net_device *net_dev = (struct net_device *)inode->i_private;
+
        return single_open(file, batadv_tt_global_seq_print_text, net_dev);
 }
 
@@ -277,6 +281,7 @@ static int batadv_transtable_global_open(struct inode *inode, struct file *file)
 static int batadv_bla_claim_table_open(struct inode *inode, struct file *file)
 {
        struct net_device *net_dev = (struct net_device *)inode->i_private;
+
        return single_open(file, batadv_bla_claim_table_seq_print_text,
                           net_dev);
 }
@@ -285,6 +290,7 @@ static int batadv_bla_backbone_table_open(struct inode *inode,
                                          struct file *file)
 {
        struct net_device *net_dev = (struct net_device *)inode->i_private;
+
        return single_open(file, batadv_bla_backbone_table_seq_print_text,
                           net_dev);
 }
@@ -300,6 +306,7 @@ static int batadv_bla_backbone_table_open(struct inode *inode,
 static int batadv_dat_cache_open(struct inode *inode, struct file *file)
 {
        struct net_device *net_dev = (struct net_device *)inode->i_private;
+
        return single_open(file, batadv_dat_cache_seq_print_text, net_dev);
 }
 #endif
@@ -307,6 +314,7 @@ static int batadv_dat_cache_open(struct inode *inode, struct file *file)
 static int batadv_transtable_local_open(struct inode *inode, struct file *file)
 {
        struct net_device *net_dev = (struct net_device *)inode->i_private;
+
        return single_open(file, batadv_tt_local_seq_print_text, net_dev);
 }
 
@@ -319,6 +327,7 @@ struct batadv_debuginfo {
 static int batadv_nc_nodes_open(struct inode *inode, struct file *file)
 {
        struct net_device *net_dev = (struct net_device *)inode->i_private;
+
        return single_open(file, batadv_nc_nodes_seq_print_text, net_dev);
 }
 #endif
@@ -333,7 +342,7 @@ struct batadv_debuginfo batadv_debuginfo_##_name = {        \
                  .llseek = seq_lseek,                  \
                  .release = single_release,            \
                }                                       \
-};
+}
 
 /* the following attributes are general and therefore they will be directly
  * placed in the BATADV_DEBUGFS_SUBDIR subdirectory of debugfs
@@ -395,7 +404,7 @@ struct batadv_debuginfo batadv_hardif_debuginfo_##_name = { \
                .llseek = seq_lseek,                            \
                .release = single_release,                      \
        },                                                      \
-};
+}
 static BATADV_HARDIF_DEBUGINFO(originators, S_IRUGO,
                               batadv_originators_hardif_open);
 
index aa5d4946d0d784d32fdcdce217c4f2bb482502f0..f2c066b2171640c4092a60bc6fc8ef603baa5c09 100644 (file)
@@ -594,7 +594,7 @@ static bool batadv_dat_send_data(struct batadv_priv *bat_priv,
                if (!neigh_node)
                        goto free_orig;
 
-               tmp_skb = pskb_copy(skb, GFP_ATOMIC);
+               tmp_skb = pskb_copy_for_clone(skb, GFP_ATOMIC);
                if (!batadv_send_skb_prepare_unicast_4addr(bat_priv, tmp_skb,
                                                           cand[i].orig_node,
                                                           packet_subtype)) {
@@ -662,6 +662,7 @@ static void batadv_dat_tvlv_container_update(struct batadv_priv *bat_priv)
 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);
 }
 
index 770dc890ceefdb712f254b378c825cbeab255742..118b990bae25d7ecc7221330546b59a5391cd21e 100644 (file)
@@ -24,7 +24,7 @@
 #define BATADV_DRIVER_DEVICE "batman-adv"
 
 #ifndef BATADV_SOURCE_VERSION
-#define BATADV_SOURCE_VERSION "2014.2.0"
+#define BATADV_SOURCE_VERSION "2014.3.0"
 #endif
 
 /* B.A.T.M.A.N. parameters */
index a9546fe541ebb0ff8905fd3fe82d48e48302ce9e..8d04d174669ed29c467436d33b099ad5d0ca13c4 100644 (file)
@@ -86,6 +86,7 @@ static void batadv_nc_tvlv_container_update(struct batadv_priv *bat_priv)
 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);
 }
 
@@ -1343,7 +1344,7 @@ static void batadv_nc_skb_store_before_coding(struct batadv_priv *bat_priv,
        struct ethhdr *ethhdr;
 
        /* Copy skb header to change the mac header */
-       skb = pskb_copy(skb, GFP_ATOMIC);
+       skb = pskb_copy_for_clone(skb, GFP_ATOMIC);
        if (!skb)
                return;
 
index 744a59b85e15ded75f61da8a9fa5a8a87cdb7b8d..e7ee65dc20bf4f25a1a8d0134c66b0bfaef25bd3 100644 (file)
@@ -884,7 +884,7 @@ static void batadv_softif_init_early(struct net_device *dev)
        /* generate random address */
        eth_hw_addr_random(dev);
 
-       SET_ETHTOOL_OPS(dev, &batadv_ethtool_ops);
+       dev->ethtool_ops = &batadv_ethtool_ops;
 
        memset(priv, 0, sizeof(*priv));
 }
index 1ebb0d9e2ea547d1c263a6b09d30d81214e4ba33..fc47baa888c54896c6ccde6352202736d3c9ba6b 100644 (file)
 static struct net_device *batadv_kobj_to_netdev(struct kobject *obj)
 {
        struct device *dev = container_of(obj->parent, struct device, kobj);
+
        return to_net_dev(dev);
 }
 
 static struct batadv_priv *batadv_kobj_to_batpriv(struct kobject *obj)
 {
        struct net_device *net_dev = batadv_kobj_to_netdev(obj);
+
        return netdev_priv(net_dev);
 }
 
@@ -106,7 +108,7 @@ struct batadv_attribute batadv_attr_vlan_##_name = {        \
                 .mode = _mode },                       \
        .show   = _show,                                \
        .store  = _store,                               \
-};
+}
 
 /* Use this, if you have customized show and store functions */
 #define BATADV_ATTR(_name, _mode, _show, _store)       \
@@ -115,7 +117,7 @@ struct batadv_attribute batadv_attr_##_name = {             \
                 .mode = _mode },                       \
        .show   = _show,                                \
        .store  = _store,                               \
-};
+}
 
 #define BATADV_ATTR_SIF_STORE_BOOL(_name, _post_func)                  \
 ssize_t batadv_store_##_name(struct kobject *kobj,                     \
@@ -124,6 +126,7 @@ ssize_t batadv_store_##_name(struct kobject *kobj,                  \
 {                                                                      \
        struct net_device *net_dev = batadv_kobj_to_netdev(kobj);       \
        struct batadv_priv *bat_priv = netdev_priv(net_dev);            \
+                                                                       \
        return __batadv_store_bool_attr(buff, count, _post_func, attr,  \
                                        &bat_priv->_name, net_dev);     \
 }
@@ -133,6 +136,7 @@ ssize_t batadv_show_##_name(struct kobject *kobj,                   \
                            struct attribute *attr, char *buff)         \
 {                                                                      \
        struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj);    \
+                                                                       \
        return sprintf(buff, "%s\n",                                    \
                       atomic_read(&bat_priv->_name) == 0 ?             \
                       "disabled" : "enabled");                         \
@@ -155,6 +159,7 @@ ssize_t batadv_store_##_name(struct kobject *kobj,                  \
 {                                                                      \
        struct net_device *net_dev = batadv_kobj_to_netdev(kobj);       \
        struct batadv_priv *bat_priv = netdev_priv(net_dev);            \
+                                                                       \
        return __batadv_store_uint_attr(buff, count, _min, _max,        \
                                        _post_func, attr,               \
                                        &bat_priv->_name, net_dev);     \
@@ -165,6 +170,7 @@ ssize_t batadv_show_##_name(struct kobject *kobj,                   \
                            struct attribute *attr, char *buff)         \
 {                                                                      \
        struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj);    \
+                                                                       \
        return sprintf(buff, "%i\n", atomic_read(&bat_priv->_name));    \
 }                                                                      \
 
@@ -188,6 +194,7 @@ ssize_t batadv_store_vlan_##_name(struct kobject *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;                                                     \
 }
@@ -202,6 +209,7 @@ ssize_t batadv_show_vlan_##_name(struct kobject *kobj,                      \
        size_t res = sprintf(buff, "%s\n",                              \
                             atomic_read(&vlan->_name) == 0 ?           \
                             "disabled" : "enabled");                   \
+                                                                       \
        batadv_softif_vlan_free_ref(vlan);                              \
        return res;                                                     \
 }
@@ -324,12 +332,14 @@ static ssize_t batadv_show_bat_algo(struct kobject *kobj,
                                    struct attribute *attr, char *buff)
 {
        struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj);
+
        return sprintf(buff, "%s\n", bat_priv->bat_algo_ops->name);
 }
 
 static void batadv_post_gw_reselect(struct net_device *net_dev)
 {
        struct batadv_priv *bat_priv = netdev_priv(net_dev);
+
        batadv_gw_reselect(bat_priv);
 }
 
index 73492b91105ac0aba10aec738415128d0582bfc5..8796ffa08b43b4f57ae1485f4a5867c0f532807c 100644 (file)
@@ -420,12 +420,18 @@ static int conn_send(struct l2cap_conn *conn,
        return 0;
 }
 
-static void get_dest_bdaddr(struct in6_addr *ip6_daddr,
-                           bdaddr_t *addr, u8 *addr_type)
+static u8 get_addr_type_from_eui64(u8 byte)
 {
-       u8 *eui64;
+       /* Is universal(0) or local(1) bit,  */
+       if (byte & 0x02)
+               return ADDR_LE_DEV_RANDOM;
 
-       eui64 = ip6_daddr->s6_addr + 8;
+       return ADDR_LE_DEV_PUBLIC;
+}
+
+static void copy_to_bdaddr(struct in6_addr *ip6_daddr, bdaddr_t *addr)
+{
+       u8 *eui64 = ip6_daddr->s6_addr + 8;
 
        addr->b[0] = eui64[7];
        addr->b[1] = eui64[6];
@@ -433,16 +439,19 @@ static void get_dest_bdaddr(struct in6_addr *ip6_daddr,
        addr->b[3] = eui64[2];
        addr->b[4] = eui64[1];
        addr->b[5] = eui64[0];
+}
 
-       addr->b[5] ^= 2;
+static void convert_dest_bdaddr(struct in6_addr *ip6_daddr,
+                               bdaddr_t *addr, u8 *addr_type)
+{
+       copy_to_bdaddr(ip6_daddr, addr);
 
-       /* Set universal/local bit to 0 */
-       if (addr->b[5] & 1) {
-               addr->b[5] &= ~1;
-               *addr_type = ADDR_LE_DEV_PUBLIC;
-       } else {
-               *addr_type = ADDR_LE_DEV_RANDOM;
-       }
+       /* We need to toggle the U/L bit that we got from IPv6 address
+        * so that we get the proper address and type of the BD address.
+        */
+       addr->b[5] ^= 0x02;
+
+       *addr_type = get_addr_type_from_eui64(addr->b[5]);
 }
 
 static int header_create(struct sk_buff *skb, struct net_device *netdev,
@@ -473,9 +482,11 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev,
                /* Get destination BT device from skb.
                 * If there is no such peer then discard the packet.
                 */
-               get_dest_bdaddr(&hdr->daddr, &addr, &addr_type);
+               convert_dest_bdaddr(&hdr->daddr, &addr, &addr_type);
 
-               BT_DBG("dest addr %pMR type %d", &addr, addr_type);
+               BT_DBG("dest addr %pMR type %s IP %pI6c", &addr,
+                      addr_type == ADDR_LE_DEV_PUBLIC ? "PUBLIC" : "RANDOM",
+                      &hdr->daddr);
 
                read_lock_irqsave(&devices_lock, flags);
                peer = peer_lookup_ba(dev, &addr, addr_type);
@@ -556,7 +567,7 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
        } else {
                unsigned long flags;
 
-               get_dest_bdaddr(&lowpan_cb(skb)->addr, &addr, &addr_type);
+               convert_dest_bdaddr(&lowpan_cb(skb)->addr, &addr, &addr_type);
                eui64_addr = lowpan_cb(skb)->addr.s6_addr + 8;
                dev = lowpan_dev(netdev);
 
@@ -564,8 +575,10 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
                peer = peer_lookup_ba(dev, &addr, addr_type);
                read_unlock_irqrestore(&devices_lock, flags);
 
-               BT_DBG("xmit from %s to %pMR (%pI6c) peer %p", netdev->name,
-                      &addr, &lowpan_cb(skb)->addr, peer);
+               BT_DBG("xmit %s to %pMR type %s IP %pI6c peer %p",
+                      netdev->name, &addr,
+                      addr_type == ADDR_LE_DEV_PUBLIC ? "PUBLIC" : "RANDOM",
+                      &lowpan_cb(skb)->addr, peer);
 
                if (peer && peer->conn)
                        err = send_pkt(peer->conn, netdev->dev_addr,
@@ -620,13 +633,13 @@ static void set_addr(u8 *eui, u8 *addr, u8 addr_type)
        eui[6] = addr[1];
        eui[7] = addr[0];
 
-       eui[0] ^= 2;
-
-       /* Universal/local bit set, RFC 4291 */
+       /* Universal/local bit set, BT 6lowpan draft ch. 3.2.1 */
        if (addr_type == ADDR_LE_DEV_PUBLIC)
-               eui[0] |= 1;
+               eui[0] &= ~0x02;
        else
-               eui[0] &= ~1;
+               eui[0] |= 0x02;
+
+       BT_DBG("type %d addr %*phC", addr_type, 8, eui);
 }
 
 static void set_dev_addr(struct net_device *netdev, bdaddr_t *addr,
@@ -634,7 +647,6 @@ static void set_dev_addr(struct net_device *netdev, bdaddr_t *addr,
 {
        netdev->addr_assign_type = NET_ADDR_PERM;
        set_addr(netdev->dev_addr, addr->b, addr_type);
-       netdev->dev_addr[0] ^= 2;
 }
 
 static void ifup(struct net_device *netdev)
@@ -684,13 +696,6 @@ static int add_peer_conn(struct l2cap_conn *conn, struct lowpan_dev *dev)
 
        memcpy(&peer->eui64_addr, (u8 *)&peer->peer_addr.s6_addr + 8,
               EUI64_ADDR_LEN);
-       peer->eui64_addr[0] ^= 2; /* second bit-flip (Universe/Local)
-                                  * is done according RFC2464
-                                  */
-
-       raw_dump_inline(__func__, "peer IPv6 address",
-                       (unsigned char *)&peer->peer_addr, 16);
-       raw_dump_inline(__func__, "peer EUI64 address", peer->eui64_addr, 8);
 
        write_lock_irqsave(&devices_lock, flags);
        INIT_LIST_HEAD(&peer->list);
index 521fd4f3985e11aef05880be5307b632a46afc53..8671bc79a35bebe23a2f6b269c582bfcfc4add37 100644 (file)
@@ -28,6 +28,7 @@
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
 
 #include "smp.h"
 #include "a2mp.h"
@@ -367,9 +368,23 @@ static void le_conn_timeout(struct work_struct *work)
 {
        struct hci_conn *conn = container_of(work, struct hci_conn,
                                             le_conn_timeout.work);
+       struct hci_dev *hdev = conn->hdev;
 
        BT_DBG("");
 
+       /* We could end up here due to having done directed advertising,
+        * so clean up the state if necessary. This should however only
+        * happen with broken hardware or if low duty cycle was used
+        * (which doesn't have a timeout of its own).
+        */
+       if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
+               u8 enable = 0x00;
+               hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable),
+                            &enable);
+               hci_le_conn_failed(conn, HCI_ERROR_ADVERTISING_TIMEOUT);
+               return;
+       }
+
        hci_le_create_connection_cancel(conn);
 }
 
@@ -393,6 +408,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
        conn->io_capability = hdev->io_capability;
        conn->remote_auth = 0xff;
        conn->key_type = 0xff;
+       conn->tx_power = HCI_TX_POWER_INVALID;
+       conn->max_tx_power = HCI_TX_POWER_INVALID;
 
        set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
        conn->disc_timeout = HCI_DISCONN_TIMEOUT;
@@ -401,6 +418,10 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
        case ACL_LINK:
                conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK;
                break;
+       case LE_LINK:
+               /* conn->src should reflect the local identity address */
+               hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
+               break;
        case SCO_LINK:
                if (lmp_esco_capable(hdev))
                        conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) |
@@ -545,6 +566,11 @@ void hci_le_conn_failed(struct hci_conn *conn, u8 status)
         * favor of connection establishment, we should restart it.
         */
        hci_update_background_scan(hdev);
+
+       /* Re-enable advertising in case this was a failed connection
+        * attempt as a peripheral.
+        */
+       mgmt_reenable_advertising(hdev);
 }
 
 static void create_le_conn_complete(struct hci_dev *hdev, u8 status)
@@ -605,6 +631,45 @@ static void hci_req_add_le_create_conn(struct hci_request *req,
        conn->state = BT_CONNECT;
 }
 
+static void hci_req_directed_advertising(struct hci_request *req,
+                                        struct hci_conn *conn)
+{
+       struct hci_dev *hdev = req->hdev;
+       struct hci_cp_le_set_adv_param cp;
+       u8 own_addr_type;
+       u8 enable;
+
+       enable = 0x00;
+       hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
+
+       /* Clear the HCI_ADVERTISING bit temporarily so that the
+        * hci_update_random_address knows that it's safe to go ahead
+        * and write a new random address. The flag will be set back on
+        * as soon as the SET_ADV_ENABLE HCI command completes.
+        */
+       clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
+
+       /* Set require_privacy to false so that the remote device has a
+        * chance of identifying us.
+        */
+       if (hci_update_random_address(req, false, &own_addr_type) < 0)
+               return;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.type = LE_ADV_DIRECT_IND;
+       cp.own_address_type = own_addr_type;
+       cp.direct_addr_type = conn->dst_type;
+       bacpy(&cp.direct_addr, &conn->dst);
+       cp.channel_map = hdev->le_adv_channel_map;
+
+       hci_req_add(req, HCI_OP_LE_SET_ADV_PARAM, sizeof(cp), &cp);
+
+       enable = 0x01;
+       hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
+
+       conn->state = BT_CONNECT;
+}
+
 struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
                                u8 dst_type, u8 sec_level, u8 auth_type)
 {
@@ -614,9 +679,6 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
        struct hci_request req;
        int err;
 
-       if (test_bit(HCI_ADVERTISING, &hdev->flags))
-               return ERR_PTR(-ENOTSUPP);
-
        /* 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
@@ -664,13 +726,20 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
                return ERR_PTR(-ENOMEM);
 
        conn->dst_type = dst_type;
-
-       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;
 
+       hci_req_init(&req, hdev);
+
+       if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
+               hci_req_directed_advertising(&req, conn);
+               goto create_conn;
+       }
+
+       conn->out = true;
+       conn->link_mode |= HCI_LM_MASTER;
+
        params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
        if (params) {
                conn->le_conn_min_interval = params->conn_min_interval;
@@ -680,8 +749,6 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
                conn->le_conn_max_interval = hdev->le_conn_max_interval;
        }
 
-       hci_req_init(&req, hdev);
-
        /* If controller is scanning, we stop it since some controllers are
         * not able to scan and connect at the same time. Also set the
         * HCI_LE_SCAN_INTERRUPTED flag so that the command complete
@@ -695,6 +762,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
 
        hci_req_add_le_create_conn(&req, conn);
 
+create_conn:
        err = hci_req_run(&req, create_le_conn_complete);
        if (err) {
                hci_conn_del(conn);
index 1c6ffaa8902f5e9fe32a86f2a19cf9073d6f0dae..0a43cce9a914b84613c7ee2d6fc30fdfdb2a0bc5 100644 (file)
@@ -34,6 +34,7 @@
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
 
 #include "smp.h"
 
@@ -579,6 +580,62 @@ static int sniff_max_interval_get(void *data, u64 *val)
 DEFINE_SIMPLE_ATTRIBUTE(sniff_max_interval_fops, sniff_max_interval_get,
                        sniff_max_interval_set, "%llu\n");
 
+static int conn_info_min_age_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val == 0 || val > hdev->conn_info_max_age)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->conn_info_min_age = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int conn_info_min_age_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->conn_info_min_age;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(conn_info_min_age_fops, conn_info_min_age_get,
+                       conn_info_min_age_set, "%llu\n");
+
+static int conn_info_max_age_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val == 0 || val < hdev->conn_info_min_age)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->conn_info_max_age = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int conn_info_max_age_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->conn_info_max_age;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(conn_info_max_age_fops, conn_info_max_age_get,
+                       conn_info_max_age_set, "%llu\n");
+
 static int identity_show(struct seq_file *f, void *p)
 {
        struct hci_dev *hdev = f->private;
@@ -955,14 +1012,9 @@ static ssize_t le_auto_conn_write(struct file *file, const char __user *data,
        if (count < 3)
                return -EINVAL;
 
-       buf = kzalloc(count, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-
-       if (copy_from_user(buf, data, count)) {
-               err = -EFAULT;
-               goto done;
-       }
+       buf = memdup_user(data, count);
+       if (IS_ERR(buf))
+               return PTR_ERR(buf);
 
        if (memcmp(buf, "add", 3) == 0) {
                n = sscanf(&buf[4], "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu %hhu",
@@ -1759,6 +1811,11 @@ static int __hci_init(struct hci_dev *hdev)
                            &blacklist_fops);
        debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
 
+       debugfs_create_file("conn_info_min_age", 0644, hdev->debugfs, hdev,
+                           &conn_info_min_age_fops);
+       debugfs_create_file("conn_info_max_age", 0644, hdev->debugfs, hdev,
+                           &conn_info_max_age_fops);
+
        if (lmp_bredr_capable(hdev)) {
                debugfs_create_file("inquiry_cache", 0444, hdev->debugfs,
                                    hdev, &inquiry_cache_fops);
@@ -1828,6 +1885,9 @@ static int __hci_init(struct hci_dev *hdev)
                                    &lowpan_debugfs_fops);
                debugfs_create_file("le_auto_conn", 0644, hdev->debugfs, hdev,
                                    &le_auto_conn_fops);
+               debugfs_create_u16("discov_interleaved_timeout", 0644,
+                                  hdev->debugfs,
+                                  &hdev->discov_interleaved_timeout);
        }
 
        return 0;
@@ -2033,12 +2093,11 @@ bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data,
 
        hci_remove_remote_oob_data(hdev, &data->bdaddr);
 
-       if (ssp)
-               *ssp = data->ssp_mode;
+       *ssp = data->ssp_mode;
 
        ie = hci_inquiry_cache_lookup(hdev, &data->bdaddr);
        if (ie) {
-               if (ie->data.ssp_mode && ssp)
+               if (ie->data.ssp_mode)
                        *ssp = true;
 
                if (ie->name_state == NAME_NEEDED &&
@@ -3791,6 +3850,9 @@ struct hci_dev *hci_alloc_dev(void)
        hdev->le_conn_max_interval = 0x0038;
 
        hdev->rpa_timeout = HCI_DEFAULT_RPA_TIMEOUT;
+       hdev->discov_interleaved_timeout = DISCOV_INTERLEAVED_TIMEOUT;
+       hdev->conn_info_min_age = DEFAULT_CONN_INFO_MIN_AGE;
+       hdev->conn_info_max_age = DEFAULT_CONN_INFO_MAX_AGE;
 
        mutex_init(&hdev->lock);
        mutex_init(&hdev->req_lock);
index 682f33a383660fac1ce141aa6bd6c54026be973e..21e5913d12e03f4c8410a9464cd5794c9e5a0c70 100644 (file)
@@ -991,10 +991,25 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb)
        if (!sent)
                return;
 
+       if (status)
+               return;
+
        hci_dev_lock(hdev);
 
-       if (!status)
-               mgmt_advertising(hdev, *sent);
+       /* If we're doing connection initation as peripheral. Set a
+        * timeout in case something goes wrong.
+        */
+       if (*sent) {
+               struct hci_conn *conn;
+
+               conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
+               if (conn)
+                       queue_delayed_work(hdev->workqueue,
+                                          &conn->le_conn_timeout,
+                                          HCI_LE_CONN_TIMEOUT);
+       }
+
+       mgmt_advertising(hdev, *sent);
 
        hci_dev_unlock(hdev);
 }
@@ -1018,6 +1033,33 @@ static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb)
        hci_dev_unlock(hdev);
 }
 
+static bool has_pending_adv_report(struct hci_dev *hdev)
+{
+       struct discovery_state *d = &hdev->discovery;
+
+       return bacmp(&d->last_adv_addr, BDADDR_ANY);
+}
+
+static void clear_pending_adv_report(struct hci_dev *hdev)
+{
+       struct discovery_state *d = &hdev->discovery;
+
+       bacpy(&d->last_adv_addr, BDADDR_ANY);
+       d->last_adv_data_len = 0;
+}
+
+static void store_pending_adv_report(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                    u8 bdaddr_type, s8 rssi, u8 *data, u8 len)
+{
+       struct discovery_state *d = &hdev->discovery;
+
+       bacpy(&d->last_adv_addr, bdaddr);
+       d->last_adv_addr_type = bdaddr_type;
+       d->last_adv_rssi = rssi;
+       memcpy(d->last_adv_data, data, len);
+       d->last_adv_data_len = len;
+}
+
 static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
                                      struct sk_buff *skb)
 {
@@ -1036,9 +1078,25 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
        switch (cp->enable) {
        case LE_SCAN_ENABLE:
                set_bit(HCI_LE_SCAN, &hdev->dev_flags);
+               if (hdev->le_scan_type == LE_SCAN_ACTIVE)
+                       clear_pending_adv_report(hdev);
                break;
 
        case LE_SCAN_DISABLE:
+               /* We do this here instead of when setting DISCOVERY_STOPPED
+                * since the latter would potentially require waiting for
+                * inquiry to stop too.
+                */
+               if (has_pending_adv_report(hdev)) {
+                       struct discovery_state *d = &hdev->discovery;
+
+                       mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK,
+                                         d->last_adv_addr_type, NULL,
+                                         d->last_adv_rssi, 0, 1,
+                                         d->last_adv_data,
+                                         d->last_adv_data_len, NULL, 0);
+               }
+
                /* Cancel this timer so that we don't try to disable scanning
                 * when it's already disabled.
                 */
@@ -1187,6 +1245,59 @@ static void hci_cc_write_remote_amp_assoc(struct hci_dev *hdev,
        amp_write_rem_assoc_continue(hdev, rp->phy_handle);
 }
 
+static void hci_cc_read_rssi(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_rp_read_rssi *rp = (void *) skb->data;
+       struct hci_conn *conn;
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+       if (rp->status)
+               return;
+
+       hci_dev_lock(hdev);
+
+       conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle));
+       if (conn)
+               conn->rssi = rp->rssi;
+
+       hci_dev_unlock(hdev);
+}
+
+static void hci_cc_read_tx_power(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_cp_read_tx_power *sent;
+       struct hci_rp_read_tx_power *rp = (void *) skb->data;
+       struct hci_conn *conn;
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+       if (rp->status)
+               return;
+
+       sent = hci_sent_cmd_data(hdev, HCI_OP_READ_TX_POWER);
+       if (!sent)
+               return;
+
+       hci_dev_lock(hdev);
+
+       conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle));
+       if (!conn)
+               goto unlock;
+
+       switch (sent->type) {
+       case 0x00:
+               conn->tx_power = rp->tx_power;
+               break;
+       case 0x01:
+               conn->max_tx_power = rp->tx_power;
+               break;
+       }
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
 static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
 {
        BT_DBG("%s status 0x%2.2x", hdev->name, status);
@@ -1342,6 +1453,7 @@ static int hci_outgoing_auth_needed(struct hci_dev *hdev,
         * is requested.
         */
        if (!hci_conn_ssp_enabled(conn) && !(conn->auth_type & 0x01) &&
+           conn->pending_sec_level != BT_SECURITY_FIPS &&
            conn->pending_sec_level != BT_SECURITY_HIGH &&
            conn->pending_sec_level != BT_SECURITY_MEDIUM)
                return 0;
@@ -1827,7 +1939,7 @@ static void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
                name_known = hci_inquiry_cache_update(hdev, &data, false, &ssp);
                mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
                                  info->dev_class, 0, !name_known, ssp, NULL,
-                                 0);
+                                 0, NULL, 0);
        }
 
        hci_dev_unlock(hdev);
@@ -2579,6 +2691,14 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cc_write_remote_amp_assoc(hdev, skb);
                break;
 
+       case HCI_OP_READ_RSSI:
+               hci_cc_read_rssi(hdev, skb);
+               break;
+
+       case HCI_OP_READ_TX_POWER:
+               hci_cc_read_tx_power(hdev, skb);
+               break;
+
        default:
                BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
                break;
@@ -2957,7 +3077,8 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
                }
 
                if (key->type == HCI_LK_COMBINATION && key->pin_len < 16 &&
-                   conn->pending_sec_level == BT_SECURITY_HIGH) {
+                   (conn->pending_sec_level == BT_SECURITY_HIGH ||
+                    conn->pending_sec_level == BT_SECURITY_FIPS)) {
                        BT_DBG("%s ignoring key unauthenticated for high security",
                               hdev->name);
                        goto not_found;
@@ -3102,7 +3223,7 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev,
                                                              false, &ssp);
                        mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
                                          info->dev_class, info->rssi,
-                                         !name_known, ssp, NULL, 0);
+                                         !name_known, ssp, NULL, 0, NULL, 0);
                }
        } else {
                struct inquiry_info_with_rssi *info = (void *) (skb->data + 1);
@@ -3120,7 +3241,7 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev,
                                                              false, &ssp);
                        mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
                                          info->dev_class, info->rssi,
-                                         !name_known, ssp, NULL, 0);
+                                         !name_known, ssp, NULL, 0, NULL, 0);
                }
        }
 
@@ -3309,7 +3430,7 @@ static void hci_extended_inquiry_result_evt(struct hci_dev *hdev,
                eir_len = eir_get_length(info->data, sizeof(info->data));
                mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
                                  info->dev_class, info->rssi, !name_known,
-                                 ssp, info->data, eir_len);
+                                 ssp, info->data, eir_len, NULL, 0);
        }
 
        hci_dev_unlock(hdev);
@@ -3367,24 +3488,20 @@ static void hci_key_refresh_complete_evt(struct hci_dev *hdev,
 
 static u8 hci_get_auth_req(struct hci_conn *conn)
 {
-       /* If remote requests dedicated bonding follow that lead */
-       if (conn->remote_auth == HCI_AT_DEDICATED_BONDING ||
-           conn->remote_auth == HCI_AT_DEDICATED_BONDING_MITM) {
-               /* If both remote and local IO capabilities allow MITM
-                * protection then require it, otherwise don't */
-               if (conn->remote_cap == HCI_IO_NO_INPUT_OUTPUT ||
-                   conn->io_capability == HCI_IO_NO_INPUT_OUTPUT)
-                       return HCI_AT_DEDICATED_BONDING;
-               else
-                       return HCI_AT_DEDICATED_BONDING_MITM;
-       }
-
        /* If remote requests no-bonding follow that lead */
        if (conn->remote_auth == HCI_AT_NO_BONDING ||
            conn->remote_auth == HCI_AT_NO_BONDING_MITM)
                return conn->remote_auth | (conn->auth_type & 0x01);
 
-       return conn->auth_type;
+       /* If both remote and local have enough IO capabilities, require
+        * MITM protection
+        */
+       if (conn->remote_cap != HCI_IO_NO_INPUT_OUTPUT &&
+           conn->io_capability != HCI_IO_NO_INPUT_OUTPUT)
+               return conn->remote_auth | 0x01;
+
+       /* No MITM protection possible so ignore remote requirement */
+       return (conn->remote_auth & ~0x01) | (conn->auth_type & 0x01);
 }
 
 static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
@@ -3414,8 +3531,21 @@ static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
                 * to DisplayYesNo as it is not supported by BT spec. */
                cp.capability = (conn->io_capability == 0x04) ?
                                HCI_IO_DISPLAY_YESNO : conn->io_capability;
-               conn->auth_type = hci_get_auth_req(conn);
-               cp.authentication = conn->auth_type;
+
+               /* If we are initiators, there is no remote information yet */
+               if (conn->remote_auth == 0xff) {
+                       cp.authentication = conn->auth_type;
+
+                       /* Request MITM protection if our IO caps allow it
+                        * except for the no-bonding case
+                        */
+                       if (conn->io_capability != HCI_IO_NO_INPUT_OUTPUT &&
+                           cp.authentication != HCI_AT_NO_BONDING)
+                               cp.authentication |= 0x01;
+               } else {
+                       conn->auth_type = hci_get_auth_req(conn);
+                       cp.authentication = conn->auth_type;
+               }
 
                if (hci_find_remote_oob_data(hdev, &conn->dst) &&
                    (conn->out || test_bit(HCI_CONN_REMOTE_OOB, &conn->flags)))
@@ -3483,12 +3613,9 @@ static void hci_user_confirm_request_evt(struct hci_dev *hdev,
        rem_mitm = (conn->remote_auth & 0x01);
 
        /* If we require MITM but the remote device can't provide that
-        * (it has NoInputNoOutput) then reject the confirmation
-        * request. The only exception is when we're dedicated bonding
-        * initiators (connect_cfm_cb set) since then we always have the MITM
-        * bit set. */
-       if (!conn->connect_cfm_cb && loc_mitm &&
-           conn->remote_cap == HCI_IO_NO_INPUT_OUTPUT) {
+        * (it has NoInputNoOutput) then reject the confirmation request
+        */
+       if (loc_mitm && conn->remote_cap == HCI_IO_NO_INPUT_OUTPUT) {
                BT_DBG("Rejecting request: remote device can't provide MITM");
                hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_NEG_REPLY,
                             sizeof(ev->bdaddr), &ev->bdaddr);
@@ -3846,17 +3973,6 @@ 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;
@@ -3881,27 +3997,24 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                                                          &conn->init_addr,
                                                          &conn->init_addr_type);
                        }
-               } else {
-                       /* Set the responder (our side) address type based on
-                        * the advertising address type.
-                        */
-                       conn->resp_addr_type = hdev->adv_addr_type;
-                       if (hdev->adv_addr_type == ADDR_LE_DEV_RANDOM)
-                               bacpy(&conn->resp_addr, &hdev->random_addr);
-                       else
-                               bacpy(&conn->resp_addr, &hdev->bdaddr);
-
-                       conn->init_addr_type = ev->bdaddr_type;
-                       bacpy(&conn->init_addr, &ev->bdaddr);
                }
        } else {
                cancel_delayed_work(&conn->le_conn_timeout);
        }
 
-       /* Ensure that the hci_conn contains the identity address type
-        * regardless of which address the connection was made with.
-        */
-       hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
+       if (!conn->out) {
+               /* Set the responder (our side) address type based on
+                * the advertising address type.
+                */
+               conn->resp_addr_type = hdev->adv_addr_type;
+               if (hdev->adv_addr_type == ADDR_LE_DEV_RANDOM)
+                       bacpy(&conn->resp_addr, &hdev->random_addr);
+               else
+                       bacpy(&conn->resp_addr, &hdev->bdaddr);
+
+               conn->init_addr_type = ev->bdaddr_type;
+               bacpy(&conn->init_addr, &ev->bdaddr);
+       }
 
        /* Lookup the identity address from the stored connection
         * address and address type.
@@ -3981,25 +4094,97 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
        }
 }
 
+static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
+                              u8 bdaddr_type, s8 rssi, u8 *data, u8 len)
+{
+       struct discovery_state *d = &hdev->discovery;
+       bool match;
+
+       /* Passive scanning shouldn't trigger any device found events */
+       if (hdev->le_scan_type == LE_SCAN_PASSIVE) {
+               if (type == LE_ADV_IND || type == LE_ADV_DIRECT_IND)
+                       check_pending_le_conn(hdev, bdaddr, bdaddr_type);
+               return;
+       }
+
+       /* If there's nothing pending either store the data from this
+        * event or send an immediate device found event if the data
+        * should not be stored for later.
+        */
+       if (!has_pending_adv_report(hdev)) {
+               /* If the report will trigger a SCAN_REQ store it for
+                * later merging.
+                */
+               if (type == LE_ADV_IND || type == LE_ADV_SCAN_IND) {
+                       store_pending_adv_report(hdev, bdaddr, bdaddr_type,
+                                                rssi, data, len);
+                       return;
+               }
+
+               mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL,
+                                 rssi, 0, 1, data, len, NULL, 0);
+               return;
+       }
+
+       /* Check if the pending report is for the same device as the new one */
+       match = (!bacmp(bdaddr, &d->last_adv_addr) &&
+                bdaddr_type == d->last_adv_addr_type);
+
+       /* If the pending data doesn't match this report or this isn't a
+        * scan response (e.g. we got a duplicate ADV_IND) then force
+        * sending of the pending data.
+        */
+       if (type != LE_ADV_SCAN_RSP || !match) {
+               /* Send out whatever is in the cache, but skip duplicates */
+               if (!match)
+                       mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK,
+                                         d->last_adv_addr_type, NULL,
+                                         d->last_adv_rssi, 0, 1,
+                                         d->last_adv_data,
+                                         d->last_adv_data_len, NULL, 0);
+
+               /* If the new report will trigger a SCAN_REQ store it for
+                * later merging.
+                */
+               if (type == LE_ADV_IND || type == LE_ADV_SCAN_IND) {
+                       store_pending_adv_report(hdev, bdaddr, bdaddr_type,
+                                                rssi, data, len);
+                       return;
+               }
+
+               /* The advertising reports cannot be merged, so clear
+                * the pending report and send out a device found event.
+                */
+               clear_pending_adv_report(hdev);
+               mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL,
+                                 rssi, 0, 1, data, len, NULL, 0);
+               return;
+       }
+
+       /* If we get here we've got a pending ADV_IND or ADV_SCAN_IND and
+        * the new event is a SCAN_RSP. We can therefore proceed with
+        * sending a merged device found event.
+        */
+       mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK,
+                         d->last_adv_addr_type, NULL, rssi, 0, 1, data, len,
+                         d->last_adv_data, d->last_adv_data_len);
+       clear_pending_adv_report(hdev);
+}
+
 static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        u8 num_reports = skb->data[0];
        void *ptr = &skb->data[1];
-       s8 rssi;
 
        hci_dev_lock(hdev);
 
        while (num_reports--) {
                struct hci_ev_le_advertising_info *ev = ptr;
-
-               if (ev->evt_type == LE_ADV_IND ||
-                   ev->evt_type == LE_ADV_DIRECT_IND)
-                       check_pending_le_conn(hdev, &ev->bdaddr,
-                                             ev->bdaddr_type);
+               s8 rssi;
 
                rssi = ev->data[ev->length];
-               mgmt_device_found(hdev, &ev->bdaddr, LE_LINK, ev->bdaddr_type,
-                                 NULL, rssi, 0, 1, ev->data, ev->length);
+               process_adv_report(hdev, ev->evt_type, &ev->bdaddr,
+                                  ev->bdaddr_type, rssi, ev->data, ev->length);
 
                ptr += sizeof(*ev) + ev->length + 1;
        }
index b9a418e578e0000ec4ad68734b3e34575cd759b5..80d25c150a653b57977c5b56c227908e20f4fa32 100644 (file)
@@ -143,7 +143,7 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
 
                if (!skb_copy) {
                        /* Create a private copy with headroom */
-                       skb_copy = __pskb_copy(skb, 1, GFP_ATOMIC);
+                       skb_copy = __pskb_copy_fclone(skb, 1, GFP_ATOMIC, true);
                        if (!skb_copy)
                                continue;
 
@@ -247,8 +247,8 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
                        struct hci_mon_hdr *hdr;
 
                        /* Create a private copy with headroom */
-                       skb_copy = __pskb_copy(skb, HCI_MON_HDR_SIZE,
-                                              GFP_ATOMIC);
+                       skb_copy = __pskb_copy_fclone(skb, HCI_MON_HDR_SIZE,
+                                                     GFP_ATOMIC, true);
                        if (!skb_copy)
                                continue;
 
@@ -524,16 +524,7 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
        case HCISETRAW:
                if (!capable(CAP_NET_ADMIN))
                        return -EPERM;
-
-               if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
-                       return -EPERM;
-
-               if (arg)
-                       set_bit(HCI_RAW, &hdev->flags);
-               else
-                       clear_bit(HCI_RAW, &hdev->flags);
-
-               return 0;
+               return -EOPNOTSUPP;
 
        case HCIGETCONNINFO:
                return hci_get_conn_info(hdev, (void __user *) arg);
index dc4d301d3a728ccd0af8efe4e9648b843d598406..6eabbe05fe54fe8ecc39707a05350439d99ce830 100644 (file)
@@ -471,8 +471,14 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan)
        chan->max_tx = L2CAP_DEFAULT_MAX_TX;
        chan->tx_win = L2CAP_DEFAULT_TX_WINDOW;
        chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW;
+       chan->remote_max_tx = chan->max_tx;
+       chan->remote_tx_win = chan->tx_win;
        chan->ack_win = L2CAP_DEFAULT_TX_WINDOW;
        chan->sec_level = BT_SECURITY_LOW;
+       chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
+       chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
+       chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
+       chan->conf_state = 0;
 
        set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
 }
index ef5e5b04f34fbd3c130c74dc2a41ffe775870b3b..ade3fb4c23bce81aa054e2bdd064f74019767dae 100644 (file)
@@ -1180,13 +1180,16 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
        /* Check for backlog size */
        if (sk_acceptq_is_full(parent)) {
                BT_DBG("backlog full %d", parent->sk_ack_backlog);
+               release_sock(parent);
                return NULL;
        }
 
        sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP,
                              GFP_ATOMIC);
-       if (!sk)
+       if (!sk) {
+               release_sock(parent);
                return NULL;
+        }
 
        bt_sock_reclassify_lock(sk, BTPROTO_L2CAP);
 
index b3fbc73516c415ee1654eb6f2aa38d5e390fb00b..941ad7530eda48f21dcf7faef9167cbce6574a8e 100644 (file)
@@ -58,6 +58,7 @@ int bt_to_errno(__u16 code)
                return EIO;
 
        case 0x04:
+       case 0x3c:
                return EHOSTDOWN;
 
        case 0x05:
index d2d4e0d5aed017366668bf263538baf332255d20..0fce54412ffdc077f6d337eadc4cfbe20b51ed26 100644 (file)
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
 #include <net/bluetooth/mgmt.h>
 
 #include "smp.h"
 
 #define MGMT_VERSION   1
-#define MGMT_REVISION  5
+#define MGMT_REVISION  6
 
 static const u16 mgmt_commands[] = {
        MGMT_OP_READ_INDEX_LIST,
@@ -83,6 +84,7 @@ static const u16 mgmt_commands[] = {
        MGMT_OP_SET_DEBUG_KEYS,
        MGMT_OP_SET_PRIVACY,
        MGMT_OP_LOAD_IRKS,
+       MGMT_OP_GET_CONN_INFO,
 };
 
 static const u16 mgmt_events[] = {
@@ -2850,10 +2852,7 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
        }
 
        sec_level = BT_SECURITY_MEDIUM;
-       if (cp->io_cap == 0x03)
-               auth_type = HCI_AT_DEDICATED_BONDING;
-       else
-               auth_type = HCI_AT_DEDICATED_BONDING_MITM;
+       auth_type = HCI_AT_DEDICATED_BONDING;
 
        if (cp->addr.type == BDADDR_BREDR) {
                conn = hci_connect_acl(hdev, &cp->addr.bdaddr, sec_level,
@@ -3351,6 +3350,8 @@ static int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status)
 
 static void start_discovery_complete(struct hci_dev *hdev, u8 status)
 {
+       unsigned long timeout = 0;
+
        BT_DBG("status %d", status);
 
        if (status) {
@@ -3366,13 +3367,11 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status)
 
        switch (hdev->discovery.type) {
        case DISCOV_TYPE_LE:
-               queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable,
-                                  DISCOV_LE_TIMEOUT);
+               timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
                break;
 
        case DISCOV_TYPE_INTERLEAVED:
-               queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable,
-                                  DISCOV_INTERLEAVED_TIMEOUT);
+               timeout = msecs_to_jiffies(hdev->discov_interleaved_timeout);
                break;
 
        case DISCOV_TYPE_BREDR:
@@ -3381,6 +3380,11 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status)
        default:
                BT_ERR("Invalid discovery type %d", hdev->discovery.type);
        }
+
+       if (!timeout)
+               return;
+
+       queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable, timeout);
 }
 
 static int start_discovery(struct sock *sk, struct hci_dev *hdev,
@@ -4530,7 +4534,7 @@ 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, addr_type;
+               u8 type, addr_type, authenticated;
 
                if (key->addr.type == BDADDR_LE_PUBLIC)
                        addr_type = ADDR_LE_DEV_PUBLIC;
@@ -4542,8 +4546,19 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
                else
                        type = HCI_SMP_LTK_SLAVE;
 
+               switch (key->type) {
+               case MGMT_LTK_UNAUTHENTICATED:
+                       authenticated = 0x00;
+                       break;
+               case MGMT_LTK_AUTHENTICATED:
+                       authenticated = 0x01;
+                       break;
+               default:
+                       continue;
+               }
+
                hci_add_ltk(hdev, &key->addr.bdaddr, addr_type, type,
-                           key->type, key->val, key->enc_size, key->ediv,
+                           authenticated, key->val, key->enc_size, key->ediv,
                            key->rand);
        }
 
@@ -4555,6 +4570,218 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
        return err;
 }
 
+struct cmd_conn_lookup {
+       struct hci_conn *conn;
+       bool valid_tx_power;
+       u8 mgmt_status;
+};
+
+static void get_conn_info_complete(struct pending_cmd *cmd, void *data)
+{
+       struct cmd_conn_lookup *match = data;
+       struct mgmt_cp_get_conn_info *cp;
+       struct mgmt_rp_get_conn_info rp;
+       struct hci_conn *conn = cmd->user_data;
+
+       if (conn != match->conn)
+               return;
+
+       cp = (struct mgmt_cp_get_conn_info *) cmd->param;
+
+       memset(&rp, 0, sizeof(rp));
+       bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
+       rp.addr.type = cp->addr.type;
+
+       if (!match->mgmt_status) {
+               rp.rssi = conn->rssi;
+
+               if (match->valid_tx_power) {
+                       rp.tx_power = conn->tx_power;
+                       rp.max_tx_power = conn->max_tx_power;
+               } else {
+                       rp.tx_power = HCI_TX_POWER_INVALID;
+                       rp.max_tx_power = HCI_TX_POWER_INVALID;
+               }
+       }
+
+       cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO,
+                    match->mgmt_status, &rp, sizeof(rp));
+
+       hci_conn_drop(conn);
+
+       mgmt_pending_remove(cmd);
+}
+
+static void conn_info_refresh_complete(struct hci_dev *hdev, u8 status)
+{
+       struct hci_cp_read_rssi *cp;
+       struct hci_conn *conn;
+       struct cmd_conn_lookup match;
+       u16 handle;
+
+       BT_DBG("status 0x%02x", status);
+
+       hci_dev_lock(hdev);
+
+       /* TX power data is valid in case request completed successfully,
+        * otherwise we assume it's not valid. At the moment we assume that
+        * either both or none of current and max values are valid to keep code
+        * simple.
+        */
+       match.valid_tx_power = !status;
+
+       /* Commands sent in request are either Read RSSI or Read Transmit Power
+        * Level so we check which one was last sent to retrieve connection
+        * handle.  Both commands have handle as first parameter so it's safe to
+        * cast data on the same command struct.
+        *
+        * First command sent is always Read RSSI and we fail only if it fails.
+        * In other case we simply override error to indicate success as we
+        * already remembered if TX power value is actually valid.
+        */
+       cp = hci_sent_cmd_data(hdev, HCI_OP_READ_RSSI);
+       if (!cp) {
+               cp = hci_sent_cmd_data(hdev, HCI_OP_READ_TX_POWER);
+               status = 0;
+       }
+
+       if (!cp) {
+               BT_ERR("invalid sent_cmd in response");
+               goto unlock;
+       }
+
+       handle = __le16_to_cpu(cp->handle);
+       conn = hci_conn_hash_lookup_handle(hdev, handle);
+       if (!conn) {
+               BT_ERR("unknown handle (%d) in response", handle);
+               goto unlock;
+       }
+
+       match.conn = conn;
+       match.mgmt_status = mgmt_status(status);
+
+       /* Cache refresh is complete, now reply for mgmt request for given
+        * connection only.
+        */
+       mgmt_pending_foreach(MGMT_OP_GET_CONN_INFO, hdev,
+                            get_conn_info_complete, &match);
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
+static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
+                        u16 len)
+{
+       struct mgmt_cp_get_conn_info *cp = data;
+       struct mgmt_rp_get_conn_info rp;
+       struct hci_conn *conn;
+       unsigned long conn_info_age;
+       int err = 0;
+
+       BT_DBG("%s", hdev->name);
+
+       memset(&rp, 0, sizeof(rp));
+       bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
+       rp.addr.type = cp->addr.type;
+
+       if (!bdaddr_type_is_valid(cp->addr.type))
+               return cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
+                                   MGMT_STATUS_INVALID_PARAMS,
+                                   &rp, sizeof(rp));
+
+       hci_dev_lock(hdev);
+
+       if (!hdev_is_powered(hdev)) {
+               err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
+                                  MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp));
+               goto unlock;
+       }
+
+       if (cp->addr.type == BDADDR_BREDR)
+               conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
+                                              &cp->addr.bdaddr);
+       else
+               conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
+
+       if (!conn || conn->state != BT_CONNECTED) {
+               err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
+                                  MGMT_STATUS_NOT_CONNECTED, &rp, sizeof(rp));
+               goto unlock;
+       }
+
+       /* To avoid client trying to guess when to poll again for information we
+        * calculate conn info age as random value between min/max set in hdev.
+        */
+       conn_info_age = hdev->conn_info_min_age +
+                       prandom_u32_max(hdev->conn_info_max_age -
+                                       hdev->conn_info_min_age);
+
+       /* Query controller to refresh cached values if they are too old or were
+        * never read.
+        */
+       if (time_after(jiffies, conn->conn_info_timestamp +
+                      msecs_to_jiffies(conn_info_age)) ||
+           !conn->conn_info_timestamp) {
+               struct hci_request req;
+               struct hci_cp_read_tx_power req_txp_cp;
+               struct hci_cp_read_rssi req_rssi_cp;
+               struct pending_cmd *cmd;
+
+               hci_req_init(&req, hdev);
+               req_rssi_cp.handle = cpu_to_le16(conn->handle);
+               hci_req_add(&req, HCI_OP_READ_RSSI, sizeof(req_rssi_cp),
+                           &req_rssi_cp);
+
+               /* For LE links TX power does not change thus we don't need to
+                * query for it once value is known.
+                */
+               if (!bdaddr_type_is_le(cp->addr.type) ||
+                   conn->tx_power == HCI_TX_POWER_INVALID) {
+                       req_txp_cp.handle = cpu_to_le16(conn->handle);
+                       req_txp_cp.type = 0x00;
+                       hci_req_add(&req, HCI_OP_READ_TX_POWER,
+                                   sizeof(req_txp_cp), &req_txp_cp);
+               }
+
+               /* Max TX power needs to be read only once per connection */
+               if (conn->max_tx_power == HCI_TX_POWER_INVALID) {
+                       req_txp_cp.handle = cpu_to_le16(conn->handle);
+                       req_txp_cp.type = 0x01;
+                       hci_req_add(&req, HCI_OP_READ_TX_POWER,
+                                   sizeof(req_txp_cp), &req_txp_cp);
+               }
+
+               err = hci_req_run(&req, conn_info_refresh_complete);
+               if (err < 0)
+                       goto unlock;
+
+               cmd = mgmt_pending_add(sk, MGMT_OP_GET_CONN_INFO, hdev,
+                                      data, len);
+               if (!cmd) {
+                       err = -ENOMEM;
+                       goto unlock;
+               }
+
+               hci_conn_hold(conn);
+               cmd->user_data = conn;
+
+               conn->conn_info_timestamp = jiffies;
+       } else {
+               /* Cache is valid, just reply with values cached in hci_conn */
+               rp.rssi = conn->rssi;
+               rp.tx_power = conn->tx_power;
+               rp.max_tx_power = conn->max_tx_power;
+
+               err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
+                                  MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
+       }
+
+unlock:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
 static const struct mgmt_handler {
        int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
                     u16 data_len);
@@ -4610,6 +4837,7 @@ static const struct mgmt_handler {
        { set_debug_keys,         false, MGMT_SETTING_SIZE },
        { set_privacy,            false, MGMT_SET_PRIVACY_SIZE },
        { load_irks,              true,  MGMT_LOAD_IRKS_SIZE },
+       { get_conn_info,          false, MGMT_GET_CONN_INFO_SIZE },
 };
 
 
@@ -5005,6 +5233,14 @@ void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
        mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
 }
 
+static u8 mgmt_ltk_type(struct smp_ltk *ltk)
+{
+       if (ltk->authenticated)
+               return MGMT_LTK_AUTHENTICATED;
+
+       return MGMT_LTK_UNAUTHENTICATED;
+}
+
 void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent)
 {
        struct mgmt_ev_new_long_term_key ev;
@@ -5030,7 +5266,7 @@ void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent)
 
        bacpy(&ev.key.addr.bdaddr, &key->bdaddr);
        ev.key.addr.type = link_to_bdaddr(LE_LINK, key->bdaddr_type);
-       ev.key.type = key->authenticated;
+       ev.key.type = mgmt_ltk_type(key);
        ev.key.enc_size = key->enc_size;
        ev.key.ediv = key->ediv;
        ev.key.rand = key->rand;
@@ -5668,8 +5904,9 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
 }
 
 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)
+                      u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name,
+                      u8 ssp, u8 *eir, u16 eir_len, u8 *scan_rsp,
+                      u8 scan_rsp_len)
 {
        char buf[512];
        struct mgmt_ev_device_found *ev = (void *) buf;
@@ -5679,8 +5916,10 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
        if (!hci_discovery_active(hdev))
                return;
 
-       /* Leave 5 bytes for a potential CoD field */
-       if (sizeof(*ev) + eir_len + 5 > sizeof(buf))
+       /* Make sure that the buffer is big enough. The 5 extra bytes
+        * are for the potential CoD field.
+        */
+       if (sizeof(*ev) + eir_len + scan_rsp_len + 5 > sizeof(buf))
                return;
 
        memset(buf, 0, sizeof(buf));
@@ -5707,8 +5946,11 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV,
                                          dev_class, 3);
 
-       ev->eir_len = cpu_to_le16(eir_len);
-       ev_size = sizeof(*ev) + eir_len;
+       if (scan_rsp_len > 0)
+               memcpy(ev->eir + eir_len, scan_rsp, scan_rsp_len);
+
+       ev->eir_len = cpu_to_le16(eir_len + scan_rsp_len);
+       ev_size = sizeof(*ev) + eir_len + scan_rsp_len;
 
        mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL);
 }
index cf620260affaea75c4c1e9875ca338afb04f7b2e..754b6fe4f742af8ce4662a25065c6caba242295b 100644 (file)
@@ -307,7 +307,7 @@ struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio)
        setup_timer(&d->timer, rfcomm_dlc_timeout, (unsigned long)d);
 
        skb_queue_head_init(&d->tx_queue);
-       spin_lock_init(&d->lock);
+       mutex_init(&d->lock);
        atomic_set(&d->refcnt, 1);
 
        rfcomm_dlc_clear_state(d);
index 403ec09f480a2a72983b0b1b9db0222fd6e86742..8e385a0ae60e0bd6e4b1ed7615905a7170c4a8bc 100644 (file)
@@ -70,7 +70,7 @@ struct rfcomm_dev {
 };
 
 static LIST_HEAD(rfcomm_dev_list);
-static DEFINE_SPINLOCK(rfcomm_dev_lock);
+static DEFINE_MUTEX(rfcomm_dev_lock);
 
 static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb);
 static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err);
@@ -96,9 +96,9 @@ static void rfcomm_dev_destruct(struct tty_port *port)
        if (dev->tty_dev)
                tty_unregister_device(rfcomm_tty_driver, dev->id);
 
-       spin_lock(&rfcomm_dev_lock);
+       mutex_lock(&rfcomm_dev_lock);
        list_del(&dev->list);
-       spin_unlock(&rfcomm_dev_lock);
+       mutex_unlock(&rfcomm_dev_lock);
 
        kfree(dev);
 
@@ -161,14 +161,14 @@ static struct rfcomm_dev *rfcomm_dev_get(int id)
 {
        struct rfcomm_dev *dev;
 
-       spin_lock(&rfcomm_dev_lock);
+       mutex_lock(&rfcomm_dev_lock);
 
        dev = __rfcomm_dev_lookup(id);
 
        if (dev && !tty_port_get(&dev->port))
                dev = NULL;
 
-       spin_unlock(&rfcomm_dev_lock);
+       mutex_unlock(&rfcomm_dev_lock);
 
        return dev;
 }
@@ -224,7 +224,7 @@ static struct rfcomm_dev *__rfcomm_dev_add(struct rfcomm_dev_req *req,
        if (!dev)
                return ERR_PTR(-ENOMEM);
 
-       spin_lock(&rfcomm_dev_lock);
+       mutex_lock(&rfcomm_dev_lock);
 
        if (req->dev_id < 0) {
                dev->id = 0;
@@ -305,11 +305,11 @@ static struct rfcomm_dev *__rfcomm_dev_add(struct rfcomm_dev_req *req,
           holds reference to this module. */
        __module_get(THIS_MODULE);
 
-       spin_unlock(&rfcomm_dev_lock);
+       mutex_unlock(&rfcomm_dev_lock);
        return dev;
 
 out:
-       spin_unlock(&rfcomm_dev_lock);
+       mutex_unlock(&rfcomm_dev_lock);
        kfree(dev);
        return ERR_PTR(err);
 }
@@ -524,7 +524,7 @@ static int rfcomm_get_dev_list(void __user *arg)
 
        di = dl->dev_info;
 
-       spin_lock(&rfcomm_dev_lock);
+       mutex_lock(&rfcomm_dev_lock);
 
        list_for_each_entry(dev, &rfcomm_dev_list, list) {
                if (!tty_port_get(&dev->port))
@@ -540,7 +540,7 @@ static int rfcomm_get_dev_list(void __user *arg)
                        break;
        }
 
-       spin_unlock(&rfcomm_dev_lock);
+       mutex_unlock(&rfcomm_dev_lock);
 
        dl->dev_num = n;
        size = sizeof(*dl) + n * sizeof(*di);
index dfb4e1161c10fbb62b6ac43220949992a5075dfc..3d1cc164557de1750e4768f229aaede55b03e80c 100644 (file)
 
 #define AUTH_REQ_MASK   0x07
 
+#define SMP_FLAG_TK_VALID      1
+#define SMP_FLAG_CFM_PENDING   2
+#define SMP_FLAG_MITM_AUTH     3
+#define SMP_FLAG_COMPLETE      4
+#define SMP_FLAG_INITIATOR     5
+
+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;
+       u8              remote_key_dist;
+       bdaddr_t        id_addr;
+       u8              id_addr_type;
+       u8              irk[16];
+       struct smp_csrk *csrk;
+       struct smp_csrk *slave_csrk;
+       struct smp_ltk  *ltk;
+       struct smp_ltk  *slave_ltk;
+       struct smp_irk  *remote_irk;
+       unsigned long   flags;
+};
+
 static inline void swap128(const u8 src[16], u8 dst[16])
 {
        int i;
@@ -369,7 +396,7 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
 
        /* Initialize key for JUST WORKS */
        memset(smp->tk, 0, sizeof(smp->tk));
-       clear_bit(SMP_FLAG_TK_VALID, &smp->smp_flags);
+       clear_bit(SMP_FLAG_TK_VALID, &smp->flags);
 
        BT_DBG("tk_request: auth:%d lcl:%d rem:%d", auth, local_io, remote_io);
 
@@ -388,19 +415,18 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
                method = JUST_WORKS;
 
        /* Don't confirm locally initiated pairing attempts */
-       if (method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR,
-                                          &smp->smp_flags))
+       if (method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR, &smp->flags))
                method = JUST_WORKS;
 
        /* If Just Works, Continue with Zero TK */
        if (method == JUST_WORKS) {
-               set_bit(SMP_FLAG_TK_VALID, &smp->smp_flags);
+               set_bit(SMP_FLAG_TK_VALID, &smp->flags);
                return 0;
        }
 
        /* Not Just Works/Confirm results in MITM Authentication */
        if (method != JUST_CFM)
-               set_bit(SMP_FLAG_MITM_AUTH, &smp->smp_flags);
+               set_bit(SMP_FLAG_MITM_AUTH, &smp->flags);
 
        /* If both devices have Keyoard-Display I/O, the master
         * Confirms and the slave Enters the passkey.
@@ -419,7 +445,7 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
                passkey %= 1000000;
                put_unaligned_le32(passkey, smp->tk);
                BT_DBG("PassKey: %d", passkey);
-               set_bit(SMP_FLAG_TK_VALID, &smp->smp_flags);
+               set_bit(SMP_FLAG_TK_VALID, &smp->flags);
        }
 
        hci_dev_lock(hcon->hdev);
@@ -441,15 +467,13 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
        return ret;
 }
 
-static void confirm_work(struct work_struct *work)
+static u8 smp_confirm(struct smp_chan *smp)
 {
-       struct smp_chan *smp = container_of(work, struct smp_chan, confirm);
        struct l2cap_conn *conn = smp->conn;
        struct hci_dev *hdev = conn->hcon->hdev;
        struct crypto_blkcipher *tfm = hdev->tfm_aes;
        struct smp_cmd_pairing_confirm cp;
        int ret;
-       u8 reason;
 
        BT_DBG("conn %p", conn);
 
@@ -463,35 +487,27 @@ static void confirm_work(struct work_struct *work)
 
        hci_dev_unlock(hdev);
 
-       if (ret) {
-               reason = SMP_UNSPECIFIED;
-               goto error;
-       }
+       if (ret)
+               return SMP_UNSPECIFIED;
 
-       clear_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags);
+       clear_bit(SMP_FLAG_CFM_PENDING, &smp->flags);
 
        smp_send_cmd(smp->conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
 
-       return;
-
-error:
-       smp_failure(conn, reason);
+       return 0;
 }
 
-static void random_work(struct work_struct *work)
+static u8 smp_random(struct smp_chan *smp)
 {
-       struct smp_chan *smp = container_of(work, struct smp_chan, random);
        struct l2cap_conn *conn = smp->conn;
        struct hci_conn *hcon = conn->hcon;
        struct hci_dev *hdev = hcon->hdev;
        struct crypto_blkcipher *tfm = hdev->tfm_aes;
-       u8 reason, confirm[16];
+       u8 confirm[16];
        int ret;
 
-       if (IS_ERR_OR_NULL(tfm)) {
-               reason = SMP_UNSPECIFIED;
-               goto error;
-       }
+       if (IS_ERR_OR_NULL(tfm))
+               return SMP_UNSPECIFIED;
 
        BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
 
@@ -504,15 +520,12 @@ static void random_work(struct work_struct *work)
 
        hci_dev_unlock(hdev);
 
-       if (ret) {
-               reason = SMP_UNSPECIFIED;
-               goto error;
-       }
+       if (ret)
+               return SMP_UNSPECIFIED;
 
        if (memcmp(smp->pcnf, confirm, sizeof(smp->pcnf)) != 0) {
                BT_ERR("Pairing failed (confirmation values mismatch)");
-               reason = SMP_CONFIRM_FAILED;
-               goto error;
+               return SMP_CONFIRM_FAILED;
        }
 
        if (hcon->out) {
@@ -525,10 +538,8 @@ static void random_work(struct work_struct *work)
                memset(stk + smp->enc_key_size, 0,
                       SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
 
-               if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags)) {
-                       reason = SMP_UNSPECIFIED;
-                       goto error;
-               }
+               if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags))
+                       return SMP_UNSPECIFIED;
 
                hci_le_start_enc(hcon, ediv, rand, stk);
                hcon->enc_key_size = smp->enc_key_size;
@@ -550,10 +561,7 @@ static void random_work(struct work_struct *work)
                            ediv, rand);
        }
 
-       return;
-
-error:
-       smp_failure(conn, reason);
+       return 0;
 }
 
 static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
@@ -564,9 +572,6 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
        if (!smp)
                return NULL;
 
-       INIT_WORK(&smp->confirm, confirm_work);
-       INIT_WORK(&smp->random, random_work);
-
        smp->conn = conn;
        conn->smp_chan = smp;
        conn->hcon->smp_conn = conn;
@@ -583,7 +588,7 @@ void smp_chan_destroy(struct l2cap_conn *conn)
 
        BUG_ON(!smp);
 
-       complete = test_bit(SMP_FLAG_COMPLETE, &smp->smp_flags);
+       complete = test_bit(SMP_FLAG_COMPLETE, &smp->flags);
        mgmt_smp_complete(conn->hcon, complete);
 
        kfree(smp->csrk);
@@ -634,7 +639,7 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
                put_unaligned_le32(value, smp->tk);
                /* Fall Through */
        case MGMT_OP_USER_CONFIRM_REPLY:
-               set_bit(SMP_FLAG_TK_VALID, &smp->smp_flags);
+               set_bit(SMP_FLAG_TK_VALID, &smp->flags);
                break;
        case MGMT_OP_USER_PASSKEY_NEG_REPLY:
        case MGMT_OP_USER_CONFIRM_NEG_REPLY:
@@ -646,8 +651,11 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
        }
 
        /* If it is our turn to send Pairing Confirm, do so now */
-       if (test_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags))
-               queue_work(hcon->hdev->workqueue, &smp->confirm);
+       if (test_bit(SMP_FLAG_CFM_PENDING, &smp->flags)) {
+               u8 rsp = smp_confirm(smp);
+               if (rsp)
+                       smp_failure(conn, rsp);
+       }
 
        return 0;
 }
@@ -656,14 +664,13 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct smp_cmd_pairing rsp, *req = (void *) skb->data;
        struct smp_chan *smp;
-       u8 key_size;
-       u8 auth = SMP_AUTH_NONE;
+       u8 key_size, auth;
        int ret;
 
        BT_DBG("conn %p", conn);
 
        if (skb->len < sizeof(*req))
-               return SMP_UNSPECIFIED;
+               return SMP_INVALID_PARAMS;
 
        if (conn->hcon->link_mode & HCI_LM_MASTER)
                return SMP_CMD_NOTSUPP;
@@ -681,8 +688,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
        skb_pull(skb, sizeof(*req));
 
        /* We didn't start the pairing, so match remote */
-       if (req->auth_req & SMP_AUTH_BONDING)
-               auth = req->auth_req;
+       auth = req->auth_req;
 
        conn->hcon->pending_sec_level = authreq_to_seclevel(auth);
 
@@ -704,7 +710,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
        if (ret)
                return SMP_UNSPECIFIED;
 
-       clear_bit(SMP_FLAG_INITIATOR, &smp->smp_flags);
+       clear_bit(SMP_FLAG_INITIATOR, &smp->flags);
 
        return 0;
 }
@@ -713,14 +719,13 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct smp_cmd_pairing *req, *rsp = (void *) skb->data;
        struct smp_chan *smp = conn->smp_chan;
-       struct hci_dev *hdev = conn->hcon->hdev;
        u8 key_size, auth = SMP_AUTH_NONE;
        int ret;
 
        BT_DBG("conn %p", conn);
 
        if (skb->len < sizeof(*rsp))
-               return SMP_UNSPECIFIED;
+               return SMP_INVALID_PARAMS;
 
        if (!(conn->hcon->link_mode & HCI_LM_MASTER))
                return SMP_CMD_NOTSUPP;
@@ -753,11 +758,11 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
        if (ret)
                return SMP_UNSPECIFIED;
 
-       set_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags);
+       set_bit(SMP_FLAG_CFM_PENDING, &smp->flags);
 
        /* Can't compose response until we have been confirmed */
-       if (test_bit(SMP_FLAG_TK_VALID, &smp->smp_flags))
-               queue_work(hdev->workqueue, &smp->confirm);
+       if (test_bit(SMP_FLAG_TK_VALID, &smp->flags))
+               return smp_confirm(smp);
 
        return 0;
 }
@@ -765,12 +770,11 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
 static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct smp_chan *smp = conn->smp_chan;
-       struct hci_dev *hdev = conn->hcon->hdev;
 
        BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
 
        if (skb->len < sizeof(smp->pcnf))
-               return SMP_UNSPECIFIED;
+               return SMP_INVALID_PARAMS;
 
        memcpy(smp->pcnf, skb->data, sizeof(smp->pcnf));
        skb_pull(skb, sizeof(smp->pcnf));
@@ -778,10 +782,10 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
        if (conn->hcon->out)
                smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
                             smp->prnd);
-       else if (test_bit(SMP_FLAG_TK_VALID, &smp->smp_flags))
-               queue_work(hdev->workqueue, &smp->confirm);
+       else if (test_bit(SMP_FLAG_TK_VALID, &smp->flags))
+               return smp_confirm(smp);
        else
-               set_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags);
+               set_bit(SMP_FLAG_CFM_PENDING, &smp->flags);
 
        return 0;
 }
@@ -789,19 +793,16 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
 static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct smp_chan *smp = conn->smp_chan;
-       struct hci_dev *hdev = conn->hcon->hdev;
 
        BT_DBG("conn %p", conn);
 
        if (skb->len < sizeof(smp->rrnd))
-               return SMP_UNSPECIFIED;
+               return SMP_INVALID_PARAMS;
 
        memcpy(smp->rrnd, skb->data, sizeof(smp->rrnd));
        skb_pull(skb, sizeof(smp->rrnd));
 
-       queue_work(hdev->workqueue, &smp->random);
-
-       return 0;
+       return smp_random(smp);
 }
 
 static u8 smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
@@ -836,7 +837,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
        BT_DBG("conn %p", conn);
 
        if (skb->len < sizeof(*rp))
-               return SMP_UNSPECIFIED;
+               return SMP_INVALID_PARAMS;
 
        if (!(conn->hcon->link_mode & HCI_LM_MASTER))
                return SMP_CMD_NOTSUPP;
@@ -861,7 +862,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
 
        smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
 
-       clear_bit(SMP_FLAG_INITIATOR, &smp->smp_flags);
+       clear_bit(SMP_FLAG_INITIATOR, &smp->flags);
 
        return 0;
 }
@@ -908,10 +909,11 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
 
        authreq = seclevel_to_authreq(sec_level);
 
-       /* hcon->auth_type is set by pair_device in mgmt.c. If the MITM
-        * flag is set we should also set it for the SMP request.
+       /* Require MITM if IO Capability allows or the security level
+        * requires it.
         */
-       if ((hcon->auth_type & 0x01))
+       if (hcon->io_capability != HCI_IO_NO_INPUT_OUTPUT ||
+           sec_level > BT_SECURITY_MEDIUM)
                authreq |= SMP_AUTH_MITM;
 
        if (hcon->link_mode & HCI_LM_MASTER) {
@@ -928,7 +930,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
                smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp);
        }
 
-       set_bit(SMP_FLAG_INITIATOR, &smp->smp_flags);
+       set_bit(SMP_FLAG_INITIATOR, &smp->flags);
 
 done:
        hcon->pending_sec_level = sec_level;
@@ -944,7 +946,7 @@ static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb)
        BT_DBG("conn %p", conn);
 
        if (skb->len < sizeof(*rp))
-               return SMP_UNSPECIFIED;
+               return SMP_INVALID_PARAMS;
 
        /* Ignore this PDU if it wasn't requested */
        if (!(smp->remote_key_dist & SMP_DIST_ENC_KEY))
@@ -969,7 +971,7 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
        BT_DBG("conn %p", conn);
 
        if (skb->len < sizeof(*rp))
-               return SMP_UNSPECIFIED;
+               return SMP_INVALID_PARAMS;
 
        /* Ignore this PDU if it wasn't requested */
        if (!(smp->remote_key_dist & SMP_DIST_ENC_KEY))
@@ -1001,7 +1003,7 @@ static int smp_cmd_ident_info(struct l2cap_conn *conn, struct sk_buff *skb)
        BT_DBG("");
 
        if (skb->len < sizeof(*info))
-               return SMP_UNSPECIFIED;
+               return SMP_INVALID_PARAMS;
 
        /* Ignore this PDU if it wasn't requested */
        if (!(smp->remote_key_dist & SMP_DIST_ID_KEY))
@@ -1025,7 +1027,7 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn,
        BT_DBG("");
 
        if (skb->len < sizeof(*info))
-               return SMP_UNSPECIFIED;
+               return SMP_INVALID_PARAMS;
 
        /* Ignore this PDU if it wasn't requested */
        if (!(smp->remote_key_dist & SMP_DIST_ID_KEY))
@@ -1075,7 +1077,7 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
        BT_DBG("conn %p", conn);
 
        if (skb->len < sizeof(*rp))
-               return SMP_UNSPECIFIED;
+               return SMP_INVALID_PARAMS;
 
        /* Ignore this PDU if it wasn't requested */
        if (!(smp->remote_key_dist & SMP_DIST_SIGN))
@@ -1358,7 +1360,7 @@ int smp_distribute_keys(struct l2cap_conn *conn)
 
        clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags);
        cancel_delayed_work_sync(&conn->security_timer);
-       set_bit(SMP_FLAG_COMPLETE, &smp->smp_flags);
+       set_bit(SMP_FLAG_COMPLETE, &smp->flags);
        smp_notify_keys(conn);
 
        smp_chan_destroy(conn);
index 1277147a915070e3a5256debdd98316fa1d916c5..5a8dc36460a1b7e3a61b08d18407708673a7d1a5 100644 (file)
@@ -111,39 +111,11 @@ struct smp_cmd_security_req {
 #define SMP_CMD_NOTSUPP                        0x07
 #define SMP_UNSPECIFIED                        0x08
 #define SMP_REPEATED_ATTEMPTS          0x09
+#define SMP_INVALID_PARAMS             0x0a
 
 #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
-#define SMP_FLAG_COMPLETE      4
-#define SMP_FLAG_INITIATOR     5
-
-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;
-       u8              remote_key_dist;
-       bdaddr_t        id_addr;
-       u8              id_addr_type;
-       u8              irk[16];
-       struct smp_csrk *csrk;
-       struct smp_csrk *slave_csrk;
-       struct smp_ltk  *ltk;
-       struct smp_ltk  *slave_ltk;
-       struct smp_irk  *remote_irk;
-       unsigned long   smp_flags;
-       struct work_struct confirm;
-       struct work_struct random;
-};
-
 /* SMP Commands */
 bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level);
 int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
index e85498b2f1669f7dae3b7f5f5e1c0970b1be65c6..8590b942bffa62a11d23c4e7a0666c9564d310c1 100644 (file)
@@ -5,7 +5,7 @@
 obj-$(CONFIG_BRIDGE) += bridge.o
 
 bridge-y       := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
-                       br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+                       br_ioctl.o br_stp.o br_stp_bpdu.o \
                        br_stp_if.o br_stp_timer.o br_netlink.o
 
 bridge-$(CONFIG_SYSFS) += br_sysfs_if.o br_sysfs_br.o
@@ -16,4 +16,4 @@ bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o
 
 bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o
 
-obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/
+obj-$(CONFIG_NETFILTER) += netfilter/
index 19311aafcf5a06e6ae7abaaf82527711573dc6d6..1a755a1e54101d924e88ea240a82c154dcb7bbe5 100644 (file)
 
 #include "br_private.h"
 
+/*
+ * Handle changes in state of network devices enslaved to a bridge.
+ *
+ * Note: don't care about up/down if bridge itself is down, because
+ *     port state is checked when bridge is brought up.
+ */
+static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
+{
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+       struct net_bridge_port *p;
+       struct net_bridge *br;
+       bool changed_addr;
+       int err;
+
+       /* register of bridge completed, add sysfs entries */
+       if ((dev->priv_flags & IFF_EBRIDGE) && event == NETDEV_REGISTER) {
+               br_sysfs_addbr(dev);
+               return NOTIFY_DONE;
+       }
+
+       /* not a port of a bridge */
+       p = br_port_get_rtnl(dev);
+       if (!p)
+               return NOTIFY_DONE;
+
+       br = p->br;
+
+       switch (event) {
+       case NETDEV_CHANGEMTU:
+               dev_set_mtu(br->dev, br_min_mtu(br));
+               break;
+
+       case NETDEV_CHANGEADDR:
+               spin_lock_bh(&br->lock);
+               br_fdb_changeaddr(p, dev->dev_addr);
+               changed_addr = br_stp_recalculate_bridge_id(br);
+               spin_unlock_bh(&br->lock);
+
+               if (changed_addr)
+                       call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev);
+
+               break;
+
+       case NETDEV_CHANGE:
+               br_port_carrier_check(p);
+               break;
+
+       case NETDEV_FEAT_CHANGE:
+               netdev_update_features(br->dev);
+               break;
+
+       case NETDEV_DOWN:
+               spin_lock_bh(&br->lock);
+               if (br->dev->flags & IFF_UP)
+                       br_stp_disable_port(p);
+               spin_unlock_bh(&br->lock);
+               break;
+
+       case NETDEV_UP:
+               if (netif_running(br->dev) && netif_oper_up(dev)) {
+                       spin_lock_bh(&br->lock);
+                       br_stp_enable_port(p);
+                       spin_unlock_bh(&br->lock);
+               }
+               break;
+
+       case NETDEV_UNREGISTER:
+               br_del_if(br, dev);
+               break;
+
+       case NETDEV_CHANGENAME:
+               err = br_sysfs_renameif(p);
+               if (err)
+                       return notifier_from_errno(err);
+               break;
+
+       case NETDEV_PRE_TYPE_CHANGE:
+               /* Forbid underlaying device to change its type. */
+               return NOTIFY_BAD;
+
+       case NETDEV_RESEND_IGMP:
+               /* Propagate to master device */
+               call_netdevice_notifiers(event, br->dev);
+               break;
+       }
+
+       /* Events that may cause spanning tree to refresh */
+       if (event == NETDEV_CHANGEADDR || event == NETDEV_UP ||
+           event == NETDEV_CHANGE || event == NETDEV_DOWN)
+               br_ifinfo_notify(RTM_NEWLINK, p);
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block br_device_notifier = {
+       .notifier_call = br_device_event
+};
+
 static void __net_exit br_net_exit(struct net *net)
 {
        struct net_device *dev;
index 3e2da2cb72db1725f064ec21d3ce6ab8765532c1..568cccd39a3d8a25716ef13e14b2f4e4850c1db5 100644 (file)
@@ -112,6 +112,12 @@ static void br_dev_set_multicast_list(struct net_device *dev)
 {
 }
 
+static void br_dev_change_rx_flags(struct net_device *dev, int change)
+{
+       if (change & IFF_PROMISC)
+               br_manage_promisc(netdev_priv(dev));
+}
+
 static int br_dev_stop(struct net_device *dev)
 {
        struct net_bridge *br = netdev_priv(dev);
@@ -309,6 +315,7 @@ static const struct net_device_ops br_netdev_ops = {
        .ndo_get_stats64         = br_get_stats64,
        .ndo_set_mac_address     = br_set_mac_address,
        .ndo_set_rx_mode         = br_dev_set_multicast_list,
+       .ndo_change_rx_flags     = br_dev_change_rx_flags,
        .ndo_change_mtu          = br_change_mtu,
        .ndo_do_ioctl            = br_dev_ioctl,
 #ifdef CONFIG_NET_POLL_CONTROLLER
@@ -348,14 +355,15 @@ void br_dev_setup(struct net_device *dev)
 
        dev->netdev_ops = &br_netdev_ops;
        dev->destructor = br_dev_free;
-       SET_ETHTOOL_OPS(dev, &br_ethtool_ops);
+       dev->ethtool_ops = &br_ethtool_ops;
        SET_NETDEV_DEVTYPE(dev, &br_type);
        dev->tx_queue_len = 0;
        dev->priv_flags = IFF_EBRIDGE;
 
        dev->features = COMMON_FEATURES | NETIF_F_LLTX | NETIF_F_NETNS_LOCAL |
-                       NETIF_F_HW_VLAN_CTAG_TX;
-       dev->hw_features = COMMON_FEATURES | NETIF_F_HW_VLAN_CTAG_TX;
+                       NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX;
+       dev->hw_features = COMMON_FEATURES | NETIF_F_HW_VLAN_CTAG_TX |
+                          NETIF_F_HW_VLAN_STAG_TX;
        dev->vlan_features = COMMON_FEATURES;
 
        br->dev = dev;
@@ -370,6 +378,7 @@ void br_dev_setup(struct net_device *dev)
 
        br->stp_enabled = BR_NO_STP;
        br->group_fwd_mask = BR_GROUPFWD_DEFAULT;
+       br->group_fwd_mask_required = BR_GROUPFWD_DEFAULT;
 
        br->designated_root = br->bridge_id;
        br->bridge_max_age = br->max_age = 20 * HZ;
@@ -380,4 +389,5 @@ void br_dev_setup(struct net_device *dev)
        br_netfilter_rtable_init(br);
        br_stp_timer_init(br);
        br_multicast_init(br);
+       br_vlan_init(br);
 }
index 474d36f93342ea5c3a7cfcb210d3520b2fe78c6c..b524c36c12731b6c84a7587125fdf2db6417c830 100644 (file)
@@ -85,8 +85,58 @@ static void fdb_rcu_free(struct rcu_head *head)
        kmem_cache_free(br_fdb_cache, ent);
 }
 
+/* When a static FDB entry is added, the mac address from the entry is
+ * added to the bridge private HW address list and all required ports
+ * are then updated with the new information.
+ * Called under RTNL.
+ */
+static void fdb_add_hw(struct net_bridge *br, const unsigned char *addr)
+{
+       int err;
+       struct net_bridge_port *p, *tmp;
+
+       ASSERT_RTNL();
+
+       list_for_each_entry(p, &br->port_list, list) {
+               if (!br_promisc_port(p)) {
+                       err = dev_uc_add(p->dev, addr);
+                       if (err)
+                               goto undo;
+               }
+       }
+
+       return;
+undo:
+       list_for_each_entry(tmp, &br->port_list, list) {
+               if (tmp == p)
+                       break;
+               if (!br_promisc_port(tmp))
+                       dev_uc_del(tmp->dev, addr);
+       }
+}
+
+/* When a static FDB entry is deleted, the HW address from that entry is
+ * also removed from the bridge private HW address list and updates all
+ * the ports with needed information.
+ * Called under RTNL.
+ */
+static void fdb_del_hw(struct net_bridge *br, const unsigned char *addr)
+{
+       struct net_bridge_port *p;
+
+       ASSERT_RTNL();
+
+       list_for_each_entry(p, &br->port_list, list) {
+               if (!br_promisc_port(p))
+                       dev_uc_del(p->dev, addr);
+       }
+}
+
 static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
 {
+       if (f->is_static)
+               fdb_del_hw(br, f->addr.addr);
+
        hlist_del_rcu(&f->hlist);
        fdb_notify(br, f, RTM_DELNEIGH);
        call_rcu(&f->rcu, fdb_rcu_free);
@@ -466,6 +516,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
                return -ENOMEM;
 
        fdb->is_local = fdb->is_static = 1;
+       fdb_add_hw(br, addr);
        fdb_notify(br, fdb, RTM_NEWNEIGH);
        return 0;
 }
@@ -571,6 +622,8 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br,
 
        if (nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->addr))
                goto nla_put_failure;
+       if (nla_put_u32(skb, NDA_MASTER, br->dev->ifindex))
+               goto nla_put_failure;
        ci.ndm_used      = jiffies_to_clock_t(now - fdb->used);
        ci.ndm_confirmed = 0;
        ci.ndm_updated   = jiffies_to_clock_t(now - fdb->updated);
@@ -592,6 +645,7 @@ static inline size_t fdb_nlmsg_size(void)
 {
        return NLMSG_ALIGN(sizeof(struct ndmsg))
                + nla_total_size(ETH_ALEN) /* NDA_LLADDR */
+               + nla_total_size(sizeof(u32)) /* NDA_MASTER */
                + nla_total_size(sizeof(u16)) /* NDA_VLAN */
                + nla_total_size(sizeof(struct nda_cacheinfo));
 }
@@ -684,13 +738,25 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
        }
 
        if (fdb_to_nud(fdb) != state) {
-               if (state & NUD_PERMANENT)
-                       fdb->is_local = fdb->is_static = 1;
-               else if (state & NUD_NOARP) {
+               if (state & NUD_PERMANENT) {
+                       fdb->is_local = 1;
+                       if (!fdb->is_static) {
+                               fdb->is_static = 1;
+                               fdb_add_hw(br, addr);
+                       }
+               } else if (state & NUD_NOARP) {
                        fdb->is_local = 0;
-                       fdb->is_static = 1;
-               } else
-                       fdb->is_local = fdb->is_static = 0;
+                       if (!fdb->is_static) {
+                               fdb->is_static = 1;
+                               fdb_add_hw(br, addr);
+                       }
+               } else {
+                       fdb->is_local = 0;
+                       if (fdb->is_static) {
+                               fdb->is_static = 0;
+                               fdb_del_hw(br, addr);
+                       }
+               }
 
                modified = true;
        }
@@ -880,3 +946,59 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
 out:
        return err;
 }
+
+int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p)
+{
+       struct net_bridge_fdb_entry *fdb, *tmp;
+       int i;
+       int err;
+
+       ASSERT_RTNL();
+
+       for (i = 0; i < BR_HASH_SIZE; i++) {
+               hlist_for_each_entry(fdb, &br->hash[i], hlist) {
+                       /* We only care for static entries */
+                       if (!fdb->is_static)
+                               continue;
+
+                       err = dev_uc_add(p->dev, fdb->addr.addr);
+                       if (err)
+                               goto rollback;
+               }
+       }
+       return 0;
+
+rollback:
+       for (i = 0; i < BR_HASH_SIZE; i++) {
+               hlist_for_each_entry(tmp, &br->hash[i], hlist) {
+                       /* If we reached the fdb that failed, we can stop */
+                       if (tmp == fdb)
+                               break;
+
+                       /* We only care for static entries */
+                       if (!tmp->is_static)
+                               continue;
+
+                       dev_uc_del(p->dev, tmp->addr.addr);
+               }
+       }
+       return err;
+}
+
+void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p)
+{
+       struct net_bridge_fdb_entry *fdb;
+       int i;
+
+       ASSERT_RTNL();
+
+       for (i = 0; i < BR_HASH_SIZE; i++) {
+               hlist_for_each_entry_rcu(fdb, &br->hash[i], hlist) {
+                       /* We only care for static entries */
+                       if (!fdb->is_static)
+                               continue;
+
+                       dev_uc_del(p->dev, fdb->addr.addr);
+               }
+       }
+}
index 5262b8617eb9cc21b1070e48d1c1efda584aef6f..3eca3fdf8fe1c7563150f735991e66380c4312ca 100644 (file)
@@ -85,6 +85,111 @@ void br_port_carrier_check(struct net_bridge_port *p)
        spin_unlock_bh(&br->lock);
 }
 
+static void br_port_set_promisc(struct net_bridge_port *p)
+{
+       int err = 0;
+
+       if (br_promisc_port(p))
+               return;
+
+       err = dev_set_promiscuity(p->dev, 1);
+       if (err)
+               return;
+
+       br_fdb_unsync_static(p->br, p);
+       p->flags |= BR_PROMISC;
+}
+
+static void br_port_clear_promisc(struct net_bridge_port *p)
+{
+       int err;
+
+       /* Check if the port is already non-promisc or if it doesn't
+        * support UNICAST filtering.  Without unicast filtering support
+        * we'll end up re-enabling promisc mode anyway, so just check for
+        * it here.
+        */
+       if (!br_promisc_port(p) || !(p->dev->priv_flags & IFF_UNICAST_FLT))
+               return;
+
+       /* Since we'll be clearing the promisc mode, program the port
+        * first so that we don't have interruption in traffic.
+        */
+       err = br_fdb_sync_static(p->br, p);
+       if (err)
+               return;
+
+       dev_set_promiscuity(p->dev, -1);
+       p->flags &= ~BR_PROMISC;
+}
+
+/* When a port is added or removed or when certain port flags
+ * change, this function is called to automatically manage
+ * promiscuity setting of all the bridge ports.  We are always called
+ * under RTNL so can skip using rcu primitives.
+ */
+void br_manage_promisc(struct net_bridge *br)
+{
+       struct net_bridge_port *p;
+       bool set_all = false;
+
+       /* If vlan filtering is disabled or bridge interface is placed
+        * into promiscuous mode, place all ports in promiscuous mode.
+        */
+       if ((br->dev->flags & IFF_PROMISC) || !br_vlan_enabled(br))
+               set_all = true;
+
+       list_for_each_entry(p, &br->port_list, list) {
+               if (set_all) {
+                       br_port_set_promisc(p);
+               } else {
+                       /* If the number of auto-ports is <= 1, then all other
+                        * ports will have their output configuration
+                        * statically specified through fdbs.  Since ingress
+                        * on the auto-port becomes forwarding/egress to other
+                        * ports and egress configuration is statically known,
+                        * we can say that ingress configuration of the
+                        * auto-port is also statically known.
+                        * This lets us disable promiscuous mode and write
+                        * this config to hw.
+                        */
+                       if (br->auto_cnt == 0 ||
+                           (br->auto_cnt == 1 && br_auto_port(p)))
+                               br_port_clear_promisc(p);
+                       else
+                               br_port_set_promisc(p);
+               }
+       }
+}
+
+static void nbp_update_port_count(struct net_bridge *br)
+{
+       struct net_bridge_port *p;
+       u32 cnt = 0;
+
+       list_for_each_entry(p, &br->port_list, list) {
+               if (br_auto_port(p))
+                       cnt++;
+       }
+       if (br->auto_cnt != cnt) {
+               br->auto_cnt = cnt;
+               br_manage_promisc(br);
+       }
+}
+
+static void nbp_delete_promisc(struct net_bridge_port *p)
+{
+       /* If port is currently promiscuous, unset promiscuity.
+        * Otherwise, it is a static port so remove all addresses
+        * from it.
+        */
+       dev_set_allmulti(p->dev, -1);
+       if (br_promisc_port(p))
+               dev_set_promiscuity(p->dev, -1);
+       else
+               br_fdb_unsync_static(p->br, p);
+}
+
 static void release_nbp(struct kobject *kobj)
 {
        struct net_bridge_port *p
@@ -133,7 +238,7 @@ static void del_nbp(struct net_bridge_port *p)
 
        sysfs_remove_link(br->ifobj, p->dev->name);
 
-       dev_set_promiscuity(dev, -1);
+       nbp_delete_promisc(p);
 
        spin_lock_bh(&br->lock);
        br_stp_disable_port(p);
@@ -141,10 +246,11 @@ static void del_nbp(struct net_bridge_port *p)
 
        br_ifinfo_notify(RTM_DELLINK, p);
 
+       list_del_rcu(&p->list);
+
        nbp_vlan_flush(p);
        br_fdb_delete_by_port(br, p, 1);
-
-       list_del_rcu(&p->list);
+       nbp_update_port_count(br);
 
        dev->priv_flags &= ~IFF_BRIDGE_PORT;
 
@@ -353,7 +459,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
 
        call_netdevice_notifiers(NETDEV_JOIN, dev);
 
-       err = dev_set_promiscuity(dev, 1);
+       err = dev_set_allmulti(dev, 1);
        if (err)
                goto put_back;
 
@@ -384,6 +490,8 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
 
        list_add_rcu(&p->list, &br->port_list);
 
+       nbp_update_port_count(br);
+
        netdev_update_features(br->dev);
 
        if (br->dev->needed_headroom < dev->needed_headroom)
@@ -421,7 +529,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
        kobject_put(&p->kobj);
        p = NULL; /* kobject_put frees */
 err1:
-       dev_set_promiscuity(dev, -1);
+       dev_set_allmulti(dev, -1);
 put_back:
        dev_put(dev);
        kfree(p);
@@ -455,3 +563,11 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
 
        return 0;
 }
+
+void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
+{
+       struct net_bridge *br = p->br;
+
+       if (mask & BR_AUTO_MASK)
+               nbp_update_port_count(br);
+}
index 04d6348fd530f854769bf2e0220ba4b4a4489833..366c43649079d9bdef66063af2ab848965ca8197 100644 (file)
@@ -177,6 +177,8 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
        p = br_port_get_rcu(skb->dev);
 
        if (unlikely(is_link_local_ether_addr(dest))) {
+               u16 fwd_mask = p->br->group_fwd_mask_required;
+
                /*
                 * See IEEE 802.1D Table 7-10 Reserved addresses
                 *
@@ -194,7 +196,8 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
                case 0x00:      /* Bridge Group Address */
                        /* If STP is turned off,
                           then must forward to keep loop detection */
-                       if (p->br->stp_enabled == BR_NO_STP)
+                       if (p->br->stp_enabled == BR_NO_STP ||
+                           fwd_mask & (1u << dest[5]))
                                goto forward;
                        break;
 
@@ -203,7 +206,8 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
 
                default:
                        /* Allow selective forwarding for most other protocols */
-                       if (p->br->group_fwd_mask & (1u << dest[5]))
+                       fwd_mask |= p->br->group_fwd_mask;
+                       if (fwd_mask & (1u << dest[5]))
                                goto forward;
                }
 
index b7b1914dfa252a3731a26e2bcf2be3afc5171661..5df05269d17a6f9b478aa6d90d5c322f0708e0c4 100644 (file)
@@ -418,13 +418,13 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)
 
        ip.proto = entry->addr.proto;
        if (ip.proto == htons(ETH_P_IP)) {
-               if (timer_pending(&br->ip4_querier.timer))
+               if (timer_pending(&br->ip4_other_query.timer))
                        return -EBUSY;
 
                ip.u.ip4 = entry->addr.u.ip4;
 #if IS_ENABLED(CONFIG_IPV6)
        } else {
-               if (timer_pending(&br->ip6_querier.timer))
+               if (timer_pending(&br->ip6_other_query.timer))
                        return -EBUSY;
 
                ip.u.ip6 = entry->addr.u.ip6;
index 7b757b5dc773fc2dcaedfa8f7c97e7884a622d89..abfa0b65a1118eb0abae4dd69f78e7285537acd8 100644 (file)
@@ -11,6 +11,7 @@
  */
 
 #include <linux/err.h>
+#include <linux/export.h>
 #include <linux/if_ether.h>
 #include <linux/igmp.h>
 #include <linux/jhash.h>
@@ -35,7 +36,7 @@
 #include "br_private.h"
 
 static void br_multicast_start_querier(struct net_bridge *br,
-                                      struct bridge_mcast_query *query);
+                                      struct bridge_mcast_own_query *query);
 unsigned int br_mdb_rehash_seq;
 
 static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b)
@@ -761,7 +762,7 @@ static void br_multicast_local_router_expired(unsigned long data)
 }
 
 static void br_multicast_querier_expired(struct net_bridge *br,
-                                        struct bridge_mcast_query *query)
+                                        struct bridge_mcast_own_query *query)
 {
        spin_lock(&br->multicast_lock);
        if (!netif_running(br->dev) || br->multicast_disabled)
@@ -777,7 +778,7 @@ static void br_ip4_multicast_querier_expired(unsigned long data)
 {
        struct net_bridge *br = (void *)data;
 
-       br_multicast_querier_expired(br, &br->ip4_query);
+       br_multicast_querier_expired(br, &br->ip4_own_query);
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
@@ -785,10 +786,22 @@ static void br_ip6_multicast_querier_expired(unsigned long data)
 {
        struct net_bridge *br = (void *)data;
 
-       br_multicast_querier_expired(br, &br->ip6_query);
+       br_multicast_querier_expired(br, &br->ip6_own_query);
 }
 #endif
 
+static void br_multicast_select_own_querier(struct net_bridge *br,
+                                           struct br_ip *ip,
+                                           struct sk_buff *skb)
+{
+       if (ip->proto == htons(ETH_P_IP))
+               br->ip4_querier.addr.u.ip4 = ip_hdr(skb)->saddr;
+#if IS_ENABLED(CONFIG_IPV6)
+       else
+               br->ip6_querier.addr.u.ip6 = ipv6_hdr(skb)->saddr;
+#endif
+}
+
 static void __br_multicast_send_query(struct net_bridge *br,
                                      struct net_bridge_port *port,
                                      struct br_ip *ip)
@@ -804,17 +817,19 @@ static void __br_multicast_send_query(struct net_bridge *br,
                skb->dev = port->dev;
                NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
                        dev_queue_xmit);
-       } else
+       } else {
+               br_multicast_select_own_querier(br, ip, skb);
                netif_rx(skb);
+       }
 }
 
 static void br_multicast_send_query(struct net_bridge *br,
                                    struct net_bridge_port *port,
-                                   struct bridge_mcast_query *query)
+                                   struct bridge_mcast_own_query *own_query)
 {
        unsigned long time;
        struct br_ip br_group;
-       struct bridge_mcast_querier *querier = NULL;
+       struct bridge_mcast_other_query *other_query = NULL;
 
        if (!netif_running(br->dev) || br->multicast_disabled ||
            !br->multicast_querier)
@@ -822,31 +837,32 @@ static void br_multicast_send_query(struct net_bridge *br,
 
        memset(&br_group.u, 0, sizeof(br_group.u));
 
-       if (port ? (query == &port->ip4_query) :
-                  (query == &br->ip4_query)) {
-               querier = &br->ip4_querier;
+       if (port ? (own_query == &port->ip4_own_query) :
+                  (own_query == &br->ip4_own_query)) {
+               other_query = &br->ip4_other_query;
                br_group.proto = htons(ETH_P_IP);
 #if IS_ENABLED(CONFIG_IPV6)
        } else {
-               querier = &br->ip6_querier;
+               other_query = &br->ip6_other_query;
                br_group.proto = htons(ETH_P_IPV6);
 #endif
        }
 
-       if (!querier || timer_pending(&querier->timer))
+       if (!other_query || timer_pending(&other_query->timer))
                return;
 
        __br_multicast_send_query(br, port, &br_group);
 
        time = jiffies;
-       time += query->startup_sent < br->multicast_startup_query_count ?
+       time += own_query->startup_sent < br->multicast_startup_query_count ?
                br->multicast_startup_query_interval :
                br->multicast_query_interval;
-       mod_timer(&query->timer, time);
+       mod_timer(&own_query->timer, time);
 }
 
-static void br_multicast_port_query_expired(struct net_bridge_port *port,
-                                           struct bridge_mcast_query *query)
+static void
+br_multicast_port_query_expired(struct net_bridge_port *port,
+                               struct bridge_mcast_own_query *query)
 {
        struct net_bridge *br = port->br;
 
@@ -868,7 +884,7 @@ static void br_ip4_multicast_port_query_expired(unsigned long data)
 {
        struct net_bridge_port *port = (void *)data;
 
-       br_multicast_port_query_expired(port, &port->ip4_query);
+       br_multicast_port_query_expired(port, &port->ip4_own_query);
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
@@ -876,7 +892,7 @@ static void br_ip6_multicast_port_query_expired(unsigned long data)
 {
        struct net_bridge_port *port = (void *)data;
 
-       br_multicast_port_query_expired(port, &port->ip6_query);
+       br_multicast_port_query_expired(port, &port->ip6_own_query);
 }
 #endif
 
@@ -886,11 +902,11 @@ void br_multicast_add_port(struct net_bridge_port *port)
 
        setup_timer(&port->multicast_router_timer, br_multicast_router_expired,
                    (unsigned long)port);
-       setup_timer(&port->ip4_query.timer, br_ip4_multicast_port_query_expired,
-                   (unsigned long)port);
+       setup_timer(&port->ip4_own_query.timer,
+                   br_ip4_multicast_port_query_expired, (unsigned long)port);
 #if IS_ENABLED(CONFIG_IPV6)
-       setup_timer(&port->ip6_query.timer, br_ip6_multicast_port_query_expired,
-                   (unsigned long)port);
+       setup_timer(&port->ip6_own_query.timer,
+                   br_ip6_multicast_port_query_expired, (unsigned long)port);
 #endif
 }
 
@@ -899,7 +915,7 @@ void br_multicast_del_port(struct net_bridge_port *port)
        del_timer_sync(&port->multicast_router_timer);
 }
 
-static void br_multicast_enable(struct bridge_mcast_query *query)
+static void br_multicast_enable(struct bridge_mcast_own_query *query)
 {
        query->startup_sent = 0;
 
@@ -916,9 +932,9 @@ void br_multicast_enable_port(struct net_bridge_port *port)
        if (br->multicast_disabled || !netif_running(br->dev))
                goto out;
 
-       br_multicast_enable(&port->ip4_query);
+       br_multicast_enable(&port->ip4_own_query);
 #if IS_ENABLED(CONFIG_IPV6)
-       br_multicast_enable(&port->ip6_query);
+       br_multicast_enable(&port->ip6_own_query);
 #endif
 
 out:
@@ -938,9 +954,9 @@ void br_multicast_disable_port(struct net_bridge_port *port)
        if (!hlist_unhashed(&port->rlist))
                hlist_del_init_rcu(&port->rlist);
        del_timer(&port->multicast_router_timer);
-       del_timer(&port->ip4_query.timer);
+       del_timer(&port->ip4_own_query.timer);
 #if IS_ENABLED(CONFIG_IPV6)
-       del_timer(&port->ip6_query.timer);
+       del_timer(&port->ip6_own_query.timer);
 #endif
        spin_unlock(&br->multicast_lock);
 }
@@ -1064,15 +1080,80 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,
 }
 #endif
 
+static bool br_ip4_multicast_select_querier(struct net_bridge *br,
+                                           struct net_bridge_port *port,
+                                           __be32 saddr)
+{
+       if (!timer_pending(&br->ip4_own_query.timer) &&
+           !timer_pending(&br->ip4_other_query.timer))
+               goto update;
+
+       if (!br->ip4_querier.addr.u.ip4)
+               goto update;
+
+       if (ntohl(saddr) <= ntohl(br->ip4_querier.addr.u.ip4))
+               goto update;
+
+       return false;
+
+update:
+       br->ip4_querier.addr.u.ip4 = saddr;
+
+       /* update protected by general multicast_lock by caller */
+       rcu_assign_pointer(br->ip4_querier.port, port);
+
+       return true;
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+static bool br_ip6_multicast_select_querier(struct net_bridge *br,
+                                           struct net_bridge_port *port,
+                                           struct in6_addr *saddr)
+{
+       if (!timer_pending(&br->ip6_own_query.timer) &&
+           !timer_pending(&br->ip6_other_query.timer))
+               goto update;
+
+       if (ipv6_addr_cmp(saddr, &br->ip6_querier.addr.u.ip6) <= 0)
+               goto update;
+
+       return false;
+
+update:
+       br->ip6_querier.addr.u.ip6 = *saddr;
+
+       /* update protected by general multicast_lock by caller */
+       rcu_assign_pointer(br->ip6_querier.port, port);
+
+       return true;
+}
+#endif
+
+static bool br_multicast_select_querier(struct net_bridge *br,
+                                       struct net_bridge_port *port,
+                                       struct br_ip *saddr)
+{
+       switch (saddr->proto) {
+       case htons(ETH_P_IP):
+               return br_ip4_multicast_select_querier(br, port, saddr->u.ip4);
+#if IS_ENABLED(CONFIG_IPV6)
+       case htons(ETH_P_IPV6):
+               return br_ip6_multicast_select_querier(br, port, &saddr->u.ip6);
+#endif
+       }
+
+       return false;
+}
+
 static void
-br_multicast_update_querier_timer(struct net_bridge *br,
-                                 struct bridge_mcast_querier *querier,
-                                 unsigned long max_delay)
+br_multicast_update_query_timer(struct net_bridge *br,
+                               struct bridge_mcast_other_query *query,
+                               unsigned long max_delay)
 {
-       if (!timer_pending(&querier->timer))
-               querier->delay_time = jiffies + max_delay;
+       if (!timer_pending(&query->timer))
+               query->delay_time = jiffies + max_delay;
 
-       mod_timer(&querier->timer, jiffies + br->multicast_querier_interval);
+       mod_timer(&query->timer, jiffies + br->multicast_querier_interval);
 }
 
 /*
@@ -1125,16 +1206,14 @@ static void br_multicast_mark_router(struct net_bridge *br,
 
 static void br_multicast_query_received(struct net_bridge *br,
                                        struct net_bridge_port *port,
-                                       struct bridge_mcast_querier *querier,
-                                       int saddr,
-                                       bool is_general_query,
+                                       struct bridge_mcast_other_query *query,
+                                       struct br_ip *saddr,
                                        unsigned long max_delay)
 {
-       if (saddr && is_general_query)
-               br_multicast_update_querier_timer(br, querier, max_delay);
-       else if (timer_pending(&querier->timer))
+       if (!br_multicast_select_querier(br, port, saddr))
                return;
 
+       br_multicast_update_query_timer(br, query, max_delay);
        br_multicast_mark_router(br, port);
 }
 
@@ -1149,6 +1228,7 @@ static int br_ip4_multicast_query(struct net_bridge *br,
        struct igmpv3_query *ih3;
        struct net_bridge_port_group *p;
        struct net_bridge_port_group __rcu **pp;
+       struct br_ip saddr;
        unsigned long max_delay;
        unsigned long now = jiffies;
        __be32 group;
@@ -1190,11 +1270,14 @@ static int br_ip4_multicast_query(struct net_bridge *br,
                goto out;
        }
 
-       br_multicast_query_received(br, port, &br->ip4_querier, !!iph->saddr,
-                                   !group, max_delay);
+       if (!group) {
+               saddr.proto = htons(ETH_P_IP);
+               saddr.u.ip4 = iph->saddr;
 
-       if (!group)
+               br_multicast_query_received(br, port, &br->ip4_other_query,
+                                           &saddr, max_delay);
                goto out;
+       }
 
        mp = br_mdb_ip4_get(mlock_dereference(br->mdb, br), group, vid);
        if (!mp)
@@ -1234,6 +1317,7 @@ static int br_ip6_multicast_query(struct net_bridge *br,
        struct mld2_query *mld2q;
        struct net_bridge_port_group *p;
        struct net_bridge_port_group __rcu **pp;
+       struct br_ip saddr;
        unsigned long max_delay;
        unsigned long now = jiffies;
        const struct in6_addr *group = NULL;
@@ -1282,12 +1366,16 @@ static int br_ip6_multicast_query(struct net_bridge *br,
                goto out;
        }
 
-       br_multicast_query_received(br, port, &br->ip6_querier,
-                                   !ipv6_addr_any(&ip6h->saddr),
-                                   is_general_query, max_delay);
+       if (is_general_query) {
+               saddr.proto = htons(ETH_P_IPV6);
+               saddr.u.ip6 = ip6h->saddr;
 
-       if (!group)
+               br_multicast_query_received(br, port, &br->ip6_other_query,
+                                           &saddr, max_delay);
+               goto out;
+       } else if (!group) {
                goto out;
+       }
 
        mp = br_mdb_ip6_get(mlock_dereference(br->mdb, br), group, vid);
        if (!mp)
@@ -1315,11 +1403,12 @@ static int br_ip6_multicast_query(struct net_bridge *br,
 }
 #endif
 
-static void br_multicast_leave_group(struct net_bridge *br,
-                                    struct net_bridge_port *port,
-                                    struct br_ip *group,
-                                    struct bridge_mcast_querier *querier,
-                                    struct bridge_mcast_query *query)
+static void
+br_multicast_leave_group(struct net_bridge *br,
+                        struct net_bridge_port *port,
+                        struct br_ip *group,
+                        struct bridge_mcast_other_query *other_query,
+                        struct bridge_mcast_own_query *own_query)
 {
        struct net_bridge_mdb_htable *mdb;
        struct net_bridge_mdb_entry *mp;
@@ -1330,7 +1419,7 @@ static void br_multicast_leave_group(struct net_bridge *br,
        spin_lock(&br->multicast_lock);
        if (!netif_running(br->dev) ||
            (port && port->state == BR_STATE_DISABLED) ||
-           timer_pending(&querier->timer))
+           timer_pending(&other_query->timer))
                goto out;
 
        mdb = mlock_dereference(br->mdb, br);
@@ -1344,7 +1433,7 @@ static void br_multicast_leave_group(struct net_bridge *br,
                time = jiffies + br->multicast_last_member_count *
                                 br->multicast_last_member_interval;
 
-               mod_timer(&query->timer, time);
+               mod_timer(&own_query->timer, time);
 
                for (p = mlock_dereference(mp->ports, br);
                     p != NULL;
@@ -1425,17 +1514,19 @@ static void br_ip4_multicast_leave_group(struct net_bridge *br,
                                         __u16 vid)
 {
        struct br_ip br_group;
-       struct bridge_mcast_query *query = port ? &port->ip4_query :
-                                                 &br->ip4_query;
+       struct bridge_mcast_own_query *own_query;
 
        if (ipv4_is_local_multicast(group))
                return;
 
+       own_query = port ? &port->ip4_own_query : &br->ip4_own_query;
+
        br_group.u.ip4 = group;
        br_group.proto = htons(ETH_P_IP);
        br_group.vid = vid;
 
-       br_multicast_leave_group(br, port, &br_group, &br->ip4_querier, query);
+       br_multicast_leave_group(br, port, &br_group, &br->ip4_other_query,
+                                own_query);
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
@@ -1445,18 +1536,19 @@ static void br_ip6_multicast_leave_group(struct net_bridge *br,
                                         __u16 vid)
 {
        struct br_ip br_group;
-       struct bridge_mcast_query *query = port ? &port->ip6_query :
-                                                 &br->ip6_query;
-
+       struct bridge_mcast_own_query *own_query;
 
        if (ipv6_addr_is_ll_all_nodes(group))
                return;
 
+       own_query = port ? &port->ip6_own_query : &br->ip6_own_query;
+
        br_group.u.ip6 = *group;
        br_group.proto = htons(ETH_P_IPV6);
        br_group.vid = vid;
 
-       br_multicast_leave_group(br, port, &br_group, &br->ip6_querier, query);
+       br_multicast_leave_group(br, port, &br_group, &br->ip6_other_query,
+                                own_query);
 }
 #endif
 
@@ -1723,12 +1815,14 @@ int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port,
 }
 
 static void br_multicast_query_expired(struct net_bridge *br,
-                                      struct bridge_mcast_query *query)
+                                      struct bridge_mcast_own_query *query,
+                                      struct bridge_mcast_querier *querier)
 {
        spin_lock(&br->multicast_lock);
        if (query->startup_sent < br->multicast_startup_query_count)
                query->startup_sent++;
 
+       rcu_assign_pointer(querier, NULL);
        br_multicast_send_query(br, NULL, query);
        spin_unlock(&br->multicast_lock);
 }
@@ -1737,7 +1831,7 @@ static void br_ip4_multicast_query_expired(unsigned long data)
 {
        struct net_bridge *br = (void *)data;
 
-       br_multicast_query_expired(br, &br->ip4_query);
+       br_multicast_query_expired(br, &br->ip4_own_query, &br->ip4_querier);
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
@@ -1745,7 +1839,7 @@ static void br_ip6_multicast_query_expired(unsigned long data)
 {
        struct net_bridge *br = (void *)data;
 
-       br_multicast_query_expired(br, &br->ip6_query);
+       br_multicast_query_expired(br, &br->ip6_own_query, &br->ip6_querier);
 }
 #endif
 
@@ -1767,28 +1861,30 @@ void br_multicast_init(struct net_bridge *br)
        br->multicast_querier_interval = 255 * HZ;
        br->multicast_membership_interval = 260 * HZ;
 
-       br->ip4_querier.delay_time = 0;
+       br->ip4_other_query.delay_time = 0;
+       br->ip4_querier.port = NULL;
 #if IS_ENABLED(CONFIG_IPV6)
-       br->ip6_querier.delay_time = 0;
+       br->ip6_other_query.delay_time = 0;
+       br->ip6_querier.port = NULL;
 #endif
 
        spin_lock_init(&br->multicast_lock);
        setup_timer(&br->multicast_router_timer,
                    br_multicast_local_router_expired, 0);
-       setup_timer(&br->ip4_querier.timer, br_ip4_multicast_querier_expired,
-                   (unsigned long)br);
-       setup_timer(&br->ip4_query.timer, br_ip4_multicast_query_expired,
+       setup_timer(&br->ip4_other_query.timer,
+                   br_ip4_multicast_querier_expired, (unsigned long)br);
+       setup_timer(&br->ip4_own_query.timer, br_ip4_multicast_query_expired,
                    (unsigned long)br);
 #if IS_ENABLED(CONFIG_IPV6)
-       setup_timer(&br->ip6_querier.timer, br_ip6_multicast_querier_expired,
-                   (unsigned long)br);
-       setup_timer(&br->ip6_query.timer, br_ip6_multicast_query_expired,
+       setup_timer(&br->ip6_other_query.timer,
+                   br_ip6_multicast_querier_expired, (unsigned long)br);
+       setup_timer(&br->ip6_own_query.timer, br_ip6_multicast_query_expired,
                    (unsigned long)br);
 #endif
 }
 
 static void __br_multicast_open(struct net_bridge *br,
-                               struct bridge_mcast_query *query)
+                               struct bridge_mcast_own_query *query)
 {
        query->startup_sent = 0;
 
@@ -1800,9 +1896,9 @@ static void __br_multicast_open(struct net_bridge *br,
 
 void br_multicast_open(struct net_bridge *br)
 {
-       __br_multicast_open(br, &br->ip4_query);
+       __br_multicast_open(br, &br->ip4_own_query);
 #if IS_ENABLED(CONFIG_IPV6)
-       __br_multicast_open(br, &br->ip6_query);
+       __br_multicast_open(br, &br->ip6_own_query);
 #endif
 }
 
@@ -1815,11 +1911,11 @@ void br_multicast_stop(struct net_bridge *br)
        int i;
 
        del_timer_sync(&br->multicast_router_timer);
-       del_timer_sync(&br->ip4_querier.timer);
-       del_timer_sync(&br->ip4_query.timer);
+       del_timer_sync(&br->ip4_other_query.timer);
+       del_timer_sync(&br->ip4_own_query.timer);
 #if IS_ENABLED(CONFIG_IPV6)
-       del_timer_sync(&br->ip6_querier.timer);
-       del_timer_sync(&br->ip6_query.timer);
+       del_timer_sync(&br->ip6_other_query.timer);
+       del_timer_sync(&br->ip6_own_query.timer);
 #endif
 
        spin_lock_bh(&br->multicast_lock);
@@ -1923,7 +2019,7 @@ int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val)
 }
 
 static void br_multicast_start_querier(struct net_bridge *br,
-                                      struct bridge_mcast_query *query)
+                                      struct bridge_mcast_own_query *query)
 {
        struct net_bridge_port *port;
 
@@ -1934,11 +2030,11 @@ static void br_multicast_start_querier(struct net_bridge *br,
                    port->state == BR_STATE_BLOCKING)
                        continue;
 
-               if (query == &br->ip4_query)
-                       br_multicast_enable(&port->ip4_query);
+               if (query == &br->ip4_own_query)
+                       br_multicast_enable(&port->ip4_own_query);
 #if IS_ENABLED(CONFIG_IPV6)
                else
-                       br_multicast_enable(&port->ip6_query);
+                       br_multicast_enable(&port->ip6_own_query);
 #endif
        }
 }
@@ -1974,9 +2070,9 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val)
                        goto rollback;
        }
 
-       br_multicast_start_querier(br, &br->ip4_query);
+       br_multicast_start_querier(br, &br->ip4_own_query);
 #if IS_ENABLED(CONFIG_IPV6)
-       br_multicast_start_querier(br, &br->ip6_query);
+       br_multicast_start_querier(br, &br->ip6_own_query);
 #endif
 
 unlock:
@@ -2001,16 +2097,16 @@ int br_multicast_set_querier(struct net_bridge *br, unsigned long val)
 
        max_delay = br->multicast_query_response_interval;
 
-       if (!timer_pending(&br->ip4_querier.timer))
-               br->ip4_querier.delay_time = jiffies + max_delay;
+       if (!timer_pending(&br->ip4_other_query.timer))
+               br->ip4_other_query.delay_time = jiffies + max_delay;
 
-       br_multicast_start_querier(br, &br->ip4_query);
+       br_multicast_start_querier(br, &br->ip4_own_query);
 
 #if IS_ENABLED(CONFIG_IPV6)
-       if (!timer_pending(&br->ip6_querier.timer))
-               br->ip6_querier.delay_time = jiffies + max_delay;
+       if (!timer_pending(&br->ip6_other_query.timer))
+               br->ip6_other_query.delay_time = jiffies + max_delay;
 
-       br_multicast_start_querier(br, &br->ip6_query);
+       br_multicast_start_querier(br, &br->ip6_own_query);
 #endif
 
 unlock:
@@ -2061,3 +2157,109 @@ int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val)
 
        return err;
 }
+
+/**
+ * br_multicast_list_adjacent - Returns snooped multicast addresses
+ * @dev:       The bridge port adjacent to which to retrieve addresses
+ * @br_ip_list:        The list to store found, snooped multicast IP addresses in
+ *
+ * Creates a list of IP addresses (struct br_ip_list) sensed by the multicast
+ * snooping feature on all bridge ports of dev's bridge device, excluding
+ * the addresses from dev itself.
+ *
+ * Returns the number of items added to br_ip_list.
+ *
+ * Notes:
+ * - br_ip_list needs to be initialized by caller
+ * - br_ip_list might contain duplicates in the end
+ *   (needs to be taken care of by caller)
+ * - br_ip_list needs to be freed by caller
+ */
+int br_multicast_list_adjacent(struct net_device *dev,
+                              struct list_head *br_ip_list)
+{
+       struct net_bridge *br;
+       struct net_bridge_port *port;
+       struct net_bridge_port_group *group;
+       struct br_ip_list *entry;
+       int count = 0;
+
+       rcu_read_lock();
+       if (!br_ip_list || !br_port_exists(dev))
+               goto unlock;
+
+       port = br_port_get_rcu(dev);
+       if (!port || !port->br)
+               goto unlock;
+
+       br = port->br;
+
+       list_for_each_entry_rcu(port, &br->port_list, list) {
+               if (!port->dev || port->dev == dev)
+                       continue;
+
+               hlist_for_each_entry_rcu(group, &port->mglist, mglist) {
+                       entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+                       if (!entry)
+                               goto unlock;
+
+                       entry->addr = group->addr;
+                       list_add(&entry->list, br_ip_list);
+                       count++;
+               }
+       }
+
+unlock:
+       rcu_read_unlock();
+       return count;
+}
+EXPORT_SYMBOL_GPL(br_multicast_list_adjacent);
+
+/**
+ * br_multicast_has_querier_adjacent - Checks for a querier behind a bridge port
+ * @dev: The bridge port adjacent to which to check for a querier
+ * @proto: The protocol family to check for: IGMP -> ETH_P_IP, MLD -> ETH_P_IPV6
+ *
+ * Checks whether the given interface has a bridge on top and if so returns
+ * true if a selected querier is behind one of the other ports of this
+ * bridge. Otherwise returns false.
+ */
+bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto)
+{
+       struct net_bridge *br;
+       struct net_bridge_port *port;
+       bool ret = false;
+
+       rcu_read_lock();
+       if (!br_port_exists(dev))
+               goto unlock;
+
+       port = br_port_get_rcu(dev);
+       if (!port || !port->br)
+               goto unlock;
+
+       br = port->br;
+
+       switch (proto) {
+       case ETH_P_IP:
+               if (!timer_pending(&br->ip4_other_query.timer) ||
+                   rcu_dereference(br->ip4_querier.port) == port)
+                       goto unlock;
+               break;
+#if IS_ENABLED(CONFIG_IPV6)
+       case ETH_P_IPV6:
+               if (!timer_pending(&br->ip6_other_query.timer) ||
+                   rcu_dereference(br->ip6_querier.port) == port)
+                       goto unlock;
+               break;
+#endif
+       default:
+               goto unlock;
+       }
+
+       ret = true;
+unlock:
+       rcu_read_unlock();
+       return ret;
+}
+EXPORT_SYMBOL_GPL(br_multicast_has_querier_adjacent);
index 2acf7fa1fec6c2309123dc189ea964f66f230d07..a615264cf01a950aafd894109d43f55cfd8dff91 100644 (file)
@@ -535,7 +535,7 @@ static struct net_device *brnf_get_logical_dev(struct sk_buff *skb, const struct
        if (brnf_pass_vlan_indev == 0 || !vlan_tx_tag_present(skb))
                return br;
 
-       vlan = __vlan_find_dev_deep(br, skb->vlan_proto,
+       vlan = __vlan_find_dev_deep_rcu(br, skb->vlan_proto,
                                    vlan_tx_tag_get(skb) & VLAN_VID_MASK);
 
        return vlan ? vlan : br;
index e8844d975b321ac4e3f6a0cca76035918ec93a1b..26edb518b839b38240ab1de7cb619cd5ba924b6f 100644 (file)
@@ -328,6 +328,7 @@ static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
 static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
 {
        int err;
+       unsigned long old_flags = p->flags;
 
        br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
        br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD);
@@ -353,6 +354,8 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
                if (err)
                        return err;
        }
+
+       br_port_flags_change(p, old_flags ^ p->flags);
        return 0;
 }
 
diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c
deleted file mode 100644 (file)
index 2998dd1..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- *     Device event handling
- *     Linux ethernet bridge
- *
- *     Authors:
- *     Lennert Buytenhek               <buytenh@gnu.org>
- *
- *     This program is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License
- *     as published by the Free Software Foundation; either version
- *     2 of the License, or (at your option) any later version.
- */
-
-#include <linux/kernel.h>
-#include <linux/rtnetlink.h>
-#include <net/net_namespace.h>
-
-#include "br_private.h"
-
-static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr);
-
-struct notifier_block br_device_notifier = {
-       .notifier_call = br_device_event
-};
-
-/*
- * Handle changes in state of network devices enslaved to a bridge.
- *
- * Note: don't care about up/down if bridge itself is down, because
- *     port state is checked when bridge is brought up.
- */
-static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
-{
-       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
-       struct net_bridge_port *p;
-       struct net_bridge *br;
-       bool changed_addr;
-       int err;
-
-       /* register of bridge completed, add sysfs entries */
-       if ((dev->priv_flags & IFF_EBRIDGE) && event == NETDEV_REGISTER) {
-               br_sysfs_addbr(dev);
-               return NOTIFY_DONE;
-       }
-
-       /* not a port of a bridge */
-       p = br_port_get_rtnl(dev);
-       if (!p)
-               return NOTIFY_DONE;
-
-       br = p->br;
-
-       switch (event) {
-       case NETDEV_CHANGEMTU:
-               dev_set_mtu(br->dev, br_min_mtu(br));
-               break;
-
-       case NETDEV_CHANGEADDR:
-               spin_lock_bh(&br->lock);
-               br_fdb_changeaddr(p, dev->dev_addr);
-               changed_addr = br_stp_recalculate_bridge_id(br);
-               spin_unlock_bh(&br->lock);
-
-               if (changed_addr)
-                       call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev);
-
-               break;
-
-       case NETDEV_CHANGE:
-               br_port_carrier_check(p);
-               break;
-
-       case NETDEV_FEAT_CHANGE:
-               netdev_update_features(br->dev);
-               break;
-
-       case NETDEV_DOWN:
-               spin_lock_bh(&br->lock);
-               if (br->dev->flags & IFF_UP)
-                       br_stp_disable_port(p);
-               spin_unlock_bh(&br->lock);
-               break;
-
-       case NETDEV_UP:
-               if (netif_running(br->dev) && netif_oper_up(dev)) {
-                       spin_lock_bh(&br->lock);
-                       br_stp_enable_port(p);
-                       spin_unlock_bh(&br->lock);
-               }
-               break;
-
-       case NETDEV_UNREGISTER:
-               br_del_if(br, dev);
-               break;
-
-       case NETDEV_CHANGENAME:
-               err = br_sysfs_renameif(p);
-               if (err)
-                       return notifier_from_errno(err);
-               break;
-
-       case NETDEV_PRE_TYPE_CHANGE:
-               /* Forbid underlaying device to change its type. */
-               return NOTIFY_BAD;
-
-       case NETDEV_RESEND_IGMP:
-               /* Propagate to master device */
-               call_netdevice_notifiers(event, br->dev);
-               break;
-       }
-
-       /* Events that may cause spanning tree to refresh */
-       if (event == NETDEV_CHANGEADDR || event == NETDEV_UP ||
-           event == NETDEV_CHANGE || event == NETDEV_DOWN)
-               br_ifinfo_notify(RTM_NEWLINK, p);
-
-       return NOTIFY_DONE;
-}
index 59d3a85c58736b14d5fb43d214d4af1ee7e29954..23caf5b0309efe9e37b5d2b7bace5e1a9a75bf82 100644 (file)
@@ -35,6 +35,8 @@
 #define BR_GROUPFWD_DEFAULT    0
 /* Don't allow forwarding control protocols like STP and LLDP */
 #define BR_GROUPFWD_RESTRICTED 0x4007u
+/* The Nearest Customer Bridge Group Address, 01-80-C2-00-00-[00,0B,0C,0D,0F] */
+#define BR_GROUPFWD_8021AD     0xB801u
 
 /* Path to usermode spanning tree program */
 #define BR_STP_PROG    "/sbin/bridge-stp"
@@ -54,30 +56,24 @@ struct mac_addr
        unsigned char   addr[ETH_ALEN];
 };
 
-struct br_ip
-{
-       union {
-               __be32  ip4;
-#if IS_ENABLED(CONFIG_IPV6)
-               struct in6_addr ip6;
-#endif
-       } u;
-       __be16          proto;
-       __u16           vid;
-};
-
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
 /* our own querier */
-struct bridge_mcast_query {
+struct bridge_mcast_own_query {
        struct timer_list       timer;
        u32                     startup_sent;
 };
 
 /* other querier */
-struct bridge_mcast_querier {
+struct bridge_mcast_other_query {
        struct timer_list               timer;
        unsigned long                   delay_time;
 };
+
+/* selected querier */
+struct bridge_mcast_querier {
+       struct br_ip addr;
+       struct net_bridge_port __rcu    *port;
+};
 #endif
 
 struct net_port_vlans {
@@ -174,11 +170,13 @@ struct net_bridge_port
 #define BR_ADMIN_COST          0x00000010
 #define BR_LEARNING            0x00000020
 #define BR_FLOOD               0x00000040
+#define BR_AUTO_MASK (BR_FLOOD | BR_LEARNING)
+#define BR_PROMISC             0x00000080
 
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
-       struct bridge_mcast_query       ip4_query;
+       struct bridge_mcast_own_query   ip4_own_query;
 #if IS_ENABLED(CONFIG_IPV6)
-       struct bridge_mcast_query       ip6_query;
+       struct bridge_mcast_own_query   ip6_own_query;
 #endif /* IS_ENABLED(CONFIG_IPV6) */
        unsigned char                   multicast_router;
        struct timer_list               multicast_router_timer;
@@ -198,6 +196,9 @@ struct net_bridge_port
 #endif
 };
 
+#define br_auto_port(p) ((p)->flags & BR_AUTO_MASK)
+#define br_promisc_port(p) ((p)->flags & BR_PROMISC)
+
 #define br_port_exists(dev) (dev->priv_flags & IFF_BRIDGE_PORT)
 
 static inline struct net_bridge_port *br_port_get_rcu(const struct net_device *dev)
@@ -227,6 +228,7 @@ struct net_bridge
        bool                            nf_call_arptables;
 #endif
        u16                             group_fwd_mask;
+       u16                             group_fwd_mask_required;
 
        /* STP */
        bridge_id                       designated_root;
@@ -241,6 +243,7 @@ struct net_bridge
        unsigned long                   bridge_forward_delay;
 
        u8                              group_addr[ETH_ALEN];
+       bool                            group_addr_set;
        u16                             root_port;
 
        enum {
@@ -277,11 +280,13 @@ struct net_bridge
        struct hlist_head               router_list;
 
        struct timer_list               multicast_router_timer;
+       struct bridge_mcast_other_query ip4_other_query;
+       struct bridge_mcast_own_query   ip4_own_query;
        struct bridge_mcast_querier     ip4_querier;
-       struct bridge_mcast_query       ip4_query;
 #if IS_ENABLED(CONFIG_IPV6)
+       struct bridge_mcast_other_query ip6_other_query;
+       struct bridge_mcast_own_query   ip6_own_query;
        struct bridge_mcast_querier     ip6_querier;
-       struct bridge_mcast_query       ip6_query;
 #endif /* IS_ENABLED(CONFIG_IPV6) */
 #endif
 
@@ -290,8 +295,10 @@ struct net_bridge
        struct timer_list               topology_change_timer;
        struct timer_list               gc_timer;
        struct kobject                  *ifobj;
+       u32                             auto_cnt;
 #ifdef CONFIG_BRIDGE_VLAN_FILTERING
        u8                              vlan_enabled;
+       __be16                          vlan_proto;
        struct net_port_vlans __rcu     *vlan_info;
 #endif
 };
@@ -327,8 +334,6 @@ struct br_input_skb_cb {
 #define br_debug(br, format, args...)                  \
        pr_debug("%s: " format,  (br)->dev->name, ##args)
 
-extern struct notifier_block br_device_notifier;
-
 /* called under bridge lock */
 static inline int br_is_root_bridge(const struct net_bridge *br)
 {
@@ -395,6 +400,8 @@ 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);
+int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p);
+void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p);
 
 /* br_forward.c */
 void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
@@ -415,6 +422,8 @@ 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);
+void br_port_flags_change(struct net_bridge_port *port, unsigned long mask);
+void br_manage_promisc(struct net_bridge *br);
 
 /* br_input.c */
 int br_handle_frame_finish(struct sk_buff *skb);
@@ -485,7 +494,7 @@ static inline bool br_multicast_is_router(struct net_bridge *br)
 
 static inline bool
 __br_multicast_querier_exists(struct net_bridge *br,
-                             struct bridge_mcast_querier *querier)
+                             struct bridge_mcast_other_query *querier)
 {
        return time_is_before_jiffies(querier->delay_time) &&
               (br->multicast_querier || timer_pending(&querier->timer));
@@ -496,10 +505,10 @@ static inline bool br_multicast_querier_exists(struct net_bridge *br,
 {
        switch (eth->h_proto) {
        case (htons(ETH_P_IP)):
-               return __br_multicast_querier_exists(br, &br->ip4_querier);
+               return __br_multicast_querier_exists(br, &br->ip4_other_query);
 #if IS_ENABLED(CONFIG_IPV6)
        case (htons(ETH_P_IPV6)):
-               return __br_multicast_querier_exists(br, &br->ip6_querier);
+               return __br_multicast_querier_exists(br, &br->ip6_other_query);
 #endif
        default:
                return false;
@@ -589,7 +598,10 @@ 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);
 bool br_vlan_find(struct net_bridge *br, u16 vid);
+void br_recalculate_fwd_mask(struct net_bridge *br);
 int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val);
+int br_vlan_set_proto(struct net_bridge *br, unsigned long val);
+void br_vlan_init(struct net_bridge *br);
 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);
@@ -633,6 +645,10 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
        return v->pvid ?: VLAN_N_VID;
 }
 
+static inline int br_vlan_enabled(struct net_bridge *br)
+{
+       return br->vlan_enabled;
+}
 #else
 static inline bool br_allowed_ingress(struct net_bridge *br,
                                      struct net_port_vlans *v,
@@ -681,6 +697,14 @@ static inline bool br_vlan_find(struct net_bridge *br, u16 vid)
        return false;
 }
 
+static inline void br_recalculate_fwd_mask(struct net_bridge *br)
+{
+}
+
+static inline void br_vlan_init(struct net_bridge *br)
+{
+}
+
 static inline int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags)
 {
        return -EOPNOTSUPP;
@@ -719,6 +743,11 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v)
 {
        return VLAN_N_VID;      /* Returns invalid vid */
 }
+
+static inline int br_vlan_enabled(struct net_bridge *br)
+{
+       return 0;
+}
 #endif
 
 /* br_netfilter.c */
index 8dac65552f1901817489421c2042553a663cbaf9..c9e2572b15f400f9183606922a87f024ffe6570f 100644 (file)
@@ -312,10 +312,19 @@ static ssize_t group_addr_store(struct device *d,
            new_addr[5] == 3)           /* 802.1X PAE address */
                return -EINVAL;
 
+       if (!rtnl_trylock())
+               return restart_syscall();
+
        spin_lock_bh(&br->lock);
        for (i = 0; i < 6; i++)
                br->group_addr[i] = new_addr[i];
        spin_unlock_bh(&br->lock);
+
+       br->group_addr_set = true;
+       br_recalculate_fwd_mask(br);
+
+       rtnl_unlock();
+
        return len;
 }
 
@@ -700,6 +709,22 @@ static ssize_t vlan_filtering_store(struct device *d,
        return store_bridge_parm(d, buf, len, br_vlan_filter_toggle);
 }
 static DEVICE_ATTR_RW(vlan_filtering);
+
+static ssize_t vlan_protocol_show(struct device *d,
+                                 struct device_attribute *attr,
+                                 char *buf)
+{
+       struct net_bridge *br = to_bridge(d);
+       return sprintf(buf, "%#06x\n", ntohs(br->vlan_proto));
+}
+
+static ssize_t vlan_protocol_store(struct device *d,
+                                  struct device_attribute *attr,
+                                  const char *buf, size_t len)
+{
+       return store_bridge_parm(d, buf, len, br_vlan_set_proto);
+}
+static DEVICE_ATTR_RW(vlan_protocol);
 #endif
 
 static struct attribute *bridge_attrs[] = {
@@ -745,6 +770,7 @@ static struct attribute *bridge_attrs[] = {
 #endif
 #ifdef CONFIG_BRIDGE_VLAN_FILTERING
        &dev_attr_vlan_filtering.attr,
+       &dev_attr_vlan_protocol.attr,
 #endif
        NULL
 };
index dd595bd7fa820444f6e8c424eb7dd4f307998af2..e561cd59b8a6ef0e764b3028d350b13954deac05 100644 (file)
@@ -41,20 +41,30 @@ static ssize_t show_##_name(struct net_bridge_port *p, char *buf) \
 }                                                              \
 static int store_##_name(struct net_bridge_port *p, unsigned long v) \
 {                                                              \
-       unsigned long flags = p->flags;                         \
-       if (v)                                                  \
-               flags |= _mask;                                 \
-       else                                                    \
-               flags &= ~_mask;                                \
-       if (flags != p->flags) {                                \
-               p->flags = flags;                               \
-               br_ifinfo_notify(RTM_NEWLINK, p);               \
-       }                                                       \
-       return 0;                                               \
+       return store_flag(p, v, _mask);                         \
 }                                                              \
 static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR,                   \
                   show_##_name, store_##_name)
 
+static int store_flag(struct net_bridge_port *p, unsigned long v,
+                     unsigned long mask)
+{
+       unsigned long flags;
+
+       flags = p->flags;
+
+       if (v)
+               flags |= mask;
+       else
+               flags &= ~mask;
+
+       if (flags != p->flags) {
+               p->flags = flags;
+               br_port_flags_change(p, mask);
+               br_ifinfo_notify(RTM_NEWLINK, p);
+       }
+       return 0;
+}
 
 static ssize_t show_path_cost(struct net_bridge_port *p, char *buf)
 {
index 5fee2feaf292fa76562a6e62ef2f29cb776d7b0c..2b2774fe0703871e7e4e65ee1d5816375c398e11 100644 (file)
@@ -60,7 +60,7 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
                 * that ever changes this code will allow tagged
                 * traffic to enter the bridge.
                 */
-               err = vlan_vid_add(dev, htons(ETH_P_8021Q), vid);
+               err = vlan_vid_add(dev, br->vlan_proto, vid);
                if (err)
                        return err;
        }
@@ -80,7 +80,7 @@ static int __vlan_add(struct net_port_vlans *v, u16 vid, u16 flags)
 
 out_filt:
        if (p)
-               vlan_vid_del(dev, htons(ETH_P_8021Q), vid);
+               vlan_vid_del(dev, br->vlan_proto, vid);
        return err;
 }
 
@@ -92,8 +92,10 @@ 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)
-               vlan_vid_del(v->parent.port->dev, htons(ETH_P_8021Q), vid);
+       if (v->port_idx) {
+               struct net_bridge_port *p = v->parent.port;
+               vlan_vid_del(p->dev, p->br->vlan_proto, vid);
+       }
 
        clear_bit(vid, v->vlan_bitmap);
        v->num_vlans--;
@@ -158,7 +160,8 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br,
 bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
                        struct sk_buff *skb, u16 *vid)
 {
-       int err;
+       bool tagged;
+       __be16 proto;
 
        /* If VLAN filtering is disabled on the bridge, all packets are
         * permitted.
@@ -172,19 +175,41 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
        if (!v)
                goto drop;
 
+       proto = br->vlan_proto;
+
        /* If vlan tx offload is disabled on bridge device and frame was
         * sent from vlan device on the bridge device, it does not have
         * HW accelerated vlan tag.
         */
        if (unlikely(!vlan_tx_tag_present(skb) &&
-                    (skb->protocol == htons(ETH_P_8021Q) ||
-                     skb->protocol == htons(ETH_P_8021AD)))) {
+                    skb->protocol == proto)) {
                skb = vlan_untag(skb);
                if (unlikely(!skb))
                        return false;
        }
 
-       err = br_vlan_get_tag(skb, vid);
+       if (!br_vlan_get_tag(skb, vid)) {
+               /* Tagged frame */
+               if (skb->vlan_proto != proto) {
+                       /* Protocol-mismatch, empty out vlan_tci for new tag */
+                       skb_push(skb, ETH_HLEN);
+                       skb = __vlan_put_tag(skb, skb->vlan_proto,
+                                            vlan_tx_tag_get(skb));
+                       if (unlikely(!skb))
+                               return false;
+
+                       skb_pull(skb, ETH_HLEN);
+                       skb_reset_mac_len(skb);
+                       *vid = 0;
+                       tagged = false;
+               } else {
+                       tagged = true;
+               }
+       } else {
+               /* Untagged frame */
+               tagged = false;
+       }
+
        if (!*vid) {
                u16 pvid = br_get_pvid(v);
 
@@ -199,9 +224,9 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
                 * ingress frame is considered to belong to this vlan.
                 */
                *vid = pvid;
-               if (likely(err))
+               if (likely(!tagged))
                        /* Untagged Frame. */
-                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), pvid);
+                       __vlan_hwaccel_put_tag(skb, proto, pvid);
                else
                        /* Priority-tagged Frame.
                         * At this point, We know that skb->vlan_tci had
@@ -254,7 +279,9 @@ bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid)
        if (!v)
                return false;
 
-       br_vlan_get_tag(skb, vid);
+       if (!br_vlan_get_tag(skb, vid) && skb->vlan_proto != br->vlan_proto)
+               *vid = 0;
+
        if (!*vid) {
                *vid = br_get_pvid(v);
                if (*vid == VLAN_N_VID)
@@ -351,6 +378,33 @@ bool br_vlan_find(struct net_bridge *br, u16 vid)
        return found;
 }
 
+/* Must be protected by RTNL. */
+static void recalculate_group_addr(struct net_bridge *br)
+{
+       if (br->group_addr_set)
+               return;
+
+       spin_lock_bh(&br->lock);
+       if (!br->vlan_enabled || br->vlan_proto == htons(ETH_P_8021Q)) {
+               /* Bridge Group Address */
+               br->group_addr[5] = 0x00;
+       } else { /* vlan_enabled && ETH_P_8021AD */
+               /* Provider Bridge Group Address */
+               br->group_addr[5] = 0x08;
+       }
+       spin_unlock_bh(&br->lock);
+}
+
+/* Must be protected by RTNL. */
+void br_recalculate_fwd_mask(struct net_bridge *br)
+{
+       if (!br->vlan_enabled || br->vlan_proto == htons(ETH_P_8021Q))
+               br->group_fwd_mask_required = BR_GROUPFWD_DEFAULT;
+       else /* vlan_enabled && ETH_P_8021AD */
+               br->group_fwd_mask_required = BR_GROUPFWD_8021AD &
+                                             ~(1u << br->group_addr[5]);
+}
+
 int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
 {
        if (!rtnl_trylock())
@@ -360,12 +414,88 @@ int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
                goto unlock;
 
        br->vlan_enabled = val;
+       br_manage_promisc(br);
+       recalculate_group_addr(br);
+       br_recalculate_fwd_mask(br);
 
 unlock:
        rtnl_unlock();
        return 0;
 }
 
+int br_vlan_set_proto(struct net_bridge *br, unsigned long val)
+{
+       int err = 0;
+       struct net_bridge_port *p;
+       struct net_port_vlans *pv;
+       __be16 proto, oldproto;
+       u16 vid, errvid;
+
+       if (val != ETH_P_8021Q && val != ETH_P_8021AD)
+               return -EPROTONOSUPPORT;
+
+       if (!rtnl_trylock())
+               return restart_syscall();
+
+       proto = htons(val);
+       if (br->vlan_proto == proto)
+               goto unlock;
+
+       /* Add VLANs for the new proto to the device filter. */
+       list_for_each_entry(p, &br->port_list, list) {
+               pv = rtnl_dereference(p->vlan_info);
+               if (!pv)
+                       continue;
+
+               for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
+                       err = vlan_vid_add(p->dev, proto, vid);
+                       if (err)
+                               goto err_filt;
+               }
+       }
+
+       oldproto = br->vlan_proto;
+       br->vlan_proto = proto;
+
+       recalculate_group_addr(br);
+       br_recalculate_fwd_mask(br);
+
+       /* Delete VLANs for the old proto from the device filter. */
+       list_for_each_entry(p, &br->port_list, list) {
+               pv = rtnl_dereference(p->vlan_info);
+               if (!pv)
+                       continue;
+
+               for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID)
+                       vlan_vid_del(p->dev, oldproto, vid);
+       }
+
+unlock:
+       rtnl_unlock();
+       return err;
+
+err_filt:
+       errvid = vid;
+       for_each_set_bit(vid, pv->vlan_bitmap, errvid)
+               vlan_vid_del(p->dev, proto, vid);
+
+       list_for_each_entry_continue_reverse(p, &br->port_list, list) {
+               pv = rtnl_dereference(p->vlan_info);
+               if (!pv)
+                       continue;
+
+               for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID)
+                       vlan_vid_del(p->dev, proto, vid);
+       }
+
+       goto unlock;
+}
+
+void br_vlan_init(struct net_bridge *br)
+{
+       br->vlan_proto = htons(ETH_P_8021Q);
+}
+
 /* Must be protected by RTNL.
  * Must be called with vid in range from 1 to 4094 inclusive.
  */
@@ -432,7 +562,7 @@ void nbp_vlan_flush(struct net_bridge_port *port)
                return;
 
        for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID)
-               vlan_vid_del(port->dev, htons(ETH_P_8021Q), vid);
+               vlan_vid_del(port->dev, port->br->vlan_proto, vid);
 
        __vlan_flush(pv);
 }
index 5ca74a0e595fe5ce782985f6b2e17f185b5a52ec..629dc77874a9975feed56bb7b6abefad76cfad94 100644 (file)
@@ -2,14 +2,23 @@
 # Bridge netfilter configuration
 #
 #
-config NF_TABLES_BRIDGE
-       depends on NF_TABLES
+menuconfig NF_TABLES_BRIDGE
+       depends on BRIDGE && NETFILTER && NF_TABLES
        tristate "Ethernet Bridge nf_tables support"
 
+if NF_TABLES_BRIDGE
+
+config NFT_BRIDGE_META
+       tristate "Netfilter nf_table bridge meta support"
+       depends on NFT_META
+       help
+         Add support for bridge dedicated meta key.
+
+endif # NF_TABLES_BRIDGE
+
 menuconfig BRIDGE_NF_EBTABLES
        tristate "Ethernet Bridge tables (ebtables) support"
-       depends on BRIDGE && NETFILTER
-       select NETFILTER_XTABLES
+       depends on BRIDGE && NETFILTER && NETFILTER_XTABLES
        help
          ebtables is a general, extensible frame/packet identification
          framework. Say 'Y' or 'M' here if you want to do Ethernet
index ea7629f58b3d1c44e28524df8a0937de3a18546b..6f2f3943d66f34b43c72be21b603bbf51ba0d289 100644 (file)
@@ -3,6 +3,7 @@
 #
 
 obj-$(CONFIG_NF_TABLES_BRIDGE) += nf_tables_bridge.o
+obj-$(CONFIG_NFT_BRIDGE_META)  += nft_meta_bridge.o
 
 obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
 
diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c
new file mode 100644 (file)
index 0000000..4f02109
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2014 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.
+ *
+ */
+
+#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/nft_meta.h>
+
+#include "../br_private.h"
+
+static void nft_meta_bridge_get_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 net_device *in = pkt->in, *out = pkt->out;
+       struct nft_data *dest = &data[priv->dreg];
+       const struct net_bridge_port *p;
+
+       switch (priv->key) {
+       case NFT_META_BRI_IIFNAME:
+               if (in == NULL || (p = br_port_get_rcu(in)) == NULL)
+                       goto err;
+               break;
+       case NFT_META_BRI_OIFNAME:
+               if (out == NULL || (p = br_port_get_rcu(out)) == NULL)
+                       goto err;
+               break;
+       default:
+               goto out;
+       }
+
+       strncpy((char *)dest->data, p->br->dev->name, sizeof(dest->data));
+       return;
+out:
+       return nft_meta_get_eval(expr, data, pkt);
+err:
+       data[NFT_REG_VERDICT].verdict = NFT_BREAK;
+}
+
+static int nft_meta_bridge_get_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;
+
+       priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
+       switch (priv->key) {
+       case NFT_META_BRI_IIFNAME:
+       case NFT_META_BRI_OIFNAME:
+               break;
+       default:
+               return nft_meta_get_init(ctx, expr, tb);
+       }
+
+       priv->dreg = ntohl(nla_get_be32(tb[NFTA_META_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;
+
+       return 0;
+}
+
+static struct nft_expr_type nft_meta_bridge_type;
+static const struct nft_expr_ops nft_meta_bridge_get_ops = {
+       .type           = &nft_meta_bridge_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
+       .eval           = nft_meta_bridge_get_eval,
+       .init           = nft_meta_bridge_get_init,
+       .dump           = nft_meta_get_dump,
+};
+
+static const struct nft_expr_ops nft_meta_bridge_set_ops = {
+       .type           = &nft_meta_bridge_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
+       .eval           = nft_meta_set_eval,
+       .init           = nft_meta_set_init,
+       .dump           = nft_meta_set_dump,
+};
+
+static const struct nft_expr_ops *
+nft_meta_bridge_select_ops(const struct nft_ctx *ctx,
+                          const struct nlattr * const tb[])
+{
+       if (tb[NFTA_META_KEY] == NULL)
+               return ERR_PTR(-EINVAL);
+
+       if (tb[NFTA_META_DREG] && tb[NFTA_META_SREG])
+               return ERR_PTR(-EINVAL);
+
+       if (tb[NFTA_META_DREG])
+               return &nft_meta_bridge_get_ops;
+
+       if (tb[NFTA_META_SREG])
+               return &nft_meta_bridge_set_ops;
+
+       return ERR_PTR(-EINVAL);
+}
+
+static struct nft_expr_type nft_meta_bridge_type __read_mostly = {
+       .family         = NFPROTO_BRIDGE,
+       .name           = "meta",
+       .select_ops     = &nft_meta_bridge_select_ops,
+       .policy         = nft_meta_policy,
+       .maxattr        = NFTA_META_MAX,
+       .owner          = THIS_MODULE,
+};
+
+static int __init nft_meta_bridge_module_init(void)
+{
+       return nft_register_expr(&nft_meta_bridge_type);
+}
+
+static void __exit nft_meta_bridge_module_exit(void)
+{
+       nft_unregister_expr(&nft_meta_bridge_type);
+}
+
+module_init(nft_meta_bridge_module_init);
+module_exit(nft_meta_bridge_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>");
+MODULE_ALIAS_NFT_AF_EXPR(AF_BRIDGE, "meta");
index a27f8aad9e991f95cc5366bce3e975bff4f16bdd..ce82337521f665c5847819402d8a9c167452fb90 100644 (file)
@@ -337,6 +337,29 @@ static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
                return (struct dev_rcv_lists *)dev->ml_priv;
 }
 
+/**
+ * effhash - hash function for 29 bit CAN identifier reduction
+ * @can_id: 29 bit CAN identifier
+ *
+ * Description:
+ *  To reduce the linear traversal in one linked list of _single_ EFF CAN
+ *  frame subscriptions the 29 bit identifier is mapped to 10 bits.
+ *  (see CAN_EFF_RCV_HASH_BITS definition)
+ *
+ * Return:
+ *  Hash value from 0x000 - 0x3FF ( enforced by CAN_EFF_RCV_HASH_BITS mask )
+ */
+static unsigned int effhash(canid_t can_id)
+{
+       unsigned int hash;
+
+       hash = can_id;
+       hash ^= can_id >> CAN_EFF_RCV_HASH_BITS;
+       hash ^= can_id >> (2 * CAN_EFF_RCV_HASH_BITS);
+
+       return hash & ((1 << CAN_EFF_RCV_HASH_BITS) - 1);
+}
+
 /**
  * find_rcv_list - determine optimal filterlist inside device filter struct
  * @can_id: pointer to CAN identifier of a given can_filter
@@ -400,10 +423,8 @@ static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask,
            !(*can_id & CAN_RTR_FLAG)) {
 
                if (*can_id & CAN_EFF_FLAG) {
-                       if (*mask == (CAN_EFF_MASK | CAN_EFF_RTR_FLAGS)) {
-                               /* RFC: a future use-case for hash-tables? */
-                               return &d->rx[RX_EFF];
-                       }
+                       if (*mask == (CAN_EFF_MASK | CAN_EFF_RTR_FLAGS))
+                               return &d->rx_eff[effhash(*can_id)];
                } else {
                        if (*mask == (CAN_SFF_MASK | CAN_EFF_RTR_FLAGS))
                                return &d->rx_sff[*can_id];
@@ -632,7 +653,7 @@ static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
                return matches;
 
        if (can_id & CAN_EFF_FLAG) {
-               hlist_for_each_entry_rcu(r, &d->rx[RX_EFF], list) {
+               hlist_for_each_entry_rcu(r, &d->rx_eff[effhash(can_id)], list) {
                        if (r->can_id == can_id) {
                                deliver(skb, r);
                                matches++;
index 6de58b40535cc309e59f1a61ed624c930b7c7ffe..fca0fe9fc45a497cdf3da82d5414e846e7cc61b7 100644 (file)
@@ -59,12 +59,17 @@ struct receiver {
        char *ident;
 };
 
-enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_EFF, RX_MAX };
+#define CAN_SFF_RCV_ARRAY_SZ (1 << CAN_SFF_ID_BITS)
+#define CAN_EFF_RCV_HASH_BITS 10
+#define CAN_EFF_RCV_ARRAY_SZ (1 << CAN_EFF_RCV_HASH_BITS)
+
+enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_MAX };
 
 /* per device receive filters linked at dev->ml_priv */
 struct dev_rcv_lists {
        struct hlist_head rx[RX_MAX];
-       struct hlist_head rx_sff[0x800];
+       struct hlist_head rx_sff[CAN_SFF_RCV_ARRAY_SZ];
+       struct hlist_head rx_eff[CAN_EFF_RCV_ARRAY_SZ];
        int remove_on_zero_entries;
        int entries;
 };
index b543470c8f8b5ef7e5196b7f895cf30a9b0db90e..1a19b985a8685b0aff4450acda4a8d4f429568bc 100644 (file)
@@ -80,7 +80,6 @@ static const char rx_list_name[][8] = {
        [RX_ALL] = "rx_all",
        [RX_FIL] = "rx_fil",
        [RX_INV] = "rx_inv",
-       [RX_EFF] = "rx_eff",
 };
 
 /*
@@ -389,25 +388,26 @@ static const struct file_operations can_rcvlist_proc_fops = {
        .release        = single_release,
 };
 
-static inline void can_rcvlist_sff_proc_show_one(struct seq_file *m,
-                                                struct net_device *dev,
-                                                struct dev_rcv_lists *d)
+static inline void can_rcvlist_proc_show_array(struct seq_file *m,
+                                              struct net_device *dev,
+                                              struct hlist_head *rcv_array,
+                                              unsigned int rcv_array_sz)
 {
-       int i;
+       unsigned int i;
        int all_empty = 1;
 
        /* check whether at least one list is non-empty */
-       for (i = 0; i < 0x800; i++)
-               if (!hlist_empty(&d->rx_sff[i])) {
+       for (i = 0; i < rcv_array_sz; i++)
+               if (!hlist_empty(&rcv_array[i])) {
                        all_empty = 0;
                        break;
                }
 
        if (!all_empty) {
                can_print_recv_banner(m);
-               for (i = 0; i < 0x800; i++) {
-                       if (!hlist_empty(&d->rx_sff[i]))
-                               can_print_rcvlist(m, &d->rx_sff[i], dev);
+               for (i = 0; i < rcv_array_sz; i++) {
+                       if (!hlist_empty(&rcv_array[i]))
+                               can_print_rcvlist(m, &rcv_array[i], dev);
                }
        } else
                seq_printf(m, "  (%s: no entry)\n", DNAME(dev));
@@ -425,12 +425,15 @@ static int can_rcvlist_sff_proc_show(struct seq_file *m, void *v)
 
        /* sff receive list for 'all' CAN devices (dev == NULL) */
        d = &can_rx_alldev_list;
-       can_rcvlist_sff_proc_show_one(m, NULL, d);
+       can_rcvlist_proc_show_array(m, NULL, d->rx_sff, ARRAY_SIZE(d->rx_sff));
 
        /* sff receive list for registered CAN devices */
        for_each_netdev_rcu(&init_net, dev) {
-               if (dev->type == ARPHRD_CAN && dev->ml_priv)
-                       can_rcvlist_sff_proc_show_one(m, dev, dev->ml_priv);
+               if (dev->type == ARPHRD_CAN && dev->ml_priv) {
+                       d = dev->ml_priv;
+                       can_rcvlist_proc_show_array(m, dev, d->rx_sff,
+                                                   ARRAY_SIZE(d->rx_sff));
+               }
        }
 
        rcu_read_unlock();
@@ -452,6 +455,49 @@ static const struct file_operations can_rcvlist_sff_proc_fops = {
        .release        = single_release,
 };
 
+
+static int can_rcvlist_eff_proc_show(struct seq_file *m, void *v)
+{
+       struct net_device *dev;
+       struct dev_rcv_lists *d;
+
+       /* RX_EFF */
+       seq_puts(m, "\nreceive list 'rx_eff':\n");
+
+       rcu_read_lock();
+
+       /* eff receive list for 'all' CAN devices (dev == NULL) */
+       d = &can_rx_alldev_list;
+       can_rcvlist_proc_show_array(m, NULL, d->rx_eff, ARRAY_SIZE(d->rx_eff));
+
+       /* eff receive list for registered CAN devices */
+       for_each_netdev_rcu(&init_net, dev) {
+               if (dev->type == ARPHRD_CAN && dev->ml_priv) {
+                       d = dev->ml_priv;
+                       can_rcvlist_proc_show_array(m, dev, d->rx_eff,
+                                                   ARRAY_SIZE(d->rx_eff));
+               }
+       }
+
+       rcu_read_unlock();
+
+       seq_putc(m, '\n');
+       return 0;
+}
+
+static int can_rcvlist_eff_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, can_rcvlist_eff_proc_show, NULL);
+}
+
+static const struct file_operations can_rcvlist_eff_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = can_rcvlist_eff_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
 /*
  * proc utility functions
  */
@@ -491,8 +537,8 @@ void can_init_proc(void)
                                           &can_rcvlist_proc_fops, (void *)RX_FIL);
        pde_rcvlist_inv = proc_create_data(CAN_PROC_RCVLIST_INV, 0644, can_dir,
                                           &can_rcvlist_proc_fops, (void *)RX_INV);
-       pde_rcvlist_eff = proc_create_data(CAN_PROC_RCVLIST_EFF, 0644, can_dir,
-                                          &can_rcvlist_proc_fops, (void *)RX_EFF);
+       pde_rcvlist_eff = proc_create(CAN_PROC_RCVLIST_EFF, 0644, can_dir,
+                                     &can_rcvlist_eff_proc_fops);
        pde_rcvlist_sff = proc_create(CAN_PROC_RCVLIST_SFF, 0644, can_dir,
                                      &can_rcvlist_sff_proc_fops);
 }
index 67d7721d237e1638c21bc62da98f7275a9991026..1675021d8c12cf6e941720802553f942171eb79b 100644 (file)
@@ -72,6 +72,8 @@ const char *ceph_msg_type_name(int type)
        case CEPH_MSG_MON_SUBSCRIBE_ACK: return "mon_subscribe_ack";
        case CEPH_MSG_STATFS: return "statfs";
        case CEPH_MSG_STATFS_REPLY: return "statfs_reply";
+       case CEPH_MSG_MON_GET_VERSION: return "mon_get_version";
+       case CEPH_MSG_MON_GET_VERSION_REPLY: return "mon_get_version_reply";
        case CEPH_MSG_MDS_MAP: return "mds_map";
        case CEPH_MSG_CLIENT_SESSION: return "client_session";
        case CEPH_MSG_CLIENT_RECONNECT: return "client_reconnect";
index 10421a4b76f8710def7328e45dabf227cc3cdb3b..d1a62c69a9f4fd1cff25ca458a66d8e2c6093099 100644 (file)
@@ -126,9 +126,13 @@ static int monc_show(struct seq_file *s, void *p)
                req = rb_entry(rp, struct ceph_mon_generic_request, node);
                op = le16_to_cpu(req->request->hdr.type);
                if (op == CEPH_MSG_STATFS)
-                       seq_printf(s, "%lld statfs\n", req->tid);
+                       seq_printf(s, "%llu statfs\n", req->tid);
+               else if (op == CEPH_MSG_POOLOP)
+                       seq_printf(s, "%llu poolop\n", req->tid);
+               else if (op == CEPH_MSG_MON_GET_VERSION)
+                       seq_printf(s, "%llu mon_get_version", req->tid);
                else
-                       seq_printf(s, "%lld unknown\n", req->tid);
+                       seq_printf(s, "%llu unknown\n", req->tid);
        }
 
        mutex_unlock(&monc->mutex);
index 2ac9ef35110b3e9ea07363d6f2d3c0eb2e4ee27b..067d3af2eaf61be41d601dc4324fc1a1565ab27d 100644 (file)
@@ -296,6 +296,33 @@ void ceph_monc_request_next_osdmap(struct ceph_mon_client *monc)
                __send_subscribe(monc);
        mutex_unlock(&monc->mutex);
 }
+EXPORT_SYMBOL(ceph_monc_request_next_osdmap);
+
+int ceph_monc_wait_osdmap(struct ceph_mon_client *monc, u32 epoch,
+                         unsigned long timeout)
+{
+       unsigned long started = jiffies;
+       int ret;
+
+       mutex_lock(&monc->mutex);
+       while (monc->have_osdmap < epoch) {
+               mutex_unlock(&monc->mutex);
+
+               if (timeout != 0 && time_after_eq(jiffies, started + timeout))
+                       return -ETIMEDOUT;
+
+               ret = wait_event_interruptible_timeout(monc->client->auth_wq,
+                                        monc->have_osdmap >= epoch, timeout);
+               if (ret < 0)
+                       return ret;
+
+               mutex_lock(&monc->mutex);
+       }
+
+       mutex_unlock(&monc->mutex);
+       return 0;
+}
+EXPORT_SYMBOL(ceph_monc_wait_osdmap);
 
 /*
  *
@@ -477,14 +504,13 @@ static struct ceph_msg *get_generic_reply(struct ceph_connection *con,
        return m;
 }
 
-static int do_generic_request(struct ceph_mon_client *monc,
-                             struct ceph_mon_generic_request *req)
+static int __do_generic_request(struct ceph_mon_client *monc, u64 tid,
+                               struct ceph_mon_generic_request *req)
 {
        int err;
 
        /* register request */
-       mutex_lock(&monc->mutex);
-       req->tid = ++monc->last_tid;
+       req->tid = tid != 0 ? tid : ++monc->last_tid;
        req->request->hdr.tid = cpu_to_le64(req->tid);
        __insert_generic_request(monc, req);
        monc->num_generic_requests++;
@@ -496,13 +522,24 @@ static int do_generic_request(struct ceph_mon_client *monc,
        mutex_lock(&monc->mutex);
        rb_erase(&req->node, &monc->generic_request_tree);
        monc->num_generic_requests--;
-       mutex_unlock(&monc->mutex);
 
        if (!err)
                err = req->result;
        return err;
 }
 
+static int do_generic_request(struct ceph_mon_client *monc,
+                             struct ceph_mon_generic_request *req)
+{
+       int err;
+
+       mutex_lock(&monc->mutex);
+       err = __do_generic_request(monc, 0, req);
+       mutex_unlock(&monc->mutex);
+
+       return err;
+}
+
 /*
  * statfs
  */
@@ -579,6 +616,96 @@ int ceph_monc_do_statfs(struct ceph_mon_client *monc, struct ceph_statfs *buf)
 }
 EXPORT_SYMBOL(ceph_monc_do_statfs);
 
+static void handle_get_version_reply(struct ceph_mon_client *monc,
+                                    struct ceph_msg *msg)
+{
+       struct ceph_mon_generic_request *req;
+       u64 tid = le64_to_cpu(msg->hdr.tid);
+       void *p = msg->front.iov_base;
+       void *end = p + msg->front_alloc_len;
+       u64 handle;
+
+       dout("%s %p tid %llu\n", __func__, msg, tid);
+
+       ceph_decode_need(&p, end, 2*sizeof(u64), bad);
+       handle = ceph_decode_64(&p);
+       if (tid != 0 && tid != handle)
+               goto bad;
+
+       mutex_lock(&monc->mutex);
+       req = __lookup_generic_req(monc, handle);
+       if (req) {
+               *(u64 *)req->buf = ceph_decode_64(&p);
+               req->result = 0;
+               get_generic_request(req);
+       }
+       mutex_unlock(&monc->mutex);
+       if (req) {
+               complete_all(&req->completion);
+               put_generic_request(req);
+       }
+
+       return;
+bad:
+       pr_err("corrupt mon_get_version reply\n");
+       ceph_msg_dump(msg);
+}
+
+/*
+ * Send MMonGetVersion and wait for the reply.
+ *
+ * @what: one of "mdsmap", "osdmap" or "monmap"
+ */
+int ceph_monc_do_get_version(struct ceph_mon_client *monc, const char *what,
+                            u64 *newest)
+{
+       struct ceph_mon_generic_request *req;
+       void *p, *end;
+       u64 tid;
+       int err;
+
+       req = kzalloc(sizeof(*req), GFP_NOFS);
+       if (!req)
+               return -ENOMEM;
+
+       kref_init(&req->kref);
+       req->buf = newest;
+       req->buf_len = sizeof(*newest);
+       init_completion(&req->completion);
+
+       req->request = ceph_msg_new(CEPH_MSG_MON_GET_VERSION,
+                                   sizeof(u64) + sizeof(u32) + strlen(what),
+                                   GFP_NOFS, true);
+       if (!req->request) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       req->reply = ceph_msg_new(CEPH_MSG_MON_GET_VERSION_REPLY, 1024,
+                                 GFP_NOFS, true);
+       if (!req->reply) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       p = req->request->front.iov_base;
+       end = p + req->request->front_alloc_len;
+
+       /* fill out request */
+       mutex_lock(&monc->mutex);
+       tid = ++monc->last_tid;
+       ceph_encode_64(&p, tid); /* handle */
+       ceph_encode_string(&p, end, what, strlen(what));
+
+       err = __do_generic_request(monc, tid, req);
+
+       mutex_unlock(&monc->mutex);
+out:
+       kref_put(&req->kref, release_generic_request);
+       return err;
+}
+EXPORT_SYMBOL(ceph_monc_do_get_version);
+
 /*
  * pool ops
  */
@@ -981,6 +1108,10 @@ static void dispatch(struct ceph_connection *con, struct ceph_msg *msg)
                handle_statfs_reply(monc, msg);
                break;
 
+       case CEPH_MSG_MON_GET_VERSION_REPLY:
+               handle_get_version_reply(monc, msg);
+               break;
+
        case CEPH_MSG_POOLOP_REPLY:
                handle_poolop_reply(monc, msg);
                break;
@@ -1029,6 +1160,15 @@ static struct ceph_msg *mon_alloc_msg(struct ceph_connection *con,
        case CEPH_MSG_AUTH_REPLY:
                m = ceph_msg_get(monc->m_auth_reply);
                break;
+       case CEPH_MSG_MON_GET_VERSION_REPLY:
+               if (le64_to_cpu(hdr->tid) != 0)
+                       return get_generic_reply(con, hdr, skip);
+
+               /*
+                * Older OSDs don't set reply tid even if the orignal
+                * request had a non-zero tid.  Workaround this weirdness
+                * by falling through to the allocate case.
+                */
        case CEPH_MSG_MON_MAP:
        case CEPH_MSG_MDS_MAP:
        case CEPH_MSG_OSD_MAP:
index b0dfce77656a0ba9c43d6d6616be1d28fb4dc137..05be0c1816958b0d0db6b2c319631d41f273e3d0 100644 (file)
@@ -2491,7 +2491,7 @@ EXPORT_SYMBOL(ceph_osdc_sync);
  * Call all pending notify callbacks - for use after a watch is
  * unregistered, to make sure no more callbacks for it will be invoked
  */
-extern void ceph_osdc_flush_notifies(struct ceph_osd_client *osdc)
+void ceph_osdc_flush_notifies(struct ceph_osd_client *osdc)
 {
        flush_workqueue(osdc->notify_wq);
 }
index 826b925aa4530a0de280b7b01442beb191cf8b5c..71093d94ad2bb22e01e09676c482abd3d8d37ab5 100644 (file)
@@ -9,7 +9,7 @@ obj-$(CONFIG_SYSCTL) += sysctl_net_core.o
 
 obj-y               += dev.o ethtool.o dev_addr_lists.o dst.o netevent.o \
                        neighbour.o rtnetlink.o utils.o link_watch.o filter.o \
-                       sock_diag.o dev_ioctl.o
+                       sock_diag.o dev_ioctl.o tso.o
 
 obj-$(CONFIG_XFRM) += flow.o
 obj-y += net-sysfs.o
index a16ed7bbe37689007e1fabed938076619c65a2e2..6b1c04ca1d5090410f436fbf74e50813e09634c3 100644 (file)
@@ -739,11 +739,15 @@ __sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len)
        __sum16 sum;
 
        sum = csum_fold(skb_checksum(skb, 0, len, skb->csum));
-       if (likely(!sum)) {
-               if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE))
-                       netdev_rx_csum_fault(skb->dev);
-               skb->ip_summed = CHECKSUM_UNNECESSARY;
-       }
+       if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) && !sum &&
+           !skb->csum_complete_sw)
+               netdev_rx_csum_fault(skb->dev);
+
+       /* Save checksum complete for later use */
+       skb->csum = sum;
+       skb->ip_summed = CHECKSUM_COMPLETE;
+       skb->csum_complete_sw = 1;
+
        return sum;
 }
 EXPORT_SYMBOL(__skb_checksum_complete_head);
index 8908a68db44921c91a4f359b90e575c4a7190468..30eedf6779138d77ae9c54ca5efa8bf57587e7ac 100644 (file)
@@ -1661,6 +1661,29 @@ bool is_skb_forwardable(struct net_device *dev, struct sk_buff *skb)
 }
 EXPORT_SYMBOL_GPL(is_skb_forwardable);
 
+int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb)
+{
+       if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) {
+               if (skb_copy_ubufs(skb, GFP_ATOMIC)) {
+                       atomic_long_inc(&dev->rx_dropped);
+                       kfree_skb(skb);
+                       return NET_RX_DROP;
+               }
+       }
+
+       if (unlikely(!is_skb_forwardable(dev, skb))) {
+               atomic_long_inc(&dev->rx_dropped);
+               kfree_skb(skb);
+               return NET_RX_DROP;
+       }
+
+       skb_scrub_packet(skb, true);
+       skb->protocol = eth_type_trans(skb, dev);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(__dev_forward_skb);
+
 /**
  * dev_forward_skb - loopback an skb to another netif
  *
@@ -1681,24 +1704,7 @@ EXPORT_SYMBOL_GPL(is_skb_forwardable);
  */
 int dev_forward_skb(struct net_device *dev, struct sk_buff *skb)
 {
-       if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) {
-               if (skb_copy_ubufs(skb, GFP_ATOMIC)) {
-                       atomic_long_inc(&dev->rx_dropped);
-                       kfree_skb(skb);
-                       return NET_RX_DROP;
-               }
-       }
-
-       if (unlikely(!is_skb_forwardable(dev, skb))) {
-               atomic_long_inc(&dev->rx_dropped);
-               kfree_skb(skb);
-               return NET_RX_DROP;
-       }
-
-       skb_scrub_packet(skb, true);
-       skb->protocol = eth_type_trans(skb, dev);
-
-       return netif_rx_internal(skb);
+       return __dev_forward_skb(dev, skb) ?: netif_rx_internal(skb);
 }
 EXPORT_SYMBOL_GPL(dev_forward_skb);
 
@@ -2507,13 +2513,39 @@ static int dev_gso_segment(struct sk_buff *skb, netdev_features_t features)
        return 0;
 }
 
+/* If MPLS offload request, verify we are testing hardware MPLS features
+ * instead of standard features for the netdev.
+ */
+#ifdef CONFIG_NET_MPLS_GSO
+static netdev_features_t net_mpls_features(struct sk_buff *skb,
+                                          netdev_features_t features,
+                                          __be16 type)
+{
+       if (type == htons(ETH_P_MPLS_UC) || type == htons(ETH_P_MPLS_MC))
+               features &= skb->dev->mpls_features;
+
+       return features;
+}
+#else
+static netdev_features_t net_mpls_features(struct sk_buff *skb,
+                                          netdev_features_t features,
+                                          __be16 type)
+{
+       return features;
+}
+#endif
+
 static netdev_features_t harmonize_features(struct sk_buff *skb,
        netdev_features_t features)
 {
        int tmp;
+       __be16 type;
+
+       type = skb_network_protocol(skb, &tmp);
+       features = net_mpls_features(skb, features, type);
 
        if (skb->ip_summed != CHECKSUM_NONE &&
-           !can_checksum_protocol(features, skb_network_protocol(skb, &tmp))) {
+           !can_checksum_protocol(features, type)) {
                features &= ~NETIF_F_ALL_CSUM;
        } else if (illegal_highdma(skb->dev, skb)) {
                features &= ~NETIF_F_SG;
@@ -5689,10 +5721,6 @@ static void rollback_registered_many(struct list_head *head)
                */
                call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
 
-               if (!dev->rtnl_link_ops ||
-                   dev->rtnl_link_state == RTNL_LINK_INITIALIZED)
-                       rtmsg_ifinfo(RTM_DELLINK, dev, ~0U, GFP_KERNEL);
-
                /*
                 *      Flush the unicast and multicast chains
                 */
@@ -5702,6 +5730,10 @@ static void rollback_registered_many(struct list_head *head)
                if (dev->netdev_ops->ndo_uninit)
                        dev->netdev_ops->ndo_uninit(dev);
 
+               if (!dev->rtnl_link_ops ||
+                   dev->rtnl_link_state == RTNL_LINK_INITIALIZED)
+                       rtmsg_ifinfo(RTM_DELLINK, dev, ~0U, GFP_KERNEL);
+
                /* Notifier chain MUST detach us all upper devices. */
                WARN_ON(netdev_has_any_upper_dev(dev));
 
@@ -5927,10 +5959,7 @@ static void netdev_init_one_queue(struct net_device *dev,
 
 static void netif_free_tx_queues(struct net_device *dev)
 {
-       if (is_vmalloc_addr(dev->_tx))
-               vfree(dev->_tx);
-       else
-               kfree(dev->_tx);
+       kvfree(dev->_tx);
 }
 
 static int netif_alloc_netdev_queues(struct net_device *dev)
@@ -6404,10 +6433,7 @@ void netdev_freemem(struct net_device *dev)
 {
        char *addr = (char *)dev - dev->padded;
 
-       if (is_vmalloc_addr(addr))
-               vfree(addr);
-       else
-               kfree(addr);
+       kvfree(addr);
 }
 
 /**
@@ -6512,11 +6538,6 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
 
 free_pcpu:
        free_percpu(dev->pcpu_refcnt);
-       netif_free_tx_queues(dev);
-#ifdef CONFIG_SYSFS
-       kfree(dev->_rx);
-#endif
-
 free_dev:
        netdev_freemem(dev);
        return NULL;
@@ -6613,6 +6634,9 @@ EXPORT_SYMBOL(unregister_netdevice_queue);
 /**
  *     unregister_netdevice_many - unregister many devices
  *     @head: list of devices
+ *
+ *  Note: As most callers use a stack allocated list_head,
+ *  we force a list_del() to make sure stack wont be corrupted later.
  */
 void unregister_netdevice_many(struct list_head *head)
 {
@@ -6622,6 +6646,7 @@ void unregister_netdevice_many(struct list_head *head)
                rollback_registered_many(head);
                list_for_each_entry(dev, head, unreg_list)
                        net_set_todo(dev);
+               list_del(head);
        }
 }
 EXPORT_SYMBOL(unregister_netdevice_many);
@@ -7077,7 +7102,6 @@ static void __net_exit default_device_exit_batch(struct list_head *net_list)
                }
        }
        unregister_netdevice_many(&dev_kill_list);
-       list_del(&dev_kill_list);
        rtnl_unlock();
 }
 
index 329d5794e7dca8bf7535800c2becc9bb3883e583..b6b230600b974ab908679fb62d225ed084ea5276 100644 (file)
@@ -225,6 +225,91 @@ void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
 }
 EXPORT_SYMBOL(__hw_addr_unsync);
 
+/**
+ *  __hw_addr_sync_dev - Synchonize device's multicast list
+ *  @list: address list to syncronize
+ *  @dev:  device to sync
+ *  @sync: function to call if address should be added
+ *  @unsync: function to call if address should be removed
+ *
+ *  This funciton is intended to be called from the ndo_set_rx_mode
+ *  function of devices that require explicit address add/remove
+ *  notifications.  The unsync function may be NULL in which case
+ *  the addresses requiring removal will simply be removed without
+ *  any notification to the device.
+ **/
+int __hw_addr_sync_dev(struct netdev_hw_addr_list *list,
+                      struct net_device *dev,
+                      int (*sync)(struct net_device *, const unsigned char *),
+                      int (*unsync)(struct net_device *,
+                                    const unsigned char *))
+{
+       struct netdev_hw_addr *ha, *tmp;
+       int err;
+
+       /* first go through and flush out any stale entries */
+       list_for_each_entry_safe(ha, tmp, &list->list, list) {
+               if (!ha->sync_cnt || ha->refcount != 1)
+                       continue;
+
+               /* if unsync is defined and fails defer unsyncing address */
+               if (unsync && unsync(dev, ha->addr))
+                       continue;
+
+               ha->sync_cnt--;
+               __hw_addr_del_entry(list, ha, false, false);
+       }
+
+       /* go through and sync new entries to the list */
+       list_for_each_entry_safe(ha, tmp, &list->list, list) {
+               if (ha->sync_cnt)
+                       continue;
+
+               err = sync(dev, ha->addr);
+               if (err)
+                       return err;
+
+               ha->sync_cnt++;
+               ha->refcount++;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(__hw_addr_sync_dev);
+
+/**
+ *  __hw_addr_unsync_dev - Remove synchonized addresses from device
+ *  @list: address list to remove syncronized addresses from
+ *  @dev:  device to sync
+ *  @unsync: function to call if address should be removed
+ *
+ *  Remove all addresses that were added to the device by __hw_addr_sync_dev().
+ *  This function is intended to be called from the ndo_stop or ndo_open
+ *  functions on devices that require explicit address add/remove
+ *  notifications.  If the unsync function pointer is NULL then this function
+ *  can be used to just reset the sync_cnt for the addresses in the list.
+ **/
+void __hw_addr_unsync_dev(struct netdev_hw_addr_list *list,
+                         struct net_device *dev,
+                         int (*unsync)(struct net_device *,
+                                       const unsigned char *))
+{
+       struct netdev_hw_addr *ha, *tmp;
+
+       list_for_each_entry_safe(ha, tmp, &list->list, list) {
+               if (!ha->sync_cnt)
+                       continue;
+
+               /* if unsync is defined and fails defer unsyncing address */
+               if (unsync && unsync(dev, ha->addr))
+                       continue;
+
+               ha->sync_cnt--;
+               __hw_addr_del_entry(list, ha, false, false);
+       }
+}
+EXPORT_SYMBOL(__hw_addr_unsync_dev);
+
 static void __hw_addr_flush(struct netdev_hw_addr_list *list)
 {
        struct netdev_hw_addr *ha, *tmp;
index 640ba0e5831ce0334a9cbf03983c16a022e85065..17cb912793fa5ef0d221abda0dfb447b216b4268 100644 (file)
@@ -557,6 +557,23 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
        return ret;
 }
 
+static int ethtool_copy_validate_indir(u32 *indir, void __user *useraddr,
+                                       struct ethtool_rxnfc *rx_rings,
+                                       u32 size)
+{
+       int i;
+
+       if (copy_from_user(indir, useraddr, size * sizeof(indir[0])))
+               return -EFAULT;
+
+       /* Validate ring indices */
+       for (i = 0; i < size; i++)
+               if (indir[i] >= rx_rings->data)
+                       return -EINVAL;
+
+       return 0;
+}
+
 static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
                                                     void __user *useraddr)
 {
@@ -565,7 +582,7 @@ static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
        int ret;
 
        if (!dev->ethtool_ops->get_rxfh_indir_size ||
-           !dev->ethtool_ops->get_rxfh_indir)
+           !dev->ethtool_ops->get_rxfh)
                return -EOPNOTSUPP;
        dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev);
        if (dev_size == 0)
@@ -591,7 +608,7 @@ static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
        if (!indir)
                return -ENOMEM;
 
-       ret = dev->ethtool_ops->get_rxfh_indir(dev, indir);
+       ret = dev->ethtool_ops->get_rxfh(dev, indir, NULL);
        if (ret)
                goto out;
 
@@ -613,8 +630,9 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
        u32 *indir;
        const struct ethtool_ops *ops = dev->ethtool_ops;
        int ret;
+       u32 ringidx_offset = offsetof(struct ethtool_rxfh_indir, ring_index[0]);
 
-       if (!ops->get_rxfh_indir_size || !ops->set_rxfh_indir ||
+       if (!ops->get_rxfh_indir_size || !ops->set_rxfh ||
            !ops->get_rxnfc)
                return -EOPNOTSUPP;
 
@@ -643,28 +661,184 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
                for (i = 0; i < dev_size; i++)
                        indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data);
        } else {
-               if (copy_from_user(indir,
-                                 useraddr +
-                                 offsetof(struct ethtool_rxfh_indir,
-                                          ring_index[0]),
-                                 dev_size * sizeof(indir[0]))) {
+               ret = ethtool_copy_validate_indir(indir,
+                                                 useraddr + ringidx_offset,
+                                                 &rx_rings,
+                                                 dev_size);
+               if (ret)
+                       goto out;
+       }
+
+       ret = ops->set_rxfh(dev, indir, NULL);
+
+out:
+       kfree(indir);
+       return ret;
+}
+
+static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
+                                              void __user *useraddr)
+{
+       int ret;
+       const struct ethtool_ops *ops = dev->ethtool_ops;
+       u32 user_indir_size, user_key_size;
+       u32 dev_indir_size = 0, dev_key_size = 0;
+       struct ethtool_rxfh rxfh;
+       u32 total_size;
+       u32 indir_bytes;
+       u32 *indir = NULL;
+       u8 *hkey = NULL;
+       u8 *rss_config;
+
+       if (!(dev->ethtool_ops->get_rxfh_indir_size ||
+             dev->ethtool_ops->get_rxfh_key_size) ||
+             !dev->ethtool_ops->get_rxfh)
+               return -EOPNOTSUPP;
+
+       if (ops->get_rxfh_indir_size)
+               dev_indir_size = ops->get_rxfh_indir_size(dev);
+       if (ops->get_rxfh_key_size)
+               dev_key_size = ops->get_rxfh_key_size(dev);
+
+       if ((dev_key_size + dev_indir_size) == 0)
+               return -EOPNOTSUPP;
+
+       if (copy_from_user(&rxfh, useraddr, sizeof(rxfh)))
+               return -EFAULT;
+       user_indir_size = rxfh.indir_size;
+       user_key_size = rxfh.key_size;
+
+       /* Check that reserved fields are 0 for now */
+       if (rxfh.rss_context || rxfh.rsvd[0] || rxfh.rsvd[1])
+               return -EINVAL;
+
+       rxfh.indir_size = dev_indir_size;
+       rxfh.key_size = dev_key_size;
+       if (copy_to_user(useraddr, &rxfh, sizeof(rxfh)))
+               return -EFAULT;
+
+       /* If the user buffer size is 0, this is just a query for the
+        * device table size and key size.  Otherwise, if the User size is
+        * not equal to device table size or key size it's an error.
+        */
+       if (!user_indir_size && !user_key_size)
+               return 0;
+
+       if ((user_indir_size && (user_indir_size != dev_indir_size)) ||
+           (user_key_size && (user_key_size != dev_key_size)))
+               return -EINVAL;
+
+       indir_bytes = user_indir_size * sizeof(indir[0]);
+       total_size = indir_bytes + user_key_size;
+       rss_config = kzalloc(total_size, GFP_USER);
+       if (!rss_config)
+               return -ENOMEM;
+
+       if (user_indir_size)
+               indir = (u32 *)rss_config;
+
+       if (user_key_size)
+               hkey = rss_config + indir_bytes;
+
+       ret = dev->ethtool_ops->get_rxfh(dev, indir, hkey);
+       if (!ret) {
+               if (copy_to_user(useraddr +
+                                offsetof(struct ethtool_rxfh, rss_config[0]),
+                                rss_config, total_size))
                        ret = -EFAULT;
+       }
+
+       kfree(rss_config);
+
+       return ret;
+}
+
+static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
+                                              void __user *useraddr)
+{
+       int ret;
+       const struct ethtool_ops *ops = dev->ethtool_ops;
+       struct ethtool_rxnfc rx_rings;
+       struct ethtool_rxfh rxfh;
+       u32 dev_indir_size = 0, dev_key_size = 0, i;
+       u32 *indir = NULL, indir_bytes = 0;
+       u8 *hkey = NULL;
+       u8 *rss_config;
+       u32 rss_cfg_offset = offsetof(struct ethtool_rxfh, rss_config[0]);
+
+       if (!(ops->get_rxfh_indir_size || ops->get_rxfh_key_size) ||
+           !ops->get_rxnfc || !ops->set_rxfh)
+               return -EOPNOTSUPP;
+
+       if (ops->get_rxfh_indir_size)
+               dev_indir_size = ops->get_rxfh_indir_size(dev);
+       if (ops->get_rxfh_key_size)
+               dev_key_size = dev->ethtool_ops->get_rxfh_key_size(dev);
+       if ((dev_key_size + dev_indir_size) == 0)
+               return -EOPNOTSUPP;
+
+       if (copy_from_user(&rxfh, useraddr, sizeof(rxfh)))
+               return -EFAULT;
+
+       /* Check that reserved fields are 0 for now */
+       if (rxfh.rss_context || rxfh.rsvd[0] || rxfh.rsvd[1])
+               return -EINVAL;
+
+       /* If either indir or hash key is valid, proceed further.
+        * It is not valid to request that both be unchanged.
+        */
+       if ((rxfh.indir_size &&
+            rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE &&
+            rxfh.indir_size != dev_indir_size) ||
+           (rxfh.key_size && (rxfh.key_size != dev_key_size)) ||
+           (rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE &&
+            rxfh.key_size == 0))
+               return -EINVAL;
+
+       if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE)
+               indir_bytes = dev_indir_size * sizeof(indir[0]);
+
+       rss_config = kzalloc(indir_bytes + rxfh.key_size, GFP_USER);
+       if (!rss_config)
+               return -ENOMEM;
+
+       rx_rings.cmd = ETHTOOL_GRXRINGS;
+       ret = ops->get_rxnfc(dev, &rx_rings, NULL);
+       if (ret)
+               goto out;
+
+       /* rxfh.indir_size == 0 means reset the indir table to default.
+        * rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE means leave it unchanged.
+        */
+       if (rxfh.indir_size &&
+           rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE) {
+               indir = (u32 *)rss_config;
+               ret = ethtool_copy_validate_indir(indir,
+                                                 useraddr + rss_cfg_offset,
+                                                 &rx_rings,
+                                                 rxfh.indir_size);
+               if (ret)
                        goto out;
-               }
+       } else if (rxfh.indir_size == 0) {
+               indir = (u32 *)rss_config;
+               for (i = 0; i < dev_indir_size; i++)
+                       indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data);
+       }
 
-               /* Validate ring indices */
-               for (i = 0; i < dev_size; i++) {
-                       if (indir[i] >= rx_rings.data) {
-                               ret = -EINVAL;
-                               goto out;
-                       }
+       if (rxfh.key_size) {
+               hkey = rss_config + indir_bytes;
+               if (copy_from_user(hkey,
+                                  useraddr + rss_cfg_offset + indir_bytes,
+                                  rxfh.key_size)) {
+                       ret = -EFAULT;
+                       goto out;
                }
        }
 
-       ret = ops->set_rxfh_indir(dev, indir);
+       ret = ops->set_rxfh(dev, indir, hkey);
 
 out:
-       kfree(indir);
+       kfree(rss_config);
        return ret;
 }
 
@@ -1491,6 +1665,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
        case ETHTOOL_GRXCLSRULE:
        case ETHTOOL_GRXCLSRLALL:
        case ETHTOOL_GRXFHINDIR:
+       case ETHTOOL_GRSSH:
        case ETHTOOL_GFEATURES:
        case ETHTOOL_GCHANNELS:
        case ETHTOOL_GET_TS_INFO:
@@ -1628,6 +1803,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
        case ETHTOOL_SRXFHINDIR:
                rc = ethtool_set_rxfh_indir(dev, useraddr);
                break;
+       case ETHTOOL_GRSSH:
+               rc = ethtool_get_rxfh(dev, useraddr);
+               break;
+       case ETHTOOL_SRSSH:
+               rc = ethtool_set_rxfh(dev, useraddr);
+               break;
        case ETHTOOL_GFEATURES:
                rc = ethtool_get_features(dev, useraddr);
                break;
index 4aec7b93f1a9cd2f7695d2056acff0e9dec6f80a..735fad89749630b71b2b64d6c82d59fec60a7c7f 100644 (file)
 #include <linux/seccomp.h>
 #include <linux/if_vlan.h>
 
+/* Registers */
+#define BPF_R0 regs[BPF_REG_0]
+#define BPF_R1 regs[BPF_REG_1]
+#define BPF_R2 regs[BPF_REG_2]
+#define BPF_R3 regs[BPF_REG_3]
+#define BPF_R4 regs[BPF_REG_4]
+#define BPF_R5 regs[BPF_REG_5]
+#define BPF_R6 regs[BPF_REG_6]
+#define BPF_R7 regs[BPF_REG_7]
+#define BPF_R8 regs[BPF_REG_8]
+#define BPF_R9 regs[BPF_REG_9]
+#define BPF_R10        regs[BPF_REG_10]
+
+/* Named registers */
+#define DST    regs[insn->dst_reg]
+#define SRC    regs[insn->src_reg]
+#define FP     regs[BPF_REG_FP]
+#define ARG1   regs[BPF_REG_ARG1]
+#define CTX    regs[BPF_REG_CTX]
+#define IMM    insn->imm
+
 /* No hurry in this branch
  *
  * Exported for the bpf jit load helper.
@@ -57,9 +78,9 @@ void *bpf_internal_load_pointer_neg_helper(const struct sk_buff *skb, int k, uns
                ptr = skb_network_header(skb) + k - SKF_NET_OFF;
        else if (k >= SKF_LL_OFF)
                ptr = skb_mac_header(skb) + k - SKF_LL_OFF;
-
        if (ptr >= skb->head && ptr + size <= skb_tail_pointer(skb))
                return ptr;
+
        return NULL;
 }
 
@@ -68,6 +89,7 @@ static inline void *load_pointer(const struct sk_buff *skb, int k,
 {
        if (k >= 0)
                return skb_header_pointer(skb, k, size, buffer);
+
        return bpf_internal_load_pointer_neg_helper(skb, k, size);
 }
 
@@ -122,13 +144,6 @@ noinline u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
        return 0;
 }
 
-/* Register mappings for user programs. */
-#define A_REG          0
-#define X_REG          7
-#define TMP_REG                8
-#define ARG2_REG       2
-#define ARG3_REG       3
-
 /**
  *     __sk_run_filter - run a filter on a given context
  *     @ctx: buffer to run the filter on
@@ -138,447 +153,442 @@ noinline u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
  * keep, 0 for none. @ctx is the data we are operating on, @insn is the
  * array of filter instructions.
  */
-unsigned int __sk_run_filter(void *ctx, const struct sock_filter_int *insn)
+static unsigned int __sk_run_filter(void *ctx, const struct sock_filter_int *insn)
 {
        u64 stack[MAX_BPF_STACK / sizeof(u64)];
        u64 regs[MAX_BPF_REG], tmp;
-       void *ptr;
-       int off;
-
-#define K  insn->imm
-#define A  regs[insn->a_reg]
-#define X  regs[insn->x_reg]
-#define R0 regs[0]
-
-#define CONT    ({insn++; goto select_insn; })
-#define CONT_JMP ({insn++; goto select_insn; })
-
        static const void *jumptable[256] = {
                [0 ... 255] = &&default_label,
                /* Now overwrite non-defaults ... */
-#define DL(A, B, C)    [A|B|C] = &&A##_##B##_##C
-               DL(BPF_ALU, BPF_ADD, BPF_X),
-               DL(BPF_ALU, BPF_ADD, BPF_K),
-               DL(BPF_ALU, BPF_SUB, BPF_X),
-               DL(BPF_ALU, BPF_SUB, BPF_K),
-               DL(BPF_ALU, BPF_AND, BPF_X),
-               DL(BPF_ALU, BPF_AND, BPF_K),
-               DL(BPF_ALU, BPF_OR, BPF_X),
-               DL(BPF_ALU, BPF_OR, BPF_K),
-               DL(BPF_ALU, BPF_LSH, BPF_X),
-               DL(BPF_ALU, BPF_LSH, BPF_K),
-               DL(BPF_ALU, BPF_RSH, BPF_X),
-               DL(BPF_ALU, BPF_RSH, BPF_K),
-               DL(BPF_ALU, BPF_XOR, BPF_X),
-               DL(BPF_ALU, BPF_XOR, BPF_K),
-               DL(BPF_ALU, BPF_MUL, BPF_X),
-               DL(BPF_ALU, BPF_MUL, BPF_K),
-               DL(BPF_ALU, BPF_MOV, BPF_X),
-               DL(BPF_ALU, BPF_MOV, BPF_K),
-               DL(BPF_ALU, BPF_DIV, BPF_X),
-               DL(BPF_ALU, BPF_DIV, BPF_K),
-               DL(BPF_ALU, BPF_MOD, BPF_X),
-               DL(BPF_ALU, BPF_MOD, BPF_K),
-               DL(BPF_ALU, BPF_NEG, 0),
-               DL(BPF_ALU, BPF_END, BPF_TO_BE),
-               DL(BPF_ALU, BPF_END, BPF_TO_LE),
-               DL(BPF_ALU64, BPF_ADD, BPF_X),
-               DL(BPF_ALU64, BPF_ADD, BPF_K),
-               DL(BPF_ALU64, BPF_SUB, BPF_X),
-               DL(BPF_ALU64, BPF_SUB, BPF_K),
-               DL(BPF_ALU64, BPF_AND, BPF_X),
-               DL(BPF_ALU64, BPF_AND, BPF_K),
-               DL(BPF_ALU64, BPF_OR, BPF_X),
-               DL(BPF_ALU64, BPF_OR, BPF_K),
-               DL(BPF_ALU64, BPF_LSH, BPF_X),
-               DL(BPF_ALU64, BPF_LSH, BPF_K),
-               DL(BPF_ALU64, BPF_RSH, BPF_X),
-               DL(BPF_ALU64, BPF_RSH, BPF_K),
-               DL(BPF_ALU64, BPF_XOR, BPF_X),
-               DL(BPF_ALU64, BPF_XOR, BPF_K),
-               DL(BPF_ALU64, BPF_MUL, BPF_X),
-               DL(BPF_ALU64, BPF_MUL, BPF_K),
-               DL(BPF_ALU64, BPF_MOV, BPF_X),
-               DL(BPF_ALU64, BPF_MOV, BPF_K),
-               DL(BPF_ALU64, BPF_ARSH, BPF_X),
-               DL(BPF_ALU64, BPF_ARSH, BPF_K),
-               DL(BPF_ALU64, BPF_DIV, BPF_X),
-               DL(BPF_ALU64, BPF_DIV, BPF_K),
-               DL(BPF_ALU64, BPF_MOD, BPF_X),
-               DL(BPF_ALU64, BPF_MOD, BPF_K),
-               DL(BPF_ALU64, BPF_NEG, 0),
-               DL(BPF_JMP, BPF_CALL, 0),
-               DL(BPF_JMP, BPF_JA, 0),
-               DL(BPF_JMP, BPF_JEQ, BPF_X),
-               DL(BPF_JMP, BPF_JEQ, BPF_K),
-               DL(BPF_JMP, BPF_JNE, BPF_X),
-               DL(BPF_JMP, BPF_JNE, BPF_K),
-               DL(BPF_JMP, BPF_JGT, BPF_X),
-               DL(BPF_JMP, BPF_JGT, BPF_K),
-               DL(BPF_JMP, BPF_JGE, BPF_X),
-               DL(BPF_JMP, BPF_JGE, BPF_K),
-               DL(BPF_JMP, BPF_JSGT, BPF_X),
-               DL(BPF_JMP, BPF_JSGT, BPF_K),
-               DL(BPF_JMP, BPF_JSGE, BPF_X),
-               DL(BPF_JMP, BPF_JSGE, BPF_K),
-               DL(BPF_JMP, BPF_JSET, BPF_X),
-               DL(BPF_JMP, BPF_JSET, BPF_K),
-               DL(BPF_JMP, BPF_EXIT, 0),
-               DL(BPF_STX, BPF_MEM, BPF_B),
-               DL(BPF_STX, BPF_MEM, BPF_H),
-               DL(BPF_STX, BPF_MEM, BPF_W),
-               DL(BPF_STX, BPF_MEM, BPF_DW),
-               DL(BPF_STX, BPF_XADD, BPF_W),
-               DL(BPF_STX, BPF_XADD, BPF_DW),
-               DL(BPF_ST, BPF_MEM, BPF_B),
-               DL(BPF_ST, BPF_MEM, BPF_H),
-               DL(BPF_ST, BPF_MEM, BPF_W),
-               DL(BPF_ST, BPF_MEM, BPF_DW),
-               DL(BPF_LDX, BPF_MEM, BPF_B),
-               DL(BPF_LDX, BPF_MEM, BPF_H),
-               DL(BPF_LDX, BPF_MEM, BPF_W),
-               DL(BPF_LDX, BPF_MEM, BPF_DW),
-               DL(BPF_LD, BPF_ABS, BPF_W),
-               DL(BPF_LD, BPF_ABS, BPF_H),
-               DL(BPF_LD, BPF_ABS, BPF_B),
-               DL(BPF_LD, BPF_IND, BPF_W),
-               DL(BPF_LD, BPF_IND, BPF_H),
-               DL(BPF_LD, BPF_IND, BPF_B),
-#undef DL
+               /* 32 bit ALU operations */
+               [BPF_ALU | BPF_ADD | BPF_X] = &&ALU_ADD_X,
+               [BPF_ALU | BPF_ADD | BPF_K] = &&ALU_ADD_K,
+               [BPF_ALU | BPF_SUB | BPF_X] = &&ALU_SUB_X,
+               [BPF_ALU | BPF_SUB | BPF_K] = &&ALU_SUB_K,
+               [BPF_ALU | BPF_AND | BPF_X] = &&ALU_AND_X,
+               [BPF_ALU | BPF_AND | BPF_K] = &&ALU_AND_K,
+               [BPF_ALU | BPF_OR | BPF_X]  = &&ALU_OR_X,
+               [BPF_ALU | BPF_OR | BPF_K]  = &&ALU_OR_K,
+               [BPF_ALU | BPF_LSH | BPF_X] = &&ALU_LSH_X,
+               [BPF_ALU | BPF_LSH | BPF_K] = &&ALU_LSH_K,
+               [BPF_ALU | BPF_RSH | BPF_X] = &&ALU_RSH_X,
+               [BPF_ALU | BPF_RSH | BPF_K] = &&ALU_RSH_K,
+               [BPF_ALU | BPF_XOR | BPF_X] = &&ALU_XOR_X,
+               [BPF_ALU | BPF_XOR | BPF_K] = &&ALU_XOR_K,
+               [BPF_ALU | BPF_MUL | BPF_X] = &&ALU_MUL_X,
+               [BPF_ALU | BPF_MUL | BPF_K] = &&ALU_MUL_K,
+               [BPF_ALU | BPF_MOV | BPF_X] = &&ALU_MOV_X,
+               [BPF_ALU | BPF_MOV | BPF_K] = &&ALU_MOV_K,
+               [BPF_ALU | BPF_DIV | BPF_X] = &&ALU_DIV_X,
+               [BPF_ALU | BPF_DIV | BPF_K] = &&ALU_DIV_K,
+               [BPF_ALU | BPF_MOD | BPF_X] = &&ALU_MOD_X,
+               [BPF_ALU | BPF_MOD | BPF_K] = &&ALU_MOD_K,
+               [BPF_ALU | BPF_NEG] = &&ALU_NEG,
+               [BPF_ALU | BPF_END | BPF_TO_BE] = &&ALU_END_TO_BE,
+               [BPF_ALU | BPF_END | BPF_TO_LE] = &&ALU_END_TO_LE,
+               /* 64 bit ALU operations */
+               [BPF_ALU64 | BPF_ADD | BPF_X] = &&ALU64_ADD_X,
+               [BPF_ALU64 | BPF_ADD | BPF_K] = &&ALU64_ADD_K,
+               [BPF_ALU64 | BPF_SUB | BPF_X] = &&ALU64_SUB_X,
+               [BPF_ALU64 | BPF_SUB | BPF_K] = &&ALU64_SUB_K,
+               [BPF_ALU64 | BPF_AND | BPF_X] = &&ALU64_AND_X,
+               [BPF_ALU64 | BPF_AND | BPF_K] = &&ALU64_AND_K,
+               [BPF_ALU64 | BPF_OR | BPF_X] = &&ALU64_OR_X,
+               [BPF_ALU64 | BPF_OR | BPF_K] = &&ALU64_OR_K,
+               [BPF_ALU64 | BPF_LSH | BPF_X] = &&ALU64_LSH_X,
+               [BPF_ALU64 | BPF_LSH | BPF_K] = &&ALU64_LSH_K,
+               [BPF_ALU64 | BPF_RSH | BPF_X] = &&ALU64_RSH_X,
+               [BPF_ALU64 | BPF_RSH | BPF_K] = &&ALU64_RSH_K,
+               [BPF_ALU64 | BPF_XOR | BPF_X] = &&ALU64_XOR_X,
+               [BPF_ALU64 | BPF_XOR | BPF_K] = &&ALU64_XOR_K,
+               [BPF_ALU64 | BPF_MUL | BPF_X] = &&ALU64_MUL_X,
+               [BPF_ALU64 | BPF_MUL | BPF_K] = &&ALU64_MUL_K,
+               [BPF_ALU64 | BPF_MOV | BPF_X] = &&ALU64_MOV_X,
+               [BPF_ALU64 | BPF_MOV | BPF_K] = &&ALU64_MOV_K,
+               [BPF_ALU64 | BPF_ARSH | BPF_X] = &&ALU64_ARSH_X,
+               [BPF_ALU64 | BPF_ARSH | BPF_K] = &&ALU64_ARSH_K,
+               [BPF_ALU64 | BPF_DIV | BPF_X] = &&ALU64_DIV_X,
+               [BPF_ALU64 | BPF_DIV | BPF_K] = &&ALU64_DIV_K,
+               [BPF_ALU64 | BPF_MOD | BPF_X] = &&ALU64_MOD_X,
+               [BPF_ALU64 | BPF_MOD | BPF_K] = &&ALU64_MOD_K,
+               [BPF_ALU64 | BPF_NEG] = &&ALU64_NEG,
+               /* Call instruction */
+               [BPF_JMP | BPF_CALL] = &&JMP_CALL,
+               /* Jumps */
+               [BPF_JMP | BPF_JA] = &&JMP_JA,
+               [BPF_JMP | BPF_JEQ | BPF_X] = &&JMP_JEQ_X,
+               [BPF_JMP | BPF_JEQ | BPF_K] = &&JMP_JEQ_K,
+               [BPF_JMP | BPF_JNE | BPF_X] = &&JMP_JNE_X,
+               [BPF_JMP | BPF_JNE | BPF_K] = &&JMP_JNE_K,
+               [BPF_JMP | BPF_JGT | BPF_X] = &&JMP_JGT_X,
+               [BPF_JMP | BPF_JGT | BPF_K] = &&JMP_JGT_K,
+               [BPF_JMP | BPF_JGE | BPF_X] = &&JMP_JGE_X,
+               [BPF_JMP | BPF_JGE | BPF_K] = &&JMP_JGE_K,
+               [BPF_JMP | BPF_JSGT | BPF_X] = &&JMP_JSGT_X,
+               [BPF_JMP | BPF_JSGT | BPF_K] = &&JMP_JSGT_K,
+               [BPF_JMP | BPF_JSGE | BPF_X] = &&JMP_JSGE_X,
+               [BPF_JMP | BPF_JSGE | BPF_K] = &&JMP_JSGE_K,
+               [BPF_JMP | BPF_JSET | BPF_X] = &&JMP_JSET_X,
+               [BPF_JMP | BPF_JSET | BPF_K] = &&JMP_JSET_K,
+               /* Program return */
+               [BPF_JMP | BPF_EXIT] = &&JMP_EXIT,
+               /* Store instructions */
+               [BPF_STX | BPF_MEM | BPF_B] = &&STX_MEM_B,
+               [BPF_STX | BPF_MEM | BPF_H] = &&STX_MEM_H,
+               [BPF_STX | BPF_MEM | BPF_W] = &&STX_MEM_W,
+               [BPF_STX | BPF_MEM | BPF_DW] = &&STX_MEM_DW,
+               [BPF_STX | BPF_XADD | BPF_W] = &&STX_XADD_W,
+               [BPF_STX | BPF_XADD | BPF_DW] = &&STX_XADD_DW,
+               [BPF_ST | BPF_MEM | BPF_B] = &&ST_MEM_B,
+               [BPF_ST | BPF_MEM | BPF_H] = &&ST_MEM_H,
+               [BPF_ST | BPF_MEM | BPF_W] = &&ST_MEM_W,
+               [BPF_ST | BPF_MEM | BPF_DW] = &&ST_MEM_DW,
+               /* Load instructions */
+               [BPF_LDX | BPF_MEM | BPF_B] = &&LDX_MEM_B,
+               [BPF_LDX | BPF_MEM | BPF_H] = &&LDX_MEM_H,
+               [BPF_LDX | BPF_MEM | BPF_W] = &&LDX_MEM_W,
+               [BPF_LDX | BPF_MEM | BPF_DW] = &&LDX_MEM_DW,
+               [BPF_LD | BPF_ABS | BPF_W] = &&LD_ABS_W,
+               [BPF_LD | BPF_ABS | BPF_H] = &&LD_ABS_H,
+               [BPF_LD | BPF_ABS | BPF_B] = &&LD_ABS_B,
+               [BPF_LD | BPF_IND | BPF_W] = &&LD_IND_W,
+               [BPF_LD | BPF_IND | BPF_H] = &&LD_IND_H,
+               [BPF_LD | BPF_IND | BPF_B] = &&LD_IND_B,
        };
+       void *ptr;
+       int off;
+
+#define CONT    ({ insn++; goto select_insn; })
+#define CONT_JMP ({ insn++; goto select_insn; })
 
-       regs[FP_REG]  = (u64) (unsigned long) &stack[ARRAY_SIZE(stack)];
-       regs[ARG1_REG] = (u64) (unsigned long) ctx;
-       regs[A_REG] = 0;
-       regs[X_REG] = 0;
+       FP = (u64) (unsigned long) &stack[ARRAY_SIZE(stack)];
+       ARG1 = (u64) (unsigned long) ctx;
+
+       /* Registers used in classic BPF programs need to be reset first. */
+       regs[BPF_REG_A] = 0;
+       regs[BPF_REG_X] = 0;
 
 select_insn:
        goto *jumptable[insn->code];
 
        /* ALU */
 #define ALU(OPCODE, OP)                        \
-       BPF_ALU64_##OPCODE##_BPF_X:     \
-               A = A OP X;             \
+       ALU64_##OPCODE##_X:             \
+               DST = DST OP SRC;       \
                CONT;                   \
-       BPF_ALU_##OPCODE##_BPF_X:       \
-               A = (u32) A OP (u32) X; \
+       ALU_##OPCODE##_X:               \
+               DST = (u32) DST OP (u32) SRC;   \
                CONT;                   \
-       BPF_ALU64_##OPCODE##_BPF_K:     \
-               A = A OP K;             \
+       ALU64_##OPCODE##_K:             \
+               DST = DST OP IMM;               \
                CONT;                   \
-       BPF_ALU_##OPCODE##_BPF_K:       \
-               A = (u32) A OP (u32) K; \
+       ALU_##OPCODE##_K:               \
+               DST = (u32) DST OP (u32) IMM;   \
                CONT;
 
-       ALU(BPF_ADD,  +)
-       ALU(BPF_SUB,  -)
-       ALU(BPF_AND,  &)
-       ALU(BPF_OR,   |)
-       ALU(BPF_LSH, <<)
-       ALU(BPF_RSH, >>)
-       ALU(BPF_XOR,  ^)
-       ALU(BPF_MUL,  *)
+       ALU(ADD,  +)
+       ALU(SUB,  -)
+       ALU(AND,  &)
+       ALU(OR,   |)
+       ALU(LSH, <<)
+       ALU(RSH, >>)
+       ALU(XOR,  ^)
+       ALU(MUL,  *)
 #undef ALU
-       BPF_ALU_BPF_NEG_0:
-               A = (u32) -A;
+       ALU_NEG:
+               DST = (u32) -DST;
                CONT;
-       BPF_ALU64_BPF_NEG_0:
-               A = -A;
+       ALU64_NEG:
+               DST = -DST;
                CONT;
-       BPF_ALU_BPF_MOV_BPF_X:
-               A = (u32) X;
+       ALU_MOV_X:
+               DST = (u32) SRC;
                CONT;
-       BPF_ALU_BPF_MOV_BPF_K:
-               A = (u32) K;
+       ALU_MOV_K:
+               DST = (u32) IMM;
                CONT;
-       BPF_ALU64_BPF_MOV_BPF_X:
-               A = X;
+       ALU64_MOV_X:
+               DST = SRC;
                CONT;
-       BPF_ALU64_BPF_MOV_BPF_K:
-               A = K;
+       ALU64_MOV_K:
+               DST = IMM;
                CONT;
-       BPF_ALU64_BPF_ARSH_BPF_X:
-               (*(s64 *) &A) >>= X;
+       ALU64_ARSH_X:
+               (*(s64 *) &DST) >>= SRC;
                CONT;
-       BPF_ALU64_BPF_ARSH_BPF_K:
-               (*(s64 *) &A) >>= K;
+       ALU64_ARSH_K:
+               (*(s64 *) &DST) >>= IMM;
                CONT;
-       BPF_ALU64_BPF_MOD_BPF_X:
-               if (unlikely(X == 0))
+       ALU64_MOD_X:
+               if (unlikely(SRC == 0))
                        return 0;
-               tmp = A;
-               A = do_div(tmp, X);
+               tmp = DST;
+               DST = do_div(tmp, SRC);
                CONT;
-       BPF_ALU_BPF_MOD_BPF_X:
-               if (unlikely(X == 0))
+       ALU_MOD_X:
+               if (unlikely(SRC == 0))
                        return 0;
-               tmp = (u32) A;
-               A = do_div(tmp, (u32) X);
+               tmp = (u32) DST;
+               DST = do_div(tmp, (u32) SRC);
                CONT;
-       BPF_ALU64_BPF_MOD_BPF_K:
-               tmp = A;
-               A = do_div(tmp, K);
+       ALU64_MOD_K:
+               tmp = DST;
+               DST = do_div(tmp, IMM);
                CONT;
-       BPF_ALU_BPF_MOD_BPF_K:
-               tmp = (u32) A;
-               A = do_div(tmp, (u32) K);
+       ALU_MOD_K:
+               tmp = (u32) DST;
+               DST = do_div(tmp, (u32) IMM);
                CONT;
-       BPF_ALU64_BPF_DIV_BPF_X:
-               if (unlikely(X == 0))
+       ALU64_DIV_X:
+               if (unlikely(SRC == 0))
                        return 0;
-               do_div(A, X);
+               do_div(DST, SRC);
                CONT;
-       BPF_ALU_BPF_DIV_BPF_X:
-               if (unlikely(X == 0))
+       ALU_DIV_X:
+               if (unlikely(SRC == 0))
                        return 0;
-               tmp = (u32) A;
-               do_div(tmp, (u32) X);
-               A = (u32) tmp;
+               tmp = (u32) DST;
+               do_div(tmp, (u32) SRC);
+               DST = (u32) tmp;
                CONT;
-       BPF_ALU64_BPF_DIV_BPF_K:
-               do_div(A, K);
+       ALU64_DIV_K:
+               do_div(DST, IMM);
                CONT;
-       BPF_ALU_BPF_DIV_BPF_K:
-               tmp = (u32) A;
-               do_div(tmp, (u32) K);
-               A = (u32) tmp;
+       ALU_DIV_K:
+               tmp = (u32) DST;
+               do_div(tmp, (u32) IMM);
+               DST = (u32) tmp;
                CONT;
-       BPF_ALU_BPF_END_BPF_TO_BE:
-               switch (K) {
+       ALU_END_TO_BE:
+               switch (IMM) {
                case 16:
-                       A = (__force u16) cpu_to_be16(A);
+                       DST = (__force u16) cpu_to_be16(DST);
                        break;
                case 32:
-                       A = (__force u32) cpu_to_be32(A);
+                       DST = (__force u32) cpu_to_be32(DST);
                        break;
                case 64:
-                       A = (__force u64) cpu_to_be64(A);
+                       DST = (__force u64) cpu_to_be64(DST);
                        break;
                }
                CONT;
-       BPF_ALU_BPF_END_BPF_TO_LE:
-               switch (K) {
+       ALU_END_TO_LE:
+               switch (IMM) {
                case 16:
-                       A = (__force u16) cpu_to_le16(A);
+                       DST = (__force u16) cpu_to_le16(DST);
                        break;
                case 32:
-                       A = (__force u32) cpu_to_le32(A);
+                       DST = (__force u32) cpu_to_le32(DST);
                        break;
                case 64:
-                       A = (__force u64) cpu_to_le64(A);
+                       DST = (__force u64) cpu_to_le64(DST);
                        break;
                }
                CONT;
 
        /* CALL */
-       BPF_JMP_BPF_CALL_0:
-               /* Function call scratches R1-R5 registers, preserves R6-R9,
-                * and stores return value into R0.
+       JMP_CALL:
+               /* Function call scratches BPF_R1-BPF_R5 registers,
+                * preserves BPF_R6-BPF_R9, and stores return value
+                * into BPF_R0.
                 */
-               R0 = (__bpf_call_base + insn->imm)(regs[1], regs[2], regs[3],
-                                                  regs[4], regs[5]);
+               BPF_R0 = (__bpf_call_base + insn->imm)(BPF_R1, BPF_R2, BPF_R3,
+                                                      BPF_R4, BPF_R5);
                CONT;
 
        /* JMP */
-       BPF_JMP_BPF_JA_0:
+       JMP_JA:
                insn += insn->off;
                CONT;
-       BPF_JMP_BPF_JEQ_BPF_X:
-               if (A == X) {
+       JMP_JEQ_X:
+               if (DST == SRC) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JEQ_BPF_K:
-               if (A == K) {
+       JMP_JEQ_K:
+               if (DST == IMM) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JNE_BPF_X:
-               if (A != X) {
+       JMP_JNE_X:
+               if (DST != SRC) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JNE_BPF_K:
-               if (A != K) {
+       JMP_JNE_K:
+               if (DST != IMM) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JGT_BPF_X:
-               if (A > X) {
+       JMP_JGT_X:
+               if (DST > SRC) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JGT_BPF_K:
-               if (A > K) {
+       JMP_JGT_K:
+               if (DST > IMM) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JGE_BPF_X:
-               if (A >= X) {
+       JMP_JGE_X:
+               if (DST >= SRC) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JGE_BPF_K:
-               if (A >= K) {
+       JMP_JGE_K:
+               if (DST >= IMM) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JSGT_BPF_X:
-               if (((s64)A) > ((s64)X)) {
+       JMP_JSGT_X:
+               if (((s64) DST) > ((s64) SRC)) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JSGT_BPF_K:
-               if (((s64)A) > ((s64)K)) {
+       JMP_JSGT_K:
+               if (((s64) DST) > ((s64) IMM)) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JSGE_BPF_X:
-               if (((s64)A) >= ((s64)X)) {
+       JMP_JSGE_X:
+               if (((s64) DST) >= ((s64) SRC)) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JSGE_BPF_K:
-               if (((s64)A) >= ((s64)K)) {
+       JMP_JSGE_K:
+               if (((s64) DST) >= ((s64) IMM)) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JSET_BPF_X:
-               if (A & X) {
+       JMP_JSET_X:
+               if (DST & SRC) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_JSET_BPF_K:
-               if (A & K) {
+       JMP_JSET_K:
+               if (DST & IMM) {
                        insn += insn->off;
                        CONT_JMP;
                }
                CONT;
-       BPF_JMP_BPF_EXIT_0:
-               return R0;
+       JMP_EXIT:
+               return BPF_R0;
 
        /* STX and ST and LDX*/
-#define LDST(SIZEOP, SIZE)                                     \
-       BPF_STX_BPF_MEM_##SIZEOP:                               \
-               *(SIZE *)(unsigned long) (A + insn->off) = X;   \
-               CONT;                                           \
-       BPF_ST_BPF_MEM_##SIZEOP:                                \
-               *(SIZE *)(unsigned long) (A + insn->off) = K;   \
-               CONT;                                           \
-       BPF_LDX_BPF_MEM_##SIZEOP:                               \
-               A = *(SIZE *)(unsigned long) (X + insn->off);   \
+#define LDST(SIZEOP, SIZE)                                             \
+       STX_MEM_##SIZEOP:                                               \
+               *(SIZE *)(unsigned long) (DST + insn->off) = SRC;       \
+               CONT;                                                   \
+       ST_MEM_##SIZEOP:                                                \
+               *(SIZE *)(unsigned long) (DST + insn->off) = IMM;       \
+               CONT;                                                   \
+       LDX_MEM_##SIZEOP:                                               \
+               DST = *(SIZE *)(unsigned long) (SRC + insn->off);       \
                CONT;
 
-       LDST(BPF_B,   u8)
-       LDST(BPF_H,  u16)
-       LDST(BPF_W,  u32)
-       LDST(BPF_DW, u64)
+       LDST(B,   u8)
+       LDST(H,  u16)
+       LDST(W,  u32)
+       LDST(DW, u64)
 #undef LDST
-       BPF_STX_BPF_XADD_BPF_W: /* lock xadd *(u32 *)(A + insn->off) += X */
-               atomic_add((u32) X, (atomic_t *)(unsigned long)
-                          (A + insn->off));
+       STX_XADD_W: /* lock xadd *(u32 *)(dst_reg + off16) += src_reg */
+               atomic_add((u32) SRC, (atomic_t *)(unsigned long)
+                          (DST + insn->off));
                CONT;
-       BPF_STX_BPF_XADD_BPF_DW: /* lock xadd *(u64 *)(A + insn->off) += X */
-               atomic64_add((u64) X, (atomic64_t *)(unsigned long)
-                            (A + insn->off));
+       STX_XADD_DW: /* lock xadd *(u64 *)(dst_reg + off16) += src_reg */
+               atomic64_add((u64) SRC, (atomic64_t *)(unsigned long)
+                            (DST + insn->off));
                CONT;
-       BPF_LD_BPF_ABS_BPF_W: /* R0 = ntohl(*(u32 *) (skb->data + K)) */
-               off = K;
+       LD_ABS_W: /* BPF_R0 = ntohl(*(u32 *) (skb->data + imm32)) */
+               off = IMM;
 load_word:
-               /* BPF_LD + BPD_ABS and BPF_LD + BPF_IND insns are only
-                * appearing in the programs where ctx == skb. All programs
-                * keep 'ctx' in regs[CTX_REG] == R6, sk_convert_filter()
-                * saves it in R6, internal BPF verifier will check that
-                * R6 == ctx.
+               /* BPF_LD + BPD_ABS and BPF_LD + BPF_IND insns are
+                * only appearing in the programs where ctx ==
+                * skb. All programs keep 'ctx' in regs[BPF_REG_CTX]
+                * == BPF_R6, sk_convert_filter() saves it in BPF_R6,
+                * internal BPF verifier will check that BPF_R6 ==
+                * ctx.
                 *
-                * BPF_ABS and BPF_IND are wrappers of function calls, so
-                * they scratch R1-R5 registers, preserve R6-R9, and store
-                * return value into R0.
+                * BPF_ABS and BPF_IND are wrappers of function calls,
+                * so they scratch BPF_R1-BPF_R5 registers, preserve
+                * BPF_R6-BPF_R9, and store return value into BPF_R0.
                 *
                 * Implicit input:
-                *   ctx
+                *   ctx == skb == BPF_R6 == CTX
                 *
                 * Explicit input:
-                *   X == any register
-                *   K == 32-bit immediate
+                *   SRC == any register
+                *   IMM == 32-bit immediate
                 *
                 * Output:
-                *   R0 - 8/16/32-bit skb data converted to cpu endianness
+                *   BPF_R0 - 8/16/32-bit skb data converted to cpu endianness
                 */
-               ptr = load_pointer((struct sk_buff *) ctx, off, 4, &tmp);
+
+               ptr = load_pointer((struct sk_buff *) (unsigned long) CTX, off, 4, &tmp);
                if (likely(ptr != NULL)) {
-                       R0 = get_unaligned_be32(ptr);
+                       BPF_R0 = get_unaligned_be32(ptr);
                        CONT;
                }
+
                return 0;
-       BPF_LD_BPF_ABS_BPF_H: /* R0 = ntohs(*(u16 *) (skb->data + K)) */
-               off = K;
+       LD_ABS_H: /* BPF_R0 = ntohs(*(u16 *) (skb->data + imm32)) */
+               off = IMM;
 load_half:
-               ptr = load_pointer((struct sk_buff *) ctx, off, 2, &tmp);
+               ptr = load_pointer((struct sk_buff *) (unsigned long) CTX, off, 2, &tmp);
                if (likely(ptr != NULL)) {
-                       R0 = get_unaligned_be16(ptr);
+                       BPF_R0 = get_unaligned_be16(ptr);
                        CONT;
                }
+
                return 0;
-       BPF_LD_BPF_ABS_BPF_B: /* R0 = *(u8 *) (ctx + K) */
-               off = K;
+       LD_ABS_B: /* BPF_R0 = *(u8 *) (skb->data + imm32) */
+               off = IMM;
 load_byte:
-               ptr = load_pointer((struct sk_buff *) ctx, off, 1, &tmp);
+               ptr = load_pointer((struct sk_buff *) (unsigned long) CTX, off, 1, &tmp);
                if (likely(ptr != NULL)) {
-                       R0 = *(u8 *)ptr;
+                       BPF_R0 = *(u8 *)ptr;
                        CONT;
                }
+
                return 0;
-       BPF_LD_BPF_IND_BPF_W: /* R0 = ntohl(*(u32 *) (skb->data + X + K)) */
-               off = K + X;
+       LD_IND_W: /* BPF_R0 = ntohl(*(u32 *) (skb->data + src_reg + imm32)) */
+               off = IMM + SRC;
                goto load_word;
-       BPF_LD_BPF_IND_BPF_H: /* R0 = ntohs(*(u16 *) (skb->data + X + K)) */
-               off = K + X;
+       LD_IND_H: /* BPF_R0 = ntohs(*(u16 *) (skb->data + src_reg + imm32)) */
+               off = IMM + SRC;
                goto load_half;
-       BPF_LD_BPF_IND_BPF_B: /* R0 = *(u8 *) (skb->data + X + K) */
-               off = K + X;
+       LD_IND_B: /* BPF_R0 = *(u8 *) (skb->data + src_reg + imm32) */
+               off = IMM + SRC;
                goto load_byte;
 
        default_label:
                /* If we ever reach this, we have a bug somewhere. */
                WARN_RATELIMIT(1, "unknown opcode %02x\n", insn->code);
                return 0;
-#undef CONT_JMP
-#undef CONT
-
-#undef R0
-#undef X
-#undef A
-#undef K
 }
 
-u32 sk_run_filter_int_seccomp(const struct seccomp_data *ctx,
-                             const struct sock_filter_int *insni)
-    __attribute__ ((alias ("__sk_run_filter")));
-
-u32 sk_run_filter_int_skb(const struct sk_buff *ctx,
-                         const struct sock_filter_int *insni)
-    __attribute__ ((alias ("__sk_run_filter")));
-EXPORT_SYMBOL_GPL(sk_run_filter_int_skb);
-
 /* Helper to find the offset of pkt_type in sk_buff structure. We want
  * to make sure its still a 3bit field starting at a byte boundary;
  * taken from arch/x86/net/bpf_jit_comp.c.
  */
+#ifdef __BIG_ENDIAN_BITFIELD
+#define PKT_TYPE_MAX   (7 << 5)
+#else
 #define PKT_TYPE_MAX   7
+#endif
 static unsigned int pkt_type_offset(void)
 {
        struct sk_buff skb_probe = { .pkt_type = ~0, };
@@ -594,16 +604,14 @@ static unsigned int pkt_type_offset(void)
        return -1;
 }
 
-static u64 __skb_get_pay_offset(u64 ctx, u64 A, u64 X, u64 r4, u64 r5)
+static u64 __skb_get_pay_offset(u64 ctx, u64 a, u64 x, u64 r4, u64 r5)
 {
-       struct sk_buff *skb = (struct sk_buff *)(long) ctx;
-
-       return __skb_get_poff(skb);
+       return __skb_get_poff((struct sk_buff *)(unsigned long) ctx);
 }
 
-static u64 __skb_get_nlattr(u64 ctx, u64 A, u64 X, u64 r4, u64 r5)
+static u64 __skb_get_nlattr(u64 ctx, u64 a, u64 x, u64 r4, u64 r5)
 {
-       struct sk_buff *skb = (struct sk_buff *)(long) ctx;
+       struct sk_buff *skb = (struct sk_buff *)(unsigned long) ctx;
        struct nlattr *nla;
 
        if (skb_is_nonlinear(skb))
@@ -612,19 +620,19 @@ static u64 __skb_get_nlattr(u64 ctx, u64 A, u64 X, u64 r4, u64 r5)
        if (skb->len < sizeof(struct nlattr))
                return 0;
 
-       if (A > skb->len - sizeof(struct nlattr))
+       if (a > skb->len - sizeof(struct nlattr))
                return 0;
 
-       nla = nla_find((struct nlattr *) &skb->data[A], skb->len - A, X);
+       nla = nla_find((struct nlattr *) &skb->data[a], skb->len - a, x);
        if (nla)
                return (void *) nla - (void *) skb->data;
 
        return 0;
 }
 
-static u64 __skb_get_nlattr_nest(u64 ctx, u64 A, u64 X, u64 r4, u64 r5)
+static u64 __skb_get_nlattr_nest(u64 ctx, u64 a, u64 x, u64 r4, u64 r5)
 {
-       struct sk_buff *skb = (struct sk_buff *)(long) ctx;
+       struct sk_buff *skb = (struct sk_buff *)(unsigned long) ctx;
        struct nlattr *nla;
 
        if (skb_is_nonlinear(skb))
@@ -633,25 +641,31 @@ static u64 __skb_get_nlattr_nest(u64 ctx, u64 A, u64 X, u64 r4, u64 r5)
        if (skb->len < sizeof(struct nlattr))
                return 0;
 
-       if (A > skb->len - sizeof(struct nlattr))
+       if (a > skb->len - sizeof(struct nlattr))
                return 0;
 
-       nla = (struct nlattr *) &skb->data[A];
-       if (nla->nla_len > skb->len - A)
+       nla = (struct nlattr *) &skb->data[a];
+       if (nla->nla_len > skb->len - a)
                return 0;
 
-       nla = nla_find_nested(nla, X);
+       nla = nla_find_nested(nla, x);
        if (nla)
                return (void *) nla - (void *) skb->data;
 
        return 0;
 }
 
-static u64 __get_raw_cpu_id(u64 ctx, u64 A, u64 X, u64 r4, u64 r5)
+static u64 __get_raw_cpu_id(u64 ctx, u64 a, u64 x, u64 r4, u64 r5)
 {
        return raw_smp_processor_id();
 }
 
+/* note that this only generates 32-bit random numbers */
+static u64 __get_random_u32(u64 ctx, u64 a, u64 x, u64 r4, u64 r5)
+{
+       return prandom_u32();
+}
+
 static bool convert_bpf_extensions(struct sock_filter *fp,
                                   struct sock_filter_int **insnp)
 {
@@ -661,119 +675,83 @@ static bool convert_bpf_extensions(struct sock_filter *fp,
        case SKF_AD_OFF + SKF_AD_PROTOCOL:
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, protocol) != 2);
 
-               insn->code = BPF_LDX | BPF_MEM | BPF_H;
-               insn->a_reg = A_REG;
-               insn->x_reg = CTX_REG;
-               insn->off = offsetof(struct sk_buff, protocol);
-               insn++;
-
+               /* A = *(u16 *) (CTX + offsetof(protocol)) */
+               *insn++ = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_CTX,
+                                     offsetof(struct sk_buff, protocol));
                /* A = ntohs(A) [emitting a nop or swap16] */
-               insn->code = BPF_ALU | BPF_END | BPF_FROM_BE;
-               insn->a_reg = A_REG;
-               insn->imm = 16;
+               *insn = BPF_ENDIAN(BPF_FROM_BE, BPF_REG_A, 16);
                break;
 
        case SKF_AD_OFF + SKF_AD_PKTTYPE:
-               insn->code = BPF_LDX | BPF_MEM | BPF_B;
-               insn->a_reg = A_REG;
-               insn->x_reg = CTX_REG;
-               insn->off = pkt_type_offset();
+               *insn = BPF_LDX_MEM(BPF_B, BPF_REG_A, BPF_REG_CTX,
+                                   pkt_type_offset());
                if (insn->off < 0)
                        return false;
                insn++;
-
-               insn->code = BPF_ALU | BPF_AND | BPF_K;
-               insn->a_reg = A_REG;
-               insn->imm = PKT_TYPE_MAX;
+               *insn = BPF_ALU32_IMM(BPF_AND, BPF_REG_A, PKT_TYPE_MAX);
+#ifdef __BIG_ENDIAN_BITFIELD
+               insn++;
+                *insn = BPF_ALU32_IMM(BPF_RSH, BPF_REG_A, 5);
+#endif
                break;
 
        case SKF_AD_OFF + SKF_AD_IFINDEX:
        case SKF_AD_OFF + SKF_AD_HATYPE:
-               if (FIELD_SIZEOF(struct sk_buff, dev) == 8)
-                       insn->code = BPF_LDX | BPF_MEM | BPF_DW;
-               else
-                       insn->code = BPF_LDX | BPF_MEM | BPF_W;
-               insn->a_reg = TMP_REG;
-               insn->x_reg = CTX_REG;
-               insn->off = offsetof(struct sk_buff, dev);
-               insn++;
-
-               insn->code = BPF_JMP | BPF_JNE | BPF_K;
-               insn->a_reg = TMP_REG;
-               insn->imm = 0;
-               insn->off = 1;
-               insn++;
-
-               insn->code = BPF_JMP | BPF_EXIT;
-               insn++;
-
                BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, ifindex) != 4);
                BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, type) != 2);
-
-               insn->a_reg = A_REG;
-               insn->x_reg = TMP_REG;
-
-               if (fp->k == SKF_AD_OFF + SKF_AD_IFINDEX) {
-                       insn->code = BPF_LDX | BPF_MEM | BPF_W;
-                       insn->off = offsetof(struct net_device, ifindex);
-               } else {
-                       insn->code = BPF_LDX | BPF_MEM | BPF_H;
-                       insn->off = offsetof(struct net_device, type);
-               }
+               BUILD_BUG_ON(bytes_to_bpf_size(FIELD_SIZEOF(struct sk_buff, dev)) < 0);
+
+               *insn++ = BPF_LDX_MEM(bytes_to_bpf_size(FIELD_SIZEOF(struct sk_buff, dev)),
+                                     BPF_REG_TMP, BPF_REG_CTX,
+                                     offsetof(struct sk_buff, dev));
+               /* if (tmp != 0) goto pc + 1 */
+               *insn++ = BPF_JMP_IMM(BPF_JNE, BPF_REG_TMP, 0, 1);
+               *insn++ = BPF_EXIT_INSN();
+               if (fp->k == SKF_AD_OFF + SKF_AD_IFINDEX)
+                       *insn = BPF_LDX_MEM(BPF_W, BPF_REG_A, BPF_REG_TMP,
+                                           offsetof(struct net_device, ifindex));
+               else
+                       *insn = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_TMP,
+                                           offsetof(struct net_device, type));
                break;
 
        case SKF_AD_OFF + SKF_AD_MARK:
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4);
 
-               insn->code = BPF_LDX | BPF_MEM | BPF_W;
-               insn->a_reg = A_REG;
-               insn->x_reg = CTX_REG;
-               insn->off = offsetof(struct sk_buff, mark);
+               *insn = BPF_LDX_MEM(BPF_W, BPF_REG_A, BPF_REG_CTX,
+                                   offsetof(struct sk_buff, mark));
                break;
 
        case SKF_AD_OFF + SKF_AD_RXHASH:
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4);
 
-               insn->code = BPF_LDX | BPF_MEM | BPF_W;
-               insn->a_reg = A_REG;
-               insn->x_reg = CTX_REG;
-               insn->off = offsetof(struct sk_buff, hash);
+               *insn = BPF_LDX_MEM(BPF_W, BPF_REG_A, BPF_REG_CTX,
+                                   offsetof(struct sk_buff, hash));
                break;
 
        case SKF_AD_OFF + SKF_AD_QUEUE:
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, queue_mapping) != 2);
 
-               insn->code = BPF_LDX | BPF_MEM | BPF_H;
-               insn->a_reg = A_REG;
-               insn->x_reg = CTX_REG;
-               insn->off = offsetof(struct sk_buff, queue_mapping);
+               *insn = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_CTX,
+                                   offsetof(struct sk_buff, queue_mapping));
                break;
 
        case SKF_AD_OFF + SKF_AD_VLAN_TAG:
        case SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT:
                BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2);
-
-               insn->code = BPF_LDX | BPF_MEM | BPF_H;
-               insn->a_reg = A_REG;
-               insn->x_reg = CTX_REG;
-               insn->off = offsetof(struct sk_buff, vlan_tci);
-               insn++;
-
                BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000);
 
+               /* A = *(u16 *) (CTX + offsetof(vlan_tci)) */
+               *insn++ = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_CTX,
+                                     offsetof(struct sk_buff, vlan_tci));
                if (fp->k == SKF_AD_OFF + SKF_AD_VLAN_TAG) {
-                       insn->code = BPF_ALU | BPF_AND | BPF_K;
-                       insn->a_reg = A_REG;
-                       insn->imm = ~VLAN_TAG_PRESENT;
+                       *insn = BPF_ALU32_IMM(BPF_AND, BPF_REG_A,
+                                             ~VLAN_TAG_PRESENT);
                } else {
-                       insn->code = BPF_ALU | BPF_RSH | BPF_K;
-                       insn->a_reg = A_REG;
-                       insn->imm = 12;
-                       insn++;
-
-                       insn->code = BPF_ALU | BPF_AND | BPF_K;
-                       insn->a_reg = A_REG;
-                       insn->imm = 1;
+                       /* A >>= 12 */
+                       *insn++ = BPF_ALU32_IMM(BPF_RSH, BPF_REG_A, 12);
+                       /* A &= 1 */
+                       *insn = BPF_ALU32_IMM(BPF_AND, BPF_REG_A, 1);
                }
                break;
 
@@ -781,46 +759,36 @@ static bool convert_bpf_extensions(struct sock_filter *fp,
        case SKF_AD_OFF + SKF_AD_NLATTR:
        case SKF_AD_OFF + SKF_AD_NLATTR_NEST:
        case SKF_AD_OFF + SKF_AD_CPU:
-               /* arg1 = ctx */
-               insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
-               insn->a_reg = ARG1_REG;
-               insn->x_reg = CTX_REG;
-               insn++;
-
+       case SKF_AD_OFF + SKF_AD_RANDOM:
+               /* arg1 = CTX */
+               *insn++ = BPF_MOV64_REG(BPF_REG_ARG1, BPF_REG_CTX);
                /* arg2 = A */
-               insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
-               insn->a_reg = ARG2_REG;
-               insn->x_reg = A_REG;
-               insn++;
-
+               *insn++ = BPF_MOV64_REG(BPF_REG_ARG2, BPF_REG_A);
                /* arg3 = X */
-               insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
-               insn->a_reg = ARG3_REG;
-               insn->x_reg = X_REG;
-               insn++;
-
-               /* Emit call(ctx, arg2=A, arg3=X) */
-               insn->code = BPF_JMP | BPF_CALL;
+               *insn++ = BPF_MOV64_REG(BPF_REG_ARG3, BPF_REG_X);
+               /* Emit call(arg1=CTX, arg2=A, arg3=X) */
                switch (fp->k) {
                case SKF_AD_OFF + SKF_AD_PAY_OFFSET:
-                       insn->imm = __skb_get_pay_offset - __bpf_call_base;
+                       *insn = BPF_EMIT_CALL(__skb_get_pay_offset);
                        break;
                case SKF_AD_OFF + SKF_AD_NLATTR:
-                       insn->imm = __skb_get_nlattr - __bpf_call_base;
+                       *insn = BPF_EMIT_CALL(__skb_get_nlattr);
                        break;
                case SKF_AD_OFF + SKF_AD_NLATTR_NEST:
-                       insn->imm = __skb_get_nlattr_nest - __bpf_call_base;
+                       *insn = BPF_EMIT_CALL(__skb_get_nlattr_nest);
                        break;
                case SKF_AD_OFF + SKF_AD_CPU:
-                       insn->imm = __get_raw_cpu_id - __bpf_call_base;
+                       *insn = BPF_EMIT_CALL(__get_raw_cpu_id);
+                       break;
+               case SKF_AD_OFF + SKF_AD_RANDOM:
+                       *insn = BPF_EMIT_CALL(__get_random_u32);
                        break;
                }
                break;
 
        case SKF_AD_OFF + SKF_AD_ALU_XOR_X:
-               insn->code = BPF_ALU | BPF_XOR | BPF_X;
-               insn->a_reg = A_REG;
-               insn->x_reg = X_REG;
+               /* A ^= X */
+               *insn = BPF_ALU32_REG(BPF_XOR, BPF_REG_A, BPF_REG_X);
                break;
 
        default:
@@ -870,7 +838,7 @@ int sk_convert_filter(struct sock_filter *prog, int len,
        u8 bpf_src;
 
        BUILD_BUG_ON(BPF_MEMWORDS * sizeof(u32) > MAX_BPF_STACK);
-       BUILD_BUG_ON(FP_REG + 1 != MAX_BPF_REG);
+       BUILD_BUG_ON(BPF_REG_FP + 1 != MAX_BPF_REG);
 
        if (len <= 0 || len >= BPF_MAXINSNS)
                return -EINVAL;
@@ -885,11 +853,8 @@ int sk_convert_filter(struct sock_filter *prog, int len,
        new_insn = new_prog;
        fp = prog;
 
-       if (new_insn) {
-               new_insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
-               new_insn->a_reg = CTX_REG;
-               new_insn->x_reg = ARG1_REG;
-       }
+       if (new_insn)
+               *new_insn = BPF_MOV64_REG(BPF_REG_CTX, BPF_REG_ARG1);
        new_insn++;
 
        for (i = 0; i < len; fp++, i++) {
@@ -937,17 +902,16 @@ int sk_convert_filter(struct sock_filter *prog, int len,
                            convert_bpf_extensions(fp, &insn))
                                break;
 
-                       insn->code = fp->code;
-                       insn->a_reg = A_REG;
-                       insn->x_reg = X_REG;
-                       insn->imm = fp->k;
+                       *insn = BPF_RAW_INSN(fp->code, BPF_REG_A, BPF_REG_X, 0, fp->k);
                        break;
 
-               /* Jump opcodes map as-is, but offsets need adjustment. */
-               case BPF_JMP | BPF_JA:
-                       target = i + fp->k + 1;
-                       insn->code = fp->code;
-#define EMIT_JMP                                                       \
+               /* Jump transformation cannot use BPF block macros
+                * everywhere as offset calculation and target updates
+                * require a bit more work than the rest, i.e. jump
+                * opcodes map as-is, but offsets need adjustment.
+                */
+
+#define BPF_EMIT_JMP                                                   \
        do {                                                            \
                if (target >= len || target < 0)                        \
                        goto err;                                       \
@@ -956,7 +920,10 @@ int sk_convert_filter(struct sock_filter *prog, int len,
                insn->off -= insn - tmp_insns;                          \
        } while (0)
 
-                       EMIT_JMP;
+               case BPF_JMP | BPF_JA:
+                       target = i + fp->k + 1;
+                       insn->code = fp->code;
+                       BPF_EMIT_JMP;
                        break;
 
                case BPF_JMP | BPF_JEQ | BPF_K:
@@ -972,17 +939,14 @@ int sk_convert_filter(struct sock_filter *prog, int len,
                                 * immediate into tmp register and use it
                                 * in compare insn.
                                 */
-                               insn->code = BPF_ALU | BPF_MOV | BPF_K;
-                               insn->a_reg = TMP_REG;
-                               insn->imm = fp->k;
-                               insn++;
+                               *insn++ = BPF_MOV32_IMM(BPF_REG_TMP, fp->k);
 
-                               insn->a_reg = A_REG;
-                               insn->x_reg = TMP_REG;
+                               insn->dst_reg = BPF_REG_A;
+                               insn->src_reg = BPF_REG_TMP;
                                bpf_src = BPF_X;
                        } else {
-                               insn->a_reg = A_REG;
-                               insn->x_reg = X_REG;
+                               insn->dst_reg = BPF_REG_A;
+                               insn->src_reg = BPF_REG_X;
                                insn->imm = fp->k;
                                bpf_src = BPF_SRC(fp->code);
                        }
@@ -991,7 +955,7 @@ int sk_convert_filter(struct sock_filter *prog, int len,
                        if (fp->jf == 0) {
                                insn->code = BPF_JMP | BPF_OP(fp->code) | bpf_src;
                                target = i + fp->jt + 1;
-                               EMIT_JMP;
+                               BPF_EMIT_JMP;
                                break;
                        }
 
@@ -999,127 +963,94 @@ int sk_convert_filter(struct sock_filter *prog, int len,
                        if (fp->jt == 0 && BPF_OP(fp->code) == BPF_JEQ) {
                                insn->code = BPF_JMP | BPF_JNE | bpf_src;
                                target = i + fp->jf + 1;
-                               EMIT_JMP;
+                               BPF_EMIT_JMP;
                                break;
                        }
 
                        /* Other jumps are mapped into two insns: Jxx and JA. */
                        target = i + fp->jt + 1;
                        insn->code = BPF_JMP | BPF_OP(fp->code) | bpf_src;
-                       EMIT_JMP;
+                       BPF_EMIT_JMP;
                        insn++;
 
                        insn->code = BPF_JMP | BPF_JA;
                        target = i + fp->jf + 1;
-                       EMIT_JMP;
+                       BPF_EMIT_JMP;
                        break;
 
                /* ldxb 4 * ([14] & 0xf) is remaped into 6 insns. */
                case BPF_LDX | BPF_MSH | BPF_B:
-                       insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
-                       insn->a_reg = TMP_REG;
-                       insn->x_reg = A_REG;
-                       insn++;
-
-                       insn->code = BPF_LD | BPF_ABS | BPF_B;
-                       insn->a_reg = A_REG;
-                       insn->imm = fp->k;
-                       insn++;
-
-                       insn->code = BPF_ALU | BPF_AND | BPF_K;
-                       insn->a_reg = A_REG;
-                       insn->imm = 0xf;
-                       insn++;
-
-                       insn->code = BPF_ALU | BPF_LSH | BPF_K;
-                       insn->a_reg = A_REG;
-                       insn->imm = 2;
-                       insn++;
-
-                       insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
-                       insn->a_reg = X_REG;
-                       insn->x_reg = A_REG;
-                       insn++;
-
-                       insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
-                       insn->a_reg = A_REG;
-                       insn->x_reg = TMP_REG;
+                       /* tmp = A */
+                       *insn++ = BPF_MOV64_REG(BPF_REG_TMP, BPF_REG_A);
+                       /* A = BPF_R0 = *(u8 *) (skb->data + K) */
+                       *insn++ = BPF_LD_ABS(BPF_B, fp->k);
+                       /* A &= 0xf */
+                       *insn++ = BPF_ALU32_IMM(BPF_AND, BPF_REG_A, 0xf);
+                       /* A <<= 2 */
+                       *insn++ = BPF_ALU32_IMM(BPF_LSH, BPF_REG_A, 2);
+                       /* X = A */
+                       *insn++ = BPF_MOV64_REG(BPF_REG_X, BPF_REG_A);
+                       /* A = tmp */
+                       *insn = BPF_MOV64_REG(BPF_REG_A, BPF_REG_TMP);
                        break;
 
                /* RET_K, RET_A are remaped into 2 insns. */
                case BPF_RET | BPF_A:
                case BPF_RET | BPF_K:
-                       insn->code = BPF_ALU | BPF_MOV |
-                                    (BPF_RVAL(fp->code) == BPF_K ?
-                                     BPF_K : BPF_X);
-                       insn->a_reg = 0;
-                       insn->x_reg = A_REG;
-                       insn->imm = fp->k;
-                       insn++;
-
-                       insn->code = BPF_JMP | BPF_EXIT;
+                       *insn++ = BPF_MOV32_RAW(BPF_RVAL(fp->code) == BPF_K ?
+                                               BPF_K : BPF_X, BPF_REG_0,
+                                               BPF_REG_A, fp->k);
+                       *insn = BPF_EXIT_INSN();
                        break;
 
                /* Store to stack. */
                case BPF_ST:
                case BPF_STX:
-                       insn->code = BPF_STX | BPF_MEM | BPF_W;
-                       insn->a_reg = FP_REG;
-                       insn->x_reg = fp->code == BPF_ST ? A_REG : X_REG;
-                       insn->off = -(BPF_MEMWORDS - fp->k) * 4;
+                       *insn = BPF_STX_MEM(BPF_W, BPF_REG_FP, BPF_CLASS(fp->code) ==
+                                           BPF_ST ? BPF_REG_A : BPF_REG_X,
+                                           -(BPF_MEMWORDS - fp->k) * 4);
                        break;
 
                /* Load from stack. */
                case BPF_LD | BPF_MEM:
                case BPF_LDX | BPF_MEM:
-                       insn->code = BPF_LDX | BPF_MEM | BPF_W;
-                       insn->a_reg = BPF_CLASS(fp->code) == BPF_LD ?
-                                     A_REG : X_REG;
-                       insn->x_reg = FP_REG;
-                       insn->off = -(BPF_MEMWORDS - fp->k) * 4;
+                       *insn = BPF_LDX_MEM(BPF_W, BPF_CLASS(fp->code) == BPF_LD  ?
+                                           BPF_REG_A : BPF_REG_X, BPF_REG_FP,
+                                           -(BPF_MEMWORDS - fp->k) * 4);
                        break;
 
                /* A = K or X = K */
                case BPF_LD | BPF_IMM:
                case BPF_LDX | BPF_IMM:
-                       insn->code = BPF_ALU | BPF_MOV | BPF_K;
-                       insn->a_reg = BPF_CLASS(fp->code) == BPF_LD ?
-                                     A_REG : X_REG;
-                       insn->imm = fp->k;
+                       *insn = BPF_MOV32_IMM(BPF_CLASS(fp->code) == BPF_LD ?
+                                             BPF_REG_A : BPF_REG_X, fp->k);
                        break;
 
                /* X = A */
                case BPF_MISC | BPF_TAX:
-                       insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
-                       insn->a_reg = X_REG;
-                       insn->x_reg = A_REG;
+                       *insn = BPF_MOV64_REG(BPF_REG_X, BPF_REG_A);
                        break;
 
                /* A = X */
                case BPF_MISC | BPF_TXA:
-                       insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
-                       insn->a_reg = A_REG;
-                       insn->x_reg = X_REG;
+                       *insn = BPF_MOV64_REG(BPF_REG_A, BPF_REG_X);
                        break;
 
                /* A = skb->len or X = skb->len */
                case BPF_LD | BPF_W | BPF_LEN:
                case BPF_LDX | BPF_W | BPF_LEN:
-                       insn->code = BPF_LDX | BPF_MEM | BPF_W;
-                       insn->a_reg = BPF_CLASS(fp->code) == BPF_LD ?
-                                     A_REG : X_REG;
-                       insn->x_reg = CTX_REG;
-                       insn->off = offsetof(struct sk_buff, len);
+                       *insn = BPF_LDX_MEM(BPF_W, BPF_CLASS(fp->code) == BPF_LD ?
+                                           BPF_REG_A : BPF_REG_X, BPF_REG_CTX,
+                                           offsetof(struct sk_buff, len));
                        break;
 
-               /* access seccomp_data fields */
+               /* Access seccomp_data fields. */
                case BPF_LDX | BPF_ABS | BPF_W:
-                       insn->code = BPF_LDX | BPF_MEM | BPF_W;
-                       insn->a_reg = A_REG;
-                       insn->x_reg = CTX_REG;
-                       insn->off = fp->k;
+                       /* A = *(u32 *) (ctx + K) */
+                       *insn = BPF_LDX_MEM(BPF_W, BPF_REG_A, BPF_REG_CTX, fp->k);
                        break;
 
+               /* Unkown instruction. */
                default:
                        goto err;
                }
@@ -1128,7 +1059,6 @@ int sk_convert_filter(struct sock_filter *prog, int len,
                if (new_prog)
                        memcpy(new_insn, tmp_insns,
                               sizeof(*insn) * (insn - tmp_insns));
-
                new_insn += insn - tmp_insns;
        }
 
@@ -1143,7 +1073,6 @@ int sk_convert_filter(struct sock_filter *prog, int len,
                new_flen = new_insn - new_prog;
                if (pass > 2)
                        goto err;
-
                goto do_pass;
        }
 
@@ -1167,44 +1096,46 @@ int sk_convert_filter(struct sock_filter *prog, int len,
  */
 static int check_load_and_stores(struct sock_filter *filter, int flen)
 {
-       u16 *masks, memvalid = 0; /* one bit per cell, 16 cells */
+       u16 *masks, memvalid = 0; /* One bit per cell, 16 cells */
        int pc, ret = 0;
 
        BUILD_BUG_ON(BPF_MEMWORDS > 16);
+
        masks = kmalloc(flen * sizeof(*masks), GFP_KERNEL);
        if (!masks)
                return -ENOMEM;
+
        memset(masks, 0xff, flen * sizeof(*masks));
 
        for (pc = 0; pc < flen; pc++) {
                memvalid &= masks[pc];
 
                switch (filter[pc].code) {
-               case BPF_S_ST:
-               case BPF_S_STX:
+               case BPF_ST:
+               case BPF_STX:
                        memvalid |= (1 << filter[pc].k);
                        break;
-               case BPF_S_LD_MEM:
-               case BPF_S_LDX_MEM:
+               case BPF_LD | BPF_MEM:
+               case BPF_LDX | BPF_MEM:
                        if (!(memvalid & (1 << filter[pc].k))) {
                                ret = -EINVAL;
                                goto error;
                        }
                        break;
-               case BPF_S_JMP_JA:
-                       /* a jump must set masks on target */
+               case BPF_JMP | BPF_JA:
+                       /* A jump must set masks on target */
                        masks[pc + 1 + filter[pc].k] &= memvalid;
                        memvalid = ~0;
                        break;
-               case BPF_S_JMP_JEQ_K:
-               case BPF_S_JMP_JEQ_X:
-               case BPF_S_JMP_JGE_K:
-               case BPF_S_JMP_JGE_X:
-               case BPF_S_JMP_JGT_K:
-               case BPF_S_JMP_JGT_X:
-               case BPF_S_JMP_JSET_X:
-               case BPF_S_JMP_JSET_K:
-                       /* a jump must set masks on targets */
+               case BPF_JMP | BPF_JEQ | BPF_K:
+               case BPF_JMP | BPF_JEQ | BPF_X:
+               case BPF_JMP | BPF_JGE | BPF_K:
+               case BPF_JMP | BPF_JGE | BPF_X:
+               case BPF_JMP | BPF_JGT | BPF_K:
+               case BPF_JMP | BPF_JGT | BPF_X:
+               case BPF_JMP | BPF_JSET | BPF_K:
+               case BPF_JMP | BPF_JSET | BPF_X:
+                       /* A jump must set masks on targets */
                        masks[pc + 1 + filter[pc].jt] &= memvalid;
                        masks[pc + 1 + filter[pc].jf] &= memvalid;
                        memvalid = ~0;
@@ -1216,6 +1147,72 @@ static int check_load_and_stores(struct sock_filter *filter, int flen)
        return ret;
 }
 
+static bool chk_code_allowed(u16 code_to_probe)
+{
+       static const bool codes[] = {
+               /* 32 bit ALU operations */
+               [BPF_ALU | BPF_ADD | BPF_K] = true,
+               [BPF_ALU | BPF_ADD | BPF_X] = true,
+               [BPF_ALU | BPF_SUB | BPF_K] = true,
+               [BPF_ALU | BPF_SUB | BPF_X] = true,
+               [BPF_ALU | BPF_MUL | BPF_K] = true,
+               [BPF_ALU | BPF_MUL | BPF_X] = true,
+               [BPF_ALU | BPF_DIV | BPF_K] = true,
+               [BPF_ALU | BPF_DIV | BPF_X] = true,
+               [BPF_ALU | BPF_MOD | BPF_K] = true,
+               [BPF_ALU | BPF_MOD | BPF_X] = true,
+               [BPF_ALU | BPF_AND | BPF_K] = true,
+               [BPF_ALU | BPF_AND | BPF_X] = true,
+               [BPF_ALU | BPF_OR | BPF_K] = true,
+               [BPF_ALU | BPF_OR | BPF_X] = true,
+               [BPF_ALU | BPF_XOR | BPF_K] = true,
+               [BPF_ALU | BPF_XOR | BPF_X] = true,
+               [BPF_ALU | BPF_LSH | BPF_K] = true,
+               [BPF_ALU | BPF_LSH | BPF_X] = true,
+               [BPF_ALU | BPF_RSH | BPF_K] = true,
+               [BPF_ALU | BPF_RSH | BPF_X] = true,
+               [BPF_ALU | BPF_NEG] = true,
+               /* Load instructions */
+               [BPF_LD | BPF_W | BPF_ABS] = true,
+               [BPF_LD | BPF_H | BPF_ABS] = true,
+               [BPF_LD | BPF_B | BPF_ABS] = true,
+               [BPF_LD | BPF_W | BPF_LEN] = true,
+               [BPF_LD | BPF_W | BPF_IND] = true,
+               [BPF_LD | BPF_H | BPF_IND] = true,
+               [BPF_LD | BPF_B | BPF_IND] = true,
+               [BPF_LD | BPF_IMM] = true,
+               [BPF_LD | BPF_MEM] = true,
+               [BPF_LDX | BPF_W | BPF_LEN] = true,
+               [BPF_LDX | BPF_B | BPF_MSH] = true,
+               [BPF_LDX | BPF_IMM] = true,
+               [BPF_LDX | BPF_MEM] = true,
+               /* Store instructions */
+               [BPF_ST] = true,
+               [BPF_STX] = true,
+               /* Misc instructions */
+               [BPF_MISC | BPF_TAX] = true,
+               [BPF_MISC | BPF_TXA] = true,
+               /* Return instructions */
+               [BPF_RET | BPF_K] = true,
+               [BPF_RET | BPF_A] = true,
+               /* Jump instructions */
+               [BPF_JMP | BPF_JA] = true,
+               [BPF_JMP | BPF_JEQ | BPF_K] = true,
+               [BPF_JMP | BPF_JEQ | BPF_X] = true,
+               [BPF_JMP | BPF_JGE | BPF_K] = true,
+               [BPF_JMP | BPF_JGE | BPF_X] = true,
+               [BPF_JMP | BPF_JGT | BPF_K] = true,
+               [BPF_JMP | BPF_JGT | BPF_X] = true,
+               [BPF_JMP | BPF_JSET | BPF_K] = true,
+               [BPF_JMP | BPF_JSET | BPF_X] = true,
+       };
+
+       if (code_to_probe >= ARRAY_SIZE(codes))
+               return false;
+
+       return codes[code_to_probe];
+}
+
 /**
  *     sk_chk_filter - verify socket filter code
  *     @filter: filter to verify
@@ -1232,153 +1229,76 @@ static int check_load_and_stores(struct sock_filter *filter, int flen)
  */
 int sk_chk_filter(struct sock_filter *filter, unsigned int flen)
 {
-       /*
-        * Valid instructions are initialized to non-0.
-        * Invalid instructions are initialized to 0.
-        */
-       static const u8 codes[] = {
-               [BPF_ALU|BPF_ADD|BPF_K]  = BPF_S_ALU_ADD_K,
-               [BPF_ALU|BPF_ADD|BPF_X]  = BPF_S_ALU_ADD_X,
-               [BPF_ALU|BPF_SUB|BPF_K]  = BPF_S_ALU_SUB_K,
-               [BPF_ALU|BPF_SUB|BPF_X]  = BPF_S_ALU_SUB_X,
-               [BPF_ALU|BPF_MUL|BPF_K]  = BPF_S_ALU_MUL_K,
-               [BPF_ALU|BPF_MUL|BPF_X]  = BPF_S_ALU_MUL_X,
-               [BPF_ALU|BPF_DIV|BPF_X]  = BPF_S_ALU_DIV_X,
-               [BPF_ALU|BPF_MOD|BPF_K]  = BPF_S_ALU_MOD_K,
-               [BPF_ALU|BPF_MOD|BPF_X]  = BPF_S_ALU_MOD_X,
-               [BPF_ALU|BPF_AND|BPF_K]  = BPF_S_ALU_AND_K,
-               [BPF_ALU|BPF_AND|BPF_X]  = BPF_S_ALU_AND_X,
-               [BPF_ALU|BPF_OR|BPF_K]   = BPF_S_ALU_OR_K,
-               [BPF_ALU|BPF_OR|BPF_X]   = BPF_S_ALU_OR_X,
-               [BPF_ALU|BPF_XOR|BPF_K]  = BPF_S_ALU_XOR_K,
-               [BPF_ALU|BPF_XOR|BPF_X]  = BPF_S_ALU_XOR_X,
-               [BPF_ALU|BPF_LSH|BPF_K]  = BPF_S_ALU_LSH_K,
-               [BPF_ALU|BPF_LSH|BPF_X]  = BPF_S_ALU_LSH_X,
-               [BPF_ALU|BPF_RSH|BPF_K]  = BPF_S_ALU_RSH_K,
-               [BPF_ALU|BPF_RSH|BPF_X]  = BPF_S_ALU_RSH_X,
-               [BPF_ALU|BPF_NEG]        = BPF_S_ALU_NEG,
-               [BPF_LD|BPF_W|BPF_ABS]   = BPF_S_LD_W_ABS,
-               [BPF_LD|BPF_H|BPF_ABS]   = BPF_S_LD_H_ABS,
-               [BPF_LD|BPF_B|BPF_ABS]   = BPF_S_LD_B_ABS,
-               [BPF_LD|BPF_W|BPF_LEN]   = BPF_S_LD_W_LEN,
-               [BPF_LD|BPF_W|BPF_IND]   = BPF_S_LD_W_IND,
-               [BPF_LD|BPF_H|BPF_IND]   = BPF_S_LD_H_IND,
-               [BPF_LD|BPF_B|BPF_IND]   = BPF_S_LD_B_IND,
-               [BPF_LD|BPF_IMM]         = BPF_S_LD_IMM,
-               [BPF_LDX|BPF_W|BPF_LEN]  = BPF_S_LDX_W_LEN,
-               [BPF_LDX|BPF_B|BPF_MSH]  = BPF_S_LDX_B_MSH,
-               [BPF_LDX|BPF_IMM]        = BPF_S_LDX_IMM,
-               [BPF_MISC|BPF_TAX]       = BPF_S_MISC_TAX,
-               [BPF_MISC|BPF_TXA]       = BPF_S_MISC_TXA,
-               [BPF_RET|BPF_K]          = BPF_S_RET_K,
-               [BPF_RET|BPF_A]          = BPF_S_RET_A,
-               [BPF_ALU|BPF_DIV|BPF_K]  = BPF_S_ALU_DIV_K,
-               [BPF_LD|BPF_MEM]         = BPF_S_LD_MEM,
-               [BPF_LDX|BPF_MEM]        = BPF_S_LDX_MEM,
-               [BPF_ST]                 = BPF_S_ST,
-               [BPF_STX]                = BPF_S_STX,
-               [BPF_JMP|BPF_JA]         = BPF_S_JMP_JA,
-               [BPF_JMP|BPF_JEQ|BPF_K]  = BPF_S_JMP_JEQ_K,
-               [BPF_JMP|BPF_JEQ|BPF_X]  = BPF_S_JMP_JEQ_X,
-               [BPF_JMP|BPF_JGE|BPF_K]  = BPF_S_JMP_JGE_K,
-               [BPF_JMP|BPF_JGE|BPF_X]  = BPF_S_JMP_JGE_X,
-               [BPF_JMP|BPF_JGT|BPF_K]  = BPF_S_JMP_JGT_K,
-               [BPF_JMP|BPF_JGT|BPF_X]  = BPF_S_JMP_JGT_X,
-               [BPF_JMP|BPF_JSET|BPF_K] = BPF_S_JMP_JSET_K,
-               [BPF_JMP|BPF_JSET|BPF_X] = BPF_S_JMP_JSET_X,
-       };
-       int pc;
        bool anc_found;
+       int pc;
 
        if (flen == 0 || flen > BPF_MAXINSNS)
                return -EINVAL;
 
-       /* check the filter code now */
+       /* Check the filter code now */
        for (pc = 0; pc < flen; pc++) {
                struct sock_filter *ftest = &filter[pc];
-               u16 code = ftest->code;
 
-               if (code >= ARRAY_SIZE(codes))
-                       return -EINVAL;
-               code = codes[code];
-               if (!code)
+               /* May we actually operate on this code? */
+               if (!chk_code_allowed(ftest->code))
                        return -EINVAL;
+
                /* Some instructions need special checks */
-               switch (code) {
-               case BPF_S_ALU_DIV_K:
-               case BPF_S_ALU_MOD_K:
-                       /* check for division by zero */
+               switch (ftest->code) {
+               case BPF_ALU | BPF_DIV | BPF_K:
+               case BPF_ALU | BPF_MOD | BPF_K:
+                       /* Check for division by zero */
                        if (ftest->k == 0)
                                return -EINVAL;
                        break;
-               case BPF_S_LD_MEM:
-               case BPF_S_LDX_MEM:
-               case BPF_S_ST:
-               case BPF_S_STX:
-                       /* check for invalid memory addresses */
+               case BPF_LD | BPF_MEM:
+               case BPF_LDX | BPF_MEM:
+               case BPF_ST:
+               case BPF_STX:
+                       /* Check for invalid memory addresses */
                        if (ftest->k >= BPF_MEMWORDS)
                                return -EINVAL;
                        break;
-               case BPF_S_JMP_JA:
-                       /*
-                        * Note, the large ftest->k might cause loops.
+               case BPF_JMP | BPF_JA:
+                       /* Note, the large ftest->k might cause loops.
                         * Compare this with conditional jumps below,
                         * where offsets are limited. --ANK (981016)
                         */
-                       if (ftest->k >= (unsigned int)(flen-pc-1))
+                       if (ftest->k >= (unsigned int)(flen - pc - 1))
                                return -EINVAL;
                        break;
-               case BPF_S_JMP_JEQ_K:
-               case BPF_S_JMP_JEQ_X:
-               case BPF_S_JMP_JGE_K:
-               case BPF_S_JMP_JGE_X:
-               case BPF_S_JMP_JGT_K:
-               case BPF_S_JMP_JGT_X:
-               case BPF_S_JMP_JSET_X:
-               case BPF_S_JMP_JSET_K:
-                       /* for conditionals both must be safe */
+               case BPF_JMP | BPF_JEQ | BPF_K:
+               case BPF_JMP | BPF_JEQ | BPF_X:
+               case BPF_JMP | BPF_JGE | BPF_K:
+               case BPF_JMP | BPF_JGE | BPF_X:
+               case BPF_JMP | BPF_JGT | BPF_K:
+               case BPF_JMP | BPF_JGT | BPF_X:
+               case BPF_JMP | BPF_JSET | BPF_K:
+               case BPF_JMP | BPF_JSET | BPF_X:
+                       /* Both conditionals must be safe */
                        if (pc + ftest->jt + 1 >= flen ||
                            pc + ftest->jf + 1 >= flen)
                                return -EINVAL;
                        break;
-               case BPF_S_LD_W_ABS:
-               case BPF_S_LD_H_ABS:
-               case BPF_S_LD_B_ABS:
+               case BPF_LD | BPF_W | BPF_ABS:
+               case BPF_LD | BPF_H | BPF_ABS:
+               case BPF_LD | BPF_B | BPF_ABS:
                        anc_found = false;
-#define ANCILLARY(CODE) case SKF_AD_OFF + SKF_AD_##CODE:       \
-                               code = BPF_S_ANC_##CODE;        \
-                               anc_found = true;               \
-                               break
-                       switch (ftest->k) {
-                       ANCILLARY(PROTOCOL);
-                       ANCILLARY(PKTTYPE);
-                       ANCILLARY(IFINDEX);
-                       ANCILLARY(NLATTR);
-                       ANCILLARY(NLATTR_NEST);
-                       ANCILLARY(MARK);
-                       ANCILLARY(QUEUE);
-                       ANCILLARY(HATYPE);
-                       ANCILLARY(RXHASH);
-                       ANCILLARY(CPU);
-                       ANCILLARY(ALU_XOR_X);
-                       ANCILLARY(VLAN_TAG);
-                       ANCILLARY(VLAN_TAG_PRESENT);
-                       ANCILLARY(PAY_OFFSET);
-                       }
-
-                       /* ancillary operation unknown or unsupported */
+                       if (bpf_anc_helper(ftest) & BPF_ANC)
+                               anc_found = true;
+                       /* Ancillary operation unknown or unsupported */
                        if (anc_found == false && ftest->k >= SKF_AD_OFF)
                                return -EINVAL;
                }
-               ftest->code = code;
        }
 
-       /* last instruction must be a RET code */
+       /* Last instruction must be a RET code */
        switch (filter[flen - 1].code) {
-       case BPF_S_RET_K:
-       case BPF_S_RET_A:
+       case BPF_RET | BPF_K:
+       case BPF_RET | BPF_A:
                return check_load_and_stores(filter, flen);
        }
+
        return -EINVAL;
 }
 EXPORT_SYMBOL(sk_chk_filter);
@@ -1423,7 +1343,7 @@ static void sk_filter_release_rcu(struct rcu_head *rcu)
        struct sk_filter *fp = container_of(rcu, struct sk_filter, rcu);
 
        sk_release_orig_filter(fp);
-       bpf_jit_free(fp);
+       sk_filter_free(fp);
 }
 
 /**
@@ -1461,7 +1381,7 @@ static struct sk_filter *__sk_migrate_realloc(struct sk_filter *fp,
 
        fp_new = sock_kmalloc(sk, len, GFP_KERNEL);
        if (fp_new) {
-               memcpy(fp_new, fp, sizeof(struct sk_filter));
+               *fp_new = *fp;
                /* As we're kepping orig_prog in fp_new along,
                 * we need to make sure we're not evicting it
                 * from the old fp.
@@ -1478,7 +1398,7 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp,
 {
        struct sock_filter *old_prog;
        struct sk_filter *old_fp;
-       int i, err, new_len, old_len = fp->len;
+       int err, new_len, old_len = fp->len;
 
        /* We are free to overwrite insns et al right here as it
         * won't be used at this point in time anymore internally
@@ -1488,13 +1408,6 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp,
        BUILD_BUG_ON(sizeof(struct sock_filter) !=
                     sizeof(struct sock_filter_int));
 
-       /* For now, we need to unfiddle BPF_S_* identifiers in place.
-        * This can sooner or later on be subject to removal, e.g. when
-        * JITs have been converted.
-        */
-       for (i = 0; i < fp->len; i++)
-               sk_decode_filter(&fp->insns[i], &fp->insns[i]);
-
        /* Conversion cannot happen on overlapping memory areas,
         * so we need to keep the user BPF around until the 2nd
         * pass. At this time, the user BPF is stored in fp->insns.
@@ -1523,7 +1436,6 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp,
                goto out_err_free;
        }
 
-       fp->bpf_func = sk_run_filter_int_skb;
        fp->len = new_len;
 
        /* 2nd pass: remap sock_filter insns into sock_filter_int insns. */
@@ -1536,6 +1448,8 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp,
                 */
                goto out_err_free;
 
+       sk_filter_select_runtime(fp);
+
        kfree(old_prog);
        return fp;
 
@@ -1550,6 +1464,33 @@ static struct sk_filter *__sk_migrate_filter(struct sk_filter *fp,
        return ERR_PTR(err);
 }
 
+void __weak bpf_int_jit_compile(struct sk_filter *prog)
+{
+}
+
+/**
+ *     sk_filter_select_runtime - select execution runtime for BPF program
+ *     @fp: sk_filter populated with internal BPF program
+ *
+ * try to JIT internal BPF program, if JIT is not available select interpreter
+ * BPF program will be executed via SK_RUN_FILTER() macro
+ */
+void sk_filter_select_runtime(struct sk_filter *fp)
+{
+       fp->bpf_func = (void *) __sk_run_filter;
+
+       /* Probe if internal BPF can be JITed */
+       bpf_int_jit_compile(fp);
+}
+EXPORT_SYMBOL_GPL(sk_filter_select_runtime);
+
+/* free internal BPF program */
+void sk_filter_free(struct sk_filter *fp)
+{
+       bpf_jit_free(fp);
+}
+EXPORT_SYMBOL_GPL(sk_filter_free);
+
 static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp,
                                             struct sock *sk)
 {
@@ -1592,7 +1533,7 @@ static struct sk_filter *__sk_prepare_filter(struct sk_filter *fp,
  * a negative errno code is returned. On success the return is zero.
  */
 int sk_unattached_filter_create(struct sk_filter **pfp,
-                               struct sock_fprog *fprog)
+                               struct sock_fprog_kern *fprog)
 {
        unsigned int fsize = sk_filter_proglen(fprog);
        struct sk_filter *fp;
@@ -1713,83 +1654,6 @@ int sk_detach_filter(struct sock *sk)
 }
 EXPORT_SYMBOL_GPL(sk_detach_filter);
 
-void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to)
-{
-       static const u16 decodes[] = {
-               [BPF_S_ALU_ADD_K]       = BPF_ALU|BPF_ADD|BPF_K,
-               [BPF_S_ALU_ADD_X]       = BPF_ALU|BPF_ADD|BPF_X,
-               [BPF_S_ALU_SUB_K]       = BPF_ALU|BPF_SUB|BPF_K,
-               [BPF_S_ALU_SUB_X]       = BPF_ALU|BPF_SUB|BPF_X,
-               [BPF_S_ALU_MUL_K]       = BPF_ALU|BPF_MUL|BPF_K,
-               [BPF_S_ALU_MUL_X]       = BPF_ALU|BPF_MUL|BPF_X,
-               [BPF_S_ALU_DIV_X]       = BPF_ALU|BPF_DIV|BPF_X,
-               [BPF_S_ALU_MOD_K]       = BPF_ALU|BPF_MOD|BPF_K,
-               [BPF_S_ALU_MOD_X]       = BPF_ALU|BPF_MOD|BPF_X,
-               [BPF_S_ALU_AND_K]       = BPF_ALU|BPF_AND|BPF_K,
-               [BPF_S_ALU_AND_X]       = BPF_ALU|BPF_AND|BPF_X,
-               [BPF_S_ALU_OR_K]        = BPF_ALU|BPF_OR|BPF_K,
-               [BPF_S_ALU_OR_X]        = BPF_ALU|BPF_OR|BPF_X,
-               [BPF_S_ALU_XOR_K]       = BPF_ALU|BPF_XOR|BPF_K,
-               [BPF_S_ALU_XOR_X]       = BPF_ALU|BPF_XOR|BPF_X,
-               [BPF_S_ALU_LSH_K]       = BPF_ALU|BPF_LSH|BPF_K,
-               [BPF_S_ALU_LSH_X]       = BPF_ALU|BPF_LSH|BPF_X,
-               [BPF_S_ALU_RSH_K]       = BPF_ALU|BPF_RSH|BPF_K,
-               [BPF_S_ALU_RSH_X]       = BPF_ALU|BPF_RSH|BPF_X,
-               [BPF_S_ALU_NEG]         = BPF_ALU|BPF_NEG,
-               [BPF_S_LD_W_ABS]        = BPF_LD|BPF_W|BPF_ABS,
-               [BPF_S_LD_H_ABS]        = BPF_LD|BPF_H|BPF_ABS,
-               [BPF_S_LD_B_ABS]        = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_PROTOCOL]    = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_PKTTYPE]     = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_IFINDEX]     = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_NLATTR]      = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_NLATTR_NEST] = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_MARK]        = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_QUEUE]       = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_HATYPE]      = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_RXHASH]      = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_CPU]         = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_ALU_XOR_X]   = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_VLAN_TAG]    = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_VLAN_TAG_PRESENT] = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_ANC_PAY_OFFSET]  = BPF_LD|BPF_B|BPF_ABS,
-               [BPF_S_LD_W_LEN]        = BPF_LD|BPF_W|BPF_LEN,
-               [BPF_S_LD_W_IND]        = BPF_LD|BPF_W|BPF_IND,
-               [BPF_S_LD_H_IND]        = BPF_LD|BPF_H|BPF_IND,
-               [BPF_S_LD_B_IND]        = BPF_LD|BPF_B|BPF_IND,
-               [BPF_S_LD_IMM]          = BPF_LD|BPF_IMM,
-               [BPF_S_LDX_W_LEN]       = BPF_LDX|BPF_W|BPF_LEN,
-               [BPF_S_LDX_B_MSH]       = BPF_LDX|BPF_B|BPF_MSH,
-               [BPF_S_LDX_IMM]         = BPF_LDX|BPF_IMM,
-               [BPF_S_MISC_TAX]        = BPF_MISC|BPF_TAX,
-               [BPF_S_MISC_TXA]        = BPF_MISC|BPF_TXA,
-               [BPF_S_RET_K]           = BPF_RET|BPF_K,
-               [BPF_S_RET_A]           = BPF_RET|BPF_A,
-               [BPF_S_ALU_DIV_K]       = BPF_ALU|BPF_DIV|BPF_K,
-               [BPF_S_LD_MEM]          = BPF_LD|BPF_MEM,
-               [BPF_S_LDX_MEM]         = BPF_LDX|BPF_MEM,
-               [BPF_S_ST]              = BPF_ST,
-               [BPF_S_STX]             = BPF_STX,
-               [BPF_S_JMP_JA]          = BPF_JMP|BPF_JA,
-               [BPF_S_JMP_JEQ_K]       = BPF_JMP|BPF_JEQ|BPF_K,
-               [BPF_S_JMP_JEQ_X]       = BPF_JMP|BPF_JEQ|BPF_X,
-               [BPF_S_JMP_JGE_K]       = BPF_JMP|BPF_JGE|BPF_K,
-               [BPF_S_JMP_JGE_X]       = BPF_JMP|BPF_JGE|BPF_X,
-               [BPF_S_JMP_JGT_K]       = BPF_JMP|BPF_JGT|BPF_K,
-               [BPF_S_JMP_JGT_X]       = BPF_JMP|BPF_JGT|BPF_X,
-               [BPF_S_JMP_JSET_K]      = BPF_JMP|BPF_JSET|BPF_K,
-               [BPF_S_JMP_JSET_X]      = BPF_JMP|BPF_JSET|BPF_X,
-       };
-       u16 code;
-
-       code = filt->code;
-
-       to->code = decodes[code];
-       to->jt = filt->jt;
-       to->jf = filt->jf;
-       to->k = filt->k;
-}
-
 int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf,
                  unsigned int len)
 {
index 7c8ffd97496175c60d3947019bfd138bb9746938..85b62691f4f2d18b9b39bc9c610d1916f8f09273 100644 (file)
@@ -273,7 +273,7 @@ static void cleanup_net(struct work_struct *work)
 {
        const struct pernet_operations *ops;
        struct net *net, *tmp;
-       LIST_HEAD(net_kill_list);
+       struct list_head net_kill_list;
        LIST_HEAD(net_exit_list);
 
        /* Atomically snapshot the list of namespaces to cleanup */
index 0304f981f7ffa5f005b28f08f17ff8264c41bb65..fc17a9d309ac028fc61ac7db6e35a05c77513a24 100644 (file)
@@ -573,7 +573,7 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
                   is_zero_ether_addr(pkt_dev->src_mac) ?
                             pkt_dev->odev->dev_addr : pkt_dev->src_mac);
 
-       seq_printf(seq, "dst_mac: ");
+       seq_puts(seq, "dst_mac: ");
        seq_printf(seq, "%pM\n", pkt_dev->dst_mac);
 
        seq_printf(seq,
@@ -588,7 +588,7 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
 
        if (pkt_dev->nr_labels) {
                unsigned int i;
-               seq_printf(seq, "     mpls: ");
+               seq_puts(seq, "     mpls: ");
                for (i = 0; i < pkt_dev->nr_labels; i++)
                        seq_printf(seq, "%08x%s", ntohl(pkt_dev->labels[i]),
                                   i == pkt_dev->nr_labels-1 ? "\n" : ", ");
@@ -613,67 +613,67 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
        if (pkt_dev->node >= 0)
                seq_printf(seq, "     node: %d\n", pkt_dev->node);
 
-       seq_printf(seq, "     Flags: ");
+       seq_puts(seq, "     Flags: ");
 
        if (pkt_dev->flags & F_IPV6)
-               seq_printf(seq, "IPV6  ");
+               seq_puts(seq, "IPV6  ");
 
        if (pkt_dev->flags & F_IPSRC_RND)
-               seq_printf(seq, "IPSRC_RND  ");
+               seq_puts(seq, "IPSRC_RND  ");
 
        if (pkt_dev->flags & F_IPDST_RND)
-               seq_printf(seq, "IPDST_RND  ");
+               seq_puts(seq, "IPDST_RND  ");
 
        if (pkt_dev->flags & F_TXSIZE_RND)
-               seq_printf(seq, "TXSIZE_RND  ");
+               seq_puts(seq, "TXSIZE_RND  ");
 
        if (pkt_dev->flags & F_UDPSRC_RND)
-               seq_printf(seq, "UDPSRC_RND  ");
+               seq_puts(seq, "UDPSRC_RND  ");
 
        if (pkt_dev->flags & F_UDPDST_RND)
-               seq_printf(seq, "UDPDST_RND  ");
+               seq_puts(seq, "UDPDST_RND  ");
 
        if (pkt_dev->flags & F_UDPCSUM)
-               seq_printf(seq, "UDPCSUM  ");
+               seq_puts(seq, "UDPCSUM  ");
 
        if (pkt_dev->flags & F_MPLS_RND)
-               seq_printf(seq,  "MPLS_RND  ");
+               seq_puts(seq,  "MPLS_RND  ");
 
        if (pkt_dev->flags & F_QUEUE_MAP_RND)
-               seq_printf(seq,  "QUEUE_MAP_RND  ");
+               seq_puts(seq,  "QUEUE_MAP_RND  ");
 
        if (pkt_dev->flags & F_QUEUE_MAP_CPU)
-               seq_printf(seq,  "QUEUE_MAP_CPU  ");
+               seq_puts(seq,  "QUEUE_MAP_CPU  ");
 
        if (pkt_dev->cflows) {
                if (pkt_dev->flags & F_FLOW_SEQ)
-                       seq_printf(seq,  "FLOW_SEQ  "); /*in sequence flows*/
+                       seq_puts(seq,  "FLOW_SEQ  "); /*in sequence flows*/
                else
-                       seq_printf(seq,  "FLOW_RND  ");
+                       seq_puts(seq,  "FLOW_RND  ");
        }
 
 #ifdef CONFIG_XFRM
        if (pkt_dev->flags & F_IPSEC_ON) {
-               seq_printf(seq,  "IPSEC  ");
+               seq_puts(seq,  "IPSEC  ");
                if (pkt_dev->spi)
                        seq_printf(seq, "spi:%u", pkt_dev->spi);
        }
 #endif
 
        if (pkt_dev->flags & F_MACSRC_RND)
-               seq_printf(seq, "MACSRC_RND  ");
+               seq_puts(seq, "MACSRC_RND  ");
 
        if (pkt_dev->flags & F_MACDST_RND)
-               seq_printf(seq, "MACDST_RND  ");
+               seq_puts(seq, "MACDST_RND  ");
 
        if (pkt_dev->flags & F_VID_RND)
-               seq_printf(seq, "VID_RND  ");
+               seq_puts(seq, "VID_RND  ");
 
        if (pkt_dev->flags & F_SVID_RND)
-               seq_printf(seq, "SVID_RND  ");
+               seq_puts(seq, "SVID_RND  ");
 
        if (pkt_dev->flags & F_NODE)
-               seq_printf(seq, "NODE_ALLOC  ");
+               seq_puts(seq, "NODE_ALLOC  ");
 
        seq_puts(seq, "\n");
 
@@ -716,7 +716,7 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
        if (pkt_dev->result[0])
                seq_printf(seq, "Result: %s\n", pkt_dev->result);
        else
-               seq_printf(seq, "Result: Idle\n");
+               seq_puts(seq, "Result: Idle\n");
 
        return 0;
 }
@@ -1735,14 +1735,14 @@ static int pktgen_thread_show(struct seq_file *seq, void *v)
 
        BUG_ON(!t);
 
-       seq_printf(seq, "Running: ");
+       seq_puts(seq, "Running: ");
 
        if_lock(t);
        list_for_each_entry(pkt_dev, &t->if_list, list)
                if (pkt_dev->running)
                        seq_printf(seq, "%s ", pkt_dev->odevname);
 
-       seq_printf(seq, "\nStopped: ");
+       seq_puts(seq, "\nStopped: ");
 
        list_for_each_entry(pkt_dev, &t->if_list, list)
                if (!pkt_dev->running)
@@ -1751,7 +1751,7 @@ static int pktgen_thread_show(struct seq_file *seq, void *v)
        if (t->result[0])
                seq_printf(seq, "\nResult: %s\n", t->result);
        else
-               seq_printf(seq, "\nResult: NA\n");
+               seq_puts(seq, "\nResult: NA\n");
 
        if_unlock(t);
 
index eaba0f68f8608618870a7009f27352523bee108f..d3027a73fd4bbc152f13011cb09335af365f19dc 100644 (file)
@@ -88,7 +88,7 @@ EXPORT_SYMBOL_GPL(ptp_classify_raw);
 
 void __init ptp_classifier_init(void)
 {
-       static struct sock_filter ptp_filter[] = {
+       static struct sock_filter ptp_filter[] __initdata = {
                { 0x28,  0,  0, 0x0000000c },
                { 0x15,  0, 12, 0x00000800 },
                { 0x30,  0,  0, 0x00000017 },
@@ -133,7 +133,7 @@ void __init ptp_classifier_init(void)
                { 0x16,  0,  0, 0x00000000 },
                { 0x06,  0,  0, 0x00000000 },
        };
-       struct sock_fprog ptp_prog = {
+       struct sock_fprog_kern ptp_prog = {
                .len = ARRAY_SIZE(ptp_filter), .filter = ptp_filter,
        };
 
index 2d8d8fcfa060c51e2f6cbe240aff3d01b3f964cc..1063996f8317fb95fea964e095ae2e372ceb74be 100644 (file)
@@ -798,8 +798,8 @@ static inline int rtnl_vfinfo_size(const struct net_device *dev,
                size += num_vfs *
                        (nla_total_size(sizeof(struct ifla_vf_mac)) +
                         nla_total_size(sizeof(struct ifla_vf_vlan)) +
-                        nla_total_size(sizeof(struct ifla_vf_tx_rate)) +
-                        nla_total_size(sizeof(struct ifla_vf_spoofchk)));
+                        nla_total_size(sizeof(struct ifla_vf_spoofchk)) +
+                        nla_total_size(sizeof(struct ifla_vf_rate)));
                return size;
        } else
                return 0;
@@ -1065,6 +1065,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                        struct ifla_vf_info ivi;
                        struct ifla_vf_mac vf_mac;
                        struct ifla_vf_vlan vf_vlan;
+                       struct ifla_vf_rate vf_rate;
                        struct ifla_vf_tx_rate vf_tx_rate;
                        struct ifla_vf_spoofchk vf_spoofchk;
                        struct ifla_vf_link_state vf_linkstate;
@@ -1085,6 +1086,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                                break;
                        vf_mac.vf =
                                vf_vlan.vf =
+                               vf_rate.vf =
                                vf_tx_rate.vf =
                                vf_spoofchk.vf =
                                vf_linkstate.vf = ivi.vf;
@@ -1092,7 +1094,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                        memcpy(vf_mac.mac, ivi.mac, sizeof(ivi.mac));
                        vf_vlan.vlan = ivi.vlan;
                        vf_vlan.qos = ivi.qos;
-                       vf_tx_rate.rate = ivi.tx_rate;
+                       vf_tx_rate.rate = ivi.max_tx_rate;
+                       vf_rate.min_tx_rate = ivi.min_tx_rate;
+                       vf_rate.max_tx_rate = ivi.max_tx_rate;
                        vf_spoofchk.setting = ivi.spoofchk;
                        vf_linkstate.link_state = ivi.linkstate;
                        vf = nla_nest_start(skb, IFLA_VF_INFO);
@@ -1102,6 +1106,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                        }
                        if (nla_put(skb, IFLA_VF_MAC, sizeof(vf_mac), &vf_mac) ||
                            nla_put(skb, IFLA_VF_VLAN, sizeof(vf_vlan), &vf_vlan) ||
+                           nla_put(skb, IFLA_VF_RATE, sizeof(vf_rate),
+                                   &vf_rate) ||
                            nla_put(skb, IFLA_VF_TX_RATE, sizeof(vf_tx_rate),
                                    &vf_tx_rate) ||
                            nla_put(skb, IFLA_VF_SPOOFCHK, sizeof(vf_spoofchk),
@@ -1208,6 +1214,10 @@ static const struct nla_policy ifla_vf_policy[IFLA_VF_MAX+1] = {
                                    .len = sizeof(struct ifla_vf_tx_rate) },
        [IFLA_VF_SPOOFCHK]      = { .type = NLA_BINARY,
                                    .len = sizeof(struct ifla_vf_spoofchk) },
+       [IFLA_VF_RATE]          = { .type = NLA_BINARY,
+                                   .len = sizeof(struct ifla_vf_rate) },
+       [IFLA_VF_LINK_STATE]    = { .type = NLA_BINARY,
+                                   .len = sizeof(struct ifla_vf_link_state) },
 };
 
 static const struct nla_policy ifla_port_policy[IFLA_PORT_MAX+1] = {
@@ -1234,6 +1244,7 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
        struct nlattr *tb[IFLA_MAX+1];
        u32 ext_filter_mask = 0;
        int err;
+       int hdrlen;
 
        s_h = cb->args[0];
        s_idx = cb->args[1];
@@ -1241,8 +1252,17 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
        rcu_read_lock();
        cb->seq = net->dev_base_seq;
 
-       if (nlmsg_parse(cb->nlh, sizeof(struct ifinfomsg), tb, IFLA_MAX,
-                       ifla_policy) >= 0) {
+       /* A hack to preserve kernel<->userspace interface.
+        * The correct header is ifinfomsg. It is consistent with rtnl_getlink.
+        * However, before Linux v3.9 the code here assumed rtgenmsg and that's
+        * what iproute2 < v3.9.0 used.
+        * We can detect the old iproute2. Even including the IFLA_EXT_MASK
+        * attribute, its netlink message is shorter than struct ifinfomsg.
+        */
+       hdrlen = nlmsg_len(cb->nlh) < sizeof(struct ifinfomsg) ?
+                sizeof(struct rtgenmsg) : sizeof(struct ifinfomsg);
+
+       if (nlmsg_parse(cb->nlh, hdrlen, tb, IFLA_MAX, ifla_policy) >= 0) {
 
                if (tb[IFLA_EXT_MASK])
                        ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
@@ -1367,11 +1387,29 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr *attr)
                }
                case IFLA_VF_TX_RATE: {
                        struct ifla_vf_tx_rate *ivt;
+                       struct ifla_vf_info ivf;
                        ivt = nla_data(vf);
                        err = -EOPNOTSUPP;
-                       if (ops->ndo_set_vf_tx_rate)
-                               err = ops->ndo_set_vf_tx_rate(dev, ivt->vf,
-                                                             ivt->rate);
+                       if (ops->ndo_get_vf_config)
+                               err = ops->ndo_get_vf_config(dev, ivt->vf,
+                                                            &ivf);
+                       if (err)
+                               break;
+                       err = -EOPNOTSUPP;
+                       if (ops->ndo_set_vf_rate)
+                               err = ops->ndo_set_vf_rate(dev, ivt->vf,
+                                                          ivf.min_tx_rate,
+                                                          ivt->rate);
+                       break;
+               }
+               case IFLA_VF_RATE: {
+                       struct ifla_vf_rate *ivt;
+                       ivt = nla_data(vf);
+                       err = -EOPNOTSUPP;
+                       if (ops->ndo_set_vf_rate)
+                               err = ops->ndo_set_vf_rate(dev, ivt->vf,
+                                                          ivt->min_tx_rate,
+                                                          ivt->max_tx_rate);
                        break;
                }
                case IFLA_VF_SPOOFCHK: {
@@ -1744,7 +1782,6 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh)
 
        ops->dellink(dev, &list_kill);
        unregister_netdevice_many(&list_kill);
-       list_del(&list_kill);
        return 0;
 }
 
@@ -2019,11 +2056,15 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh)
                if (ops->newlink) {
                        err = ops->newlink(net, dev, tb, data);
                        /* Drivers should call free_netdev() in ->destructor
-                        * and unregister it on failure so that device could be
-                        * finally freed in rtnl_unlock.
+                        * and unregister it on failure after registration
+                        * so that device could be finally freed in rtnl_unlock.
                         */
-                       if (err < 0)
+                       if (err < 0) {
+                               /* If device is not registered at all, free it now */
+                               if (dev->reg_state == NETREG_UNINITIALIZED)
+                                       free_netdev(dev);
                                goto out;
+                       }
                } else {
                        err = register_netdevice(dev);
                        if (err < 0) {
@@ -2095,9 +2136,13 @@ static u16 rtnl_calcit(struct sk_buff *skb, struct nlmsghdr *nlh)
        struct nlattr *tb[IFLA_MAX+1];
        u32 ext_filter_mask = 0;
        u16 min_ifinfo_dump_size = 0;
+       int hdrlen;
+
+       /* Same kernel<->userspace interface hack as in rtnl_dump_ifinfo. */
+       hdrlen = nlmsg_len(nlh) < sizeof(struct ifinfomsg) ?
+                sizeof(struct rtgenmsg) : sizeof(struct ifinfomsg);
 
-       if (nlmsg_parse(nlh, sizeof(struct ifinfomsg), tb, IFLA_MAX,
-                       ifla_policy) >= 0) {
+       if (nlmsg_parse(nlh, hdrlen, tb, IFLA_MAX, ifla_policy) >= 0) {
                if (tb[IFLA_EXT_MASK])
                        ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
        }
index 897da56f3affae13161d4610989667a82244c308..ba71212f0251218c3c41599838e36aa8e169fea7 100644 (file)
@@ -85,31 +85,6 @@ EXPORT_SYMBOL(secure_ipv6_port_ephemeral);
 #endif
 
 #ifdef CONFIG_INET
-__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];
-       hash[3] = net_secret[15];
-
-       md5_transform(hash, net_secret);
-
-       return hash[0];
-}
-
-__u32 secure_ipv6_id(const __be32 daddr[4])
-{
-       __u32 hash[4];
-
-       net_secret_init();
-       memcpy(hash, daddr, 16);
-       md5_transform(hash, net_secret);
-
-       return hash[0];
-}
 
 __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr,
                                 __be16 sport, __be16 dport)
index 8383b2bddeb923bd629da7d9eac2cdd1ff1d81bd..bf92824af3f77179e519f73085f6642c32ca61b6 100644 (file)
@@ -694,7 +694,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
 #endif
        memcpy(new->cb, old->cb, sizeof(old->cb));
        new->csum               = old->csum;
-       new->local_df           = old->local_df;
+       new->ignore_df          = old->ignore_df;
        new->pkt_type           = old->pkt_type;
        new->ip_summed          = old->ip_summed;
        skb_copy_queue_mapping(new, old);
@@ -951,10 +951,13 @@ struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask)
 EXPORT_SYMBOL(skb_copy);
 
 /**
- *     __pskb_copy     -       create copy of an sk_buff with private head.
+ *     __pskb_copy_fclone      -  create copy of an sk_buff with private head.
  *     @skb: buffer to copy
  *     @headroom: headroom of new skb
  *     @gfp_mask: allocation priority
+ *     @fclone: if true allocate the copy of the skb from the fclone
+ *     cache instead of the head cache; it is recommended to set this
+ *     to true for the cases where the copy will likely be cloned
  *
  *     Make a copy of both an &sk_buff and part of its data, located
  *     in header. Fragmented data remain shared. This is used when
@@ -964,11 +967,12 @@ EXPORT_SYMBOL(skb_copy);
  *     The returned buffer has a reference count of 1.
  */
 
-struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, gfp_t gfp_mask)
+struct sk_buff *__pskb_copy_fclone(struct sk_buff *skb, int headroom,
+                                  gfp_t gfp_mask, bool fclone)
 {
        unsigned int size = skb_headlen(skb) + headroom;
-       struct sk_buff *n = __alloc_skb(size, gfp_mask,
-                                       skb_alloc_rx_flag(skb), NUMA_NO_NODE);
+       int flags = skb_alloc_rx_flag(skb) | (fclone ? SKB_ALLOC_FCLONE : 0);
+       struct sk_buff *n = __alloc_skb(size, gfp_mask, flags, NUMA_NO_NODE);
 
        if (!n)
                goto out;
@@ -1008,7 +1012,7 @@ struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, gfp_t gfp_mask)
 out:
        return n;
 }
-EXPORT_SYMBOL(__pskb_copy);
+EXPORT_SYMBOL(__pskb_copy_fclone);
 
 /**
  *     pskb_expand_head - reallocate header of &sk_buff
@@ -2881,12 +2885,14 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
        int pos;
        int dummy;
 
+       __skb_push(head_skb, doffset);
        proto = skb_network_protocol(head_skb, &dummy);
        if (unlikely(!proto))
                return ERR_PTR(-EINVAL);
 
-       csum = !!can_checksum_protocol(features, proto);
-       __skb_push(head_skb, doffset);
+       csum = !head_skb->encap_hdr_csum &&
+           !!can_checksum_protocol(features, proto);
+
        headroom = skb_headroom(head_skb);
        pos = skb_headlen(head_skb);
 
@@ -2983,6 +2989,8 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
                        nskb->csum = skb_copy_and_csum_bits(head_skb, offset,
                                                            skb_put(nskb, len),
                                                            len, 0);
+                       SKB_GSO_CB(nskb)->csum_start =
+                           skb_headroom(nskb) + offset;
                        continue;
                }
 
@@ -3052,6 +3060,8 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
                        nskb->csum = skb_checksum(nskb, doffset,
                                                  nskb->len - doffset, 0);
                        nskb->ip_summed = CHECKSUM_NONE;
+                       SKB_GSO_CB(nskb)->csum_start =
+                           skb_headroom(nskb) + doffset;
                }
        } while ((offset += len) < head_skb->len);
 
@@ -3913,7 +3923,7 @@ void skb_scrub_packet(struct sk_buff *skb, bool xnet)
        skb->tstamp.tv64 = 0;
        skb->pkt_type = PACKET_HOST;
        skb->skb_iif = 0;
-       skb->local_df = 0;
+       skb->ignore_df = 0;
        skb_dst_drop(skb);
        skb->mark = 0;
        secpath_reset(skb);
index 664ee4295b6f6ec38fb4f89d11c52eaa383a15d1..026e01f70274f8a5550af6a7a06fd6846735e4ae 100644 (file)
@@ -784,7 +784,7 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
                break;
 
        case SO_NO_CHECK:
-               sk->sk_no_check = valbool;
+               sk->sk_no_check_tx = valbool;
                break;
 
        case SO_PRIORITY:
@@ -1064,7 +1064,7 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
                break;
 
        case SO_NO_CHECK:
-               v.val = sk->sk_no_check;
+               v.val = sk->sk_no_check_tx;
                break;
 
        case SO_PRIORITY:
diff --git a/net/core/tso.c b/net/core/tso.c
new file mode 100644 (file)
index 0000000..8c3203c
--- /dev/null
@@ -0,0 +1,77 @@
+#include <linux/export.h>
+#include <net/ip.h>
+#include <net/tso.h>
+
+/* Calculate expected number of TX descriptors */
+int tso_count_descs(struct sk_buff *skb)
+{
+       /* The Marvell Way */
+       return skb_shinfo(skb)->gso_segs * 2 + skb_shinfo(skb)->nr_frags;
+}
+EXPORT_SYMBOL(tso_count_descs);
+
+void tso_build_hdr(struct sk_buff *skb, char *hdr, struct tso_t *tso,
+                  int size, bool is_last)
+{
+       struct iphdr *iph;
+       struct tcphdr *tcph;
+       int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+       int mac_hdr_len = skb_network_offset(skb);
+
+       memcpy(hdr, skb->data, hdr_len);
+       iph = (struct iphdr *)(hdr + mac_hdr_len);
+       iph->id = htons(tso->ip_id);
+       iph->tot_len = htons(size + hdr_len - mac_hdr_len);
+       tcph = (struct tcphdr *)(hdr + skb_transport_offset(skb));
+       tcph->seq = htonl(tso->tcp_seq);
+       tso->ip_id++;
+
+       if (!is_last) {
+               /* Clear all special flags for not last packet */
+               tcph->psh = 0;
+               tcph->fin = 0;
+               tcph->rst = 0;
+       }
+}
+EXPORT_SYMBOL(tso_build_hdr);
+
+void tso_build_data(struct sk_buff *skb, struct tso_t *tso, int size)
+{
+       tso->tcp_seq += size;
+       tso->size -= size;
+       tso->data += size;
+
+       if ((tso->size == 0) &&
+           (tso->next_frag_idx < skb_shinfo(skb)->nr_frags)) {
+               skb_frag_t *frag = &skb_shinfo(skb)->frags[tso->next_frag_idx];
+
+               /* Move to next segment */
+               tso->size = frag->size;
+               tso->data = page_address(frag->page.p) + frag->page_offset;
+               tso->next_frag_idx++;
+       }
+}
+EXPORT_SYMBOL(tso_build_data);
+
+void tso_start(struct sk_buff *skb, struct tso_t *tso)
+{
+       int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+
+       tso->ip_id = ntohs(ip_hdr(skb)->id);
+       tso->tcp_seq = ntohl(tcp_hdr(skb)->seq);
+       tso->next_frag_idx = 0;
+
+       /* Build first data */
+       tso->size = skb_headlen(skb) - hdr_len;
+       tso->data = skb->data + hdr_len;
+       if ((tso->size == 0) &&
+           (tso->next_frag_idx < skb_shinfo(skb)->nr_frags)) {
+               skb_frag_t *frag = &skb_shinfo(skb)->frags[tso->next_frag_idx];
+
+               /* Move to next segment */
+               tso->size = frag->size;
+               tso->data = page_address(frag->page.p) + frag->page_offset;
+               tso->next_frag_idx++;
+       }
+}
+EXPORT_SYMBOL(tso_start);
index 22b5d818b2001b177b765cbb67eb2551e87502ce..6ca645c4b48e8b56a88f7d549b073fb6dc82eeb7 100644 (file)
@@ -1024,7 +1024,6 @@ static struct inet_protosw dccp_v4_protosw = {
        .protocol       = IPPROTO_DCCP,
        .prot           = &dccp_v4_prot,
        .ops            = &inet_dccp_ops,
-       .no_check       = 0,
        .flags          = INET_PROTOSW_ICSK,
 };
 
index eb892b4f48144966e47f386108942f51b8b85e50..de2c1e7193057dee2e994386f5bb685e05b47163 100644 (file)
@@ -1084,14 +1084,15 @@ EXPORT_SYMBOL_GPL(dccp_shutdown);
 
 static inline int dccp_mib_init(void)
 {
-       return snmp_mib_init((void __percpu **)dccp_statistics,
-                            sizeof(struct dccp_mib),
-                            __alignof__(struct dccp_mib));
+       dccp_statistics = alloc_percpu(struct dccp_mib);
+       if (!dccp_statistics)
+               return -ENOMEM;
+       return 0;
 }
 
 static inline void dccp_mib_exit(void)
 {
-       snmp_mib_free((void __percpu **)dccp_statistics);
+       free_percpu(dccp_statistics);
 }
 
 static int thash_entries;
index 607ab71b5a0cb3af65067e7d69badb862d5bfb5a..53731e45403c83ba2edf48da01e71aeda48359b0 100644 (file)
@@ -20,6 +20,7 @@
 
 /* Boundary values */
 static int             zero     = 0,
+                       one      = 1,
                        u8_max   = 0xFF;
 static unsigned long   seqw_min = DCCPF_SEQ_WMIN,
                        seqw_max = 0xFFFFFFFF;          /* maximum on 32 bit */
@@ -58,7 +59,7 @@ static struct ctl_table dccp_default_table[] = {
                .maxlen         = sizeof(sysctl_dccp_request_retries),
                .mode           = 0644,
                .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &zero,
+               .extra1         = &one,
                .extra2         = &u8_max,
        },
        {
index 16f0b223102e6619b3b3430a108be740e2e7b373..1cd46a345cb04387a50843a251637b6e3cbd7501 100644 (file)
@@ -280,7 +280,7 @@ static ktime_t dccp_timestamp_seed;
  */
 u32 dccp_timestamp(void)
 {
-       s64 delta = ktime_us_delta(ktime_get_real(), dccp_timestamp_seed);
+       u64 delta = (u64)ktime_us_delta(ktime_get_real(), dccp_timestamp_seed);
 
        do_div(delta, 10);
        return delta;
index 4c04848953bdb4caddeae9debd195ea3d004ee0d..ae011b46c0710fc96204deda6bddb1d912d78f22 100644 (file)
@@ -481,7 +481,7 @@ static struct sock *dn_alloc_sock(struct net *net, struct socket *sock, gfp_t gf
 
        sk->sk_backlog_rcv = dn_nsp_backlog_rcv;
        sk->sk_destruct    = dn_destruct;
-       sk->sk_no_check    = 1;
+       sk->sk_no_check_tx = 1;
        sk->sk_family      = PF_DECnet;
        sk->sk_protocol    = 0;
        sk->sk_allocation  = gfp;
index e7b6d53eef88d250d6a93e3a75f26c3a6cad4cba..9acec61f54334f146d87711f145f1992dbf2c360 100644 (file)
@@ -93,8 +93,8 @@ int dns_query(const char *type, const char *name, size_t namelen,
        }
 
        if (!namelen)
-               namelen = strlen(name);
-       if (namelen < 3)
+               namelen = strnlen(name, 256);
+       if (namelen < 3 || namelen > 255)
                return -EINVAL;
        desclen += namelen + 1;
 
@@ -149,7 +149,9 @@ int dns_query(const char *type, const char *name, size_t namelen,
        if (!*_result)
                goto put;
 
-       memcpy(*_result, upayload->data, len + 1);
+       memcpy(*_result, upayload->data, len);
+       *_result[len] = '\0';
+
        if (_expiry)
                *_expiry = rkey->expiry;
 
index 02c0e1716f641c947a5b906ee82c755e568a4a7c..64c5af0a10dd82169ccada3d82baa866a5b82cc8 100644 (file)
@@ -346,7 +346,7 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent,
                return slave_dev;
 
        slave_dev->features = master->vlan_features;
-       SET_ETHTOOL_OPS(slave_dev, &dsa_slave_ethtool_ops);
+       slave_dev->ethtool_ops = &dsa_slave_ethtool_ops;
        eth_hw_addr_inherit(slave_dev, master);
        slave_dev->tx_queue_len = 0;
 
index 0f5a69ed746d8384891ff9e3fd1aa570f04f91d5..fe6bd7a7108169138faf198fefcab7f3217e9c55 100644 (file)
@@ -92,6 +92,7 @@ static int lowpan_header_create(struct sk_buff *skb,
        const u8 *saddr = _saddr;
        const u8 *daddr = _daddr;
        struct ieee802154_addr sa, da;
+       struct ieee802154_mac_cb *cb = mac_cb_init(skb);
 
        /* TODO:
         * if this package isn't ipv6 one, where should it be routed?
@@ -115,8 +116,7 @@ static int lowpan_header_create(struct sk_buff *skb,
         * from MAC subif of the 'dev' and 'real_dev' network devices, but
         * this isn't implemented in mainline yet, so currently we assign 0xff
         */
-       mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA;
-       mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
+       cb->type = IEEE802154_FC_TYPE_DATA;
 
        /* prepare wpan address data */
        sa.mode = IEEE802154_ADDR_LONG;
@@ -135,11 +135,10 @@ static int lowpan_header_create(struct sk_buff *skb,
        } else {
                da.mode = IEEE802154_ADDR_LONG;
                da.extended_addr = ieee802154_devaddr_from_raw(daddr);
-
-               /* request acknowledgment */
-               mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ;
        }
 
+       cb->ackreq = !lowpan_is_addr_broadcast(daddr);
+
        return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev,
                        type, (void *)&da, (void *)&sa, 0);
 }
@@ -221,139 +220,149 @@ static int lowpan_set_address(struct net_device *dev, void *p)
        return 0;
 }
 
-static int
-lowpan_fragment_xmit(struct sk_buff *skb, u8 *head,
-                    int mlen, int plen, int offset, int type)
+static struct sk_buff*
+lowpan_alloc_frag(struct sk_buff *skb, int size,
+                 const struct ieee802154_hdr *master_hdr)
 {
+       struct net_device *real_dev = lowpan_dev_info(skb->dev)->real_dev;
        struct sk_buff *frag;
-       int hlen;
-
-       hlen = (type == LOWPAN_DISPATCH_FRAG1) ?
-                       LOWPAN_FRAG1_HEAD_SIZE : LOWPAN_FRAGN_HEAD_SIZE;
-
-       raw_dump_inline(__func__, "6lowpan fragment header", head, hlen);
+       int rc;
+
+       frag = alloc_skb(real_dev->hard_header_len +
+                        real_dev->needed_tailroom + size,
+                        GFP_ATOMIC);
+
+       if (likely(frag)) {
+               frag->dev = real_dev;
+               frag->priority = skb->priority;
+               skb_reserve(frag, real_dev->hard_header_len);
+               skb_reset_network_header(frag);
+               *mac_cb(frag) = *mac_cb(skb);
+
+               rc = dev_hard_header(frag, real_dev, 0, &master_hdr->dest,
+                                    &master_hdr->source, size);
+               if (rc < 0) {
+                       kfree_skb(frag);
+                       return ERR_PTR(-rc);
+               }
+       } else {
+               frag = ERR_PTR(ENOMEM);
+       }
 
-       frag = netdev_alloc_skb(skb->dev,
-                               hlen + mlen + plen + IEEE802154_MFR_SIZE);
-       if (!frag)
-               return -ENOMEM;
+       return frag;
+}
 
-       frag->priority = skb->priority;
+static int
+lowpan_xmit_fragment(struct sk_buff *skb, const struct ieee802154_hdr *wpan_hdr,
+                    u8 *frag_hdr, int frag_hdrlen,
+                    int offset, int len)
+{
+       struct sk_buff *frag;
 
-       /* copy header, MFR and payload */
-       skb_put(frag, mlen);
-       skb_copy_to_linear_data(frag, skb_mac_header(skb), mlen);
+       raw_dump_inline(__func__, " fragment header", frag_hdr, frag_hdrlen);
 
-       skb_put(frag, hlen);
-       skb_copy_to_linear_data_offset(frag, mlen, head, hlen);
+       frag = lowpan_alloc_frag(skb, frag_hdrlen + len, wpan_hdr);
+       if (IS_ERR(frag))
+               return -PTR_ERR(frag);
 
-       skb_put(frag, plen);
-       skb_copy_to_linear_data_offset(frag, mlen + hlen,
-                                      skb_network_header(skb) + offset, plen);
+       memcpy(skb_put(frag, frag_hdrlen), frag_hdr, frag_hdrlen);
+       memcpy(skb_put(frag, len), skb_network_header(skb) + offset, len);
 
-       raw_dump_table(__func__, " raw fragment dump", frag->data, frag->len);
+       raw_dump_table(__func__, " fragment dump", frag->data, frag->len);
 
        return dev_queue_xmit(frag);
 }
 
 static int
-lowpan_skb_fragmentation(struct sk_buff *skb, struct net_device *dev)
+lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev,
+                      const struct ieee802154_hdr *wpan_hdr)
 {
-       int err;
-       u16 dgram_offset, dgram_size, payload_length, header_length,
-           lowpan_size, frag_plen, offset;
-       __be16 tag;
-       u8 head[5];
-
-       header_length = skb->mac_len;
-       payload_length = skb->len - header_length;
-       tag = lowpan_dev_info(dev)->fragment_tag++;
-       lowpan_size = skb_network_header_len(skb);
+       u16 dgram_size, dgram_offset;
+       __be16 frag_tag;
+       u8 frag_hdr[5];
+       int frag_cap, frag_len, payload_cap, rc;
+       int skb_unprocessed, skb_offset;
+
        dgram_size = lowpan_uncompress_size(skb, &dgram_offset) -
-                    header_length;
+                    skb->mac_len;
+       frag_tag = lowpan_dev_info(dev)->fragment_tag++;
 
-       /* first fragment header */
-       head[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x7);
-       head[1] = dgram_size & 0xff;
-       memcpy(head + 2, &tag, sizeof(tag));
+       frag_hdr[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x07);
+       frag_hdr[1] = dgram_size & 0xff;
+       memcpy(frag_hdr + 2, &frag_tag, sizeof(frag_tag));
 
-       /* calc the nearest payload length(divided to 8) for first fragment
-        * which fits into a IEEE802154_MTU
-        */
-       frag_plen = round_down(IEEE802154_MTU - header_length -
-                              LOWPAN_FRAG1_HEAD_SIZE - lowpan_size -
-                              IEEE802154_MFR_SIZE, 8);
-
-       err = lowpan_fragment_xmit(skb, head, header_length,
-                                  frag_plen + lowpan_size, 0,
-                                  LOWPAN_DISPATCH_FRAG1);
-       if (err) {
-               pr_debug("%s unable to send FRAG1 packet (tag: %d)",
-                        __func__, tag);
-               goto exit;
-       }
+       payload_cap = ieee802154_max_payload(wpan_hdr);
 
-       offset = lowpan_size + frag_plen;
-       dgram_offset += frag_plen;
+       frag_len = round_down(payload_cap - LOWPAN_FRAG1_HEAD_SIZE -
+                             skb_network_header_len(skb), 8);
 
-       /* next fragment header */
-       head[0] &= ~LOWPAN_DISPATCH_FRAG1;
-       head[0] |= LOWPAN_DISPATCH_FRAGN;
+       skb_offset = skb_network_header_len(skb);
+       skb_unprocessed = skb->len - skb->mac_len - skb_offset;
 
-       frag_plen = round_down(IEEE802154_MTU - header_length -
-                              LOWPAN_FRAGN_HEAD_SIZE - IEEE802154_MFR_SIZE, 8);
+       rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr,
+                                 LOWPAN_FRAG1_HEAD_SIZE, 0,
+                                 frag_len + skb_network_header_len(skb));
+       if (rc) {
+               pr_debug("%s unable to send FRAG1 packet (tag: %d)",
+                        __func__, frag_tag);
+               goto err;
+       }
 
-       while (payload_length - offset > 0) {
-               int len = frag_plen;
+       frag_hdr[0] &= ~LOWPAN_DISPATCH_FRAG1;
+       frag_hdr[0] |= LOWPAN_DISPATCH_FRAGN;
+       frag_cap = round_down(payload_cap - LOWPAN_FRAGN_HEAD_SIZE, 8);
 
-               head[4] = dgram_offset >> 3;
+       do {
+               dgram_offset += frag_len;
+               skb_offset += frag_len;
+               skb_unprocessed -= frag_len;
+               frag_len = min(frag_cap, skb_unprocessed);
 
-               if (payload_length - offset < len)
-                       len = payload_length - offset;
+               frag_hdr[4] = dgram_offset >> 3;
 
-               err = lowpan_fragment_xmit(skb, head, header_length, len,
-                                          offset, LOWPAN_DISPATCH_FRAGN);
-               if (err) {
+               rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr,
+                                         LOWPAN_FRAGN_HEAD_SIZE, skb_offset,
+                                         frag_len);
+               if (rc) {
                        pr_debug("%s unable to send a FRAGN packet. (tag: %d, offset: %d)\n",
-                                __func__, tag, offset);
-                       goto exit;
+                                __func__, frag_tag, skb_offset);
+                       goto err;
                }
+       } while (skb_unprocessed > frag_cap);
 
-               offset += len;
-               dgram_offset += len;
-       }
+       consume_skb(skb);
+       return NET_XMIT_SUCCESS;
 
-exit:
-       return err;
+err:
+       kfree_skb(skb);
+       return rc;
 }
 
 static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev)
 {
-       int err = -1;
+       struct ieee802154_hdr wpan_hdr;
+       int max_single;
 
        pr_debug("package xmit\n");
 
-       skb->dev = lowpan_dev_info(dev)->real_dev;
-       if (skb->dev == NULL) {
-               pr_debug("ERROR: no real wpan device found\n");
-               goto error;
+       if (ieee802154_hdr_peek(skb, &wpan_hdr) < 0) {
+               kfree_skb(skb);
+               return NET_XMIT_DROP;
        }
 
-       /* Send directly if less than the MTU minus the 2 checksum bytes. */
-       if (skb->len <= IEEE802154_MTU - IEEE802154_MFR_SIZE) {
-               err = dev_queue_xmit(skb);
-               goto out;
-       }
+       max_single = ieee802154_max_payload(&wpan_hdr);
 
-       pr_debug("frame is too big, fragmentation is needed\n");
-       err = lowpan_skb_fragmentation(skb, dev);
-error:
-       dev_kfree_skb(skb);
-out:
-       if (err)
-               pr_debug("ERROR: xmit failed\n");
+       if (skb_tail_pointer(skb) - skb_network_header(skb) <= max_single) {
+               skb->dev = lowpan_dev_info(dev)->real_dev;
+               return dev_queue_xmit(skb);
+       } else {
+               netdev_tx_t rc;
+
+               pr_debug("frame is too big, fragmentation is needed\n");
+               rc = lowpan_xmit_fragmented(skb, dev, &wpan_hdr);
 
-       return (err < 0) ? NET_XMIT_DROP : err;
+               return rc < 0 ? NET_XMIT_DROP : rc;
+       }
 }
 
 static struct wpan_phy *lowpan_get_phy(const struct net_device *dev)
index 786437bc0c08531785d3f5fa1deb5bff8efe9e62..4f0ed8780194502465f0d5b60383bf6794bfadf0 100644 (file)
@@ -21,6 +21,7 @@
  * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
  */
 
+#include <linux/capability.h>
 #include <linux/net.h>
 #include <linux/module.h>
 #include <linux/if_arp.h>
@@ -45,7 +46,12 @@ struct dgram_sock {
        struct ieee802154_addr dst_addr;
 
        unsigned int bound:1;
+       unsigned int connected:1;
        unsigned int want_ack:1;
+       unsigned int secen:1;
+       unsigned int secen_override:1;
+       unsigned int seclevel:3;
+       unsigned int seclevel_override:1;
 };
 
 static inline struct dgram_sock *dgram_sk(const struct sock *sk)
@@ -73,10 +79,7 @@ static int dgram_init(struct sock *sk)
 {
        struct dgram_sock *ro = dgram_sk(sk);
 
-       ro->dst_addr.mode = IEEE802154_ADDR_LONG;
-       ro->dst_addr.pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
        ro->want_ack = 1;
-       memset(&ro->dst_addr.extended_addr, 0xff, IEEE802154_ADDR_LEN);
        return 0;
 }
 
@@ -183,6 +186,7 @@ static int dgram_connect(struct sock *sk, struct sockaddr *uaddr,
        }
 
        ieee802154_addr_from_sa(&ro->dst_addr, &addr->addr);
+       ro->connected = 1;
 
 out:
        release_sock(sk);
@@ -194,10 +198,7 @@ static int dgram_disconnect(struct sock *sk, int flags)
        struct dgram_sock *ro = dgram_sk(sk);
 
        lock_sock(sk);
-
-       ro->dst_addr.mode = IEEE802154_ADDR_LONG;
-       memset(&ro->dst_addr.extended_addr, 0xff, IEEE802154_ADDR_LEN);
-
+       ro->connected = 0;
        release_sock(sk);
 
        return 0;
@@ -209,7 +210,9 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk,
        struct net_device *dev;
        unsigned int mtu;
        struct sk_buff *skb;
+       struct ieee802154_mac_cb *cb;
        struct dgram_sock *ro = dgram_sk(sk);
+       struct ieee802154_addr dst_addr;
        int hlen, tlen;
        int err;
 
@@ -218,6 +221,11 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk,
                return -EOPNOTSUPP;
        }
 
+       if (!ro->connected && !msg->msg_name)
+               return -EDESTADDRREQ;
+       else if (ro->connected && msg->msg_name)
+               return -EISCONN;
+
        if (!ro->bound)
                dev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_IEEE802154);
        else
@@ -249,18 +257,28 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk,
 
        skb_reset_network_header(skb);
 
-       mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA;
-       if (ro->want_ack)
-               mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ;
+       cb = mac_cb_init(skb);
+       cb->type = IEEE802154_FC_TYPE_DATA;
+       cb->ackreq = ro->want_ack;
+
+       if (msg->msg_name) {
+               DECLARE_SOCKADDR(struct sockaddr_ieee802154*, daddr, msg->msg_name);
 
-       mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
-       err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &ro->dst_addr,
-                       ro->bound ? &ro->src_addr : NULL, size);
+               ieee802154_addr_from_sa(&dst_addr, &daddr->addr);
+       } else {
+               dst_addr = ro->dst_addr;
+       }
+
+       cb->secen = ro->secen;
+       cb->secen_override = ro->secen_override;
+       cb->seclevel = ro->seclevel;
+       cb->seclevel_override = ro->seclevel_override;
+
+       err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &dst_addr,
+                             ro->bound ? &ro->src_addr : NULL, size);
        if (err < 0)
                goto out_skb;
 
-       skb_reset_mac_header(skb);
-
        err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
        if (err < 0)
                goto out_skb;
@@ -419,6 +437,20 @@ static int dgram_getsockopt(struct sock *sk, int level, int optname,
        case WPAN_WANTACK:
                val = ro->want_ack;
                break;
+       case WPAN_SECURITY:
+               if (!ro->secen_override)
+                       val = WPAN_SECURITY_DEFAULT;
+               else if (ro->secen)
+                       val = WPAN_SECURITY_ON;
+               else
+                       val = WPAN_SECURITY_OFF;
+               break;
+       case WPAN_SECURITY_LEVEL:
+               if (!ro->seclevel_override)
+                       val = WPAN_SECURITY_LEVEL_DEFAULT;
+               else
+                       val = ro->seclevel;
+               break;
        default:
                return -ENOPROTOOPT;
        }
@@ -434,6 +466,7 @@ static int dgram_setsockopt(struct sock *sk, int level, int optname,
                    char __user *optval, unsigned int optlen)
 {
        struct dgram_sock *ro = dgram_sk(sk);
+       struct net *net = sock_net(sk);
        int val;
        int err = 0;
 
@@ -449,6 +482,47 @@ static int dgram_setsockopt(struct sock *sk, int level, int optname,
        case WPAN_WANTACK:
                ro->want_ack = !!val;
                break;
+       case WPAN_SECURITY:
+               if (!ns_capable(net->user_ns, CAP_NET_ADMIN) &&
+                   !ns_capable(net->user_ns, CAP_NET_RAW)) {
+                       err = -EPERM;
+                       break;
+               }
+
+               switch (val) {
+               case WPAN_SECURITY_DEFAULT:
+                       ro->secen_override = 0;
+                       break;
+               case WPAN_SECURITY_ON:
+                       ro->secen_override = 1;
+                       ro->secen = 1;
+                       break;
+               case WPAN_SECURITY_OFF:
+                       ro->secen_override = 1;
+                       ro->secen = 0;
+                       break;
+               default:
+                       err = -EINVAL;
+                       break;
+               }
+               break;
+       case WPAN_SECURITY_LEVEL:
+               if (!ns_capable(net->user_ns, CAP_NET_ADMIN) &&
+                   !ns_capable(net->user_ns, CAP_NET_RAW)) {
+                       err = -EPERM;
+                       break;
+               }
+
+               if (val < WPAN_SECURITY_LEVEL_DEFAULT ||
+                   val > IEEE802154_SCF_SECLEVEL_ENC_MIC128) {
+                       err = -EINVAL;
+               } else if (val == WPAN_SECURITY_LEVEL_DEFAULT) {
+                       ro->seclevel_override = 0;
+               } else {
+                       ro->seclevel_override = 1;
+                       ro->seclevel = val;
+               }
+               break;
        default:
                err = -ENOPROTOOPT;
                break;
index bed42a48408c6cc71cf4a47ba5bd897603cfaf7b..c09294e39ca60326d5b40c8431bf202ce5559225 100644 (file)
@@ -195,15 +195,16 @@ ieee802154_hdr_get_sechdr(const u8 *buf, struct ieee802154_sechdr *hdr)
        return pos;
 }
 
+static int ieee802154_sechdr_lengths[4] = {
+       [IEEE802154_SCF_KEY_IMPLICIT] = 5,
+       [IEEE802154_SCF_KEY_INDEX] = 6,
+       [IEEE802154_SCF_KEY_SHORT_INDEX] = 10,
+       [IEEE802154_SCF_KEY_HW_INDEX] = 14,
+};
+
 static int ieee802154_hdr_sechdr_len(u8 sc)
 {
-       switch (IEEE802154_SCF_KEY_ID_MODE(sc)) {
-       case IEEE802154_SCF_KEY_IMPLICIT: return 5;
-       case IEEE802154_SCF_KEY_INDEX: return 6;
-       case IEEE802154_SCF_KEY_SHORT_INDEX: return 10;
-       case IEEE802154_SCF_KEY_HW_INDEX: return 14;
-       default: return -EINVAL;
-       }
+       return ieee802154_sechdr_lengths[IEEE802154_SCF_KEY_ID_MODE(sc)];
 }
 
 static int ieee802154_hdr_minlen(const struct ieee802154_hdr *hdr)
@@ -285,3 +286,40 @@ ieee802154_hdr_peek_addrs(const struct sk_buff *skb, struct ieee802154_hdr *hdr)
        return pos;
 }
 EXPORT_SYMBOL_GPL(ieee802154_hdr_peek_addrs);
+
+int
+ieee802154_hdr_peek(const struct sk_buff *skb, struct ieee802154_hdr *hdr)
+{
+       const u8 *buf = skb_mac_header(skb);
+       int pos;
+
+       pos = ieee802154_hdr_peek_addrs(skb, hdr);
+       if (pos < 0)
+               return -EINVAL;
+
+       if (hdr->fc.security_enabled) {
+               u8 key_id_mode = IEEE802154_SCF_KEY_ID_MODE(*(buf + pos));
+               int want = pos + ieee802154_sechdr_lengths[key_id_mode];
+
+               if (buf + want > skb_tail_pointer(skb))
+                       return -EINVAL;
+
+               pos += ieee802154_hdr_get_sechdr(buf + pos, &hdr->sec);
+       }
+
+       return pos;
+}
+EXPORT_SYMBOL_GPL(ieee802154_hdr_peek);
+
+int ieee802154_max_payload(const struct ieee802154_hdr *hdr)
+{
+       int hlen = ieee802154_hdr_minlen(hdr);
+
+       if (hdr->fc.security_enabled) {
+               hlen += ieee802154_sechdr_lengths[hdr->sec.key_id_mode] - 1;
+               hlen += ieee802154_sechdr_authtag_len(&hdr->sec);
+       }
+
+       return IEEE802154_MTU - hlen - IEEE802154_MFR_SIZE;
+}
+EXPORT_SYMBOL_GPL(ieee802154_max_payload);
index 6693a5cf01ce5e5fc39fcd33a64547dc4bdb5748..8b83a231299e46a0668b3fe329803fa1a6154791 100644 (file)
@@ -68,4 +68,23 @@ int ieee802154_list_iface(struct sk_buff *skb, struct genl_info *info);
 int ieee802154_dump_iface(struct sk_buff *skb, struct netlink_callback *cb);
 int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info);
 
+int ieee802154_llsec_getparams(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_llsec_setparams(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_llsec_add_key(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_llsec_del_key(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_llsec_dump_keys(struct sk_buff *skb,
+                              struct netlink_callback *cb);
+int ieee802154_llsec_add_dev(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_llsec_del_dev(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_llsec_dump_devs(struct sk_buff *skb,
+                              struct netlink_callback *cb);
+int ieee802154_llsec_add_devkey(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_llsec_del_devkey(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_llsec_dump_devkeys(struct sk_buff *skb,
+                                 struct netlink_callback *cb);
+int ieee802154_llsec_add_seclevel(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_llsec_del_seclevel(struct sk_buff *skb, struct genl_info *info);
+int ieee802154_llsec_dump_seclevels(struct sk_buff *skb,
+                                   struct netlink_callback *cb);
+
 #endif
index 04b20589d97ab91eeb203ba0527122184936dc06..26efcf4fd2ff72079a678ef3a4dbd0ae887848e1 100644 (file)
@@ -124,6 +124,26 @@ static const struct genl_ops ieee8021154_ops[] = {
        IEEE802154_DUMP(IEEE802154_LIST_IFACE, ieee802154_list_iface,
                        ieee802154_dump_iface),
        IEEE802154_OP(IEEE802154_SET_MACPARAMS, ieee802154_set_macparams),
+       IEEE802154_OP(IEEE802154_LLSEC_GETPARAMS, ieee802154_llsec_getparams),
+       IEEE802154_OP(IEEE802154_LLSEC_SETPARAMS, ieee802154_llsec_setparams),
+       IEEE802154_DUMP(IEEE802154_LLSEC_LIST_KEY, NULL,
+                       ieee802154_llsec_dump_keys),
+       IEEE802154_OP(IEEE802154_LLSEC_ADD_KEY, ieee802154_llsec_add_key),
+       IEEE802154_OP(IEEE802154_LLSEC_DEL_KEY, ieee802154_llsec_del_key),
+       IEEE802154_DUMP(IEEE802154_LLSEC_LIST_DEV, NULL,
+                       ieee802154_llsec_dump_devs),
+       IEEE802154_OP(IEEE802154_LLSEC_ADD_DEV, ieee802154_llsec_add_dev),
+       IEEE802154_OP(IEEE802154_LLSEC_DEL_DEV, ieee802154_llsec_del_dev),
+       IEEE802154_DUMP(IEEE802154_LLSEC_LIST_DEVKEY, NULL,
+                       ieee802154_llsec_dump_devkeys),
+       IEEE802154_OP(IEEE802154_LLSEC_ADD_DEVKEY, ieee802154_llsec_add_devkey),
+       IEEE802154_OP(IEEE802154_LLSEC_DEL_DEVKEY, ieee802154_llsec_del_devkey),
+       IEEE802154_DUMP(IEEE802154_LLSEC_LIST_SECLEVEL, NULL,
+                       ieee802154_llsec_dump_seclevels),
+       IEEE802154_OP(IEEE802154_LLSEC_ADD_SECLEVEL,
+                     ieee802154_llsec_add_seclevel),
+       IEEE802154_OP(IEEE802154_LLSEC_DEL_SECLEVEL,
+                     ieee802154_llsec_del_seclevel),
 };
 
 static const struct genl_multicast_group ieee802154_mcgrps[] = {
index 5d285498c0f691a906de5d9c82c64f867a857418..a3281b8bfd5bf1fa24bd05a351b476031776797f 100644 (file)
@@ -715,3 +715,812 @@ int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info)
        dev_put(dev);
        return rc;
 }
+
+
+
+static int
+ieee802154_llsec_parse_key_id(struct genl_info *info,
+                             struct ieee802154_llsec_key_id *desc)
+{
+       memset(desc, 0, sizeof(*desc));
+
+       if (!info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE])
+               return -EINVAL;
+
+       desc->mode = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE]);
+
+       if (desc->mode == IEEE802154_SCF_KEY_IMPLICIT) {
+               if (!info->attrs[IEEE802154_ATTR_PAN_ID] &&
+                   !(info->attrs[IEEE802154_ATTR_SHORT_ADDR] ||
+                     info->attrs[IEEE802154_ATTR_HW_ADDR]))
+                       return -EINVAL;
+
+               desc->device_addr.pan_id = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_PAN_ID]);
+
+               if (info->attrs[IEEE802154_ATTR_SHORT_ADDR]) {
+                       desc->device_addr.mode = IEEE802154_ADDR_SHORT;
+                       desc->device_addr.short_addr = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_SHORT_ADDR]);
+               } else {
+                       desc->device_addr.mode = IEEE802154_ADDR_LONG;
+                       desc->device_addr.extended_addr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
+               }
+       }
+
+       if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT &&
+           !info->attrs[IEEE802154_ATTR_LLSEC_KEY_ID])
+               return -EINVAL;
+
+       if (desc->mode == IEEE802154_SCF_KEY_SHORT_INDEX &&
+           !info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT])
+               return -EINVAL;
+
+       if (desc->mode == IEEE802154_SCF_KEY_HW_INDEX &&
+           !info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED])
+               return -EINVAL;
+
+       if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT)
+               desc->id = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_ID]);
+
+       switch (desc->mode) {
+       case IEEE802154_SCF_KEY_SHORT_INDEX:
+       {
+               u32 source = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT]);
+               desc->short_source = cpu_to_le32(source);
+               break;
+       }
+       case IEEE802154_SCF_KEY_HW_INDEX:
+               desc->extended_source = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED]);
+               break;
+       }
+
+       return 0;
+}
+
+static int
+ieee802154_llsec_fill_key_id(struct sk_buff *msg,
+                            const struct ieee802154_llsec_key_id *desc)
+{
+       if (nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_MODE, desc->mode))
+               return -EMSGSIZE;
+
+       if (desc->mode == IEEE802154_SCF_KEY_IMPLICIT) {
+               if (nla_put_shortaddr(msg, IEEE802154_ATTR_PAN_ID,
+                                     desc->device_addr.pan_id))
+                       return -EMSGSIZE;
+
+               if (desc->device_addr.mode == IEEE802154_ADDR_SHORT &&
+                   nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR,
+                                     desc->device_addr.short_addr))
+                       return -EMSGSIZE;
+
+               if (desc->device_addr.mode == IEEE802154_ADDR_LONG &&
+                   nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR,
+                                  desc->device_addr.extended_addr))
+                       return -EMSGSIZE;
+       }
+
+       if (desc->mode != IEEE802154_SCF_KEY_IMPLICIT &&
+           nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_ID, desc->id))
+               return -EMSGSIZE;
+
+       if (desc->mode == IEEE802154_SCF_KEY_SHORT_INDEX &&
+           nla_put_u32(msg, IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT,
+                       le32_to_cpu(desc->short_source)))
+               return -EMSGSIZE;
+
+       if (desc->mode == IEEE802154_SCF_KEY_HW_INDEX &&
+           nla_put_hwaddr(msg, IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED,
+                          desc->extended_source))
+               return -EMSGSIZE;
+
+       return 0;
+}
+
+int ieee802154_llsec_getparams(struct sk_buff *skb, struct genl_info *info)
+{
+       struct sk_buff *msg;
+       struct net_device *dev = NULL;
+       int rc = -ENOBUFS;
+       struct ieee802154_mlme_ops *ops;
+       void *hdr;
+       struct ieee802154_llsec_params params;
+
+       pr_debug("%s\n", __func__);
+
+       dev = ieee802154_nl_get_dev(info);
+       if (!dev)
+               return -ENODEV;
+
+       ops = ieee802154_mlme_ops(dev);
+       if (!ops->llsec) {
+               rc = -EOPNOTSUPP;
+               goto out_dev;
+       }
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               goto out_dev;
+
+       hdr = genlmsg_put(msg, 0, info->snd_seq, &nl802154_family, 0,
+               IEEE802154_LLSEC_GETPARAMS);
+       if (!hdr)
+               goto out_free;
+
+       rc = ops->llsec->get_params(dev, &params);
+       if (rc < 0)
+               goto out_free;
+
+       if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
+           nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
+           nla_put_u8(msg, IEEE802154_ATTR_LLSEC_ENABLED, params.enabled) ||
+           nla_put_u8(msg, IEEE802154_ATTR_LLSEC_SECLEVEL, params.out_level) ||
+           nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER,
+                       be32_to_cpu(params.frame_counter)) ||
+           ieee802154_llsec_fill_key_id(msg, &params.out_key))
+               goto out_free;
+
+       dev_put(dev);
+
+       return ieee802154_nl_reply(msg, info);
+out_free:
+       nlmsg_free(msg);
+out_dev:
+       dev_put(dev);
+       return rc;
+}
+
+int ieee802154_llsec_setparams(struct sk_buff *skb, struct genl_info *info)
+{
+       struct net_device *dev = NULL;
+       int rc = -EINVAL;
+       struct ieee802154_mlme_ops *ops;
+       struct ieee802154_llsec_params params;
+       int changed = 0;
+
+       pr_debug("%s\n", __func__);
+
+       dev = ieee802154_nl_get_dev(info);
+       if (!dev)
+               return -ENODEV;
+
+       if (!info->attrs[IEEE802154_ATTR_LLSEC_ENABLED] &&
+           !info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE] &&
+           !info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL])
+               goto out;
+
+       ops = ieee802154_mlme_ops(dev);
+       if (!ops->llsec) {
+               rc = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL] &&
+           nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]) > 7)
+               goto out;
+
+       if (info->attrs[IEEE802154_ATTR_LLSEC_ENABLED]) {
+               params.enabled = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_ENABLED]);
+               changed |= IEEE802154_LLSEC_PARAM_ENABLED;
+       }
+
+       if (info->attrs[IEEE802154_ATTR_LLSEC_KEY_MODE]) {
+               if (ieee802154_llsec_parse_key_id(info, &params.out_key))
+                       goto out;
+
+               changed |= IEEE802154_LLSEC_PARAM_OUT_KEY;
+       }
+
+       if (info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]) {
+               params.out_level = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVEL]);
+               changed |= IEEE802154_LLSEC_PARAM_OUT_LEVEL;
+       }
+
+       if (info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]) {
+               u32 fc = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]);
+
+               params.frame_counter = cpu_to_be32(fc);
+               changed |= IEEE802154_LLSEC_PARAM_FRAME_COUNTER;
+       }
+
+       rc = ops->llsec->set_params(dev, &params, changed);
+
+       dev_put(dev);
+
+       return rc;
+out:
+       dev_put(dev);
+       return rc;
+}
+
+
+
+struct llsec_dump_data {
+       struct sk_buff *skb;
+       int s_idx, s_idx2;
+       int portid;
+       int nlmsg_seq;
+       struct net_device *dev;
+       struct ieee802154_mlme_ops *ops;
+       struct ieee802154_llsec_table *table;
+};
+
+static int
+ieee802154_llsec_dump_table(struct sk_buff *skb, struct netlink_callback *cb,
+                           int (*step)(struct llsec_dump_data*))
+{
+       struct net *net = sock_net(skb->sk);
+       struct net_device *dev;
+       struct llsec_dump_data data;
+       int idx = 0;
+       int first_dev = cb->args[0];
+       int rc;
+
+       for_each_netdev(net, dev) {
+               if (idx < first_dev || dev->type != ARPHRD_IEEE802154)
+                       goto skip;
+
+               data.ops = ieee802154_mlme_ops(dev);
+               if (!data.ops->llsec)
+                       goto skip;
+
+               data.skb = skb;
+               data.s_idx = cb->args[1];
+               data.s_idx2 = cb->args[2];
+               data.dev = dev;
+               data.portid = NETLINK_CB(cb->skb).portid;
+               data.nlmsg_seq = cb->nlh->nlmsg_seq;
+
+               data.ops->llsec->lock_table(dev);
+               data.ops->llsec->get_table(data.dev, &data.table);
+               rc = step(&data);
+               data.ops->llsec->unlock_table(dev);
+
+               if (rc < 0)
+                       break;
+
+skip:
+               idx++;
+       }
+       cb->args[0] = idx;
+
+       return skb->len;
+}
+
+static int
+ieee802154_nl_llsec_change(struct sk_buff *skb, struct genl_info *info,
+                          int (*fn)(struct net_device*, struct genl_info*))
+{
+       struct net_device *dev = NULL;
+       int rc = -EINVAL;
+
+       dev = ieee802154_nl_get_dev(info);
+       if (!dev)
+               return -ENODEV;
+
+       if (!ieee802154_mlme_ops(dev)->llsec)
+               rc = -EOPNOTSUPP;
+       else
+               rc = fn(dev, info);
+
+       dev_put(dev);
+       return rc;
+}
+
+
+
+static int
+ieee802154_llsec_parse_key(struct genl_info *info,
+                          struct ieee802154_llsec_key *key)
+{
+       u8 frames;
+       u32 commands[256 / 32];
+
+       memset(key, 0, sizeof(*key));
+
+       if (!info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES] ||
+           !info->attrs[IEEE802154_ATTR_LLSEC_KEY_BYTES])
+               return -EINVAL;
+
+       frames = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES]);
+       if ((frames & BIT(IEEE802154_FC_TYPE_MAC_CMD)) &&
+           !info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS])
+               return -EINVAL;
+
+       if (info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS]) {
+               nla_memcpy(commands,
+                          info->attrs[IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS],
+                          256 / 8);
+
+               if (commands[0] || commands[1] || commands[2] || commands[3] ||
+                   commands[4] || commands[5] || commands[6] ||
+                   commands[7] >= BIT(IEEE802154_CMD_GTS_REQ + 1))
+                       return -EINVAL;
+
+               key->cmd_frame_ids = commands[7];
+       }
+
+       key->frame_types = frames;
+
+       nla_memcpy(key->key, info->attrs[IEEE802154_ATTR_LLSEC_KEY_BYTES],
+                  IEEE802154_LLSEC_KEY_SIZE);
+
+       return 0;
+}
+
+static int llsec_add_key(struct net_device *dev, struct genl_info *info)
+{
+       struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+       struct ieee802154_llsec_key key;
+       struct ieee802154_llsec_key_id id;
+
+       if (ieee802154_llsec_parse_key(info, &key) ||
+           ieee802154_llsec_parse_key_id(info, &id))
+               return -EINVAL;
+
+       return ops->llsec->add_key(dev, &id, &key);
+}
+
+int ieee802154_llsec_add_key(struct sk_buff *skb, struct genl_info *info)
+{
+       if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) !=
+           (NLM_F_CREATE | NLM_F_EXCL))
+               return -EINVAL;
+
+       return ieee802154_nl_llsec_change(skb, info, llsec_add_key);
+}
+
+static int llsec_remove_key(struct net_device *dev, struct genl_info *info)
+{
+       struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+       struct ieee802154_llsec_key_id id;
+
+       if (ieee802154_llsec_parse_key_id(info, &id))
+               return -EINVAL;
+
+       return ops->llsec->del_key(dev, &id);
+}
+
+int ieee802154_llsec_del_key(struct sk_buff *skb, struct genl_info *info)
+{
+       return ieee802154_nl_llsec_change(skb, info, llsec_remove_key);
+}
+
+static int
+ieee802154_nl_fill_key(struct sk_buff *msg, u32 portid, u32 seq,
+                      const struct ieee802154_llsec_key_entry *key,
+                      const struct net_device *dev)
+{
+       void *hdr;
+       u32 commands[256 / 32];
+
+       hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI,
+                         IEEE802154_LLSEC_LIST_KEY);
+       if (!hdr)
+               goto out;
+
+       if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
+           nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
+           ieee802154_llsec_fill_key_id(msg, &key->id) ||
+           nla_put_u8(msg, IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES,
+                      key->key->frame_types))
+               goto nla_put_failure;
+
+       if (key->key->frame_types & BIT(IEEE802154_FC_TYPE_MAC_CMD)) {
+               memset(commands, 0, sizeof(commands));
+               commands[7] = key->key->cmd_frame_ids;
+               if (nla_put(msg, IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS,
+                           sizeof(commands), commands))
+                       goto nla_put_failure;
+       }
+
+       if (nla_put(msg, IEEE802154_ATTR_LLSEC_KEY_BYTES,
+                   IEEE802154_LLSEC_KEY_SIZE, key->key->key))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+out:
+       return -EMSGSIZE;
+}
+
+static int llsec_iter_keys(struct llsec_dump_data *data)
+{
+       struct ieee802154_llsec_key_entry *pos;
+       int rc = 0, idx = 0;
+
+       list_for_each_entry(pos, &data->table->keys, list) {
+               if (idx++ < data->s_idx)
+                       continue;
+
+               if (ieee802154_nl_fill_key(data->skb, data->portid,
+                                          data->nlmsg_seq, pos, data->dev)) {
+                       rc = -EMSGSIZE;
+                       break;
+               }
+
+               data->s_idx++;
+       }
+
+       return rc;
+}
+
+int ieee802154_llsec_dump_keys(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       return ieee802154_llsec_dump_table(skb, cb, llsec_iter_keys);
+}
+
+
+
+static int
+llsec_parse_dev(struct genl_info *info,
+               struct ieee802154_llsec_device *dev)
+{
+       memset(dev, 0, sizeof(*dev));
+
+       if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER] ||
+           !info->attrs[IEEE802154_ATTR_HW_ADDR] ||
+           !info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE] ||
+           !info->attrs[IEEE802154_ATTR_LLSEC_DEV_KEY_MODE] ||
+           (!!info->attrs[IEEE802154_ATTR_PAN_ID] !=
+            !!info->attrs[IEEE802154_ATTR_SHORT_ADDR]))
+               return -EINVAL;
+
+       if (info->attrs[IEEE802154_ATTR_PAN_ID]) {
+               dev->pan_id = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_PAN_ID]);
+               dev->short_addr = nla_get_shortaddr(info->attrs[IEEE802154_ATTR_SHORT_ADDR]);
+       } else {
+               dev->short_addr = cpu_to_le16(IEEE802154_ADDR_UNDEF);
+       }
+
+       dev->hwaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
+       dev->frame_counter = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]);
+       dev->seclevel_exempt = !!nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE]);
+       dev->key_mode = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_KEY_MODE]);
+
+       if (dev->key_mode >= __IEEE802154_LLSEC_DEVKEY_MAX)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int llsec_add_dev(struct net_device *dev, struct genl_info *info)
+{
+       struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+       struct ieee802154_llsec_device desc;
+
+       if (llsec_parse_dev(info, &desc))
+               return -EINVAL;
+
+       return ops->llsec->add_dev(dev, &desc);
+}
+
+int ieee802154_llsec_add_dev(struct sk_buff *skb, struct genl_info *info)
+{
+       if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) !=
+           (NLM_F_CREATE | NLM_F_EXCL))
+               return -EINVAL;
+
+       return ieee802154_nl_llsec_change(skb, info, llsec_add_dev);
+}
+
+static int llsec_del_dev(struct net_device *dev, struct genl_info *info)
+{
+       struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+       __le64 devaddr;
+
+       if (!info->attrs[IEEE802154_ATTR_HW_ADDR])
+               return -EINVAL;
+
+       devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
+
+       return ops->llsec->del_dev(dev, devaddr);
+}
+
+int ieee802154_llsec_del_dev(struct sk_buff *skb, struct genl_info *info)
+{
+       return ieee802154_nl_llsec_change(skb, info, llsec_del_dev);
+}
+
+static int
+ieee802154_nl_fill_dev(struct sk_buff *msg, u32 portid, u32 seq,
+                      const struct ieee802154_llsec_device *desc,
+                      const struct net_device *dev)
+{
+       void *hdr;
+
+       hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI,
+                         IEEE802154_LLSEC_LIST_DEV);
+       if (!hdr)
+               goto out;
+
+       if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
+           nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
+           nla_put_shortaddr(msg, IEEE802154_ATTR_PAN_ID, desc->pan_id) ||
+           nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR,
+                             desc->short_addr) ||
+           nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR, desc->hwaddr) ||
+           nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER,
+                       desc->frame_counter) ||
+           nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_OVERRIDE,
+                      desc->seclevel_exempt) ||
+           nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_KEY_MODE, desc->key_mode))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+out:
+       return -EMSGSIZE;
+}
+
+static int llsec_iter_devs(struct llsec_dump_data *data)
+{
+       struct ieee802154_llsec_device *pos;
+       int rc = 0, idx = 0;
+
+       list_for_each_entry(pos, &data->table->devices, list) {
+               if (idx++ < data->s_idx)
+                       continue;
+
+               if (ieee802154_nl_fill_dev(data->skb, data->portid,
+                                          data->nlmsg_seq, pos, data->dev)) {
+                       rc = -EMSGSIZE;
+                       break;
+               }
+
+               data->s_idx++;
+       }
+
+       return rc;
+}
+
+int ieee802154_llsec_dump_devs(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devs);
+}
+
+
+
+static int llsec_add_devkey(struct net_device *dev, struct genl_info *info)
+{
+       struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+       struct ieee802154_llsec_device_key key;
+       __le64 devaddr;
+
+       if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER] ||
+           !info->attrs[IEEE802154_ATTR_HW_ADDR] ||
+           ieee802154_llsec_parse_key_id(info, &key.key_id))
+               return -EINVAL;
+
+       devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
+       key.frame_counter = nla_get_u32(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_COUNTER]);
+
+       return ops->llsec->add_devkey(dev, devaddr, &key);
+}
+
+int ieee802154_llsec_add_devkey(struct sk_buff *skb, struct genl_info *info)
+{
+       if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) !=
+           (NLM_F_CREATE | NLM_F_EXCL))
+               return -EINVAL;
+
+       return ieee802154_nl_llsec_change(skb, info, llsec_add_devkey);
+}
+
+static int llsec_del_devkey(struct net_device *dev, struct genl_info *info)
+{
+       struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+       struct ieee802154_llsec_device_key key;
+       __le64 devaddr;
+
+       if (!info->attrs[IEEE802154_ATTR_HW_ADDR] ||
+           ieee802154_llsec_parse_key_id(info, &key.key_id))
+               return -EINVAL;
+
+       devaddr = nla_get_hwaddr(info->attrs[IEEE802154_ATTR_HW_ADDR]);
+
+       return ops->llsec->del_devkey(dev, devaddr, &key);
+}
+
+int ieee802154_llsec_del_devkey(struct sk_buff *skb, struct genl_info *info)
+{
+       return ieee802154_nl_llsec_change(skb, info, llsec_del_devkey);
+}
+
+static int
+ieee802154_nl_fill_devkey(struct sk_buff *msg, u32 portid, u32 seq,
+                         __le64 devaddr,
+                         const struct ieee802154_llsec_device_key *devkey,
+                         const struct net_device *dev)
+{
+       void *hdr;
+
+       hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI,
+                         IEEE802154_LLSEC_LIST_DEVKEY);
+       if (!hdr)
+               goto out;
+
+       if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
+           nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
+           nla_put_hwaddr(msg, IEEE802154_ATTR_HW_ADDR, devaddr) ||
+           nla_put_u32(msg, IEEE802154_ATTR_LLSEC_FRAME_COUNTER,
+                       devkey->frame_counter) ||
+           ieee802154_llsec_fill_key_id(msg, &devkey->key_id))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+out:
+       return -EMSGSIZE;
+}
+
+static int llsec_iter_devkeys(struct llsec_dump_data *data)
+{
+       struct ieee802154_llsec_device *dpos;
+       struct ieee802154_llsec_device_key *kpos;
+       int rc = 0, idx = 0, idx2;
+
+       list_for_each_entry(dpos, &data->table->devices, list) {
+               if (idx++ < data->s_idx)
+                       continue;
+
+               idx2 = 0;
+
+               list_for_each_entry(kpos, &dpos->keys, list) {
+                       if (idx2++ < data->s_idx2)
+                               continue;
+
+                       if (ieee802154_nl_fill_devkey(data->skb, data->portid,
+                                                     data->nlmsg_seq,
+                                                     dpos->hwaddr, kpos,
+                                                     data->dev)) {
+                               return rc = -EMSGSIZE;
+                       }
+
+                       data->s_idx2++;
+               }
+
+               data->s_idx++;
+       }
+
+       return rc;
+}
+
+int ieee802154_llsec_dump_devkeys(struct sk_buff *skb,
+                                 struct netlink_callback *cb)
+{
+       return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devkeys);
+}
+
+
+
+static int
+llsec_parse_seclevel(struct genl_info *info,
+                    struct ieee802154_llsec_seclevel *sl)
+{
+       memset(sl, 0, sizeof(*sl));
+
+       if (!info->attrs[IEEE802154_ATTR_LLSEC_FRAME_TYPE] ||
+           !info->attrs[IEEE802154_ATTR_LLSEC_SECLEVELS] ||
+           !info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE])
+               return -EINVAL;
+
+       sl->frame_type = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_FRAME_TYPE]);
+       if (sl->frame_type == IEEE802154_FC_TYPE_MAC_CMD) {
+               if (!info->attrs[IEEE802154_ATTR_LLSEC_CMD_FRAME_ID])
+                       return -EINVAL;
+
+               sl->cmd_frame_id = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_CMD_FRAME_ID]);
+       }
+
+       sl->sec_levels = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_SECLEVELS]);
+       sl->device_override = nla_get_u8(info->attrs[IEEE802154_ATTR_LLSEC_DEV_OVERRIDE]);
+
+       return 0;
+}
+
+static int llsec_add_seclevel(struct net_device *dev, struct genl_info *info)
+{
+       struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+       struct ieee802154_llsec_seclevel sl;
+
+       if (llsec_parse_seclevel(info, &sl))
+               return -EINVAL;
+
+       return ops->llsec->add_seclevel(dev, &sl);
+}
+
+int ieee802154_llsec_add_seclevel(struct sk_buff *skb, struct genl_info *info)
+{
+       if ((info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) !=
+           (NLM_F_CREATE | NLM_F_EXCL))
+               return -EINVAL;
+
+       return ieee802154_nl_llsec_change(skb, info, llsec_add_seclevel);
+}
+
+static int llsec_del_seclevel(struct net_device *dev, struct genl_info *info)
+{
+       struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+       struct ieee802154_llsec_seclevel sl;
+
+       if (llsec_parse_seclevel(info, &sl))
+               return -EINVAL;
+
+       return ops->llsec->del_seclevel(dev, &sl);
+}
+
+int ieee802154_llsec_del_seclevel(struct sk_buff *skb, struct genl_info *info)
+{
+       return ieee802154_nl_llsec_change(skb, info, llsec_del_seclevel);
+}
+
+static int
+ieee802154_nl_fill_seclevel(struct sk_buff *msg, u32 portid, u32 seq,
+                           const struct ieee802154_llsec_seclevel *sl,
+                           const struct net_device *dev)
+{
+       void *hdr;
+
+       hdr = genlmsg_put(msg, 0, seq, &nl802154_family, NLM_F_MULTI,
+                         IEEE802154_LLSEC_LIST_SECLEVEL);
+       if (!hdr)
+               goto out;
+
+       if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
+           nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
+           nla_put_u8(msg, IEEE802154_ATTR_LLSEC_FRAME_TYPE, sl->frame_type) ||
+           nla_put_u8(msg, IEEE802154_ATTR_LLSEC_SECLEVELS, sl->sec_levels) ||
+           nla_put_u8(msg, IEEE802154_ATTR_LLSEC_DEV_OVERRIDE,
+                      sl->device_override))
+               goto nla_put_failure;
+
+       if (sl->frame_type == IEEE802154_FC_TYPE_MAC_CMD &&
+           nla_put_u8(msg, IEEE802154_ATTR_LLSEC_CMD_FRAME_ID,
+                      sl->cmd_frame_id))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+out:
+       return -EMSGSIZE;
+}
+
+static int llsec_iter_seclevels(struct llsec_dump_data *data)
+{
+       struct ieee802154_llsec_seclevel *pos;
+       int rc = 0, idx = 0;
+
+       list_for_each_entry(pos, &data->table->security_levels, list) {
+               if (idx++ < data->s_idx)
+                       continue;
+
+               if (ieee802154_nl_fill_seclevel(data->skb, data->portid,
+                                               data->nlmsg_seq, pos,
+                                               data->dev)) {
+                       rc = -EMSGSIZE;
+                       break;
+               }
+
+               data->s_idx++;
+       }
+
+       return rc;
+}
+
+int ieee802154_llsec_dump_seclevels(struct sk_buff *skb,
+                                   struct netlink_callback *cb)
+{
+       return ieee802154_llsec_dump_table(skb, cb, llsec_iter_seclevels);
+}
index fd7be5e45cefb99d37df9d173c9bf7c0ab1d7dc3..3a703ab88348fc38481ec583ff5d0885ae8c9655 100644 (file)
@@ -62,5 +62,21 @@ const struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = {
        [IEEE802154_ATTR_CSMA_MAX_BE] = { .type = NLA_U8, },
 
        [IEEE802154_ATTR_FRAME_RETRIES] = { .type = NLA_S8, },
+
+       [IEEE802154_ATTR_LLSEC_ENABLED] = { .type = NLA_U8, },
+       [IEEE802154_ATTR_LLSEC_SECLEVEL] = { .type = NLA_U8, },
+       [IEEE802154_ATTR_LLSEC_KEY_MODE] = { .type = NLA_U8, },
+       [IEEE802154_ATTR_LLSEC_KEY_SOURCE_SHORT] = { .type = NLA_U32, },
+       [IEEE802154_ATTR_LLSEC_KEY_SOURCE_EXTENDED] = { .type = NLA_HW_ADDR, },
+       [IEEE802154_ATTR_LLSEC_KEY_ID] = { .type = NLA_U8, },
+       [IEEE802154_ATTR_LLSEC_FRAME_COUNTER] = { .type = NLA_U32 },
+       [IEEE802154_ATTR_LLSEC_KEY_BYTES] = { .len = 16, },
+       [IEEE802154_ATTR_LLSEC_KEY_USAGE_FRAME_TYPES] = { .type = NLA_U8, },
+       [IEEE802154_ATTR_LLSEC_KEY_USAGE_COMMANDS] = { .len = 258 / 8 },
+       [IEEE802154_ATTR_LLSEC_FRAME_TYPE] = { .type = NLA_U8, },
+       [IEEE802154_ATTR_LLSEC_CMD_FRAME_ID] = { .type = NLA_U8, },
+       [IEEE802154_ATTR_LLSEC_SECLEVELS] = { .type = NLA_U8, },
+       [IEEE802154_ATTR_LLSEC_DEV_OVERRIDE] = { .type = NLA_U8, },
+       [IEEE802154_ATTR_LLSEC_DEV_KEY_MODE] = { .type = NLA_U8, },
 };
 
index ef2d54372b134330d0fa7c51c2049379ce69e85e..6f1428c4870b11e9bab087c54d414079514caf8a 100644 (file)
@@ -36,7 +36,7 @@ struct lowpan_frag_info {
        u8 d_offset;
 };
 
-struct lowpan_frag_info *lowpan_cb(struct sk_buff *skb)
+static struct lowpan_frag_info *lowpan_cb(struct sk_buff *skb)
 {
        return (struct lowpan_frag_info *)skb->cb;
 }
@@ -120,6 +120,8 @@ fq_find(struct net *net, const struct lowpan_frag_info *frag_info,
        struct inet_frag_queue *q;
        struct lowpan_create_arg arg;
        unsigned int hash;
+       struct netns_ieee802154_lowpan *ieee802154_lowpan =
+               net_ieee802154_lowpan(net);
 
        arg.tag = frag_info->d_tag;
        arg.d_size = frag_info->d_size;
@@ -129,7 +131,7 @@ fq_find(struct net *net, const struct lowpan_frag_info *frag_info,
        read_lock(&lowpan_frags.lock);
        hash = lowpan_hash_frag(frag_info->d_tag, frag_info->d_size, src, dst);
 
-       q = inet_frag_find(&net->ieee802154_lowpan.frags,
+       q = inet_frag_find(&ieee802154_lowpan->frags,
                           &lowpan_frags, &arg, hash);
        if (IS_ERR_OR_NULL(q)) {
                inet_frag_maybe_warn_overflow(q, pr_fmt());
@@ -357,6 +359,8 @@ int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type)
        struct net *net = dev_net(skb->dev);
        struct lowpan_frag_info *frag_info = lowpan_cb(skb);
        struct ieee802154_addr source, dest;
+       struct netns_ieee802154_lowpan *ieee802154_lowpan =
+               net_ieee802154_lowpan(net);
        int err;
 
        source = mac_cb(skb)->source;
@@ -366,10 +370,10 @@ int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type)
        if (err < 0)
                goto err;
 
-       if (frag_info->d_size > net->ieee802154_lowpan.max_dsize)
+       if (frag_info->d_size > ieee802154_lowpan->max_dsize)
                goto err;
 
-       inet_frag_evictor(&net->ieee802154_lowpan.frags, &lowpan_frags, false);
+       inet_frag_evictor(&ieee802154_lowpan->frags, &lowpan_frags, false);
 
        fq = fq_find(net, frag_info, &source, &dest);
        if (fq != NULL) {
@@ -436,6 +440,8 @@ static int __net_init lowpan_frags_ns_sysctl_register(struct net *net)
 {
        struct ctl_table *table;
        struct ctl_table_header *hdr;
+       struct netns_ieee802154_lowpan *ieee802154_lowpan =
+               net_ieee802154_lowpan(net);
 
        table = lowpan_frags_ns_ctl_table;
        if (!net_eq(net, &init_net)) {
@@ -444,10 +450,10 @@ static int __net_init lowpan_frags_ns_sysctl_register(struct net *net)
                if (table == NULL)
                        goto err_alloc;
 
-               table[0].data = &net->ieee802154_lowpan.frags.high_thresh;
-               table[1].data = &net->ieee802154_lowpan.frags.low_thresh;
-               table[2].data = &net->ieee802154_lowpan.frags.timeout;
-               table[3].data = &net->ieee802154_lowpan.max_dsize;
+               table[0].data = &ieee802154_lowpan->frags.high_thresh;
+               table[1].data = &ieee802154_lowpan->frags.low_thresh;
+               table[2].data = &ieee802154_lowpan->frags.timeout;
+               table[3].data = &ieee802154_lowpan->max_dsize;
 
                /* Don't export sysctls to unprivileged users */
                if (net->user_ns != &init_user_ns)
@@ -458,7 +464,7 @@ static int __net_init lowpan_frags_ns_sysctl_register(struct net *net)
        if (hdr == NULL)
                goto err_reg;
 
-       net->ieee802154_lowpan.sysctl.frags_hdr = hdr;
+       ieee802154_lowpan->sysctl.frags_hdr = hdr;
        return 0;
 
 err_reg:
@@ -471,9 +477,11 @@ static int __net_init lowpan_frags_ns_sysctl_register(struct net *net)
 static void __net_exit lowpan_frags_ns_sysctl_unregister(struct net *net)
 {
        struct ctl_table *table;
+       struct netns_ieee802154_lowpan *ieee802154_lowpan =
+               net_ieee802154_lowpan(net);
 
-       table = net->ieee802154_lowpan.sysctl.frags_hdr->ctl_table_arg;
-       unregister_net_sysctl_table(net->ieee802154_lowpan.sysctl.frags_hdr);
+       table = ieee802154_lowpan->sysctl.frags_hdr->ctl_table_arg;
+       unregister_net_sysctl_table(ieee802154_lowpan->sysctl.frags_hdr);
        if (!net_eq(net, &init_net))
                kfree(table);
 }
@@ -514,20 +522,26 @@ static inline void lowpan_frags_sysctl_unregister(void)
 
 static int __net_init lowpan_frags_init_net(struct net *net)
 {
-       net->ieee802154_lowpan.frags.high_thresh = IPV6_FRAG_HIGH_THRESH;
-       net->ieee802154_lowpan.frags.low_thresh = IPV6_FRAG_LOW_THRESH;
-       net->ieee802154_lowpan.frags.timeout = IPV6_FRAG_TIMEOUT;
-       net->ieee802154_lowpan.max_dsize = 0xFFFF;
+       struct netns_ieee802154_lowpan *ieee802154_lowpan =
+               net_ieee802154_lowpan(net);
 
-       inet_frags_init_net(&net->ieee802154_lowpan.frags);
+       ieee802154_lowpan->frags.high_thresh = IPV6_FRAG_HIGH_THRESH;
+       ieee802154_lowpan->frags.low_thresh = IPV6_FRAG_LOW_THRESH;
+       ieee802154_lowpan->frags.timeout = IPV6_FRAG_TIMEOUT;
+       ieee802154_lowpan->max_dsize = 0xFFFF;
+
+       inet_frags_init_net(&ieee802154_lowpan->frags);
 
        return lowpan_frags_ns_sysctl_register(net);
 }
 
 static void __net_exit lowpan_frags_exit_net(struct net *net)
 {
+       struct netns_ieee802154_lowpan *ieee802154_lowpan =
+               net_ieee802154_lowpan(net);
+
        lowpan_frags_ns_sysctl_unregister(net);
-       inet_frags_exit_net(&net->ieee802154_lowpan.frags, &lowpan_frags);
+       inet_frags_exit_net(&ieee802154_lowpan->frags, &lowpan_frags);
 }
 
 static struct pernet_operations lowpan_frags_ops = {
index 6d6dd345bc4d89dea7dac7b179e4ef0f391b3954..d5e6836cf772d677271c46681ffa442baa9d0c99 100644 (file)
@@ -254,7 +254,6 @@ static int inet_create(struct net *net, struct socket *sock, int protocol,
        struct inet_sock *inet;
        struct proto *answer_prot;
        unsigned char answer_flags;
-       char answer_no_check;
        int try_loading_module = 0;
        int err;
 
@@ -312,7 +311,6 @@ static int inet_create(struct net *net, struct socket *sock, int protocol,
 
        sock->ops = answer->ops;
        answer_prot = answer->prot;
-       answer_no_check = answer->no_check;
        answer_flags = answer->flags;
        rcu_read_unlock();
 
@@ -324,7 +322,6 @@ static int inet_create(struct net *net, struct socket *sock, int protocol,
                goto out;
 
        err = 0;
-       sk->sk_no_check = answer_no_check;
        if (INET_PROTOSW_REUSE & answer_flags)
                sk->sk_reuse = SK_CAN_REUSE;
 
@@ -1002,7 +999,6 @@ static struct inet_protosw inetsw_array[] =
                .protocol =   IPPROTO_TCP,
                .prot =       &tcp_prot,
                .ops =        &inet_stream_ops,
-               .no_check =   0,
                .flags =      INET_PROTOSW_PERMANENT |
                              INET_PROTOSW_ICSK,
        },
@@ -1012,7 +1008,6 @@ static struct inet_protosw inetsw_array[] =
                .protocol =   IPPROTO_UDP,
                .prot =       &udp_prot,
                .ops =        &inet_dgram_ops,
-               .no_check =   UDP_CSUM_DEFAULT,
                .flags =      INET_PROTOSW_PERMANENT,
        },
 
@@ -1021,7 +1016,6 @@ static struct inet_protosw inetsw_array[] =
                .protocol =   IPPROTO_ICMP,
                .prot =       &ping_prot,
                .ops =        &inet_dgram_ops,
-               .no_check =   UDP_CSUM_DEFAULT,
                .flags =      INET_PROTOSW_REUSE,
        },
 
@@ -1030,7 +1024,6 @@ static struct inet_protosw inetsw_array[] =
               .protocol =   IPPROTO_IP,        /* wild card */
               .prot =       &raw_prot,
               .ops =        &inet_sockraw_ops,
-              .no_check =   UDP_CSUM_DEFAULT,
               .flags =      INET_PROTOSW_REUSE,
        }
 };
@@ -1261,10 +1254,12 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
                       SKB_GSO_DODGY |
                       SKB_GSO_TCP_ECN |
                       SKB_GSO_GRE |
+                      SKB_GSO_GRE_CSUM |
                       SKB_GSO_IPIP |
                       SKB_GSO_SIT |
                       SKB_GSO_TCPV6 |
                       SKB_GSO_UDP_TUNNEL |
+                      SKB_GSO_UDP_TUNNEL_CSUM |
                       SKB_GSO_MPLS |
                       0)))
                goto out;
@@ -1476,22 +1471,20 @@ int inet_ctl_sock_create(struct sock **sk, unsigned short family,
 }
 EXPORT_SYMBOL_GPL(inet_ctl_sock_create);
 
-unsigned long snmp_fold_field(void __percpu *mib[], int offt)
+unsigned long snmp_fold_field(void __percpu *mib, int offt)
 {
        unsigned long res = 0;
-       int i, j;
+       int i;
 
-       for_each_possible_cpu(i) {
-               for (j = 0; j < SNMP_ARRAY_SZ; j++)
-                       res += *(((unsigned long *) per_cpu_ptr(mib[j], i)) + offt);
-       }
+       for_each_possible_cpu(i)
+               res += *(((unsigned long *) per_cpu_ptr(mib, i)) + offt);
        return res;
 }
 EXPORT_SYMBOL_GPL(snmp_fold_field);
 
 #if BITS_PER_LONG==32
 
-u64 snmp_fold_field64(void __percpu *mib[], int offt, size_t syncp_offset)
+u64 snmp_fold_field64(void __percpu *mib, int offt, size_t syncp_offset)
 {
        u64 res = 0;
        int cpu;
@@ -1502,7 +1495,7 @@ u64 snmp_fold_field64(void __percpu *mib[], int offt, size_t syncp_offset)
                u64 v;
                unsigned int start;
 
-               bhptr = per_cpu_ptr(mib[0], cpu);
+               bhptr = per_cpu_ptr(mib, cpu);
                syncp = (struct u64_stats_sync *)(bhptr + syncp_offset);
                do {
                        start = u64_stats_fetch_begin_irq(syncp);
@@ -1516,25 +1509,6 @@ u64 snmp_fold_field64(void __percpu *mib[], int offt, size_t syncp_offset)
 EXPORT_SYMBOL_GPL(snmp_fold_field64);
 #endif
 
-int snmp_mib_init(void __percpu *ptr[2], size_t mibsize, size_t align)
-{
-       BUG_ON(ptr == NULL);
-       ptr[0] = __alloc_percpu(mibsize, align);
-       if (!ptr[0])
-               return -ENOMEM;
-
-#if SNMP_ARRAY_SZ == 2
-       ptr[1] = __alloc_percpu(mibsize, align);
-       if (!ptr[1]) {
-               free_percpu(ptr[0]);
-               ptr[0] = NULL;
-               return -ENOMEM;
-       }
-#endif
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snmp_mib_init);
-
 #ifdef CONFIG_IP_MULTICAST
 static const struct net_protocol igmp_protocol = {
        .handler =      igmp_rcv,
@@ -1570,40 +1544,30 @@ static __net_init int ipv4_mib_init_net(struct net *net)
 {
        int i;
 
-       if (snmp_mib_init((void __percpu **)net->mib.tcp_statistics,
-                         sizeof(struct tcp_mib),
-                         __alignof__(struct tcp_mib)) < 0)
+       net->mib.tcp_statistics = alloc_percpu(struct tcp_mib);
+       if (!net->mib.tcp_statistics)
                goto err_tcp_mib;
-       if (snmp_mib_init((void __percpu **)net->mib.ip_statistics,
-                         sizeof(struct ipstats_mib),
-                         __alignof__(struct ipstats_mib)) < 0)
+       net->mib.ip_statistics = alloc_percpu(struct ipstats_mib);
+       if (!net->mib.ip_statistics)
                goto err_ip_mib;
 
        for_each_possible_cpu(i) {
                struct ipstats_mib *af_inet_stats;
-               af_inet_stats = per_cpu_ptr(net->mib.ip_statistics[0], i);
+               af_inet_stats = per_cpu_ptr(net->mib.ip_statistics, i);
                u64_stats_init(&af_inet_stats->syncp);
-#if SNMP_ARRAY_SZ == 2
-               af_inet_stats = per_cpu_ptr(net->mib.ip_statistics[1], i);
-               u64_stats_init(&af_inet_stats->syncp);
-#endif
        }
 
-       if (snmp_mib_init((void __percpu **)net->mib.net_statistics,
-                         sizeof(struct linux_mib),
-                         __alignof__(struct linux_mib)) < 0)
+       net->mib.net_statistics = alloc_percpu(struct linux_mib);
+       if (!net->mib.net_statistics)
                goto err_net_mib;
-       if (snmp_mib_init((void __percpu **)net->mib.udp_statistics,
-                         sizeof(struct udp_mib),
-                         __alignof__(struct udp_mib)) < 0)
+       net->mib.udp_statistics = alloc_percpu(struct udp_mib);
+       if (!net->mib.udp_statistics)
                goto err_udp_mib;
-       if (snmp_mib_init((void __percpu **)net->mib.udplite_statistics,
-                         sizeof(struct udp_mib),
-                         __alignof__(struct udp_mib)) < 0)
+       net->mib.udplite_statistics = alloc_percpu(struct udp_mib);
+       if (!net->mib.udplite_statistics)
                goto err_udplite_mib;
-       if (snmp_mib_init((void __percpu **)net->mib.icmp_statistics,
-                         sizeof(struct icmp_mib),
-                         __alignof__(struct icmp_mib)) < 0)
+       net->mib.icmp_statistics = alloc_percpu(struct icmp_mib);
+       if (!net->mib.icmp_statistics)
                goto err_icmp_mib;
        net->mib.icmpmsg_statistics = kzalloc(sizeof(struct icmpmsg_mib),
                                              GFP_KERNEL);
@@ -1614,17 +1578,17 @@ static __net_init int ipv4_mib_init_net(struct net *net)
        return 0;
 
 err_icmpmsg_mib:
-       snmp_mib_free((void __percpu **)net->mib.icmp_statistics);
+       free_percpu(net->mib.icmp_statistics);
 err_icmp_mib:
-       snmp_mib_free((void __percpu **)net->mib.udplite_statistics);
+       free_percpu(net->mib.udplite_statistics);
 err_udplite_mib:
-       snmp_mib_free((void __percpu **)net->mib.udp_statistics);
+       free_percpu(net->mib.udp_statistics);
 err_udp_mib:
-       snmp_mib_free((void __percpu **)net->mib.net_statistics);
+       free_percpu(net->mib.net_statistics);
 err_net_mib:
-       snmp_mib_free((void __percpu **)net->mib.ip_statistics);
+       free_percpu(net->mib.ip_statistics);
 err_ip_mib:
-       snmp_mib_free((void __percpu **)net->mib.tcp_statistics);
+       free_percpu(net->mib.tcp_statistics);
 err_tcp_mib:
        return -ENOMEM;
 }
@@ -1632,12 +1596,12 @@ static __net_init int ipv4_mib_init_net(struct net *net)
 static __net_exit void ipv4_mib_exit_net(struct net *net)
 {
        kfree(net->mib.icmpmsg_statistics);
-       snmp_mib_free((void __percpu **)net->mib.icmp_statistics);
-       snmp_mib_free((void __percpu **)net->mib.udplite_statistics);
-       snmp_mib_free((void __percpu **)net->mib.udp_statistics);
-       snmp_mib_free((void __percpu **)net->mib.net_statistics);
-       snmp_mib_free((void __percpu **)net->mib.ip_statistics);
-       snmp_mib_free((void __percpu **)net->mib.tcp_statistics);
+       free_percpu(net->mib.icmp_statistics);
+       free_percpu(net->mib.udplite_statistics);
+       free_percpu(net->mib.udp_statistics);
+       free_percpu(net->mib.net_statistics);
+       free_percpu(net->mib.ip_statistics);
+       free_percpu(net->mib.tcp_statistics);
 }
 
 static __net_initdata struct pernet_operations ipv4_mib_ops = {
@@ -1736,13 +1700,9 @@ static int __init inet_init(void)
 
        BUILD_BUG_ON(sizeof(struct inet_skb_parm) > FIELD_SIZEOF(struct sk_buff, cb));
 
-       sysctl_local_reserved_ports = kzalloc(65536 / 8, GFP_KERNEL);
-       if (!sysctl_local_reserved_ports)
-               goto out;
-
        rc = proto_register(&tcp_prot, 1);
        if (rc)
-               goto out_free_reserved_ports;
+               goto out;
 
        rc = proto_register(&udp_prot, 1);
        if (rc)
@@ -1852,8 +1812,6 @@ static int __init inet_init(void)
        proto_unregister(&udp_prot);
 out_unregister_tcp_proto:
        proto_unregister(&tcp_prot);
-out_free_reserved_ports:
-       kfree(sysctl_local_reserved_ports);
        goto out;
 }
 
index 8b5134c582f147a94938c34a01dd0691c288bb78..a3095fdefbed98ed4e320ac6c44ea3e18241d1a4 100644 (file)
@@ -86,18 +86,26 @@ int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 }
 EXPORT_SYMBOL(ip4_datagram_connect);
 
+/* Because UDP xmit path can manipulate sk_dst_cache without holding
+ * socket lock, we need to use sk_dst_set() here,
+ * even if we own the socket lock.
+ */
 void ip4_datagram_release_cb(struct sock *sk)
 {
        const struct inet_sock *inet = inet_sk(sk);
        const struct ip_options_rcu *inet_opt;
        __be32 daddr = inet->inet_daddr;
+       struct dst_entry *dst;
        struct flowi4 fl4;
        struct rtable *rt;
 
-       if (! __sk_dst_get(sk) || __sk_dst_check(sk, 0))
-               return;
-
        rcu_read_lock();
+
+       dst = __sk_dst_get(sk);
+       if (!dst || !dst->obsolete || dst->ops->check(dst, 0)) {
+               rcu_read_unlock();
+               return;
+       }
        inet_opt = rcu_dereference(inet->inet_opt);
        if (inet_opt && inet_opt->opt.srr)
                daddr = inet_opt->opt.faddr;
@@ -105,8 +113,10 @@ void ip4_datagram_release_cb(struct sock *sk)
                                   inet->inet_saddr, inet->inet_dport,
                                   inet->inet_sport, sk->sk_protocol,
                                   RT_CONN_FLAGS(sk), sk->sk_bound_dev_if);
-       if (!IS_ERR(rt))
-               __sk_dst_set(sk, &rt->dst);
+
+       dst = !IS_ERR(rt) ? &rt->dst : NULL;
+       sk_dst_set(sk, dst);
+
        rcu_read_unlock();
 }
 EXPORT_SYMBOL_GPL(ip4_datagram_release_cb);
index bdbf68bb2e2d194fcdf94553bd41a4ac9f184d7c..e9449376b58e4293b7735362912e1ea1191a8815 100644 (file)
@@ -106,7 +106,6 @@ static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
 #define IN4_ADDR_HSIZE         (1U << IN4_ADDR_HSIZE_SHIFT)
 
 static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
-static DEFINE_SPINLOCK(inet_addr_hash_lock);
 
 static u32 inet_addr_hash(struct net *net, __be32 addr)
 {
@@ -119,16 +118,14 @@ static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
 {
        u32 hash = inet_addr_hash(net, ifa->ifa_local);
 
-       spin_lock(&inet_addr_hash_lock);
+       ASSERT_RTNL();
        hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
-       spin_unlock(&inet_addr_hash_lock);
 }
 
 static void inet_hash_remove(struct in_ifaddr *ifa)
 {
-       spin_lock(&inet_addr_hash_lock);
+       ASSERT_RTNL();
        hlist_del_init_rcu(&ifa->hash);
-       spin_unlock(&inet_addr_hash_lock);
 }
 
 /**
@@ -830,7 +827,7 @@ static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
        ifa_existing = find_matching_ifa(ifa);
        if (!ifa_existing) {
                /* It would be best to check for !NLM_F_CREATE here but
-                * userspace alreay relies on not having to provide this.
+                * userspace already relies on not having to provide this.
                 */
                set_ifa_lifetime(ifa, valid_lft, prefered_lft);
                return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid);
index 250be7421ab36c50ce00a25dcd3c659ca1c97f18..4e9619bca732b869a70ac0db32c8b1285c9446fa 100644 (file)
@@ -84,7 +84,8 @@ void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
                        ptr--;
                }
                if (tpi->flags&TUNNEL_CSUM &&
-                   !(skb_shinfo(skb)->gso_type & SKB_GSO_GRE)) {
+                   !(skb_shinfo(skb)->gso_type &
+                     (SKB_GSO_GRE|SKB_GSO_GRE_CSUM))) {
                        *ptr = 0;
                        *(__sum16 *)ptr = csum_fold(skb_checksum(skb, 0,
                                                                 skb->len, 0));
@@ -93,28 +94,6 @@ void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
 }
 EXPORT_SYMBOL_GPL(gre_build_header);
 
-static __sum16 check_checksum(struct sk_buff *skb)
-{
-       __sum16 csum = 0;
-
-       switch (skb->ip_summed) {
-       case CHECKSUM_COMPLETE:
-               csum = csum_fold(skb->csum);
-
-               if (!csum)
-                       break;
-               /* Fall through. */
-
-       case CHECKSUM_NONE:
-               skb->csum = 0;
-               csum = __skb_checksum_complete(skb);
-               skb->ip_summed = CHECKSUM_COMPLETE;
-               break;
-       }
-
-       return csum;
-}
-
 static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
                            bool *csum_err)
 {
@@ -141,7 +120,7 @@ static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
 
        options = (__be32 *)(greh + 1);
        if (greh->flags & GRE_CSUM) {
-               if (check_checksum(skb)) {
+               if (skb_checksum_simple_validate(skb)) {
                        *csum_err = true;
                        return -EINVAL;
                }
index f1d32280cb54d596691ac7173e4f3151dc4b11b1..eb92deb12666fb56b6a289c55b8205fc27117933 100644 (file)
@@ -42,6 +42,7 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
                                  SKB_GSO_DODGY |
                                  SKB_GSO_TCP_ECN |
                                  SKB_GSO_GRE |
+                                 SKB_GSO_GRE_CSUM |
                                  SKB_GSO_IPIP)))
                goto out;
 
@@ -55,6 +56,8 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
                goto out;
 
        csum = !!(greh->flags & GRE_CSUM);
+       if (csum)
+               skb->encap_hdr_csum = 1;
 
        if (unlikely(!pskb_may_pull(skb, ghl)))
                goto out;
@@ -94,10 +97,13 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
                                }
                        }
 
-                       greh = (struct gre_base_hdr *)(skb->data);
+                       skb_reset_transport_header(skb);
+
+                       greh = (struct gre_base_hdr *)
+                           skb_transport_header(skb);
                        pcsum = (__be32 *)(greh + 1);
                        *pcsum = 0;
-                       *(__sum16 *)pcsum = csum_fold(skb_checksum(skb, 0, skb->len, 0));
+                       *(__sum16 *)pcsum = gso_make_checksum(skb, 0);
                }
                __skb_push(skb, tnl_hlen - ghl);
 
@@ -125,10 +131,12 @@ static __sum16 gro_skb_checksum(struct sk_buff *skb)
                csum_partial(skb->data, skb_gro_offset(skb), 0));
        sum = csum_fold(NAPI_GRO_CB(skb)->csum);
        if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE)) {
-               if (unlikely(!sum))
+               if (unlikely(!sum) && !skb->csum_complete_sw)
                        netdev_rx_csum_fault(skb->dev);
-       } else
+       } else {
                skb->ip_summed = CHECKSUM_COMPLETE;
+               skb->csum_complete_sw = 1;
+       }
 
        return sum;
 }
index 0134663fdbce86f6da39d8f6c9d27ce6c404687c..79c3d947a48128a8a58b58776e99f3a6602d868c 100644 (file)
@@ -337,6 +337,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
        struct sock *sk;
        struct inet_sock *inet;
        __be32 daddr, saddr;
+       u32 mark = IP4_REPLY_MARK(net, skb->mark);
 
        if (ip_options_echo(&icmp_param->replyopts.opt.opt, skb))
                return;
@@ -349,6 +350,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
        icmp_param->data.icmph.checksum = 0;
 
        inet->tos = ip_hdr(skb)->tos;
+       sk->sk_mark = mark;
        daddr = ipc.addr = ip_hdr(skb)->saddr;
        saddr = fib_compute_spec_dst(skb);
        ipc.opt = NULL;
@@ -364,6 +366,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
        memset(&fl4, 0, sizeof(fl4));
        fl4.daddr = daddr;
        fl4.saddr = saddr;
+       fl4.flowi4_mark = mark;
        fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos);
        fl4.flowi4_proto = IPPROTO_ICMP;
        security_skb_classify_flow(skb, flowi4_to_flowi(&fl4));
@@ -382,7 +385,7 @@ static struct rtable *icmp_route_lookup(struct net *net,
                                        struct flowi4 *fl4,
                                        struct sk_buff *skb_in,
                                        const struct iphdr *iph,
-                                       __be32 saddr, u8 tos,
+                                       __be32 saddr, u8 tos, u32 mark,
                                        int type, int code,
                                        struct icmp_bxm *param)
 {
@@ -394,6 +397,7 @@ static struct rtable *icmp_route_lookup(struct net *net,
        fl4->daddr = (param->replyopts.opt.opt.srr ?
                      param->replyopts.opt.opt.faddr : iph->saddr);
        fl4->saddr = saddr;
+       fl4->flowi4_mark = mark;
        fl4->flowi4_tos = RT_TOS(tos);
        fl4->flowi4_proto = IPPROTO_ICMP;
        fl4->fl4_icmp_type = type;
@@ -491,6 +495,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
        struct flowi4 fl4;
        __be32 saddr;
        u8  tos;
+       u32 mark;
        struct net *net;
        struct sock *sk;
 
@@ -592,6 +597,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
        tos = icmp_pointers[type].error ? ((iph->tos & IPTOS_TOS_MASK) |
                                           IPTOS_PREC_INTERNETCONTROL) :
                                          iph->tos;
+       mark = IP4_REPLY_MARK(net, skb_in->mark);
 
        if (ip_options_echo(&icmp_param->replyopts.opt.opt, skb_in))
                goto out_unlock;
@@ -608,13 +614,14 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
        icmp_param->skb   = skb_in;
        icmp_param->offset = skb_network_offset(skb_in);
        inet_sk(sk)->tos = tos;
+       sk->sk_mark = mark;
        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,
+       rt = icmp_route_lookup(net, &fl4, skb_in, iph, saddr, tos, mark,
                               type, code, icmp_param);
        if (IS_ERR(rt))
                goto out_unlock;
@@ -908,16 +915,8 @@ int icmp_rcv(struct sk_buff *skb)
 
        ICMP_INC_STATS_BH(net, ICMP_MIB_INMSGS);
 
-       switch (skb->ip_summed) {
-       case CHECKSUM_COMPLETE:
-               if (!csum_fold(skb->csum))
-                       break;
-               /* fall through */
-       case CHECKSUM_NONE:
-               skb->csum = 0;
-               if (__skb_checksum_complete(skb))
-                       goto csum_error;
-       }
+       if (skb_checksum_simple_validate(skb))
+               goto csum_error;
 
        if (!pskb_pull(skb, sizeof(*icmph)))
                goto error;
index 97e4d1655d26bb65121c1a8954af2d7565c288a6..6748d420f714f4add6acd23a4aefd1f35c0c067b 100644 (file)
@@ -369,7 +369,7 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size)
        pip->saddr    = fl4.saddr;
        pip->protocol = IPPROTO_IGMP;
        pip->tot_len  = 0;      /* filled in later */
-       ip_select_ident(skb, &rt->dst, NULL);
+       ip_select_ident(skb, NULL);
        ((u8 *)&pip[1])[0] = IPOPT_RA;
        ((u8 *)&pip[1])[1] = 4;
        ((u8 *)&pip[1])[2] = 0;
@@ -714,7 +714,7 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc,
        iph->daddr    = dst;
        iph->saddr    = fl4.saddr;
        iph->protocol = IPPROTO_IGMP;
-       ip_select_ident(skb, &rt->dst, NULL);
+       ip_select_ident(skb, NULL);
        ((u8 *)&iph[1])[0] = IPOPT_RA;
        ((u8 *)&iph[1])[1] = 4;
        ((u8 *)&iph[1])[2] = 0;
@@ -988,16 +988,8 @@ int igmp_rcv(struct sk_buff *skb)
        if (!pskb_may_pull(skb, sizeof(struct igmphdr)))
                goto drop;
 
-       switch (skb->ip_summed) {
-       case CHECKSUM_COMPLETE:
-               if (!csum_fold(skb->csum))
-                       break;
-               /* fall through */
-       case CHECKSUM_NONE:
-               skb->csum = 0;
-               if (__skb_checksum_complete(skb))
-                       goto drop;
-       }
+       if (skb_checksum_simple_validate(skb))
+               goto drop;
 
        ih = igmp_hdr(skb);
        switch (ih->type) {
index a56b8e6e866a8c4327f86111adac947e9ffc2445..14d02ea905b6bea37240f88054f0cd42db73c4c2 100644 (file)
@@ -29,9 +29,6 @@ const char inet_csk_timer_bug_msg[] = "inet_csk BUG: unknown timer value\n";
 EXPORT_SYMBOL(inet_csk_timer_bug_msg);
 #endif
 
-unsigned long *sysctl_local_reserved_ports;
-EXPORT_SYMBOL(sysctl_local_reserved_ports);
-
 void inet_get_local_port_range(struct net *net, int *low, int *high)
 {
        unsigned int seq;
@@ -113,7 +110,7 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum)
 
                smallest_size = -1;
                do {
-                       if (inet_is_reserved_local_port(rover))
+                       if (inet_is_local_reserved_port(net, rover))
                                goto next_nolock;
                        head = &hashinfo->bhash[inet_bhashfn(net, rover,
                                        hashinfo->bhash_size)];
@@ -408,7 +405,7 @@ struct dst_entry *inet_csk_route_req(struct sock *sk,
        struct net *net = sock_net(sk);
        int flags = inet_sk_flowi_flags(sk);
 
-       flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark,
+       flowi4_init_output(fl4, sk->sk_bound_dev_if, ireq->ir_mark,
                           RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE,
                           sk->sk_protocol,
                           flags,
@@ -445,7 +442,7 @@ struct dst_entry *inet_csk_route_child_sock(struct sock *sk,
 
        rcu_read_lock();
        opt = rcu_dereference(newinet->inet_opt);
-       flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark,
+       flowi4_init_output(fl4, sk->sk_bound_dev_if, inet_rsk(req)->ir_mark,
                           RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE,
                           sk->sk_protocol, inet_sk_flowi_flags(sk),
                           (opt && opt->opt.srr) ? opt->opt.faddr : ireq->ir_rmt_addr,
@@ -680,6 +677,8 @@ struct sock *inet_csk_clone_lock(const struct sock *sk,
                inet_sk(newsk)->inet_sport = htons(inet_rsk(req)->ir_num);
                newsk->sk_write_space = sk_stream_write_space;
 
+               newsk->sk_mark = inet_rsk(req)->ir_mark;
+
                newicsk->icsk_retransmits = 0;
                newicsk->icsk_backoff     = 0;
                newicsk->icsk_probes_out  = 0;
index 8b9cf279450d6cf0c24e64a20fb0d05b9fb89a82..43116e8c8e1323cdd8d902c5b88e42187c8df991 100644 (file)
@@ -274,7 +274,7 @@ struct sock *__inet_lookup_established(struct net *net,
                                  const __be32 daddr, const u16 hnum,
                                  const int dif)
 {
-       INET_ADDR_COOKIE(acookie, saddr, daddr)
+       INET_ADDR_COOKIE(acookie, saddr, daddr);
        const __portpair ports = INET_COMBINED_PORTS(sport, hnum);
        struct sock *sk;
        const struct hlist_nulls_node *node;
@@ -327,7 +327,7 @@ static int __inet_check_established(struct inet_timewait_death_row *death_row,
        __be32 daddr = inet->inet_rcv_saddr;
        __be32 saddr = inet->inet_daddr;
        int dif = sk->sk_bound_dev_if;
-       INET_ADDR_COOKIE(acookie, saddr, daddr)
+       INET_ADDR_COOKIE(acookie, saddr, daddr);
        const __portpair ports = INET_COMBINED_PORTS(inet->inet_dport, lport);
        struct net *net = sock_net(sk);
        unsigned int hash = inet_ehashfn(net, daddr, lport,
@@ -500,7 +500,7 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row,
                local_bh_disable();
                for (i = 1; i <= remaining; i++) {
                        port = low + (i + offset) % remaining;
-                       if (inet_is_reserved_local_port(port))
+                       if (inet_is_local_reserved_port(net, port))
                                continue;
                        head = &hinfo->bhash[inet_bhashfn(net, port,
                                        hinfo->bhash_size)];
index 56cd458a1b8c461f060b7018ed6843124d75a0ca..bd5f5928167dbd44c72fbd1a5eefd475bd29f260 100644 (file)
  *  Theory of operations.
  *  We keep one entry for each peer IP address.  The nodes contains long-living
  *  information about the peer which doesn't depend on routes.
- *  At this moment this information consists only of ID field for the next
- *  outgoing IP packet.  This field is incremented with each packet as encoded
- *  in inet_getid() function (include/net/inetpeer.h).
- *  At the moment of writing this notes identifier of IP packets is generated
- *  to be unpredictable using this code only for packets subjected
- *  (actually or potentially) to defragmentation.  I.e. DF packets less than
- *  PMTU in size when local fragmentation is disabled use a constant ID and do
- *  not use this code (see ip_select_ident() in include/net/ip.h).
  *
- *  Route cache entries hold references to our nodes.
- *  New cache entries get references via lookup by destination IP address in
- *  the avl tree.  The reference is grabbed only when it's needed i.e. only
- *  when we try to output IP packet which needs an unpredictable ID (see
- *  __ip_select_ident() in net/ipv4/route.c).
  *  Nodes are removed only when reference counter goes to 0.
  *  When it's happened the node may be removed when a sufficient amount of
  *  time has been passed since its last use.  The less-recently-used entry can
@@ -62,7 +49,6 @@
  *             refcnt: atomically against modifications on other CPU;
  *                usually under some other lock to prevent node disappearing
  *             daddr: unchangeable
- *             ip_id_count: atomic value (no lock needed)
  */
 
 static struct kmem_cache *peer_cachep __read_mostly;
@@ -120,7 +106,7 @@ int inet_peer_maxttl __read_mostly = 10 * 60 * HZ;  /* usual time to live: 10 min
 static void inetpeer_gc_worker(struct work_struct *work)
 {
        struct inet_peer *p, *n, *c;
-       LIST_HEAD(list);
+       struct list_head list;
 
        spin_lock_bh(&gc_lock);
        list_replace_init(&gc_list, &list);
@@ -497,10 +483,6 @@ struct inet_peer *inet_getpeer(struct inet_peer_base *base,
                p->daddr = *daddr;
                atomic_set(&p->refcnt, 1);
                atomic_set(&p->rid, 0);
-               atomic_set(&p->ip_id_count,
-                               (daddr->family == AF_INET) ?
-                                       secure_ip_id(daddr->addr.a4) :
-                                       secure_ipv6_id(daddr->addr.a6));
                p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW;
                p->rate_tokens = 0;
                /* 60*HZ is arbitrary, but chosen enough high so that the first
index 6f111e48e11c15a19ffd60d345b1399fd3c66953..3a83ce5efa80e3fc2c062ec08465840018159b14 100644 (file)
@@ -42,7 +42,7 @@
 static bool ip_may_fragment(const struct sk_buff *skb)
 {
        return unlikely((ip_hdr(skb)->frag_off & htons(IP_DF)) == 0) ||
-               skb->local_df;
+               skb->ignore_df;
 }
 
 static bool ip_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu)
index 94213c89156511d86682b2c3e034f9e5b6ef5ccb..9b842544aea3d8155a28a4b9f8030901bb712a9c 100644 (file)
@@ -410,7 +410,7 @@ static int ipgre_open(struct net_device *dev)
                struct flowi4 fl4;
                struct rtable *rt;
 
-               rt = ip_route_output_gre(dev_net(dev), &fl4,
+               rt = ip_route_output_gre(t->net, &fl4,
                                         t->parms.iph.daddr,
                                         t->parms.iph.saddr,
                                         t->parms.o_key,
@@ -434,7 +434,7 @@ static int ipgre_close(struct net_device *dev)
 
        if (ipv4_is_multicast(t->parms.iph.daddr) && t->mlink) {
                struct in_device *in_dev;
-               in_dev = inetdev_by_index(dev_net(dev), t->mlink);
+               in_dev = inetdev_by_index(t->net, t->mlink);
                if (in_dev)
                        ip_mc_dec_group(in_dev, t->parms.iph.daddr);
        }
@@ -478,7 +478,7 @@ static void __gre_tunnel_init(struct net_device *dev)
        dev->needed_headroom    = LL_MAX_HEADER + sizeof(struct iphdr) + 4;
        dev->mtu                = ETH_DATA_LEN - sizeof(struct iphdr) - 4;
 
-       dev->features           |= NETIF_F_NETNS_LOCAL | GRE_FEATURES;
+       dev->features           |= GRE_FEATURES;
        dev->hw_features        |= GRE_FEATURES;
 
        if (!(tunnel->parms.o_flags & TUNNEL_SEQ)) {
@@ -649,6 +649,7 @@ static void ipgre_tap_setup(struct net_device *dev)
 {
        ether_setup(dev);
        dev->netdev_ops         = &gre_tap_netdev_ops;
+       dev->priv_flags         |= IFF_LIVE_ADDR_CHANGE;
        ip_tunnel_setup(dev, gre_tap_net_id);
 }
 
index f4ab72e19af923536656e734996f4b9201684b58..5e7aecea05cd2afbd3e3e13f417e26687517b468 100644 (file)
@@ -364,7 +364,7 @@ int ip_options_compile(struct net *net,
                        }
                        if (optptr[2] <= optlen) {
                                unsigned char *timeptr = NULL;
-                               if (optptr[2]+3 > optptr[1]) {
+                               if (optptr[2]+3 > optlen) {
                                        pp_ptr = optptr + 2;
                                        goto error;
                                }
@@ -376,7 +376,7 @@ int ip_options_compile(struct net *net,
                                        optptr[2] += 4;
                                        break;
                                case IPOPT_TS_TSANDADDR:
-                                       if (optptr[2]+7 > optptr[1]) {
+                                       if (optptr[2]+7 > optlen) {
                                                pp_ptr = optptr + 2;
                                                goto error;
                                        }
@@ -390,7 +390,7 @@ int ip_options_compile(struct net *net,
                                        optptr[2] += 8;
                                        break;
                                case IPOPT_TS_PRESPEC:
-                                       if (optptr[2]+7 > optptr[1]) {
+                                       if (optptr[2]+7 > optlen) {
                                                pp_ptr = optptr + 2;
                                                goto error;
                                        }
index a52f50187b5495a1157c2ef8e0785da730414022..8d3b6b0e98574b5507f05972cf7fbac8ba32c465 100644 (file)
@@ -148,7 +148,7 @@ int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
        iph->daddr    = (opt && opt->opt.srr ? opt->opt.faddr : daddr);
        iph->saddr    = saddr;
        iph->protocol = sk->sk_protocol;
-       ip_select_ident(skb, &rt->dst, sk);
+       ip_select_ident(skb, sk);
 
        if (opt && opt->opt.optlen) {
                iph->ihl += opt->opt.optlen>>2;
@@ -415,7 +415,7 @@ int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl)
        skb_reset_network_header(skb);
        iph = ip_hdr(skb);
        *((__be16 *)iph) = htons((4 << 12) | (5 << 8) | (inet->tos & 0xff));
-       if (ip_dont_fragment(sk, &rt->dst) && !skb->local_df)
+       if (ip_dont_fragment(sk, &rt->dst) && !skb->ignore_df)
                iph->frag_off = htons(IP_DF);
        else
                iph->frag_off = 0;
@@ -430,8 +430,7 @@ int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl)
                ip_options_build(skb, &inet_opt->opt, inet->inet_daddr, rt, 0);
        }
 
-       ip_select_ident_more(skb, &rt->dst, sk,
-                            (skb_shinfo(skb)->gso_segs ?: 1) - 1);
+       ip_select_ident_segs(skb, sk, skb_shinfo(skb)->gso_segs ?: 1);
 
        /* TODO : should we use skb->sk here instead of sk ? */
        skb->priority = sk->sk_priority;
@@ -501,7 +500,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
        iph = ip_hdr(skb);
 
        mtu = ip_skb_dst_mtu(skb);
-       if (unlikely(((iph->frag_off & htons(IP_DF)) && !skb->local_df) ||
+       if (unlikely(((iph->frag_off & htons(IP_DF)) && !skb->ignore_df) ||
                     (IPCB(skb)->frag_max_size &&
                      IPCB(skb)->frag_max_size > mtu))) {
                IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
@@ -866,7 +865,7 @@ static int __ip_append_data(struct sock *sk,
 
        fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0);
        maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen;
-       maxnonfragsize = ip_sk_local_df(sk) ? 0xFFFF : mtu;
+       maxnonfragsize = ip_sk_ignore_df(sk) ? 0xFFFF : mtu;
 
        if (cork->length + length > maxnonfragsize - fragheaderlen) {
                ip_local_error(sk, EMSGSIZE, fl4->daddr, inet->inet_dport,
@@ -1189,7 +1188,7 @@ 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 = ip_sk_local_df(sk) ? 0xFFFF : mtu;
+       maxnonfragsize = ip_sk_ignore_df(sk) ? 0xFFFF : mtu;
 
        if (cork->length + size > maxnonfragsize - fragheaderlen) {
                ip_local_error(sk, EMSGSIZE, fl4->daddr, inet->inet_dport,
@@ -1350,10 +1349,10 @@ struct sk_buff *__ip_make_skb(struct sock *sk,
         * to fragment the frame generated here. No matter, what transforms
         * how transforms change size of the packet, it will come out.
         */
-       skb->local_df = ip_sk_local_df(sk);
+       skb->ignore_df = ip_sk_ignore_df(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
+        * If ignore_df is set too, we still allow to fragment this frame
         * locally. */
        if (inet->pmtudisc == IP_PMTUDISC_DO ||
            inet->pmtudisc == IP_PMTUDISC_PROBE ||
@@ -1379,7 +1378,7 @@ struct sk_buff *__ip_make_skb(struct sock *sk,
        iph->ttl = ttl;
        iph->protocol = sk->sk_protocol;
        ip_copy_addrs(iph, fl4);
-       ip_select_ident(skb, &rt->dst, sk);
+       ip_select_ident(skb, sk);
 
        if (opt) {
                iph->ihl += opt->optlen>>2;
@@ -1546,7 +1545,8 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
                        daddr = replyopts.opt.opt.faddr;
        }
 
-       flowi4_init_output(&fl4, arg->bound_dev_if, 0,
+       flowi4_init_output(&fl4, arg->bound_dev_if,
+                          IP4_REPLY_MARK(net, skb->mark),
                           RT_TOS(arg->tos),
                           RT_SCOPE_UNIVERSE, ip_hdr(skb)->protocol,
                           ip_reply_arg_flowi_flags(arg),
index 2acc2337d38bfe7f35517e13952cfa8a306c24fd..097b3e7c1e8f89052f6dd686d519a3a9f0624209 100644 (file)
@@ -268,6 +268,7 @@ static struct ip_tunnel *ip_tunnel_find(struct ip_tunnel_net *itn,
        __be32 remote = parms->iph.daddr;
        __be32 local = parms->iph.saddr;
        __be32 key = parms->i_key;
+       __be16 flags = parms->i_flags;
        int link = parms->link;
        struct ip_tunnel *t = NULL;
        struct hlist_head *head = ip_bucket(itn, parms);
@@ -275,9 +276,9 @@ static struct ip_tunnel *ip_tunnel_find(struct ip_tunnel_net *itn,
        hlist_for_each_entry_rcu(t, head, hash_node) {
                if (local == t->parms.iph.saddr &&
                    remote == t->parms.iph.daddr &&
-                   key == t->parms.i_key &&
                    link == t->parms.link &&
-                   type == t->dev->type)
+                   type == t->dev->type &&
+                   ip_tunnel_key_match(&t->parms, flags, key))
                        break;
        }
        return t;
@@ -395,11 +396,10 @@ static struct ip_tunnel *ip_tunnel_create(struct net *net,
                                          struct ip_tunnel_net *itn,
                                          struct ip_tunnel_parm *parms)
 {
-       struct ip_tunnel *nt, *fbt;
+       struct ip_tunnel *nt;
        struct net_device *dev;
 
        BUG_ON(!itn->fb_tunnel_dev);
-       fbt = netdev_priv(itn->fb_tunnel_dev);
        dev = __ip_tunnel_create(net, itn->fb_tunnel_dev->rtnl_link_ops, parms);
        if (IS_ERR(dev))
                return ERR_CAST(dev);
@@ -668,6 +668,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
                dev->needed_headroom = max_headroom;
 
        if (skb_cow_head(skb, dev->needed_headroom)) {
+               ip_rt_put(rt);
                dev->stats.tx_dropped++;
                kfree_skb(skb);
                return;
@@ -747,19 +748,19 @@ int ip_tunnel_ioctl(struct net_device *dev, struct ip_tunnel_parm *p, int cmd)
                        goto done;
                if (p->iph.ttl)
                        p->iph.frag_off |= htons(IP_DF);
-               if (!(p->i_flags&TUNNEL_KEY))
-                       p->i_key = 0;
-               if (!(p->o_flags&TUNNEL_KEY))
-                       p->o_key = 0;
+               if (!(p->i_flags & VTI_ISVTI)) {
+                       if (!(p->i_flags & TUNNEL_KEY))
+                               p->i_key = 0;
+                       if (!(p->o_flags & TUNNEL_KEY))
+                               p->o_key = 0;
+               }
 
                t = ip_tunnel_find(itn, p, itn->fb_tunnel_dev->type);
 
                if (!t && (cmd == SIOCADDTUNNEL)) {
                        t = ip_tunnel_create(net, itn, p);
-                       if (IS_ERR(t)) {
-                               err = PTR_ERR(t);
-                               break;
-                       }
+                       err = PTR_ERR_OR_ZERO(t);
+                       break;
                }
                if (dev != itn->fb_tunnel_dev && cmd == SIOCCHGTUNNEL) {
                        if (t != NULL) {
index bcf206c79005de251e3c71e387d17e0f928e4aef..f4c987bb7e94a45fd72c245a323fb4cedade4078 100644 (file)
@@ -74,7 +74,7 @@ int iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,
        iph->daddr      =       dst;
        iph->saddr      =       src;
        iph->ttl        =       ttl;
-       __ip_select_ident(iph, &rt->dst, (skb_shinfo(skb)->gso_segs ?: 1) - 1);
+       __ip_select_ident(iph, skb_shinfo(skb)->gso_segs ?: 1);
 
        err = ip_local_out_sk(sk, skb);
        if (unlikely(net_xmit_eval(err)))
@@ -135,6 +135,14 @@ struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb,
                return skb;
        }
 
+       /* If packet is not gso and we are resolving any partial checksum,
+        * clear encapsulation flag. This allows setting CHECKSUM_PARTIAL
+        * on the outer header without confusing devices that implement
+        * NETIF_F_IP_CSUM with encapsulation.
+        */
+       if (csum_help)
+               skb->encapsulation = 0;
+
        if (skb->ip_summed == CHECKSUM_PARTIAL && csum_help) {
                err = skb_checksum_help(skb);
                if (unlikely(err))
index 13ef00f1e17b88943ddee9ae9ba52b7d0efe7832..b8960f3527f366525088ca930647c9f0c803ad74 100644 (file)
@@ -313,7 +313,13 @@ vti_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
                        return -EINVAL;
        }
 
-       p.i_flags |= VTI_ISVTI;
+       if (!(p.i_flags & GRE_KEY))
+               p.i_key = 0;
+       if (!(p.o_flags & GRE_KEY))
+               p.o_key = 0;
+
+       p.i_flags = VTI_ISVTI;
+
        err = ip_tunnel_ioctl(dev, &p, cmd);
        if (err)
                return err;
index 812b1835146255fe065264dd236e901fe6937578..62eaa005e14610237f9e8a6016c366ba6d6b09cf 100644 (file)
@@ -149,13 +149,13 @@ static int ipip_err(struct sk_buff *skb, u32 info)
 
        if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
                ipv4_update_pmtu(skb, dev_net(skb->dev), info,
-                                t->dev->ifindex, 0, IPPROTO_IPIP, 0);
+                                t->parms.link, 0, IPPROTO_IPIP, 0);
                err = 0;
                goto out;
        }
 
        if (type == ICMP_REDIRECT) {
-               ipv4_redirect(skb, dev_net(skb->dev), t->dev->ifindex, 0,
+               ipv4_redirect(skb, dev_net(skb->dev), t->parms.link, 0,
                              IPPROTO_IPIP, 0);
                err = 0;
                goto out;
@@ -486,4 +486,5 @@ static void __exit ipip_fini(void)
 module_init(ipip_init);
 module_exit(ipip_fini);
 MODULE_LICENSE("GPL");
+MODULE_ALIAS_RTNL_LINK("ipip");
 MODULE_ALIAS_NETDEV("tunl0");
index d84dc8d4c916e7f50260a2b29df5d79f4adb4c1e..65bcaa7890436df5ad2ee16f4465af51e141357b 100644 (file)
@@ -484,7 +484,7 @@ static void reg_vif_setup(struct net_device *dev)
        dev->type               = ARPHRD_PIMREG;
        dev->mtu                = ETH_DATA_LEN - sizeof(struct iphdr) - 8;
        dev->flags              = IFF_NOARP;
-       dev->netdev_ops         = &reg_vif_netdev_ops,
+       dev->netdev_ops         = &reg_vif_netdev_ops;
        dev->destructor         = free_netdev;
        dev->features           |= NETIF_F_NETNS_LOCAL;
 }
@@ -1663,7 +1663,7 @@ static void ip_encap(struct sk_buff *skb, __be32 saddr, __be32 daddr)
        iph->protocol   =       IPPROTO_IPIP;
        iph->ihl        =       5;
        iph->tot_len    =       htons(skb->len);
-       ip_select_ident(skb, skb_dst(skb), NULL);
+       ip_select_ident(skb, NULL);
        ip_send_check(iph);
 
        memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
index ee2886126e3dfad44e7c801d82ad2ec4bad621d3..f1787c04a4ddfe06f70cd09c4b4c2573c434042d 100644 (file)
@@ -91,17 +91,9 @@ nf_nat_ipv4_fn(const struct nf_hook_ops *ops,
        if (nf_ct_is_untracked(ct))
                return NF_ACCEPT;
 
-       nat = nfct_nat(ct);
-       if (!nat) {
-               /* NAT module was loaded late. */
-               if (nf_ct_is_confirmed(ct))
-                       return NF_ACCEPT;
-               nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
-               if (nat == NULL) {
-                       pr_debug("failed to add NAT extension\n");
-                       return NF_ACCEPT;
-               }
-       }
+       nat = nf_ct_nat_ext_add(ct);
+       if (nat == NULL)
+               return NF_ACCEPT;
 
        switch (ctinfo) {
        case IP_CT_RELATED:
index f40f321b41fc2e30b21019333efdc5757404fe0a..b8f6381c7d0b15f49973a3748937ea23325bee03 100644 (file)
@@ -34,7 +34,7 @@ static int nf_ct_ipv4_gather_frags(struct sk_buff *skb, u_int32_t user)
 
        if (!err) {
                ip_send_check(ip_hdr(skb));
-               skb->local_df = 1;
+               skb->ignore_df = 1;
        }
 
        return err;
index b5b256d45e67b7f2dd288017b9f728edf72d32c3..3964157d826c197e27ce0578c2da409bbc6e4dec 100644 (file)
@@ -48,15 +48,9 @@ static unsigned int nf_nat_fn(const struct nf_hook_ops *ops,
 
        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;
-       }
+       nat = nf_ct_nat_ext_add(ct);
+       if (nat == NULL)
+               return NF_ACCEPT;
 
        switch (ctinfo) {
        case IP_CT_RELATED:
index ad737fad6d8b82dec74fab1260015e539647271d..ae0af9386f7ccf4cabafbcc8aff6c37309e820ea 100644 (file)
@@ -345,15 +345,15 @@ static void icmp_put(struct seq_file *seq)
        for (i = 0; icmpmibmap[i].name != NULL; i++)
                seq_printf(seq, " Out%s", icmpmibmap[i].name);
        seq_printf(seq, "\nIcmp: %lu %lu %lu",
-               snmp_fold_field((void __percpu **) net->mib.icmp_statistics, ICMP_MIB_INMSGS),
-               snmp_fold_field((void __percpu **) net->mib.icmp_statistics, ICMP_MIB_INERRORS),
-               snmp_fold_field((void __percpu **) net->mib.icmp_statistics, ICMP_MIB_CSUMERRORS));
+               snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_INMSGS),
+               snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_INERRORS),
+               snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_CSUMERRORS));
        for (i = 0; icmpmibmap[i].name != NULL; i++)
                seq_printf(seq, " %lu",
                           atomic_long_read(ptr + icmpmibmap[i].index));
        seq_printf(seq, " %lu %lu",
-               snmp_fold_field((void __percpu **) net->mib.icmp_statistics, ICMP_MIB_OUTMSGS),
-               snmp_fold_field((void __percpu **) net->mib.icmp_statistics, ICMP_MIB_OUTERRORS));
+               snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_OUTMSGS),
+               snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_OUTERRORS));
        for (i = 0; icmpmibmap[i].name != NULL; i++)
                seq_printf(seq, " %lu",
                           atomic_long_read(ptr + (icmpmibmap[i].index | 0x100)));
@@ -379,7 +379,7 @@ static int snmp_seq_show(struct seq_file *seq, void *v)
        BUILD_BUG_ON(offsetof(struct ipstats_mib, mibs) != 0);
        for (i = 0; snmp4_ipstats_list[i].name != NULL; i++)
                seq_printf(seq, " %llu",
-                          snmp_fold_field64((void __percpu **)net->mib.ip_statistics,
+                          snmp_fold_field64(net->mib.ip_statistics,
                                             snmp4_ipstats_list[i].entry,
                                             offsetof(struct ipstats_mib, syncp)));
 
@@ -395,11 +395,11 @@ static int snmp_seq_show(struct seq_file *seq, void *v)
                /* MaxConn field is signed, RFC 2012 */
                if (snmp4_tcp_list[i].entry == TCP_MIB_MAXCONN)
                        seq_printf(seq, " %ld",
-                                  snmp_fold_field((void __percpu **)net->mib.tcp_statistics,
+                                  snmp_fold_field(net->mib.tcp_statistics,
                                                   snmp4_tcp_list[i].entry));
                else
                        seq_printf(seq, " %lu",
-                                  snmp_fold_field((void __percpu **)net->mib.tcp_statistics,
+                                  snmp_fold_field(net->mib.tcp_statistics,
                                                   snmp4_tcp_list[i].entry));
        }
 
@@ -410,7 +410,7 @@ static int snmp_seq_show(struct seq_file *seq, void *v)
        seq_puts(seq, "\nUdp:");
        for (i = 0; snmp4_udp_list[i].name != NULL; i++)
                seq_printf(seq, " %lu",
-                          snmp_fold_field((void __percpu **)net->mib.udp_statistics,
+                          snmp_fold_field(net->mib.udp_statistics,
                                           snmp4_udp_list[i].entry));
 
        /* the UDP and UDP-Lite MIBs are the same */
@@ -421,7 +421,7 @@ static int snmp_seq_show(struct seq_file *seq, void *v)
        seq_puts(seq, "\nUdpLite:");
        for (i = 0; snmp4_udp_list[i].name != NULL; i++)
                seq_printf(seq, " %lu",
-                          snmp_fold_field((void __percpu **)net->mib.udplite_statistics,
+                          snmp_fold_field(net->mib.udplite_statistics,
                                           snmp4_udp_list[i].entry));
 
        seq_putc(seq, '\n');
@@ -458,7 +458,7 @@ static int netstat_seq_show(struct seq_file *seq, void *v)
        seq_puts(seq, "\nTcpExt:");
        for (i = 0; snmp4_net_list[i].name != NULL; i++)
                seq_printf(seq, " %lu",
-                          snmp_fold_field((void __percpu **)net->mib.net_statistics,
+                          snmp_fold_field(net->mib.net_statistics,
                                           snmp4_net_list[i].entry));
 
        seq_puts(seq, "\nIpExt:");
@@ -468,7 +468,7 @@ static int netstat_seq_show(struct seq_file *seq, void *v)
        seq_puts(seq, "\nIpExt:");
        for (i = 0; snmp4_ipextstats_list[i].name != NULL; i++)
                seq_printf(seq, " %llu",
-                          snmp_fold_field64((void __percpu **)net->mib.ip_statistics,
+                          snmp_fold_field64(net->mib.ip_statistics,
                                             snmp4_ipextstats_list[i].entry,
                                             offsetof(struct ipstats_mib, syncp)));
 
index a9dbe58bdfe767e62642db954b89532dab928a96..2c65160565e1a084b5ff13832cfdb80d50ba7d6c 100644 (file)
@@ -389,7 +389,7 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
                iph->check   = 0;
                iph->tot_len = htons(length);
                if (!iph->id)
-                       ip_select_ident(skb, &rt->dst, NULL);
+                       ip_select_ident(skb, NULL);
 
                iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
        }
index 5e676be3daeb19e5f48df5e767a66b83ac73b432..082239ffe34a1f42e62e9ac7901f9fe5219fdca6 100644 (file)
@@ -89,6 +89,7 @@
 #include <linux/rcupdate.h>
 #include <linux/times.h>
 #include <linux/slab.h>
+#include <linux/jhash.h>
 #include <net/dst.h>
 #include <net/net_namespace.h>
 #include <net/protocol.h>
@@ -456,39 +457,19 @@ static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst,
        return neigh_create(&arp_tbl, pkey, dev);
 }
 
-/*
- * Peer allocation may fail only in serious out-of-memory conditions.  However
- * we still can generate some output.
- * Random ID selection looks a bit dangerous because we have no chances to
- * select ID being unique in a reasonable period of time.
- * But broken packet identifier may be better than no packet at all.
- */
-static void ip_select_fb_ident(struct iphdr *iph)
-{
-       static DEFINE_SPINLOCK(ip_fb_id_lock);
-       static u32 ip_fallback_id;
-       u32 salt;
-
-       spin_lock_bh(&ip_fb_id_lock);
-       salt = secure_ip_id((__force __be32)ip_fallback_id ^ iph->daddr);
-       iph->id = htons(salt & 0xFFFF);
-       ip_fallback_id = salt;
-       spin_unlock_bh(&ip_fb_id_lock);
-}
+atomic_t *ip_idents __read_mostly;
+EXPORT_SYMBOL(ip_idents);
 
-void __ip_select_ident(struct iphdr *iph, struct dst_entry *dst, int more)
+void __ip_select_ident(struct iphdr *iph, int segs)
 {
-       struct net *net = dev_net(dst->dev);
-       struct inet_peer *peer;
+       static u32 ip_idents_hashrnd __read_mostly;
+       u32 hash, id;
 
-       peer = inet_getpeer_v4(net->ipv4.peers, iph->daddr, 1);
-       if (peer) {
-               iph->id = htons(inet_getid(peer, more));
-               inet_putpeer(peer);
-               return;
-       }
+       net_get_random_once(&ip_idents_hashrnd, sizeof(ip_idents_hashrnd));
 
-       ip_select_fb_ident(iph);
+       hash = jhash_1word((__force u32)iph->daddr, ip_idents_hashrnd);
+       id = ip_idents_reserve(hash, segs);
+       iph->id = htons(id);
 }
 EXPORT_SYMBOL(__ip_select_ident);
 
@@ -993,6 +974,9 @@ void ipv4_update_pmtu(struct sk_buff *skb, struct net *net, u32 mtu,
        struct flowi4 fl4;
        struct rtable *rt;
 
+       if (!mark)
+               mark = IP4_REPLY_MARK(net, skb->mark);
+
        __build_flow_key(&fl4, NULL, iph, oif,
                         RT_TOS(iph->tos), protocol, mark, flow_flags);
        rt = __ip_route_output_key(net, &fl4);
@@ -1010,6 +994,10 @@ static void __ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu)
        struct rtable *rt;
 
        __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0);
+
+       if (!fl4.flowi4_mark)
+               fl4.flowi4_mark = IP4_REPLY_MARK(sock_net(sk), skb->mark);
+
        rt = __ip_route_output_key(sock_net(sk), &fl4);
        if (!IS_ERR(rt)) {
                __ip_rt_update_pmtu(rt, &fl4, mtu);
@@ -2704,6 +2692,12 @@ int __init ip_rt_init(void)
 {
        int rc = 0;
 
+       ip_idents = kmalloc(IP_IDENTS_SZ * sizeof(*ip_idents), GFP_KERNEL);
+       if (!ip_idents)
+               panic("IP: failed to allocate ip_idents\n");
+
+       prandom_bytes(ip_idents, IP_IDENTS_SZ * sizeof(*ip_idents));
+
 #ifdef CONFIG_IP_ROUTE_CLASSID
        ip_rt_acct = __alloc_percpu(256 * sizeof(struct ip_rt_acct), __alignof__(struct ip_rt_acct));
        if (!ip_rt_acct)
index f2ed13c2125f7d34820c9e92a3080678f30f46fd..c86624b36a62ece1dd34bf39561d52e34f467bd3 100644 (file)
@@ -303,6 +303,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
        ireq->ir_rmt_port       = th->source;
        ireq->ir_loc_addr       = ip_hdr(skb)->daddr;
        ireq->ir_rmt_addr       = ip_hdr(skb)->saddr;
+       ireq->ir_mark           = inet_request_mark(sk, skb);
        ireq->ecn_ok            = ecn_ok;
        ireq->snd_wscale        = tcp_opt.snd_wscale;
        ireq->sack_ok           = tcp_opt.sack_ok;
@@ -339,7 +340,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
         * hasn't changed since we received the original syn, but I see
         * no easy way to do this.
         */
-       flowi4_init_output(&fl4, sk->sk_bound_dev_if, sk->sk_mark,
+       flowi4_init_output(&fl4, sk->sk_bound_dev_if, ireq->ir_mark,
                           RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, IPPROTO_TCP,
                           inet_sk_flowi_flags(sk),
                           (opt && opt->srr) ? opt->faddr : ireq->ir_rmt_addr,
index 5cde8f263d40c0eb0204d310fee99146dabb7a87..79a007c5255883f9d96011682c67ea8aac15a835 100644 (file)
@@ -436,13 +436,6 @@ static struct ctl_table ipv4_table[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec
        },
-       {
-               .procname       = "ip_local_reserved_ports",
-               .data           = NULL, /* initialized in sysctl_ipv4_init */
-               .maxlen         = 65536,
-               .mode           = 0644,
-               .proc_handler   = proc_do_large_bitmap,
-       },
        {
                .procname       = "igmp_max_memberships",
                .data           = &sysctl_igmp_max_memberships,
@@ -824,6 +817,13 @@ static struct ctl_table ipv4_net_table[] = {
                .mode           = 0644,
                .proc_handler   = ipv4_local_port_range,
        },
+       {
+               .procname       = "ip_local_reserved_ports",
+               .data           = &init_net.ipv4.sysctl_local_reserved_ports,
+               .maxlen         = 65536,
+               .mode           = 0644,
+               .proc_handler   = proc_do_large_bitmap,
+       },
        {
                .procname       = "ip_no_pmtu_disc",
                .data           = &init_net.ipv4.sysctl_ip_no_pmtu_disc,
@@ -838,6 +838,20 @@ static struct ctl_table ipv4_net_table[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec,
        },
+       {
+               .procname       = "fwmark_reflect",
+               .data           = &init_net.ipv4.sysctl_fwmark_reflect,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "tcp_fwmark_accept",
+               .data           = &init_net.ipv4.sysctl_tcp_fwmark_accept,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
        { }
 };
 
@@ -862,8 +876,14 @@ static __net_init int ipv4_sysctl_init_net(struct net *net)
        if (net->ipv4.ipv4_hdr == NULL)
                goto err_reg;
 
+       net->ipv4.sysctl_local_reserved_ports = kzalloc(65536 / 8, GFP_KERNEL);
+       if (!net->ipv4.sysctl_local_reserved_ports)
+               goto err_ports;
+
        return 0;
 
+err_ports:
+       unregister_net_sysctl_table(net->ipv4.ipv4_hdr);
 err_reg:
        if (!net_eq(net, &init_net))
                kfree(table);
@@ -875,6 +895,7 @@ static __net_exit void ipv4_sysctl_exit_net(struct net *net)
 {
        struct ctl_table *table;
 
+       kfree(net->ipv4.sysctl_local_reserved_ports);
        table = net->ipv4.ipv4_hdr->ctl_table_arg;
        unregister_net_sysctl_table(net->ipv4.ipv4_hdr);
        kfree(table);
@@ -888,16 +909,6 @@ static __net_initdata struct pernet_operations ipv4_sysctl_ops = {
 static __init int sysctl_ipv4_init(void)
 {
        struct ctl_table_header *hdr;
-       struct ctl_table *i;
-
-       for (i = ipv4_table; i->procname; i++) {
-               if (strcmp(i->procname, "ip_local_reserved_ports") == 0) {
-                       i->data = sysctl_local_reserved_ports;
-                       break;
-               }
-       }
-       if (!i->procname)
-               return -EINVAL;
 
        hdr = register_net_sysctl(&init_net, "net/ipv4", ipv4_table);
        if (hdr == NULL)
index 4bd6d52eeffb6c7ddc3e98054a7070826a4c4dcd..eb1dde37e678f6bb1570bfb452c019b88eb1a76c 100644 (file)
@@ -2916,6 +2916,14 @@ static int do_tcp_getsockopt(struct sock *sk, int level,
        case TCP_USER_TIMEOUT:
                val = jiffies_to_msecs(icsk->icsk_user_timeout);
                break;
+
+       case TCP_FASTOPEN:
+               if (icsk->icsk_accept_queue.fastopenq != NULL)
+                       val = icsk->icsk_accept_queue.fastopenq->max_qlen;
+               else
+                       val = 0;
+               break;
+
        case TCP_TIMESTAMP:
                val = tcp_time_stamp + tp->tsoffset;
                break;
index 821846fb0a7e211fc870b1afce5991bc3de28494..d5de69bc04f581ac589ae0f6fe7fcc3646b2cc3e 100644 (file)
@@ -140,13 +140,12 @@ static inline void bictcp_update(struct bictcp *ca, u32 cwnd)
                ca->cnt = 1;
 }
 
-static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked,
-                             u32 in_flight)
+static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct bictcp *ca = inet_csk_ca(sk);
 
-       if (!tcp_is_cwnd_limited(sk, in_flight))
+       if (!tcp_is_cwnd_limited(sk))
                return;
 
        if (tp->snd_cwnd <= tp->snd_ssthresh)
index 2b9464c93b8859fcbef0f900f70a4bed2dc6e617..7b09d8b49fa51271cc0fa99a3fcda9f017c0f469 100644 (file)
@@ -276,26 +276,6 @@ int tcp_set_congestion_control(struct sock *sk, const char *name)
        return err;
 }
 
-/* RFC2861 Check whether we are limited by application or congestion window
- * This is the inverse of cwnd check in tcp_tso_should_defer
- */
-bool tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight)
-{
-       const struct tcp_sock *tp = tcp_sk(sk);
-       u32 left;
-
-       if (in_flight >= tp->snd_cwnd)
-               return true;
-
-       left = tp->snd_cwnd - in_flight;
-       if (sk_can_gso(sk) &&
-           left * sysctl_tcp_tso_win_divisor < tp->snd_cwnd &&
-           left < tp->xmit_size_goal_segs)
-               return true;
-       return left <= tcp_max_tso_deferred_mss(tp);
-}
-EXPORT_SYMBOL_GPL(tcp_is_cwnd_limited);
-
 /* 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
@@ -337,11 +317,11 @@ 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 acked, u32 in_flight)
+void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
-       if (!tcp_is_cwnd_limited(sk, in_flight))
+       if (!tcp_is_cwnd_limited(sk))
                return;
 
        /* In "safe" area, increase. */
index b4f1b29b08bdf14acfc6153cd443c9e799eeb141..a9bd8a4828a9e5c2da275626c11c6c953a1b3dd6 100644 (file)
@@ -304,13 +304,12 @@ static inline void bictcp_update(struct bictcp *ca, u32 cwnd)
                ca->cnt = 1;
 }
 
-static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked,
-                             u32 in_flight)
+static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct bictcp *ca = inet_csk_ca(sk);
 
-       if (!tcp_is_cwnd_limited(sk, in_flight))
+       if (!tcp_is_cwnd_limited(sk))
                return;
 
        if (tp->snd_cwnd <= tp->snd_ssthresh) {
index f195d9316e55d3d7ef6dc5285606ea9de0f52a51..62e48cf84e602a005ab2ce61c058ca709702098a 100644 (file)
@@ -72,25 +72,224 @@ error:             kfree(ctx);
        return err;
 }
 
-/* Computes the fastopen cookie for the IP path.
- * The path is a 128 bits long (pad with zeros for IPv4).
- *
- * The caller must check foc->len to determine if a valid cookie
- * has been generated successfully.
-*/
-void tcp_fastopen_cookie_gen(__be32 src, __be32 dst,
-                            struct tcp_fastopen_cookie *foc)
+static bool __tcp_fastopen_cookie_gen(const void *path,
+                                     struct tcp_fastopen_cookie *foc)
 {
-       __be32 path[4] = { src, dst, 0, 0 };
        struct tcp_fastopen_context *ctx;
+       bool ok = false;
 
        tcp_fastopen_init_key_once(true);
 
        rcu_read_lock();
        ctx = rcu_dereference(tcp_fastopen_ctx);
        if (ctx) {
-               crypto_cipher_encrypt_one(ctx->tfm, foc->val, (__u8 *)path);
+               crypto_cipher_encrypt_one(ctx->tfm, foc->val, path);
                foc->len = TCP_FASTOPEN_COOKIE_SIZE;
+               ok = true;
        }
        rcu_read_unlock();
+       return ok;
+}
+
+/* Generate the fastopen cookie by doing aes128 encryption on both
+ * the source and destination addresses. Pad 0s for IPv4 or IPv4-mapped-IPv6
+ * addresses. For the longer IPv6 addresses use CBC-MAC.
+ *
+ * XXX (TFO) - refactor when TCP_FASTOPEN_COOKIE_SIZE != AES_BLOCK_SIZE.
+ */
+static bool tcp_fastopen_cookie_gen(struct request_sock *req,
+                                   struct sk_buff *syn,
+                                   struct tcp_fastopen_cookie *foc)
+{
+       if (req->rsk_ops->family == AF_INET) {
+               const struct iphdr *iph = ip_hdr(syn);
+
+               __be32 path[4] = { iph->saddr, iph->daddr, 0, 0 };
+               return __tcp_fastopen_cookie_gen(path, foc);
+       }
+
+#if IS_ENABLED(CONFIG_IPV6)
+       if (req->rsk_ops->family == AF_INET6) {
+               const struct ipv6hdr *ip6h = ipv6_hdr(syn);
+               struct tcp_fastopen_cookie tmp;
+
+               if (__tcp_fastopen_cookie_gen(&ip6h->saddr, &tmp)) {
+                       struct in6_addr *buf = (struct in6_addr *) tmp.val;
+                       int i = 4;
+
+                       for (i = 0; i < 4; i++)
+                               buf->s6_addr32[i] ^= ip6h->daddr.s6_addr32[i];
+                       return __tcp_fastopen_cookie_gen(buf, foc);
+               }
+       }
+#endif
+       return false;
+}
+
+static bool tcp_fastopen_create_child(struct sock *sk,
+                                     struct sk_buff *skb,
+                                     struct dst_entry *dst,
+                                     struct request_sock *req)
+{
+       struct tcp_sock *tp = tcp_sk(sk);
+       struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue;
+       struct sock *child;
+
+       req->num_retrans = 0;
+       req->num_timeout = 0;
+       req->sk = NULL;
+
+       child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL);
+       if (child == NULL)
+               return false;
+
+       spin_lock(&queue->fastopenq->lock);
+       queue->fastopenq->qlen++;
+       spin_unlock(&queue->fastopenq->lock);
+
+       /* Initialize the child socket. Have to fix some values to take
+        * into account the child is a Fast Open socket and is created
+        * only out of the bits carried in the SYN packet.
+        */
+       tp = tcp_sk(child);
+
+       tp->fastopen_rsk = req;
+       /* Do a hold on the listner sk so that if the listener is being
+        * closed, the child that has been accepted can live on and still
+        * access listen_lock.
+        */
+       sock_hold(sk);
+       tcp_rsk(req)->listener = sk;
+
+       /* RFC1323: The window in SYN & SYN/ACK segments is never
+        * scaled. So correct it appropriately.
+        */
+       tp->snd_wnd = ntohs(tcp_hdr(skb)->window);
+
+       /* Activate the retrans timer so that SYNACK can be retransmitted.
+        * The request socket is not added to the SYN table of the parent
+        * because it's been added to the accept queue directly.
+        */
+       inet_csk_reset_xmit_timer(child, ICSK_TIME_RETRANS,
+                                 TCP_TIMEOUT_INIT, TCP_RTO_MAX);
+
+       /* Add the child socket directly into the accept queue */
+       inet_csk_reqsk_queue_add(sk, req, child);
+
+       /* Now finish processing the fastopen child socket. */
+       inet_csk(child)->icsk_af_ops->rebuild_header(child);
+       tcp_init_congestion_control(child);
+       tcp_mtup_init(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.
+        *
+        * XXX (TFO) - we honor a zero-payload TFO request for now,
+        * (any reason not to?) but no need to queue the skb since
+        * there is no data. How about SYN+FIN?
+        */
+       if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1) {
+               skb = skb_get(skb);
+               skb_dst_drop(skb);
+               __skb_pull(skb, tcp_hdr(skb)->doff * 4);
+               skb_set_owner_r(skb, child);
+               __skb_queue_tail(&child->sk_receive_queue, skb);
+               tp->syn_data_acked = 1;
+       }
+       tcp_rsk(req)->rcv_nxt = tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
+       sk->sk_data_ready(sk);
+       bh_unlock_sock(child);
+       sock_put(child);
+       WARN_ON(req->sk == NULL);
+       return true;
+}
+EXPORT_SYMBOL(tcp_fastopen_create_child);
+
+static bool tcp_fastopen_queue_check(struct sock *sk)
+{
+       struct fastopen_queue *fastopenq;
+
+       /* Make sure the listener has enabled fastopen, and we don't
+        * exceed the max # of pending TFO requests allowed before trying
+        * to validating the cookie in order to avoid burning CPU cycles
+        * unnecessarily.
+        *
+        * XXX (TFO) - The implication of checking the max_qlen before
+        * processing a cookie request is that clients can't differentiate
+        * between qlen overflow causing Fast Open to be disabled
+        * temporarily vs a server not supporting Fast Open at all.
+        */
+       fastopenq = inet_csk(sk)->icsk_accept_queue.fastopenq;
+       if (fastopenq == NULL || fastopenq->max_qlen == 0)
+               return false;
+
+       if (fastopenq->qlen >= fastopenq->max_qlen) {
+               struct request_sock *req1;
+               spin_lock(&fastopenq->lock);
+               req1 = fastopenq->rskq_rst_head;
+               if ((req1 == NULL) || time_after(req1->expires, jiffies)) {
+                       spin_unlock(&fastopenq->lock);
+                       NET_INC_STATS_BH(sock_net(sk),
+                                        LINUX_MIB_TCPFASTOPENLISTENOVERFLOW);
+                       return false;
+               }
+               fastopenq->rskq_rst_head = req1->dl_next;
+               fastopenq->qlen--;
+               spin_unlock(&fastopenq->lock);
+               reqsk_free(req1);
+       }
+       return true;
+}
+
+/* Returns true if we should perform Fast Open on the SYN. The cookie (foc)
+ * may be updated and return the client in the SYN-ACK later. E.g., Fast Open
+ * cookie request (foc->len == 0).
+ */
+bool tcp_try_fastopen(struct sock *sk, struct sk_buff *skb,
+                     struct request_sock *req,
+                     struct tcp_fastopen_cookie *foc,
+                     struct dst_entry *dst)
+{
+       struct tcp_fastopen_cookie valid_foc = { .len = -1 };
+       bool syn_data = TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1;
+
+       if (!((sysctl_tcp_fastopen & TFO_SERVER_ENABLE) &&
+             (syn_data || foc->len >= 0) &&
+             tcp_fastopen_queue_check(sk))) {
+               foc->len = -1;
+               return false;
+       }
+
+       if (syn_data && (sysctl_tcp_fastopen & TFO_SERVER_COOKIE_NOT_REQD))
+               goto fastopen;
+
+       if (tcp_fastopen_cookie_gen(req, skb, &valid_foc) &&
+           foc->len == TCP_FASTOPEN_COOKIE_SIZE &&
+           foc->len == valid_foc.len &&
+           !memcmp(foc->val, valid_foc.val, foc->len)) {
+               /* Cookie is valid. Create a (full) child socket to accept
+                * the data in SYN before returning a SYN-ACK to ack the
+                * data. If we fail to create the socket, fall back and
+                * ack the ISN only but includes the same cookie.
+                *
+                * Note: Data-less SYN with valid cookie is allowed to send
+                * data in SYN_RECV state.
+                */
+fastopen:
+               if (tcp_fastopen_create_child(sk, skb, dst, req)) {
+                       foc->len = -1;
+                       NET_INC_STATS_BH(sock_net(sk),
+                                        LINUX_MIB_TCPFASTOPENPASSIVE);
+                       return true;
+               }
+       }
+
+       NET_INC_STATS_BH(sock_net(sk), foc->len ?
+                        LINUX_MIB_TCPFASTOPENPASSIVEFAIL :
+                        LINUX_MIB_TCPFASTOPENCOOKIEREQD);
+       *foc = valid_foc;
+       return false;
 }
+EXPORT_SYMBOL(tcp_try_fastopen);
index 8b9e7bad77c09a0c07706b955c29b81d4e71a542..1c4908280d921fbe9a9e21e4fd55d1a87f2db706 100644 (file)
@@ -109,12 +109,12 @@ 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 ack, u32 acked, u32 in_flight)
+static void hstcp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct hstcp *ca = inet_csk_ca(sk);
 
-       if (!tcp_is_cwnd_limited(sk, in_flight))
+       if (!tcp_is_cwnd_limited(sk))
                return;
 
        if (tp->snd_cwnd <= tp->snd_ssthresh)
index 4a194acfd9237f1aaadfe9f9831358f5c9037cf7..031361311a8b92b1f7ab1172fb00e3bbb0503129 100644 (file)
@@ -227,12 +227,12 @@ 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 acked, u32 in_flight)
+static void htcp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct htcp *ca = inet_csk_ca(sk);
 
-       if (!tcp_is_cwnd_limited(sk, in_flight))
+       if (!tcp_is_cwnd_limited(sk))
                return;
 
        if (tp->snd_cwnd <= tp->snd_ssthresh)
index a15a799bf76888f3633b27cf57e8e05f30339601..d8f8f05a49516ec2d9213f10af77b82fbf32bff7 100644 (file)
@@ -87,8 +87,7 @@ 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 acked,
-                            u32 in_flight)
+static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct hybla *ca = inet_csk_ca(sk);
@@ -101,11 +100,11 @@ static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 acked,
                ca->minrtt_us = tp->srtt_us;
        }
 
-       if (!tcp_is_cwnd_limited(sk, in_flight))
+       if (!tcp_is_cwnd_limited(sk))
                return;
 
        if (!ca->hybla_en) {
-               tcp_reno_cong_avoid(sk, ack, acked, in_flight);
+               tcp_reno_cong_avoid(sk, ack, acked);
                return;
        }
 
index 863d105e30150391e9ac3ce8545c0411e4d083ab..5999b3972e6449d616facb628d27116ab8876ac2 100644 (file)
@@ -255,8 +255,7 @@ 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 acked,
-                                   u32 in_flight)
+static void tcp_illinois_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct illinois *ca = inet_csk_ca(sk);
@@ -265,7 +264,7 @@ static void tcp_illinois_cong_avoid(struct sock *sk, u32 ack, u32 acked,
                update_params(sk);
 
        /* RFC2861 only increase cwnd if fully utilized */
-       if (!tcp_is_cwnd_limited(sk, in_flight))
+       if (!tcp_is_cwnd_limited(sk))
                return;
 
        /* In slow start */
index 3a26b3b23f1614340c0c1fe54204fe0999eb0691..40661fc1e233a28594e62640de835b8f44f7794d 100644 (file)
@@ -1167,7 +1167,7 @@ static int tcp_match_skb_to_sack(struct sock *sk, struct sk_buff *skb,
                        }
                        pkt_len = new_len;
                }
-               err = tcp_fragment(sk, skb, pkt_len, mss);
+               err = tcp_fragment(sk, skb, pkt_len, mss, GFP_ATOMIC);
                if (err < 0)
                        return err;
        }
@@ -2241,7 +2241,8 @@ static void tcp_mark_head_lost(struct sock *sk, int packets, int mark_head)
                                break;
 
                        mss = skb_shinfo(skb)->gso_size;
-                       err = tcp_fragment(sk, skb, (packets - oldcnt) * mss, mss);
+                       err = tcp_fragment(sk, skb, (packets - oldcnt) * mss,
+                                          mss, GFP_ATOMIC);
                        if (err < 0)
                                break;
                        cnt = packets;
@@ -2937,10 +2938,11 @@ static void tcp_synack_rtt_meas(struct sock *sk, const u32 synack_stamp)
                tcp_ack_update_rtt(sk, FLAG_SYN_ACKED, seq_rtt_us, -1L);
 }
 
-static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight)
+static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        const struct inet_connection_sock *icsk = inet_csk(sk);
-       icsk->icsk_ca_ops->cong_avoid(sk, ack, acked, in_flight);
+
+       icsk->icsk_ca_ops->cong_avoid(sk, ack, acked);
        tcp_sk(sk)->snd_cwnd_stamp = tcp_time_stamp;
 }
 
@@ -3363,7 +3365,6 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
        u32 ack_seq = TCP_SKB_CB(skb)->seq;
        u32 ack = TCP_SKB_CB(skb)->ack_seq;
        bool is_dupack = false;
-       u32 prior_in_flight;
        u32 prior_fackets;
        int prior_packets = tp->packets_out;
        const int prior_unsacked = tp->packets_out - tp->sacked_out;
@@ -3396,7 +3397,6 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
                flag |= FLAG_SND_UNA_ADVANCED;
 
        prior_fackets = tp->fackets_out;
-       prior_in_flight = tcp_packets_in_flight(tp);
 
        /* ts_recent update must be made after we are sure that the packet
         * is in window.
@@ -3451,7 +3451,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, acked, prior_in_flight);
+               tcp_cong_avoid(sk, ack, acked);
 
        if (tcp_ack_is_dubious(sk, flag)) {
                is_dupack = !(flag & (FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP));
@@ -4702,28 +4702,6 @@ static int tcp_prune_queue(struct sock *sk)
        return -1;
 }
 
-/* RFC2861, slow part. Adjust cwnd, after it was not full during one rto.
- * As additional protections, we do not touch cwnd in retransmission phases,
- * and if application hit its sndbuf limit recently.
- */
-void tcp_cwnd_application_limited(struct sock *sk)
-{
-       struct tcp_sock *tp = tcp_sk(sk);
-
-       if (inet_csk(sk)->icsk_ca_state == TCP_CA_Open &&
-           sk->sk_socket && !test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) {
-               /* Limited by application or receiver window. */
-               u32 init_win = tcp_init_cwnd(tp, __sk_dst_get(sk));
-               u32 win_used = max(tp->snd_cwnd_used, init_win);
-               if (win_used < tp->snd_cwnd) {
-                       tp->snd_ssthresh = tcp_current_ssthresh(sk);
-                       tp->snd_cwnd = (tp->snd_cwnd + win_used) >> 1;
-               }
-               tp->snd_cwnd_used = 0;
-       }
-       tp->snd_cwnd_stamp = tcp_time_stamp;
-}
-
 static bool tcp_should_expand_sndbuf(const struct sock *sk)
 {
        const struct tcp_sock *tp = tcp_sk(sk);
index 438f3b95143df0bffa01322c62b44c8fcd6d6b8b..77cccda1ad0c6dc62c8cb70d932eca2322304c81 100644 (file)
@@ -336,8 +336,8 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
        const int code = icmp_hdr(icmp_skb)->code;
        struct sock *sk;
        struct sk_buff *skb;
-       struct request_sock *req;
-       __u32 seq;
+       struct request_sock *fastopen;
+       __u32 seq, snd_una;
        __u32 remaining;
        int err;
        struct net *net = dev_net(icmp_skb->dev);
@@ -378,12 +378,12 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
 
        icsk = inet_csk(sk);
        tp = tcp_sk(sk);
-       req = tp->fastopen_rsk;
        seq = ntohl(th->seq);
+       /* XXX (TFO) - tp->snd_una should be ISN (tcp_create_openreq_child() */
+       fastopen = tp->fastopen_rsk;
+       snd_una = fastopen ? tcp_rsk(fastopen)->snt_isn : tp->snd_una;
        if (sk->sk_state != TCP_LISTEN &&
-           !between(seq, tp->snd_una, tp->snd_nxt) &&
-           (req == NULL || seq != tcp_rsk(req)->snt_isn)) {
-               /* For a Fast Open socket, allow seq to be snt_isn. */
+           !between(seq, snd_una, tp->snd_nxt)) {
                NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
                goto out;
        }
@@ -426,11 +426,9 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
                if (code != ICMP_NET_UNREACH && code != ICMP_HOST_UNREACH)
                        break;
                if (seq != tp->snd_una  || !icsk->icsk_retransmits ||
-                   !icsk->icsk_backoff)
+                   !icsk->icsk_backoff || fastopen)
                        break;
 
-               /* XXX (TFO) - revisit the following logic for TFO */
-
                if (sock_owned_by_user(sk))
                        break;
 
@@ -462,14 +460,6 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
                goto out;
        }
 
-       /* XXX (TFO) - if it's a TFO socket and has been accepted, rather
-        * than following the TCP_SYN_RECV case and closing the socket,
-        * we ignore the ICMP error and keep trying like a fully established
-        * socket. Is this the right thing to do?
-        */
-       if (req && req->sk == NULL)
-               goto out;
-
        switch (sk->sk_state) {
                struct request_sock *req, **prev;
        case TCP_LISTEN:
@@ -502,10 +492,13 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
                goto out;
 
        case TCP_SYN_SENT:
-       case TCP_SYN_RECV:  /* Cannot happen.
-                              It can f.e. if SYNs crossed,
-                              or Fast Open.
-                            */
+       case TCP_SYN_RECV:
+               /* Only in fast or simultaneous open. If a fast open socket is
+                * is already accepted it is treated as a connected one below.
+                */
+               if (fastopen && fastopen->sk == NULL)
+                       break;
+
                if (!sock_owned_by_user(sk)) {
                        sk->sk_err = err;
 
@@ -822,7 +815,8 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
  */
 static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst,
                              struct request_sock *req,
-                             u16 queue_mapping)
+                             u16 queue_mapping,
+                             struct tcp_fastopen_cookie *foc)
 {
        const struct inet_request_sock *ireq = inet_rsk(req);
        struct flowi4 fl4;
@@ -833,7 +827,7 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst,
        if (!dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL)
                return -1;
 
-       skb = tcp_make_synack(sk, dst, req, NULL);
+       skb = tcp_make_synack(sk, dst, req, foc);
 
        if (skb) {
                __tcp_v4_send_check(skb, ireq->ir_loc_addr, ireq->ir_rmt_addr);
@@ -852,7 +846,7 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst,
 
 static int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req)
 {
-       int res = tcp_v4_send_synack(sk, NULL, req, 0);
+       int res = tcp_v4_send_synack(sk, NULL, req, 0, NULL);
 
        if (!res) {
                TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS);
@@ -1260,187 +1254,6 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = {
 };
 #endif
 
-static bool tcp_fastopen_check(struct sock *sk, struct sk_buff *skb,
-                              struct request_sock *req,
-                              struct tcp_fastopen_cookie *foc,
-                              struct tcp_fastopen_cookie *valid_foc)
-{
-       bool skip_cookie = false;
-       struct fastopen_queue *fastopenq;
-
-       if (likely(!fastopen_cookie_present(foc))) {
-               /* See include/net/tcp.h for the meaning of these knobs */
-               if ((sysctl_tcp_fastopen & TFO_SERVER_ALWAYS) ||
-                   ((sysctl_tcp_fastopen & TFO_SERVER_COOKIE_NOT_REQD) &&
-                   (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1)))
-                       skip_cookie = true; /* no cookie to validate */
-               else
-                       return false;
-       }
-       fastopenq = inet_csk(sk)->icsk_accept_queue.fastopenq;
-       /* A FO option is present; bump the counter. */
-       NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENPASSIVE);
-
-       /* Make sure the listener has enabled fastopen, and we don't
-        * exceed the max # of pending TFO requests allowed before trying
-        * to validating the cookie in order to avoid burning CPU cycles
-        * unnecessarily.
-        *
-        * XXX (TFO) - The implication of checking the max_qlen before
-        * processing a cookie request is that clients can't differentiate
-        * between qlen overflow causing Fast Open to be disabled
-        * temporarily vs a server not supporting Fast Open at all.
-        */
-       if ((sysctl_tcp_fastopen & TFO_SERVER_ENABLE) == 0 ||
-           fastopenq == NULL || fastopenq->max_qlen == 0)
-               return false;
-
-       if (fastopenq->qlen >= fastopenq->max_qlen) {
-               struct request_sock *req1;
-               spin_lock(&fastopenq->lock);
-               req1 = fastopenq->rskq_rst_head;
-               if ((req1 == NULL) || time_after(req1->expires, jiffies)) {
-                       spin_unlock(&fastopenq->lock);
-                       NET_INC_STATS_BH(sock_net(sk),
-                           LINUX_MIB_TCPFASTOPENLISTENOVERFLOW);
-                       /* Avoid bumping LINUX_MIB_TCPFASTOPENPASSIVEFAIL*/
-                       foc->len = -1;
-                       return false;
-               }
-               fastopenq->rskq_rst_head = req1->dl_next;
-               fastopenq->qlen--;
-               spin_unlock(&fastopenq->lock);
-               reqsk_free(req1);
-       }
-       if (skip_cookie) {
-               tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
-               return true;
-       }
-
-       if (foc->len == TCP_FASTOPEN_COOKIE_SIZE) {
-               if ((sysctl_tcp_fastopen & TFO_SERVER_COOKIE_NOT_CHKED) == 0) {
-                       tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr,
-                                               ip_hdr(skb)->daddr, valid_foc);
-                       if ((valid_foc->len != TCP_FASTOPEN_COOKIE_SIZE) ||
-                           memcmp(&foc->val[0], &valid_foc->val[0],
-                           TCP_FASTOPEN_COOKIE_SIZE) != 0)
-                               return false;
-                       valid_foc->len = -1;
-               }
-               /* Acknowledge the data received from the peer. */
-               tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
-               return true;
-       } else if (foc->len == 0) { /* Client requesting a cookie */
-               tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr,
-                                       ip_hdr(skb)->daddr, valid_foc);
-               NET_INC_STATS_BH(sock_net(sk),
-                   LINUX_MIB_TCPFASTOPENCOOKIEREQD);
-       } else {
-               /* Client sent a cookie with wrong size. Treat it
-                * the same as invalid and return a valid one.
-                */
-               tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr,
-                                       ip_hdr(skb)->daddr, valid_foc);
-       }
-       return false;
-}
-
-static int tcp_v4_conn_req_fastopen(struct sock *sk,
-                                   struct sk_buff *skb,
-                                   struct sk_buff *skb_synack,
-                                   struct request_sock *req)
-{
-       struct tcp_sock *tp = tcp_sk(sk);
-       struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue;
-       const struct inet_request_sock *ireq = inet_rsk(req);
-       struct sock *child;
-       int err;
-
-       req->num_retrans = 0;
-       req->num_timeout = 0;
-       req->sk = NULL;
-
-       child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL);
-       if (child == NULL) {
-               NET_INC_STATS_BH(sock_net(sk),
-                                LINUX_MIB_TCPFASTOPENPASSIVEFAIL);
-               kfree_skb(skb_synack);
-               return -1;
-       }
-       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;
-       /* XXX (TFO) - is it ok to ignore error and continue? */
-
-       spin_lock(&queue->fastopenq->lock);
-       queue->fastopenq->qlen++;
-       spin_unlock(&queue->fastopenq->lock);
-
-       /* Initialize the child socket. Have to fix some values to take
-        * into account the child is a Fast Open socket and is created
-        * only out of the bits carried in the SYN packet.
-        */
-       tp = tcp_sk(child);
-
-       tp->fastopen_rsk = req;
-       /* Do a hold on the listner sk so that if the listener is being
-        * closed, the child that has been accepted can live on and still
-        * access listen_lock.
-        */
-       sock_hold(sk);
-       tcp_rsk(req)->listener = sk;
-
-       /* RFC1323: The window in SYN & SYN/ACK segments is never
-        * scaled. So correct it appropriately.
-        */
-       tp->snd_wnd = ntohs(tcp_hdr(skb)->window);
-
-       /* Activate the retrans timer so that SYNACK can be retransmitted.
-        * The request socket is not added to the SYN table of the parent
-        * because it's been added to the accept queue directly.
-        */
-       inet_csk_reset_xmit_timer(child, ICSK_TIME_RETRANS,
-           TCP_TIMEOUT_INIT, TCP_RTO_MAX);
-
-       /* Add the child socket directly into the accept queue */
-       inet_csk_reqsk_queue_add(sk, req, child);
-
-       /* Now finish processing the fastopen child socket. */
-       inet_csk(child)->icsk_af_ops->rebuild_header(child);
-       tcp_init_congestion_control(child);
-       tcp_mtup_init(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.
-        *
-        * XXX (TFO) - we honor a zero-payload TFO request for now.
-        * (Any reason not to?)
-        */
-       if (TCP_SKB_CB(skb)->end_seq == TCP_SKB_CB(skb)->seq + 1) {
-               /* Don't queue the skb if there is no payload in SYN.
-                * XXX (TFO) - How about SYN+FIN?
-                */
-               tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
-       } else {
-               skb = skb_get(skb);
-               skb_dst_drop(skb);
-               __skb_pull(skb, tcp_hdr(skb)->doff * 4);
-               skb_set_owner_r(skb, child);
-               __skb_queue_tail(&child->sk_receive_queue, skb);
-               tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
-               tp->syn_data_acked = 1;
-       }
-       sk->sk_data_ready(sk);
-       bh_unlock_sock(child);
-       sock_put(child);
-       WARN_ON(req->sk == NULL);
-       return 0;
-}
-
 int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
 {
        struct tcp_options_received tmp_opt;
@@ -1451,12 +1264,10 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
        __be32 saddr = ip_hdr(skb)->saddr;
        __be32 daddr = ip_hdr(skb)->daddr;
        __u32 isn = TCP_SKB_CB(skb)->when;
-       bool want_cookie = false;
+       bool want_cookie = false, fastopen;
        struct flowi4 fl4;
        struct tcp_fastopen_cookie foc = { .len = -1 };
-       struct tcp_fastopen_cookie valid_foc = { .len = -1 };
-       struct sk_buff *skb_synack;
-       int do_fastopen;
+       int err;
 
        /* Never answer to SYNs send to broadcast or multicast */
        if (skb_rtable(skb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
@@ -1507,6 +1318,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
        ireq->ir_rmt_addr = saddr;
        ireq->no_srccheck = inet_sk(sk)->transparent;
        ireq->opt = tcp_v4_save_options(skb);
+       ireq->ir_mark = inet_request_mark(sk, skb);
 
        if (security_inet_conn_request(sk, skb, req))
                goto drop_and_free;
@@ -1555,52 +1367,24 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
 
                isn = tcp_v4_init_sequence(skb);
        }
-       tcp_rsk(req)->snt_isn = isn;
-
-       if (dst == NULL) {
-               dst = inet_csk_route_req(sk, &fl4, req);
-               if (dst == NULL)
-                       goto drop_and_free;
-       }
-       do_fastopen = tcp_fastopen_check(sk, skb, req, &foc, &valid_foc);
-
-       /* We don't call tcp_v4_send_synack() directly because we need
-        * to make sure a child socket can be created successfully before
-        * sending back synack!
-        *
-        * XXX (TFO) - Ideally one would simply call tcp_v4_send_synack()
-        * (or better yet, call tcp_send_synack() in the child context
-        * directly, but will have to fix bunch of other code first)
-        * after syn_recv_sock() except one will need to first fix the
-        * latter to remove its dependency on the current implementation
-        * of tcp_v4_send_synack()->tcp_select_initial_window().
-        */
-       skb_synack = tcp_make_synack(sk, dst, req,
-           fastopen_cookie_present(&valid_foc) ? &valid_foc : NULL);
-
-       if (skb_synack) {
-               __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
+       if (!dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL)
                goto drop_and_free;
 
-       if (likely(!do_fastopen)) {
-               int err;
-               err = ip_build_and_send_pkt(skb_synack, sk, ireq->ir_loc_addr,
-                    ireq->ir_rmt_addr, ireq->opt);
-               err = net_xmit_eval(err);
+       tcp_rsk(req)->snt_isn = isn;
+       tcp_rsk(req)->snt_synack = tcp_time_stamp;
+       tcp_openreq_init_rwin(req, sk, dst);
+       fastopen = !want_cookie &&
+                  tcp_try_fastopen(sk, skb, req, &foc, dst);
+       err = tcp_v4_send_synack(sk, dst, req,
+                                skb_get_queue_mapping(skb), &foc);
+       if (!fastopen) {
                if (err || want_cookie)
                        goto drop_and_free;
 
                tcp_rsk(req)->snt_synack = tcp_time_stamp;
                tcp_rsk(req)->listener = NULL;
-               /* Add the request_sock to the SYN table */
                inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
-               if (fastopen_cookie_present(&foc) && foc.len != 0)
-                       NET_INC_STATS_BH(sock_net(sk),
-                           LINUX_MIB_TCPFASTOPENPASSIVEFAIL);
-       } else if (tcp_v4_conn_req_fastopen(sk, skb, skb_synack, req))
-               goto drop_and_free;
+       }
 
        return 0;
 
@@ -1744,28 +1528,6 @@ static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)
        return sk;
 }
 
-static __sum16 tcp_v4_checksum_init(struct sk_buff *skb)
-{
-       const struct iphdr *iph = ip_hdr(skb);
-
-       if (skb->ip_summed == CHECKSUM_COMPLETE) {
-               if (!tcp_v4_check(skb->len, iph->saddr,
-                                 iph->daddr, skb->csum)) {
-                       skb->ip_summed = CHECKSUM_UNNECESSARY;
-                       return 0;
-               }
-       }
-
-       skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr,
-                                      skb->len, IPPROTO_TCP, 0);
-
-       if (skb->len <= 76) {
-               return __skb_checksum_complete(skb);
-       }
-       return 0;
-}
-
-
 /* The socket must have it's spinlock held when we get
  * here.
  *
@@ -1960,7 +1722,8 @@ int tcp_v4_rcv(struct sk_buff *skb)
         * Packet length and doff are validated by header prediction,
         * provided case of th->doff==0 is eliminated.
         * So, we defer the checks. */
-       if (!skb_csum_unnecessary(skb) && tcp_v4_checksum_init(skb))
+
+       if (skb_checksum_init(skb, IPPROTO_TCP, inet_compute_pseudo))
                goto csum_error;
 
        th = tcp_hdr(skb);
index c9aecae313276d134ef56d1385ac44266c200a51..1e70fa8fa793fdbca3ca782231f8b775ed96f205 100644 (file)
@@ -115,13 +115,12 @@ 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 acked,
-                             u32 in_flight)
+static void tcp_lp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        struct lp *lp = inet_csk_ca(sk);
 
        if (!(lp->flag & LP_WITHIN_INF))
-               tcp_reno_cong_avoid(sk, ack, acked, in_flight);
+               tcp_reno_cong_avoid(sk, ack, acked);
 }
 
 /**
index dcaf72f10216c22f00ef918963d260d4ce472b99..4fe04180598969e653a01db2c459edd2c8d8acce 100644 (file)
@@ -1159,10 +1159,7 @@ static void __net_exit tcp_net_metrics_exit(struct net *net)
                        tm = next;
                }
        }
-       if (is_vmalloc_addr(net->ipv4.tcp_metrics_hash))
-               vfree(net->ipv4.tcp_metrics_hash);
-       else
-               kfree(net->ipv4.tcp_metrics_hash);
+       kvfree(net->ipv4.tcp_metrics_hash);
 }
 
 static __net_initdata struct pernet_operations tcp_net_metrics_ops = {
index 05c1b155251d39d2e559d050f1e191f51f32b3e0..e68e0d4af6c97bcd0f8c983890ba555adbfb3a00 100644 (file)
@@ -362,6 +362,37 @@ void tcp_twsk_destructor(struct sock *sk)
 }
 EXPORT_SYMBOL_GPL(tcp_twsk_destructor);
 
+void tcp_openreq_init_rwin(struct request_sock *req,
+                          struct sock *sk, struct dst_entry *dst)
+{
+       struct inet_request_sock *ireq = inet_rsk(req);
+       struct tcp_sock *tp = tcp_sk(sk);
+       __u8 rcv_wscale;
+       int mss = dst_metric_advmss(dst);
+
+       if (tp->rx_opt.user_mss && tp->rx_opt.user_mss < mss)
+               mss = tp->rx_opt.user_mss;
+
+       /* Set this up on the first call only */
+       req->window_clamp = tp->window_clamp ? : dst_metric(dst, RTAX_WINDOW);
+
+       /* limit the window selection if the user enforce a smaller rx buffer */
+       if (sk->sk_userlocks & SOCK_RCVBUF_LOCK &&
+           (req->window_clamp > tcp_full_space(sk) || req->window_clamp == 0))
+               req->window_clamp = tcp_full_space(sk);
+
+       /* tcp_full_space because it is guaranteed to be the first packet */
+       tcp_select_initial_window(tcp_full_space(sk),
+               mss - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0),
+               &req->rcv_wnd,
+               &req->window_clamp,
+               ireq->wscale_ok,
+               &rcv_wscale,
+               dst_metric(dst, RTAX_INITRWND));
+       ireq->rcv_wscale = rcv_wscale;
+}
+EXPORT_SYMBOL(tcp_openreq_init_rwin);
+
 static inline void TCP_ECN_openreq_child(struct tcp_sock *tp,
                                         struct request_sock *req)
 {
index b92b81718ca42bc9e47b415c321eb143d7266852..4e86c59ec7f7f07fe06c6db20d17851da6f1563f 100644 (file)
@@ -57,10 +57,12 @@ struct sk_buff *tcp_gso_segment(struct sk_buff *skb,
                               SKB_GSO_TCP_ECN |
                               SKB_GSO_TCPV6 |
                               SKB_GSO_GRE |
+                              SKB_GSO_GRE_CSUM |
                               SKB_GSO_IPIP |
                               SKB_GSO_SIT |
                               SKB_GSO_MPLS |
                               SKB_GSO_UDP_TUNNEL |
+                              SKB_GSO_UDP_TUNNEL_CSUM |
                               0) ||
                             !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))))
                        goto out;
@@ -97,9 +99,7 @@ struct sk_buff *tcp_gso_segment(struct sk_buff *skb,
                th->check = newcheck;
 
                if (skb->ip_summed != CHECKSUM_PARTIAL)
-                       th->check =
-                            csum_fold(csum_partial(skb_transport_header(skb),
-                                                   thlen, skb->csum));
+                       th->check = gso_make_checksum(skb, ~th->check);
 
                seq += mss;
                if (copy_destructor) {
@@ -133,8 +133,7 @@ struct sk_buff *tcp_gso_segment(struct sk_buff *skb,
        th->check = ~csum_fold((__force __wsum)((__force u32)th->check +
                                (__force u32)delta));
        if (skb->ip_summed != CHECKSUM_PARTIAL)
-               th->check = csum_fold(csum_partial(skb_transport_header(skb),
-                                                  thlen, skb->csum));
+               th->check = gso_make_checksum(skb, ~th->check);
 out:
        return segs;
 }
index 2d340bd2cd3d50d97da3e86b77de8117eabe0c10..d92bce0ea24ec54fb559941979906337d68881ad 100644 (file)
@@ -627,7 +627,7 @@ static unsigned int tcp_synack_options(struct sock *sk,
                if (unlikely(!ireq->tstamp_ok))
                        remaining -= TCPOLEN_SACKPERM_ALIGNED;
        }
-       if (foc != NULL) {
+       if (foc != NULL && foc->len >= 0) {
                u32 need = TCPOLEN_EXP_FASTOPEN_BASE + foc->len;
                need = (need + 3) & ~3U;  /* Align to 32 bits */
                if (remaining >= need) {
@@ -878,15 +878,8 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
        BUG_ON(!skb || !tcp_skb_pcount(skb));
 
        if (clone_it) {
-               const struct sk_buff *fclone = skb + 1;
-
                skb_mstamp_get(&skb->skb_mstamp);
 
-               if (unlikely(skb->fclone == SKB_FCLONE_ORIG &&
-                            fclone->fclone == SKB_FCLONE_CLONE))
-                       NET_INC_STATS(sock_net(sk),
-                                     LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES);
-
                if (unlikely(skb_cloned(skb)))
                        skb = pskb_copy(skb, gfp_mask);
                else
@@ -1081,7 +1074,7 @@ static void tcp_adjust_pcount(struct sock *sk, const struct sk_buff *skb, int de
  * Remember, these are still headerless SKBs at this point.
  */
 int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len,
-                unsigned int mss_now)
+                unsigned int mss_now, gfp_t gfp)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct sk_buff *buff;
@@ -1096,11 +1089,11 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len,
        if (nsize < 0)
                nsize = 0;
 
-       if (skb_unclone(skb, GFP_ATOMIC))
+       if (skb_unclone(skb, gfp))
                return -ENOMEM;
 
        /* Get a new skb... force flag on. */
-       buff = sk_stream_alloc_skb(sk, nsize, GFP_ATOMIC);
+       buff = sk_stream_alloc_skb(sk, nsize, gfp);
        if (buff == NULL)
                return -ENOMEM; /* We'll just try again later. */
 
@@ -1387,12 +1380,43 @@ unsigned int tcp_current_mss(struct sock *sk)
        return mss_now;
 }
 
-/* Congestion window validation. (RFC2861) */
-static void tcp_cwnd_validate(struct sock *sk)
+/* RFC2861, slow part. Adjust cwnd, after it was not full during one rto.
+ * As additional protections, we do not touch cwnd in retransmission phases,
+ * and if application hit its sndbuf limit recently.
+ */
+static void tcp_cwnd_application_limited(struct sock *sk)
+{
+       struct tcp_sock *tp = tcp_sk(sk);
+
+       if (inet_csk(sk)->icsk_ca_state == TCP_CA_Open &&
+           sk->sk_socket && !test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) {
+               /* Limited by application or receiver window. */
+               u32 init_win = tcp_init_cwnd(tp, __sk_dst_get(sk));
+               u32 win_used = max(tp->snd_cwnd_used, init_win);
+               if (win_used < tp->snd_cwnd) {
+                       tp->snd_ssthresh = tcp_current_ssthresh(sk);
+                       tp->snd_cwnd = (tp->snd_cwnd + win_used) >> 1;
+               }
+               tp->snd_cwnd_used = 0;
+       }
+       tp->snd_cwnd_stamp = tcp_time_stamp;
+}
+
+static void tcp_cwnd_validate(struct sock *sk, bool is_cwnd_limited)
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
-       if (tp->packets_out >= tp->snd_cwnd) {
+       /* Track the maximum number of outstanding packets in each
+        * window, and remember whether we were cwnd-limited then.
+        */
+       if (!before(tp->snd_una, tp->max_packets_seq) ||
+           tp->packets_out > tp->max_packets_out) {
+               tp->max_packets_out = tp->packets_out;
+               tp->max_packets_seq = tp->snd_nxt;
+               tp->is_cwnd_limited = is_cwnd_limited;
+       }
+
+       if (tcp_is_cwnd_limited(sk)) {
                /* Network is feed fully. */
                tp->snd_cwnd_used = 0;
                tp->snd_cwnd_stamp = tcp_time_stamp;
@@ -1601,7 +1625,7 @@ static int tso_fragment(struct sock *sk, struct sk_buff *skb, unsigned int len,
 
        /* All of a TSO frame must be composed of paged data.  */
        if (skb->len != skb->data_len)
-               return tcp_fragment(sk, skb, len, mss_now);
+               return tcp_fragment(sk, skb, len, mss_now, gfp);
 
        buff = sk_stream_alloc_skb(sk, 0, gfp);
        if (unlikely(buff == NULL))
@@ -1644,7 +1668,8 @@ static int tso_fragment(struct sock *sk, struct sk_buff *skb, unsigned int len,
  *
  * This algorithm is from John Heffner.
  */
-static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb)
+static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb,
+                                bool *is_cwnd_limited)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        const struct inet_connection_sock *icsk = inet_csk(sk);
@@ -1708,6 +1733,9 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb)
        if (!tp->tso_deferred)
                tp->tso_deferred = 1 | (jiffies << 1);
 
+       if (cong_win < send_win && cong_win < skb->len)
+               *is_cwnd_limited = true;
+
        return true;
 
 send_now:
@@ -1868,6 +1896,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
        unsigned int tso_segs, sent_pkts;
        int cwnd_quota;
        int result;
+       bool is_cwnd_limited = false;
 
        sent_pkts = 0;
 
@@ -1892,6 +1921,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
 
                cwnd_quota = tcp_cwnd_test(tp, skb);
                if (!cwnd_quota) {
+                       is_cwnd_limited = true;
                        if (push_one == 2)
                                /* Force out a loss probe pkt. */
                                cwnd_quota = 1;
@@ -1908,7 +1938,8 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
                                                      nonagle : TCP_NAGLE_PUSH))))
                                break;
                } else {
-                       if (!push_one && tcp_tso_should_defer(sk, skb))
+                       if (!push_one &&
+                           tcp_tso_should_defer(sk, skb, &is_cwnd_limited))
                                break;
                }
 
@@ -1973,7 +2004,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
                /* Send one loss probe per tail loss episode. */
                if (push_one != 2)
                        tcp_schedule_loss_probe(sk);
-               tcp_cwnd_validate(sk);
+               tcp_cwnd_validate(sk, is_cwnd_limited);
                return false;
        }
        return (push_one == 2) || (!tp->packets_out && tcp_send_head(sk));
@@ -2037,6 +2068,25 @@ bool tcp_schedule_loss_probe(struct sock *sk)
        return true;
 }
 
+/* Thanks to skb fast clones, we can detect if a prior transmit of
+ * a packet is still in a qdisc or driver queue.
+ * In this case, there is very little point doing a retransmit !
+ * Note: This is called from BH context only.
+ */
+static bool skb_still_in_host_queue(const struct sock *sk,
+                                   const struct sk_buff *skb)
+{
+       const struct sk_buff *fclone = skb + 1;
+
+       if (unlikely(skb->fclone == SKB_FCLONE_ORIG &&
+                    fclone->fclone == SKB_FCLONE_CLONE)) {
+               NET_INC_STATS_BH(sock_net(sk),
+                                LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES);
+               return true;
+       }
+       return false;
+}
+
 /* When probe timeout (PTO) fires, send a new segment if one exists, else
  * retransmit the last segment.
  */
@@ -2062,12 +2112,16 @@ void tcp_send_loss_probe(struct sock *sk)
        if (WARN_ON(!skb))
                goto rearm_timer;
 
+       if (skb_still_in_host_queue(sk, skb))
+               goto rearm_timer;
+
        pcount = tcp_skb_pcount(skb);
        if (WARN_ON(!pcount))
                goto rearm_timer;
 
        if ((pcount > 1) && (skb->len > (pcount - 1) * mss)) {
-               if (unlikely(tcp_fragment(sk, skb, (pcount - 1) * mss, mss)))
+               if (unlikely(tcp_fragment(sk, skb, (pcount - 1) * mss, mss,
+                                         GFP_ATOMIC)))
                        goto rearm_timer;
                skb = tcp_write_queue_tail(sk);
        }
@@ -2075,9 +2129,7 @@ void tcp_send_loss_probe(struct sock *sk)
        if (WARN_ON(!skb || !tcp_skb_pcount(skb)))
                goto rearm_timer;
 
-       /* Probe with zero data doesn't trigger fast recovery. */
-       if (skb->len > 0)
-               err = __tcp_retransmit_skb(sk, skb);
+       err = __tcp_retransmit_skb(sk, skb);
 
        /* Record snd_nxt for loss detection. */
        if (likely(!err))
@@ -2383,6 +2435,9 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
            min(sk->sk_wmem_queued + (sk->sk_wmem_queued >> 2), sk->sk_sndbuf))
                return -EAGAIN;
 
+       if (skb_still_in_host_queue(sk, skb))
+               return -EBUSY;
+
        if (before(TCP_SKB_CB(skb)->seq, tp->snd_una)) {
                if (before(TCP_SKB_CB(skb)->end_seq, tp->snd_una))
                        BUG();
@@ -2405,7 +2460,7 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
                return -EAGAIN;
 
        if (skb->len > cur_mss) {
-               if (tcp_fragment(sk, skb, cur_mss, cur_mss))
+               if (tcp_fragment(sk, skb, cur_mss, cur_mss, GFP_ATOMIC))
                        return -ENOMEM; /* We'll try again later. */
        } else {
                int oldpcount = tcp_skb_pcount(skb);
@@ -2476,7 +2531,7 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
                 * see tcp_input.c tcp_sacktag_write_queue().
                 */
                TCP_SKB_CB(skb)->ack_seq = tp->snd_nxt;
-       } else {
+       } else if (err != -EBUSY) {
                NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPRETRANSFAIL);
        }
        return err;
@@ -2754,27 +2809,6 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
        if (tp->rx_opt.user_mss && tp->rx_opt.user_mss < mss)
                mss = tp->rx_opt.user_mss;
 
-       if (req->rcv_wnd == 0) { /* ignored for retransmitted syns */
-               __u8 rcv_wscale;
-               /* Set this up on the first call only */
-               req->window_clamp = tp->window_clamp ? : dst_metric(dst, RTAX_WINDOW);
-
-               /* limit the window selection if the user enforce a smaller rx buffer */
-               if (sk->sk_userlocks & SOCK_RCVBUF_LOCK &&
-                   (req->window_clamp > tcp_full_space(sk) || req->window_clamp == 0))
-                       req->window_clamp = tcp_full_space(sk);
-
-               /* tcp_full_space because it is guaranteed to be the first packet */
-               tcp_select_initial_window(tcp_full_space(sk),
-                       mss - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0),
-                       &req->rcv_wnd,
-                       &req->window_clamp,
-                       ireq->wscale_ok,
-                       &rcv_wscale,
-                       dst_metric(dst, RTAX_INITRWND));
-               ireq->rcv_wscale = rcv_wscale;
-       }
-
        memset(&opts, 0, sizeof(opts));
 #ifdef CONFIG_SYN_COOKIES
        if (unlikely(req->cookie_ts))
@@ -3207,7 +3241,7 @@ int tcp_write_wakeup(struct sock *sk)
                    skb->len > mss) {
                        seg_size = min(seg_size, mss);
                        TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_PSH;
-                       if (tcp_fragment(sk, skb, seg_size, mss))
+                       if (tcp_fragment(sk, skb, seg_size, mss, GFP_ATOMIC))
                                return -1;
                } else if (!tcp_skb_pcount(skb))
                        tcp_set_skb_tso_segs(sk, skb, mss);
index 0ac50836da4d42832f3aa35c9a4cebbf79f69981..8250949b88538db4e0f2d318050518855e74cf3d 100644 (file)
 #define TCP_SCALABLE_AI_CNT    50U
 #define TCP_SCALABLE_MD_SCALE  3
 
-static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 acked,
-                                   u32 in_flight)
+static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
-       if (!tcp_is_cwnd_limited(sk, in_flight))
+       if (!tcp_is_cwnd_limited(sk))
                return;
 
        if (tp->snd_cwnd <= tp->snd_ssthresh)
index 48539fff6357a4e778c537b99bb9a7fd49eb43b3..9a5e05f27f4f7cce16cb285698cb0fc73ef49a91 100644 (file)
@@ -163,14 +163,13 @@ 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 acked,
-                                u32 in_flight)
+static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        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, acked, in_flight);
+               tcp_reno_cong_avoid(sk, ack, acked);
                return;
        }
 
@@ -195,7 +194,7 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 acked,
                        /* We don't have enough RTT samples to do the Vegas
                         * calculation, so we'll behave like Reno.
                         */
-                       tcp_reno_cong_avoid(sk, ack, acked, in_flight);
+                       tcp_reno_cong_avoid(sk, ack, acked);
                } else {
                        u32 rtt, diff;
                        u64 target_cwnd;
index 1b8e28fcd7e1cab3edd586db1b716742a4402fd7..27b9825753d15d89717d7b76e1c38fae4758cfbc 100644 (file)
@@ -114,19 +114,18 @@ 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 acked,
-                               u32 in_flight)
+static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        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, acked, in_flight);
+               tcp_reno_cong_avoid(sk, ack, acked);
                return;
        }
 
        /* limited by applications */
-       if (!tcp_is_cwnd_limited(sk, in_flight))
+       if (!tcp_is_cwnd_limited(sk))
                return;
 
        /* We do the Veno calculations only if we got enough rtt samples */
@@ -134,7 +133,7 @@ static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, u32 acked,
                /* We don't have enough rtt samples to do the Veno
                 * calculation, so we'll behave like Reno.
                 */
-               tcp_reno_cong_avoid(sk, ack, acked, in_flight);
+               tcp_reno_cong_avoid(sk, ack, acked);
        } else {
                u64 target_cwnd;
                u32 rtt;
index 5ede0e727945add71904a2d3c57d334e77e94baf..599b79b8eac07298a34cff43ae87e546bdb36847 100644 (file)
@@ -69,13 +69,12 @@ 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 acked,
-                               u32 in_flight)
+static void tcp_yeah_cong_avoid(struct sock *sk, u32 ack, u32 acked)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct yeah *yeah = inet_csk_ca(sk);
 
-       if (!tcp_is_cwnd_limited(sk, in_flight))
+       if (!tcp_is_cwnd_limited(sk))
                return;
 
        if (tp->snd_cwnd <= tp->snd_ssthresh)
index 4468e1adc094a1f6f12eb20cf7c79a9b9187826d..185ed3e598027a475b6ed62c4a0e9f9838793f4d 100644 (file)
@@ -246,7 +246,7 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum,
                        do {
                                if (low <= snum && snum <= high &&
                                    !test_bit(snum >> udptable->log, bitmap) &&
-                                   !inet_is_reserved_local_port(snum))
+                                   !inet_is_local_reserved_port(net, snum))
                                        goto found;
                                snum += rand;
                        } while (snum != first);
@@ -727,13 +727,12 @@ EXPORT_SYMBOL(udp_flush_pending_frames);
 void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst)
 {
        struct udphdr *uh = udp_hdr(skb);
-       struct sk_buff *frags = skb_shinfo(skb)->frag_list;
        int offset = skb_transport_offset(skb);
        int len = skb->len - offset;
        int hlen = len;
        __wsum csum = 0;
 
-       if (!frags) {
+       if (!skb_has_frag_list(skb)) {
                /*
                 * Only one fragment on the socket.
                 */
@@ -742,15 +741,17 @@ void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst)
                uh->check = ~csum_tcpudp_magic(src, dst, len,
                                               IPPROTO_UDP, 0);
        } else {
+               struct sk_buff *frags;
+
                /*
                 * HW-checksum won't work as there are two or more
                 * fragments on the socket so that all csums of sk_buffs
                 * should be together
                 */
-               do {
+               skb_walk_frags(skb, frags) {
                        csum = csum_add(csum, frags->csum);
                        hlen -= frags->len;
-               } while ((frags = frags->next));
+               }
 
                csum = skb_checksum(skb, offset, hlen, csum);
                skb->ip_summed = CHECKSUM_NONE;
@@ -762,6 +763,43 @@ void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst)
 }
 EXPORT_SYMBOL_GPL(udp4_hwcsum);
 
+/* Function to set UDP checksum for an IPv4 UDP packet. This is intended
+ * for the simple case like when setting the checksum for a UDP tunnel.
+ */
+void udp_set_csum(bool nocheck, struct sk_buff *skb,
+                 __be32 saddr, __be32 daddr, int len)
+{
+       struct udphdr *uh = udp_hdr(skb);
+
+       if (nocheck)
+               uh->check = 0;
+       else if (skb_is_gso(skb))
+               uh->check = ~udp_v4_check(len, saddr, daddr, 0);
+       else if (skb_dst(skb) && skb_dst(skb)->dev &&
+                (skb_dst(skb)->dev->features & NETIF_F_V4_CSUM)) {
+
+               BUG_ON(skb->ip_summed == CHECKSUM_PARTIAL);
+
+               skb->ip_summed = CHECKSUM_PARTIAL;
+               skb->csum_start = skb_transport_header(skb) - skb->head;
+               skb->csum_offset = offsetof(struct udphdr, check);
+               uh->check = ~udp_v4_check(len, saddr, daddr, 0);
+       } else {
+               __wsum csum;
+
+               BUG_ON(skb->ip_summed == CHECKSUM_PARTIAL);
+
+               uh->check = 0;
+               csum = skb_checksum(skb, 0, len, 0);
+               uh->check = udp_v4_check(len, saddr, daddr, csum);
+               if (uh->check == 0)
+                       uh->check = CSUM_MANGLED_0;
+
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+       }
+}
+EXPORT_SYMBOL(udp_set_csum);
+
 static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4)
 {
        struct sock *sk = skb->sk;
@@ -785,7 +823,7 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4)
        if (is_udplite)                                  /*     UDP-Lite      */
                csum = udplite_csum(skb);
 
-       else if (sk->sk_no_check == UDP_CSUM_NOXMIT) {   /* UDP csum disabled */
+       else if (sk->sk_no_check_tx) {   /* UDP csum disabled */
 
                skb->ip_summed = CHECKSUM_NONE;
                goto send;
@@ -1495,6 +1533,10 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
                if (skb->len > sizeof(struct udphdr) && encap_rcv != NULL) {
                        int ret;
 
+                       /* Verify checksum before giving to encap */
+                       if (udp_lib_checksum_complete(skb))
+                               goto csum_error;
+
                        ret = encap_rcv(sk, skb);
                        if (ret <= 0) {
                                UDP_INC_STATS_BH(sock_net(sk),
@@ -1672,7 +1714,6 @@ static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
 static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh,
                                 int proto)
 {
-       const struct iphdr *iph;
        int err;
 
        UDP_SKB_CB(skb)->partial_cov = 0;
@@ -1684,22 +1725,8 @@ static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh,
                        return err;
        }
 
-       iph = ip_hdr(skb);
-       if (uh->check == 0) {
-               skb->ip_summed = CHECKSUM_UNNECESSARY;
-       } else if (skb->ip_summed == CHECKSUM_COMPLETE) {
-               if (!csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len,
-                                     proto, skb->csum))
-                       skb->ip_summed = CHECKSUM_UNNECESSARY;
-       }
-       if (!skb_csum_unnecessary(skb))
-               skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr,
-                                              skb->len, proto, 0);
-       /* Probably, we should checksum udp header (it should be in cache
-        * in any case) and data in tiny packets (< rx copybreak).
-        */
-
-       return 0;
+       return skb_checksum_init_zero_check(skb, proto, uh->check,
+                                           inet_compute_pseudo);
 }
 
 /*
@@ -1886,7 +1913,7 @@ static struct sock *__udp4_lib_demux_lookup(struct net *net,
        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)
+       INET_ADDR_COOKIE(acookie, rmt_addr, loc_addr);
        const __portpair ports = INET_COMBINED_PORTS(rmt_port, hnum);
 
        rcu_read_lock();
@@ -1979,7 +2006,7 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
                       int (*push_pending_frames)(struct sock *))
 {
        struct udp_sock *up = udp_sk(sk);
-       int val;
+       int val, valbool;
        int err = 0;
        int is_udplite = IS_UDPLITE(sk);
 
@@ -1989,6 +2016,8 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
        if (get_user(val, (int __user *)optval))
                return -EFAULT;
 
+       valbool = val ? 1 : 0;
+
        switch (optname) {
        case UDP_CORK:
                if (val != 0) {
@@ -2018,6 +2047,14 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
                }
                break;
 
+       case UDP_NO_CHECK6_TX:
+               up->no_check6_tx = valbool;
+               break;
+
+       case UDP_NO_CHECK6_RX:
+               up->no_check6_rx = valbool;
+               break;
+
        /*
         *      UDP-Lite's partial checksum coverage (RFC 3828).
         */
@@ -2100,6 +2137,14 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname,
                val = up->encap_type;
                break;
 
+       case UDP_NO_CHECK6_TX:
+               val = up->no_check6_tx;
+               break;
+
+       case UDP_NO_CHECK6_RX:
+               val = up->no_check6_rx;
+               break;
+
        /* The following two cannot be changed on UDP sockets, the return is
         * always 0 (which corresponds to the full checksum coverage of UDP). */
        case UDPLITE_SEND_CSCOV:
@@ -2484,7 +2529,11 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
        int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb);
        __be16 protocol = skb->protocol;
        netdev_features_t enc_features;
-       int outer_hlen;
+       int udp_offset, outer_hlen;
+       unsigned int oldlen;
+       bool need_csum;
+
+       oldlen = (u16)~skb->len;
 
        if (unlikely(!pskb_may_pull(skb, tnl_hlen)))
                goto out;
@@ -2496,6 +2545,10 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
        skb->mac_len = skb_inner_network_offset(skb);
        skb->protocol = htons(ETH_P_TEB);
 
+       need_csum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM);
+       if (need_csum)
+               skb->encap_hdr_csum = 1;
+
        /* segment inner packet. */
        enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
        segs = skb_mac_gso_segment(skb, enc_features);
@@ -2506,10 +2559,11 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
        }
 
        outer_hlen = skb_tnl_header_len(skb);
+       udp_offset = outer_hlen - tnl_hlen;
        skb = segs;
        do {
                struct udphdr *uh;
-               int udp_offset = outer_hlen - tnl_hlen;
+               int len;
 
                skb_reset_inner_headers(skb);
                skb->encapsulation = 1;
@@ -2520,31 +2574,20 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
                skb_reset_mac_header(skb);
                skb_set_network_header(skb, mac_len);
                skb_set_transport_header(skb, udp_offset);
+               len = skb->len - udp_offset;
                uh = udp_hdr(skb);
-               uh->len = htons(skb->len - udp_offset);
-
-               /* csum segment if tunnel sets skb with csum. */
-               if (protocol == htons(ETH_P_IP) && unlikely(uh->check)) {
-                       struct iphdr *iph = ip_hdr(skb);
+               uh->len = htons(len);
 
-                       uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
-                                                      skb->len - udp_offset,
-                                                      IPPROTO_UDP, 0);
-                       uh->check = csum_fold(skb_checksum(skb, udp_offset,
-                                                          skb->len - udp_offset, 0));
-                       if (uh->check == 0)
-                               uh->check = CSUM_MANGLED_0;
+               if (need_csum) {
+                       __be32 delta = htonl(oldlen + len);
 
-               } else if (protocol == htons(ETH_P_IPV6)) {
-                       struct ipv6hdr *ipv6h = ipv6_hdr(skb);
-                       u32 len = skb->len - udp_offset;
+                       uh->check = ~csum_fold((__force __wsum)
+                                              ((__force u32)uh->check +
+                                               (__force u32)delta));
+                       uh->check = gso_make_checksum(skb, ~uh->check);
 
-                       uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
-                                                    len, IPPROTO_UDP, 0);
-                       uh->check = csum_fold(skb_checksum(skb, udp_offset, len, 0));
                        if (uh->check == 0)
                                uh->check = CSUM_MANGLED_0;
-                       skb->ip_summed = CHECKSUM_NONE;
                }
 
                skb->protocol = protocol;
index 88b4023ecfcfc85df907ff7472354084b3b16264..546d2d439dda65a195f7e635b7dc564def1da077 100644 (file)
@@ -56,7 +56,8 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
        __wsum csum;
 
        if (skb->encapsulation &&
-           skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL) {
+           (skb_shinfo(skb)->gso_type &
+            (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM))) {
                segs = skb_udp_tunnel_segment(skb, features);
                goto out;
        }
@@ -71,8 +72,10 @@ 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_UDP_TUNNEL_CSUM |
                                      SKB_GSO_IPIP |
-                                     SKB_GSO_GRE | SKB_GSO_MPLS) ||
+                                     SKB_GSO_GRE | SKB_GSO_GRE_CSUM |
+                                     SKB_GSO_MPLS) ||
                             !(type & (SKB_GSO_UDP))))
                        goto out;
 
@@ -197,6 +200,7 @@ static struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *s
        }
 
        skb_gro_pull(skb, sizeof(struct udphdr)); /* pull encapsulating udp header */
+       skb_gro_postpull_rcsum(skb, uh, sizeof(struct udphdr));
        pp = uo_priv->offload->callbacks.gro_receive(head, skb);
 
 out_unlock:
index 2c46acd4cc3636e33fb240a963367ec629b482b5..3b3efbda48e13941b35388238508706844c043aa 100644 (file)
@@ -70,7 +70,6 @@ static struct inet_protosw udplite4_protosw = {
        .protocol       =  IPPROTO_UDPLITE,
        .prot           =  &udplite_prot,
        .ops            =  &inet_dgram_ops,
-       .no_check       =  0,           /* must checksum (RFC 3828) */
        .flags          =  INET_PROTOSW_PERMANENT,
 };
 
index 05f2b484954feda957d04ff2f0300eedf9c97263..91771a7c802f828c33429413674c5f78ca393b6f 100644 (file)
@@ -58,12 +58,12 @@ static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
 
        top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ?
                0 : (XFRM_MODE_SKB_CB(skb)->frag_off & htons(IP_DF));
-       ip_select_ident(skb, dst->child, NULL);
 
        top_iph->ttl = ip4_dst_hoplimit(dst->child);
 
        top_iph->saddr = x->props.saddr.a4;
        top_iph->daddr = x->id.daddr.a4;
+       ip_select_ident(skb, NULL);
 
        return 0;
 }
index 186a8ecf92fa84bda9d0f6b050131da603c7694a..d5f6bd9a210ab93ed27177ecffc9499259dbe999 100644 (file)
@@ -25,7 +25,7 @@ static int xfrm4_tunnel_check_size(struct sk_buff *skb)
        if (IPCB(skb)->flags & IPSKB_XFRM_TUNNEL_SIZE)
                goto out;
 
-       if (!(ip_hdr(skb)->frag_off & htons(IP_DF)) || skb->local_df)
+       if (!(ip_hdr(skb)->frag_off & htons(IP_DF)) || skb->ignore_df)
                goto out;
 
        mtu = dst_mtu(skb_dst(skb));
index 6c7fa0853fc74ef179b00de52d78aecee342e18b..5667b3003af9b51779ff322717e999282113c4b7 100644 (file)
@@ -275,19 +275,14 @@ static int snmp6_alloc_dev(struct inet6_dev *idev)
 {
        int i;
 
-       if (snmp_mib_init((void __percpu **)idev->stats.ipv6,
-                         sizeof(struct ipstats_mib),
-                         __alignof__(struct ipstats_mib)) < 0)
+       idev->stats.ipv6 = alloc_percpu(struct ipstats_mib);
+       if (!idev->stats.ipv6)
                goto err_ip;
 
        for_each_possible_cpu(i) {
                struct ipstats_mib *addrconf_stats;
-               addrconf_stats = per_cpu_ptr(idev->stats.ipv6[0], i);
+               addrconf_stats = per_cpu_ptr(idev->stats.ipv6, i);
                u64_stats_init(&addrconf_stats->syncp);
-#if SNMP_ARRAY_SZ == 2
-               addrconf_stats = per_cpu_ptr(idev->stats.ipv6[1], i);
-               u64_stats_init(&addrconf_stats->syncp);
-#endif
        }
 
 
@@ -305,7 +300,7 @@ static int snmp6_alloc_dev(struct inet6_dev *idev)
 err_icmpmsg:
        kfree(idev->stats.icmpv6dev);
 err_icmp:
-       snmp_mib_free((void __percpu **)idev->stats.ipv6);
+       free_percpu(idev->stats.ipv6);
 err_ip:
        return -ENOMEM;
 }
@@ -2504,8 +2499,8 @@ static int inet6_addr_add(struct net *net, int ifindex,
        return PTR_ERR(ifp);
 }
 
-static int inet6_addr_del(struct net *net, int ifindex, const struct in6_addr *pfx,
-                         unsigned int plen)
+static int inet6_addr_del(struct net *net, int ifindex, u32 ifa_flags,
+                         const struct in6_addr *pfx, unsigned int plen)
 {
        struct inet6_ifaddr *ifp;
        struct inet6_dev *idev;
@@ -2528,7 +2523,12 @@ static int inet6_addr_del(struct net *net, int ifindex, const struct in6_addr *p
                        in6_ifa_hold(ifp);
                        read_unlock_bh(&idev->lock);
 
+                       if (!(ifp->flags & IFA_F_TEMPORARY) &&
+                           (ifa_flags & IFA_F_MANAGETEMPADDR))
+                               manage_tempaddrs(idev, ifp, 0, 0, false,
+                                                jiffies);
                        ipv6_del_addr(ifp);
+                       addrconf_verify_rtnl();
                        return 0;
                }
        }
@@ -2568,7 +2568,7 @@ int addrconf_del_ifaddr(struct net *net, void __user *arg)
                return -EFAULT;
 
        rtnl_lock();
-       err = inet6_addr_del(net, ireq.ifr6_ifindex, &ireq.ifr6_addr,
+       err = inet6_addr_del(net, ireq.ifr6_ifindex, 0, &ireq.ifr6_addr,
                             ireq.ifr6_prefixlen);
        rtnl_unlock();
        return err;
@@ -2813,18 +2813,6 @@ static void addrconf_gre_config(struct net_device *dev)
 }
 #endif
 
-static inline int
-ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev)
-{
-       struct in6_addr lladdr;
-
-       if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) {
-               addrconf_add_linklocal(idev, &lladdr);
-               return 0;
-       }
-       return -1;
-}
-
 static int addrconf_notify(struct notifier_block *this, unsigned long event,
                           void *ptr)
 {
@@ -3743,6 +3731,7 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
        struct ifaddrmsg *ifm;
        struct nlattr *tb[IFA_MAX+1];
        struct in6_addr *pfx, *peer_pfx;
+       u32 ifa_flags;
        int err;
 
        err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy);
@@ -3754,7 +3743,13 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
        if (pfx == NULL)
                return -EINVAL;
 
-       return inet6_addr_del(net, ifm->ifa_index, pfx, ifm->ifa_prefixlen);
+       ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : ifm->ifa_flags;
+
+       /* We ignore other flags so far. */
+       ifa_flags &= IFA_F_MANAGETEMPADDR;
+
+       return inet6_addr_del(net, ifm->ifa_index, ifa_flags, pfx,
+                             ifm->ifa_prefixlen);
 }
 
 static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 ifa_flags,
@@ -4363,7 +4358,7 @@ static inline void __snmp6_fill_statsdev(u64 *stats, atomic_long_t *mib,
        memset(&stats[items], 0, pad);
 }
 
-static inline void __snmp6_fill_stats64(u64 *stats, void __percpu **mib,
+static inline void __snmp6_fill_stats64(u64 *stats, void __percpu *mib,
                                      int items, int bytes, size_t syncpoff)
 {
        int i;
@@ -4383,7 +4378,7 @@ static void snmp6_fill_stats(u64 *stats, struct inet6_dev *idev, int attrtype,
 {
        switch (attrtype) {
        case IFLA_INET6_STATS:
-               __snmp6_fill_stats64(stats, (void __percpu **)idev->stats.ipv6,
+               __snmp6_fill_stats64(stats, idev->stats.ipv6,
                                     IPSTATS_MIB_MAX, bytes, offsetof(struct ipstats_mib, syncp));
                break;
        case IFLA_INET6_ICMP6STATS:
index 4c11cbcf83089152052b60072dbe11d147d10803..e6960457f62582c4ca41c674cc531d64ca76b038 100644 (file)
@@ -123,7 +123,7 @@ static void snmp6_free_dev(struct inet6_dev *idev)
 {
        kfree(idev->stats.icmpv6msgdev);
        kfree(idev->stats.icmpv6dev);
-       snmp_mib_free((void __percpu **)idev->stats.ipv6);
+       free_percpu(idev->stats.ipv6);
 }
 
 /* Nobody refers to this device, we may destroy it. */
index d935889f1008ae93ff1efe52badeedf41352f257..7cb4392690dd614b1672ad51cc57bbe36218e34c 100644 (file)
@@ -106,7 +106,6 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol,
        struct inet_protosw *answer;
        struct proto *answer_prot;
        unsigned char answer_flags;
-       char answer_no_check;
        int try_loading_module = 0;
        int err;
 
@@ -162,7 +161,6 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol,
 
        sock->ops = answer->ops;
        answer_prot = answer->prot;
-       answer_no_check = answer->no_check;
        answer_flags = answer->flags;
        rcu_read_unlock();
 
@@ -176,7 +174,6 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol,
        sock_init_data(sock, sk);
 
        err = 0;
-       sk->sk_no_check = answer_no_check;
        if (INET_PROTOSW_REUSE & answer_flags)
                sk->sk_reuse = SK_CAN_REUSE;
 
@@ -715,33 +712,25 @@ static int __net_init ipv6_init_mibs(struct net *net)
 {
        int i;
 
-       if (snmp_mib_init((void __percpu **)net->mib.udp_stats_in6,
-                         sizeof(struct udp_mib),
-                         __alignof__(struct udp_mib)) < 0)
+       net->mib.udp_stats_in6 = alloc_percpu(struct udp_mib);
+       if (!net->mib.udp_stats_in6)
                return -ENOMEM;
-       if (snmp_mib_init((void __percpu **)net->mib.udplite_stats_in6,
-                         sizeof(struct udp_mib),
-                         __alignof__(struct udp_mib)) < 0)
+       net->mib.udplite_stats_in6 = alloc_percpu(struct udp_mib);
+       if (!net->mib.udplite_stats_in6)
                goto err_udplite_mib;
-       if (snmp_mib_init((void __percpu **)net->mib.ipv6_statistics,
-                         sizeof(struct ipstats_mib),
-                         __alignof__(struct ipstats_mib)) < 0)
+       net->mib.ipv6_statistics = alloc_percpu(struct ipstats_mib);
+       if (!net->mib.ipv6_statistics)
                goto err_ip_mib;
 
        for_each_possible_cpu(i) {
                struct ipstats_mib *af_inet6_stats;
-               af_inet6_stats = per_cpu_ptr(net->mib.ipv6_statistics[0], i);
+               af_inet6_stats = per_cpu_ptr(net->mib.ipv6_statistics, i);
                u64_stats_init(&af_inet6_stats->syncp);
-#if SNMP_ARRAY_SZ == 2
-               af_inet6_stats = per_cpu_ptr(net->mib.ipv6_statistics[1], i);
-               u64_stats_init(&af_inet6_stats->syncp);
-#endif
        }
 
 
-       if (snmp_mib_init((void __percpu **)net->mib.icmpv6_statistics,
-                         sizeof(struct icmpv6_mib),
-                         __alignof__(struct icmpv6_mib)) < 0)
+       net->mib.icmpv6_statistics = alloc_percpu(struct icmpv6_mib);
+       if (!net->mib.icmpv6_statistics)
                goto err_icmp_mib;
        net->mib.icmpv6msg_statistics = kzalloc(sizeof(struct icmpv6msg_mib),
                                                GFP_KERNEL);
@@ -750,22 +739,22 @@ static int __net_init ipv6_init_mibs(struct net *net)
        return 0;
 
 err_icmpmsg_mib:
-       snmp_mib_free((void __percpu **)net->mib.icmpv6_statistics);
+       free_percpu(net->mib.icmpv6_statistics);
 err_icmp_mib:
-       snmp_mib_free((void __percpu **)net->mib.ipv6_statistics);
+       free_percpu(net->mib.ipv6_statistics);
 err_ip_mib:
-       snmp_mib_free((void __percpu **)net->mib.udplite_stats_in6);
+       free_percpu(net->mib.udplite_stats_in6);
 err_udplite_mib:
-       snmp_mib_free((void __percpu **)net->mib.udp_stats_in6);
+       free_percpu(net->mib.udp_stats_in6);
        return -ENOMEM;
 }
 
 static void ipv6_cleanup_mibs(struct net *net)
 {
-       snmp_mib_free((void __percpu **)net->mib.udp_stats_in6);
-       snmp_mib_free((void __percpu **)net->mib.udplite_stats_in6);
-       snmp_mib_free((void __percpu **)net->mib.ipv6_statistics);
-       snmp_mib_free((void __percpu **)net->mib.icmpv6_statistics);
+       free_percpu(net->mib.udp_stats_in6);
+       free_percpu(net->mib.udplite_stats_in6);
+       free_percpu(net->mib.ipv6_statistics);
+       free_percpu(net->mib.icmpv6_statistics);
        kfree(net->mib.icmpv6msg_statistics);
 }
 
index 7b326529e6a2cba57695697cfff436357c347b2c..f6c84a6eb2389c55f4abd55fefbd073c73743a2a 100644 (file)
@@ -400,6 +400,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
        int len;
        int hlimit;
        int err = 0;
+       u32 mark = IP6_REPLY_MARK(net, skb->mark);
 
        if ((u8 *)hdr < skb->head ||
            (skb_network_header(skb) + sizeof(*hdr)) > skb_tail_pointer(skb))
@@ -466,6 +467,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
        fl6.daddr = hdr->saddr;
        if (saddr)
                fl6.saddr = *saddr;
+       fl6.flowi6_mark = mark;
        fl6.flowi6_oif = iif;
        fl6.fl6_icmp_type = type;
        fl6.fl6_icmp_code = code;
@@ -474,6 +476,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
        sk = icmpv6_xmit_lock(net);
        if (sk == NULL)
                return;
+       sk->sk_mark = mark;
        np = inet6_sk(sk);
 
        if (!icmpv6_xrlim_allow(sk, type, &fl6))
@@ -493,12 +496,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
        if (IS_ERR(dst))
                goto out;
 
-       if (ipv6_addr_is_multicast(&fl6.daddr))
-               hlimit = np->mcast_hops;
-       else
-               hlimit = np->hop_limit;
-       if (hlimit < 0)
-               hlimit = ip6_dst_hoplimit(dst);
+       hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
 
        msg.skb = skb;
        msg.offset = skb_network_offset(skb);
@@ -556,6 +554,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
        int err = 0;
        int hlimit;
        u8 tclass;
+       u32 mark = IP6_REPLY_MARK(net, skb->mark);
 
        saddr = &ipv6_hdr(skb)->daddr;
 
@@ -574,11 +573,13 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
                fl6.saddr = *saddr;
        fl6.flowi6_oif = skb->dev->ifindex;
        fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY;
+       fl6.flowi6_mark = mark;
        security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
 
        sk = icmpv6_xmit_lock(net);
        if (sk == NULL)
                return;
+       sk->sk_mark = mark;
        np = inet6_sk(sk);
 
        if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
@@ -593,12 +594,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
        if (IS_ERR(dst))
                goto out;
 
-       if (ipv6_addr_is_multicast(&fl6.daddr))
-               hlimit = np->mcast_hops;
-       else
-               hlimit = np->hop_limit;
-       if (hlimit < 0)
-               hlimit = ip6_dst_hoplimit(dst);
+       hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
 
        idev = __in6_dev_get(skb->dev);
 
@@ -702,22 +698,11 @@ static int icmpv6_rcv(struct sk_buff *skb)
        saddr = &ipv6_hdr(skb)->saddr;
        daddr = &ipv6_hdr(skb)->daddr;
 
-       /* Perform checksum. */
-       switch (skb->ip_summed) {
-       case CHECKSUM_COMPLETE:
-               if (!csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_ICMPV6,
-                                    skb->csum))
-                       break;
-               /* fall through */
-       case CHECKSUM_NONE:
-               skb->csum = ~csum_unfold(csum_ipv6_magic(saddr, daddr, skb->len,
-                                            IPPROTO_ICMPV6, 0));
-               if (__skb_checksum_complete(skb)) {
-                       LIMIT_NETDEBUG(KERN_DEBUG
-                                      "ICMPv6 checksum failed [%pI6c > %pI6c]\n",
-                                      saddr, daddr);
-                       goto csum_error;
-               }
+       if (skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo)) {
+               LIMIT_NETDEBUG(KERN_DEBUG
+                              "ICMPv6 checksum failed [%pI6c > %pI6c]\n",
+                              saddr, daddr);
+               goto csum_error;
        }
 
        if (!pskb_pull(skb, sizeof(*hdr)))
index d4ade34ab37566d8cca9e164f5fde5fb5a762fe6..a245e5ddffbd0450968c44de7d3fcd8a1dd055cf 100644 (file)
@@ -81,7 +81,7 @@ struct dst_entry *inet6_csk_route_req(struct sock *sk,
        final_p = fl6_update_dst(fl6, np->opt, &final);
        fl6->saddr = ireq->ir_v6_loc_addr;
        fl6->flowi6_oif = ireq->ir_iif;
-       fl6->flowi6_mark = sk->sk_mark;
+       fl6->flowi6_mark = ireq->ir_mark;
        fl6->fl6_dport = ireq->ir_rmt_port;
        fl6->fl6_sport = htons(ireq->ir_num);
        security_req_classify_flow(req, flowi6_to_flowi(fl6));
index ee7a97f510cbd9f94fa24eafa43ddba201c75f3e..9a4d7322fb22234c5d697c384a74949beb2e50c5 100644 (file)
@@ -75,25 +75,50 @@ int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto)
                        return err;
        }
 
-       if (uh->check == 0) {
-               /* RFC 2460 section 8.1 says that we SHOULD log
-                  this error. Well, it is reasonable.
-                */
-               LIMIT_NETDEBUG(KERN_INFO "IPv6: udp checksum is 0 for [%pI6c]:%u->[%pI6c]:%u\n",
-                              &ipv6_hdr(skb)->saddr, ntohs(uh->source),
-                              &ipv6_hdr(skb)->daddr, ntohs(uh->dest));
-               return 1;
-       }
-       if (skb->ip_summed == CHECKSUM_COMPLETE &&
-           !csum_ipv6_magic(&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr,
-                            skb->len, proto, skb->csum))
-               skb->ip_summed = CHECKSUM_UNNECESSARY;
+       /* To support RFC 6936 (allow zero checksum in UDP/IPV6 for tunnels)
+        * we accept a checksum of zero here. When we find the socket
+        * for the UDP packet we'll check if that socket allows zero checksum
+        * for IPv6 (set by socket option).
+        */
+       return skb_checksum_init_zero_check(skb, proto, uh->check,
+                                          ip6_compute_pseudo);
+}
+EXPORT_SYMBOL(udp6_csum_init);
+
+/* Function to set UDP checksum for an IPv6 UDP packet. This is intended
+ * for the simple case like when setting the checksum for a UDP tunnel.
+ */
+void udp6_set_csum(bool nocheck, struct sk_buff *skb,
+                  const struct in6_addr *saddr,
+                  const struct in6_addr *daddr, int len)
+{
+       struct udphdr *uh = udp_hdr(skb);
+
+       if (nocheck)
+               uh->check = 0;
+       else if (skb_is_gso(skb))
+               uh->check = ~udp_v6_check(len, saddr, daddr, 0);
+       else if (skb_dst(skb) && skb_dst(skb)->dev &&
+                (skb_dst(skb)->dev->features & NETIF_F_IPV6_CSUM)) {
 
-       if (!skb_csum_unnecessary(skb))
-               skb->csum = ~csum_unfold(csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
-                                                        &ipv6_hdr(skb)->daddr,
-                                                        skb->len, proto, 0));
+               BUG_ON(skb->ip_summed == CHECKSUM_PARTIAL);
 
-       return 0;
+               skb->ip_summed = CHECKSUM_PARTIAL;
+               skb->csum_start = skb_transport_header(skb) - skb->head;
+               skb->csum_offset = offsetof(struct udphdr, check);
+               uh->check = ~udp_v6_check(len, saddr, daddr, 0);
+       } else {
+               __wsum csum;
+
+               BUG_ON(skb->ip_summed == CHECKSUM_PARTIAL);
+
+               uh->check = 0;
+               csum = skb_checksum(skb, 0, len, 0);
+               uh->check = udp_v6_check(len, saddr, daddr, csum);
+               if (uh->check == 0)
+                       uh->check = CSUM_MANGLED_0;
+
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+       }
 }
-EXPORT_SYMBOL(udp6_csum_init);
+EXPORT_SYMBOL(udp6_set_csum);
index 87891f5f57b5786725321d2fc301941531064bf2..cb4459bd1d294d1901cc03b9c203fbcbf761f53f 100644 (file)
@@ -71,8 +71,7 @@ static DEFINE_RWLOCK(fib6_walker_lock);
 #define FWS_INIT FWS_L
 #endif
 
-static void fib6_prune_clones(struct net *net, struct fib6_node *fn,
-                             struct rt6_info *rt);
+static void fib6_prune_clones(struct net *net, struct fib6_node *fn);
 static struct rt6_info *fib6_find_prefix(struct net *net, struct fib6_node *fn);
 static struct fib6_node *fib6_repair_tree(struct net *net, struct fib6_node *fn);
 static int fib6_walk(struct fib6_walker_t *w);
@@ -941,7 +940,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info,
        if (!err) {
                fib6_start_gc(info->nl_net, rt);
                if (!(rt->rt6i_flags & RTF_CACHE))
-                       fib6_prune_clones(info->nl_net, pn, rt);
+                       fib6_prune_clones(info->nl_net, pn);
        }
 
 out:
@@ -1375,7 +1374,7 @@ int fib6_del(struct rt6_info *rt, struct nl_info *info)
                        pn = pn->parent;
                }
 #endif
-               fib6_prune_clones(info->nl_net, pn, rt);
+               fib6_prune_clones(info->nl_net, pn);
        }
 
        /*
@@ -1601,10 +1600,9 @@ static int fib6_prune_clone(struct rt6_info *rt, void *arg)
        return 0;
 }
 
-static void fib6_prune_clones(struct net *net, struct fib6_node *fn,
-                             struct rt6_info *rt)
+static void fib6_prune_clones(struct net *net, struct fib6_node *fn)
 {
-       fib6_clean_tree(net, fn, fib6_prune_clone, 1, rt);
+       fib6_clean_tree(net, fn, fib6_prune_clone, 1, NULL);
 }
 
 /*
index 0961b5ef866d04803cf91243aec32bb9e2ea8cf7..4052694c6f2cb196b54ef7d5235d61d74e4ea69c 100644 (file)
@@ -26,7 +26,6 @@
 #include <net/sock.h>
 
 #include <net/ipv6.h>
-#include <net/addrconf.h>
 #include <net/rawv6.h>
 #include <net/transp_v6.h>
 
index 9d921462b57f293f9f49f6ec78936c84a3f756a0..3873181ed85614a28f9857d7d53acf2be9d2b9fb 100644 (file)
@@ -72,6 +72,7 @@ struct ip6gre_net {
 };
 
 static struct rtnl_link_ops ip6gre_link_ops __read_mostly;
+static struct rtnl_link_ops ip6gre_tap_ops __read_mostly;
 static int ip6gre_tunnel_init(struct net_device *dev);
 static void ip6gre_tunnel_setup(struct net_device *dev);
 static void ip6gre_tunnel_link(struct ip6gre_net *ign, struct ip6_tnl *t);
@@ -353,10 +354,10 @@ static struct ip6_tnl *ip6gre_tunnel_locate(struct net *net,
 
 static void ip6gre_tunnel_uninit(struct net_device *dev)
 {
-       struct net *net = dev_net(dev);
-       struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
+       struct ip6_tnl *t = netdev_priv(dev);
+       struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id);
 
-       ip6gre_tunnel_unlink(ign, netdev_priv(dev));
+       ip6gre_tunnel_unlink(ign, t);
        dev_put(dev);
 }
 
@@ -467,17 +468,7 @@ static int ip6gre_rcv(struct sk_buff *skb)
                        goto drop;
 
                if (flags&GRE_CSUM) {
-                       switch (skb->ip_summed) {
-                       case CHECKSUM_COMPLETE:
-                               csum = csum_fold(skb->csum);
-                               if (!csum)
-                                       break;
-                               /* fall through */
-                       case CHECKSUM_NONE:
-                               skb->csum = 0;
-                               csum = __skb_checksum_complete(skb);
-                               skb->ip_summed = CHECKSUM_COMPLETE;
-                       }
+                       csum = skb_checksum_simple_validate(skb);
                        offset += 4;
                }
                if (flags&GRE_KEY) {
@@ -611,8 +602,8 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb,
                         int encap_limit,
                         __u32 *pmtu)
 {
-       struct net *net = dev_net(dev);
        struct ip6_tnl *tunnel = netdev_priv(dev);
+       struct net *net = tunnel->net;
        struct net_device *tdev;    /* Device to other host */
        struct ipv6hdr  *ipv6h;     /* Our new IP header */
        unsigned int max_headroom = 0; /* The extra header space needed */
@@ -979,7 +970,7 @@ static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)
                int strict = (ipv6_addr_type(&p->raddr) &
                              (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL));
 
-               struct rt6_info *rt = rt6_lookup(dev_net(dev),
+               struct rt6_info *rt = rt6_lookup(t->net,
                                                 &p->raddr, &p->laddr,
                                                 p->link, strict);
 
@@ -1063,13 +1054,12 @@ static int ip6gre_tunnel_ioctl(struct net_device *dev,
        int err = 0;
        struct ip6_tnl_parm2 p;
        struct __ip6_tnl_parm p1;
-       struct ip6_tnl *t;
-       struct net *net = dev_net(dev);
+       struct ip6_tnl *t = netdev_priv(dev);
+       struct net *net = t->net;
        struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
 
        switch (cmd) {
        case SIOCGETTUNNEL:
-               t = NULL;
                if (dev == ign->fb_tunnel_dev) {
                        if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) {
                                err = -EFAULT;
@@ -1077,9 +1067,9 @@ static int ip6gre_tunnel_ioctl(struct net_device *dev,
                        }
                        ip6gre_tnl_parm_from_user(&p1, &p);
                        t = ip6gre_tunnel_locate(net, &p1, 0);
+                       if (t == NULL)
+                               t = netdev_priv(dev);
                }
-               if (t == NULL)
-                       t = netdev_priv(dev);
                memset(&p, 0, sizeof(p));
                ip6gre_tnl_parm_to_user(&p, &t->parms);
                if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
@@ -1242,7 +1232,6 @@ static void ip6gre_tunnel_setup(struct net_device *dev)
        dev->flags |= IFF_NOARP;
        dev->iflink = 0;
        dev->addr_len = sizeof(struct in6_addr);
-       dev->features |= NETIF_F_NETNS_LOCAL;
        dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
 }
 
@@ -1297,11 +1286,17 @@ static struct inet6_protocol ip6gre_protocol __read_mostly = {
        .flags       = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
 };
 
-static void ip6gre_destroy_tunnels(struct ip6gre_net *ign,
-       struct list_head *head)
+static void ip6gre_destroy_tunnels(struct net *net, struct list_head *head)
 {
+       struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
+       struct net_device *dev, *aux;
        int prio;
 
+       for_each_netdev_safe(net, dev, aux)
+               if (dev->rtnl_link_ops == &ip6gre_link_ops ||
+                   dev->rtnl_link_ops == &ip6gre_tap_ops)
+                       unregister_netdevice_queue(dev, head);
+
        for (prio = 0; prio < 4; prio++) {
                int h;
                for (h = 0; h < HASH_SIZE; h++) {
@@ -1310,7 +1305,12 @@ static void ip6gre_destroy_tunnels(struct ip6gre_net *ign,
                        t = rtnl_dereference(ign->tunnels[prio][h]);
 
                        while (t != NULL) {
-                               unregister_netdevice_queue(t->dev, head);
+                               /* If dev is in the same netns, it has already
+                                * been added to the list by the previous loop.
+                                */
+                               if (!net_eq(dev_net(t->dev), net))
+                                       unregister_netdevice_queue(t->dev,
+                                                                  head);
                                t = rtnl_dereference(t->next);
                        }
                }
@@ -1329,6 +1329,11 @@ static int __net_init ip6gre_init_net(struct net *net)
                goto err_alloc_dev;
        }
        dev_net_set(ign->fb_tunnel_dev, net);
+       /* FB netdevice is special: we have one, and only one per netns.
+        * Allowing to move it to another netns is clearly unsafe.
+        */
+       ign->fb_tunnel_dev->features |= NETIF_F_NETNS_LOCAL;
+
 
        ip6gre_fb_tunnel_init(ign->fb_tunnel_dev);
        ign->fb_tunnel_dev->rtnl_link_ops = &ip6gre_link_ops;
@@ -1349,12 +1354,10 @@ static int __net_init ip6gre_init_net(struct net *net)
 
 static void __net_exit ip6gre_exit_net(struct net *net)
 {
-       struct ip6gre_net *ign;
        LIST_HEAD(list);
 
-       ign = net_generic(net, ip6gre_net_id);
        rtnl_lock();
-       ip6gre_destroy_tunnels(ign, &list);
+       ip6gre_destroy_tunnels(net, &list);
        unregister_netdevice_many(&list);
        rtnl_unlock();
 }
@@ -1531,15 +1534,14 @@ static int ip6gre_newlink(struct net *src_net, struct net_device *dev,
 static int ip6gre_changelink(struct net_device *dev, struct nlattr *tb[],
                            struct nlattr *data[])
 {
-       struct ip6_tnl *t, *nt;
-       struct net *net = dev_net(dev);
+       struct ip6_tnl *t, *nt = netdev_priv(dev);
+       struct net *net = nt->net;
        struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
        struct __ip6_tnl_parm p;
 
        if (dev == ign->fb_tunnel_dev)
                return -EINVAL;
 
-       nt = netdev_priv(dev);
        ip6gre_netlink_parms(data, &p);
 
        t = ip6gre_tunnel_locate(net, &p, 0);
index b2f091566f88453bce5eb3c33b47e1c9c040447c..65eda2a8af48280e0c068f24d8ef7adc8c7d8c9e 100644 (file)
@@ -97,9 +97,11 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
                       SKB_GSO_DODGY |
                       SKB_GSO_TCP_ECN |
                       SKB_GSO_GRE |
+                      SKB_GSO_GRE_CSUM |
                       SKB_GSO_IPIP |
                       SKB_GSO_SIT |
                       SKB_GSO_UDP_TUNNEL |
+                      SKB_GSO_UDP_TUNNEL_CSUM |
                       SKB_GSO_MPLS |
                       SKB_GSO_TCPV6 |
                       0)))
index fbf11562b54c1a7c8809f33cc969c66319517377..cb9df0eb40237065e696dd1013180b6c82a9c2dc 100644 (file)
@@ -219,7 +219,7 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
        skb->mark = sk->sk_mark;
 
        mtu = dst_mtu(dst);
-       if ((skb->len <= mtu) || skb->local_df || skb_is_gso(skb)) {
+       if ((skb->len <= mtu) || skb->ignore_df || skb_is_gso(skb)) {
                IP6_UPD_PO_STATS(net, ip6_dst_idev(skb_dst(skb)),
                              IPSTATS_MIB_OUT, skb->len);
                return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL,
@@ -347,11 +347,11 @@ static bool ip6_pkt_too_big(const struct sk_buff *skb, unsigned int mtu)
        if (skb->len <= mtu)
                return false;
 
-       /* ipv6 conntrack defrag sets max_frag_size + local_df */
+       /* ipv6 conntrack defrag sets max_frag_size + ignore_df */
        if (IP6CB(skb)->frag_max_size && IP6CB(skb)->frag_max_size > mtu)
                return true;
 
-       if (skb->local_df)
+       if (skb->ignore_df)
                return false;
 
        if (skb_is_gso(skb) && skb_gso_network_seglen(skb) <= mtu)
@@ -537,6 +537,18 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from)
        skb_copy_secmark(to, from);
 }
 
+static void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt)
+{
+       static u32 ip6_idents_hashrnd __read_mostly;
+       u32 hash, id;
+
+       net_get_random_once(&ip6_idents_hashrnd, sizeof(ip6_idents_hashrnd));
+
+       hash = __ipv6_addr_jhash(&rt->rt6i_dst.addr, ip6_idents_hashrnd);
+       id = ip_idents_reserve(hash, 1);
+       fhdr->identification = htonl(id);
+}
+
 int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
 {
        struct sk_buff *frag;
@@ -559,7 +571,7 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
        /* We must not fragment if the socket is set to force MTU discovery
         * or if the skb it not generated by a local socket.
         */
-       if (unlikely(!skb->local_df && skb->len > mtu) ||
+       if (unlikely(!skb->ignore_df && skb->len > mtu) ||
                     (IP6CB(skb)->frag_max_size &&
                      IP6CB(skb)->frag_max_size > mtu)) {
                if (skb->sk && dst_allfrag(skb_dst(skb)))
@@ -1234,7 +1246,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
                              sizeof(struct frag_hdr) : 0) +
                             rt->rt6i_nfheader_len;
 
-               if (ip6_sk_local_df(sk))
+               if (ip6_sk_ignore_df(sk))
                        maxnonfragsize = sizeof(struct ipv6hdr) + IPV6_MAXPLEN;
                else
                        maxnonfragsize = mtu;
@@ -1544,7 +1556,7 @@ int ip6_push_pending_frames(struct sock *sk)
        }
 
        /* Allow local fragmentation. */
-       skb->local_df = ip6_sk_local_df(sk);
+       skb->ignore_df = ip6_sk_ignore_df(sk);
 
        *final_dst = fl6->daddr;
        __skb_pull(skb, skb_network_header_len(skb));
index f6a66bb4114db3af0f23ee1f950acf8b9720304d..afa082458360216ff33012ee43e93354888e44b9 100644 (file)
@@ -61,6 +61,7 @@
 MODULE_AUTHOR("Ville Nuorvala");
 MODULE_DESCRIPTION("IPv6 tunneling device");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS_RTNL_LINK("ip6tnl");
 MODULE_ALIAS_NETDEV("ip6tnl0");
 
 #ifdef IP6_TNL_DEBUG
index 6cc9f9371cc57cd6b0233815a73134dddfa6207c..9aaa6bb229e485fd657a5ca4bd30b6ebb9e90c5f 100644 (file)
@@ -795,15 +795,12 @@ static const struct net_device_ops vti6_netdev_ops = {
  **/
 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->priv_flags &= ~IFF_XMIT_DST_RELEASE;
index 84c7f33d0cf858115abdb5f359a658b574637d57..387d8b8fc18db9744426e6f8c258a71d3f3d06fb 100644 (file)
@@ -90,17 +90,9 @@ nf_nat_ipv6_fn(const struct nf_hook_ops *ops,
        if (nf_ct_is_untracked(ct))
                return NF_ACCEPT;
 
-       nat = nfct_nat(ct);
-       if (!nat) {
-               /* NAT module was loaded late. */
-               if (nf_ct_is_confirmed(ct))
-                       return NF_ACCEPT;
-               nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
-               if (nat == NULL) {
-                       pr_debug("failed to add NAT extension\n");
-                       return NF_ACCEPT;
-               }
-       }
+       nat = nf_ct_nat_ext_add(ct);
+       if (nat == NULL)
+               return NF_ACCEPT;
 
        switch (ctinfo) {
        case IP_CT_RELATED:
index 767ab8da82189479456c632ecec95eef67da4bae..0d5279fd852a48643b5b0b54834b2ee68a893116 100644 (file)
@@ -451,7 +451,7 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev)
        }
        sub_frag_mem_limit(&fq->q, head->truesize);
 
-       head->local_df = 1;
+       head->ignore_df = 1;
        head->next = NULL;
        head->dev = dev;
        head->tstamp = fq->q.stamp;
index 9c3297a768fd1f0bb0aae5f784f2482b03a8ce15..d189fcb437feb9de2f43d5217389c90e029dbd21 100644 (file)
@@ -47,15 +47,9 @@ static unsigned int nf_nat_ipv6_fn(const struct nf_hook_ops *ops,
        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;
-       }
+       nat = nf_ct_nat_ext_add(ct);
+       if (nat == NULL)
+               return NF_ACCEPT;
 
        switch (ctinfo) {
        case IP_CT_RELATED:
index 56596ce390a19783fa20819aeb5110195fb7d484..5ec867e4a8b74fb23d8fdd2bcf9e0dce52f94455 100644 (file)
@@ -8,32 +8,6 @@
 #include <net/addrconf.h>
 #include <net/secure_seq.h>
 
-void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt)
-{
-       static atomic_t ipv6_fragmentation_id;
-       struct in6_addr addr;
-       int ident;
-
-#if IS_ENABLED(CONFIG_IPV6)
-       struct inet_peer *peer;
-       struct net *net;
-
-       net = dev_net(rt->dst.dev);
-       peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1);
-       if (peer) {
-               fhdr->identification = htonl(inet_getid(peer, 0));
-               inet_putpeer(peer);
-               return;
-       }
-#endif
-       ident = atomic_inc_return(&ipv6_fragmentation_id);
-
-       addr = rt->rt6i_dst.addr;
-       addr.s6_addr32[0] ^= (__force __be32)ident;
-       fhdr->identification = htonl(secure_ipv6_id(addr.s6_addr32));
-}
-EXPORT_SYMBOL(ipv6_select_ident);
-
 int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
 {
        u16 offset = sizeof(struct ipv6hdr);
@@ -104,6 +78,7 @@ int __ip6_local_out(struct sk_buff *skb)
        if (len > IPV6_MAXPLEN)
                len = 0;
        ipv6_hdr(skb)->payload_len = htons(len);
+       IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
 
        return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL,
                       skb_dst(skb)->dev, dst_output);
index bda74291c3e0d09c94ff5961cf393cb7695ee518..5b7a1ed2aba95a837a0063b96cb9803ff8925f0c 100644 (file)
@@ -51,7 +51,6 @@ static struct inet_protosw pingv6_protosw = {
        .protocol =  IPPROTO_ICMPV6,
        .prot =      &pingv6_prot,
        .ops =       &inet6_dgram_ops,
-       .no_check =  UDP_CSUM_DEFAULT,
        .flags =     INET_PROTOSW_REUSE,
 };
 
@@ -168,12 +167,7 @@ int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
        pfh.wcheck = 0;
        pfh.family = AF_INET6;
 
-       if (ipv6_addr_is_multicast(&fl6.daddr))
-               hlimit = np->mcast_hops;
-       else
-               hlimit = np->hop_limit;
-       if (hlimit < 0)
-               hlimit = ip6_dst_hoplimit(dst);
+       hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
 
        lock_sock(sk);
        err = ip6_append_data(sk, ping_getfrag, &pfh, len,
index 091d066a57b3711c5bd0eb5a352573d0aa2f3776..3317440ea34174032a7b0bee73d8f5b36cfb08ee 100644 (file)
@@ -186,7 +186,7 @@ static void snmp6_seq_show_icmpv6msg(struct seq_file *seq, atomic_long_t *smib)
 /* can be called either with percpu mib (pcpumib != NULL),
  * or shared one (smib != NULL)
  */
-static void snmp6_seq_show_item(struct seq_file *seq, void __percpu **pcpumib,
+static void snmp6_seq_show_item(struct seq_file *seq, void __percpu *pcpumib,
                                atomic_long_t *smib,
                                const struct snmp_mib *itemlist)
 {
@@ -201,7 +201,7 @@ static void snmp6_seq_show_item(struct seq_file *seq, void __percpu **pcpumib,
        }
 }
 
-static void snmp6_seq_show_item64(struct seq_file *seq, void __percpu **mib,
+static void snmp6_seq_show_item64(struct seq_file *seq, void __percpu *mib,
                                  const struct snmp_mib *itemlist, size_t syncpoff)
 {
        int i;
@@ -215,14 +215,14 @@ static int snmp6_seq_show(struct seq_file *seq, void *v)
 {
        struct net *net = (struct net *)seq->private;
 
-       snmp6_seq_show_item64(seq, (void __percpu **)net->mib.ipv6_statistics,
+       snmp6_seq_show_item64(seq, net->mib.ipv6_statistics,
                            snmp6_ipstats_list, offsetof(struct ipstats_mib, syncp));
-       snmp6_seq_show_item(seq, (void __percpu **)net->mib.icmpv6_statistics,
+       snmp6_seq_show_item(seq, net->mib.icmpv6_statistics,
                            NULL, snmp6_icmp6_list);
        snmp6_seq_show_icmpv6msg(seq, net->mib.icmpv6msg_statistics->mibs);
-       snmp6_seq_show_item(seq, (void __percpu **)net->mib.udp_stats_in6,
+       snmp6_seq_show_item(seq, net->mib.udp_stats_in6,
                            NULL, snmp6_udp6_list);
-       snmp6_seq_show_item(seq, (void __percpu **)net->mib.udplite_stats_in6,
+       snmp6_seq_show_item(seq, net->mib.udplite_stats_in6,
                            NULL, snmp6_udplite6_list);
        return 0;
 }
@@ -245,7 +245,7 @@ static int snmp6_dev_seq_show(struct seq_file *seq, void *v)
        struct inet6_dev *idev = (struct inet6_dev *)seq->private;
 
        seq_printf(seq, "%-32s\t%u\n", "ifIndex", idev->dev->ifindex);
-       snmp6_seq_show_item64(seq, (void __percpu **)idev->stats.ipv6,
+       snmp6_seq_show_item64(seq, idev->stats.ipv6,
                            snmp6_ipstats_list, offsetof(struct ipstats_mib, syncp));
        snmp6_seq_show_item(seq, NULL, idev->stats.icmpv6dev->mibs,
                            snmp6_icmp6_list);
index 1f29996e368a23e67bfe09d9b2ff10bd9b61805d..b2dc60b0c76403d38a6a96c8a1c299977ecb7bad 100644 (file)
@@ -873,14 +873,8 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
                err = PTR_ERR(dst);
                goto out;
        }
-       if (hlimit < 0) {
-               if (ipv6_addr_is_multicast(&fl6.daddr))
-                       hlimit = np->mcast_hops;
-               else
-                       hlimit = np->hop_limit;
-               if (hlimit < 0)
-                       hlimit = ip6_dst_hoplimit(dst);
-       }
+       if (hlimit < 0)
+               hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
 
        if (tclass < 0)
                tclass = np->tclass;
@@ -1328,7 +1322,6 @@ static struct inet_protosw rawv6_protosw = {
        .protocol       = IPPROTO_IP,   /* wild card */
        .prot           = &rawv6_prot,
        .ops            = &inet6_sockraw_ops,
-       .no_check       = UDP_CSUM_DEFAULT,
        .flags          = INET_PROTOSW_REUSE,
 };
 
index 6ebdb7b6744cc933801dcf4c6b3dea6ddce8a89c..f23fbd28a501ed5c3438abb7f1cbbec85233688b 100644 (file)
@@ -1176,7 +1176,7 @@ 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_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark);
        fl6.daddr = iph->daddr;
        fl6.saddr = iph->saddr;
        fl6.flowlabel = ip6_flowinfo(iph);
@@ -1455,7 +1455,7 @@ static int ip6_dst_gc(struct dst_ops *ops)
                goto out;
 
        net->ipv6.ip6_rt_gc_expire++;
-       fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, entries > rt_max_size);
+       fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, true);
        entries = dst_entries_get_slow(ops);
        if (entries < ops->gc_thresh)
                net->ipv6.ip6_rt_gc_expire = rt_gc_timeout>>1;
index e5a453ca302e1e55e4d8e6ca7069f97fbb2b6347..4f408176dc64eeb306e25e5bde275581080fa523 100644 (file)
@@ -560,12 +560,12 @@ static int ipip6_err(struct sk_buff *skb, u32 info)
 
        if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
                ipv4_update_pmtu(skb, dev_net(skb->dev), info,
-                                t->dev->ifindex, 0, IPPROTO_IPV6, 0);
+                                t->parms.link, 0, IPPROTO_IPV6, 0);
                err = 0;
                goto out;
        }
        if (type == ICMP_REDIRECT) {
-               ipv4_redirect(skb, dev_net(skb->dev), t->dev->ifindex, 0,
+               ipv4_redirect(skb, dev_net(skb->dev), t->parms.link, 0,
                              IPPROTO_IPV6, 0);
                err = 0;
                goto out;
@@ -1828,4 +1828,5 @@ static int __init sit_init(void)
 module_init(sit_init);
 module_exit(sit_cleanup);
 MODULE_LICENSE("GPL");
+MODULE_ALIAS_RTNL_LINK("sit");
 MODULE_ALIAS_NETDEV("sit0");
index bb53a5e73c1ab67c7a11430488b8418c4edbf98b..a822b880689b5fea5adeed30956afd2328a9c8b9 100644 (file)
@@ -216,6 +216,8 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
            ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL)
                ireq->ir_iif = inet6_iif(skb);
 
+       ireq->ir_mark = inet_request_mark(sk, skb);
+
        req->expires = 0UL;
        req->num_retrans = 0;
        ireq->ecn_ok            = ecn_ok;
@@ -242,7 +244,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
                final_p = fl6_update_dst(&fl6, np->opt, &final);
                fl6.saddr = ireq->ir_v6_loc_addr;
                fl6.flowi6_oif = sk->sk_bound_dev_if;
-               fl6.flowi6_mark = sk->sk_mark;
+               fl6.flowi6_mark = ireq->ir_mark;
                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 7f405a168822afab4fa5349317ef43f2ed8e3a0f..058f3eca2e53efd1fe016cfe8450d3ab0a9c13b1 100644 (file)
@@ -38,6 +38,13 @@ static struct ctl_table ipv6_table_template[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec
        },
+       {
+               .procname       = "fwmark_reflect",
+               .data           = &init_net.ipv6.sysctl.fwmark_reflect,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec
+       },
        { }
 };
 
index e289830ed6e35a3be4feda700b5b6789dac20292..229239ad96b1645de84bfc5b0ad76311e295f82c 100644 (file)
@@ -340,7 +340,8 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        struct sock *sk;
        int err;
        struct tcp_sock *tp;
-       __u32 seq;
+       struct request_sock *fastopen;
+       __u32 seq, snd_una;
        struct net *net = dev_net(skb->dev);
 
        sk = inet6_lookup(net, &tcp_hashinfo, &hdr->daddr,
@@ -371,8 +372,11 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
 
        tp = tcp_sk(sk);
        seq = ntohl(th->seq);
+       /* XXX (TFO) - tp->snd_una should be ISN (tcp_create_openreq_child() */
+       fastopen = tp->fastopen_rsk;
+       snd_una = fastopen ? tcp_rsk(fastopen)->snt_isn : tp->snd_una;
        if (sk->sk_state != TCP_LISTEN &&
-           !between(seq, tp->snd_una, tp->snd_nxt)) {
+           !between(seq, snd_una, tp->snd_nxt)) {
                NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
                goto out;
        }
@@ -436,8 +440,13 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                goto out;
 
        case TCP_SYN_SENT:
-       case TCP_SYN_RECV:  /* Cannot happen.
-                              It can, it SYNs are crossed. --ANK */
+       case TCP_SYN_RECV:
+               /* Only in fast or simultaneous open. If a fast open socket is
+                * is already accepted it is treated as a connected one below.
+                */
+               if (fastopen && fastopen->sk == NULL)
+                       break;
+
                if (!sock_owned_by_user(sk)) {
                        sk->sk_err = err;
                        sk->sk_error_report(sk);                /* Wake people up to see the error (see connect in sock.c) */
@@ -463,7 +472,8 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
 static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst,
                              struct flowi6 *fl6,
                              struct request_sock *req,
-                             u16 queue_mapping)
+                             u16 queue_mapping,
+                             struct tcp_fastopen_cookie *foc)
 {
        struct inet_request_sock *ireq = inet_rsk(req);
        struct ipv6_pinfo *np = inet6_sk(sk);
@@ -474,7 +484,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst,
        if (!dst && (dst = inet6_csk_route_req(sk, fl6, req)) == NULL)
                goto done;
 
-       skb = tcp_make_synack(sk, dst, req, NULL);
+       skb = tcp_make_synack(sk, dst, req, foc);
 
        if (skb) {
                __tcp_v6_send_check(skb, &ireq->ir_v6_loc_addr,
@@ -498,7 +508,7 @@ static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req)
        struct flowi6 fl6;
        int res;
 
-       res = tcp_v6_send_synack(sk, NULL, &fl6, req, 0);
+       res = tcp_v6_send_synack(sk, NULL, &fl6, req, 0, NULL);
        if (!res) {
                TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS);
                NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSYNRETRANS);
@@ -802,6 +812,7 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win,
                fl6.flowi6_oif = inet6_iif(skb);
        else
                fl6.flowi6_oif = oif;
+       fl6.flowi6_mark = IP6_REPLY_MARK(net, skb->mark);
        fl6.fl6_dport = t1->dest;
        fl6.fl6_sport = t1->source;
        security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
@@ -917,7 +928,12 @@ static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb)
 static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
                                  struct request_sock *req)
 {
-       tcp_v6_send_ack(skb, tcp_rsk(req)->snt_isn + 1, tcp_rsk(req)->rcv_isn + 1,
+       /* sk->sk_state == TCP_LISTEN -> for regular TCP_SYN_RECV
+        * sk->sk_state == TCP_SYN_RECV -> for Fast Open.
+        */
+       tcp_v6_send_ack(skb, (sk->sk_state == TCP_LISTEN) ?
+                       tcp_rsk(req)->snt_isn + 1 : tcp_sk(sk)->snd_nxt,
+                       tcp_rsk(req)->rcv_nxt,
                        req->rcv_wnd, tcp_time_stamp, req->ts_recent, sk->sk_bound_dev_if,
                        tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr),
                        0, 0);
@@ -969,8 +985,10 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
        struct tcp_sock *tp = tcp_sk(sk);
        __u32 isn = TCP_SKB_CB(skb)->when;
        struct dst_entry *dst = NULL;
+       struct tcp_fastopen_cookie foc = { .len = -1 };
+       bool want_cookie = false, fastopen;
        struct flowi6 fl6;
-       bool want_cookie = false;
+       int err;
 
        if (skb->protocol == htons(ETH_P_IP))
                return tcp_v4_conn_request(sk, skb);
@@ -1001,7 +1019,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
        tcp_clear_options(&tmp_opt);
        tmp_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr);
        tmp_opt.user_mss = tp->rx_opt.user_mss;
-       tcp_parse_options(skb, &tmp_opt, 0, NULL);
+       tcp_parse_options(skb, &tmp_opt, 0, want_cookie ? NULL : &foc);
 
        if (want_cookie && !tmp_opt.saw_tstamp)
                tcp_clear_options(&tmp_opt);
@@ -1016,6 +1034,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
                TCP_ECN_create_request(req, skb, sock_net(sk));
 
        ireq->ir_iif = sk->sk_bound_dev_if;
+       ireq->ir_mark = inet_request_mark(sk, skb);
 
        /* So that link locals have meaning */
        if (!sk->sk_bound_dev_if &&
@@ -1074,19 +1093,27 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
                isn = tcp_v6_init_sequence(skb);
        }
 have_isn:
-       tcp_rsk(req)->snt_isn = isn;
 
        if (security_inet_conn_request(sk, skb, req))
                goto drop_and_release;
 
-       if (tcp_v6_send_synack(sk, dst, &fl6, req,
-                              skb_get_queue_mapping(skb)) ||
-           want_cookie)
+       if (!dst && (dst = inet6_csk_route_req(sk, &fl6, req)) == NULL)
                goto drop_and_free;
 
+       tcp_rsk(req)->snt_isn = isn;
        tcp_rsk(req)->snt_synack = tcp_time_stamp;
-       tcp_rsk(req)->listener = NULL;
-       inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
+       tcp_openreq_init_rwin(req, sk, dst);
+       fastopen = !want_cookie &&
+                  tcp_try_fastopen(sk, skb, req, &foc, dst);
+       err = tcp_v6_send_synack(sk, dst, &fl6, req,
+                                skb_get_queue_mapping(skb), &foc);
+       if (!fastopen) {
+               if (err || want_cookie)
+                       goto drop_and_free;
+
+               tcp_rsk(req)->listener = NULL;
+               inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
+       }
        return 0;
 
 drop_and_release:
@@ -1294,25 +1321,6 @@ static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
        return NULL;
 }
 
-static __sum16 tcp_v6_checksum_init(struct sk_buff *skb)
-{
-       if (skb->ip_summed == CHECKSUM_COMPLETE) {
-               if (!tcp_v6_check(skb->len, &ipv6_hdr(skb)->saddr,
-                                 &ipv6_hdr(skb)->daddr, skb->csum)) {
-                       skb->ip_summed = CHECKSUM_UNNECESSARY;
-                       return 0;
-               }
-       }
-
-       skb->csum = ~csum_unfold(tcp_v6_check(skb->len,
-                                             &ipv6_hdr(skb)->saddr,
-                                             &ipv6_hdr(skb)->daddr, 0));
-
-       if (skb->len <= 76)
-               return __skb_checksum_complete(skb);
-       return 0;
-}
-
 /* The socket must have it's spinlock held when we get
  * here.
  *
@@ -1486,7 +1494,7 @@ static int tcp_v6_rcv(struct sk_buff *skb)
        if (!pskb_may_pull(skb, th->doff*4))
                goto discard_it;
 
-       if (!skb_csum_unnecessary(skb) && tcp_v6_checksum_init(skb))
+       if (skb_checksum_init(skb, IPPROTO_TCP, ip6_compute_pseudo))
                goto csum_error;
 
        th = tcp_hdr(skb);
@@ -1779,6 +1787,7 @@ 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);
+       struct fastopen_queue *fastopenq = icsk->icsk_accept_queue.fastopenq;
 
        dest  = &sp->sk_v6_daddr;
        src   = &sp->sk_v6_rcv_saddr;
@@ -1821,7 +1830,9 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
                   jiffies_to_clock_t(icsk->icsk_ack.ato),
                   (icsk->icsk_ack.quick << 1) | icsk->icsk_ack.pingpong,
                   tp->snd_cwnd,
-                  tcp_in_initial_slowstart(tp) ? -1 : tp->snd_ssthresh
+                  sp->sk_state == TCP_LISTEN ?
+                       (fastopenq ? fastopenq->max_qlen : 0) :
+                       (tcp_in_initial_slowstart(tp) ? -1 : tp->snd_ssthresh)
                   );
 }
 
@@ -1981,7 +1992,6 @@ static struct inet_protosw tcpv6_protosw = {
        .protocol       =       IPPROTO_TCP,
        .prot           =       &tcpv6_prot,
        .ops            =       &inet6_stream_ops,
-       .no_check       =       0,
        .flags          =       INET_PROTOSW_PERMANENT |
                                INET_PROTOSW_ICSK,
 };
index 1e586d92260e1e75957060b896a8748e9beaa61a..95c8347992882e5cbef7719b0ae940fc659af057 100644 (file)
@@ -634,6 +634,10 @@ int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
                if (skb->len > sizeof(struct udphdr) && encap_rcv != NULL) {
                        int ret;
 
+                       /* Verify checksum before giving to encap */
+                       if (udp_lib_checksum_complete(skb))
+                               goto csum_error;
+
                        ret = encap_rcv(sk, skb);
                        if (ret <= 0) {
                                UDP_INC_STATS_BH(sock_net(sk),
@@ -701,17 +705,16 @@ static struct sock *udp_v6_mcast_next(struct net *net, struct sock *sk,
                                      int dif)
 {
        struct hlist_nulls_node *node;
-       struct sock *s = sk;
        unsigned short num = ntohs(loc_port);
 
-       sk_nulls_for_each_from(s, node) {
-               struct inet_sock *inet = inet_sk(s);
+       sk_nulls_for_each_from(sk, node) {
+               struct inet_sock *inet = inet_sk(sk);
 
-               if (!net_eq(sock_net(s), net))
+               if (!net_eq(sock_net(sk), net))
                        continue;
 
-               if (udp_sk(s)->udp_port_hash == num &&
-                   s->sk_family == PF_INET6) {
+               if (udp_sk(sk)->udp_port_hash == num &&
+                   sk->sk_family == PF_INET6) {
                        if (inet->inet_dport) {
                                if (inet->inet_dport != rmt_port)
                                        continue;
@@ -720,16 +723,16 @@ static struct sock *udp_v6_mcast_next(struct net *net, struct sock *sk,
                            !ipv6_addr_equal(&sk->sk_v6_daddr, rmt_addr))
                                continue;
 
-                       if (s->sk_bound_dev_if && s->sk_bound_dev_if != dif)
+                       if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif)
                                continue;
 
                        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))
+                       if (!inet6_mc_check(sk, loc_addr, rmt_addr))
                                continue;
-                       return s;
+                       return sk;
                }
        }
        return NULL;
@@ -760,6 +763,17 @@ static void flush_stack(struct sock **stack, unsigned int count,
        if (unlikely(skb1))
                kfree_skb(skb1);
 }
+
+static void udp6_csum_zero_error(struct sk_buff *skb)
+{
+       /* RFC 2460 section 8.1 says that we SHOULD log
+        * this error. Well, it is reasonable.
+        */
+       LIMIT_NETDEBUG(KERN_INFO "IPv6: udp checksum is 0 for [%pI6c]:%u->[%pI6c]:%u\n",
+                      &ipv6_hdr(skb)->saddr, ntohs(udp_hdr(skb)->source),
+                      &ipv6_hdr(skb)->daddr, ntohs(udp_hdr(skb)->dest));
+}
+
 /*
  * Note: called only from the BH handler context,
  * so we don't need to lock the hashes.
@@ -779,7 +793,12 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
        dif = inet6_iif(skb);
        sk = udp_v6_mcast_next(net, sk, uh->dest, daddr, uh->source, saddr, dif);
        while (sk) {
-               stack[count++] = sk;
+               /* If zero checksum and no_check is not on for
+                * the socket then skip it.
+                */
+               if (uh->check || udp_sk(sk)->no_check6_rx)
+                       stack[count++] = sk;
+
                sk = udp_v6_mcast_next(net, sk_nulls_next(sk), uh->dest, daddr,
                                       uh->source, saddr, dif);
                if (unlikely(count == ARRAY_SIZE(stack))) {
@@ -867,6 +886,12 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
        if (sk != NULL) {
                int ret;
 
+               if (!uh->check && !udp_sk(sk)->no_check6_rx) {
+                       sock_put(sk);
+                       udp6_csum_zero_error(skb);
+                       goto csum_error;
+               }
+
                ret = udpv6_queue_rcv_skb(sk, skb);
                sock_put(sk);
 
@@ -879,6 +904,11 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
                return 0;
        }
 
+       if (!uh->check) {
+               udp6_csum_zero_error(skb);
+               goto csum_error;
+       }
+
        if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
                goto discard;
 
@@ -1006,7 +1036,10 @@ static int udp_v6_push_pending_frames(struct sock *sk)
 
        if (is_udplite)
                csum = udplite_csum_outgoing(sk, skb);
-       else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */
+       else if (up->no_check6_tx) {   /* UDP csum disabled */
+               skb->ip_summed = CHECKSUM_NONE;
+               goto send;
+       } else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */
                udp6_hwcsum_outgoing(sk, skb, &fl6->saddr, &fl6->daddr,
                                     up->len);
                goto send;
@@ -1232,14 +1265,8 @@ int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
                goto out;
        }
 
-       if (hlimit < 0) {
-               if (ipv6_addr_is_multicast(&fl6.daddr))
-                       hlimit = np->mcast_hops;
-               else
-                       hlimit = np->hop_limit;
-               if (hlimit < 0)
-                       hlimit = ip6_dst_hoplimit(dst);
-       }
+       if (hlimit < 0)
+               hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
 
        if (tclass < 0)
                tclass = np->tclass;
@@ -1479,7 +1506,6 @@ static struct inet_protosw udpv6_protosw = {
        .protocol =  IPPROTO_UDP,
        .prot =      &udpv6_prot,
        .ops =       &inet6_dgram_ops,
-       .no_check =  UDP_CSUM_DEFAULT,
        .flags =     INET_PROTOSW_PERMANENT,
 };
 
index b261ee8b83fc87ddabcb447d1235ea8b67429de2..0ae3d98f83e00533c14e6f8c23ab78a5a6a72625 100644 (file)
@@ -63,7 +63,9 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
                if (unlikely(type & ~(SKB_GSO_UDP |
                                      SKB_GSO_DODGY |
                                      SKB_GSO_UDP_TUNNEL |
+                                     SKB_GSO_UDP_TUNNEL_CSUM |
                                      SKB_GSO_GRE |
+                                     SKB_GSO_GRE_CSUM |
                                      SKB_GSO_IPIP |
                                      SKB_GSO_SIT |
                                      SKB_GSO_MPLS) ||
@@ -76,7 +78,8 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
                goto out;
        }
 
-       if (skb->encapsulation && skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL)
+       if (skb->encapsulation && skb_shinfo(skb)->gso_type &
+           (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM))
                segs = skb_udp_tunnel_segment(skb, features);
        else {
                /* Do software UFO. Complete and fill in the UDP checksum as HW cannot
index dfcc4be46898281f09038bbd7638edfefa04ea3e..9cf097e206e931c6e3c184f22b8adcabedc4c03a 100644 (file)
@@ -64,7 +64,6 @@ static struct inet_protosw udplite6_protosw = {
        .protocol       = IPPROTO_UDPLITE,
        .prot           = &udplitev6_prot,
        .ops            = &inet6_dgram_ops,
-       .no_check       = 0,
        .flags          = INET_PROTOSW_PERMANENT,
 };
 
index b930d080c66f231f338c7ea860c8c1d798d91fea..433672d07d0b55e1e436be704780e8c6f5777447 100644 (file)
@@ -78,7 +78,7 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb)
        if (mtu < IPV6_MIN_MTU)
                mtu = IPV6_MIN_MTU;
 
-       if (!skb->local_df && skb->len > mtu) {
+       if (!skb->ignore_df && skb->len > mtu) {
                skb->dev = dst->dev;
 
                if (xfrm6_local_dontfrag(skb))
@@ -114,7 +114,7 @@ int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
        if (err)
                return err;
 
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        return x->outer_mode->output2(x, skb);
 }
@@ -153,7 +153,7 @@ static int __xfrm6_output(struct sk_buff *skb)
        if (skb->len > mtu && xfrm6_local_dontfrag(skb)) {
                xfrm6_local_rxpmtu(skb, mtu);
                return -EMSGSIZE;
-       } else if (!skb->local_df && skb->len > mtu && skb->sk) {
+       } else if (!skb->ignore_df && skb->len > mtu && skb->sk) {
                xfrm_local_error(skb, mtu);
                return -EMSGSIZE;
        }
index 41e4e93cb3aae37df41ff419ef34be9c2255b5cc..91729b807c7d041ae379e89df335acefe5218635 100644 (file)
@@ -1353,7 +1353,7 @@ static int ipx_create(struct net *net, struct socket *sock, int protocol,
 
        sk_refcnt_debug_inc(sk);
        sock_init_data(sock, sk);
-       sk->sk_no_check = 1;            /* Checksum off by default */
+       sk->sk_no_check_tx = 1;         /* Checksum off by default */
        sock->ops = &ipx_dgram_ops;
        rc = 0;
 out:
index c1f03185c5e115ffe359e39a0bfb17efe8d4c38a..67e7ad3d46b1fb4489a175836351607e7f5ae741 100644 (file)
@@ -236,7 +236,8 @@ int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx,
        }
 
        /* Apply checksum. Not allowed on 802.3 links. */
-       if (sk->sk_no_check || intrfc->if_dlink_type == htons(IPX_FRAME_8023))
+       if (sk->sk_no_check_tx ||
+           intrfc->if_dlink_type == htons(IPX_FRAME_8023))
                ipx->ipx_checksum = htons(0xFFFF);
        else
                ipx->ipx_checksum = ipx_cksum(ipx, len + sizeof(struct ipxhdr));
index 8c9d7302c84682f4eec438405cf312da02cf9aab..7a95fa4a3de1e558a07485bd8f6dbb3b4dcf32b3 100644 (file)
@@ -682,6 +682,18 @@ struct sock *iucv_accept_dequeue(struct sock *parent, struct socket *newsock)
        return NULL;
 }
 
+static void __iucv_auto_name(struct iucv_sock *iucv)
+{
+       char name[12];
+
+       sprintf(name, "%08x", atomic_inc_return(&iucv_sk_list.autobind_name));
+       while (__iucv_get_sock_by_name(name)) {
+               sprintf(name, "%08x",
+                       atomic_inc_return(&iucv_sk_list.autobind_name));
+       }
+       memcpy(iucv->src_name, name, 8);
+}
+
 /* Bind an unbound socket */
 static int iucv_sock_bind(struct socket *sock, struct sockaddr *addr,
                          int addr_len)
@@ -724,8 +736,12 @@ static int iucv_sock_bind(struct socket *sock, struct sockaddr *addr,
        rcu_read_lock();
        for_each_netdev_rcu(&init_net, dev) {
                if (!memcmp(dev->perm_addr, uid, 8)) {
-                       memcpy(iucv->src_name, sa->siucv_name, 8);
                        memcpy(iucv->src_user_id, sa->siucv_user_id, 8);
+                       /* Check for unitialized siucv_name */
+                       if (strncmp(sa->siucv_name, "        ", 8) == 0)
+                               __iucv_auto_name(iucv);
+                       else
+                               memcpy(iucv->src_name, sa->siucv_name, 8);
                        sk->sk_bound_dev_if = dev->ifindex;
                        iucv->hs_dev = dev;
                        dev_hold(dev);
@@ -763,7 +779,6 @@ static int iucv_sock_bind(struct socket *sock, struct sockaddr *addr,
 static int iucv_sock_autobind(struct sock *sk)
 {
        struct iucv_sock *iucv = iucv_sk(sk);
-       char name[12];
        int err = 0;
 
        if (unlikely(!pr_iucv))
@@ -772,17 +787,9 @@ static int iucv_sock_autobind(struct sock *sk)
        memcpy(iucv->src_user_id, iucv_userid, 8);
 
        write_lock_bh(&iucv_sk_list.lock);
-
-       sprintf(name, "%08x", atomic_inc_return(&iucv_sk_list.autobind_name));
-       while (__iucv_get_sock_by_name(name)) {
-               sprintf(name, "%08x",
-                       atomic_inc_return(&iucv_sk_list.autobind_name));
-       }
-
+       __iucv_auto_name(iucv);
        write_unlock_bh(&iucv_sk_list.lock);
 
-       memcpy(&iucv->src_name, name, 8);
-
        if (!iucv->msglimit)
                iucv->msglimit = IUCV_QUEUELEN_DEFAULT;
 
@@ -1936,11 +1943,10 @@ static int afiucv_hs_callback_syn(struct sock *sk, struct sk_buff *skb)
            sk_acceptq_is_full(sk) ||
            !nsk) {
                /* error on server socket - connection refused */
-               if (nsk)
-                       sk_free(nsk);
                afiucv_swap_src_dest(skb);
                trans_hdr->flags = AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_FIN;
                err = dev_queue_xmit(skb);
+               iucv_sock_kill(nsk);
                bh_unlock_sock(sk);
                goto out;
        }
index f3c83073afc49f7aad98262cc5776b3edddc7f87..ba2a2f95911c99732dc2e3fb1ae37b9f977d1f5a 100644 (file)
@@ -1476,9 +1476,7 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, const struct sadb_msg
        else
                err = xfrm_state_update(x);
 
-       xfrm_audit_state_add(x, err ? 0 : 1,
-                            audit_get_loginuid(current),
-                            audit_get_sessionid(current), 0);
+       xfrm_audit_state_add(x, err ? 0 : 1, true);
 
        if (err < 0) {
                x->km.state = XFRM_STATE_DEAD;
@@ -1532,9 +1530,7 @@ static int pfkey_delete(struct sock *sk, struct sk_buff *skb, const struct sadb_
        c.event = XFRM_MSG_DELSA;
        km_state_notify(x, &c);
 out:
-       xfrm_audit_state_delete(x, err ? 0 : 1,
-                               audit_get_loginuid(current),
-                               audit_get_sessionid(current), 0);
+       xfrm_audit_state_delete(x, err ? 0 : 1, true);
        xfrm_state_put(x);
 
        return err;
@@ -1726,17 +1722,13 @@ static int pfkey_flush(struct sock *sk, struct sk_buff *skb, const struct sadb_m
        struct net *net = sock_net(sk);
        unsigned int proto;
        struct km_event c;
-       struct xfrm_audit audit_info;
        int err, err2;
 
        proto = pfkey_satype2proto(hdr->sadb_msg_satype);
        if (proto == 0)
                return -EINVAL;
 
-       audit_info.loginuid = audit_get_loginuid(current);
-       audit_info.sessionid = audit_get_sessionid(current);
-       audit_info.secid = 0;
-       err = xfrm_state_flush(net, proto, &audit_info);
+       err = xfrm_state_flush(net, proto, true);
        err2 = unicast_flush_resp(sk, hdr);
        if (err || err2) {
                if (err == -ESRCH) /* empty table - go quietly */
@@ -2288,9 +2280,7 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, const struct sadb_
        err = xfrm_policy_insert(pol->sadb_x_policy_dir-1, xp,
                                 hdr->sadb_msg_type != SADB_X_SPDUPDATE);
 
-       xfrm_audit_policy_add(xp, err ? 0 : 1,
-                             audit_get_loginuid(current),
-                             audit_get_sessionid(current), 0);
+       xfrm_audit_policy_add(xp, err ? 0 : 1, true);
 
        if (err)
                goto out;
@@ -2372,9 +2362,7 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, const struct sa
        if (xp == NULL)
                return -ENOENT;
 
-       xfrm_audit_policy_delete(xp, err ? 0 : 1,
-                                audit_get_loginuid(current),
-                                audit_get_sessionid(current), 0);
+       xfrm_audit_policy_delete(xp, err ? 0 : 1, true);
 
        if (err)
                goto out;
@@ -2553,7 +2541,7 @@ static int pfkey_migrate(struct sock *sk, struct sk_buff *skb,
                sel.sport_mask = htons(0xffff);
 
        /* set destination address info of selector */
-       sa = ext_hdrs[SADB_EXT_ADDRESS_DST - 1],
+       sa = ext_hdrs[SADB_EXT_ADDRESS_DST - 1];
        pfkey_sadb_addr2xfrm_addr(sa, &sel.daddr);
        sel.prefixlen_d = sa->sadb_address_prefixlen;
        sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
@@ -2622,9 +2610,7 @@ static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, const struct sadb_
                return -ENOENT;
 
        if (delete) {
-               xfrm_audit_policy_delete(xp, err ? 0 : 1,
-                               audit_get_loginuid(current),
-                               audit_get_sessionid(current), 0);
+               xfrm_audit_policy_delete(xp, err ? 0 : 1, true);
 
                if (err)
                        goto out;
@@ -2733,13 +2719,9 @@ static int pfkey_spdflush(struct sock *sk, struct sk_buff *skb, const struct sad
 {
        struct net *net = sock_net(sk);
        struct km_event c;
-       struct xfrm_audit audit_info;
        int err, err2;
 
-       audit_info.loginuid = audit_get_loginuid(current);
-       audit_info.sessionid = audit_get_sessionid(current);
-       audit_info.secid = 0;
-       err = xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, &audit_info);
+       err = xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, true);
        err2 = unicast_flush_resp(sk, hdr);
        if (err || err2) {
                if (err == -ESRCH) /* empty table - old silent behavior */
index a4e37d7158dcca42455a04eaf0460a48e39242fd..bea259043205ebe3605c34ed35da504807792af1 100644 (file)
@@ -495,52 +495,6 @@ static void l2tp_recv_dequeue(struct l2tp_session *session)
        spin_unlock_bh(&session->reorder_q.lock);
 }
 
-static inline int l2tp_verify_udp_checksum(struct sock *sk,
-                                          struct sk_buff *skb)
-{
-       struct udphdr *uh = udp_hdr(skb);
-       u16 ulen = ntohs(uh->len);
-       __wsum psum;
-
-       if (sk->sk_no_check || skb_csum_unnecessary(skb))
-               return 0;
-
-#if IS_ENABLED(CONFIG_IPV6)
-       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;
-               }
-               if ((skb->ip_summed == CHECKSUM_COMPLETE) &&
-                   !csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
-                                    &ipv6_hdr(skb)->daddr, ulen,
-                                    IPPROTO_UDP, skb->csum)) {
-                       skb->ip_summed = CHECKSUM_UNNECESSARY;
-                       return 0;
-               }
-               skb->csum = ~csum_unfold(csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
-                                                        &ipv6_hdr(skb)->daddr,
-                                                        skb->len, IPPROTO_UDP,
-                                                        0));
-       } else
-#endif
-       {
-               struct inet_sock *inet;
-               if (!uh->check)
-                       return 0;
-               inet = inet_sk(sk);
-               psum = csum_tcpudp_nofold(inet->inet_saddr, inet->inet_daddr,
-                                         ulen, IPPROTO_UDP, 0);
-
-               if ((skb->ip_summed == CHECKSUM_COMPLETE) &&
-                   !csum_fold(csum_add(psum, skb->csum)))
-                       return 0;
-               skb->csum = psum;
-       }
-
-       return __skb_checksum_complete(skb);
-}
-
 static int l2tp_seq_check_rx_window(struct l2tp_session *session, u32 nr)
 {
        u32 nws;
@@ -895,8 +849,7 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb,
        u16 version;
        int length;
 
-       if (tunnel->sock && l2tp_verify_udp_checksum(tunnel->sock, skb))
-               goto discard_bad_csum;
+       /* UDP has verifed checksum */
 
        /* UDP always verifies the packet length. */
        __skb_pull(skb, sizeof(struct udphdr));
@@ -979,14 +932,6 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb,
 
        return 0;
 
-discard_bad_csum:
-       LIMIT_NETDEBUG("%s: UDP: bad checksum\n", tunnel->name);
-       UDP_INC_STATS_USER(tunnel->l2tp_net, UDP_MIB_INERRORS, 0);
-       atomic_long_inc(&tunnel->stats.rx_errors);
-       kfree_skb(skb);
-
-       return 0;
-
 error:
        /* Put UDP header back */
        __skb_push(skb, sizeof(struct udphdr));
@@ -1128,7 +1073,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;
+       skb->ignore_df = 1;
 #if IS_ENABLED(CONFIG_IPV6)
        if (tunnel->sock->sk_family == PF_INET6 && !tunnel->v4mapped)
                error = inet6_csk_xmit(tunnel->sock, skb, NULL);
@@ -1150,31 +1095,6 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb,
        return 0;
 }
 
-#if IS_ENABLED(CONFIG_IPV6)
-static void l2tp_xmit_ipv6_csum(struct sock *sk, struct sk_buff *skb,
-                               int udp_len)
-{
-       struct ipv6_pinfo *np = inet6_sk(sk);
-       struct udphdr *uh = udp_hdr(skb);
-
-       if (!skb_dst(skb) || !skb_dst(skb)->dev ||
-           !(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, &sk->sk_v6_daddr, udp_len,
-                                           IPPROTO_UDP, csum);
-               if (uh->check == 0)
-                       uh->check = CSUM_MANGLED_0;
-       } else {
-               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, &sk->sk_v6_daddr,
-                                            udp_len, IPPROTO_UDP, 0);
-       }
-}
-#endif
-
 /* If caller requires the skb to have a ppp header, the header must be
  * inserted in the skb data before calling this function.
  */
@@ -1186,7 +1106,6 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len
        struct flowi *fl;
        struct udphdr *uh;
        struct inet_sock *inet;
-       __wsum csum;
        int headroom;
        int uhlen = (tunnel->encap == L2TP_ENCAPTYPE_UDP) ? sizeof(struct udphdr) : 0;
        int udp_len;
@@ -1235,33 +1154,17 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len
                uh->dest = inet->inet_dport;
                udp_len = uhlen + hdr_len + data_len;
                uh->len = htons(udp_len);
-               uh->check = 0;
 
                /* Calculate UDP checksum if configured to do so */
 #if IS_ENABLED(CONFIG_IPV6)
                if (sk->sk_family == PF_INET6 && !tunnel->v4mapped)
-                       l2tp_xmit_ipv6_csum(sk, skb, udp_len);
+                       udp6_set_csum(udp_get_no_check6_tx(sk),
+                                     skb, &inet6_sk(sk)->saddr,
+                                     &sk->sk_v6_daddr, udp_len);
                else
 #endif
-               if (sk->sk_no_check == UDP_CSUM_NOXMIT)
-                       skb->ip_summed = CHECKSUM_NONE;
-               else if ((skb_dst(skb) && skb_dst(skb)->dev) &&
-                        (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM))) {
-                       skb->ip_summed = CHECKSUM_COMPLETE;
-                       csum = skb_checksum(skb, 0, udp_len, 0);
-                       uh->check = csum_tcpudp_magic(inet->inet_saddr,
-                                                     inet->inet_daddr,
-                                                     udp_len, IPPROTO_UDP, csum);
-                       if (uh->check == 0)
-                               uh->check = CSUM_MANGLED_0;
-               } else {
-                       skb->ip_summed = CHECKSUM_PARTIAL;
-                       skb->csum_start = skb_transport_header(skb) - skb->head;
-                       skb->csum_offset = offsetof(struct udphdr, check);
-                       uh->check = ~csum_tcpudp_magic(inet->inet_saddr,
-                                                      inet->inet_daddr,
-                                                      udp_len, IPPROTO_UDP, 0);
-               }
+               udp_set_csum(sk->sk_no_check_tx, skb, inet->inet_saddr,
+                            inet->inet_daddr, udp_len);
                break;
 
        case L2TP_ENCAPTYPE_IP:
@@ -1490,6 +1393,11 @@ static int l2tp_tunnel_sock_create(struct net *net,
                                             sizeof(udp6_addr), 0);
                        if (err < 0)
                                goto out;
+
+                       if (cfg->udp6_zero_tx_checksums)
+                               udp_set_no_check6_tx(sock->sk, true);
+                       if (cfg->udp6_zero_rx_checksums)
+                               udp_set_no_check6_rx(sock->sk, true);
                } else
 #endif
                {
@@ -1518,7 +1426,7 @@ static int l2tp_tunnel_sock_create(struct net *net,
                }
 
                if (!cfg->use_udp_checksums)
-                       sock->sk->sk_no_check = UDP_CSUM_NOXMIT;
+                       sock->sk->sk_no_check_tx = 1;
 
                break;
 
index 3f93ccd6ba9768fe171f4e35e79d613226c1dbc7..68aa9ffd4ae4d972cdd57ea742ddf3b78ba497d6 100644 (file)
@@ -162,7 +162,9 @@ struct l2tp_tunnel_cfg {
 #endif
        u16                     local_udp_port;
        u16                     peer_udp_port;
-       unsigned int            use_udp_checksums:1;
+       unsigned int            use_udp_checksums:1,
+                               udp6_zero_tx_checksums:1,
+                               udp6_zero_rx_checksums:1;
 };
 
 struct l2tp_tunnel {
index 3397fe6897c0326d3efb2277a2824f5c2038d3c4..369a9822488c45fc10be28ca00300e9382645a9e 100644 (file)
@@ -606,7 +606,6 @@ static struct inet_protosw l2tp_ip_protosw = {
        .protocol       = IPPROTO_L2TP,
        .prot           = &l2tp_ip_prot,
        .ops            = &l2tp_ip_ops,
-       .no_check       = 0,
 };
 
 static struct net_protocol l2tp_ip_protocol __read_mostly = {
index 7704ea9502fdc9e49a2b8bb7fda4b9b9ec8d1722..f3f98a156ceed8ebc5843368aa0d1988175e5fc7 100644 (file)
@@ -605,14 +605,8 @@ static int l2tp_ip6_sendmsg(struct kiocb *iocb, struct sock *sk,
                goto out;
        }
 
-       if (hlimit < 0) {
-               if (ipv6_addr_is_multicast(&fl6.daddr))
-                       hlimit = np->mcast_hops;
-               else
-                       hlimit = np->hop_limit;
-               if (hlimit < 0)
-                       hlimit = ip6_dst_hoplimit(dst);
-       }
+       if (hlimit < 0)
+               hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
 
        if (tclass < 0)
                tclass = np->tclass;
@@ -761,7 +755,6 @@ static struct inet_protosw l2tp_ip6_protosw = {
        .protocol       = IPPROTO_L2TP,
        .prot           = &l2tp_ip6_prot,
        .ops            = &l2tp_ip6_ops,
-       .no_check       = 0,
 };
 
 static struct inet6_protocol l2tp_ip6_protocol __read_mostly = {
index bd7387adea9eff25ce7ffd0683589dcdcc020ad0..0ac907adb2f472c0d49508ca6843363db27129b1 100644 (file)
@@ -161,6 +161,13 @@ static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info
                        cfg.peer_udp_port = nla_get_u16(info->attrs[L2TP_ATTR_UDP_DPORT]);
                if (info->attrs[L2TP_ATTR_UDP_CSUM])
                        cfg.use_udp_checksums = nla_get_flag(info->attrs[L2TP_ATTR_UDP_CSUM]);
+
+#if IS_ENABLED(CONFIG_IPV6)
+               if (info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX])
+                       cfg.udp6_zero_tx_checksums = nla_get_flag(info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX]);
+               if (info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX])
+                       cfg.udp6_zero_rx_checksums = nla_get_flag(info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX]);
+#endif
        }
 
        if (info->attrs[L2TP_ATTR_DEBUG])
@@ -297,8 +304,7 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, int fla
        case L2TP_ENCAPTYPE_UDP:
                if (nla_put_u16(skb, L2TP_ATTR_UDP_SPORT, ntohs(inet->inet_sport)) ||
                    nla_put_u16(skb, L2TP_ATTR_UDP_DPORT, ntohs(inet->inet_dport)) ||
-                   nla_put_u8(skb, L2TP_ATTR_UDP_CSUM,
-                              (sk->sk_no_check != UDP_CSUM_NOXMIT)))
+                   nla_put_u8(skb, L2TP_ATTR_UDP_CSUM, !sk->sk_no_check_tx))
                        goto nla_put_failure;
                /* NOBREAK */
        case L2TP_ENCAPTYPE_IP:
index 9d7d840aac6d11630ef902f4ce57db7a8491d1d4..1e46ffa69167973921b795f8757f903234a61b94 100644 (file)
@@ -25,7 +25,8 @@ mac80211-y := \
        wme.o \
        event.o \
        chan.o \
-       trace.o mlme.o
+       trace.o mlme.o \
+       tdls.o
 
 mac80211-$(CONFIG_MAC80211_LEDS) += led.o
 mac80211-$(CONFIG_MAC80211_DEBUGFS) += \
index 7c7df475a401693dd44cb1a6b9d68dd255b2acd5..ec24378caaafaf333152e856aa0e2e920ddbb13f 100644 (file)
@@ -23,12 +23,13 @@ void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
                               u8 *data, size_t data_len, u8 *mic)
 {
        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));
+       char aead_req_data[sizeof(struct aead_request) +
+                          crypto_aead_reqsize(tfm)]
+               __aligned(__alignof__(struct aead_request));
+       struct aead_request *aead_req = (void *) aead_req_data;
+
+       memset(aead_req, 0, sizeof(aead_req_data));
 
        sg_init_one(&pt, data, data_len);
        sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
@@ -36,23 +37,23 @@ void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
        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, &pt, ct, data_len, b_0);
+       aead_request_set_tfm(aead_req, tfm);
+       aead_request_set_assoc(aead_req, &assoc, assoc.length);
+       aead_request_set_crypt(aead_req, &pt, ct, data_len, b_0);
 
-       crypto_aead_encrypt(&aead_req.req);
+       crypto_aead_encrypt(aead_req);
 }
 
 int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
                              u8 *data, size_t data_len, u8 *mic)
 {
        struct scatterlist assoc, pt, ct[2];
-       struct {
-               struct aead_request     req;
-               u8                      priv[crypto_aead_reqsize(tfm)];
-       } aead_req;
+       char aead_req_data[sizeof(struct aead_request) +
+                          crypto_aead_reqsize(tfm)]
+               __aligned(__alignof__(struct aead_request));
+       struct aead_request *aead_req = (void *) aead_req_data;
 
-       memset(&aead_req, 0, sizeof(aead_req));
+       memset(aead_req, 0, sizeof(aead_req_data));
 
        sg_init_one(&pt, data, data_len);
        sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
@@ -60,12 +61,12 @@ int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
        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,
+       aead_request_set_tfm(aead_req, tfm);
+       aead_request_set_assoc(aead_req, &assoc, assoc.length);
+       aead_request_set_crypt(aead_req, ct, &pt,
                               data_len + IEEE80211_CCMP_MIC_LEN, b_0);
 
-       return crypto_aead_decrypt(&aead_req.req);
+       return crypto_aead_decrypt(aead_req);
 }
 
 struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[])
index aaa59d719592c0b7dc6ef3ddb4df8aaa578bc45c..d7513a503be11b180031342dcf316450fd6c69d3 100644 (file)
@@ -109,6 +109,15 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
 static int ieee80211_start_p2p_device(struct wiphy *wiphy,
                                      struct wireless_dev *wdev)
 {
+       struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+       int ret;
+
+       mutex_lock(&sdata->local->chanctx_mtx);
+       ret = ieee80211_check_combinations(sdata, NULL, 0, 0);
+       mutex_unlock(&sdata->local->chanctx_mtx);
+       if (ret < 0)
+               return ret;
+
        return ieee80211_do_open(wdev, true);
 }
 
@@ -463,8 +472,10 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
 {
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        struct ieee80211_local *local = sdata->local;
+       struct rate_control_ref *ref = local->rate_ctrl;
        struct timespec uptime;
        u64 packets = 0;
+       u32 thr = 0;
        int i, ac;
 
        sinfo->generation = sdata->local->sta_generation;
@@ -578,6 +589,17 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
                sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
        if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
                sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
+
+       /* check if the driver has a SW RC implementation */
+       if (ref && ref->ops->get_expected_throughput)
+               thr = ref->ops->get_expected_throughput(sta->rate_ctrl_priv);
+       else
+               thr = drv_get_expected_throughput(local, &sta->sta);
+
+       if (thr != 0) {
+               sinfo->filled |= STATION_INFO_EXPECTED_THROUGHPUT;
+               sinfo->expected_throughput = thr;
+       }
 }
 
 static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = {
@@ -768,7 +790,7 @@ static void ieee80211_get_et_strings(struct wiphy *wiphy,
 }
 
 static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
-                                int idx, u8 *mac, struct station_info *sinfo)
+                                 int idx, u8 *mac, struct station_info *sinfo)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
@@ -798,7 +820,7 @@ static int ieee80211_dump_survey(struct wiphy *wiphy, struct net_device *dev,
 }
 
 static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
-                                u8 *mac, struct station_info *sinfo)
+                                const u8 *mac, struct station_info *sinfo)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
@@ -972,13 +994,13 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
        sdata->needed_rx_chains = sdata->local->rx_chains;
 
        mutex_lock(&local->mtx);
-       sdata->radar_required = params->radar_required;
        err = ieee80211_vif_use_channel(sdata, &params->chandef,
                                        IEEE80211_CHANCTX_SHARED);
+       if (!err)
+               ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
        mutex_unlock(&local->mtx);
        if (err)
                return err;
-       ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
 
        /*
         * Apply control port protocol, this allows us to
@@ -1075,6 +1097,31 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
        return 0;
 }
 
+bool ieee80211_csa_needs_block_tx(struct ieee80211_local *local)
+{
+       struct ieee80211_sub_if_data *sdata;
+
+       lockdep_assert_held(&local->mtx);
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+               if (!ieee80211_sdata_running(sdata))
+                       continue;
+
+               if (!sdata->vif.csa_active)
+                       continue;
+
+               if (!sdata->csa_block_tx)
+                       continue;
+
+               rcu_read_unlock();
+               return true;
+       }
+       rcu_read_unlock();
+
+       return false;
+}
+
 static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -1092,7 +1139,14 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
        old_probe_resp = sdata_dereference(sdata->u.ap.probe_resp, sdata);
 
        /* abort any running channel switch */
+       mutex_lock(&local->mtx);
        sdata->vif.csa_active = false;
+       if (!ieee80211_csa_needs_block_tx(local))
+               ieee80211_wake_queues_by_reason(&local->hw,
+                                       IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+       mutex_unlock(&local->mtx);
+
        kfree(sdata->u.ap.next_beacon);
        sdata->u.ap.next_beacon = NULL;
 
@@ -1131,8 +1185,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
        local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
        skb_queue_purge(&sdata->u.ap.ps.bc_buf);
 
-       ieee80211_vif_copy_chanctx_to_vlans(sdata, true);
        mutex_lock(&local->mtx);
+       ieee80211_vif_copy_chanctx_to_vlans(sdata, true);
        ieee80211_vif_release_channel(sdata);
        mutex_unlock(&local->mtx);
 
@@ -1416,7 +1470,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
 }
 
 static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
-                                u8 *mac, struct station_parameters *params)
+                                const u8 *mac,
+                                struct station_parameters *params)
 {
        struct ieee80211_local *local = wiphy_priv(wiphy);
        struct sta_info *sta;
@@ -1450,6 +1505,8 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
        if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) {
                sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
                sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
+       } else {
+               sta->sta.tdls = true;
        }
 
        err = sta_apply_parameters(local, sta, params);
@@ -1483,7 +1540,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
 }
 
 static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
-                                u8 *mac)
+                                const u8 *mac)
 {
        struct ieee80211_sub_if_data *sdata;
 
@@ -1497,7 +1554,7 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
 }
 
 static int ieee80211_change_station(struct wiphy *wiphy,
-                                   struct net_device *dev, u8 *mac,
+                                   struct net_device *dev, const u8 *mac,
                                    struct station_parameters *params)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -1566,7 +1623,7 @@ static int ieee80211_change_station(struct wiphy *wiphy,
 
                if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
                    sta->sdata->u.vlan.sta) {
-                       rcu_assign_pointer(sta->sdata->u.vlan.sta, NULL);
+                       RCU_INIT_POINTER(sta->sdata->u.vlan.sta, NULL);
                        prev_4addr = true;
                }
 
@@ -1622,7 +1679,7 @@ static int ieee80211_change_station(struct wiphy *wiphy,
 
 #ifdef CONFIG_MAC80211_MESH
 static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
-                                u8 *dst, u8 *next_hop)
+                              const u8 *dst, const u8 *next_hop)
 {
        struct ieee80211_sub_if_data *sdata;
        struct mesh_path *mpath;
@@ -1650,7 +1707,7 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
 }
 
 static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev,
-                              u8 *dst)
+                              const u8 *dst)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
@@ -1661,9 +1718,8 @@ static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev,
        return 0;
 }
 
-static int ieee80211_change_mpath(struct wiphy *wiphy,
-                                   struct net_device *dev,
-                                   u8 *dst, u8 *next_hop)
+static int ieee80211_change_mpath(struct wiphy *wiphy, struct net_device *dev,
+                                 const u8 *dst, const u8 *next_hop)
 {
        struct ieee80211_sub_if_data *sdata;
        struct mesh_path *mpath;
@@ -1755,8 +1811,8 @@ static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,
 }
 
 static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
-                                int idx, u8 *dst, u8 *next_hop,
-                                struct mpath_info *pinfo)
+                               int idx, u8 *dst, u8 *next_hop,
+                               struct mpath_info *pinfo)
 {
        struct ieee80211_sub_if_data *sdata;
        struct mesh_path *mpath;
@@ -2930,7 +2986,6 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
        /* whatever, but channel contexts should not complain about that one */
        sdata->smps_mode = IEEE80211_SMPS_OFF;
        sdata->needed_rx_chains = local->rx_chains;
-       sdata->radar_required = true;
 
        err = ieee80211_vif_use_channel(sdata, chandef,
                                        IEEE80211_CHANCTX_SHARED);
@@ -3011,26 +3066,11 @@ void ieee80211_csa_finish(struct ieee80211_vif *vif)
 }
 EXPORT_SYMBOL(ieee80211_csa_finish);
 
-static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
+static int ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data *sdata,
+                                         u32 *changed)
 {
-       struct ieee80211_local *local = sdata->local;
-       int err, changed = 0;
-
-       sdata_assert_lock(sdata);
-
-       mutex_lock(&local->mtx);
-       sdata->radar_required = sdata->csa_radar_required;
-       err = ieee80211_vif_change_channel(sdata, &changed);
-       mutex_unlock(&local->mtx);
-       if (WARN_ON(err < 0))
-               return;
-
-       if (!local->use_chanctx) {
-               local->_oper_chandef = sdata->csa_chandef;
-               ieee80211_hw_config(local, 0);
-       }
+       int err;
 
-       sdata->vif.csa_active = false;
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
                err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
@@ -3038,35 +3078,74 @@ static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
                sdata->u.ap.next_beacon = NULL;
 
                if (err < 0)
-                       return;
-               changed |= err;
+                       return err;
+               *changed |= err;
                break;
        case NL80211_IFTYPE_ADHOC:
                err = ieee80211_ibss_finish_csa(sdata);
                if (err < 0)
-                       return;
-               changed |= err;
+                       return err;
+               *changed |= err;
                break;
 #ifdef CONFIG_MAC80211_MESH
        case NL80211_IFTYPE_MESH_POINT:
                err = ieee80211_mesh_finish_csa(sdata);
                if (err < 0)
-                       return;
-               changed |= err;
+                       return err;
+               *changed |= err;
                break;
 #endif
        default:
                WARN_ON(1);
-               return;
+               return -EINVAL;
        }
 
+       return 0;
+}
+
+static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       u32 changed = 0;
+       int err;
+
+       sdata_assert_lock(sdata);
+       lockdep_assert_held(&local->mtx);
+
+       sdata->radar_required = sdata->csa_radar_required;
+       err = ieee80211_vif_change_channel(sdata, &changed);
+       if (err < 0)
+               return err;
+
+       if (!local->use_chanctx) {
+               local->_oper_chandef = sdata->csa_chandef;
+               ieee80211_hw_config(local, 0);
+       }
+
+       sdata->vif.csa_active = false;
+
+       err = ieee80211_set_after_csa_beacon(sdata, &changed);
+       if (err)
+               return err;
+
        ieee80211_bss_info_change_notify(sdata, changed);
+       cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
 
-       ieee80211_wake_queues_by_reason(&sdata->local->hw,
+       if (!ieee80211_csa_needs_block_tx(local))
+               ieee80211_wake_queues_by_reason(&local->hw,
                                        IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
 
-       cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
+       return 0;
+}
+
+static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
+{
+       if (__ieee80211_csa_finalize(sdata)) {
+               sdata_info(sdata, "failed to finalize CSA, disconnecting\n");
+               cfg80211_stop_iface(sdata->local->hw.wiphy, &sdata->wdev,
+                                   GFP_KERNEL);
+       }
 }
 
 void ieee80211_csa_finalize_work(struct work_struct *work)
@@ -3074,8 +3153,11 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
        struct ieee80211_sub_if_data *sdata =
                container_of(work, struct ieee80211_sub_if_data,
                             csa_finalize_work);
+       struct ieee80211_local *local = sdata->local;
 
        sdata_lock(sdata);
+       mutex_lock(&local->mtx);
+
        /* AP might have been stopped while waiting for the lock. */
        if (!sdata->vif.csa_active)
                goto unlock;
@@ -3086,6 +3168,7 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
        ieee80211_csa_finalize(sdata);
 
 unlock:
+       mutex_unlock(&local->mtx);
        sdata_unlock(sdata);
 }
 
@@ -3121,9 +3204,25 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
                if (params->count <= 1)
                        break;
 
-               sdata->csa_counter_offset_beacon =
-                       params->counter_offset_beacon;
-               sdata->csa_counter_offset_presp = params->counter_offset_presp;
+               if ((params->n_counter_offsets_beacon >
+                    IEEE80211_MAX_CSA_COUNTERS_NUM) ||
+                   (params->n_counter_offsets_presp >
+                    IEEE80211_MAX_CSA_COUNTERS_NUM))
+                       return -EINVAL;
+
+               /* make sure we don't have garbage in other counters */
+               memset(sdata->csa_counter_offset_beacon, 0,
+                      sizeof(sdata->csa_counter_offset_beacon));
+               memset(sdata->csa_counter_offset_presp, 0,
+                      sizeof(sdata->csa_counter_offset_presp));
+
+               memcpy(sdata->csa_counter_offset_beacon,
+                      params->counter_offsets_beacon,
+                      params->n_counter_offsets_beacon * sizeof(u16));
+               memcpy(sdata->csa_counter_offset_presp,
+                      params->counter_offsets_presp,
+                      params->n_counter_offsets_presp * sizeof(u16));
+
                err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
                if (err < 0) {
                        kfree(sdata->u.ap.next_beacon);
@@ -3212,16 +3311,18 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
        return 0;
 }
 
-int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
-                            struct cfg80211_csa_settings *params)
+static int
+__ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+                          struct cfg80211_csa_settings *params)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
-       struct ieee80211_chanctx_conf *chanctx_conf;
+       struct ieee80211_chanctx_conf *conf;
        struct ieee80211_chanctx *chanctx;
        int err, num_chanctx, changed = 0;
 
        sdata_assert_lock(sdata);
+       lockdep_assert_held(&local->mtx);
 
        if (!list_empty(&local->roc_list) || local->scanning)
                return -EBUSY;
@@ -3233,23 +3334,24 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                                       &sdata->vif.bss_conf.chandef))
                return -EINVAL;
 
-       rcu_read_lock();
-       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
-       if (!chanctx_conf) {
-               rcu_read_unlock();
+       mutex_lock(&local->chanctx_mtx);
+       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                                        lockdep_is_held(&local->chanctx_mtx));
+       if (!conf) {
+               mutex_unlock(&local->chanctx_mtx);
                return -EBUSY;
        }
 
        /* don't handle for multi-VIF cases */
-       chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
-       if (chanctx->refcount > 1) {
-               rcu_read_unlock();
+       chanctx = container_of(conf, struct ieee80211_chanctx, conf);
+       if (ieee80211_chanctx_refcount(local, chanctx) > 1) {
+               mutex_unlock(&local->chanctx_mtx);
                return -EBUSY;
        }
        num_chanctx = 0;
        list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
                num_chanctx++;
-       rcu_read_unlock();
+       mutex_unlock(&local->chanctx_mtx);
 
        if (num_chanctx > 1)
                return -EBUSY;
@@ -3263,15 +3365,16 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                return err;
 
        sdata->csa_radar_required = params->radar_required;
-
-       if (params->block_tx)
-               ieee80211_stop_queues_by_reason(&local->hw,
-                               IEEE80211_MAX_QUEUE_MAP,
-                               IEEE80211_QUEUE_STOP_REASON_CSA);
-
        sdata->csa_chandef = params->chandef;
+       sdata->csa_block_tx = params->block_tx;
+       sdata->csa_current_counter = params->count;
        sdata->vif.csa_active = true;
 
+       if (sdata->csa_block_tx)
+               ieee80211_stop_queues_by_reason(&local->hw,
+                                       IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+
        if (changed) {
                ieee80211_bss_info_change_notify(sdata, changed);
                drv_channel_switch_beacon(sdata, &params->chandef);
@@ -3283,6 +3386,20 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
        return 0;
 }
 
+int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+                            struct cfg80211_csa_settings *params)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
+       int err;
+
+       mutex_lock(&local->mtx);
+       err = __ieee80211_channel_switch(wiphy, dev, params);
+       mutex_unlock(&local->mtx);
+
+       return err;
+}
+
 static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                             struct cfg80211_mgmt_tx_params *params,
                             u64 *cookie)
@@ -3295,6 +3412,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
        bool need_offchan = false;
        u32 flags;
        int ret;
+       u8 *data;
 
        if (params->dont_wait_for_ack)
                flags = IEEE80211_TX_CTL_NO_ACK;
@@ -3388,7 +3506,20 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
        }
        skb_reserve(skb, local->hw.extra_tx_headroom);
 
-       memcpy(skb_put(skb, params->len), params->buf, params->len);
+       data = skb_put(skb, params->len);
+       memcpy(data, params->buf, params->len);
+
+       /* Update CSA counters */
+       if (sdata->vif.csa_active &&
+           (sdata->vif.type == NL80211_IFTYPE_AP ||
+            sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
+           params->n_csa_offsets) {
+               int i;
+               u8 c = sdata->csa_current_counter;
+
+               for (i = 0; i < params->n_csa_offsets; i++)
+                       data[params->csa_offsets[i]] = c;
+       }
 
        IEEE80211_SKB_CB(skb)->flags = flags;
 
@@ -3497,320 +3628,6 @@ static int ieee80211_set_rekey_data(struct wiphy *wiphy,
        return 0;
 }
 
-static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb)
-{
-       u8 *pos = (void *)skb_put(skb, 7);
-
-       *pos++ = WLAN_EID_EXT_CAPABILITY;
-       *pos++ = 5; /* len */
-       *pos++ = 0x0;
-       *pos++ = 0x0;
-       *pos++ = 0x0;
-       *pos++ = 0x0;
-       *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
-}
-
-static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
-{
-       struct ieee80211_local *local = sdata->local;
-       u16 capab;
-
-       capab = 0;
-       if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ)
-               return capab;
-
-       if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
-               capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
-       if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE))
-               capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
-
-       return capab;
-}
-
-static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr,
-                                      u8 *peer, u8 *bssid)
-{
-       struct ieee80211_tdls_lnkie *lnkid;
-
-       lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
-
-       lnkid->ie_type = WLAN_EID_LINK_ID;
-       lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;
-
-       memcpy(lnkid->bssid, bssid, ETH_ALEN);
-       memcpy(lnkid->init_sta, src_addr, ETH_ALEN);
-       memcpy(lnkid->resp_sta, peer, ETH_ALEN);
-}
-
-static int
-ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
-                              u8 *peer, u8 action_code, u8 dialog_token,
-                              u16 status_code, struct sk_buff *skb)
-{
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
-       struct ieee80211_tdls_data *tf;
-
-       tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
-
-       memcpy(tf->da, peer, ETH_ALEN);
-       memcpy(tf->sa, sdata->vif.addr, ETH_ALEN);
-       tf->ether_type = cpu_to_be16(ETH_P_TDLS);
-       tf->payload_type = WLAN_TDLS_SNAP_RFTYPE;
-
-       switch (action_code) {
-       case WLAN_TDLS_SETUP_REQUEST:
-               tf->category = WLAN_CATEGORY_TDLS;
-               tf->action_code = WLAN_TDLS_SETUP_REQUEST;
-
-               skb_put(skb, sizeof(tf->u.setup_req));
-               tf->u.setup_req.dialog_token = dialog_token;
-               tf->u.setup_req.capability =
-                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
-
-               ieee80211_add_srates_ie(sdata, skb, false, band);
-               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
-               ieee80211_tdls_add_ext_capab(skb);
-               break;
-       case WLAN_TDLS_SETUP_RESPONSE:
-               tf->category = WLAN_CATEGORY_TDLS;
-               tf->action_code = WLAN_TDLS_SETUP_RESPONSE;
-
-               skb_put(skb, sizeof(tf->u.setup_resp));
-               tf->u.setup_resp.status_code = cpu_to_le16(status_code);
-               tf->u.setup_resp.dialog_token = dialog_token;
-               tf->u.setup_resp.capability =
-                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
-
-               ieee80211_add_srates_ie(sdata, skb, false, band);
-               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
-               ieee80211_tdls_add_ext_capab(skb);
-               break;
-       case WLAN_TDLS_SETUP_CONFIRM:
-               tf->category = WLAN_CATEGORY_TDLS;
-               tf->action_code = WLAN_TDLS_SETUP_CONFIRM;
-
-               skb_put(skb, sizeof(tf->u.setup_cfm));
-               tf->u.setup_cfm.status_code = cpu_to_le16(status_code);
-               tf->u.setup_cfm.dialog_token = dialog_token;
-               break;
-       case WLAN_TDLS_TEARDOWN:
-               tf->category = WLAN_CATEGORY_TDLS;
-               tf->action_code = WLAN_TDLS_TEARDOWN;
-
-               skb_put(skb, sizeof(tf->u.teardown));
-               tf->u.teardown.reason_code = cpu_to_le16(status_code);
-               break;
-       case WLAN_TDLS_DISCOVERY_REQUEST:
-               tf->category = WLAN_CATEGORY_TDLS;
-               tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST;
-
-               skb_put(skb, sizeof(tf->u.discover_req));
-               tf->u.discover_req.dialog_token = dialog_token;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int
-ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
-                          u8 *peer, u8 action_code, u8 dialog_token,
-                          u16 status_code, struct sk_buff *skb)
-{
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
-       struct ieee80211_mgmt *mgmt;
-
-       mgmt = (void *)skb_put(skb, 24);
-       memset(mgmt, 0, 24);
-       memcpy(mgmt->da, peer, ETH_ALEN);
-       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
-       memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
-
-       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-                                         IEEE80211_STYPE_ACTION);
-
-       switch (action_code) {
-       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
-               skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp));
-               mgmt->u.action.category = WLAN_CATEGORY_PUBLIC;
-               mgmt->u.action.u.tdls_discover_resp.action_code =
-                       WLAN_PUB_ACTION_TDLS_DISCOVER_RES;
-               mgmt->u.action.u.tdls_discover_resp.dialog_token =
-                       dialog_token;
-               mgmt->u.action.u.tdls_discover_resp.capability =
-                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
-
-               ieee80211_add_srates_ie(sdata, skb, false, band);
-               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
-               ieee80211_tdls_add_ext_capab(skb);
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
-                              u8 *peer, u8 action_code, u8 dialog_token,
-                              u16 status_code, u32 peer_capability,
-                              const u8 *extra_ies, size_t extra_ies_len)
-{
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       struct ieee80211_local *local = sdata->local;
-       struct sk_buff *skb = NULL;
-       bool send_direct;
-       int ret;
-
-       if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
-               return -ENOTSUPP;
-
-       /* make sure we are in managed mode, and associated */
-       if (sdata->vif.type != NL80211_IFTYPE_STATION ||
-           !sdata->u.mgd.associated)
-               return -EINVAL;
-
-       tdls_dbg(sdata, "TDLS mgmt action %d peer %pM\n",
-                action_code, peer);
-
-       skb = dev_alloc_skb(local->hw.extra_tx_headroom +
-                           max(sizeof(struct ieee80211_mgmt),
-                               sizeof(struct ieee80211_tdls_data)) +
-                           50 + /* supported rates */
-                           7 + /* ext capab */
-                           extra_ies_len +
-                           sizeof(struct ieee80211_tdls_lnkie));
-       if (!skb)
-               return -ENOMEM;
-
-       skb_reserve(skb, local->hw.extra_tx_headroom);
-
-       switch (action_code) {
-       case WLAN_TDLS_SETUP_REQUEST:
-       case WLAN_TDLS_SETUP_RESPONSE:
-       case WLAN_TDLS_SETUP_CONFIRM:
-       case WLAN_TDLS_TEARDOWN:
-       case WLAN_TDLS_DISCOVERY_REQUEST:
-               ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer,
-                                                    action_code, dialog_token,
-                                                    status_code, skb);
-               send_direct = false;
-               break;
-       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
-               ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code,
-                                                dialog_token, status_code,
-                                                skb);
-               send_direct = true;
-               break;
-       default:
-               ret = -ENOTSUPP;
-               break;
-       }
-
-       if (ret < 0)
-               goto fail;
-
-       if (extra_ies_len)
-               memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
-
-       /* the TDLS link IE is always added last */
-       switch (action_code) {
-       case WLAN_TDLS_SETUP_REQUEST:
-       case WLAN_TDLS_SETUP_CONFIRM:
-       case WLAN_TDLS_TEARDOWN:
-       case WLAN_TDLS_DISCOVERY_REQUEST:
-               /* we are the initiator */
-               ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer,
-                                          sdata->u.mgd.bssid);
-               break;
-       case WLAN_TDLS_SETUP_RESPONSE:
-       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
-               /* we are the responder */
-               ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr,
-                                          sdata->u.mgd.bssid);
-               break;
-       default:
-               ret = -ENOTSUPP;
-               goto fail;
-       }
-
-       if (send_direct) {
-               ieee80211_tx_skb(sdata, skb);
-               return 0;
-       }
-
-       /*
-        * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise
-        * we should default to AC_VI.
-        */
-       switch (action_code) {
-       case WLAN_TDLS_SETUP_REQUEST:
-       case WLAN_TDLS_SETUP_RESPONSE:
-               skb_set_queue_mapping(skb, IEEE80211_AC_BK);
-               skb->priority = 2;
-               break;
-       default:
-               skb_set_queue_mapping(skb, IEEE80211_AC_VI);
-               skb->priority = 5;
-               break;
-       }
-
-       /* disable bottom halves when entering the Tx path */
-       local_bh_disable();
-       ret = ieee80211_subif_start_xmit(skb, dev);
-       local_bh_enable();
-
-       return ret;
-
-fail:
-       dev_kfree_skb(skb);
-       return ret;
-}
-
-static int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
-                              u8 *peer, enum nl80211_tdls_operation oper)
-{
-       struct sta_info *sta;
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
-       if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
-               return -ENOTSUPP;
-
-       if (sdata->vif.type != NL80211_IFTYPE_STATION)
-               return -EINVAL;
-
-       tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer);
-
-       switch (oper) {
-       case NL80211_TDLS_ENABLE_LINK:
-               rcu_read_lock();
-               sta = sta_info_get(sdata, peer);
-               if (!sta) {
-                       rcu_read_unlock();
-                       return -ENOLINK;
-               }
-
-               set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
-               rcu_read_unlock();
-               break;
-       case NL80211_TDLS_DISABLE_LINK:
-               return sta_info_destroy_addr(sdata, peer);
-       case NL80211_TDLS_TEARDOWN:
-       case NL80211_TDLS_SETUP:
-       case NL80211_TDLS_DISCOVERY_REQ:
-               /* We don't support in-driver setup/teardown/discovery */
-               return -ENOTSUPP;
-       default:
-               return -ENOTSUPP;
-       }
-
-       return 0;
-}
-
 static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
                                  const u8 *peer, u64 *cookie)
 {
@@ -3949,6 +3766,21 @@ static int ieee80211_set_qos_map(struct wiphy *wiphy,
        return 0;
 }
 
+static int ieee80211_set_ap_chanwidth(struct wiphy *wiphy,
+                                     struct net_device *dev,
+                                     struct cfg80211_chan_def *chandef)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       int ret;
+       u32 changed = 0;
+
+       ret = ieee80211_vif_change_bandwidth(sdata, chandef, &changed);
+       if (ret == 0)
+               ieee80211_bss_info_change_notify(sdata, changed);
+
+       return ret;
+}
+
 const struct cfg80211_ops mac80211_config_ops = {
        .add_virtual_intf = ieee80211_add_iface,
        .del_virtual_intf = ieee80211_del_iface,
@@ -4029,4 +3861,5 @@ const struct cfg80211_ops mac80211_config_ops = {
        .start_radar_detection = ieee80211_start_radar_detection,
        .channel_switch = ieee80211_channel_switch,
        .set_qos_map = ieee80211_set_qos_map,
+       .set_ap_chanwidth = ieee80211_set_ap_chanwidth,
 };
index 75b5dd2c9267f10e8cb0e5680c3aa11c94a5dbe4..a310e33972de8881bf4dd71bdff36d55fa966226 100644 (file)
@@ -9,6 +9,170 @@
 #include "ieee80211_i.h"
 #include "driver-ops.h"
 
+static int ieee80211_chanctx_num_assigned(struct ieee80211_local *local,
+                                         struct ieee80211_chanctx *ctx)
+{
+       struct ieee80211_sub_if_data *sdata;
+       int num = 0;
+
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       list_for_each_entry(sdata, &ctx->assigned_vifs, assigned_chanctx_list)
+               num++;
+
+       return num;
+}
+
+static int ieee80211_chanctx_num_reserved(struct ieee80211_local *local,
+                                         struct ieee80211_chanctx *ctx)
+{
+       struct ieee80211_sub_if_data *sdata;
+       int num = 0;
+
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       list_for_each_entry(sdata, &ctx->reserved_vifs, reserved_chanctx_list)
+               num++;
+
+       return num;
+}
+
+int ieee80211_chanctx_refcount(struct ieee80211_local *local,
+                              struct ieee80211_chanctx *ctx)
+{
+       return ieee80211_chanctx_num_assigned(local, ctx) +
+              ieee80211_chanctx_num_reserved(local, ctx);
+}
+
+static int ieee80211_num_chanctx(struct ieee80211_local *local)
+{
+       struct ieee80211_chanctx *ctx;
+       int num = 0;
+
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       list_for_each_entry(ctx, &local->chanctx_list, list)
+               num++;
+
+       return num;
+}
+
+static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local)
+{
+       lockdep_assert_held(&local->chanctx_mtx);
+       return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local);
+}
+
+static const struct cfg80211_chan_def *
+ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local,
+                                  struct ieee80211_chanctx *ctx,
+                                  const struct cfg80211_chan_def *compat)
+{
+       struct ieee80211_sub_if_data *sdata;
+
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       list_for_each_entry(sdata, &ctx->reserved_vifs,
+                           reserved_chanctx_list) {
+               if (!compat)
+                       compat = &sdata->reserved_chandef;
+
+               compat = cfg80211_chandef_compatible(&sdata->reserved_chandef,
+                                                    compat);
+               if (!compat)
+                       break;
+       }
+
+       return compat;
+}
+
+static const struct cfg80211_chan_def *
+ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local *local,
+                                      struct ieee80211_chanctx *ctx,
+                                      const struct cfg80211_chan_def *compat)
+{
+       struct ieee80211_sub_if_data *sdata;
+
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       list_for_each_entry(sdata, &ctx->assigned_vifs,
+                           assigned_chanctx_list) {
+               if (sdata->reserved_chanctx != NULL)
+                       continue;
+
+               if (!compat)
+                       compat = &sdata->vif.bss_conf.chandef;
+
+               compat = cfg80211_chandef_compatible(
+                               &sdata->vif.bss_conf.chandef, compat);
+               if (!compat)
+                       break;
+       }
+
+       return compat;
+}
+
+static const struct cfg80211_chan_def *
+ieee80211_chanctx_combined_chandef(struct ieee80211_local *local,
+                                  struct ieee80211_chanctx *ctx,
+                                  const struct cfg80211_chan_def *compat)
+{
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       compat = ieee80211_chanctx_reserved_chandef(local, ctx, compat);
+       if (!compat)
+               return NULL;
+
+       compat = ieee80211_chanctx_non_reserved_chandef(local, ctx, compat);
+       if (!compat)
+               return NULL;
+
+       return compat;
+}
+
+static bool
+ieee80211_chanctx_can_reserve_chandef(struct ieee80211_local *local,
+                                     struct ieee80211_chanctx *ctx,
+                                     const struct cfg80211_chan_def *def)
+{
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       if (ieee80211_chanctx_combined_chandef(local, ctx, def))
+               return true;
+
+       if (!list_empty(&ctx->reserved_vifs) &&
+           ieee80211_chanctx_reserved_chandef(local, ctx, def))
+               return true;
+
+       return false;
+}
+
+static struct ieee80211_chanctx *
+ieee80211_find_reservation_chanctx(struct ieee80211_local *local,
+                                  const struct cfg80211_chan_def *chandef,
+                                  enum ieee80211_chanctx_mode mode)
+{
+       struct ieee80211_chanctx *ctx;
+
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
+               return NULL;
+
+       list_for_each_entry(ctx, &local->chanctx_list, list) {
+               if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
+                       continue;
+
+               if (!ieee80211_chanctx_can_reserve_chandef(local, ctx,
+                                                          chandef))
+                       continue;
+
+               return ctx;
+       }
+
+       return NULL;
+}
+
 static enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta)
 {
        switch (sta->bandwidth) {
@@ -190,6 +354,11 @@ ieee80211_find_chanctx(struct ieee80211_local *local,
                if (!compat)
                        continue;
 
+               compat = ieee80211_chanctx_reserved_chandef(local, ctx,
+                                                           compat);
+               if (!compat)
+                       continue;
+
                ieee80211_change_chanctx(local, ctx, compat);
 
                return ctx;
@@ -217,62 +386,91 @@ static bool ieee80211_is_radar_required(struct ieee80211_local *local)
 }
 
 static struct ieee80211_chanctx *
-ieee80211_new_chanctx(struct ieee80211_local *local,
-                     const struct cfg80211_chan_def *chandef,
-                     enum ieee80211_chanctx_mode mode)
+ieee80211_alloc_chanctx(struct ieee80211_local *local,
+                       const struct cfg80211_chan_def *chandef,
+                       enum ieee80211_chanctx_mode mode)
 {
        struct ieee80211_chanctx *ctx;
-       u32 changed;
-       int err;
 
        lockdep_assert_held(&local->chanctx_mtx);
 
        ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
        if (!ctx)
-               return ERR_PTR(-ENOMEM);
+               return NULL;
 
+       INIT_LIST_HEAD(&ctx->assigned_vifs);
+       INIT_LIST_HEAD(&ctx->reserved_vifs);
        ctx->conf.def = *chandef;
        ctx->conf.rx_chains_static = 1;
        ctx->conf.rx_chains_dynamic = 1;
        ctx->mode = mode;
        ctx->conf.radar_enabled = ieee80211_is_radar_required(local);
        ieee80211_recalc_chanctx_min_def(local, ctx);
+
+       return ctx;
+}
+
+static int ieee80211_add_chanctx(struct ieee80211_local *local,
+                                struct ieee80211_chanctx *ctx)
+{
+       u32 changed;
+       int err;
+
+       lockdep_assert_held(&local->mtx);
+       lockdep_assert_held(&local->chanctx_mtx);
+
        if (!local->use_chanctx)
                local->hw.conf.radar_enabled = ctx->conf.radar_enabled;
 
-       /* we hold the mutex to prevent idle from changing */
-       lockdep_assert_held(&local->mtx);
        /* turn idle off *before* setting channel -- some drivers need that */
        changed = ieee80211_idle_off(local);
        if (changed)
                ieee80211_hw_config(local, changed);
 
        if (!local->use_chanctx) {
-               local->_oper_chandef = *chandef;
+               local->_oper_chandef = ctx->conf.def;
                ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
        } else {
                err = drv_add_chanctx(local, ctx);
                if (err) {
-                       kfree(ctx);
                        ieee80211_recalc_idle(local);
-                       return ERR_PTR(err);
+                       return err;
                }
        }
 
-       /* and keep the mutex held until the new chanctx is on the list */
-       list_add_rcu(&ctx->list, &local->chanctx_list);
+       return 0;
+}
 
+static struct ieee80211_chanctx *
+ieee80211_new_chanctx(struct ieee80211_local *local,
+                     const struct cfg80211_chan_def *chandef,
+                     enum ieee80211_chanctx_mode mode)
+{
+       struct ieee80211_chanctx *ctx;
+       int err;
+
+       lockdep_assert_held(&local->mtx);
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       ctx = ieee80211_alloc_chanctx(local, chandef, mode);
+       if (!ctx)
+               return ERR_PTR(-ENOMEM);
+
+       err = ieee80211_add_chanctx(local, ctx);
+       if (err) {
+               kfree(ctx);
+               return ERR_PTR(err);
+       }
+
+       list_add_rcu(&ctx->list, &local->chanctx_list);
        return ctx;
 }
 
-static void ieee80211_free_chanctx(struct ieee80211_local *local,
-                                  struct ieee80211_chanctx *ctx)
+static void ieee80211_del_chanctx(struct ieee80211_local *local,
+                                 struct ieee80211_chanctx *ctx)
 {
-       bool check_single_channel = false;
        lockdep_assert_held(&local->chanctx_mtx);
 
-       WARN_ON_ONCE(ctx->refcount != 0);
-
        if (!local->use_chanctx) {
                struct cfg80211_chan_def *chandef = &local->_oper_chandef;
                chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
@@ -282,8 +480,9 @@ static void ieee80211_free_chanctx(struct ieee80211_local *local,
                /* NOTE: Disabling radar is only valid here for
                 * single channel context. To be sure, check it ...
                 */
-               if (local->hw.conf.radar_enabled)
-                       check_single_channel = true;
+               WARN_ON(local->hw.conf.radar_enabled &&
+                       !list_empty(&local->chanctx_list));
+
                local->hw.conf.radar_enabled = false;
 
                ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
@@ -291,39 +490,19 @@ static void ieee80211_free_chanctx(struct ieee80211_local *local,
                drv_remove_chanctx(local, ctx);
        }
 
-       list_del_rcu(&ctx->list);
-       kfree_rcu(ctx, rcu_head);
-
-       /* throw a warning if this wasn't the only channel context. */
-       WARN_ON(check_single_channel && !list_empty(&local->chanctx_list));
-
        ieee80211_recalc_idle(local);
 }
 
-static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
-                                       struct ieee80211_chanctx *ctx)
+static void ieee80211_free_chanctx(struct ieee80211_local *local,
+                                  struct ieee80211_chanctx *ctx)
 {
-       struct ieee80211_local *local = sdata->local;
-       int ret;
-
        lockdep_assert_held(&local->chanctx_mtx);
 
-       ret = drv_assign_vif_chanctx(local, sdata, ctx);
-       if (ret)
-               return ret;
+       WARN_ON_ONCE(ieee80211_chanctx_refcount(local, ctx) != 0);
 
-       rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
-       ctx->refcount++;
-
-       ieee80211_recalc_txpower(sdata);
-       ieee80211_recalc_chanctx_min_def(local, ctx);
-       sdata->vif.bss_conf.idle = false;
-
-       if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
-           sdata->vif.type != NL80211_IFTYPE_MONITOR)
-               ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
-
-       return 0;
+       list_del_rcu(&ctx->list);
+       ieee80211_del_chanctx(local, ctx);
+       kfree_rcu(ctx, rcu_head);
 }
 
 static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
@@ -384,30 +563,58 @@ static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
        drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR);
 }
 
-static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
-                                          struct ieee80211_chanctx *ctx)
+static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
+                                       struct ieee80211_chanctx *new_ctx)
 {
        struct ieee80211_local *local = sdata->local;
+       struct ieee80211_chanctx_conf *conf;
+       struct ieee80211_chanctx *curr_ctx = NULL;
+       int ret = 0;
 
-       lockdep_assert_held(&local->chanctx_mtx);
+       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                                        lockdep_is_held(&local->chanctx_mtx));
 
-       ctx->refcount--;
-       rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
+       if (conf) {
+               curr_ctx = container_of(conf, struct ieee80211_chanctx, conf);
 
-       sdata->vif.bss_conf.idle = true;
+               drv_unassign_vif_chanctx(local, sdata, curr_ctx);
+               conf = NULL;
+               list_del(&sdata->assigned_chanctx_list);
+       }
 
-       if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
-           sdata->vif.type != NL80211_IFTYPE_MONITOR)
-               ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
+       if (new_ctx) {
+               ret = drv_assign_vif_chanctx(local, sdata, new_ctx);
+               if (ret)
+                       goto out;
 
-       drv_unassign_vif_chanctx(local, sdata, ctx);
+               conf = &new_ctx->conf;
+               list_add(&sdata->assigned_chanctx_list,
+                        &new_ctx->assigned_vifs);
+       }
+
+out:
+       rcu_assign_pointer(sdata->vif.chanctx_conf, conf);
+
+       sdata->vif.bss_conf.idle = !conf;
+
+       if (curr_ctx && ieee80211_chanctx_num_assigned(local, curr_ctx) > 0) {
+               ieee80211_recalc_chanctx_chantype(local, curr_ctx);
+               ieee80211_recalc_smps_chanctx(local, curr_ctx);
+               ieee80211_recalc_radar_chanctx(local, curr_ctx);
+               ieee80211_recalc_chanctx_min_def(local, curr_ctx);
+       }
 
-       if (ctx->refcount > 0) {
-               ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
-               ieee80211_recalc_smps_chanctx(local, ctx);
-               ieee80211_recalc_radar_chanctx(local, ctx);
-               ieee80211_recalc_chanctx_min_def(local, ctx);
+       if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) {
+               ieee80211_recalc_txpower(sdata);
+               ieee80211_recalc_chanctx_min_def(local, new_ctx);
        }
+
+       if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
+           sdata->vif.type != NL80211_IFTYPE_MONITOR)
+               ieee80211_bss_info_change_notify(sdata,
+                                                BSS_CHANGED_IDLE);
+
+       return ret;
 }
 
 static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
@@ -425,8 +632,11 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
 
        ctx = container_of(conf, struct ieee80211_chanctx, conf);
 
-       ieee80211_unassign_vif_chanctx(sdata, ctx);
-       if (ctx->refcount == 0)
+       if (sdata->reserved_chanctx)
+               ieee80211_vif_unreserve_chanctx(sdata);
+
+       ieee80211_assign_vif_chanctx(sdata, NULL);
+       if (ieee80211_chanctx_refcount(local, ctx) == 0)
                ieee80211_free_chanctx(local, ctx);
 }
 
@@ -526,6 +736,7 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx *ctx;
+       u8 radar_detect_width = 0;
        int ret;
 
        lockdep_assert_held(&local->mtx);
@@ -533,6 +744,22 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
        WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
 
        mutex_lock(&local->chanctx_mtx);
+
+       ret = cfg80211_chandef_dfs_required(local->hw.wiphy,
+                                           chandef,
+                                           sdata->wdev.iftype);
+       if (ret < 0)
+               goto out;
+       if (ret > 0)
+               radar_detect_width = BIT(chandef->width);
+
+       sdata->radar_required = ret;
+
+       ret = ieee80211_check_combinations(sdata, chandef, mode,
+                                          radar_detect_width);
+       if (ret < 0)
+               goto out;
+
        __ieee80211_vif_release_channel(sdata);
 
        ctx = ieee80211_find_chanctx(local, chandef, mode);
@@ -548,7 +775,7 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
        ret = ieee80211_assign_vif_chanctx(sdata, ctx);
        if (ret) {
                /* if assign fails refcount stays the same */
-               if (ctx->refcount == 0)
+               if (ieee80211_chanctx_refcount(local, ctx) == 0)
                        ieee80211_free_chanctx(local, ctx);
                goto out;
        }
@@ -560,15 +787,47 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
        return ret;
 }
 
+static int __ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
+                                         struct ieee80211_chanctx *ctx,
+                                         u32 *changed)
+{
+       struct ieee80211_local *local = sdata->local;
+       const struct cfg80211_chan_def *chandef = &sdata->csa_chandef;
+       u32 chanctx_changed = 0;
+
+       if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
+                                    IEEE80211_CHAN_DISABLED))
+               return -EINVAL;
+
+       if (ieee80211_chanctx_refcount(local, ctx) != 1)
+               return -EINVAL;
+
+       if (sdata->vif.bss_conf.chandef.width != chandef->width) {
+               chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH;
+               *changed |= BSS_CHANGED_BANDWIDTH;
+       }
+
+       sdata->vif.bss_conf.chandef = *chandef;
+       ctx->conf.def = *chandef;
+
+       chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
+       drv_change_chanctx(local, ctx, chanctx_changed);
+
+       ieee80211_recalc_chanctx_chantype(local, ctx);
+       ieee80211_recalc_smps_chanctx(local, ctx);
+       ieee80211_recalc_radar_chanctx(local, ctx);
+       ieee80211_recalc_chanctx_min_def(local, ctx);
+
+       return 0;
+}
+
 int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
                                 u32 *changed)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx_conf *conf;
        struct ieee80211_chanctx *ctx;
-       const struct cfg80211_chan_def *chandef = &sdata->csa_chandef;
        int ret;
-       u32 chanctx_changed = 0;
 
        lockdep_assert_held(&local->mtx);
 
@@ -576,11 +835,94 @@ int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
        if (WARN_ON(!sdata->vif.csa_active))
                return -EINVAL;
 
-       if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
-                                    IEEE80211_CHAN_DISABLED))
+       mutex_lock(&local->chanctx_mtx);
+       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                                        lockdep_is_held(&local->chanctx_mtx));
+       if (!conf) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ctx = container_of(conf, struct ieee80211_chanctx, conf);
+
+       ret = __ieee80211_vif_change_channel(sdata, ctx, changed);
+ out:
+       mutex_unlock(&local->chanctx_mtx);
+       return ret;
+}
+
+static void
+__ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
+                                     bool clear)
+{
+       struct ieee80211_local *local __maybe_unused = sdata->local;
+       struct ieee80211_sub_if_data *vlan;
+       struct ieee80211_chanctx_conf *conf;
+
+       if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
+               return;
+
+       lockdep_assert_held(&local->mtx);
+
+       /* Check that conf exists, even when clearing this function
+        * must be called with the AP's channel context still there
+        * as it would otherwise cause VLANs to have an invalid
+        * channel context pointer for a while, possibly pointing
+        * to a channel context that has already been freed.
+        */
+       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                                        lockdep_is_held(&local->chanctx_mtx));
+       WARN_ON(!conf);
+
+       if (clear)
+               conf = NULL;
+
+       list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+               rcu_assign_pointer(vlan->vif.chanctx_conf, conf);
+}
+
+void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
+                                        bool clear)
+{
+       struct ieee80211_local *local = sdata->local;
+
+       mutex_lock(&local->chanctx_mtx);
+
+       __ieee80211_vif_copy_chanctx_to_vlans(sdata, clear);
+
+       mutex_unlock(&local->chanctx_mtx);
+}
+
+int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_chanctx *ctx = sdata->reserved_chanctx;
+
+       lockdep_assert_held(&sdata->local->chanctx_mtx);
+
+       if (WARN_ON(!ctx))
                return -EINVAL;
 
+       list_del(&sdata->reserved_chanctx_list);
+       sdata->reserved_chanctx = NULL;
+
+       if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0)
+               ieee80211_free_chanctx(sdata->local, ctx);
+
+       return 0;
+}
+
+int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
+                                 const struct cfg80211_chan_def *chandef,
+                                 enum ieee80211_chanctx_mode mode,
+                                 bool radar_required)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_chanctx_conf *conf;
+       struct ieee80211_chanctx *new_ctx, *curr_ctx;
+       int ret = 0;
+
        mutex_lock(&local->chanctx_mtx);
+
        conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
                                         lockdep_is_held(&local->chanctx_mtx));
        if (!conf) {
@@ -588,30 +930,108 @@ int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
                goto out;
        }
 
-       ctx = container_of(conf, struct ieee80211_chanctx, conf);
-       if (ctx->refcount != 1) {
+       curr_ctx = container_of(conf, struct ieee80211_chanctx, conf);
+
+       new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode);
+       if (!new_ctx) {
+               if (ieee80211_chanctx_refcount(local, curr_ctx) == 1 &&
+                   (local->hw.flags & IEEE80211_HW_CHANGE_RUNNING_CHANCTX)) {
+                       /* if we're the only users of the chanctx and
+                        * the driver supports changing a running
+                        * context, reserve our current context
+                        */
+                       new_ctx = curr_ctx;
+               } else if (ieee80211_can_create_new_chanctx(local)) {
+                       /* create a new context and reserve it */
+                       new_ctx = ieee80211_new_chanctx(local, chandef, mode);
+                       if (IS_ERR(new_ctx)) {
+                               ret = PTR_ERR(new_ctx);
+                               goto out;
+                       }
+               } else {
+                       ret = -EBUSY;
+                       goto out;
+               }
+       }
+
+       list_add(&sdata->reserved_chanctx_list, &new_ctx->reserved_vifs);
+       sdata->reserved_chanctx = new_ctx;
+       sdata->reserved_chandef = *chandef;
+       sdata->reserved_radar_required = radar_required;
+out:
+       mutex_unlock(&local->chanctx_mtx);
+       return ret;
+}
+
+int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata,
+                                      u32 *changed)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_chanctx *ctx;
+       struct ieee80211_chanctx *old_ctx;
+       struct ieee80211_chanctx_conf *conf;
+       int ret;
+       u32 tmp_changed = *changed;
+
+       /* TODO: need to recheck if the chandef is usable etc.? */
+
+       lockdep_assert_held(&local->mtx);
+
+       mutex_lock(&local->chanctx_mtx);
+
+       ctx = sdata->reserved_chanctx;
+       if (WARN_ON(!ctx)) {
                ret = -EINVAL;
                goto out;
        }
 
-       if (sdata->vif.bss_conf.chandef.width != chandef->width) {
-               chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH;
-               *changed |= BSS_CHANGED_BANDWIDTH;
+       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                                        lockdep_is_held(&local->chanctx_mtx));
+       if (!conf) {
+               ret = -EINVAL;
+               goto out;
        }
 
-       sdata->vif.bss_conf.chandef = *chandef;
-       ctx->conf.def = *chandef;
+       old_ctx = container_of(conf, struct ieee80211_chanctx, conf);
 
-       chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
-       drv_change_chanctx(local, ctx, chanctx_changed);
+       if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width)
+               tmp_changed |= BSS_CHANGED_BANDWIDTH;
+
+       sdata->vif.bss_conf.chandef = sdata->reserved_chandef;
+
+       /* unref our reservation */
+       sdata->reserved_chanctx = NULL;
+       sdata->radar_required = sdata->reserved_radar_required;
+       list_del(&sdata->reserved_chanctx_list);
+
+       if (old_ctx == ctx) {
+               /* This is our own context, just change it */
+               ret = __ieee80211_vif_change_channel(sdata, old_ctx,
+                                                    &tmp_changed);
+               if (ret)
+                       goto out;
+       } else {
+               ret = ieee80211_assign_vif_chanctx(sdata, ctx);
+               if (ieee80211_chanctx_refcount(local, old_ctx) == 0)
+                       ieee80211_free_chanctx(local, old_ctx);
+               if (ret) {
+                       /* if assign fails refcount stays the same */
+                       if (ieee80211_chanctx_refcount(local, ctx) == 0)
+                               ieee80211_free_chanctx(local, ctx);
+                       goto out;
+               }
+
+               if (sdata->vif.type == NL80211_IFTYPE_AP)
+                       __ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
+       }
+
+       *changed = tmp_changed;
 
        ieee80211_recalc_chanctx_chantype(local, ctx);
        ieee80211_recalc_smps_chanctx(local, ctx);
        ieee80211_recalc_radar_chanctx(local, ctx);
        ieee80211_recalc_chanctx_min_def(local, ctx);
-
-       ret = 0;
- out:
+out:
        mutex_unlock(&local->chanctx_mtx);
        return ret;
 }
@@ -695,40 +1115,6 @@ void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata)
        mutex_unlock(&local->chanctx_mtx);
 }
 
-void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
-                                        bool clear)
-{
-       struct ieee80211_local *local = sdata->local;
-       struct ieee80211_sub_if_data *vlan;
-       struct ieee80211_chanctx_conf *conf;
-
-       ASSERT_RTNL();
-
-       if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
-               return;
-
-       mutex_lock(&local->chanctx_mtx);
-
-       /*
-        * Check that conf exists, even when clearing this function
-        * must be called with the AP's channel context still there
-        * as it would otherwise cause VLANs to have an invalid
-        * channel context pointer for a while, possibly pointing
-        * to a channel context that has already been freed.
-        */
-       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
-                               lockdep_is_held(&local->chanctx_mtx));
-       WARN_ON(!conf);
-
-       if (clear)
-               conf = NULL;
-
-       list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
-               rcu_assign_pointer(vlan->vif.chanctx_conf, conf);
-
-       mutex_unlock(&local->chanctx_mtx);
-}
-
 void ieee80211_iter_chan_contexts_atomic(
        struct ieee80211_hw *hw,
        void (*iter)(struct ieee80211_hw *hw,
index fa16e54980a1d3e76ce2f85fcb3253eb2599e838..0e963bc1ceac3109f378431a617b853a7166df37 100644 (file)
@@ -128,7 +128,7 @@ static ssize_t sta_tx_latency_stat_write(struct file *file,
        if (!strcmp(buf, TX_LATENCY_DISABLED)) {
                if (!tx_latency)
                        goto unlock;
-               rcu_assign_pointer(local->tx_latency, NULL);
+               RCU_INIT_POINTER(local->tx_latency, NULL);
                synchronize_rcu();
                kfree(tx_latency);
                goto unlock;
index 214ed4ecd739f10ae201e6dfa9112c0dd943f5b8..60c35afee29d551727a2969b25b84998ca5501c4 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef __MAC80211_DEBUGFS_H
 #define __MAC80211_DEBUGFS_H
 
+#include "ieee80211_i.h"
+
 #ifdef CONFIG_MAC80211_DEBUGFS
 void debugfs_hw_add(struct ieee80211_local *local);
 int __printf(4, 5) mac80211_format_buffer(char __user *userbuf, size_t count,
index 40a648938985db178b6997f1541bb47a3247b5a8..e205ebabfa5015f5375a60cb79af9b2bfdbd9f80 100644 (file)
@@ -34,8 +34,7 @@ static ssize_t ieee80211_if_read(
        ssize_t ret = -EINVAL;
 
        read_lock(&dev_base_lock);
-       if (sdata->dev->reg_state == NETREG_REGISTERED)
-               ret = (*format)(sdata, buf, sizeof(buf));
+       ret = (*format)(sdata, buf, sizeof(buf));
        read_unlock(&dev_base_lock);
 
        if (ret >= 0)
@@ -62,8 +61,7 @@ static ssize_t ieee80211_if_write(
 
        ret = -ENODEV;
        rtnl_lock();
-       if (sdata->dev->reg_state == NETREG_REGISTERED)
-               ret = (*write)(sdata, buf, count);
+       ret = (*write)(sdata, buf, count);
        rtnl_unlock();
 
        return ret;
index 79025e79f4d6459dd99de5ad496e351e123f53b7..9f5501a9a79506266decdb83d0ad78c9d6e9bdf9 100644 (file)
@@ -3,6 +3,8 @@
 #ifndef __IEEE80211_DEBUGFS_NETDEV_H
 #define __IEEE80211_DEBUGFS_NETDEV_H
 
+#include "ieee80211_i.h"
+
 #ifdef CONFIG_MAC80211_DEBUGFS
 void ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data *sdata);
 void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata);
index fc689f5d971e259381f0a26e13079f1a727fe704..bd782dcffcc7b81478277bcb2b5545dfef545da8 100644 (file)
@@ -5,11 +5,11 @@
 #include "ieee80211_i.h"
 #include "trace.h"
 
-static inline void check_sdata_in_driver(struct ieee80211_sub_if_data *sdata)
+static inline bool check_sdata_in_driver(struct ieee80211_sub_if_data *sdata)
 {
-       WARN(!(sdata->flags & IEEE80211_SDATA_IN_DRIVER),
-            "%s:  Failed check-sdata-in-driver check, flags: 0x%x\n",
-            sdata->dev ? sdata->dev->name : sdata->name, sdata->flags);
+       return !WARN(!(sdata->flags & IEEE80211_SDATA_IN_DRIVER),
+                    "%s:  Failed check-sdata-in-driver check, flags: 0x%x\n",
+                    sdata->dev ? sdata->dev->name : sdata->name, sdata->flags);
 }
 
 static inline struct ieee80211_sub_if_data *
@@ -168,7 +168,8 @@ static inline int drv_change_interface(struct ieee80211_local *local,
 
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_change_interface(local, sdata, type, p2p);
        ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p);
@@ -181,7 +182,8 @@ static inline void drv_remove_interface(struct ieee80211_local *local,
 {
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_remove_interface(local, sdata);
        local->ops->remove_interface(&local->hw, &sdata->vif);
@@ -219,7 +221,8 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,
                         sdata->vif.type == NL80211_IFTYPE_MONITOR))
                return;
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_bss_info_changed(local, sdata, info, changed);
        if (local->ops->bss_info_changed)
@@ -278,7 +281,8 @@ static inline int drv_set_key(struct ieee80211_local *local,
        might_sleep();
 
        sdata = get_bss_sdata(sdata);
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_set_key(local, cmd, sdata, sta, key);
        ret = local->ops->set_key(&local->hw, cmd, &sdata->vif, sta, key);
@@ -298,7 +302,8 @@ static inline void drv_update_tkip_key(struct ieee80211_local *local,
                ista = &sta->sta;
 
        sdata = get_bss_sdata(sdata);
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_update_tkip_key(local, sdata, conf, ista, iv32);
        if (local->ops->update_tkip_key)
@@ -315,7 +320,8 @@ static inline int drv_hw_scan(struct ieee80211_local *local,
 
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_hw_scan(local, sdata);
        ret = local->ops->hw_scan(&local->hw, &sdata->vif, req);
@@ -328,7 +334,8 @@ static inline void drv_cancel_hw_scan(struct ieee80211_local *local,
 {
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_cancel_hw_scan(local, sdata);
        local->ops->cancel_hw_scan(&local->hw, &sdata->vif);
@@ -345,7 +352,8 @@ drv_sched_scan_start(struct ieee80211_local *local,
 
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_sched_scan_start(local, sdata);
        ret = local->ops->sched_scan_start(&local->hw, &sdata->vif,
@@ -361,7 +369,8 @@ static inline int drv_sched_scan_stop(struct ieee80211_local *local,
 
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_sched_scan_stop(local, sdata);
        ret = local->ops->sched_scan_stop(&local->hw, &sdata->vif);
@@ -462,7 +471,8 @@ static inline void drv_sta_notify(struct ieee80211_local *local,
                                  struct ieee80211_sta *sta)
 {
        sdata = get_bss_sdata(sdata);
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_sta_notify(local, sdata, cmd, sta);
        if (local->ops->sta_notify)
@@ -479,7 +489,8 @@ static inline int drv_sta_add(struct ieee80211_local *local,
        might_sleep();
 
        sdata = get_bss_sdata(sdata);
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_sta_add(local, sdata, sta);
        if (local->ops->sta_add)
@@ -497,7 +508,8 @@ static inline void drv_sta_remove(struct ieee80211_local *local,
        might_sleep();
 
        sdata = get_bss_sdata(sdata);
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_sta_remove(local, sdata, sta);
        if (local->ops->sta_remove)
@@ -515,7 +527,8 @@ static inline void drv_sta_add_debugfs(struct ieee80211_local *local,
        might_sleep();
 
        sdata = get_bss_sdata(sdata);
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        if (local->ops->sta_add_debugfs)
                local->ops->sta_add_debugfs(&local->hw, &sdata->vif,
@@ -545,7 +558,8 @@ static inline void drv_sta_pre_rcu_remove(struct ieee80211_local *local,
        might_sleep();
 
        sdata = get_bss_sdata(sdata);
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_sta_pre_rcu_remove(local, sdata, &sta->sta);
        if (local->ops->sta_pre_rcu_remove)
@@ -566,7 +580,8 @@ int drv_sta_state(struct ieee80211_local *local,
        might_sleep();
 
        sdata = get_bss_sdata(sdata);
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_sta_state(local, sdata, &sta->sta, old_state, new_state);
        if (local->ops->sta_state) {
@@ -590,7 +605,8 @@ static inline void drv_sta_rc_update(struct ieee80211_local *local,
                                     struct ieee80211_sta *sta, u32 changed)
 {
        sdata = get_bss_sdata(sdata);
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED &&
                (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
@@ -612,7 +628,8 @@ static inline int drv_conf_tx(struct ieee80211_local *local,
 
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_conf_tx(local, sdata, ac, params);
        if (local->ops->conf_tx)
@@ -629,7 +646,8 @@ static inline u64 drv_get_tsf(struct ieee80211_local *local,
 
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return ret;
 
        trace_drv_get_tsf(local, sdata);
        if (local->ops->get_tsf)
@@ -644,7 +662,8 @@ static inline void drv_set_tsf(struct ieee80211_local *local,
 {
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_set_tsf(local, sdata, tsf);
        if (local->ops->set_tsf)
@@ -657,7 +676,8 @@ static inline void drv_reset_tsf(struct ieee80211_local *local,
 {
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_reset_tsf(local, sdata);
        if (local->ops->reset_tsf)
@@ -689,7 +709,8 @@ static inline int drv_ampdu_action(struct ieee80211_local *local,
        might_sleep();
 
        sdata = get_bss_sdata(sdata);
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_ampdu_action(local, sdata, action, sta, tid, ssn, buf_size);
 
@@ -726,13 +747,19 @@ static inline void drv_rfkill_poll(struct ieee80211_local *local)
 }
 
 static inline void drv_flush(struct ieee80211_local *local,
+                            struct ieee80211_sub_if_data *sdata,
                             u32 queues, bool drop)
 {
+       struct ieee80211_vif *vif = sdata ? &sdata->vif : NULL;
+
        might_sleep();
 
+       if (sdata && !check_sdata_in_driver(sdata))
+               return;
+
        trace_drv_flush(local, queues, drop);
        if (local->ops->flush)
-               local->ops->flush(&local->hw, queues, drop);
+               local->ops->flush(&local->hw, vif, queues, drop);
        trace_drv_return_void(local);
 }
 
@@ -848,7 +875,8 @@ static inline int drv_set_bitrate_mask(struct ieee80211_local *local,
 
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_set_bitrate_mask(local, sdata, mask);
        if (local->ops->set_bitrate_mask)
@@ -863,7 +891,8 @@ static inline void drv_set_rekey_data(struct ieee80211_local *local,
                                      struct ieee80211_sub_if_data *sdata,
                                      struct cfg80211_gtk_rekey_data *data)
 {
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_set_rekey_data(local, sdata, data);
        if (local->ops->set_rekey_data)
@@ -931,7 +960,8 @@ static inline void drv_mgd_prepare_tx(struct ieee80211_local *local,
 {
        might_sleep();
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
        WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION);
 
        trace_drv_mgd_prepare_tx(local, sdata);
@@ -958,6 +988,9 @@ static inline int drv_add_chanctx(struct ieee80211_local *local,
 static inline void drv_remove_chanctx(struct ieee80211_local *local,
                                      struct ieee80211_chanctx *ctx)
 {
+       if (WARN_ON(!ctx->driver_present))
+               return;
+
        trace_drv_remove_chanctx(local, ctx);
        if (local->ops->remove_chanctx)
                local->ops->remove_chanctx(&local->hw, &ctx->conf);
@@ -983,7 +1016,8 @@ static inline int drv_assign_vif_chanctx(struct ieee80211_local *local,
 {
        int ret = 0;
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_assign_vif_chanctx(local, sdata, ctx);
        if (local->ops->assign_vif_chanctx) {
@@ -1001,7 +1035,8 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local,
                                            struct ieee80211_sub_if_data *sdata,
                                            struct ieee80211_chanctx *ctx)
 {
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_unassign_vif_chanctx(local, sdata, ctx);
        if (local->ops->unassign_vif_chanctx) {
@@ -1013,12 +1048,66 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local,
        trace_drv_return_void(local);
 }
 
+static inline int
+drv_switch_vif_chanctx(struct ieee80211_local *local,
+                      struct ieee80211_vif_chanctx_switch *vifs,
+                      int n_vifs,
+                      enum ieee80211_chanctx_switch_mode mode)
+{
+       int ret = 0;
+       int i;
+
+       if (!local->ops->switch_vif_chanctx)
+               return -EOPNOTSUPP;
+
+       for (i = 0; i < n_vifs; i++) {
+               struct ieee80211_chanctx *new_ctx =
+                       container_of(vifs[i].new_ctx,
+                                    struct ieee80211_chanctx,
+                                    conf);
+               struct ieee80211_chanctx *old_ctx =
+                       container_of(vifs[i].old_ctx,
+                                    struct ieee80211_chanctx,
+                                    conf);
+
+               WARN_ON_ONCE(!old_ctx->driver_present);
+               WARN_ON_ONCE((mode == CHANCTX_SWMODE_SWAP_CONTEXTS &&
+                             new_ctx->driver_present) ||
+                            (mode == CHANCTX_SWMODE_REASSIGN_VIF &&
+                             !new_ctx->driver_present));
+       }
+
+       trace_drv_switch_vif_chanctx(local, vifs, n_vifs, mode);
+       ret = local->ops->switch_vif_chanctx(&local->hw,
+                                            vifs, n_vifs, mode);
+       trace_drv_return_int(local, ret);
+
+       if (!ret && mode == CHANCTX_SWMODE_SWAP_CONTEXTS) {
+               for (i = 0; i < n_vifs; i++) {
+                       struct ieee80211_chanctx *new_ctx =
+                               container_of(vifs[i].new_ctx,
+                                            struct ieee80211_chanctx,
+                                            conf);
+                       struct ieee80211_chanctx *old_ctx =
+                               container_of(vifs[i].old_ctx,
+                                            struct ieee80211_chanctx,
+                                            conf);
+
+                       new_ctx->driver_present = true;
+                       old_ctx->driver_present = false;
+               }
+       }
+
+       return ret;
+}
+
 static inline int drv_start_ap(struct ieee80211_local *local,
                               struct ieee80211_sub_if_data *sdata)
 {
        int ret = 0;
 
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_start_ap(local, sdata, &sdata->vif.bss_conf);
        if (local->ops->start_ap)
@@ -1030,7 +1119,8 @@ static inline int drv_start_ap(struct ieee80211_local *local,
 static inline void drv_stop_ap(struct ieee80211_local *local,
                               struct ieee80211_sub_if_data *sdata)
 {
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_stop_ap(local, sdata);
        if (local->ops->stop_ap)
@@ -1053,7 +1143,8 @@ drv_set_default_unicast_key(struct ieee80211_local *local,
                            struct ieee80211_sub_if_data *sdata,
                            int key_idx)
 {
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        WARN_ON_ONCE(key_idx < -1 || key_idx > 3);
 
@@ -1095,7 +1186,8 @@ static inline int drv_join_ibss(struct ieee80211_local *local,
        int ret = 0;
 
        might_sleep();
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return -EIO;
 
        trace_drv_join_ibss(local, sdata, &sdata->vif.bss_conf);
        if (local->ops->join_ibss)
@@ -1108,7 +1200,8 @@ static inline void drv_leave_ibss(struct ieee80211_local *local,
                                  struct ieee80211_sub_if_data *sdata)
 {
        might_sleep();
-       check_sdata_in_driver(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
 
        trace_drv_leave_ibss(local, sdata);
        if (local->ops->leave_ibss)
@@ -1116,4 +1209,17 @@ static inline void drv_leave_ibss(struct ieee80211_local *local,
        trace_drv_return_void(local);
 }
 
+static inline u32 drv_get_expected_throughput(struct ieee80211_local *local,
+                                             struct ieee80211_sta *sta)
+{
+       u32 ret = 0;
+
+       trace_drv_get_expected_throughput(sta);
+       if (local->ops->get_expected_throughput)
+               ret = local->ops->get_expected_throughput(sta);
+       trace_drv_return_u32(local, ret);
+
+       return ret;
+}
+
 #endif /* __MAC80211_DRIVER_OPS */
index c150b68436d78ada5bfbb0825d128d8e89f916e3..15702ff64a4c89fb89541f620b9da7dcccc2a7bd 100644 (file)
@@ -31,6 +31,18 @@ static void __check_htcap_disable(struct ieee80211_ht_cap *ht_capa,
        }
 }
 
+static void __check_htcap_enable(struct ieee80211_ht_cap *ht_capa,
+                                 struct ieee80211_ht_cap *ht_capa_mask,
+                                 struct ieee80211_sta_ht_cap *ht_cap,
+                                 u16 flag)
+{
+       __le16 le_flag = cpu_to_le16(flag);
+
+       if ((ht_capa_mask->cap_info & le_flag) &&
+           (ht_capa->cap_info & le_flag))
+               ht_cap->cap |= flag;
+}
+
 void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
                                     struct ieee80211_sta_ht_cap *ht_cap)
 {
@@ -59,7 +71,7 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
        smask = (u8 *)(&ht_capa_mask->mcs.rx_mask);
 
        /* NOTE:  If you add more over-rides here, update register_hw
-        * ht_capa_mod_msk logic in main.c as well.
+        * ht_capa_mod_mask logic in main.c as well.
         * And, if this method can ever change ht_cap.ht_supported, fix
         * the check in ieee80211_add_ht_ie.
         */
@@ -86,6 +98,14 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
        __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
                              IEEE80211_HT_CAP_MAX_AMSDU);
 
+       /* Allow user to disable LDPC */
+       __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
+                             IEEE80211_HT_CAP_LDPC_CODING);
+
+       /* Allow user to enable 40 MHz intolerant bit. */
+       __check_htcap_enable(ht_capa, ht_capa_mask, ht_cap,
+                            IEEE80211_HT_CAP_40MHZ_INTOLERANT);
+
        /* Allow user to decrease AMPDU factor */
        if (ht_capa_mask->ampdu_params_info &
            IEEE80211_HT_AMPDU_PARM_FACTOR) {
index 06d28787945b513e6672457a1e6990da0fd644d8..18ee0a256b1e300d5fc4805d110af12ca1a77308 100644 (file)
@@ -143,7 +143,7 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
                *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);
+               sdata->csa_counter_offset_beacon[0] = (pos - presp->head);
                *pos++ = csa_settings->count;
        }
 
@@ -228,7 +228,7 @@ 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;
+       bool radar_required;
        int err;
 
        sdata_assert_lock(sdata);
@@ -253,7 +253,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 
        presp = rcu_dereference_protected(ifibss->presp,
                                          lockdep_is_held(&sdata->wdev.mtx));
-       rcu_assign_pointer(ifibss->presp, NULL);
+       RCU_INIT_POINTER(ifibss->presp, NULL);
        if (presp)
                kfree_rcu(presp, rcu_head);
 
@@ -262,7 +262,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        /* make a copy of the chandef, it could be modified below. */
        chandef = *req_chandef;
        chan = chandef.chan;
-       if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
+       if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef,
+                                    NL80211_IFTYPE_ADHOC)) {
                if (chandef.width == NL80211_CHAN_WIDTH_5 ||
                    chandef.width == NL80211_CHAN_WIDTH_10 ||
                    chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
@@ -274,7 +275,8 @@ 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)) {
+               if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef,
+                                            NL80211_IFTYPE_ADHOC)) {
                        sdata_info(sdata,
                                   "Failed to join IBSS, beacons forbidden\n");
                        return;
@@ -282,21 +284,20 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        }
 
        err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
-                                           &chandef);
+                                           &chandef, NL80211_IFTYPE_ADHOC);
        if (err < 0) {
                sdata_info(sdata,
                           "Failed to join IBSS, invalid chandef\n");
                return;
        }
-       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;
+       if (err > 0 && !ifibss->userspace_handles_dfs) {
+               sdata_info(sdata,
+                          "Failed to join IBSS, DFS channel without control program\n");
+               return;
        }
 
+       radar_required = err;
+
        mutex_lock(&local->mtx);
        if (ieee80211_vif_use_channel(sdata, &chandef,
                                      ifibss->fixed_channel ?
@@ -775,7 +776,8 @@ static void ieee80211_ibss_csa_mark_radar(struct ieee80211_sub_if_data *sdata)
         * unavailable.
         */
        err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
-                                           &ifibss->chandef);
+                                           &ifibss->chandef,
+                                           NL80211_IFTYPE_ADHOC);
        if (err > 0)
                cfg80211_radar_event(sdata->local->hw.wiphy, &ifibss->chandef,
                                     GFP_ATOMIC);
@@ -861,7 +863,8 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                goto disconnect;
        }
 
-       if (!cfg80211_reg_can_beacon(sdata->local->hw.wiphy, &params.chandef)) {
+       if (!cfg80211_reg_can_beacon(sdata->local->hw.wiphy, &params.chandef,
+                                    NL80211_IFTYPE_ADHOC)) {
                sdata_info(sdata,
                           "IBSS %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
                           ifibss->bssid,
@@ -873,17 +876,17 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        }
 
        err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
-                                           &params.chandef);
+                                           &params.chandef,
+                                           NL80211_IFTYPE_ADHOC);
        if (err < 0)
                goto disconnect;
-       if (err) {
+       if (err > 0 && !ifibss->userspace_handles_dfs) {
                /* IBSS-DFS only allowed with a control program */
-               if (!ifibss->userspace_handles_dfs)
-                       goto disconnect;
-
-               params.radar_required = true;
+               goto disconnect;
        }
 
+       params.radar_required = err;
+
        if (cfg80211_chandef_identical(&params.chandef,
                                       &sdata->vif.bss_conf.chandef)) {
                ibss_dbg(sdata,
@@ -1636,7 +1639,33 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
        u32 changed = 0;
        u32 rate_flags;
        struct ieee80211_supported_band *sband;
+       enum ieee80211_chanctx_mode chanmode;
+       struct ieee80211_local *local = sdata->local;
+       int radar_detect_width = 0;
        int i;
+       int ret;
+
+       ret = cfg80211_chandef_dfs_required(local->hw.wiphy,
+                                           &params->chandef,
+                                           sdata->wdev.iftype);
+       if (ret < 0)
+               return ret;
+
+       if (ret > 0) {
+               if (!params->userspace_handles_dfs)
+                       return -EINVAL;
+               radar_detect_width = BIT(params->chandef.width);
+       }
+
+       chanmode = (params->channel_fixed && !ret) ?
+               IEEE80211_CHANCTX_SHARED : IEEE80211_CHANCTX_EXCLUSIVE;
+
+       mutex_lock(&local->chanctx_mtx);
+       ret = ieee80211_check_combinations(sdata, &params->chandef, chanmode,
+                                          radar_detect_width);
+       mutex_unlock(&local->chanctx_mtx);
+       if (ret < 0)
+               return ret;
 
        if (params->bssid) {
                memcpy(sdata->u.ibss.bssid, params->bssid, ETH_ALEN);
@@ -1648,10 +1677,11 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
        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;
+       sdata->u.ibss.last_scan_completed = jiffies;
 
        /* fix basic_rates if channel does not support these rates */
        rate_flags = ieee80211_chandef_rate_flags(&params->chandef);
-       sband = sdata->local->hw.wiphy->bands[params->chandef.chan->band];
+       sband = local->hw.wiphy->bands[params->chandef.chan->band];
        for (i = 0; i < sband->n_bitrates; i++) {
                if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
                        sdata->u.ibss.basic_rates &= ~BIT(i);
@@ -1700,9 +1730,9 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
        ieee80211_bss_info_change_notify(sdata, changed);
 
        sdata->smps_mode = IEEE80211_SMPS_OFF;
-       sdata->needed_rx_chains = sdata->local->rx_chains;
+       sdata->needed_rx_chains = local->rx_chains;
 
-       ieee80211_queue_work(&sdata->local->hw, &sdata->work);
+       ieee80211_queue_work(&local->hw, &sdata->work);
 
        return 0;
 }
index f169b6ee94ee8d6ee9c6daa5dc047c5f8b1d2740..ac9836e0aab335ddf75295b5ad020d77e0e3dd0a 100644 (file)
@@ -260,7 +260,7 @@ struct ieee80211_if_ap {
 
        /* to be used after channel switch. */
        struct cfg80211_beacon_data *next_beacon;
-       struct list_head vlans;
+       struct list_head vlans; /* write-protected with RTNL and local->mtx */
 
        struct ps_data ps;
        atomic_t num_mcast_sta; /* number of stations receiving multicast */
@@ -276,7 +276,7 @@ struct ieee80211_if_wds {
 };
 
 struct ieee80211_if_vlan {
-       struct list_head list;
+       struct list_head list; /* write-protected with RTNL and local->mtx */
 
        /* used for all tx if the VLAN is configured to 4-addr mode */
        struct sta_info __rcu *sta;
@@ -692,8 +692,10 @@ struct ieee80211_chanctx {
        struct list_head list;
        struct rcu_head rcu_head;
 
+       struct list_head assigned_vifs;
+       struct list_head reserved_vifs;
+
        enum ieee80211_chanctx_mode mode;
-       int refcount;
        bool driver_present;
 
        struct ieee80211_chanctx_conf conf;
@@ -752,11 +754,21 @@ struct ieee80211_sub_if_data {
        struct mac80211_qos_map __rcu *qos_map;
 
        struct work_struct csa_finalize_work;
-       int csa_counter_offset_beacon;
-       int csa_counter_offset_presp;
+       u16 csa_counter_offset_beacon[IEEE80211_MAX_CSA_COUNTERS_NUM];
+       u16 csa_counter_offset_presp[IEEE80211_MAX_CSA_COUNTERS_NUM];
        bool csa_radar_required;
+       bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */
        struct cfg80211_chan_def csa_chandef;
 
+       struct list_head assigned_chanctx_list; /* protected by chanctx_mtx */
+       struct list_head reserved_chanctx_list; /* protected by chanctx_mtx */
+
+       /* context reservation -- protected with chanctx_mtx */
+       struct ieee80211_chanctx *reserved_chanctx;
+       struct cfg80211_chan_def reserved_chandef;
+       bool reserved_radar_required;
+       u8 csa_current_counter;
+
        /* used to reconfigure hardware SM PS */
        struct work_struct recalc_smps;
 
@@ -1449,6 +1461,7 @@ __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
 int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
                                       struct cfg80211_sched_scan_request *req);
 int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata);
+void ieee80211_sched_scan_end(struct ieee80211_local *local);
 void ieee80211_sched_scan_stopped_work(struct work_struct *work);
 
 /* off-channel helpers */
@@ -1463,6 +1476,7 @@ void ieee80211_sw_roc_work(struct work_struct *work);
 void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc);
 
 /* channel switch handling */
+bool ieee80211_csa_needs_block_tx(struct ieee80211_local *local);
 void ieee80211_csa_finalize_work(struct work_struct *work);
 int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                             struct cfg80211_csa_settings *params);
@@ -1771,6 +1785,16 @@ int __must_check
 ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
                          const struct cfg80211_chan_def *chandef,
                          enum ieee80211_chanctx_mode mode);
+int __must_check
+ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
+                             const struct cfg80211_chan_def *chandef,
+                             enum ieee80211_chanctx_mode mode,
+                             bool radar_required);
+int __must_check
+ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata,
+                                  u32 *changed);
+int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata);
+
 int __must_check
 ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
                               const struct cfg80211_chan_def *chandef,
@@ -1783,6 +1807,8 @@ void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata);
 void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata);
 void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
                                         bool clear);
+int ieee80211_chanctx_refcount(struct ieee80211_local *local,
+                              struct ieee80211_chanctx *ctx);
 
 void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
                                   struct ieee80211_chanctx *chanctx);
@@ -1806,6 +1832,20 @@ int ieee80211_cs_headroom(struct ieee80211_local *local,
                          enum nl80211_iftype iftype);
 void ieee80211_recalc_dtim(struct ieee80211_local *local,
                           struct ieee80211_sub_if_data *sdata);
+int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
+                                const struct cfg80211_chan_def *chandef,
+                                enum ieee80211_chanctx_mode chanmode,
+                                u8 radar_detect);
+int ieee80211_max_num_channels(struct ieee80211_local *local);
+
+/* TDLS */
+int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
+                       const u8 *peer, u8 action_code, u8 dialog_token,
+                       u16 status_code, u32 peer_capability,
+                       const u8 *extra_ies, size_t extra_ies_len);
+int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
+                       const u8 *peer, enum nl80211_tdls_operation oper);
+
 
 #ifdef CONFIG_MAC80211_NOINLINE
 #define debug_noinline noinline
index b8d331e7d883d50fd4fc3adf12c2869ac3beedb7..388b863e821c6beedbcdb01a602c0d18db701d2f 100644 (file)
@@ -250,6 +250,7 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_sub_if_data *nsdata;
+       int ret;
 
        ASSERT_RTNL();
 
@@ -300,7 +301,10 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
                }
        }
 
-       return 0;
+       mutex_lock(&local->chanctx_mtx);
+       ret = ieee80211_check_combinations(sdata, NULL, 0, 0);
+       mutex_unlock(&local->chanctx_mtx);
+       return ret;
 }
 
 static int ieee80211_check_queues(struct ieee80211_sub_if_data *sdata,
@@ -395,6 +399,7 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
        sdata->vif.type = NL80211_IFTYPE_MONITOR;
        snprintf(sdata->name, IFNAMSIZ, "%s-monitor",
                 wiphy_name(local->hw.wiphy));
+       sdata->wdev.iftype = NL80211_IFTYPE_MONITOR;
 
        sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
 
@@ -423,7 +428,7 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
        mutex_unlock(&local->mtx);
        if (ret) {
                mutex_lock(&local->iflist_mtx);
-               rcu_assign_pointer(local->monitor_sdata, NULL);
+               RCU_INIT_POINTER(local->monitor_sdata, NULL);
                mutex_unlock(&local->iflist_mtx);
                synchronize_net();
                drv_remove_interface(local, sdata);
@@ -452,7 +457,7 @@ void ieee80211_del_virtual_monitor(struct ieee80211_local *local)
                return;
        }
 
-       rcu_assign_pointer(local->monitor_sdata, NULL);
+       RCU_INIT_POINTER(local->monitor_sdata, NULL);
        mutex_unlock(&local->iflist_mtx);
 
        synchronize_net();
@@ -492,7 +497,9 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
                if (!sdata->bss)
                        return -ENOLINK;
 
+               mutex_lock(&local->mtx);
                list_add(&sdata->u.vlan.list, &sdata->bss->vlans);
+               mutex_unlock(&local->mtx);
 
                master = container_of(sdata->bss,
                                      struct ieee80211_sub_if_data, u.ap);
@@ -722,8 +729,11 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
                drv_stop(local);
  err_del_bss:
        sdata->bss = NULL;
-       if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+       if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+               mutex_lock(&local->mtx);
                list_del(&sdata->u.vlan.list);
+               mutex_unlock(&local->mtx);
+       }
        /* might already be clear but that doesn't matter */
        clear_bit(SDATA_STATE_RUNNING, &sdata->state);
        return res;
@@ -829,8 +839,15 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
 
        cancel_work_sync(&sdata->recalc_smps);
        sdata_lock(sdata);
+       mutex_lock(&local->mtx);
        sdata->vif.csa_active = false;
+       if (!ieee80211_csa_needs_block_tx(local))
+               ieee80211_wake_queues_by_reason(&local->hw,
+                                       IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+       mutex_unlock(&local->mtx);
        sdata_unlock(sdata);
+
        cancel_work_sync(&sdata->csa_finalize_work);
 
        cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
@@ -875,8 +892,10 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP_VLAN:
+               mutex_lock(&local->mtx);
                list_del(&sdata->u.vlan.list);
-               rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
+               mutex_unlock(&local->mtx);
+               RCU_INIT_POINTER(sdata->vif.chanctx_conf, NULL);
                /* no need to tell driver */
                break;
        case NL80211_IFTYPE_MONITOR:
@@ -895,7 +914,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
                break;
        case NL80211_IFTYPE_P2P_DEVICE:
                /* relies on synchronize_rcu() below */
-               rcu_assign_pointer(local->p2p_sdata, NULL);
+               RCU_INIT_POINTER(local->p2p_sdata, NULL);
                /* fall through */
        default:
                cancel_work_sync(&sdata->work);
@@ -1267,6 +1286,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
        sdata->control_port_protocol = cpu_to_be16(ETH_P_PAE);
        sdata->control_port_no_encrypt = false;
        sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
+       sdata->vif.bss_conf.idle = true;
 
        sdata->noack_map = 0;
 
@@ -1280,6 +1300,8 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
        INIT_WORK(&sdata->work, ieee80211_iface_work);
        INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
        INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work);
+       INIT_LIST_HEAD(&sdata->assigned_chanctx_list);
+       INIT_LIST_HEAD(&sdata->reserved_chanctx_list);
 
        switch (type) {
        case NL80211_IFTYPE_P2P_GO:
@@ -1758,7 +1780,6 @@ void ieee80211_remove_interfaces(struct ieee80211_local *local)
        }
        mutex_unlock(&local->iflist_mtx);
        unregister_netdevice_many(&unreg_list);
-       list_del(&unreg_list);
 
        list_for_each_entry_safe(sdata, tmp, &wdev_list, list) {
                list_del(&sdata->list);
@@ -1774,20 +1795,19 @@ static int netdev_notify(struct notifier_block *nb,
        struct ieee80211_sub_if_data *sdata;
 
        if (state != NETDEV_CHANGENAME)
-               return 0;
+               return NOTIFY_DONE;
 
        if (!dev->ieee80211_ptr || !dev->ieee80211_ptr->wiphy)
-               return 0;
+               return NOTIFY_DONE;
 
        if (dev->ieee80211_ptr->wiphy->privid != mac80211_wiphy_privid)
-               return 0;
+               return NOTIFY_DONE;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
        memcpy(sdata->name, dev->name, IFNAMSIZ);
-
        ieee80211_debugfs_rename_netdev(sdata);
-       return 0;
+
+       return NOTIFY_OK;
 }
 
 static struct notifier_block mac80211_netdev_notifier = {
index 6ff65a1ebaa905ffecfbdedebc7428882aecc551..16d97f044a202e61020f3ef563941e4c9adace0a 100644 (file)
@@ -325,7 +325,8 @@ ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
        struct ieee80211_key *key;
        int i, j, err;
 
-       BUG_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS);
+       if (WARN_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS))
+               return ERR_PTR(-EINVAL);
 
        key = kzalloc(sizeof(struct ieee80211_key) + key_len, GFP_KERNEL);
        if (!key)
@@ -481,8 +482,8 @@ int ieee80211_key_link(struct ieee80211_key *key,
        int idx, ret;
        bool pairwise;
 
-       BUG_ON(!sdata);
-       BUG_ON(!key);
+       if (WARN_ON(!sdata || !key))
+               return -EINVAL;
 
        pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE;
        idx = key->conf.keyidx;
index 4c1bf61bc778683dc352ebb32a585d45649168a6..d17c26d6e369f8db71061f3c73f4f27196ed9e3d 100644 (file)
@@ -340,7 +340,7 @@ static int ieee80211_ifa_changed(struct notifier_block *nb,
 
        sdata_unlock(sdata);
 
-       return NOTIFY_DONE;
+       return NOTIFY_OK;
 }
 #endif
 
@@ -371,7 +371,7 @@ static int ieee80211_ifa6_changed(struct notifier_block *nb,
 
        drv_ipv6_addr_change(local, sdata, idev);
 
-       return NOTIFY_DONE;
+       return NOTIFY_OK;
 }
 #endif
 
@@ -446,7 +446,9 @@ static const struct ieee80211_ht_cap mac80211_ht_capa_mod_mask = {
        .cap_info = cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
                                IEEE80211_HT_CAP_MAX_AMSDU |
                                IEEE80211_HT_CAP_SGI_20 |
-                               IEEE80211_HT_CAP_SGI_40),
+                               IEEE80211_HT_CAP_SGI_40 |
+                               IEEE80211_HT_CAP_LDPC_CODING |
+                               IEEE80211_HT_CAP_40MHZ_INTOLERANT),
        .mcs = {
                .rx_mask = { 0xff, 0xff, 0xff, 0xff, 0xff,
                             0xff, 0xff, 0xff, 0xff, 0xff, },
@@ -954,6 +956,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
        if (local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)
                local->hw.wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
 
+       local->hw.wiphy->max_num_csa_counters = IEEE80211_MAX_CSA_COUNTERS_NUM;
+
        result = wiphy_register(local->hw.wiphy);
        if (result < 0)
                goto fail_wiphy_register;
index f70e9cd10552dac6729d703edd2e9ae12750a3a6..6495a3f0428dae6a93bafea04da26b7390f45599 100644 (file)
@@ -366,20 +366,15 @@ int mesh_add_rsn_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
                return 0;
 
        /* find RSN IE */
-       data = ifmsh->ie;
-       while (data < ifmsh->ie + ifmsh->ie_len) {
-               if (*data == WLAN_EID_RSN) {
-                       len = data[1] + 2;
-                       break;
-               }
-               data++;
-       }
+       data = cfg80211_find_ie(WLAN_EID_RSN, ifmsh->ie, ifmsh->ie_len);
+       if (!data)
+               return 0;
 
-       if (len) {
-               if (skb_tailroom(skb) < len)
-                       return -ENOMEM;
-               memcpy(skb_put(skb, len), data, len);
-       }
+       len = data[1] + 2;
+
+       if (skb_tailroom(skb) < len)
+               return -ENOMEM;
+       memcpy(skb_put(skb, len), data, len);
 
        return 0;
 }
@@ -684,7 +679,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
                *pos++ = 0x0;
                *pos++ = ieee80211_frequency_to_channel(
                                csa->settings.chandef.chan->center_freq);
-               sdata->csa_counter_offset_beacon = hdr_len + 6;
+               sdata->csa_counter_offset_beacon[0] = hdr_len + 6;
                *pos++ = csa->settings.count;
                *pos++ = WLAN_EID_CHAN_SWITCH_PARAM;
                *pos++ = 6;
@@ -829,7 +824,7 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
        bcn = rcu_dereference_protected(ifmsh->beacon,
                                        lockdep_is_held(&sdata->wdev.mtx));
-       rcu_assign_pointer(ifmsh->beacon, NULL);
+       RCU_INIT_POINTER(ifmsh->beacon, NULL);
        kfree_rcu(bcn, rcu_head);
 
        /* flush STAs and mpaths on this iface */
@@ -903,14 +898,15 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
        }
 
        err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
-                                           &params.chandef);
+                                           &params.chandef,
+                                           NL80211_IFTYPE_MESH_POINT);
        if (err < 0)
                return false;
-       if (err) {
-               params.radar_required = true;
+       if (err > 0)
                /* TODO: DFS not (yet) supported */
                return false;
-       }
+
+       params.radar_required = err;
 
        if (cfg80211_chandef_identical(&params.chandef,
                                       &sdata->vif.bss_conf.chandef)) {
@@ -1068,7 +1064,7 @@ int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata)
 
        /* Remove the CSA and MCSP elements from the beacon */
        tmp_csa_settings = rcu_dereference(ifmsh->csa);
-       rcu_assign_pointer(ifmsh->csa, NULL);
+       RCU_INIT_POINTER(ifmsh->csa, NULL);
        if (tmp_csa_settings)
                kfree_rcu(tmp_csa_settings, rcu_head);
        ret = ieee80211_mesh_rebuild_beacon(sdata);
@@ -1102,7 +1098,7 @@ int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
        ret = ieee80211_mesh_rebuild_beacon(sdata);
        if (ret) {
                tmp_csa_settings = rcu_dereference(ifmsh->csa);
-               rcu_assign_pointer(ifmsh->csa, NULL);
+               RCU_INIT_POINTER(ifmsh->csa, NULL);
                kfree_rcu(tmp_csa_settings, rcu_head);
                return ret;
        }
index f9514685d45a54802cb0f21dce0953ccbe9b78f4..94758b9c9ed48a5d1ea9921f4f9f0da40e34bd0b 100644 (file)
@@ -37,7 +37,7 @@ static inline u32 u32_field_get(const u8 *preq_elem, int offset, bool ae)
        return get_unaligned_le32(preq_elem + offset);
 }
 
-static inline u32 u16_field_get(const u8 *preq_elem, int offset, bool ae)
+static inline u16 u16_field_get(const u8 *preq_elem, int offset, bool ae)
 {
        if (ae)
                offset += 6;
@@ -544,9 +544,10 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
                if (time_after(jiffies, ifmsh->last_sn_update +
                                        net_traversal_jiffies(sdata)) ||
                    time_before(jiffies, ifmsh->last_sn_update)) {
-                       target_sn = ++ifmsh->sn;
+                       ++ifmsh->sn;
                        ifmsh->last_sn_update = jiffies;
                }
+               target_sn = ifmsh->sn;
        } else if (is_broadcast_ether_addr(target_addr) &&
                   (target_flags & IEEE80211_PREQ_TO_FLAG)) {
                rcu_read_lock();
index 7d050ed6fe5a4ec879bc9711c088db8360154448..cf032a8db9d78e9c13fe9df1ee2f2c35dbe8ec62 100644 (file)
@@ -287,8 +287,10 @@ static void mesh_path_move_to_queue(struct mesh_path *gate_mpath,
        struct sk_buff_head failq;
        unsigned long flags;
 
-       BUG_ON(gate_mpath == from_mpath);
-       BUG_ON(!gate_mpath->next_hop);
+       if (WARN_ON(gate_mpath == from_mpath))
+               return;
+       if (WARN_ON(!gate_mpath->next_hop))
+               return;
 
        __skb_queue_head_init(&failq);
 
index 2bc5dc25d5adc79a92cd29bc94d058e141d011c6..09625d6205c31418edba53a62ee027245f232050 100644 (file)
@@ -171,7 +171,7 @@ static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata,
        u8 cap;
 
        WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET);
-       BUG_ON(!rcu_read_lock_held());
+       WARN_ON(!rcu_read_lock_held());
        cap = beacon->meshconf->meshconf_cap;
 
        spin_lock_bh(&ifmsh->sync_offset_lock);
index 3b848dad958762ccfc48732623d9ddf1f56aa8ae..0e4886f881f1e49f438fa0f61d5c2b021877bf30 100644 (file)
@@ -11,6 +11,7 @@
 #define MICHAEL_H
 
 #include <linux/types.h>
+#include <linux/ieee80211.h>
 
 #define MICHAEL_MIC_LEN 8
 
index 27600a9808baeaa31be82ecb0f4218225b28665e..3345401be1b3c26744cb2ab6e384672a0cab0d6b 100644 (file)
@@ -975,16 +975,23 @@ static void ieee80211_chswitch_work(struct work_struct *work)
        /* XXX: shouldn't really modify cfg80211-owned data! */
        ifmgd->associated->channel = sdata->csa_chandef.chan;
 
+       ieee80211_bss_info_change_notify(sdata, changed);
+
+       mutex_lock(&local->mtx);
+       sdata->vif.csa_active = false;
        /* XXX: wait for a beacon first? */
-       ieee80211_wake_queues_by_reason(&local->hw,
+       if (!ieee80211_csa_needs_block_tx(local))
+               ieee80211_wake_queues_by_reason(&local->hw,
                                        IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
+       mutex_unlock(&local->mtx);
 
-       ieee80211_bss_info_change_notify(sdata, changed);
-
- out:
-       sdata->vif.csa_active = false;
        ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
+
+       ieee80211_sta_reset_beacon_monitor(sdata);
+       ieee80211_sta_reset_conn_monitor(sdata);
+
+out:
        sdata_unlock(sdata);
 }
 
@@ -1089,7 +1096,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        }
        chanctx = container_of(rcu_access_pointer(sdata->vif.chanctx_conf),
                               struct ieee80211_chanctx, conf);
-       if (chanctx->refcount > 1) {
+       if (ieee80211_chanctx_refcount(local, chanctx) > 1) {
                sdata_info(sdata,
                           "channel switch with multiple interfaces on the same channel, disconnecting\n");
                ieee80211_queue_work(&local->hw,
@@ -1100,12 +1107,16 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        mutex_unlock(&local->chanctx_mtx);
 
        sdata->csa_chandef = csa_ie.chandef;
+
+       mutex_lock(&local->mtx);
        sdata->vif.csa_active = true;
+       sdata->csa_block_tx = csa_ie.mode;
 
-       if (csa_ie.mode)
+       if (sdata->csa_block_tx)
                ieee80211_stop_queues_by_reason(&local->hw,
-                               IEEE80211_MAX_QUEUE_MAP,
-                               IEEE80211_QUEUE_STOP_REASON_CSA);
+                                       IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+       mutex_unlock(&local->mtx);
 
        if (local->ops->channel_switch) {
                /* use driver's channel switch callback */
@@ -1817,6 +1828,12 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        ifmgd->flags = 0;
        mutex_lock(&local->mtx);
        ieee80211_vif_release_channel(sdata);
+
+       sdata->vif.csa_active = false;
+       if (!ieee80211_csa_needs_block_tx(local))
+               ieee80211_wake_queues_by_reason(&local->hw,
+                                       IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_CSA);
        mutex_unlock(&local->mtx);
 
        sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
@@ -2045,6 +2062,7 @@ EXPORT_SYMBOL(ieee80211_ap_probereq_get);
 
 static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
 {
+       struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
@@ -2058,10 +2076,14 @@ 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;
+
+       mutex_lock(&local->mtx);
        sdata->vif.csa_active = false;
-       ieee80211_wake_queues_by_reason(&sdata->local->hw,
+       if (!ieee80211_csa_needs_block_tx(local))
+               ieee80211_wake_queues_by_reason(&local->hw,
                                        IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
+       mutex_unlock(&local->mtx);
 
        cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
                              IEEE80211_DEAUTH_FRAME_LEN);
@@ -3546,6 +3568,9 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)
        if (local->quiescing)
                return;
 
+       if (sdata->vif.csa_active)
+               return;
+
        sdata->u.mgd.connection_loss = false;
        ieee80211_queue_work(&sdata->local->hw,
                             &sdata->u.mgd.beacon_connection_loss_work);
@@ -3561,6 +3586,9 @@ static void ieee80211_sta_conn_mon_timer(unsigned long data)
        if (local->quiescing)
                return;
 
+       if (sdata->vif.csa_active)
+               return;
+
        ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);
 }
 
@@ -3707,7 +3735,7 @@ int ieee80211_max_network_latency(struct notifier_block *nb,
        ieee80211_recalc_ps(local, latency_usec);
        mutex_unlock(&local->iflist_mtx);
 
-       return 0;
+       return NOTIFY_OK;
 }
 
 static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata,
index 26fd94fa0aedb86e43382781f791b1f36de44954..1c1469c36dca05cbdf55e2aba55dc296962aba55 100644 (file)
@@ -657,6 +657,17 @@ minstrel_free(void *priv)
        kfree(priv);
 }
 
+static u32 minstrel_get_expected_throughput(void *priv_sta)
+{
+       struct minstrel_sta_info *mi = priv_sta;
+       int idx = mi->max_tp_rate[0];
+
+       /* convert pkt per sec in kbps (1200 is the average pkt size used for
+        * computing cur_tp
+        */
+       return MINSTREL_TRUNC(mi->r[idx].cur_tp) * 1200 * 8 / 1024;
+}
+
 const struct rate_control_ops mac80211_minstrel = {
        .name = "minstrel",
        .tx_status = minstrel_tx_status,
@@ -670,6 +681,7 @@ const struct rate_control_ops mac80211_minstrel = {
        .add_sta_debugfs = minstrel_add_sta_debugfs,
        .remove_sta_debugfs = minstrel_remove_sta_debugfs,
 #endif
+       .get_expected_throughput = minstrel_get_expected_throughput,
 };
 
 int __init
index bccaf854a309e9434fb9044d0e98082e3de287de..85c1e74b7714c2160ffd6976bbec4322bb7e428d 100644 (file)
@@ -22,7 +22,7 @@
 #define MCS_NBITS (AVG_PKT_SIZE << 3)
 
 /* Number of symbols for a packet with (bps) bits per symbol */
-#define MCS_NSYMS(bps) ((MCS_NBITS + (bps) - 1) / (bps))
+#define MCS_NSYMS(bps) DIV_ROUND_UP(MCS_NBITS, (bps))
 
 /* Transmission time (nanoseconds) for a packet containing (syms) symbols */
 #define MCS_SYMBOL_TIME(sgi, syms)                                     \
@@ -226,8 +226,9 @@ minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate)
                nsecs = 1000 * mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len);
 
        nsecs += minstrel_mcs_groups[group].duration[rate];
-       tp = 1000000 * ((prob * 1000) / nsecs);
 
+       /* prob is scaled - see MINSTREL_FRAC above */
+       tp = 1000000 * ((prob * 1000) / nsecs);
        mr->cur_tp = MINSTREL_TRUNC(tp);
 }
 
@@ -1031,6 +1032,22 @@ minstrel_ht_free(void *priv)
        mac80211_minstrel.free(priv);
 }
 
+static u32 minstrel_ht_get_expected_throughput(void *priv_sta)
+{
+       struct minstrel_ht_sta_priv *msp = priv_sta;
+       struct minstrel_ht_sta *mi = &msp->ht;
+       int i, j;
+
+       if (!msp->is_ht)
+               return mac80211_minstrel.get_expected_throughput(priv_sta);
+
+       i = mi->max_tp_rate / MCS_GROUP_RATES;
+       j = mi->max_tp_rate % MCS_GROUP_RATES;
+
+       /* convert cur_tp from pkt per second in kbps */
+       return mi->groups[i].rates[j].cur_tp * AVG_PKT_SIZE * 8 / 1024;
+}
+
 static const struct rate_control_ops mac80211_minstrel_ht = {
        .name = "minstrel_ht",
        .tx_status = minstrel_ht_tx_status,
@@ -1045,6 +1062,7 @@ static const struct rate_control_ops mac80211_minstrel_ht = {
        .add_sta_debugfs = minstrel_ht_add_sta_debugfs,
        .remove_sta_debugfs = minstrel_ht_remove_sta_debugfs,
 #endif
+       .get_expected_throughput = minstrel_ht_get_expected_throughput,
 };
 
 
index 2b608b2b70ece8a6ed0cb8f8d8eed5b1d44e1536..394e201cde6d3b6d4375f973937df55395547fea 100644 (file)
@@ -54,24 +54,25 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
        return skb;
 }
 
-static inline int should_drop_frame(struct sk_buff *skb, int present_fcs_len)
+static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len)
 {
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
-       struct ieee80211_hdr *hdr;
-
-       hdr = (void *)(skb->data);
+       struct ieee80211_hdr *hdr = (void *)skb->data;
 
        if (status->flag & (RX_FLAG_FAILED_FCS_CRC |
                            RX_FLAG_FAILED_PLCP_CRC |
                            RX_FLAG_AMPDU_IS_ZEROLEN))
-               return 1;
+               return true;
+
        if (unlikely(skb->len < 16 + present_fcs_len))
-               return 1;
+               return true;
+
        if (ieee80211_is_ctl(hdr->frame_control) &&
            !ieee80211_is_pspoll(hdr->frame_control) &&
            !ieee80211_is_back_req(hdr->frame_control))
-               return 1;
-       return 0;
+               return true;
+
+       return false;
 }
 
 static int
@@ -3191,7 +3192,7 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
 }
 
 /*
- * This is the actual Rx frames handler. as it blongs to Rx path it must
+ * This is the actual Rx frames handler. as it belongs to Rx path it must
  * be called with rcu_read_lock protection.
  */
 static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
index 3ce7f2c8539a1f626f7488833ee966fb3af5d502..f40661eb75b578dd2e409757331c085d36461723 100644 (file)
@@ -309,7 +309,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
        if (local->scan_req != local->int_scan_req)
                cfg80211_scan_done(local->scan_req, aborted);
        local->scan_req = NULL;
-       rcu_assign_pointer(local->scan_sdata, NULL);
+       RCU_INIT_POINTER(local->scan_sdata, NULL);
 
        local->scanning = 0;
        local->scan_chandef.chan = NULL;
@@ -559,7 +559,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
                ieee80211_recalc_idle(local);
 
                local->scan_req = NULL;
-               rcu_assign_pointer(local->scan_sdata, NULL);
+               RCU_INIT_POINTER(local->scan_sdata, NULL);
        }
 
        return rc;
@@ -773,7 +773,7 @@ void ieee80211_scan_work(struct work_struct *work)
                int rc;
 
                local->scan_req = NULL;
-               rcu_assign_pointer(local->scan_sdata, NULL);
+               RCU_INIT_POINTER(local->scan_sdata, NULL);
 
                rc = __ieee80211_start_scan(sdata, req);
                if (rc) {
@@ -1014,7 +1014,7 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
 
        if (ret) {
                /* Clean in case of failure after HW restart or upon resume. */
-               rcu_assign_pointer(local->sched_scan_sdata, NULL);
+               RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
                local->sched_scan_req = NULL;
        }
 
@@ -1076,12 +1076,8 @@ void ieee80211_sched_scan_results(struct ieee80211_hw *hw)
 }
 EXPORT_SYMBOL(ieee80211_sched_scan_results);
 
-void ieee80211_sched_scan_stopped_work(struct work_struct *work)
+void ieee80211_sched_scan_end(struct ieee80211_local *local)
 {
-       struct ieee80211_local *local =
-               container_of(work, struct ieee80211_local,
-                            sched_scan_stopped_work);
-
        mutex_lock(&local->mtx);
 
        if (!rcu_access_pointer(local->sched_scan_sdata)) {
@@ -1089,7 +1085,7 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work)
                return;
        }
 
-       rcu_assign_pointer(local->sched_scan_sdata, NULL);
+       RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
 
        /* If sched scan was aborted by the driver. */
        local->sched_scan_req = NULL;
@@ -1099,6 +1095,15 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work)
        cfg80211_sched_scan_stopped(local->hw.wiphy);
 }
 
+void ieee80211_sched_scan_stopped_work(struct work_struct *work)
+{
+       struct ieee80211_local *local =
+               container_of(work, struct ieee80211_local,
+                            sched_scan_stopped_work);
+
+       ieee80211_sched_scan_end(local);
+}
+
 void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw)
 {
        struct ieee80211_local *local = hw_to_local(hw);
index 847d92f6bef60856be53ad13f0534695e4c6dac7..a9b46d8ea22ff696623cec4cad55949fc5afd2a1 100644 (file)
@@ -240,6 +240,7 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
 
        sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr);
 
+       kfree(rcu_dereference_raw(sta->sta.rates));
        kfree(sta);
 }
 
@@ -552,7 +553,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
 int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
 {
        struct ieee80211_local *local = sta->local;
-       int err = 0;
+       int err;
 
        might_sleep();
 
@@ -570,7 +571,6 @@ int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
 
        return 0;
  out_free:
-       BUG_ON(!err);
        sta_info_free(local, sta);
        return err;
 }
index 60cb7a665976e10e7a909a9545b7643cf34e67a4..ba29ebc8614121ea90ff5ff1cec44f137ac85a69 100644 (file)
@@ -541,6 +541,23 @@ static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local,
  */
 #define STA_LOST_PKT_THRESHOLD 50
 
+static void ieee80211_lost_packet(struct sta_info *sta, struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+       /* This packet was aggregated but doesn't carry status info */
+       if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
+           !(info->flags & IEEE80211_TX_STAT_AMPDU))
+               return;
+
+       if (++sta->lost_packets < STA_LOST_PKT_THRESHOLD)
+               return;
+
+       cfg80211_cqm_pktloss_notify(sta->sdata->dev, sta->sta.addr,
+                                   sta->lost_packets, GFP_ATOMIC);
+       sta->lost_packets = 0;
+}
+
 void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
 {
        struct sk_buff *skb2;
@@ -680,12 +697,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
                        if (info->flags & IEEE80211_TX_STAT_ACK) {
                                if (sta->lost_packets)
                                        sta->lost_packets = 0;
-                       } else if (++sta->lost_packets >= STA_LOST_PKT_THRESHOLD) {
-                               cfg80211_cqm_pktloss_notify(sta->sdata->dev,
-                                                           sta->sta.addr,
-                                                           sta->lost_packets,
-                                                           GFP_ATOMIC);
-                               sta->lost_packets = 0;
+                       } else {
+                               ieee80211_lost_packet(sta, skb);
                        }
                }
 
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
new file mode 100644 (file)
index 0000000..652813b
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * mac80211 TDLS handling code
+ *
+ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2014, Intel Corporation
+ *
+ * This file is GPLv2 as found in COPYING.
+ */
+
+#include <linux/ieee80211.h>
+#include "ieee80211_i.h"
+
+static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb)
+{
+       u8 *pos = (void *)skb_put(skb, 7);
+
+       *pos++ = WLAN_EID_EXT_CAPABILITY;
+       *pos++ = 5; /* len */
+       *pos++ = 0x0;
+       *pos++ = 0x0;
+       *pos++ = 0x0;
+       *pos++ = 0x0;
+       *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
+}
+
+static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       u16 capab;
+
+       capab = 0;
+       if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ)
+               return capab;
+
+       if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
+               capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
+       if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE))
+               capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
+
+       return capab;
+}
+
+static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr,
+                                      const u8 *peer, const u8 *bssid)
+{
+       struct ieee80211_tdls_lnkie *lnkid;
+
+       lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
+
+       lnkid->ie_type = WLAN_EID_LINK_ID;
+       lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;
+
+       memcpy(lnkid->bssid, bssid, ETH_ALEN);
+       memcpy(lnkid->init_sta, src_addr, ETH_ALEN);
+       memcpy(lnkid->resp_sta, peer, ETH_ALEN);
+}
+
+static int
+ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
+                              const u8 *peer, u8 action_code, u8 dialog_token,
+                              u16 status_code, struct sk_buff *skb)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
+       struct ieee80211_tdls_data *tf;
+
+       tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
+
+       memcpy(tf->da, peer, ETH_ALEN);
+       memcpy(tf->sa, sdata->vif.addr, ETH_ALEN);
+       tf->ether_type = cpu_to_be16(ETH_P_TDLS);
+       tf->payload_type = WLAN_TDLS_SNAP_RFTYPE;
+
+       switch (action_code) {
+       case WLAN_TDLS_SETUP_REQUEST:
+               tf->category = WLAN_CATEGORY_TDLS;
+               tf->action_code = WLAN_TDLS_SETUP_REQUEST;
+
+               skb_put(skb, sizeof(tf->u.setup_req));
+               tf->u.setup_req.dialog_token = dialog_token;
+               tf->u.setup_req.capability =
+                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
+
+               ieee80211_add_srates_ie(sdata, skb, false, band);
+               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
+               ieee80211_tdls_add_ext_capab(skb);
+               break;
+       case WLAN_TDLS_SETUP_RESPONSE:
+               tf->category = WLAN_CATEGORY_TDLS;
+               tf->action_code = WLAN_TDLS_SETUP_RESPONSE;
+
+               skb_put(skb, sizeof(tf->u.setup_resp));
+               tf->u.setup_resp.status_code = cpu_to_le16(status_code);
+               tf->u.setup_resp.dialog_token = dialog_token;
+               tf->u.setup_resp.capability =
+                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
+
+               ieee80211_add_srates_ie(sdata, skb, false, band);
+               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
+               ieee80211_tdls_add_ext_capab(skb);
+               break;
+       case WLAN_TDLS_SETUP_CONFIRM:
+               tf->category = WLAN_CATEGORY_TDLS;
+               tf->action_code = WLAN_TDLS_SETUP_CONFIRM;
+
+               skb_put(skb, sizeof(tf->u.setup_cfm));
+               tf->u.setup_cfm.status_code = cpu_to_le16(status_code);
+               tf->u.setup_cfm.dialog_token = dialog_token;
+               break;
+       case WLAN_TDLS_TEARDOWN:
+               tf->category = WLAN_CATEGORY_TDLS;
+               tf->action_code = WLAN_TDLS_TEARDOWN;
+
+               skb_put(skb, sizeof(tf->u.teardown));
+               tf->u.teardown.reason_code = cpu_to_le16(status_code);
+               break;
+       case WLAN_TDLS_DISCOVERY_REQUEST:
+               tf->category = WLAN_CATEGORY_TDLS;
+               tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST;
+
+               skb_put(skb, sizeof(tf->u.discover_req));
+               tf->u.discover_req.dialog_token = dialog_token;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int
+ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
+                          const u8 *peer, u8 action_code, u8 dialog_token,
+                          u16 status_code, struct sk_buff *skb)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
+       struct ieee80211_mgmt *mgmt;
+
+       mgmt = (void *)skb_put(skb, 24);
+       memset(mgmt, 0, 24);
+       memcpy(mgmt->da, peer, ETH_ALEN);
+       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
+       memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
+
+       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                         IEEE80211_STYPE_ACTION);
+
+       switch (action_code) {
+       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+               skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp));
+               mgmt->u.action.category = WLAN_CATEGORY_PUBLIC;
+               mgmt->u.action.u.tdls_discover_resp.action_code =
+                       WLAN_PUB_ACTION_TDLS_DISCOVER_RES;
+               mgmt->u.action.u.tdls_discover_resp.dialog_token =
+                       dialog_token;
+               mgmt->u.action.u.tdls_discover_resp.capability =
+                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
+
+               ieee80211_add_srates_ie(sdata, skb, false, band);
+               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
+               ieee80211_tdls_add_ext_capab(skb);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
+                       const u8 *peer, u8 action_code, u8 dialog_token,
+                       u16 status_code, u32 peer_capability,
+                       const u8 *extra_ies, size_t extra_ies_len)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
+       struct sk_buff *skb = NULL;
+       bool send_direct;
+       int ret;
+
+       if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
+               return -ENOTSUPP;
+
+       /* make sure we are in managed mode, and associated */
+       if (sdata->vif.type != NL80211_IFTYPE_STATION ||
+           !sdata->u.mgd.associated)
+               return -EINVAL;
+
+       tdls_dbg(sdata, "TDLS mgmt action %d peer %pM\n",
+                action_code, peer);
+
+       skb = dev_alloc_skb(local->hw.extra_tx_headroom +
+                           max(sizeof(struct ieee80211_mgmt),
+                               sizeof(struct ieee80211_tdls_data)) +
+                           50 + /* supported rates */
+                           7 + /* ext capab */
+                           extra_ies_len +
+                           sizeof(struct ieee80211_tdls_lnkie));
+       if (!skb)
+               return -ENOMEM;
+
+       skb_reserve(skb, local->hw.extra_tx_headroom);
+
+       switch (action_code) {
+       case WLAN_TDLS_SETUP_REQUEST:
+       case WLAN_TDLS_SETUP_RESPONSE:
+       case WLAN_TDLS_SETUP_CONFIRM:
+       case WLAN_TDLS_TEARDOWN:
+       case WLAN_TDLS_DISCOVERY_REQUEST:
+               ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer,
+                                                    action_code, dialog_token,
+                                                    status_code, skb);
+               send_direct = false;
+               break;
+       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+               ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code,
+                                                dialog_token, status_code,
+                                                skb);
+               send_direct = true;
+               break;
+       default:
+               ret = -ENOTSUPP;
+               break;
+       }
+
+       if (ret < 0)
+               goto fail;
+
+       if (extra_ies_len)
+               memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
+
+       /* the TDLS link IE is always added last */
+       switch (action_code) {
+       case WLAN_TDLS_SETUP_REQUEST:
+       case WLAN_TDLS_SETUP_CONFIRM:
+       case WLAN_TDLS_TEARDOWN:
+       case WLAN_TDLS_DISCOVERY_REQUEST:
+               /* we are the initiator */
+               ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer,
+                                          sdata->u.mgd.bssid);
+               break;
+       case WLAN_TDLS_SETUP_RESPONSE:
+       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+               /* we are the responder */
+               ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr,
+                                          sdata->u.mgd.bssid);
+               break;
+       default:
+               ret = -ENOTSUPP;
+               goto fail;
+       }
+
+       if (send_direct) {
+               ieee80211_tx_skb(sdata, skb);
+               return 0;
+       }
+
+       /*
+        * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise
+        * we should default to AC_VI.
+        */
+       switch (action_code) {
+       case WLAN_TDLS_SETUP_REQUEST:
+       case WLAN_TDLS_SETUP_RESPONSE:
+               skb_set_queue_mapping(skb, IEEE80211_AC_BK);
+               skb->priority = 2;
+               break;
+       default:
+               skb_set_queue_mapping(skb, IEEE80211_AC_VI);
+               skb->priority = 5;
+               break;
+       }
+
+       /* disable bottom halves when entering the Tx path */
+       local_bh_disable();
+       ret = ieee80211_subif_start_xmit(skb, dev);
+       local_bh_enable();
+
+       return ret;
+
+fail:
+       dev_kfree_skb(skb);
+       return ret;
+}
+
+int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
+                       const u8 *peer, enum nl80211_tdls_operation oper)
+{
+       struct sta_info *sta;
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
+               return -ENOTSUPP;
+
+       if (sdata->vif.type != NL80211_IFTYPE_STATION)
+               return -EINVAL;
+
+       tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer);
+
+       switch (oper) {
+       case NL80211_TDLS_ENABLE_LINK:
+               rcu_read_lock();
+               sta = sta_info_get(sdata, peer);
+               if (!sta) {
+                       rcu_read_unlock();
+                       return -ENOLINK;
+               }
+
+               set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
+               rcu_read_unlock();
+               break;
+       case NL80211_TDLS_DISABLE_LINK:
+               return sta_info_destroy_addr(sdata, peer);
+       case NL80211_TDLS_TEARDOWN:
+       case NL80211_TDLS_SETUP:
+       case NL80211_TDLS_DISCOVERY_REQ:
+               /* We don't support in-driver setup/teardown/discovery */
+               return -ENOTSUPP;
+       default:
+               return -ENOTSUPP;
+       }
+
+       return 0;
+}
index cec5b60487a4032e2b805df374616ea9ad038669..cfe1a0688b5ce2846eb906b3f196c5cf51a4590b 100644 (file)
@@ -184,6 +184,20 @@ TRACE_EVENT(drv_return_bool,
                  "true" : "false")
 );
 
+TRACE_EVENT(drv_return_u32,
+       TP_PROTO(struct ieee80211_local *local, u32 ret),
+       TP_ARGS(local, ret),
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               __field(u32, ret)
+       ),
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               __entry->ret = ret;
+       ),
+       TP_printk(LOCAL_PR_FMT " - %u", LOCAL_PR_ARG, __entry->ret)
+);
+
 TRACE_EVENT(drv_return_u64,
        TP_PROTO(struct ieee80211_local *local, u64 ret),
        TP_ARGS(local, ret),
@@ -1375,6 +1389,91 @@ TRACE_EVENT(drv_change_chanctx,
        )
 );
 
+#if !defined(__TRACE_VIF_ENTRY)
+#define __TRACE_VIF_ENTRY
+struct trace_vif_entry {
+       enum nl80211_iftype vif_type;
+       bool p2p;
+       char vif_name[IFNAMSIZ];
+} __packed;
+
+struct trace_chandef_entry {
+       u32 control_freq;
+       u32 chan_width;
+       u32 center_freq1;
+       u32 center_freq2;
+} __packed;
+
+struct trace_switch_entry {
+       struct trace_vif_entry vif;
+       struct trace_chandef_entry old_chandef;
+       struct trace_chandef_entry new_chandef;
+} __packed;
+
+#define SWITCH_ENTRY_ASSIGN(to, from) local_vifs[i].to = vifs[i].from
+#endif
+
+TRACE_EVENT(drv_switch_vif_chanctx,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_vif_chanctx_switch *vifs,
+                int n_vifs, enum ieee80211_chanctx_switch_mode mode),
+           TP_ARGS(local, vifs, n_vifs, mode),
+
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               __field(int, n_vifs)
+               __field(u32, mode)
+               __dynamic_array(u8, vifs,
+                               sizeof(struct trace_switch_entry) * n_vifs)
+       ),
+
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               __entry->n_vifs = n_vifs;
+               __entry->mode = mode;
+               {
+                       struct trace_switch_entry *local_vifs =
+                               __get_dynamic_array(vifs);
+                       int i;
+
+                       for (i = 0; i < n_vifs; i++) {
+                               struct ieee80211_sub_if_data *sdata;
+
+                               sdata = container_of(vifs[i].vif,
+                                               struct ieee80211_sub_if_data,
+                                               vif);
+
+                               SWITCH_ENTRY_ASSIGN(vif.vif_type, vif->type);
+                               SWITCH_ENTRY_ASSIGN(vif.p2p, vif->p2p);
+                               strncpy(local_vifs[i].vif.vif_name,
+                                       sdata->name,
+                                       sizeof(local_vifs[i].vif.vif_name));
+                               SWITCH_ENTRY_ASSIGN(old_chandef.control_freq,
+                                               old_ctx->def.chan->center_freq);
+                               SWITCH_ENTRY_ASSIGN(old_chandef.chan_width,
+                                                   old_ctx->def.width);
+                               SWITCH_ENTRY_ASSIGN(old_chandef.center_freq1,
+                                                   old_ctx->def.center_freq1);
+                               SWITCH_ENTRY_ASSIGN(old_chandef.center_freq2,
+                                                   old_ctx->def.center_freq2);
+                               SWITCH_ENTRY_ASSIGN(new_chandef.control_freq,
+                                               new_ctx->def.chan->center_freq);
+                               SWITCH_ENTRY_ASSIGN(new_chandef.chan_width,
+                                                   new_ctx->def.width);
+                               SWITCH_ENTRY_ASSIGN(new_chandef.center_freq1,
+                                                   new_ctx->def.center_freq1);
+                               SWITCH_ENTRY_ASSIGN(new_chandef.center_freq2,
+                                                   new_ctx->def.center_freq2);
+                       }
+               }
+       ),
+
+       TP_printk(
+               LOCAL_PR_FMT " n_vifs:%d mode:%d",
+               LOCAL_PR_ARG, __entry->n_vifs, __entry->mode
+       )
+);
+
 DECLARE_EVENT_CLASS(local_sdata_chanctx,
        TP_PROTO(struct ieee80211_local *local,
                 struct ieee80211_sub_if_data *sdata,
@@ -1499,6 +1598,24 @@ DEFINE_EVENT(local_sdata_evt, drv_leave_ibss,
        TP_ARGS(local, sdata)
 );
 
+TRACE_EVENT(drv_get_expected_throughput,
+       TP_PROTO(struct ieee80211_sta *sta),
+
+       TP_ARGS(sta),
+
+       TP_STRUCT__entry(
+               STA_ENTRY
+       ),
+
+       TP_fast_assign(
+               STA_ASSIGN;
+       ),
+
+       TP_printk(
+               STA_PR_FMT, STA_PR_ARG
+       )
+);
+
 /*
  * Tracing for API calls that drivers call.
  */
index 19d36d4117e0da0b5524b7e3f8102a86810dceed..5214686d9fd1ec9ab4bc1e2a466532bd3c829c10 100644 (file)
@@ -2328,7 +2328,8 @@ void ieee80211_tx_pending(unsigned long data)
 /* functions for drivers to get certain frames */
 
 static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
-                                      struct ps_data *ps, struct sk_buff *skb)
+                                      struct ps_data *ps, struct sk_buff *skb,
+                                      bool is_template)
 {
        u8 *pos, *tim;
        int aid0 = 0;
@@ -2341,11 +2342,12 @@ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
                 * checking byte-for-byte */
                have_bits = !bitmap_empty((unsigned long *)ps->tim,
                                          IEEE80211_MAX_AID+1);
-
-       if (ps->dtim_count == 0)
-               ps->dtim_count = sdata->vif.bss_conf.dtim_period - 1;
-       else
-               ps->dtim_count--;
+       if (!is_template) {
+               if (ps->dtim_count == 0)
+                       ps->dtim_count = sdata->vif.bss_conf.dtim_period - 1;
+               else
+                       ps->dtim_count--;
+       }
 
        tim = pos = (u8 *) skb_put(skb, 6);
        *pos++ = WLAN_EID_TIM;
@@ -2391,7 +2393,8 @@ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
 }
 
 static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
-                                   struct ps_data *ps, struct sk_buff *skb)
+                                   struct ps_data *ps, struct sk_buff *skb,
+                                   bool is_template)
 {
        struct ieee80211_local *local = sdata->local;
 
@@ -2403,24 +2406,24 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
         * of the tim bitmap in mac80211 and the driver.
         */
        if (local->tim_in_locked_section) {
-               __ieee80211_beacon_add_tim(sdata, ps, skb);
+               __ieee80211_beacon_add_tim(sdata, ps, skb, is_template);
        } else {
                spin_lock_bh(&local->tim_lock);
-               __ieee80211_beacon_add_tim(sdata, ps, skb);
+               __ieee80211_beacon_add_tim(sdata, ps, skb, is_template);
                spin_unlock_bh(&local->tim_lock);
        }
 
        return 0;
 }
 
-static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
-                                struct beacon_data *beacon)
+static void ieee80211_set_csa(struct ieee80211_sub_if_data *sdata,
+                             struct beacon_data *beacon)
 {
        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;
+       int i;
+       u8 count = sdata->csa_current_counter;
 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
@@ -2438,40 +2441,57 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
        default:
                return;
        }
-       if (WARN_ON(counter_offset_beacon >= beacon_data_len))
-               return;
 
-       /* Warn if the driver did not check for/react to csa
-        * completeness.  A beacon with CSA counter set to 0 should
-        * never occur, because a counter of 1 means switch just
-        * before the next beacon.
-        */
-       if (WARN_ON(beacon_data[counter_offset_beacon] == 1))
-               return;
+       for (i = 0; i < IEEE80211_MAX_CSA_COUNTERS_NUM; ++i) {
+               u16 counter_offset_beacon =
+                       sdata->csa_counter_offset_beacon[i];
+               u16 counter_offset_presp = sdata->csa_counter_offset_presp[i];
 
-       beacon_data[counter_offset_beacon]--;
+               if (counter_offset_beacon) {
+                       if (WARN_ON(counter_offset_beacon >= beacon_data_len))
+                               return;
 
-       if (sdata->vif.type == NL80211_IFTYPE_AP && counter_offset_presp) {
-               rcu_read_lock();
-               resp = rcu_dereference(sdata->u.ap.probe_resp);
+                       beacon_data[counter_offset_beacon] = count;
+               }
+
+               if (sdata->vif.type == NL80211_IFTYPE_AP &&
+                   counter_offset_presp) {
+                       rcu_read_lock();
+                       resp = rcu_dereference(sdata->u.ap.probe_resp);
 
-               /* if nl80211 accepted the offset, this should not happen. */
-               if (WARN_ON(!resp)) {
+                       /* If nl80211 accepted the offset, this should
+                        * not happen.
+                        */
+                       if (WARN_ON(!resp)) {
+                               rcu_read_unlock();
+                               return;
+                       }
+                       resp->data[counter_offset_presp] = count;
                        rcu_read_unlock();
-                       return;
                }
-               resp->data[counter_offset_presp]--;
-               rcu_read_unlock();
        }
 }
 
+u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+       sdata->csa_current_counter--;
+
+       /* the counter should never reach 0 */
+       WARN_ON(!sdata->csa_current_counter);
+
+       return sdata->csa_current_counter;
+}
+EXPORT_SYMBOL(ieee80211_csa_update_counter);
+
 bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
 {
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
        struct beacon_data *beacon = NULL;
        u8 *beacon_data;
        size_t beacon_data_len;
-       int counter_beacon = sdata->csa_counter_offset_beacon;
+       int counter_beacon = sdata->csa_counter_offset_beacon[0];
        int ret = false;
 
        if (!ieee80211_sdata_running(sdata))
@@ -2521,9 +2541,11 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
 }
 EXPORT_SYMBOL(ieee80211_csa_is_complete);
 
-struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
-                                        struct ieee80211_vif *vif,
-                                        u16 *tim_offset, u16 *tim_length)
+static struct sk_buff *
+__ieee80211_beacon_get(struct ieee80211_hw *hw,
+                      struct ieee80211_vif *vif,
+                      struct ieee80211_mutable_offsets *offs,
+                      bool is_template)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        struct sk_buff *skb = NULL;
@@ -2532,6 +2554,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
        enum ieee80211_band band;
        struct ieee80211_tx_rate_control txrc;
        struct ieee80211_chanctx_conf *chanctx_conf;
+       int csa_off_base = 0;
 
        rcu_read_lock();
 
@@ -2541,18 +2564,20 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
        if (!ieee80211_sdata_running(sdata) || !chanctx_conf)
                goto out;
 
-       if (tim_offset)
-               *tim_offset = 0;
-       if (tim_length)
-               *tim_length = 0;
+       if (offs)
+               memset(offs, 0, sizeof(*offs));
 
        if (sdata->vif.type == NL80211_IFTYPE_AP) {
                struct ieee80211_if_ap *ap = &sdata->u.ap;
                struct beacon_data *beacon = rcu_dereference(ap->beacon);
 
                if (beacon) {
-                       if (sdata->vif.csa_active)
-                               ieee80211_update_csa(sdata, beacon);
+                       if (sdata->vif.csa_active) {
+                               if (!is_template)
+                                       ieee80211_csa_update_counter(vif);
+
+                               ieee80211_set_csa(sdata, beacon);
+                       }
 
                        /*
                         * headroom, head length,
@@ -2569,12 +2594,16 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
                        memcpy(skb_put(skb, beacon->head_len), beacon->head,
                               beacon->head_len);
 
-                       ieee80211_beacon_add_tim(sdata, &ap->ps, skb);
+                       ieee80211_beacon_add_tim(sdata, &ap->ps, skb,
+                                                is_template);
 
-                       if (tim_offset)
-                               *tim_offset = beacon->head_len;
-                       if (tim_length)
-                               *tim_length = skb->len - beacon->head_len;
+                       if (offs) {
+                               offs->tim_offset = beacon->head_len;
+                               offs->tim_length = skb->len - beacon->head_len;
+
+                               /* for AP the csa offsets are from tail */
+                               csa_off_base = skb->len;
+                       }
 
                        if (beacon->tail)
                                memcpy(skb_put(skb, beacon->tail_len),
@@ -2589,9 +2618,12 @@ 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);
+               if (sdata->vif.csa_active) {
+                       if (!is_template)
+                               ieee80211_csa_update_counter(vif);
 
+                       ieee80211_set_csa(sdata, presp);
+               }
 
                skb = dev_alloc_skb(local->tx_headroom + presp->head_len +
                                    local->hw.extra_beacon_tailroom);
@@ -2611,8 +2643,17 @@ 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 (sdata->vif.csa_active) {
+                       if (!is_template)
+                               /* TODO: For mesh csa_counter is in TU, so
+                                * decrementing it by one isn't correct, but
+                                * for now we leave it consistent with overall
+                                * mac80211's behavior.
+                                */
+                               ieee80211_csa_update_counter(vif);
+
+                       ieee80211_set_csa(sdata, bcn);
+               }
 
                if (ifmsh->sync_ops)
                        ifmsh->sync_ops->adjust_tbtt(sdata, bcn);
@@ -2626,13 +2667,33 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
                        goto out;
                skb_reserve(skb, local->tx_headroom);
                memcpy(skb_put(skb, bcn->head_len), bcn->head, bcn->head_len);
-               ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb);
+               ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb, is_template);
+
+               if (offs) {
+                       offs->tim_offset = bcn->head_len;
+                       offs->tim_length = skb->len - bcn->head_len;
+               }
+
                memcpy(skb_put(skb, bcn->tail_len), bcn->tail, bcn->tail_len);
        } else {
                WARN_ON(1);
                goto out;
        }
 
+       /* CSA offsets */
+       if (offs) {
+               int i;
+
+               for (i = 0; i < IEEE80211_MAX_CSA_COUNTERS_NUM; i++) {
+                       u16 csa_off = sdata->csa_counter_offset_beacon[i];
+
+                       if (!csa_off)
+                               continue;
+
+                       offs->csa_counter_offs[i] = csa_off_base + csa_off;
+               }
+       }
+
        band = chanctx_conf->def.chan->band;
 
        info = IEEE80211_SKB_CB(skb);
@@ -2663,6 +2724,32 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
  out:
        rcu_read_unlock();
        return skb;
+
+}
+
+struct sk_buff *
+ieee80211_beacon_get_template(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif,
+                             struct ieee80211_mutable_offsets *offs)
+{
+       return __ieee80211_beacon_get(hw, vif, offs, true);
+}
+EXPORT_SYMBOL(ieee80211_beacon_get_template);
+
+struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
+                                        struct ieee80211_vif *vif,
+                                        u16 *tim_offset, u16 *tim_length)
+{
+       struct ieee80211_mutable_offsets offs = {};
+       struct sk_buff *bcn = __ieee80211_beacon_get(hw, vif, &offs, false);
+
+       if (tim_offset)
+               *tim_offset = offs.tim_offset;
+
+       if (tim_length)
+               *tim_length = offs.tim_length;
+
+       return bcn;
 }
 EXPORT_SYMBOL(ieee80211_beacon_get_tim);
 
index 3c365837e910edce60ac2348b0002361c4fca6ea..6886601afe1c731c3cc7b5409745307b2f48e67c 100644 (file)
@@ -554,7 +554,7 @@ void ieee80211_flush_queues(struct ieee80211_local *local,
        ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_FLUSH);
 
-       drv_flush(local, queues, false);
+       drv_flush(local, sdata, queues, false);
 
        ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
                                        IEEE80211_QUEUE_STOP_REASON_FLUSH);
@@ -1457,6 +1457,44 @@ void ieee80211_stop_device(struct ieee80211_local *local)
        drv_stop(local);
 }
 
+static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local)
+{
+       struct ieee80211_sub_if_data *sdata;
+       struct ieee80211_chanctx *ctx;
+
+       /*
+        * We get here if during resume the device can't be restarted properly.
+        * We might also get here if this happens during HW reset, which is a
+        * slightly different situation and we need to drop all connections in
+        * the latter case.
+        *
+        * Ask cfg80211 to turn off all interfaces, this will result in more
+        * warnings but at least we'll then get into a clean stopped state.
+        */
+
+       local->resuming = false;
+       local->suspended = false;
+       local->started = false;
+
+       /* scheduled scan clearly can't be running any more, but tell
+        * cfg80211 and clear local state
+        */
+       ieee80211_sched_scan_end(local);
+
+       list_for_each_entry(sdata, &local->interfaces, list)
+               sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER;
+
+       /* Mark channel contexts as not being in the driver any more to avoid
+        * removing them from the driver during the shutdown process...
+        */
+       mutex_lock(&local->chanctx_mtx);
+       list_for_each_entry(ctx, &local->chanctx_list, list)
+               ctx->driver_present = false;
+       mutex_unlock(&local->chanctx_mtx);
+
+       cfg80211_shutdown_all_interfaces(local->hw.wiphy);
+}
+
 static void ieee80211_assign_chanctx(struct ieee80211_local *local,
                                     struct ieee80211_sub_if_data *sdata)
 {
@@ -1520,9 +1558,11 @@ int ieee80211_reconfig(struct ieee80211_local *local)
         */
        res = drv_start(local);
        if (res) {
-               WARN(local->suspended, "Hardware became unavailable "
-                    "upon resume. This could be a software issue "
-                    "prior to suspend or a hardware issue.\n");
+               if (local->suspended)
+                       WARN(1, "Hardware became unavailable upon resume. This could be a software issue prior to suspend or a hardware issue.\n");
+               else
+                       WARN(1, "Hardware became unavailable during restart.\n");
+               ieee80211_handle_reconfig_failure(local);
                return res;
        }
 
@@ -1546,7 +1586,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                WARN_ON(local->resuming);
                res = drv_add_interface(local, sdata);
                if (WARN_ON(res)) {
-                       rcu_assign_pointer(local->monitor_sdata, NULL);
+                       RCU_INIT_POINTER(local->monitor_sdata, NULL);
                        synchronize_net();
                        kfree(sdata);
                }
@@ -1565,17 +1605,17 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                list_for_each_entry(ctx, &local->chanctx_list, list)
                        WARN_ON(drv_add_chanctx(local, ctx));
                mutex_unlock(&local->chanctx_mtx);
-       }
 
-       list_for_each_entry(sdata, &local->interfaces, list) {
-               if (!ieee80211_sdata_running(sdata))
-                       continue;
-               ieee80211_assign_chanctx(local, sdata);
-       }
+               list_for_each_entry(sdata, &local->interfaces, list) {
+                       if (!ieee80211_sdata_running(sdata))
+                               continue;
+                       ieee80211_assign_chanctx(local, sdata);
+               }
 
-       sdata = rtnl_dereference(local->monitor_sdata);
-       if (sdata && ieee80211_sdata_running(sdata))
-               ieee80211_assign_chanctx(local, sdata);
+               sdata = rtnl_dereference(local->monitor_sdata);
+               if (sdata && ieee80211_sdata_running(sdata))
+                       ieee80211_assign_chanctx(local, sdata);
+       }
 
        /* add STAs back */
        mutex_lock(&local->sta_mtx);
@@ -1671,13 +1711,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                        }
                        break;
                case NL80211_IFTYPE_WDS:
-                       break;
                case NL80211_IFTYPE_AP_VLAN:
                case NL80211_IFTYPE_MONITOR:
-                       /* ignore virtual */
-                       break;
                case NL80211_IFTYPE_P2P_DEVICE:
-                       changed = BSS_CHANGED_IDLE;
+                       /* nothing to do */
                        break;
                case NL80211_IFTYPE_UNSPECIFIED:
                case NUM_NL80211_IFTYPES:
@@ -2797,3 +2834,121 @@ void ieee80211_recalc_dtim(struct ieee80211_local *local,
 
        ps->dtim_count = dtim_count;
 }
+
+int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
+                                const struct cfg80211_chan_def *chandef,
+                                enum ieee80211_chanctx_mode chanmode,
+                                u8 radar_detect)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_sub_if_data *sdata_iter;
+       enum nl80211_iftype iftype = sdata->wdev.iftype;
+       int num[NUM_NL80211_IFTYPES];
+       struct ieee80211_chanctx *ctx;
+       int num_different_channels = 0;
+       int total = 1;
+
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       if (WARN_ON(hweight32(radar_detect) > 1))
+               return -EINVAL;
+
+       if (WARN_ON(chandef && chanmode == IEEE80211_CHANCTX_SHARED &&
+                   !chandef->chan))
+               return -EINVAL;
+
+       if (chandef)
+               num_different_channels = 1;
+
+       if (WARN_ON(iftype >= NUM_NL80211_IFTYPES))
+               return -EINVAL;
+
+       /* Always allow software iftypes */
+       if (local->hw.wiphy->software_iftypes & BIT(iftype)) {
+               if (radar_detect)
+                       return -EINVAL;
+               return 0;
+       }
+
+       memset(num, 0, sizeof(num));
+
+       if (iftype != NL80211_IFTYPE_UNSPECIFIED)
+               num[iftype] = 1;
+
+       list_for_each_entry(ctx, &local->chanctx_list, list) {
+               if (ctx->conf.radar_enabled)
+                       radar_detect |= BIT(ctx->conf.def.width);
+               if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) {
+                       num_different_channels++;
+                       continue;
+               }
+               if (chandef && chanmode == IEEE80211_CHANCTX_SHARED &&
+                   cfg80211_chandef_compatible(chandef,
+                                               &ctx->conf.def))
+                       continue;
+               num_different_channels++;
+       }
+
+       list_for_each_entry_rcu(sdata_iter, &local->interfaces, list) {
+               struct wireless_dev *wdev_iter;
+
+               wdev_iter = &sdata_iter->wdev;
+
+               if (sdata_iter == sdata ||
+                   rcu_access_pointer(sdata_iter->vif.chanctx_conf) == NULL ||
+                   local->hw.wiphy->software_iftypes & BIT(wdev_iter->iftype))
+                       continue;
+
+               num[wdev_iter->iftype]++;
+               total++;
+       }
+
+       if (total == 1 && !radar_detect)
+               return 0;
+
+       return cfg80211_check_combinations(local->hw.wiphy,
+                                          num_different_channels,
+                                          radar_detect, num);
+}
+
+static void
+ieee80211_iter_max_chans(const struct ieee80211_iface_combination *c,
+                        void *data)
+{
+       u32 *max_num_different_channels = data;
+
+       *max_num_different_channels = max(*max_num_different_channels,
+                                         c->num_different_channels);
+}
+
+int ieee80211_max_num_channels(struct ieee80211_local *local)
+{
+       struct ieee80211_sub_if_data *sdata;
+       int num[NUM_NL80211_IFTYPES] = {};
+       struct ieee80211_chanctx *ctx;
+       int num_different_channels = 0;
+       u8 radar_detect = 0;
+       u32 max_num_different_channels = 1;
+       int err;
+
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       list_for_each_entry(ctx, &local->chanctx_list, list) {
+               num_different_channels++;
+
+               if (ctx->conf.radar_enabled)
+                       radar_detect |= BIT(ctx->conf.def.width);
+       }
+
+       list_for_each_entry_rcu(sdata, &local->interfaces, list)
+               num[sdata->wdev.iftype]++;
+
+       err = cfg80211_iter_combinations(local->hw.wiphy,
+                                        num_different_channels, radar_detect,
+                                        num, ieee80211_iter_max_chans,
+                                        &max_num_different_channels);
+       if (err < 0)
+               return err;
+
+       return max_num_different_channels;
+}
index b8600e3c29c828d918b3676397f73a4d0fe7892c..9b3dcc201145dd3942bf30771e1638ea973759f7 100644 (file)
@@ -406,7 +406,10 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
 
        if (info->control.hw_key &&
            !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) &&
-           !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) {
+           !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) &&
+           !((info->control.hw_key->flags &
+              IEEE80211_KEY_FLAG_GENERATE_IV_MGMT) &&
+             ieee80211_is_mgmt(hdr->frame_control))) {
                /*
                 * hwaccel has no need for preallocated room for CCMP
                 * header or MIC fields
index b33dd76d4307309bb02477606f8e7ccdefaa870d..1818a99b3081e5a87a5c1ed72e6dba1760f2c1bc 100644 (file)
@@ -2,6 +2,10 @@ config MAC802154
        tristate "Generic IEEE 802.15.4 Soft Networking Stack (mac802154)"
        depends on IEEE802154
        select CRC_CCITT
+       select CRYPTO_AUTHENC
+       select CRYPTO_CCM
+       select CRYPTO_CTR
+       select CRYPTO_AES
        ---help---
          This option enables the hardware independent IEEE 802.15.4
          networking stack for SoftMAC devices (the ones implementing
index 15d62df521825c8581fc25ec25a9caebc8902a72..9723d6f3f3e5b742e1d105b2667627949f1d7c7d 100644 (file)
@@ -1,4 +1,5 @@
 obj-$(CONFIG_MAC802154)        += mac802154.o
-mac802154-objs         := ieee802154_dev.o rx.o tx.o mac_cmd.o mib.o monitor.o wpan.o
+mac802154-objs         := ieee802154_dev.o rx.o tx.o mac_cmd.o mib.o \
+                          monitor.o wpan.o llsec.o
 
 ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c
new file mode 100644 (file)
index 0000000..1456f73
--- /dev/null
@@ -0,0 +1,1070 @@
+/*
+ * Copyright (C) 2014 Fraunhofer ITWM
+ *
+ * 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 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.
+ *
+ * Written by:
+ * Phoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de>
+ */
+
+#include <linux/err.h>
+#include <linux/bug.h>
+#include <linux/completion.h>
+#include <net/ieee802154.h>
+#include <crypto/algapi.h>
+
+#include "mac802154.h"
+#include "llsec.h"
+
+static void llsec_key_put(struct mac802154_llsec_key *key);
+static bool llsec_key_id_equal(const struct ieee802154_llsec_key_id *a,
+                              const struct ieee802154_llsec_key_id *b);
+
+static void llsec_dev_free(struct mac802154_llsec_device *dev);
+
+void mac802154_llsec_init(struct mac802154_llsec *sec)
+{
+       memset(sec, 0, sizeof(*sec));
+
+       memset(&sec->params.default_key_source, 0xFF, IEEE802154_ADDR_LEN);
+
+       INIT_LIST_HEAD(&sec->table.security_levels);
+       INIT_LIST_HEAD(&sec->table.devices);
+       INIT_LIST_HEAD(&sec->table.keys);
+       hash_init(sec->devices_short);
+       hash_init(sec->devices_hw);
+       rwlock_init(&sec->lock);
+}
+
+void mac802154_llsec_destroy(struct mac802154_llsec *sec)
+{
+       struct ieee802154_llsec_seclevel *sl, *sn;
+       struct ieee802154_llsec_device *dev, *dn;
+       struct ieee802154_llsec_key_entry *key, *kn;
+
+       list_for_each_entry_safe(sl, sn, &sec->table.security_levels, list) {
+               struct mac802154_llsec_seclevel *msl;
+
+               msl = container_of(sl, struct mac802154_llsec_seclevel, level);
+               list_del(&sl->list);
+               kfree(msl);
+       }
+
+       list_for_each_entry_safe(dev, dn, &sec->table.devices, list) {
+               struct mac802154_llsec_device *mdev;
+
+               mdev = container_of(dev, struct mac802154_llsec_device, dev);
+               list_del(&dev->list);
+               llsec_dev_free(mdev);
+       }
+
+       list_for_each_entry_safe(key, kn, &sec->table.keys, list) {
+               struct mac802154_llsec_key *mkey;
+
+               mkey = container_of(key->key, struct mac802154_llsec_key, key);
+               list_del(&key->list);
+               llsec_key_put(mkey);
+               kfree(key);
+       }
+}
+
+
+
+int mac802154_llsec_get_params(struct mac802154_llsec *sec,
+                              struct ieee802154_llsec_params *params)
+{
+       read_lock_bh(&sec->lock);
+       *params = sec->params;
+       read_unlock_bh(&sec->lock);
+
+       return 0;
+}
+
+int mac802154_llsec_set_params(struct mac802154_llsec *sec,
+                              const struct ieee802154_llsec_params *params,
+                              int changed)
+{
+       write_lock_bh(&sec->lock);
+
+       if (changed & IEEE802154_LLSEC_PARAM_ENABLED)
+               sec->params.enabled = params->enabled;
+       if (changed & IEEE802154_LLSEC_PARAM_FRAME_COUNTER)
+               sec->params.frame_counter = params->frame_counter;
+       if (changed & IEEE802154_LLSEC_PARAM_OUT_LEVEL)
+               sec->params.out_level = params->out_level;
+       if (changed & IEEE802154_LLSEC_PARAM_OUT_KEY)
+               sec->params.out_key = params->out_key;
+       if (changed & IEEE802154_LLSEC_PARAM_KEY_SOURCE)
+               sec->params.default_key_source = params->default_key_source;
+       if (changed & IEEE802154_LLSEC_PARAM_PAN_ID)
+               sec->params.pan_id = params->pan_id;
+       if (changed & IEEE802154_LLSEC_PARAM_HWADDR)
+               sec->params.hwaddr = params->hwaddr;
+       if (changed & IEEE802154_LLSEC_PARAM_COORD_HWADDR)
+               sec->params.coord_hwaddr = params->coord_hwaddr;
+       if (changed & IEEE802154_LLSEC_PARAM_COORD_SHORTADDR)
+               sec->params.coord_shortaddr = params->coord_shortaddr;
+
+       write_unlock_bh(&sec->lock);
+
+       return 0;
+}
+
+
+
+static struct mac802154_llsec_key*
+llsec_key_alloc(const struct ieee802154_llsec_key *template)
+{
+       const int authsizes[3] = { 4, 8, 16 };
+       struct mac802154_llsec_key *key;
+       int i;
+
+       key = kzalloc(sizeof(*key), GFP_KERNEL);
+       if (!key)
+               return NULL;
+
+       kref_init(&key->ref);
+       key->key = *template;
+
+       BUILD_BUG_ON(ARRAY_SIZE(authsizes) != ARRAY_SIZE(key->tfm));
+
+       for (i = 0; i < ARRAY_SIZE(key->tfm); i++) {
+               key->tfm[i] = crypto_alloc_aead("ccm(aes)", 0,
+                                               CRYPTO_ALG_ASYNC);
+               if (!key->tfm[i])
+                       goto err_tfm;
+               if (crypto_aead_setkey(key->tfm[i], template->key,
+                                      IEEE802154_LLSEC_KEY_SIZE))
+                       goto err_tfm;
+               if (crypto_aead_setauthsize(key->tfm[i], authsizes[i]))
+                       goto err_tfm;
+       }
+
+       key->tfm0 = crypto_alloc_blkcipher("ctr(aes)", 0, CRYPTO_ALG_ASYNC);
+       if (!key->tfm0)
+               goto err_tfm;
+
+       if (crypto_blkcipher_setkey(key->tfm0, template->key,
+                                   IEEE802154_LLSEC_KEY_SIZE))
+               goto err_tfm0;
+
+       return key;
+
+err_tfm0:
+       crypto_free_blkcipher(key->tfm0);
+err_tfm:
+       for (i = 0; i < ARRAY_SIZE(key->tfm); i++)
+               if (key->tfm[i])
+                       crypto_free_aead(key->tfm[i]);
+
+       kfree(key);
+       return NULL;
+}
+
+static void llsec_key_release(struct kref *ref)
+{
+       struct mac802154_llsec_key *key;
+       int i;
+
+       key = container_of(ref, struct mac802154_llsec_key, ref);
+
+       for (i = 0; i < ARRAY_SIZE(key->tfm); i++)
+               crypto_free_aead(key->tfm[i]);
+
+       crypto_free_blkcipher(key->tfm0);
+       kfree(key);
+}
+
+static struct mac802154_llsec_key*
+llsec_key_get(struct mac802154_llsec_key *key)
+{
+       kref_get(&key->ref);
+       return key;
+}
+
+static void llsec_key_put(struct mac802154_llsec_key *key)
+{
+       kref_put(&key->ref, llsec_key_release);
+}
+
+static bool llsec_key_id_equal(const struct ieee802154_llsec_key_id *a,
+                              const struct ieee802154_llsec_key_id *b)
+{
+       if (a->mode != b->mode)
+               return false;
+
+       if (a->mode == IEEE802154_SCF_KEY_IMPLICIT)
+               return ieee802154_addr_equal(&a->device_addr, &b->device_addr);
+
+       if (a->id != b->id)
+               return false;
+
+       switch (a->mode) {
+       case IEEE802154_SCF_KEY_INDEX:
+               return true;
+       case IEEE802154_SCF_KEY_SHORT_INDEX:
+               return a->short_source == b->short_source;
+       case IEEE802154_SCF_KEY_HW_INDEX:
+               return a->extended_source == b->extended_source;
+       }
+
+       return false;
+}
+
+int mac802154_llsec_key_add(struct mac802154_llsec *sec,
+                           const struct ieee802154_llsec_key_id *id,
+                           const struct ieee802154_llsec_key *key)
+{
+       struct mac802154_llsec_key *mkey = NULL;
+       struct ieee802154_llsec_key_entry *pos, *new;
+
+       if (!(key->frame_types & (1 << IEEE802154_FC_TYPE_MAC_CMD)) &&
+           key->cmd_frame_ids)
+               return -EINVAL;
+
+       list_for_each_entry(pos, &sec->table.keys, list) {
+               if (llsec_key_id_equal(&pos->id, id))
+                       return -EEXIST;
+
+               if (memcmp(pos->key->key, key->key,
+                          IEEE802154_LLSEC_KEY_SIZE))
+                       continue;
+
+               mkey = container_of(pos->key, struct mac802154_llsec_key, key);
+
+               /* Don't allow multiple instances of the same AES key to have
+                * different allowed frame types/command frame ids, as this is
+                * not possible in the 802.15.4 PIB.
+                */
+               if (pos->key->frame_types != key->frame_types ||
+                   pos->key->cmd_frame_ids != key->cmd_frame_ids)
+                       return -EEXIST;
+
+               break;
+       }
+
+       new = kzalloc(sizeof(*new), GFP_KERNEL);
+       if (!new)
+               return -ENOMEM;
+
+       if (!mkey)
+               mkey = llsec_key_alloc(key);
+       else
+               mkey = llsec_key_get(mkey);
+
+       if (!mkey)
+               goto fail;
+
+       new->id = *id;
+       new->key = &mkey->key;
+
+       list_add_rcu(&new->list, &sec->table.keys);
+
+       return 0;
+
+fail:
+       kfree(new);
+       return -ENOMEM;
+}
+
+int mac802154_llsec_key_del(struct mac802154_llsec *sec,
+                           const struct ieee802154_llsec_key_id *key)
+{
+       struct ieee802154_llsec_key_entry *pos;
+
+       list_for_each_entry(pos, &sec->table.keys, list) {
+               struct mac802154_llsec_key *mkey;
+
+               mkey = container_of(pos->key, struct mac802154_llsec_key, key);
+
+               if (llsec_key_id_equal(&pos->id, key)) {
+                       list_del_rcu(&pos->list);
+                       llsec_key_put(mkey);
+                       return 0;
+               }
+       }
+
+       return -ENOENT;
+}
+
+
+
+static bool llsec_dev_use_shortaddr(__le16 short_addr)
+{
+       return short_addr != cpu_to_le16(IEEE802154_ADDR_UNDEF) &&
+               short_addr != cpu_to_le16(0xffff);
+}
+
+static u32 llsec_dev_hash_short(__le16 short_addr, __le16 pan_id)
+{
+       return ((__force u16) short_addr) << 16 | (__force u16) pan_id;
+}
+
+static u64 llsec_dev_hash_long(__le64 hwaddr)
+{
+       return (__force u64) hwaddr;
+}
+
+static struct mac802154_llsec_device*
+llsec_dev_find_short(struct mac802154_llsec *sec, __le16 short_addr,
+                    __le16 pan_id)
+{
+       struct mac802154_llsec_device *dev;
+       u32 key = llsec_dev_hash_short(short_addr, pan_id);
+
+       hash_for_each_possible_rcu(sec->devices_short, dev, bucket_s, key) {
+               if (dev->dev.short_addr == short_addr &&
+                   dev->dev.pan_id == pan_id)
+                       return dev;
+       }
+
+       return NULL;
+}
+
+static struct mac802154_llsec_device*
+llsec_dev_find_long(struct mac802154_llsec *sec, __le64 hwaddr)
+{
+       struct mac802154_llsec_device *dev;
+       u64 key = llsec_dev_hash_long(hwaddr);
+
+       hash_for_each_possible_rcu(sec->devices_hw, dev, bucket_hw, key) {
+               if (dev->dev.hwaddr == hwaddr)
+                       return dev;
+       }
+
+       return NULL;
+}
+
+static void llsec_dev_free(struct mac802154_llsec_device *dev)
+{
+       struct ieee802154_llsec_device_key *pos, *pn;
+       struct mac802154_llsec_device_key *devkey;
+
+       list_for_each_entry_safe(pos, pn, &dev->dev.keys, list) {
+               devkey = container_of(pos, struct mac802154_llsec_device_key,
+                                     devkey);
+
+               list_del(&pos->list);
+               kfree(devkey);
+       }
+
+       kfree(dev);
+}
+
+int mac802154_llsec_dev_add(struct mac802154_llsec *sec,
+                           const struct ieee802154_llsec_device *dev)
+{
+       struct mac802154_llsec_device *entry;
+       u32 skey = llsec_dev_hash_short(dev->short_addr, dev->pan_id);
+       u64 hwkey = llsec_dev_hash_long(dev->hwaddr);
+
+       BUILD_BUG_ON(sizeof(hwkey) != IEEE802154_ADDR_LEN);
+
+       if ((llsec_dev_use_shortaddr(dev->short_addr) &&
+            llsec_dev_find_short(sec, dev->short_addr, dev->pan_id)) ||
+            llsec_dev_find_long(sec, dev->hwaddr))
+               return -EEXIST;
+
+       entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       if (!entry)
+               return -ENOMEM;
+
+       entry->dev = *dev;
+       spin_lock_init(&entry->lock);
+       INIT_LIST_HEAD(&entry->dev.keys);
+
+       if (llsec_dev_use_shortaddr(dev->short_addr))
+               hash_add_rcu(sec->devices_short, &entry->bucket_s, skey);
+       else
+               INIT_HLIST_NODE(&entry->bucket_s);
+
+       hash_add_rcu(sec->devices_hw, &entry->bucket_hw, hwkey);
+       list_add_tail_rcu(&entry->dev.list, &sec->table.devices);
+
+       return 0;
+}
+
+static void llsec_dev_free_rcu(struct rcu_head *rcu)
+{
+       llsec_dev_free(container_of(rcu, struct mac802154_llsec_device, rcu));
+}
+
+int mac802154_llsec_dev_del(struct mac802154_llsec *sec, __le64 device_addr)
+{
+       struct mac802154_llsec_device *pos;
+
+       pos = llsec_dev_find_long(sec, device_addr);
+       if (!pos)
+               return -ENOENT;
+
+       hash_del_rcu(&pos->bucket_s);
+       hash_del_rcu(&pos->bucket_hw);
+       call_rcu(&pos->rcu, llsec_dev_free_rcu);
+
+       return 0;
+}
+
+
+
+static struct mac802154_llsec_device_key*
+llsec_devkey_find(struct mac802154_llsec_device *dev,
+                 const struct ieee802154_llsec_key_id *key)
+{
+       struct ieee802154_llsec_device_key *devkey;
+
+       list_for_each_entry_rcu(devkey, &dev->dev.keys, list) {
+               if (!llsec_key_id_equal(key, &devkey->key_id))
+                       continue;
+
+               return container_of(devkey, struct mac802154_llsec_device_key,
+                                   devkey);
+       }
+
+       return NULL;
+}
+
+int mac802154_llsec_devkey_add(struct mac802154_llsec *sec,
+                              __le64 dev_addr,
+                              const struct ieee802154_llsec_device_key *key)
+{
+       struct mac802154_llsec_device *dev;
+       struct mac802154_llsec_device_key *devkey;
+
+       dev = llsec_dev_find_long(sec, dev_addr);
+
+       if (!dev)
+               return -ENOENT;
+
+       if (llsec_devkey_find(dev, &key->key_id))
+               return -EEXIST;
+
+       devkey = kmalloc(sizeof(*devkey), GFP_KERNEL);
+       if (!devkey)
+               return -ENOMEM;
+
+       devkey->devkey = *key;
+       list_add_tail_rcu(&devkey->devkey.list, &dev->dev.keys);
+       return 0;
+}
+
+int mac802154_llsec_devkey_del(struct mac802154_llsec *sec,
+                              __le64 dev_addr,
+                              const struct ieee802154_llsec_device_key *key)
+{
+       struct mac802154_llsec_device *dev;
+       struct mac802154_llsec_device_key *devkey;
+
+       dev = llsec_dev_find_long(sec, dev_addr);
+
+       if (!dev)
+               return -ENOENT;
+
+       devkey = llsec_devkey_find(dev, &key->key_id);
+       if (!devkey)
+               return -ENOENT;
+
+       list_del_rcu(&devkey->devkey.list);
+       kfree_rcu(devkey, rcu);
+       return 0;
+}
+
+
+
+static struct mac802154_llsec_seclevel*
+llsec_find_seclevel(const struct mac802154_llsec *sec,
+                   const struct ieee802154_llsec_seclevel *sl)
+{
+       struct ieee802154_llsec_seclevel *pos;
+
+       list_for_each_entry(pos, &sec->table.security_levels, list) {
+               if (pos->frame_type != sl->frame_type ||
+                   (pos->frame_type == IEEE802154_FC_TYPE_MAC_CMD &&
+                    pos->cmd_frame_id != sl->cmd_frame_id) ||
+                   pos->device_override != sl->device_override ||
+                   pos->sec_levels != sl->sec_levels)
+                       continue;
+
+               return container_of(pos, struct mac802154_llsec_seclevel,
+                                   level);
+       }
+
+       return NULL;
+}
+
+int mac802154_llsec_seclevel_add(struct mac802154_llsec *sec,
+                                const struct ieee802154_llsec_seclevel *sl)
+{
+       struct mac802154_llsec_seclevel *entry;
+
+       if (llsec_find_seclevel(sec, sl))
+               return -EEXIST;
+
+       entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       if (!entry)
+               return -ENOMEM;
+
+       entry->level = *sl;
+
+       list_add_tail_rcu(&entry->level.list, &sec->table.security_levels);
+
+       return 0;
+}
+
+int mac802154_llsec_seclevel_del(struct mac802154_llsec *sec,
+                                const struct ieee802154_llsec_seclevel *sl)
+{
+       struct mac802154_llsec_seclevel *pos;
+
+       pos = llsec_find_seclevel(sec, sl);
+       if (!pos)
+               return -ENOENT;
+
+       list_del_rcu(&pos->level.list);
+       kfree_rcu(pos, rcu);
+
+       return 0;
+}
+
+
+
+static int llsec_recover_addr(struct mac802154_llsec *sec,
+                             struct ieee802154_addr *addr)
+{
+       __le16 caddr = sec->params.coord_shortaddr;
+       addr->pan_id = sec->params.pan_id;
+
+       if (caddr == cpu_to_le16(IEEE802154_ADDR_BROADCAST)) {
+               return -EINVAL;
+       } else if (caddr == cpu_to_le16(IEEE802154_ADDR_UNDEF)) {
+               addr->extended_addr = sec->params.coord_hwaddr;
+               addr->mode = IEEE802154_ADDR_LONG;
+       } else {
+               addr->short_addr = sec->params.coord_shortaddr;
+               addr->mode = IEEE802154_ADDR_SHORT;
+       }
+
+       return 0;
+}
+
+static struct mac802154_llsec_key*
+llsec_lookup_key(struct mac802154_llsec *sec,
+                const struct ieee802154_hdr *hdr,
+                const struct ieee802154_addr *addr,
+                struct ieee802154_llsec_key_id *key_id)
+{
+       struct ieee802154_addr devaddr = *addr;
+       u8 key_id_mode = hdr->sec.key_id_mode;
+       struct ieee802154_llsec_key_entry *key_entry;
+       struct mac802154_llsec_key *key;
+
+       if (key_id_mode == IEEE802154_SCF_KEY_IMPLICIT &&
+           devaddr.mode == IEEE802154_ADDR_NONE) {
+               if (hdr->fc.type == IEEE802154_FC_TYPE_BEACON) {
+                       devaddr.extended_addr = sec->params.coord_hwaddr;
+                       devaddr.mode = IEEE802154_ADDR_LONG;
+               } else if (llsec_recover_addr(sec, &devaddr) < 0) {
+                       return NULL;
+               }
+       }
+
+       list_for_each_entry_rcu(key_entry, &sec->table.keys, list) {
+               const struct ieee802154_llsec_key_id *id = &key_entry->id;
+
+               if (!(key_entry->key->frame_types & BIT(hdr->fc.type)))
+                       continue;
+
+               if (id->mode != key_id_mode)
+                       continue;
+
+               if (key_id_mode == IEEE802154_SCF_KEY_IMPLICIT) {
+                       if (ieee802154_addr_equal(&devaddr, &id->device_addr))
+                               goto found;
+               } else {
+                       if (id->id != hdr->sec.key_id)
+                               continue;
+
+                       if ((key_id_mode == IEEE802154_SCF_KEY_INDEX) ||
+                           (key_id_mode == IEEE802154_SCF_KEY_SHORT_INDEX &&
+                            id->short_source == hdr->sec.short_src) ||
+                           (key_id_mode == IEEE802154_SCF_KEY_HW_INDEX &&
+                            id->extended_source == hdr->sec.extended_src))
+                               goto found;
+               }
+       }
+
+       return NULL;
+
+found:
+       key = container_of(key_entry->key, struct mac802154_llsec_key, key);
+       if (key_id)
+               *key_id = key_entry->id;
+       return llsec_key_get(key);
+}
+
+
+static void llsec_geniv(u8 iv[16], __le64 addr,
+                       const struct ieee802154_sechdr *sec)
+{
+       __be64 addr_bytes = (__force __be64) swab64((__force u64) addr);
+       __be32 frame_counter = (__force __be32) swab32((__force u32) sec->frame_counter);
+
+       iv[0] = 1; /* L' = L - 1 = 1 */
+       memcpy(iv + 1, &addr_bytes, sizeof(addr_bytes));
+       memcpy(iv + 9, &frame_counter, sizeof(frame_counter));
+       iv[13] = sec->level;
+       iv[14] = 0;
+       iv[15] = 1;
+}
+
+static int
+llsec_do_encrypt_unauth(struct sk_buff *skb, const struct mac802154_llsec *sec,
+                       const struct ieee802154_hdr *hdr,
+                       struct mac802154_llsec_key *key)
+{
+       u8 iv[16];
+       struct scatterlist src;
+       struct blkcipher_desc req = {
+               .tfm = key->tfm0,
+               .info = iv,
+               .flags = 0,
+       };
+
+       llsec_geniv(iv, sec->params.hwaddr, &hdr->sec);
+       sg_init_one(&src, skb->data, skb->len);
+       return crypto_blkcipher_encrypt_iv(&req, &src, &src, skb->len);
+}
+
+static struct crypto_aead*
+llsec_tfm_by_len(struct mac802154_llsec_key *key, int authlen)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(key->tfm); i++)
+               if (crypto_aead_authsize(key->tfm[i]) == authlen)
+                       return key->tfm[i];
+
+       BUG();
+}
+
+static int
+llsec_do_encrypt_auth(struct sk_buff *skb, const struct mac802154_llsec *sec,
+                     const struct ieee802154_hdr *hdr,
+                     struct mac802154_llsec_key *key)
+{
+       u8 iv[16];
+       unsigned char *data;
+       int authlen, assoclen, datalen, rc;
+       struct scatterlist src, assoc[2], dst[2];
+       struct aead_request *req;
+
+       authlen = ieee802154_sechdr_authtag_len(&hdr->sec);
+       llsec_geniv(iv, sec->params.hwaddr, &hdr->sec);
+
+       req = aead_request_alloc(llsec_tfm_by_len(key, authlen), GFP_ATOMIC);
+       if (!req)
+               return -ENOMEM;
+
+       sg_init_table(assoc, 2);
+       sg_set_buf(&assoc[0], skb_mac_header(skb), skb->mac_len);
+       assoclen = skb->mac_len;
+
+       data = skb_mac_header(skb) + skb->mac_len;
+       datalen = skb_tail_pointer(skb) - data;
+
+       if (hdr->sec.level & IEEE802154_SCF_SECLEVEL_ENC) {
+               sg_set_buf(&assoc[1], data, 0);
+       } else {
+               sg_set_buf(&assoc[1], data, datalen);
+               assoclen += datalen;
+               datalen = 0;
+       }
+
+       sg_init_one(&src, data, datalen);
+
+       sg_init_table(dst, 2);
+       sg_set_buf(&dst[0], data, datalen);
+       sg_set_buf(&dst[1], skb_put(skb, authlen), authlen);
+
+       aead_request_set_callback(req, 0, NULL, NULL);
+       aead_request_set_assoc(req, assoc, assoclen);
+       aead_request_set_crypt(req, &src, dst, datalen, iv);
+
+       rc = crypto_aead_encrypt(req);
+
+       kfree(req);
+
+       return rc;
+}
+
+static int llsec_do_encrypt(struct sk_buff *skb,
+                           const struct mac802154_llsec *sec,
+                           const struct ieee802154_hdr *hdr,
+                           struct mac802154_llsec_key *key)
+{
+       if (hdr->sec.level == IEEE802154_SCF_SECLEVEL_ENC)
+               return llsec_do_encrypt_unauth(skb, sec, hdr, key);
+       else
+               return llsec_do_encrypt_auth(skb, sec, hdr, key);
+}
+
+int mac802154_llsec_encrypt(struct mac802154_llsec *sec, struct sk_buff *skb)
+{
+       struct ieee802154_hdr hdr;
+       int rc, authlen, hlen;
+       struct mac802154_llsec_key *key;
+       u32 frame_ctr;
+
+       hlen = ieee802154_hdr_pull(skb, &hdr);
+
+       if (hlen < 0 || hdr.fc.type != IEEE802154_FC_TYPE_DATA)
+               return -EINVAL;
+
+       if (!hdr.fc.security_enabled || hdr.sec.level == 0) {
+               skb_push(skb, hlen);
+               return 0;
+       }
+
+       authlen = ieee802154_sechdr_authtag_len(&hdr.sec);
+
+       if (skb->len + hlen + authlen + IEEE802154_MFR_SIZE > IEEE802154_MTU)
+               return -EMSGSIZE;
+
+       rcu_read_lock();
+
+       read_lock_bh(&sec->lock);
+
+       if (!sec->params.enabled) {
+               rc = -EINVAL;
+               goto fail_read;
+       }
+
+       key = llsec_lookup_key(sec, &hdr, &hdr.dest, NULL);
+       if (!key) {
+               rc = -ENOKEY;
+               goto fail_read;
+       }
+
+       read_unlock_bh(&sec->lock);
+
+       write_lock_bh(&sec->lock);
+
+       frame_ctr = be32_to_cpu(sec->params.frame_counter);
+       hdr.sec.frame_counter = cpu_to_le32(frame_ctr);
+       if (frame_ctr == 0xFFFFFFFF) {
+               write_unlock_bh(&sec->lock);
+               llsec_key_put(key);
+               rc = -EOVERFLOW;
+               goto fail;
+       }
+
+       sec->params.frame_counter = cpu_to_be32(frame_ctr + 1);
+
+       write_unlock_bh(&sec->lock);
+
+       rcu_read_unlock();
+
+       skb->mac_len = ieee802154_hdr_push(skb, &hdr);
+       skb_reset_mac_header(skb);
+
+       rc = llsec_do_encrypt(skb, sec, &hdr, key);
+       llsec_key_put(key);
+
+       return rc;
+
+fail_read:
+       read_unlock_bh(&sec->lock);
+fail:
+       rcu_read_unlock();
+       return rc;
+}
+
+
+
+static struct mac802154_llsec_device*
+llsec_lookup_dev(struct mac802154_llsec *sec,
+                const struct ieee802154_addr *addr)
+{
+       struct ieee802154_addr devaddr = *addr;
+       struct mac802154_llsec_device *dev = NULL;
+
+       if (devaddr.mode == IEEE802154_ADDR_NONE &&
+           llsec_recover_addr(sec, &devaddr) < 0)
+               return NULL;
+
+       if (devaddr.mode == IEEE802154_ADDR_SHORT) {
+               u32 key = llsec_dev_hash_short(devaddr.short_addr,
+                                              devaddr.pan_id);
+
+               hash_for_each_possible_rcu(sec->devices_short, dev,
+                                          bucket_s, key) {
+                       if (dev->dev.pan_id == devaddr.pan_id &&
+                           dev->dev.short_addr == devaddr.short_addr)
+                               return dev;
+               }
+       } else {
+               u64 key = llsec_dev_hash_long(devaddr.extended_addr);
+
+               hash_for_each_possible_rcu(sec->devices_hw, dev,
+                                          bucket_hw, key) {
+                       if (dev->dev.hwaddr == devaddr.extended_addr)
+                               return dev;
+               }
+       }
+
+       return NULL;
+}
+
+static int
+llsec_lookup_seclevel(const struct mac802154_llsec *sec,
+                     u8 frame_type, u8 cmd_frame_id,
+                     struct ieee802154_llsec_seclevel *rlevel)
+{
+       struct ieee802154_llsec_seclevel *level;
+
+       list_for_each_entry_rcu(level, &sec->table.security_levels, list) {
+               if (level->frame_type == frame_type &&
+                   (frame_type != IEEE802154_FC_TYPE_MAC_CMD ||
+                    level->cmd_frame_id == cmd_frame_id)) {
+                       *rlevel = *level;
+                       return 0;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static int
+llsec_do_decrypt_unauth(struct sk_buff *skb, const struct mac802154_llsec *sec,
+                       const struct ieee802154_hdr *hdr,
+                       struct mac802154_llsec_key *key, __le64 dev_addr)
+{
+       u8 iv[16];
+       unsigned char *data;
+       int datalen;
+       struct scatterlist src;
+       struct blkcipher_desc req = {
+               .tfm = key->tfm0,
+               .info = iv,
+               .flags = 0,
+       };
+
+       llsec_geniv(iv, dev_addr, &hdr->sec);
+       data = skb_mac_header(skb) + skb->mac_len;
+       datalen = skb_tail_pointer(skb) - data;
+
+       sg_init_one(&src, data, datalen);
+
+       return crypto_blkcipher_decrypt_iv(&req, &src, &src, datalen);
+}
+
+static int
+llsec_do_decrypt_auth(struct sk_buff *skb, const struct mac802154_llsec *sec,
+                     const struct ieee802154_hdr *hdr,
+                     struct mac802154_llsec_key *key, __le64 dev_addr)
+{
+       u8 iv[16];
+       unsigned char *data;
+       int authlen, datalen, assoclen, rc;
+       struct scatterlist src, assoc[2];
+       struct aead_request *req;
+
+       authlen = ieee802154_sechdr_authtag_len(&hdr->sec);
+       llsec_geniv(iv, dev_addr, &hdr->sec);
+
+       req = aead_request_alloc(llsec_tfm_by_len(key, authlen), GFP_ATOMIC);
+       if (!req)
+               return -ENOMEM;
+
+       sg_init_table(assoc, 2);
+       sg_set_buf(&assoc[0], skb_mac_header(skb), skb->mac_len);
+       assoclen = skb->mac_len;
+
+       data = skb_mac_header(skb) + skb->mac_len;
+       datalen = skb_tail_pointer(skb) - data;
+
+       if (hdr->sec.level & IEEE802154_SCF_SECLEVEL_ENC) {
+               sg_set_buf(&assoc[1], data, 0);
+       } else {
+               sg_set_buf(&assoc[1], data, datalen - authlen);
+               assoclen += datalen - authlen;
+               data += datalen - authlen;
+               datalen = authlen;
+       }
+
+       sg_init_one(&src, data, datalen);
+
+       aead_request_set_callback(req, 0, NULL, NULL);
+       aead_request_set_assoc(req, assoc, assoclen);
+       aead_request_set_crypt(req, &src, &src, datalen, iv);
+
+       rc = crypto_aead_decrypt(req);
+
+       kfree(req);
+       skb_trim(skb, skb->len - authlen);
+
+       return rc;
+}
+
+static int
+llsec_do_decrypt(struct sk_buff *skb, const struct mac802154_llsec *sec,
+                const struct ieee802154_hdr *hdr,
+                struct mac802154_llsec_key *key, __le64 dev_addr)
+{
+       if (hdr->sec.level == IEEE802154_SCF_SECLEVEL_ENC)
+               return llsec_do_decrypt_unauth(skb, sec, hdr, key, dev_addr);
+       else
+               return llsec_do_decrypt_auth(skb, sec, hdr, key, dev_addr);
+}
+
+static int
+llsec_update_devkey_record(struct mac802154_llsec_device *dev,
+                          const struct ieee802154_llsec_key_id *in_key)
+{
+       struct mac802154_llsec_device_key *devkey;
+
+       devkey = llsec_devkey_find(dev, in_key);
+
+       if (!devkey) {
+               struct mac802154_llsec_device_key *next;
+
+               next = kzalloc(sizeof(*devkey), GFP_ATOMIC);
+               if (!next)
+                       return -ENOMEM;
+
+               next->devkey.key_id = *in_key;
+
+               spin_lock_bh(&dev->lock);
+
+               devkey = llsec_devkey_find(dev, in_key);
+               if (!devkey)
+                       list_add_rcu(&next->devkey.list, &dev->dev.keys);
+               else
+                       kfree(next);
+
+               spin_unlock_bh(&dev->lock);
+       }
+
+       return 0;
+}
+
+static int
+llsec_update_devkey_info(struct mac802154_llsec_device *dev,
+                        const struct ieee802154_llsec_key_id *in_key,
+                        u32 frame_counter)
+{
+       struct mac802154_llsec_device_key *devkey = NULL;
+
+       if (dev->dev.key_mode == IEEE802154_LLSEC_DEVKEY_RESTRICT) {
+               devkey = llsec_devkey_find(dev, in_key);
+               if (!devkey)
+                       return -ENOENT;
+       }
+
+       if (dev->dev.key_mode == IEEE802154_LLSEC_DEVKEY_RECORD) {
+               int rc = llsec_update_devkey_record(dev, in_key);
+
+               if (rc < 0)
+                       return rc;
+       }
+
+       spin_lock_bh(&dev->lock);
+
+       if ((!devkey && frame_counter < dev->dev.frame_counter) ||
+           (devkey && frame_counter < devkey->devkey.frame_counter)) {
+               spin_unlock_bh(&dev->lock);
+               return -EINVAL;
+       }
+
+       if (devkey)
+               devkey->devkey.frame_counter = frame_counter + 1;
+       else
+               dev->dev.frame_counter = frame_counter + 1;
+
+       spin_unlock_bh(&dev->lock);
+
+       return 0;
+}
+
+int mac802154_llsec_decrypt(struct mac802154_llsec *sec, struct sk_buff *skb)
+{
+       struct ieee802154_hdr hdr;
+       struct mac802154_llsec_key *key;
+       struct ieee802154_llsec_key_id key_id;
+       struct mac802154_llsec_device *dev;
+       struct ieee802154_llsec_seclevel seclevel;
+       int err;
+       __le64 dev_addr;
+       u32 frame_ctr;
+
+       if (ieee802154_hdr_peek(skb, &hdr) < 0)
+               return -EINVAL;
+       if (!hdr.fc.security_enabled)
+               return 0;
+       if (hdr.fc.version == 0)
+               return -EINVAL;
+
+       read_lock_bh(&sec->lock);
+       if (!sec->params.enabled) {
+               read_unlock_bh(&sec->lock);
+               return -EINVAL;
+       }
+       read_unlock_bh(&sec->lock);
+
+       rcu_read_lock();
+
+       key = llsec_lookup_key(sec, &hdr, &hdr.source, &key_id);
+       if (!key) {
+               err = -ENOKEY;
+               goto fail;
+       }
+
+       dev = llsec_lookup_dev(sec, &hdr.source);
+       if (!dev) {
+               err = -EINVAL;
+               goto fail_dev;
+       }
+
+       if (llsec_lookup_seclevel(sec, hdr.fc.type, 0, &seclevel) < 0) {
+               err = -EINVAL;
+               goto fail_dev;
+       }
+
+       if (!(seclevel.sec_levels & BIT(hdr.sec.level)) &&
+           (hdr.sec.level == 0 && seclevel.device_override &&
+            !dev->dev.seclevel_exempt)) {
+               err = -EINVAL;
+               goto fail_dev;
+       }
+
+       frame_ctr = le32_to_cpu(hdr.sec.frame_counter);
+
+       if (frame_ctr == 0xffffffff) {
+               err = -EOVERFLOW;
+               goto fail_dev;
+       }
+
+       err = llsec_update_devkey_info(dev, &key_id, frame_ctr);
+       if (err)
+               goto fail_dev;
+
+       dev_addr = dev->dev.hwaddr;
+
+       rcu_read_unlock();
+
+       err = llsec_do_decrypt(skb, sec, &hdr, key, dev_addr);
+       llsec_key_put(key);
+       return err;
+
+fail_dev:
+       llsec_key_put(key);
+fail:
+       rcu_read_unlock();
+       return err;
+}
diff --git a/net/mac802154/llsec.h b/net/mac802154/llsec.h
new file mode 100644 (file)
index 0000000..950578e
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2014 Fraunhofer ITWM
+ *
+ * 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 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.
+ *
+ * Written by:
+ * Phoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de>
+ */
+
+#ifndef MAC802154_LLSEC_H
+#define MAC802154_LLSEC_H
+
+#include <linux/slab.h>
+#include <linux/hashtable.h>
+#include <linux/crypto.h>
+#include <linux/kref.h>
+#include <linux/spinlock.h>
+#include <net/af_ieee802154.h>
+#include <net/ieee802154_netdev.h>
+
+struct mac802154_llsec_key {
+       struct ieee802154_llsec_key key;
+
+       /* one tfm for each authsize (4/8/16) */
+       struct crypto_aead *tfm[3];
+       struct crypto_blkcipher *tfm0;
+
+       struct kref ref;
+};
+
+struct mac802154_llsec_device_key {
+       struct ieee802154_llsec_device_key devkey;
+
+       struct rcu_head rcu;
+};
+
+struct mac802154_llsec_device {
+       struct ieee802154_llsec_device dev;
+
+       struct hlist_node bucket_s;
+       struct hlist_node bucket_hw;
+
+       /* protects dev.frame_counter and the elements of dev.keys */
+       spinlock_t lock;
+
+       struct rcu_head rcu;
+};
+
+struct mac802154_llsec_seclevel {
+       struct ieee802154_llsec_seclevel level;
+
+       struct rcu_head rcu;
+};
+
+struct mac802154_llsec {
+       struct ieee802154_llsec_params params;
+       struct ieee802154_llsec_table table;
+
+       DECLARE_HASHTABLE(devices_short, 6);
+       DECLARE_HASHTABLE(devices_hw, 6);
+
+       /* protects params, all other fields are fine with RCU */
+       rwlock_t lock;
+};
+
+void mac802154_llsec_init(struct mac802154_llsec *sec);
+void mac802154_llsec_destroy(struct mac802154_llsec *sec);
+
+int mac802154_llsec_get_params(struct mac802154_llsec *sec,
+                              struct ieee802154_llsec_params *params);
+int mac802154_llsec_set_params(struct mac802154_llsec *sec,
+                              const struct ieee802154_llsec_params *params,
+                              int changed);
+
+int mac802154_llsec_key_add(struct mac802154_llsec *sec,
+                           const struct ieee802154_llsec_key_id *id,
+                           const struct ieee802154_llsec_key *key);
+int mac802154_llsec_key_del(struct mac802154_llsec *sec,
+                           const struct ieee802154_llsec_key_id *key);
+
+int mac802154_llsec_dev_add(struct mac802154_llsec *sec,
+                           const struct ieee802154_llsec_device *dev);
+int mac802154_llsec_dev_del(struct mac802154_llsec *sec,
+                           __le64 device_addr);
+
+int mac802154_llsec_devkey_add(struct mac802154_llsec *sec,
+                              __le64 dev_addr,
+                              const struct ieee802154_llsec_device_key *key);
+int mac802154_llsec_devkey_del(struct mac802154_llsec *sec,
+                              __le64 dev_addr,
+                              const struct ieee802154_llsec_device_key *key);
+
+int mac802154_llsec_seclevel_add(struct mac802154_llsec *sec,
+                                const struct ieee802154_llsec_seclevel *sl);
+int mac802154_llsec_seclevel_del(struct mac802154_llsec *sec,
+                                const struct ieee802154_llsec_seclevel *sl);
+
+int mac802154_llsec_encrypt(struct mac802154_llsec *sec, struct sk_buff *skb);
+int mac802154_llsec_decrypt(struct mac802154_llsec *sec, struct sk_buff *skb);
+
+#endif /* MAC802154_LLSEC_H */
index 28ef59c566e6ee78df73edd1246d84d4c2c4d912..762a6f849c6b7d3edf3e8677ee6f2081c10c8fac 100644 (file)
 #ifndef MAC802154_H
 #define MAC802154_H
 
+#include <linux/mutex.h>
+#include <net/mac802154.h>
 #include <net/ieee802154_netdev.h>
 
+#include "llsec.h"
+
 /* mac802154 device private data */
 struct mac802154_priv {
        struct ieee802154_dev hw;
@@ -90,6 +94,13 @@ struct mac802154_sub_if_data {
        u8 bsn;
        /* MAC DSN field */
        u8 dsn;
+
+       /* protects sec from concurrent access by netlink. access by
+        * encrypt/decrypt/header_create safe without additional protection.
+        */
+       struct mutex sec_mtx;
+
+       struct mac802154_llsec sec;
 };
 
 #define mac802154_to_priv(_hw) container_of(_hw, struct mac802154_priv, hw)
@@ -125,4 +136,37 @@ int mac802154_set_mac_params(struct net_device *dev,
 void mac802154_get_mac_params(struct net_device *dev,
                              struct ieee802154_mac_params *params);
 
+int mac802154_get_params(struct net_device *dev,
+                        struct ieee802154_llsec_params *params);
+int mac802154_set_params(struct net_device *dev,
+                        const struct ieee802154_llsec_params *params,
+                        int changed);
+
+int mac802154_add_key(struct net_device *dev,
+                     const struct ieee802154_llsec_key_id *id,
+                     const struct ieee802154_llsec_key *key);
+int mac802154_del_key(struct net_device *dev,
+                     const struct ieee802154_llsec_key_id *id);
+
+int mac802154_add_dev(struct net_device *dev,
+                     const struct ieee802154_llsec_device *llsec_dev);
+int mac802154_del_dev(struct net_device *dev, __le64 dev_addr);
+
+int mac802154_add_devkey(struct net_device *dev,
+                        __le64 device_addr,
+                        const struct ieee802154_llsec_device_key *key);
+int mac802154_del_devkey(struct net_device *dev,
+                        __le64 device_addr,
+                        const struct ieee802154_llsec_device_key *key);
+
+int mac802154_add_seclevel(struct net_device *dev,
+                          const struct ieee802154_llsec_seclevel *sl);
+int mac802154_del_seclevel(struct net_device *dev,
+                          const struct ieee802154_llsec_seclevel *sl);
+
+void mac802154_lock_table(struct net_device *dev);
+void mac802154_get_table(struct net_device *dev,
+                        struct ieee802154_llsec_table **t);
+void mac802154_unlock_table(struct net_device *dev);
+
 #endif /* MAC802154_H */
index d40c0928bc622d5802c9dcd1bdb740150ee06744..bf809131eef776209040a8496ed1e2214e6bdebe 100644 (file)
@@ -40,6 +40,9 @@ static int mac802154_mlme_start_req(struct net_device *dev,
                                    u8 pan_coord, u8 blx,
                                    u8 coord_realign)
 {
+       struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+       int rc = 0;
+
        BUG_ON(addr->mode != IEEE802154_ADDR_SHORT);
 
        mac802154_dev_set_pan_id(dev, addr->pan_id);
@@ -47,12 +50,31 @@ static int mac802154_mlme_start_req(struct net_device *dev,
        mac802154_dev_set_ieee_addr(dev);
        mac802154_dev_set_page_channel(dev, page, channel);
 
+       if (ops->llsec) {
+               struct ieee802154_llsec_params params;
+               int changed = 0;
+
+               params.coord_shortaddr = addr->short_addr;
+               changed |= IEEE802154_LLSEC_PARAM_COORD_SHORTADDR;
+
+               params.pan_id = addr->pan_id;
+               changed |= IEEE802154_LLSEC_PARAM_PAN_ID;
+
+               params.hwaddr = ieee802154_devaddr_from_raw(dev->dev_addr);
+               changed |= IEEE802154_LLSEC_PARAM_HWADDR;
+
+               params.coord_hwaddr = params.hwaddr;
+               changed |= IEEE802154_LLSEC_PARAM_COORD_HWADDR;
+
+               rc = ops->llsec->set_params(dev, &params, changed);
+       }
+
        /* FIXME: add validation for unused parameters to be sane
         * for SoftMAC
         */
        ieee802154_nl_start_confirm(dev, IEEE802154_SUCCESS);
 
-       return 0;
+       return rc;
 }
 
 static struct wpan_phy *mac802154_get_phy(const struct net_device *dev)
@@ -64,6 +86,22 @@ static struct wpan_phy *mac802154_get_phy(const struct net_device *dev)
        return to_phy(get_device(&priv->hw->phy->dev));
 }
 
+static struct ieee802154_llsec_ops mac802154_llsec_ops = {
+       .get_params = mac802154_get_params,
+       .set_params = mac802154_set_params,
+       .add_key = mac802154_add_key,
+       .del_key = mac802154_del_key,
+       .add_dev = mac802154_add_dev,
+       .del_dev = mac802154_del_dev,
+       .add_devkey = mac802154_add_devkey,
+       .del_devkey = mac802154_del_devkey,
+       .add_seclevel = mac802154_add_seclevel,
+       .del_seclevel = mac802154_del_seclevel,
+       .lock_table = mac802154_lock_table,
+       .get_table = mac802154_get_table,
+       .unlock_table = mac802154_unlock_table,
+};
+
 struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced = {
        .get_phy = mac802154_get_phy,
 };
@@ -75,6 +113,8 @@ struct ieee802154_mlme_ops mac802154_mlme_wpan = {
        .get_short_addr = mac802154_dev_get_short_addr,
        .get_dsn = mac802154_dev_get_dsn,
 
+       .llsec = &mac802154_llsec_ops,
+
        .set_mac_params = mac802154_set_mac_params,
        .get_mac_params = mac802154_get_mac_params,
 };
index f0991f2344d403f3b1ed3a1f44fc2aebf50c72b4..15aa2f2b03a78c29138db43c08073c4ba2817e54 100644 (file)
@@ -213,3 +213,190 @@ void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan)
        } else
                mutex_unlock(&priv->hw->phy->pib_lock);
 }
+
+
+int mac802154_get_params(struct net_device *dev,
+                        struct ieee802154_llsec_params *params)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       int res;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_lock(&priv->sec_mtx);
+       res = mac802154_llsec_get_params(&priv->sec, params);
+       mutex_unlock(&priv->sec_mtx);
+
+       return res;
+}
+
+int mac802154_set_params(struct net_device *dev,
+                        const struct ieee802154_llsec_params *params,
+                        int changed)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       int res;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_lock(&priv->sec_mtx);
+       res = mac802154_llsec_set_params(&priv->sec, params, changed);
+       mutex_unlock(&priv->sec_mtx);
+
+       return res;
+}
+
+
+int mac802154_add_key(struct net_device *dev,
+                     const struct ieee802154_llsec_key_id *id,
+                     const struct ieee802154_llsec_key *key)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       int res;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_lock(&priv->sec_mtx);
+       res = mac802154_llsec_key_add(&priv->sec, id, key);
+       mutex_unlock(&priv->sec_mtx);
+
+       return res;
+}
+
+int mac802154_del_key(struct net_device *dev,
+                     const struct ieee802154_llsec_key_id *id)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       int res;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_lock(&priv->sec_mtx);
+       res = mac802154_llsec_key_del(&priv->sec, id);
+       mutex_unlock(&priv->sec_mtx);
+
+       return res;
+}
+
+
+int mac802154_add_dev(struct net_device *dev,
+                     const struct ieee802154_llsec_device *llsec_dev)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       int res;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_lock(&priv->sec_mtx);
+       res = mac802154_llsec_dev_add(&priv->sec, llsec_dev);
+       mutex_unlock(&priv->sec_mtx);
+
+       return res;
+}
+
+int mac802154_del_dev(struct net_device *dev, __le64 dev_addr)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       int res;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_lock(&priv->sec_mtx);
+       res = mac802154_llsec_dev_del(&priv->sec, dev_addr);
+       mutex_unlock(&priv->sec_mtx);
+
+       return res;
+}
+
+
+int mac802154_add_devkey(struct net_device *dev,
+                        __le64 device_addr,
+                        const struct ieee802154_llsec_device_key *key)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       int res;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_lock(&priv->sec_mtx);
+       res = mac802154_llsec_devkey_add(&priv->sec, device_addr, key);
+       mutex_unlock(&priv->sec_mtx);
+
+       return res;
+}
+
+int mac802154_del_devkey(struct net_device *dev,
+                        __le64 device_addr,
+                        const struct ieee802154_llsec_device_key *key)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       int res;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_lock(&priv->sec_mtx);
+       res = mac802154_llsec_devkey_del(&priv->sec, device_addr, key);
+       mutex_unlock(&priv->sec_mtx);
+
+       return res;
+}
+
+
+int mac802154_add_seclevel(struct net_device *dev,
+                          const struct ieee802154_llsec_seclevel *sl)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       int res;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_lock(&priv->sec_mtx);
+       res = mac802154_llsec_seclevel_add(&priv->sec, sl);
+       mutex_unlock(&priv->sec_mtx);
+
+       return res;
+}
+
+int mac802154_del_seclevel(struct net_device *dev,
+                          const struct ieee802154_llsec_seclevel *sl)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       int res;
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_lock(&priv->sec_mtx);
+       res = mac802154_llsec_seclevel_del(&priv->sec, sl);
+       mutex_unlock(&priv->sec_mtx);
+
+       return res;
+}
+
+
+void mac802154_lock_table(struct net_device *dev)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_lock(&priv->sec_mtx);
+}
+
+void mac802154_get_table(struct net_device *dev,
+                        struct ieee802154_llsec_table **t)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       *t = &priv->sec.table;
+}
+
+void mac802154_unlock_table(struct net_device *dev)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+
+       BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+       mutex_unlock(&priv->sec_mtx);
+}
index 434a26f76a80f69c058d9c2a61181bede5333147..a68230e2b25f53b14907dac5bfd12d5cb09d1cab 100644 (file)
@@ -70,7 +70,8 @@ void mac802154_monitors_rx(struct mac802154_priv *priv, struct sk_buff *skb)
 
        rcu_read_lock();
        list_for_each_entry_rcu(sdata, &priv->slaves, list) {
-               if (sdata->type != IEEE802154_DEV_MONITOR)
+               if (sdata->type != IEEE802154_DEV_MONITOR ||
+                   !netif_running(sdata->dev))
                        continue;
 
                skb2 = skb_clone(skb, GFP_ATOMIC);
index 03855b0677ccf8efcb0819591bae63bd8609c693..7f820a108a9cf3461a46a57522251ea4edc51003 100644 (file)
@@ -59,27 +59,28 @@ mac802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb, u8 lqi)
        skb->protocol = htons(ETH_P_IEEE802154);
        skb_reset_mac_header(skb);
 
-       BUILD_BUG_ON(sizeof(struct ieee802154_mac_cb) > sizeof(skb->cb));
-
        if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) {
                u16 crc;
 
                if (skb->len < 2) {
                        pr_debug("got invalid frame\n");
-                       goto out;
+                       goto fail;
                }
                crc = crc_ccitt(0, skb->data, skb->len);
                if (crc) {
                        pr_debug("CRC mismatch\n");
-                       goto out;
+                       goto fail;
                }
                skb_trim(skb, skb->len - 2); /* CRC */
        }
 
        mac802154_monitors_rx(priv, skb);
        mac802154_wpans_rx(priv, skb);
-out:
-       dev_kfree_skb(skb);
+
+       return;
+
+fail:
+       kfree_skb(skb);
 }
 
 static void mac802154_rx_worker(struct work_struct *work)
index 1df7a6a573865b4add87261300cc1d7ff01fa243..3c3069fd69718277fc719e82d7bf2c51bb9747db 100644 (file)
 
 #include "mac802154.h"
 
+static int mac802154_wpan_update_llsec(struct net_device *dev)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+       int rc = 0;
+
+       if (ops->llsec) {
+               struct ieee802154_llsec_params params;
+               int changed = 0;
+
+               params.pan_id = priv->pan_id;
+               changed |= IEEE802154_LLSEC_PARAM_PAN_ID;
+
+               params.hwaddr = priv->extended_addr;
+               changed |= IEEE802154_LLSEC_PARAM_HWADDR;
+
+               rc = ops->llsec->set_params(dev, &params, changed);
+       }
+
+       return rc;
+}
+
 static int
 mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
@@ -81,7 +103,7 @@ mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
                priv->pan_id = cpu_to_le16(sa->addr.pan_id);
                priv->short_addr = cpu_to_le16(sa->addr.short_addr);
 
-               err = 0;
+               err = mac802154_wpan_update_llsec(dev);
                break;
        }
 
@@ -99,7 +121,7 @@ static int mac802154_wpan_mac_addr(struct net_device *dev, void *p)
        /* FIXME: validate addr */
        memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
        mac802154_dev_set_ieee_addr(dev);
-       return 0;
+       return mac802154_wpan_update_llsec(dev);
 }
 
 int mac802154_set_mac_params(struct net_device *dev,
@@ -124,7 +146,7 @@ void mac802154_get_mac_params(struct net_device *dev,
        mutex_unlock(&priv->hw->slaves_mtx);
 }
 
-int mac802154_wpan_open(struct net_device *dev)
+static int mac802154_wpan_open(struct net_device *dev)
 {
        int rc;
        struct mac802154_sub_if_data *priv = netdev_priv(dev);
@@ -183,6 +205,38 @@ int mac802154_wpan_open(struct net_device *dev)
        return rc;
 }
 
+static int mac802154_set_header_security(struct mac802154_sub_if_data *priv,
+                                        struct ieee802154_hdr *hdr,
+                                        const struct ieee802154_mac_cb *cb)
+{
+       struct ieee802154_llsec_params params;
+       u8 level;
+
+       mac802154_llsec_get_params(&priv->sec, &params);
+
+       if (!params.enabled && cb->secen_override && cb->secen)
+               return -EINVAL;
+       if (!params.enabled ||
+           (cb->secen_override && !cb->secen) ||
+           !params.out_level)
+               return 0;
+       if (cb->seclevel_override && !cb->seclevel)
+               return -EINVAL;
+
+       level = cb->seclevel_override ? cb->seclevel : params.out_level;
+
+       hdr->fc.security_enabled = 1;
+       hdr->sec.level = level;
+       hdr->sec.key_id_mode = params.out_key.mode;
+       if (params.out_key.mode == IEEE802154_SCF_KEY_SHORT_INDEX)
+               hdr->sec.short_src = params.out_key.short_source;
+       else if (params.out_key.mode == IEEE802154_SCF_KEY_HW_INDEX)
+               hdr->sec.extended_src = params.out_key.extended_source;
+       hdr->sec.key_id = params.out_key.id;
+
+       return 0;
+}
+
 static int mac802154_header_create(struct sk_buff *skb,
                                   struct net_device *dev,
                                   unsigned short type,
@@ -192,15 +246,20 @@ static int mac802154_header_create(struct sk_buff *skb,
 {
        struct ieee802154_hdr hdr;
        struct mac802154_sub_if_data *priv = netdev_priv(dev);
+       struct ieee802154_mac_cb *cb = mac_cb(skb);
        int hlen;
 
        if (!daddr)
                return -EINVAL;
 
        memset(&hdr.fc, 0, sizeof(hdr.fc));
-       hdr.fc.type = mac_cb_type(skb);
-       hdr.fc.security_enabled = mac_cb_is_secen(skb);
-       hdr.fc.ack_request = mac_cb_is_ackreq(skb);
+       hdr.fc.type = cb->type;
+       hdr.fc.security_enabled = cb->secen;
+       hdr.fc.ack_request = cb->ackreq;
+       hdr.seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
+
+       if (mac802154_set_header_security(priv, &hdr, cb) < 0)
+               return -EINVAL;
 
        if (!saddr) {
                spin_lock_bh(&priv->mib_lock);
@@ -231,7 +290,7 @@ static int mac802154_header_create(struct sk_buff *skb,
        skb_reset_mac_header(skb);
        skb->mac_len = hlen;
 
-       if (hlen + len + 2 > dev->mtu)
+       if (len > ieee802154_max_payload(&hdr))
                return -EMSGSIZE;
 
        return hlen;
@@ -257,6 +316,7 @@ mac802154_wpan_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct mac802154_sub_if_data *priv;
        u8 chan, page;
+       int rc;
 
        priv = netdev_priv(dev);
 
@@ -272,6 +332,13 @@ mac802154_wpan_xmit(struct sk_buff *skb, struct net_device *dev)
                return NETDEV_TX_OK;
        }
 
+       rc = mac802154_llsec_encrypt(&priv->sec, skb);
+       if (rc) {
+               pr_warn("encryption failed: %i\n", rc);
+               kfree_skb(skb);
+               return NETDEV_TX_OK;
+       }
+
        skb->skb_iif = dev->ifindex;
        dev->stats.tx_packets++;
        dev->stats.tx_bytes += skb->len;
@@ -292,6 +359,15 @@ static const struct net_device_ops mac802154_wpan_ops = {
        .ndo_set_mac_address    = mac802154_wpan_mac_addr,
 };
 
+static void mac802154_wpan_free(struct net_device *dev)
+{
+       struct mac802154_sub_if_data *priv = netdev_priv(dev);
+
+       mac802154_llsec_destroy(&priv->sec);
+
+       free_netdev(dev);
+}
+
 void mac802154_wpan_setup(struct net_device *dev)
 {
        struct mac802154_sub_if_data *priv;
@@ -301,14 +377,14 @@ void mac802154_wpan_setup(struct net_device *dev)
 
        dev->hard_header_len    = MAC802154_FRAME_HARD_HEADER_LEN;
        dev->header_ops         = &mac802154_header_ops;
-       dev->needed_tailroom    = 2; /* FCS */
+       dev->needed_tailroom    = 2 + 16; /* FCS + MIC */
        dev->mtu                = IEEE802154_MTU;
        dev->tx_queue_len       = 300;
        dev->type               = ARPHRD_IEEE802154;
        dev->flags              = IFF_NOARP | IFF_BROADCAST;
        dev->watchdog_timeo     = 0;
 
-       dev->destructor         = free_netdev;
+       dev->destructor         = mac802154_wpan_free;
        dev->netdev_ops         = &mac802154_wpan_ops;
        dev->ml_priv            = &mac802154_mlme_wpan;
 
@@ -319,6 +395,7 @@ void mac802154_wpan_setup(struct net_device *dev)
        priv->page = 0;
 
        spin_lock_init(&priv->mib_lock);
+       mutex_init(&priv->sec_mtx);
 
        get_random_bytes(&priv->bsn, 1);
        get_random_bytes(&priv->dsn, 1);
@@ -331,6 +408,8 @@ void mac802154_wpan_setup(struct net_device *dev)
 
        priv->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
        priv->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
+
+       mac802154_llsec_init(&priv->sec);
 }
 
 static int mac802154_process_data(struct net_device *dev, struct sk_buff *skb)
@@ -339,9 +418,11 @@ static int mac802154_process_data(struct net_device *dev, struct sk_buff *skb)
 }
 
 static int
-mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb)
+mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb,
+                     const struct ieee802154_hdr *hdr)
 {
        __le16 span, sshort;
+       int rc;
 
        pr_debug("getting packet via slave interface %s\n", sdata->dev->name);
 
@@ -388,15 +469,22 @@ mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb)
 
        skb->dev = sdata->dev;
 
+       rc = mac802154_llsec_decrypt(&sdata->sec, skb);
+       if (rc) {
+               pr_debug("decryption failed: %i\n", rc);
+               kfree_skb(skb);
+               return NET_RX_DROP;
+       }
+
        sdata->dev->stats.rx_packets++;
        sdata->dev->stats.rx_bytes += skb->len;
 
-       switch (mac_cb_type(skb)) {
+       switch (mac_cb(skb)->type) {
        case IEEE802154_FC_TYPE_DATA:
                return mac802154_process_data(sdata->dev, skb);
        default:
                pr_warn("ieee802154: bad frame received (type = %d)\n",
-                       mac_cb_type(skb));
+                       mac_cb(skb)->type);
                kfree_skb(skb);
                return NET_RX_DROP;
        }
@@ -419,62 +507,58 @@ static void mac802154_print_addr(const char *name,
        }
 }
 
-static int mac802154_parse_frame_start(struct sk_buff *skb)
+static int mac802154_parse_frame_start(struct sk_buff *skb,
+                                      struct ieee802154_hdr *hdr)
 {
        int hlen;
-       struct ieee802154_hdr hdr;
+       struct ieee802154_mac_cb *cb = mac_cb_init(skb);
 
-       hlen = ieee802154_hdr_pull(skb, &hdr);
+       hlen = ieee802154_hdr_pull(skb, hdr);
        if (hlen < 0)
                return -EINVAL;
 
        skb->mac_len = hlen;
 
-       pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr.fc),
-                hdr.seq);
-
-       mac_cb(skb)->flags = hdr.fc.type;
+       pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr->fc),
+                hdr->seq);
 
-       if (hdr.fc.ack_request)
-               mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ;
-       if (hdr.fc.security_enabled)
-               mac_cb(skb)->flags |= MAC_CB_FLAG_SECEN;
+       cb->type = hdr->fc.type;
+       cb->ackreq = hdr->fc.ack_request;
+       cb->secen = hdr->fc.security_enabled;
 
-       mac802154_print_addr("destination", &hdr.dest);
-       mac802154_print_addr("source", &hdr.source);
+       mac802154_print_addr("destination", &hdr->dest);
+       mac802154_print_addr("source", &hdr->source);
 
-       mac_cb(skb)->source = hdr.source;
-       mac_cb(skb)->dest = hdr.dest;
+       cb->source = hdr->source;
+       cb->dest = hdr->dest;
 
-       if (hdr.fc.security_enabled) {
+       if (hdr->fc.security_enabled) {
                u64 key;
 
-               pr_debug("seclevel %i\n", hdr.sec.level);
+               pr_debug("seclevel %i\n", hdr->sec.level);
 
-               switch (hdr.sec.key_id_mode) {
+               switch (hdr->sec.key_id_mode) {
                case IEEE802154_SCF_KEY_IMPLICIT:
                        pr_debug("implicit key\n");
                        break;
 
                case IEEE802154_SCF_KEY_INDEX:
-                       pr_debug("key %02x\n", hdr.sec.key_id);
+                       pr_debug("key %02x\n", hdr->sec.key_id);
                        break;
 
                case IEEE802154_SCF_KEY_SHORT_INDEX:
                        pr_debug("key %04x:%04x %02x\n",
-                                le32_to_cpu(hdr.sec.short_src) >> 16,
-                                le32_to_cpu(hdr.sec.short_src) & 0xffff,
-                                hdr.sec.key_id);
+                                le32_to_cpu(hdr->sec.short_src) >> 16,
+                                le32_to_cpu(hdr->sec.short_src) & 0xffff,
+                                hdr->sec.key_id);
                        break;
 
                case IEEE802154_SCF_KEY_HW_INDEX:
-                       key = swab64((__force u64) hdr.sec.extended_src);
+                       key = swab64((__force u64) hdr->sec.extended_src);
                        pr_debug("key source %8phC %02x\n", &key,
-                                hdr.sec.key_id);
+                                hdr->sec.key_id);
                        break;
                }
-
-               return -EINVAL;
        }
 
        return 0;
@@ -483,10 +567,10 @@ static int mac802154_parse_frame_start(struct sk_buff *skb)
 void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb)
 {
        int ret;
-       struct sk_buff *sskb;
        struct mac802154_sub_if_data *sdata;
+       struct ieee802154_hdr hdr;
 
-       ret = mac802154_parse_frame_start(skb);
+       ret = mac802154_parse_frame_start(skb, &hdr);
        if (ret) {
                pr_debug("got invalid frame\n");
                return;
@@ -494,12 +578,16 @@ void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb)
 
        rcu_read_lock();
        list_for_each_entry_rcu(sdata, &priv->slaves, list) {
-               if (sdata->type != IEEE802154_DEV_WPAN)
+               if (sdata->type != IEEE802154_DEV_WPAN ||
+                   !netif_running(sdata->dev))
                        continue;
 
-               sskb = skb_clone(skb, GFP_ATOMIC);
-               if (sskb)
-                       mac802154_subif_frame(sdata, sskb);
+               mac802154_subif_frame(sdata, skb, &hdr);
+               skb = NULL;
+               break;
        }
        rcu_read_unlock();
+
+       if (skb)
+               kfree_skb(skb);
 }
index 851cd880b0c048c4dcd45e1b6652888338490cf9..6b38d083e1c973c55e18914748d49fe4968a7188 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_GRE_CSUM |
                                  SKB_GSO_IPIP |
                                  SKB_GSO_MPLS)))
                goto out;
index 117208321f16997af9f24bee1f86519f8f8fa26e..ec8114fae50b297f56760c3ac22d378c31957cd4 100644 (file)
@@ -271,10 +271,7 @@ ip_set_free(void *members)
 {
        pr_debug("%p: free with %s\n", members,
                 is_vmalloc_addr(members) ? "vfree" : "kfree");
-       if (is_vmalloc_addr(members))
-               vfree(members);
-       else
-               kfree(members);
+       kvfree(members);
 }
 EXPORT_SYMBOL_GPL(ip_set_free);
 
index 3d2d2c8108cac8d681e1e0840c89eb2312fee6d6..e6836755c45d4168bb6ccc9ab6f12708e9e17f3d 100644 (file)
@@ -97,7 +97,7 @@ const char *ip_vs_proto_name(unsigned int proto)
                return "ICMPv6";
 #endif
        default:
-               sprintf(buf, "IP_%d", proto);
+               sprintf(buf, "IP_%u", proto);
                return buf;
        }
 }
index c47444e4cf8ccc9977fa0622689b4fb55799ff4b..73ba1cc7a88dd6a1126a0aaae3525ee2ce1f0fda 100644 (file)
@@ -562,7 +562,7 @@ ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
        ip_send_check(iph);
 
        /* Another hack: avoid icmp_send in ip_fragment */
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        ip_vs_send_or_cont(NFPROTO_IPV4, skb, cp, 0);
        rcu_read_unlock();
@@ -590,7 +590,7 @@ ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
                goto tx_error;
 
        /* Another hack: avoid icmp_send in ip_fragment */
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        ip_vs_send_or_cont(NFPROTO_IPV6, skb, cp, 0);
        rcu_read_unlock();
@@ -684,7 +684,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
           MTU problem. */
 
        /* Another hack: avoid icmp_send in ip_fragment */
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        rc = ip_vs_nat_send_or_cont(NFPROTO_IPV4, skb, cp, local);
        rcu_read_unlock();
@@ -774,7 +774,7 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
           MTU problem. */
 
        /* Another hack: avoid icmp_send in ip_fragment */
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        rc = ip_vs_nat_send_or_cont(NFPROTO_IPV6, skb, cp, local);
        rcu_read_unlock();
@@ -883,10 +883,10 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
        iph->daddr              =       cp->daddr.ip;
        iph->saddr              =       saddr;
        iph->ttl                =       old_iph->ttl;
-       ip_select_ident(skb, &rt->dst, NULL);
+       ip_select_ident(skb, NULL);
 
        /* Another hack: avoid icmp_send in ip_fragment */
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        ret = ip_vs_tunnel_xmit_prepare(skb, cp);
        if (ret == NF_ACCEPT)
@@ -974,7 +974,7 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
        iph->hop_limit          =       old_iph->hop_limit;
 
        /* Another hack: avoid icmp_send in ip_fragment */
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        ret = ip_vs_tunnel_xmit_prepare(skb, cp);
        if (ret == NF_ACCEPT)
@@ -1023,7 +1023,7 @@ ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
        ip_send_check(ip_hdr(skb));
 
        /* Another hack: avoid icmp_send in ip_fragment */
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        ip_vs_send_or_cont(NFPROTO_IPV4, skb, cp, 0);
        rcu_read_unlock();
@@ -1060,7 +1060,7 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
        }
 
        /* Another hack: avoid icmp_send in ip_fragment */
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        ip_vs_send_or_cont(NFPROTO_IPV6, skb, cp, 0);
        rcu_read_unlock();
@@ -1157,7 +1157,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
        ip_vs_nat_icmp(skb, pp, cp, 0);
 
        /* Another hack: avoid icmp_send in ip_fragment */
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        rc = ip_vs_nat_send_or_cont(NFPROTO_IPV4, skb, cp, local);
        rcu_read_unlock();
@@ -1249,7 +1249,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
        ip_vs_nat_icmp_v6(skb, pp, cp, 0);
 
        /* Another hack: avoid icmp_send in ip_fragment */
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        rc = ip_vs_nat_send_or_cont(NFPROTO_IPV6, skb, cp, local);
        rcu_read_unlock();
index 52ca952b802c5e3ea41c83823b90f74365b8fc76..09096a670c45b6da72fcef70646f58139ce2af39 100644 (file)
@@ -358,6 +358,19 @@ get_unique_tuple(struct nf_conntrack_tuple *tuple,
        rcu_read_unlock();
 }
 
+struct nf_conn_nat *nf_ct_nat_ext_add(struct nf_conn *ct)
+{
+       struct nf_conn_nat *nat = nfct_nat(ct);
+       if (nat)
+               return nat;
+
+       if (!nf_ct_is_confirmed(ct))
+               nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
+
+       return nat;
+}
+EXPORT_SYMBOL_GPL(nf_ct_nat_ext_add);
+
 unsigned int
 nf_nat_setup_info(struct nf_conn *ct,
                  const struct nf_nat_range *range,
@@ -368,14 +381,9 @@ nf_nat_setup_info(struct nf_conn *ct,
        struct nf_conn_nat *nat;
 
        /* nat helper or nfctnetlink also setup binding */
-       nat = nfct_nat(ct);
-       if (!nat) {
-               nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
-               if (nat == NULL) {
-                       pr_debug("failed to add NAT extension\n");
-                       return NF_ACCEPT;
-               }
-       }
+       nat = nf_ct_nat_ext_add(ct);
+       if (nat == NULL)
+               return NF_ACCEPT;
 
        NF_CT_ASSERT(maniptype == NF_NAT_MANIP_SRC ||
                     maniptype == NF_NAT_MANIP_DST);
index 3fd159db9f06bc31d3da81d8cb3e5e8f2bb3ebc2..624e083125b93b8755819f868c90307f04cd74b8 100644 (file)
@@ -88,6 +88,45 @@ nf_tables_afinfo_lookup(struct net *net, int family, bool autoload)
        return ERR_PTR(-EAFNOSUPPORT);
 }
 
+static void nft_ctx_init(struct nft_ctx *ctx,
+                        const struct sk_buff *skb,
+                        const struct nlmsghdr *nlh,
+                        struct nft_af_info *afi,
+                        struct nft_table *table,
+                        struct nft_chain *chain,
+                        const struct nlattr * const *nla)
+{
+       ctx->net        = sock_net(skb->sk);
+       ctx->afi        = afi;
+       ctx->table      = table;
+       ctx->chain      = chain;
+       ctx->nla        = nla;
+       ctx->portid     = NETLINK_CB(skb).portid;
+       ctx->report     = nlmsg_report(nlh);
+       ctx->seq        = nlh->nlmsg_seq;
+}
+
+static struct nft_trans *nft_trans_alloc(struct nft_ctx *ctx, int msg_type,
+                                        u32 size)
+{
+       struct nft_trans *trans;
+
+       trans = kzalloc(sizeof(struct nft_trans) + size, GFP_KERNEL);
+       if (trans == NULL)
+               return NULL;
+
+       trans->msg_type = msg_type;
+       trans->ctx      = *ctx;
+
+       return trans;
+}
+
+static void nft_trans_destroy(struct nft_trans *trans)
+{
+       list_del(&trans->list);
+       kfree(trans);
+}
+
 /*
  * Tables
  */
@@ -197,20 +236,13 @@ static int nf_tables_fill_table_info(struct sk_buff *skb, u32 portid, u32 seq,
        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)
+static int nf_tables_table_notify(const struct nft_ctx *ctx, int event)
 {
        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))
+       if (!ctx->report &&
+           !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
                return 0;
 
        err = -ENOBUFS;
@@ -218,18 +250,20 @@ static int nf_tables_table_notify(const struct sk_buff *oskb,
        if (skb == NULL)
                goto err;
 
-       err = nf_tables_fill_table_info(skb, portid, seq, event, 0,
-                                       family, table);
+       err = nf_tables_fill_table_info(skb, ctx->portid, ctx->seq, event, 0,
+                                       ctx->afi->family, ctx->table);
        if (err < 0) {
                kfree_skb(skb);
                goto err;
        }
 
-       err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report,
-                            GFP_KERNEL);
+       err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,
+                            ctx->report, GFP_KERNEL);
 err:
-       if (err < 0)
-               nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err);
+       if (err < 0) {
+               nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES,
+                                 err);
+       }
        return err;
 }
 
@@ -269,6 +303,9 @@ static int nf_tables_dump_tables(struct sk_buff *skb,
        return skb->len;
 }
 
+/* Internal table flags */
+#define NFT_TABLE_INACTIVE     (1 << 15)
+
 static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
                              const struct nlmsghdr *nlh,
                              const struct nlattr * const nla[])
@@ -295,6 +332,8 @@ static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
        table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]);
        if (IS_ERR(table))
                return PTR_ERR(table);
+       if (table->flags & NFT_TABLE_INACTIVE)
+               return -ENOENT;
 
        skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
        if (!skb2)
@@ -343,7 +382,7 @@ static int nf_tables_table_enable(const struct nft_af_info *afi,
        return err;
 }
 
-static int nf_tables_table_disable(const struct nft_af_info *afi,
+static void nf_tables_table_disable(const struct nft_af_info *afi,
                                   struct nft_table *table)
 {
        struct nft_chain *chain;
@@ -353,45 +392,63 @@ static int nf_tables_table_disable(const struct nft_af_info *afi,
                        nf_unregister_hooks(nft_base_chain(chain)->ops,
                                            afi->nops);
        }
-
-       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)
+static int nf_tables_updtable(struct nft_ctx *ctx)
 {
-       const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       int family = nfmsg->nfgen_family, ret = 0;
+       struct nft_trans *trans;
+       u32 flags;
+       int ret = 0;
 
-       if (nla[NFTA_TABLE_FLAGS]) {
-               u32 flags;
+       if (!ctx->nla[NFTA_TABLE_FLAGS])
+               return 0;
 
-               flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS]));
-               if (flags & ~NFT_TABLE_F_DORMANT)
-                       return -EINVAL;
+       flags = ntohl(nla_get_be32(ctx->nla[NFTA_TABLE_FLAGS]));
+       if (flags & ~NFT_TABLE_F_DORMANT)
+               return -EINVAL;
+
+       trans = nft_trans_alloc(ctx, NFT_MSG_NEWTABLE,
+                               sizeof(struct nft_trans_table));
+       if (trans == NULL)
+               return -ENOMEM;
 
-               if ((flags & NFT_TABLE_F_DORMANT) &&
-                   !(table->flags & NFT_TABLE_F_DORMANT)) {
-                       ret = nf_tables_table_disable(afi, 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(afi, table);
-                       if (ret >= 0)
-                               table->flags &= ~NFT_TABLE_F_DORMANT;
+       if ((flags & NFT_TABLE_F_DORMANT) &&
+           !(ctx->table->flags & NFT_TABLE_F_DORMANT)) {
+               nft_trans_table_enable(trans) = false;
+       } else if (!(flags & NFT_TABLE_F_DORMANT) &&
+                  ctx->table->flags & NFT_TABLE_F_DORMANT) {
+               ret = nf_tables_table_enable(ctx->afi, ctx->table);
+               if (ret >= 0) {
+                       ctx->table->flags &= ~NFT_TABLE_F_DORMANT;
+                       nft_trans_table_enable(trans) = true;
                }
-               if (ret < 0)
-                       goto err;
        }
+       if (ret < 0)
+               goto err;
 
-       nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family);
+       nft_trans_table_update(trans) = true;
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+       return 0;
 err:
+       nft_trans_destroy(trans);
        return ret;
 }
 
+static int nft_trans_table_add(struct nft_ctx *ctx, int msg_type)
+{
+       struct nft_trans *trans;
+
+       trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_table));
+       if (trans == NULL)
+               return -ENOMEM;
+
+       if (msg_type == NFT_MSG_NEWTABLE)
+               ctx->table->flags |= NFT_TABLE_INACTIVE;
+
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+       return 0;
+}
+
 static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
                              const struct nlmsghdr *nlh,
                              const struct nlattr * const nla[])
@@ -403,6 +460,8 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
        struct net *net = sock_net(skb->sk);
        int family = nfmsg->nfgen_family;
        u32 flags = 0;
+       struct nft_ctx ctx;
+       int err;
 
        afi = nf_tables_afinfo_lookup(net, family, true);
        if (IS_ERR(afi))
@@ -417,11 +476,15 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
        }
 
        if (table != NULL) {
+               if (table->flags & NFT_TABLE_INACTIVE)
+                       return -ENOENT;
                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);
+
+               nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
+               return nf_tables_updtable(&ctx);
        }
 
        if (nla[NFTA_TABLE_FLAGS]) {
@@ -444,8 +507,14 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
        INIT_LIST_HEAD(&table->sets);
        table->flags = flags;
 
+       nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
+       err = nft_trans_table_add(&ctx, NFT_MSG_NEWTABLE);
+       if (err < 0) {
+               kfree(table);
+               module_put(afi->owner);
+               return err;
+       }
        list_add_tail(&table->list, &afi->tables);
-       nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family);
        return 0;
 }
 
@@ -457,7 +526,8 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
        struct nft_af_info *afi;
        struct nft_table *table;
        struct net *net = sock_net(skb->sk);
-       int family = nfmsg->nfgen_family;
+       int family = nfmsg->nfgen_family, err;
+       struct nft_ctx ctx;
 
        afi = nf_tables_afinfo_lookup(net, family, false);
        if (IS_ERR(afi))
@@ -466,17 +536,28 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
        table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]);
        if (IS_ERR(table))
                return PTR_ERR(table);
-
-       if (!list_empty(&table->chains) || !list_empty(&table->sets))
+       if (table->flags & NFT_TABLE_INACTIVE)
+               return -ENOENT;
+       if (table->use > 0)
                return -EBUSY;
 
+       nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
+       err = nft_trans_table_add(&ctx, NFT_MSG_DELTABLE);
+       if (err < 0)
+               return err;
+
        list_del(&table->list);
-       nf_tables_table_notify(skb, nlh, table, NFT_MSG_DELTABLE, family);
-       kfree(table);
-       module_put(afi->owner);
        return 0;
 }
 
+static void nf_tables_table_destroy(struct nft_ctx *ctx)
+{
+       BUG_ON(ctx->table->use > 0);
+
+       kfree(ctx->table);
+       module_put(ctx->afi->owner);
+}
+
 int nft_register_chain_type(const struct nf_chain_type *ctype)
 {
        int err = 0;
@@ -541,7 +622,7 @@ static const struct nla_policy nft_chain_policy[NFTA_CHAIN_MAX + 1] = {
                                    .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_TYPE]       = { .type = NLA_STRING },
        [NFTA_CHAIN_COUNTERS]   = { .type = NLA_NESTED },
 };
 
@@ -637,21 +718,13 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq,
        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)
+static int nf_tables_chain_notify(const struct nft_ctx *ctx, int event)
 {
        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))
+       if (!ctx->report &&
+           !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
                return 0;
 
        err = -ENOBUFS;
@@ -659,18 +732,21 @@ static int nf_tables_chain_notify(const struct sk_buff *oskb,
        if (skb == NULL)
                goto err;
 
-       err = nf_tables_fill_chain_info(skb, portid, seq, event, 0, family,
-                                       table, chain);
+       err = nf_tables_fill_chain_info(skb, ctx->portid, ctx->seq, event, 0,
+                                       ctx->afi->family, ctx->table,
+                                       ctx->chain);
        if (err < 0) {
                kfree_skb(skb);
                goto err;
        }
 
-       err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report,
-                            GFP_KERNEL);
+       err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,
+                            ctx->report, GFP_KERNEL);
 err:
-       if (err < 0)
-               nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err);
+       if (err < 0) {
+               nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES,
+                                 err);
+       }
        return err;
 }
 
@@ -740,10 +816,14 @@ static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb,
        table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
        if (IS_ERR(table))
                return PTR_ERR(table);
+       if (table->flags & NFT_TABLE_INACTIVE)
+               return -ENOENT;
 
        chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]);
        if (IS_ERR(chain))
                return PTR_ERR(chain);
+       if (chain->flags & NFT_CHAIN_INACTIVE)
+               return -ENOENT;
 
        skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
        if (!skb2)
@@ -767,8 +847,7 @@ static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = {
        [NFTA_COUNTER_BYTES]    = { .type = NLA_U64 },
 };
 
-static int
-nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr)
+static struct nft_stats __percpu *nft_stats_alloc(const struct nlattr *attr)
 {
        struct nlattr *tb[NFTA_COUNTER_MAX+1];
        struct nft_stats __percpu *newstats;
@@ -777,14 +856,14 @@ nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr)
 
        err = nla_parse_nested(tb, NFTA_COUNTER_MAX, attr, nft_counter_policy);
        if (err < 0)
-               return err;
+               return ERR_PTR(err);
 
        if (!tb[NFTA_COUNTER_BYTES] || !tb[NFTA_COUNTER_PACKETS])
-               return -EINVAL;
+               return ERR_PTR(-EINVAL);
 
        newstats = alloc_percpu(struct nft_stats);
        if (newstats == NULL)
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
 
        /* Restore old counters on this cpu, no problem. Per-cpu statistics
         * are not exposed to userspace.
@@ -793,6 +872,12 @@ nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr)
        stats->bytes = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES]));
        stats->pkts = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS]));
 
+       return newstats;
+}
+
+static void nft_chain_stats_replace(struct nft_base_chain *chain,
+                                   struct nft_stats __percpu *newstats)
+{
        if (chain->stats) {
                struct nft_stats __percpu *oldstats =
                                nft_dereference(chain->stats);
@@ -802,17 +887,43 @@ nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr)
                free_percpu(oldstats);
        } else
                rcu_assign_pointer(chain->stats, newstats);
+}
+
+static int nft_trans_chain_add(struct nft_ctx *ctx, int msg_type)
+{
+       struct nft_trans *trans;
+
+       trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_chain));
+       if (trans == NULL)
+               return -ENOMEM;
 
+       if (msg_type == NFT_MSG_NEWCHAIN)
+               ctx->chain->flags |= NFT_CHAIN_INACTIVE;
+
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
        return 0;
 }
 
+static void nf_tables_chain_destroy(struct nft_chain *chain)
+{
+       BUG_ON(chain->use > 0);
+
+       if (chain->flags & NFT_BASE_CHAIN) {
+               module_put(nft_base_chain(chain)->type->owner);
+               free_percpu(nft_base_chain(chain)->stats);
+               kfree(nft_base_chain(chain));
+       } else {
+               kfree(chain);
+       }
+}
+
 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_af_info *afi;
        struct nft_table *table;
        struct nft_chain *chain;
        struct nft_base_chain *basechain = NULL;
@@ -822,8 +933,10 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
        u8 policy = NF_ACCEPT;
        u64 handle = 0;
        unsigned int i;
+       struct nft_stats __percpu *stats;
        int err;
        bool create;
+       struct nft_ctx ctx;
 
        create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
 
@@ -869,6 +982,11 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
        }
 
        if (chain != NULL) {
+               struct nft_stats *stats = NULL;
+               struct nft_trans *trans;
+
+               if (chain->flags & NFT_CHAIN_INACTIVE)
+                       return -ENOENT;
                if (nlh->nlmsg_flags & NLM_F_EXCL)
                        return -EEXIST;
                if (nlh->nlmsg_flags & NLM_F_REPLACE)
@@ -882,19 +1000,31 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
                        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;
+                       stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]);
+                       if (IS_ERR(stats))
+                               return PTR_ERR(stats);
                }
 
-               if (nla[NFTA_CHAIN_POLICY])
-                       nft_base_chain(chain)->policy = policy;
+               nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
+               trans = nft_trans_alloc(&ctx, NFT_MSG_NEWCHAIN,
+                                       sizeof(struct nft_trans_chain));
+               if (trans == NULL)
+                       return -ENOMEM;
+
+               nft_trans_chain_stats(trans) = stats;
+               nft_trans_chain_update(trans) = true;
 
-               if (nla[NFTA_CHAIN_HANDLE] && name)
-                       nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN);
+               if (nla[NFTA_CHAIN_POLICY])
+                       nft_trans_chain_policy(trans) = policy;
+               else
+                       nft_trans_chain_policy(trans) = -1;
 
-               goto notify;
+               if (nla[NFTA_CHAIN_HANDLE] && name) {
+                       nla_strlcpy(nft_trans_chain_name(trans), name,
+                                   NFT_CHAIN_MAXNAMELEN);
+               }
+               list_add_tail(&trans->list, &net->nft.commit_list);
+               return 0;
        }
 
        if (table->use == UINT_MAX)
@@ -939,23 +1069,21 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
                        return -ENOMEM;
 
                if (nla[NFTA_CHAIN_COUNTERS]) {
-                       err = nf_tables_counters(basechain,
-                                                nla[NFTA_CHAIN_COUNTERS]);
-                       if (err < 0) {
+                       stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]);
+                       if (IS_ERR(stats)) {
                                module_put(type->owner);
                                kfree(basechain);
-                               return err;
+                               return PTR_ERR(stats);
                        }
+                       basechain->stats = stats;
                } else {
-                       struct nft_stats __percpu *newstats;
-
-                       newstats = alloc_percpu(struct nft_stats);
-                       if (newstats == NULL) {
+                       stats = alloc_percpu(struct nft_stats);
+                       if (IS_ERR(stats)) {
                                module_put(type->owner);
                                kfree(basechain);
-                               return -ENOMEM;
+                               return PTR_ERR(stats);
                        }
-                       rcu_assign_pointer(basechain->stats, newstats);
+                       rcu_assign_pointer(basechain->stats, stats);
                }
 
                basechain->type = type;
@@ -992,31 +1120,27 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
        if (!(table->flags & NFT_TABLE_F_DORMANT) &&
            chain->flags & NFT_BASE_CHAIN) {
                err = nf_register_hooks(nft_base_chain(chain)->ops, afi->nops);
-               if (err < 0) {
-                       module_put(basechain->type->owner);
-                       free_percpu(basechain->stats);
-                       kfree(basechain);
-                       return err;
-               }
+               if (err < 0)
+                       goto err1;
        }
-       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_chain_destroy(struct nft_chain *chain)
-{
-       BUG_ON(chain->use > 0);
+       nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
+       err = nft_trans_chain_add(&ctx, NFT_MSG_NEWCHAIN);
+       if (err < 0)
+               goto err2;
 
-       if (chain->flags & NFT_BASE_CHAIN) {
-               module_put(nft_base_chain(chain)->type->owner);
-               free_percpu(nft_base_chain(chain)->stats);
-               kfree(nft_base_chain(chain));
-       } else
-               kfree(chain);
+       table->use++;
+       list_add_tail(&chain->list, &table->chains);
+       return 0;
+err2:
+       if (!(table->flags & NFT_TABLE_F_DORMANT) &&
+           chain->flags & NFT_BASE_CHAIN) {
+               nf_unregister_hooks(nft_base_chain(chain)->ops,
+                                   afi->nops);
+       }
+err1:
+       nf_tables_chain_destroy(chain);
+       return err;
 }
 
 static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
@@ -1024,11 +1148,13 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
                              const struct nlattr * const nla[])
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       const struct nft_af_info *afi;
+       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;
+       struct nft_ctx ctx;
+       int err;
 
        afi = nf_tables_afinfo_lookup(net, family, false);
        if (IS_ERR(afi))
@@ -1037,48 +1163,27 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
        table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
        if (IS_ERR(table))
                return PTR_ERR(table);
+       if (table->flags & NFT_TABLE_INACTIVE)
+               return -ENOENT;
 
        chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]);
        if (IS_ERR(chain))
                return PTR_ERR(chain);
-
-       if (!list_empty(&chain->rules) || chain->use > 0)
+       if (chain->flags & NFT_CHAIN_INACTIVE)
+               return -ENOENT;
+       if (chain->use > 0)
                return -EBUSY;
 
-       list_del(&chain->list);
-       table->use--;
-
-       if (!(table->flags & NFT_TABLE_F_DORMANT) &&
-           chain->flags & NFT_BASE_CHAIN)
-               nf_unregister_hooks(nft_base_chain(chain)->ops, afi->nops);
-
-       nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_DELCHAIN,
-                              family);
-
-       /* Make sure all rule references are gone before this is released */
-       synchronize_rcu();
+       nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
+       err = nft_trans_chain_add(&ctx, NFT_MSG_DELCHAIN);
+       if (err < 0)
+               return err;
 
-       nf_tables_chain_destroy(chain);
+       table->use--;
+       list_del(&chain->list);
        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
  */
@@ -1093,7 +1198,10 @@ static void nft_ctx_init(struct nft_ctx *ctx,
 int nft_register_expr(struct nft_expr_type *type)
 {
        nfnl_lock(NFNL_SUBSYS_NFTABLES);
-       list_add_tail(&type->list, &nf_tables_expressions);
+       if (type->family == NFPROTO_UNSPEC)
+               list_add_tail(&type->list, &nf_tables_expressions);
+       else
+               list_add(&type->list, &nf_tables_expressions);
        nfnl_unlock(NFNL_SUBSYS_NFTABLES);
        return 0;
 }
@@ -1361,22 +1469,15 @@ static int nf_tables_fill_rule_info(struct sk_buff *skb, u32 portid, u32 seq,
        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,
+static int nf_tables_rule_notify(const struct nft_ctx *ctx,
                                 const struct nft_rule *rule,
-                                int event, u32 flags, int family)
+                                int event)
 {
        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))
+       if (!ctx->report &&
+           !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
                return 0;
 
        err = -ENOBUFS;
@@ -1384,18 +1485,21 @@ static int nf_tables_rule_notify(const struct sk_buff *oskb,
        if (skb == NULL)
                goto err;
 
-       err = nf_tables_fill_rule_info(skb, portid, seq, event, flags,
-                                      family, table, chain, rule);
+       err = nf_tables_fill_rule_info(skb, ctx->portid, ctx->seq, event, 0,
+                                      ctx->afi->family, ctx->table,
+                                      ctx->chain, rule);
        if (err < 0) {
                kfree_skb(skb);
                goto err;
        }
 
-       err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report,
-                            GFP_KERNEL);
+       err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,
+                            ctx->report, GFP_KERNEL);
 err:
-       if (err < 0)
-               nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err);
+       if (err < 0) {
+               nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES,
+                                 err);
+       }
        return err;
 }
 
@@ -1511,10 +1615,14 @@ static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb,
        table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
        if (IS_ERR(table))
                return PTR_ERR(table);
+       if (table->flags & NFT_TABLE_INACTIVE)
+               return -ENOENT;
 
        chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
        if (IS_ERR(chain))
                return PTR_ERR(chain);
+       if (chain->flags & NFT_CHAIN_INACTIVE)
+               return -ENOENT;
 
        rule = nf_tables_rule_lookup(chain, nla[NFTA_RULE_HANDLE]);
        if (IS_ERR(rule))
@@ -1554,37 +1662,36 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
        kfree(rule);
 }
 
-#define NFT_RULE_MAXEXPRS      128
-
-static struct nft_expr_info *info;
-
-static struct nft_rule_trans *
-nf_tables_trans_add(struct nft_ctx *ctx, struct nft_rule *rule)
+static struct nft_trans *nft_trans_rule_add(struct nft_ctx *ctx, int msg_type,
+                                           struct nft_rule *rule)
 {
-       struct nft_rule_trans *rupd;
+       struct nft_trans *trans;
 
-       rupd = kmalloc(sizeof(struct nft_rule_trans), GFP_KERNEL);
-       if (rupd == NULL)
-              return NULL;
+       trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_rule));
+       if (trans == NULL)
+               return NULL;
 
-       rupd->ctx = *ctx;
-       rupd->rule = rule;
-       list_add_tail(&rupd->list, &ctx->net->nft.commit_list);
+       nft_trans_rule(trans) = rule;
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
 
-       return rupd;
+       return trans;
 }
 
+#define NFT_RULE_MAXEXPRS      128
+
+static struct nft_expr_info *info;
+
 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 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_trans *trans = NULL;
        struct nft_expr *expr;
        struct nft_ctx ctx;
        struct nlattr *tmp;
@@ -1682,8 +1789,9 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
 
        if (nlh->nlmsg_flags & NLM_F_REPLACE) {
                if (nft_rule_is_active_next(net, old_rule)) {
-                       repl = nf_tables_trans_add(&ctx, old_rule);
-                       if (repl == NULL) {
+                       trans = nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE,
+                                                  old_rule);
+                       if (trans == NULL) {
                                err = -ENOMEM;
                                goto err2;
                        }
@@ -1705,19 +1813,19 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
                        list_add_rcu(&rule->list, &chain->rules);
        }
 
-       if (nf_tables_trans_add(&ctx, rule) == NULL) {
+       if (nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule) == NULL) {
                err = -ENOMEM;
                goto err3;
        }
+       chain->use++;
        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);
+       if (trans) {
+               list_del_rcu(&nft_trans_rule(trans)->list);
+               nft_rule_clear(net, nft_trans_rule(trans));
+               nft_trans_destroy(trans);
        }
 err2:
        nf_tables_rule_destroy(&ctx, rule);
@@ -1734,9 +1842,10 @@ 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(ctx, rule) == NULL)
+               if (nft_trans_rule_add(ctx, NFT_MSG_DELRULE, rule) == NULL)
                        return -ENOMEM;
                nft_rule_disactivate_next(ctx->net, rule);
+               ctx->chain->use--;
                return 0;
        }
        return -ENOENT;
@@ -1760,9 +1869,9 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
                             const struct nlattr * const nla[])
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       const struct nft_af_info *afi;
+       struct nft_af_info *afi;
        struct net *net = sock_net(skb->sk);
-       const struct nft_table *table;
+       struct nft_table *table;
        struct nft_chain *chain = NULL;
        struct nft_rule *rule;
        int family = nfmsg->nfgen_family, err = 0;
@@ -1775,6 +1884,8 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
        table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
        if (IS_ERR(table))
                return PTR_ERR(table);
+       if (table->flags & NFT_TABLE_INACTIVE)
+               return -ENOENT;
 
        if (nla[NFTA_RULE_CHAIN]) {
                chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
@@ -1807,88 +1918,6 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
        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) {
-               /* 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->ctx.nlh,
-                                             rupd->ctx.table, rupd->ctx.chain,
-                                             rupd->rule, NFT_MSG_NEWRULE, 0,
-                                             rupd->ctx.afi->family);
-                       list_del(&rupd->list);
-                       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->ctx.nlh,
-                                     rupd->ctx.table, rupd->ctx.chain,
-                                     rupd->rule, NFT_MSG_DELRULE, 0,
-                                     rupd->ctx.afi->family);
-       }
-
-       /* Make sure we don't see any packet traversing old rules */
-       synchronize_rcu();
-
-       /* Now we can safely release unused old rules */
-       list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
-               nf_tables_rule_destroy(&rupd->ctx, rupd->rule);
-               list_del(&rupd->list);
-               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) {
-               if (!nft_rule_is_active_next(net, rupd->rule)) {
-                       nft_rule_clear(net, rupd->rule);
-                       list_del(&rupd->list);
-                       kfree(rupd);
-                       continue;
-               }
-
-               /* This rule is inactive, get rid of it */
-               list_del_rcu(&rupd->rule->list);
-       }
-
-       /* Make sure we don't see any packet accessing aborted rules */
-       synchronize_rcu();
-
-       list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
-               nf_tables_rule_destroy(&rupd->ctx, rupd->rule);
-               list_del(&rupd->list);
-               kfree(rupd);
-       }
-
-       return 0;
-}
-
 /*
  * Sets
  */
@@ -1912,9 +1941,18 @@ void nft_unregister_set(struct nft_set_ops *ops)
 }
 EXPORT_SYMBOL_GPL(nft_unregister_set);
 
-static const struct nft_set_ops *nft_select_set_ops(const struct nlattr * const nla[])
+/*
+ * Select a set implementation based on the data characteristics and the
+ * given policy. The total memory use might not be known if no size is
+ * given, in that case the amount of memory per element is used.
+ */
+static const struct nft_set_ops *
+nft_select_set_ops(const struct nlattr * const nla[],
+                  const struct nft_set_desc *desc,
+                  enum nft_set_policies policy)
 {
-       const struct nft_set_ops *ops;
+       const struct nft_set_ops *ops, *bops;
+       struct nft_set_estimate est, best;
        u32 features;
 
 #ifdef CONFIG_MODULES
@@ -1932,27 +1970,64 @@ static const struct nft_set_ops *nft_select_set_ops(const struct nlattr * const
                features &= NFT_SET_INTERVAL | NFT_SET_MAP;
        }
 
-       // FIXME: implement selection properly
+       bops       = NULL;
+       best.size  = ~0;
+       best.class = ~0;
+
        list_for_each_entry(ops, &nf_tables_set_ops, list) {
                if ((ops->features & features) != features)
                        continue;
-               if (!try_module_get(ops->owner))
+               if (!ops->estimate(desc, features, &est))
                        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,
-                                           .len = IFNAMSIZ - 1 },
+               switch (policy) {
+               case NFT_SET_POL_PERFORMANCE:
+                       if (est.class < best.class)
+                               break;
+                       if (est.class == best.class && est.size < best.size)
+                               break;
+                       continue;
+               case NFT_SET_POL_MEMORY:
+                       if (est.size < best.size)
+                               break;
+                       if (est.size == best.size && est.class < best.class)
+                               break;
+                       continue;
+               default:
+                       break;
+               }
+
+               if (!try_module_get(ops->owner))
+                       continue;
+               if (bops != NULL)
+                       module_put(bops->owner);
+
+               bops = ops;
+               best = est;
+       }
+
+       if (bops != NULL)
+               return bops;
+
+       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,
+                                           .len = IFNAMSIZ - 1 },
        [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 },
+       [NFTA_SET_POLICY]               = { .type = NLA_U32 },
+       [NFTA_SET_DESC]                 = { .type = NLA_NESTED },
+       [NFTA_SET_ID]                   = { .type = NLA_U32 },
+};
+
+static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = {
+       [NFTA_SET_DESC_SIZE]            = { .type = NLA_U32 },
 };
 
 static int nft_ctx_init_from_setattr(struct nft_ctx *ctx,
@@ -1962,8 +2037,8 @@ static int nft_ctx_init_from_setattr(struct nft_ctx *ctx,
 {
        struct net *net = sock_net(skb->sk);
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       const struct nft_af_info *afi = NULL;
-       const struct nft_table *table = NULL;
+       struct nft_af_info *afi = NULL;
+       struct nft_table *table = NULL;
 
        if (nfmsg->nfgen_family != NFPROTO_UNSPEC) {
                afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false);
@@ -1978,6 +2053,8 @@ static int nft_ctx_init_from_setattr(struct nft_ctx *ctx,
                table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]);
                if (IS_ERR(table))
                        return PTR_ERR(table);
+               if (table->flags & NFT_TABLE_INACTIVE)
+                       return -ENOENT;
        }
 
        nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
@@ -1999,13 +2076,27 @@ struct nft_set *nf_tables_set_lookup(const struct nft_table *table,
        return ERR_PTR(-ENOENT);
 }
 
+struct nft_set *nf_tables_set_lookup_byid(const struct net *net,
+                                         const struct nlattr *nla)
+{
+       struct nft_trans *trans;
+       u32 id = ntohl(nla_get_be32(nla));
+
+       list_for_each_entry(trans, &net->nft.commit_list, list) {
+               if (trans->msg_type == NFT_MSG_NEWSET &&
+                   id == nft_trans_set_id(trans))
+                       return nft_trans_set(trans);
+       }
+       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;
+       unsigned int n = 0, min = 0;
 
        p = strnchr(name, IFNAMSIZ, '%');
        if (p != NULL) {
@@ -2015,23 +2106,28 @@ static int nf_tables_set_alloc_name(struct nft_ctx *ctx, struct nft_set *set,
                inuse = (unsigned long *)get_zeroed_page(GFP_KERNEL);
                if (inuse == NULL)
                        return -ENOMEM;
-
+cont:
                list_for_each_entry(i, &ctx->table->sets, list) {
                        int tmp;
 
                        if (!sscanf(i->name, name, &tmp))
                                continue;
-                       if (tmp < 0 || tmp >= BITS_PER_BYTE * PAGE_SIZE)
+                       if (tmp < min || tmp >= min + BITS_PER_BYTE * PAGE_SIZE)
                                continue;
 
-                       set_bit(tmp, inuse);
+                       set_bit(tmp - min, inuse);
                }
 
                n = find_first_zero_bit(inuse, BITS_PER_BYTE * PAGE_SIZE);
+               if (n >= BITS_PER_BYTE * PAGE_SIZE) {
+                       min += BITS_PER_BYTE * PAGE_SIZE;
+                       memset(inuse, 0, PAGE_SIZE);
+                       goto cont;
+               }
                free_page((unsigned long)inuse);
        }
 
-       snprintf(set->name, sizeof(set->name), name, n);
+       snprintf(set->name, sizeof(set->name), name, min + n);
        list_for_each_entry(i, &ctx->table->sets, list) {
                if (!strcmp(set->name, i->name))
                        return -ENFILE;
@@ -2044,8 +2140,9 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
 {
        struct nfgenmsg *nfmsg;
        struct nlmsghdr *nlh;
-       u32 portid = NETLINK_CB(ctx->skb).portid;
-       u32 seq = ctx->nlh->nlmsg_seq;
+       struct nlattr *desc;
+       u32 portid = ctx->portid;
+       u32 seq = ctx->seq;
 
        event |= NFNL_SUBSYS_NFTABLES << 8;
        nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
@@ -2077,6 +2174,14 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
                        goto nla_put_failure;
        }
 
+       desc = nla_nest_start(skb, NFTA_SET_DESC);
+       if (desc == NULL)
+               goto nla_put_failure;
+       if (set->size &&
+           nla_put_be32(skb, NFTA_SET_DESC_SIZE, htonl(set->size)))
+               goto nla_put_failure;
+       nla_nest_end(skb, desc);
+
        return nlmsg_end(skb, nlh);
 
 nla_put_failure:
@@ -2086,19 +2191,18 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
 
 static int nf_tables_set_notify(const struct nft_ctx *ctx,
                                const struct nft_set *set,
-                               int event)
+                               int event, gfp_t gfp_flags)
 {
        struct sk_buff *skb;
-       u32 portid = NETLINK_CB(ctx->skb).portid;
-       bool report;
+       u32 portid = ctx->portid;
        int err;
 
-       report = nlmsg_report(ctx->nlh);
-       if (!report && !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
+       if (!ctx->report &&
+           !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
                return 0;
 
        err = -ENOBUFS;
-       skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       skb = nlmsg_new(NLMSG_GOODSIZE, gfp_flags);
        if (skb == NULL)
                goto err;
 
@@ -2108,8 +2212,8 @@ static int nf_tables_set_notify(const struct nft_ctx *ctx,
                goto err;
        }
 
-       err = nfnetlink_send(skb, ctx->net, portid, NFNLGRP_NFTABLES, report,
-                            GFP_KERNEL);
+       err = nfnetlink_send(skb, ctx->net, portid, NFNLGRP_NFTABLES,
+                            ctx->report, gfp_flags);
 err:
        if (err < 0)
                nfnetlink_set_err(ctx->net, portid, NFNLGRP_NFTABLES, err);
@@ -2183,7 +2287,7 @@ static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb,
 {
        const struct nft_set *set;
        unsigned int idx, s_idx = cb->args[0];
-       const struct nft_af_info *afi;
+       struct nft_af_info *afi;
        struct nft_table *table, *cur_table = (struct nft_table *)cb->args[2];
        struct net *net = sock_net(skb->sk);
        int cur_family = cb->args[3];
@@ -2260,6 +2364,8 @@ static int nf_tables_dump_sets(struct sk_buff *skb, struct netlink_callback *cb)
        return ret;
 }
 
+#define NFT_SET_INACTIVE       (1 << 15)       /* Internal set flag */
+
 static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb,
                            const struct nlmsghdr *nlh,
                            const struct nlattr * const nla[])
@@ -2289,6 +2395,8 @@ static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb,
        set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]);
        if (IS_ERR(set))
                return PTR_ERR(set);
+       if (set->flags & NFT_SET_INACTIVE)
+               return -ENOENT;
 
        skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
        if (skb2 == NULL)
@@ -2305,13 +2413,50 @@ static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb,
        return err;
 }
 
+static int nf_tables_set_desc_parse(const struct nft_ctx *ctx,
+                                   struct nft_set_desc *desc,
+                                   const struct nlattr *nla)
+{
+       struct nlattr *da[NFTA_SET_DESC_MAX + 1];
+       int err;
+
+       err = nla_parse_nested(da, NFTA_SET_DESC_MAX, nla, nft_set_desc_policy);
+       if (err < 0)
+               return err;
+
+       if (da[NFTA_SET_DESC_SIZE] != NULL)
+               desc->size = ntohl(nla_get_be32(da[NFTA_SET_DESC_SIZE]));
+
+       return 0;
+}
+
+static int nft_trans_set_add(struct nft_ctx *ctx, int msg_type,
+                            struct nft_set *set)
+{
+       struct nft_trans *trans;
+
+       trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_set));
+       if (trans == NULL)
+               return -ENOMEM;
+
+       if (msg_type == NFT_MSG_NEWSET && ctx->nla[NFTA_SET_ID] != NULL) {
+               nft_trans_set_id(trans) =
+                       ntohl(nla_get_be32(ctx->nla[NFTA_SET_ID]));
+               set->flags |= NFT_SET_INACTIVE;
+       }
+       nft_trans_set(trans) = set;
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+
+       return 0;
+}
+
 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 nft_af_info *afi;
        struct net *net = sock_net(skb->sk);
        struct nft_table *table;
        struct nft_set *set;
@@ -2319,14 +2464,18 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
        char name[IFNAMSIZ];
        unsigned int size;
        bool create;
-       u32 ktype, klen, dlen, dtype, flags;
+       u32 ktype, dtype, flags, policy;
+       struct nft_set_desc desc;
        int err;
 
        if (nla[NFTA_SET_TABLE] == NULL ||
            nla[NFTA_SET_NAME] == NULL ||
-           nla[NFTA_SET_KEY_LEN] == NULL)
+           nla[NFTA_SET_KEY_LEN] == NULL ||
+           nla[NFTA_SET_ID] == NULL)
                return -EINVAL;
 
+       memset(&desc, 0, sizeof(desc));
+
        ktype = NFT_DATA_VALUE;
        if (nla[NFTA_SET_KEY_TYPE] != NULL) {
                ktype = ntohl(nla_get_be32(nla[NFTA_SET_KEY_TYPE]));
@@ -2334,8 +2483,8 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
                        return -EINVAL;
        }
 
-       klen = ntohl(nla_get_be32(nla[NFTA_SET_KEY_LEN]));
-       if (klen == 0 || klen > FIELD_SIZEOF(struct nft_data, data))
+       desc.klen = ntohl(nla_get_be32(nla[NFTA_SET_KEY_LEN]));
+       if (desc.klen == 0 || desc.klen > FIELD_SIZEOF(struct nft_data, data))
                return -EINVAL;
 
        flags = 0;
@@ -2347,7 +2496,6 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
        }
 
        dtype = 0;
-       dlen  = 0;
        if (nla[NFTA_SET_DATA_TYPE] != NULL) {
                if (!(flags & NFT_SET_MAP))
                        return -EINVAL;
@@ -2360,15 +2508,25 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
                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))
+                       desc.dlen = ntohl(nla_get_be32(nla[NFTA_SET_DATA_LEN]));
+                       if (desc.dlen == 0 ||
+                           desc.dlen > FIELD_SIZEOF(struct nft_data, data))
                                return -EINVAL;
                } else
-                       dlen = sizeof(struct nft_data);
+                       desc.dlen = sizeof(struct nft_data);
        } else if (flags & NFT_SET_MAP)
                return -EINVAL;
 
+       policy = NFT_SET_POL_PERFORMANCE;
+       if (nla[NFTA_SET_POLICY] != NULL)
+               policy = ntohl(nla_get_be32(nla[NFTA_SET_POLICY]));
+
+       if (nla[NFTA_SET_DESC] != NULL) {
+               err = nf_tables_set_desc_parse(&ctx, &desc, nla[NFTA_SET_DESC]);
+               if (err < 0)
+                       return err;
+       }
+
        create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
 
        afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, create);
@@ -2399,7 +2557,7 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
        if (!(nlh->nlmsg_flags & NLM_F_CREATE))
                return -ENOENT;
 
-       ops = nft_select_set_ops(nla);
+       ops = nft_select_set_ops(nla, &desc, policy);
        if (IS_ERR(ops))
                return PTR_ERR(ops);
 
@@ -2420,17 +2578,22 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
        INIT_LIST_HEAD(&set->bindings);
        set->ops   = ops;
        set->ktype = ktype;
-       set->klen  = klen;
+       set->klen  = desc.klen;
        set->dtype = dtype;
-       set->dlen  = dlen;
+       set->dlen  = desc.dlen;
        set->flags = flags;
+       set->size  = desc.size;
 
-       err = ops->init(set, nla);
+       err = ops->init(set, &desc, nla);
+       if (err < 0)
+               goto err2;
+
+       err = nft_trans_set_add(&ctx, NFT_MSG_NEWSET, set);
        if (err < 0)
                goto err2;
 
        list_add_tail(&set->list, &table->sets);
-       nf_tables_set_notify(&ctx, set, NFT_MSG_NEWSET);
+       table->use++;
        return 0;
 
 err2:
@@ -2440,16 +2603,20 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
        return err;
 }
 
-static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
+static void nft_set_destroy(struct nft_set *set)
 {
-       list_del(&set->list);
-       nf_tables_set_notify(ctx, set, NFT_MSG_DELSET);
-
        set->ops->destroy(set);
        module_put(set->ops->owner);
        kfree(set);
 }
 
+static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
+{
+       list_del(&set->list);
+       nf_tables_set_notify(ctx, set, NFT_MSG_DELSET, GFP_ATOMIC);
+       nft_set_destroy(set);
+}
+
 static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb,
                            const struct nlmsghdr *nlh,
                            const struct nlattr * const nla[])
@@ -2471,10 +2638,17 @@ static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb,
        set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]);
        if (IS_ERR(set))
                return PTR_ERR(set);
+       if (set->flags & NFT_SET_INACTIVE)
+               return -ENOENT;
        if (!list_empty(&set->bindings))
                return -EBUSY;
 
-       nf_tables_set_destroy(&ctx, set);
+       err = nft_trans_set_add(&ctx, NFT_MSG_DELSET, set);
+       if (err < 0)
+               return err;
+
+       list_del(&set->list);
+       ctx.table->use--;
        return 0;
 }
 
@@ -2534,7 +2708,8 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
 {
        list_del(&binding->list);
 
-       if (list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS)
+       if (list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS &&
+           !(set->flags & NFT_SET_INACTIVE))
                nf_tables_set_destroy(ctx, set);
 }
 
@@ -2552,16 +2727,18 @@ static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX +
        [NFTA_SET_ELEM_LIST_TABLE]      = { .type = NLA_STRING },
        [NFTA_SET_ELEM_LIST_SET]        = { .type = NLA_STRING },
        [NFTA_SET_ELEM_LIST_ELEMENTS]   = { .type = NLA_NESTED },
+       [NFTA_SET_ELEM_LIST_SET_ID]     = { .type = NLA_U32 },
 };
 
 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 nlattr * const nla[],
+                                     bool trans)
 {
        const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
-       const struct nft_af_info *afi;
-       const struct nft_table *table;
+       struct nft_af_info *afi;
+       struct nft_table *table;
        struct net *net = sock_net(skb->sk);
 
        afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false);
@@ -2571,6 +2748,8 @@ static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx,
        table = nf_tables_table_lookup(afi, nla[NFTA_SET_ELEM_LIST_TABLE]);
        if (IS_ERR(table))
                return PTR_ERR(table);
+       if (!trans && (table->flags & NFT_TABLE_INACTIVE))
+               return -ENOENT;
 
        nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
        return 0;
@@ -2644,13 +2823,16 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
        if (err < 0)
                return err;
 
-       err = nft_ctx_init_from_elemattr(&ctx, cb->skb, cb->nlh, (void *)nla);
+       err = nft_ctx_init_from_elemattr(&ctx, cb->skb, cb->nlh, (void *)nla,
+                                        false);
        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 (set->flags & NFT_SET_INACTIVE)
+               return -ENOENT;
 
        event  = NFT_MSG_NEWSETELEM;
        event |= NFNL_SUBSYS_NFTABLES << 8;
@@ -2707,13 +2889,15 @@ static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb,
        struct nft_ctx ctx;
        int err;
 
-       err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla);
+       err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false);
        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 (set->flags & NFT_SET_INACTIVE)
+               return -ENOENT;
 
        if (nlh->nlmsg_flags & NLM_F_DUMP) {
                struct netlink_dump_control c = {
@@ -2724,7 +2908,98 @@ static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb,
        return -EOPNOTSUPP;
 }
 
-static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set,
+static int nf_tables_fill_setelem_info(struct sk_buff *skb,
+                                      const struct nft_ctx *ctx, u32 seq,
+                                      u32 portid, int event, u16 flags,
+                                      const struct nft_set *set,
+                                      const struct nft_set_elem *elem)
+{
+       struct nfgenmsg *nfmsg;
+       struct nlmsghdr *nlh;
+       struct nlattr *nest;
+       int err;
+
+       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;
+
+       nest = nla_nest_start(skb, NFTA_SET_ELEM_LIST_ELEMENTS);
+       if (nest == NULL)
+               goto nla_put_failure;
+
+       err = nf_tables_fill_setelem(skb, set, elem);
+       if (err < 0)
+               goto nla_put_failure;
+
+       nla_nest_end(skb, nest);
+
+       return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+       nlmsg_trim(skb, nlh);
+       return -1;
+}
+
+static int nf_tables_setelem_notify(const struct nft_ctx *ctx,
+                                   const struct nft_set *set,
+                                   const struct nft_set_elem *elem,
+                                   int event, u16 flags)
+{
+       struct net *net = ctx->net;
+       u32 portid = ctx->portid;
+       struct sk_buff *skb;
+       int err;
+
+       if (!ctx->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_setelem_info(skb, ctx, 0, portid, event, flags,
+                                         set, elem);
+       if (err < 0) {
+               kfree_skb(skb);
+               goto err;
+       }
+
+       err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, ctx->report,
+                            GFP_KERNEL);
+err:
+       if (err < 0)
+               nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err);
+       return err;
+}
+
+static struct nft_trans *nft_trans_elem_alloc(struct nft_ctx *ctx,
+                                             int msg_type,
+                                             struct nft_set *set)
+{
+       struct nft_trans *trans;
+
+       trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_elem));
+       if (trans == NULL)
+               return NULL;
+
+       nft_trans_elem_set(trans) = set;
+       return trans;
+}
+
+static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                            const struct nlattr *attr)
 {
        struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
@@ -2732,8 +3007,12 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set,
        struct nft_set_elem elem;
        struct nft_set_binding *binding;
        enum nft_registers dreg;
+       struct nft_trans *trans;
        int err;
 
+       if (set->size && set->nelems == set->size)
+               return -ENFILE;
+
        err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr,
                               nft_set_elem_policy);
        if (err < 0)
@@ -2786,7 +3065,7 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set,
                        struct nft_ctx bind_ctx = {
                                .afi    = ctx->afi,
                                .table  = ctx->table,
-                               .chain  = binding->chain,
+                               .chain  = (struct nft_chain *)binding->chain,
                        };
 
                        err = nft_validate_data_load(&bind_ctx, dreg,
@@ -2796,12 +3075,20 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set,
                }
        }
 
+       trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
+       if (trans == NULL)
+               goto err3;
+
        err = set->ops->insert(set, &elem);
        if (err < 0)
-               goto err3;
+               goto err4;
 
+       nft_trans_elem(trans) = elem;
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
        return 0;
 
+err4:
+       kfree(trans);
 err3:
        if (nla[NFTA_SET_ELEM_DATA] != NULL)
                nft_data_uninit(&elem.data, d2.type);
@@ -2815,35 +3102,46 @@ static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb,
                                const struct nlmsghdr *nlh,
                                const struct nlattr * const nla[])
 {
+       struct net *net = sock_net(skb->sk);
        const struct nlattr *attr;
        struct nft_set *set;
        struct nft_ctx ctx;
-       int rem, err;
+       int rem, err = 0;
 
-       err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla);
+       err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, true);
        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 (IS_ERR(set)) {
+               if (nla[NFTA_SET_ELEM_LIST_SET_ID]) {
+                       set = nf_tables_set_lookup_byid(net,
+                                       nla[NFTA_SET_ELEM_LIST_SET_ID]);
+               }
+               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;
+                       break;
+
+               set->nelems++;
        }
-       return 0;
+       return err;
 }
 
-static int nft_del_setelem(const struct nft_ctx *ctx, struct nft_set *set,
+static int nft_del_setelem(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;
+       struct nft_trans *trans;
        int err;
 
        err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr,
@@ -2867,7 +3165,12 @@ static int nft_del_setelem(const struct nft_ctx *ctx, struct nft_set *set,
        if (err < 0)
                goto err2;
 
-       set->ops->remove(set, &elem);
+       trans = nft_trans_elem_alloc(ctx, NFT_MSG_DELSETELEM, set);
+       if (trans == NULL)
+               goto err2;
+
+       nft_trans_elem(trans) = elem;
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
 
        nft_data_uninit(&elem.key, NFT_DATA_VALUE);
        if (set->flags & NFT_SET_MAP)
@@ -2886,9 +3189,9 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
        const struct nlattr *attr;
        struct nft_set *set;
        struct nft_ctx ctx;
-       int rem, err;
+       int rem, err = 0;
 
-       err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla);
+       err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false);
        if (err < 0)
                return err;
 
@@ -2901,14 +3204,16 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
        nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
                err = nft_del_setelem(&ctx, set, attr);
                if (err < 0)
-                       return err;
+                       break;
+
+               set->nelems--;
        }
-       return 0;
+       return err;
 }
 
 static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
        [NFT_MSG_NEWTABLE] = {
-               .call           = nf_tables_newtable,
+               .call_batch     = nf_tables_newtable,
                .attr_count     = NFTA_TABLE_MAX,
                .policy         = nft_table_policy,
        },
@@ -2918,12 +3223,12 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
                .policy         = nft_table_policy,
        },
        [NFT_MSG_DELTABLE] = {
-               .call           = nf_tables_deltable,
+               .call_batch     = nf_tables_deltable,
                .attr_count     = NFTA_TABLE_MAX,
                .policy         = nft_table_policy,
        },
        [NFT_MSG_NEWCHAIN] = {
-               .call           = nf_tables_newchain,
+               .call_batch     = nf_tables_newchain,
                .attr_count     = NFTA_CHAIN_MAX,
                .policy         = nft_chain_policy,
        },
@@ -2933,7 +3238,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
                .policy         = nft_chain_policy,
        },
        [NFT_MSG_DELCHAIN] = {
-               .call           = nf_tables_delchain,
+               .call_batch     = nf_tables_delchain,
                .attr_count     = NFTA_CHAIN_MAX,
                .policy         = nft_chain_policy,
        },
@@ -2953,7 +3258,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
                .policy         = nft_rule_policy,
        },
        [NFT_MSG_NEWSET] = {
-               .call           = nf_tables_newset,
+               .call_batch     = nf_tables_newset,
                .attr_count     = NFTA_SET_MAX,
                .policy         = nft_set_policy,
        },
@@ -2963,12 +3268,12 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
                .policy         = nft_set_policy,
        },
        [NFT_MSG_DELSET] = {
-               .call           = nf_tables_delset,
+               .call_batch     = nf_tables_delset,
                .attr_count     = NFTA_SET_MAX,
                .policy         = nft_set_policy,
        },
        [NFT_MSG_NEWSETELEM] = {
-               .call           = nf_tables_newsetelem,
+               .call_batch     = nf_tables_newsetelem,
                .attr_count     = NFTA_SET_ELEM_LIST_MAX,
                .policy         = nft_set_elem_list_policy,
        },
@@ -2978,12 +3283,282 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
                .policy         = nft_set_elem_list_policy,
        },
        [NFT_MSG_DELSETELEM] = {
-               .call           = nf_tables_delsetelem,
+               .call_batch     = nf_tables_delsetelem,
                .attr_count     = NFTA_SET_ELEM_LIST_MAX,
                .policy         = nft_set_elem_list_policy,
        },
 };
 
+static void nft_chain_commit_update(struct nft_trans *trans)
+{
+       struct nft_base_chain *basechain;
+
+       if (nft_trans_chain_name(trans)[0])
+               strcpy(trans->ctx.chain->name, nft_trans_chain_name(trans));
+
+       if (!(trans->ctx.chain->flags & NFT_BASE_CHAIN))
+               return;
+
+       basechain = nft_base_chain(trans->ctx.chain);
+       nft_chain_stats_replace(basechain, nft_trans_chain_stats(trans));
+
+       switch (nft_trans_chain_policy(trans)) {
+       case NF_DROP:
+       case NF_ACCEPT:
+               basechain->policy = nft_trans_chain_policy(trans);
+               break;
+       }
+}
+
+/* Schedule objects for release via rcu to make sure no packets are accesing
+ * removed rules.
+ */
+static void nf_tables_commit_release_rcu(struct rcu_head *rt)
+{
+       struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head);
+
+       switch (trans->msg_type) {
+       case NFT_MSG_DELTABLE:
+               nf_tables_table_destroy(&trans->ctx);
+               break;
+       case NFT_MSG_DELCHAIN:
+               nf_tables_chain_destroy(trans->ctx.chain);
+               break;
+       case NFT_MSG_DELRULE:
+               nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
+               break;
+       case NFT_MSG_DELSET:
+               nft_set_destroy(nft_trans_set(trans));
+               break;
+       }
+       kfree(trans);
+}
+
+static int nf_tables_commit(struct sk_buff *skb)
+{
+       struct net *net = sock_net(skb->sk);
+       struct nft_trans *trans, *next;
+       struct nft_set *set;
+
+       /* 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(trans, next, &net->nft.commit_list, list) {
+               switch (trans->msg_type) {
+               case NFT_MSG_NEWTABLE:
+                       if (nft_trans_table_update(trans)) {
+                               if (!nft_trans_table_enable(trans)) {
+                                       nf_tables_table_disable(trans->ctx.afi,
+                                                               trans->ctx.table);
+                                       trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
+                               }
+                       } else {
+                               trans->ctx.table->flags &= ~NFT_TABLE_INACTIVE;
+                       }
+                       nf_tables_table_notify(&trans->ctx, NFT_MSG_NEWTABLE);
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_DELTABLE:
+                       nf_tables_table_notify(&trans->ctx, NFT_MSG_DELTABLE);
+                       break;
+               case NFT_MSG_NEWCHAIN:
+                       if (nft_trans_chain_update(trans))
+                               nft_chain_commit_update(trans);
+                       else
+                               trans->ctx.chain->flags &= ~NFT_CHAIN_INACTIVE;
+
+                       nf_tables_chain_notify(&trans->ctx, NFT_MSG_NEWCHAIN);
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_DELCHAIN:
+                       nf_tables_chain_notify(&trans->ctx, NFT_MSG_DELCHAIN);
+                       if (!(trans->ctx.table->flags & NFT_TABLE_F_DORMANT) &&
+                           trans->ctx.chain->flags & NFT_BASE_CHAIN) {
+                               nf_unregister_hooks(nft_base_chain(trans->ctx.chain)->ops,
+                                                   trans->ctx.afi->nops);
+                       }
+                       break;
+               case NFT_MSG_NEWRULE:
+                       nft_rule_clear(trans->ctx.net, nft_trans_rule(trans));
+                       nf_tables_rule_notify(&trans->ctx,
+                                             nft_trans_rule(trans),
+                                             NFT_MSG_NEWRULE);
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_DELRULE:
+                       list_del_rcu(&nft_trans_rule(trans)->list);
+                       nf_tables_rule_notify(&trans->ctx,
+                                             nft_trans_rule(trans),
+                                             NFT_MSG_DELRULE);
+                       break;
+               case NFT_MSG_NEWSET:
+                       nft_trans_set(trans)->flags &= ~NFT_SET_INACTIVE;
+                       /* This avoids hitting -EBUSY when deleting the table
+                        * from the transaction.
+                        */
+                       if (nft_trans_set(trans)->flags & NFT_SET_ANONYMOUS &&
+                           !list_empty(&nft_trans_set(trans)->bindings))
+                               trans->ctx.table->use--;
+
+                       nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
+                                            NFT_MSG_NEWSET, GFP_KERNEL);
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_DELSET:
+                       nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
+                                            NFT_MSG_DELSET, GFP_KERNEL);
+                       break;
+               case NFT_MSG_NEWSETELEM:
+                       nf_tables_setelem_notify(&trans->ctx,
+                                                nft_trans_elem_set(trans),
+                                                &nft_trans_elem(trans),
+                                                NFT_MSG_NEWSETELEM, 0);
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_DELSETELEM:
+                       nf_tables_setelem_notify(&trans->ctx,
+                                                nft_trans_elem_set(trans),
+                                                &nft_trans_elem(trans),
+                                                NFT_MSG_DELSETELEM, 0);
+                       set = nft_trans_elem_set(trans);
+                       set->ops->get(set, &nft_trans_elem(trans));
+                       set->ops->remove(set, &nft_trans_elem(trans));
+                       nft_trans_destroy(trans);
+                       break;
+               }
+       }
+
+       list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
+               list_del(&trans->list);
+               trans->ctx.nla = NULL;
+               call_rcu(&trans->rcu_head, nf_tables_commit_release_rcu);
+       }
+
+       return 0;
+}
+
+/* Schedule objects for release via rcu to make sure no packets are accesing
+ * aborted rules.
+ */
+static void nf_tables_abort_release_rcu(struct rcu_head *rt)
+{
+       struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head);
+
+       switch (trans->msg_type) {
+       case NFT_MSG_NEWTABLE:
+               nf_tables_table_destroy(&trans->ctx);
+               break;
+       case NFT_MSG_NEWCHAIN:
+               nf_tables_chain_destroy(trans->ctx.chain);
+               break;
+       case NFT_MSG_NEWRULE:
+               nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
+               break;
+       case NFT_MSG_NEWSET:
+               nft_set_destroy(nft_trans_set(trans));
+               break;
+       }
+       kfree(trans);
+}
+
+static int nf_tables_abort(struct sk_buff *skb)
+{
+       struct net *net = sock_net(skb->sk);
+       struct nft_trans *trans, *next;
+       struct nft_set *set;
+
+       list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
+               switch (trans->msg_type) {
+               case NFT_MSG_NEWTABLE:
+                       if (nft_trans_table_update(trans)) {
+                               if (nft_trans_table_enable(trans)) {
+                                       nf_tables_table_disable(trans->ctx.afi,
+                                                               trans->ctx.table);
+                                       trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
+                               }
+                               nft_trans_destroy(trans);
+                       } else {
+                               list_del(&trans->ctx.table->list);
+                       }
+                       break;
+               case NFT_MSG_DELTABLE:
+                       list_add_tail(&trans->ctx.table->list,
+                                     &trans->ctx.afi->tables);
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_NEWCHAIN:
+                       if (nft_trans_chain_update(trans)) {
+                               if (nft_trans_chain_stats(trans))
+                                       free_percpu(nft_trans_chain_stats(trans));
+
+                               nft_trans_destroy(trans);
+                       } else {
+                               trans->ctx.table->use--;
+                               list_del(&trans->ctx.chain->list);
+                               if (!(trans->ctx.table->flags & NFT_TABLE_F_DORMANT) &&
+                                   trans->ctx.chain->flags & NFT_BASE_CHAIN) {
+                                       nf_unregister_hooks(nft_base_chain(trans->ctx.chain)->ops,
+                                                           trans->ctx.afi->nops);
+                               }
+                       }
+                       break;
+               case NFT_MSG_DELCHAIN:
+                       trans->ctx.table->use++;
+                       list_add_tail(&trans->ctx.chain->list,
+                                     &trans->ctx.table->chains);
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_NEWRULE:
+                       trans->ctx.chain->use--;
+                       list_del_rcu(&nft_trans_rule(trans)->list);
+                       break;
+               case NFT_MSG_DELRULE:
+                       trans->ctx.chain->use++;
+                       nft_rule_clear(trans->ctx.net, nft_trans_rule(trans));
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_NEWSET:
+                       trans->ctx.table->use--;
+                       list_del(&nft_trans_set(trans)->list);
+                       break;
+               case NFT_MSG_DELSET:
+                       trans->ctx.table->use++;
+                       list_add_tail(&nft_trans_set(trans)->list,
+                                     &trans->ctx.table->sets);
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_NEWSETELEM:
+                       nft_trans_elem_set(trans)->nelems--;
+                       set = nft_trans_elem_set(trans);
+                       set->ops->get(set, &nft_trans_elem(trans));
+                       set->ops->remove(set, &nft_trans_elem(trans));
+                       nft_trans_destroy(trans);
+                       break;
+               case NFT_MSG_DELSETELEM:
+                       nft_trans_elem_set(trans)->nelems++;
+                       nft_trans_destroy(trans);
+                       break;
+               }
+       }
+
+       list_for_each_entry_safe_reverse(trans, next,
+                                        &net->nft.commit_list, list) {
+               list_del(&trans->list);
+               trans->ctx.nla = NULL;
+               call_rcu(&trans->rcu_head, nf_tables_abort_release_rcu);
+       }
+
+       return 0;
+}
+
 static const struct nfnetlink_subsystem nf_tables_subsys = {
        .name           = "nf_tables",
        .subsys_id      = NFNL_SUBSYS_NFTABLES,
index 23ef77c60fffc60a1678f79dfbb769fe9be6a274..c138b8fbe280af6886693421a7fe8d9a288156cf 100644 (file)
@@ -399,19 +399,17 @@ static void nfnetlink_rcv(struct sk_buff *skb)
 }
 
 #ifdef CONFIG_MODULES
-static void nfnetlink_bind(int group)
+static int nfnetlink_bind(int group)
 {
        const struct nfnetlink_subsystem *ss;
        int type = nfnl_group2type[group];
 
        rcu_read_lock();
        ss = nfnetlink_get_subsys(type);
-       if (!ss) {
-               rcu_read_unlock();
-               request_module("nfnetlink-subsys-%d", type);
-               return;
-       }
        rcu_read_unlock();
+       if (!ss)
+               request_module("nfnetlink-subsys-%d", type);
+       return 0;
 }
 #endif
 
index c7b6d466a66247c3fa18b9a9a6f3e174a18d40da..2baa125c2e8dbcee194f8e8d8c98d6574a5b67b8 100644 (file)
@@ -32,18 +32,24 @@ static LIST_HEAD(nfnl_acct_list);
 struct nf_acct {
        atomic64_t              pkts;
        atomic64_t              bytes;
+       unsigned long           flags;
        struct list_head        head;
        atomic_t                refcnt;
        char                    name[NFACCT_NAME_MAX];
        struct rcu_head         rcu_head;
+       char                    data[0];
 };
 
+#define NFACCT_F_QUOTA (NFACCT_F_QUOTA_PKTS | NFACCT_F_QUOTA_BYTES)
+
 static int
 nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb,
             const struct nlmsghdr *nlh, const struct nlattr * const tb[])
 {
        struct nf_acct *nfacct, *matching = NULL;
        char *acct_name;
+       unsigned int size = 0;
+       u32 flags = 0;
 
        if (!tb[NFACCT_NAME])
                return -EINVAL;
@@ -68,15 +74,38 @@ nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb,
                        /* reset counters if you request a replacement. */
                        atomic64_set(&matching->pkts, 0);
                        atomic64_set(&matching->bytes, 0);
+                       smp_mb__before_atomic();
+                       /* reset overquota flag if quota is enabled. */
+                       if ((matching->flags & NFACCT_F_QUOTA))
+                               clear_bit(NFACCT_F_OVERQUOTA, &matching->flags);
                        return 0;
                }
                return -EBUSY;
        }
 
-       nfacct = kzalloc(sizeof(struct nf_acct), GFP_KERNEL);
+       if (tb[NFACCT_FLAGS]) {
+               flags = ntohl(nla_get_be32(tb[NFACCT_FLAGS]));
+               if (flags & ~NFACCT_F_QUOTA)
+                       return -EOPNOTSUPP;
+               if ((flags & NFACCT_F_QUOTA) == NFACCT_F_QUOTA)
+                       return -EINVAL;
+               if (flags & NFACCT_F_OVERQUOTA)
+                       return -EINVAL;
+
+               size += sizeof(u64);
+       }
+
+       nfacct = kzalloc(sizeof(struct nf_acct) + size, GFP_KERNEL);
        if (nfacct == NULL)
                return -ENOMEM;
 
+       if (flags & NFACCT_F_QUOTA) {
+               u64 *quota = (u64 *)nfacct->data;
+
+               *quota = be64_to_cpu(nla_get_be64(tb[NFACCT_QUOTA]));
+               nfacct->flags = flags;
+       }
+
        strncpy(nfacct->name, nla_data(tb[NFACCT_NAME]), NFACCT_NAME_MAX);
 
        if (tb[NFACCT_BYTES]) {
@@ -117,6 +146,9 @@ nfnl_acct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
        if (type == NFNL_MSG_ACCT_GET_CTRZERO) {
                pkts = atomic64_xchg(&acct->pkts, 0);
                bytes = atomic64_xchg(&acct->bytes, 0);
+               smp_mb__before_atomic();
+               if (acct->flags & NFACCT_F_QUOTA)
+                       clear_bit(NFACCT_F_OVERQUOTA, &acct->flags);
        } else {
                pkts = atomic64_read(&acct->pkts);
                bytes = atomic64_read(&acct->bytes);
@@ -125,7 +157,13 @@ nfnl_acct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
            nla_put_be64(skb, NFACCT_BYTES, cpu_to_be64(bytes)) ||
            nla_put_be32(skb, NFACCT_USE, htonl(atomic_read(&acct->refcnt))))
                goto nla_put_failure;
+       if (acct->flags & NFACCT_F_QUOTA) {
+               u64 *quota = (u64 *)acct->data;
 
+               if (nla_put_be32(skb, NFACCT_FLAGS, htonl(acct->flags)) ||
+                   nla_put_be64(skb, NFACCT_QUOTA, cpu_to_be64(*quota)))
+                       goto nla_put_failure;
+       }
        nlmsg_end(skb, nlh);
        return skb->len;
 
@@ -270,6 +308,8 @@ static const struct nla_policy nfnl_acct_policy[NFACCT_MAX+1] = {
        [NFACCT_NAME] = { .type = NLA_NUL_STRING, .len = NFACCT_NAME_MAX-1 },
        [NFACCT_BYTES] = { .type = NLA_U64 },
        [NFACCT_PKTS] = { .type = NLA_U64 },
+       [NFACCT_FLAGS] = { .type = NLA_U32 },
+       [NFACCT_QUOTA] = { .type = NLA_U64 },
 };
 
 static const struct nfnl_callback nfnl_acct_cb[NFNL_MSG_ACCT_MAX] = {
@@ -336,6 +376,50 @@ void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct)
 }
 EXPORT_SYMBOL_GPL(nfnl_acct_update);
 
+static void nfnl_overquota_report(struct nf_acct *nfacct)
+{
+       int ret;
+       struct sk_buff *skb;
+
+       skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
+       if (skb == NULL)
+               return;
+
+       ret = nfnl_acct_fill_info(skb, 0, 0, NFNL_MSG_ACCT_OVERQUOTA, 0,
+                                 nfacct);
+       if (ret <= 0) {
+               kfree_skb(skb);
+               return;
+       }
+       netlink_broadcast(init_net.nfnl, skb, 0, NFNLGRP_ACCT_QUOTA,
+                         GFP_ATOMIC);
+}
+
+int nfnl_acct_overquota(const struct sk_buff *skb, struct nf_acct *nfacct)
+{
+       u64 now;
+       u64 *quota;
+       int ret = NFACCT_UNDERQUOTA;
+
+       /* no place here if we don't have a quota */
+       if (!(nfacct->flags & NFACCT_F_QUOTA))
+               return NFACCT_NO_QUOTA;
+
+       quota = (u64 *)nfacct->data;
+       now = (nfacct->flags & NFACCT_F_QUOTA_PKTS) ?
+              atomic64_read(&nfacct->pkts) : atomic64_read(&nfacct->bytes);
+
+       ret = now > *quota;
+
+       if (now >= *quota &&
+           !test_and_set_bit(NFACCT_F_OVERQUOTA, &nfacct->flags)) {
+               nfnl_overquota_report(nfacct);
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(nfnl_acct_overquota);
+
 static int __init nfnl_acct_init(void)
 {
        int ret;
index bd0d41e693416167b4f149f64117e440a5134496..cc5603016242ea8e1f5cdce1d633e3a2687276ae 100644 (file)
@@ -215,22 +215,14 @@ static void nft_ct_l3proto_module_put(uint8_t family)
                nf_ct_l3proto_module_put(family);
 }
 
-static int nft_ct_init_validate_get(const struct nft_expr *expr,
-                                   const struct nlattr * const tb[])
+static int nft_ct_get_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_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;
-               }
-       }
-
+       priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
        switch (priv->key) {
        case NFT_CT_STATE:
        case NFT_CT_DIRECTION:
@@ -262,55 +254,55 @@ static int nft_ct_init_validate_get(const struct nft_expr *expr,
                return -EOPNOTSUPP;
        }
 
-       return 0;
-}
-
-static int nft_ct_init_validate_set(uint32_t key)
-{
-       switch (key) {
-       case NFT_CT_MARK:
-               break;
-       default:
-               return -EOPNOTSUPP;
+       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;
+               }
        }
 
+       priv->dreg = ntohl(nla_get_be32(tb[NFTA_CT_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;
+
+       err = nft_ct_l3proto_try_module_get(ctx->afi->family);
+       if (err < 0)
+               return err;
+
        return 0;
 }
 
-static int nft_ct_init(const struct nft_ctx *ctx,
-                      const struct nft_expr *expr,
-                      const struct nlattr * const tb[])
+static int nft_ct_set_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;
 
        priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
-
-       if (tb[NFTA_CT_DREG]) {
-               err = nft_ct_init_validate_get(expr, tb);
-               if (err < 0)
-                       return err;
-
-               priv->dreg = ntohl(nla_get_be32(tb[NFTA_CT_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;
-       } else {
-               err = nft_ct_init_validate_set(priv->key);
-               if (err < 0)
-                       return err;
-
-               priv->sreg = ntohl(nla_get_be32(tb[NFTA_CT_SREG]));
-               err = nft_validate_input_register(priv->sreg);
-               if (err < 0)
-                       return err;
+       switch (priv->key) {
+#ifdef CONFIG_NF_CONNTRACK_MARK
+       case NFT_CT_MARK:
+               break;
+#endif
+       default:
+               return -EOPNOTSUPP;
        }
 
+       priv->sreg = ntohl(nla_get_be32(tb[NFTA_CT_SREG]));
+       err = nft_validate_input_register(priv->sreg);
+       if (err < 0)
+               return err;
+
        err = nft_ct_l3proto_try_module_get(ctx->afi->family);
        if (err < 0)
                return err;
@@ -370,7 +362,7 @@ static const struct nft_expr_ops nft_ct_get_ops = {
        .type           = &nft_ct_type,
        .size           = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
        .eval           = nft_ct_get_eval,
-       .init           = nft_ct_init,
+       .init           = nft_ct_get_init,
        .destroy        = nft_ct_destroy,
        .dump           = nft_ct_get_dump,
 };
@@ -379,7 +371,7 @@ static const struct nft_expr_ops nft_ct_set_ops = {
        .type           = &nft_ct_type,
        .size           = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
        .eval           = nft_ct_set_eval,
-       .init           = nft_ct_init,
+       .init           = nft_ct_set_init,
        .destroy        = nft_ct_destroy,
        .dump           = nft_ct_set_dump,
 };
index 3b1ad876d6b028f987ccf8e78ceb9639d767e6bf..4080ed6a072bf7c4bb414e19a38f9a663557198a 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/list.h>
+#include <linux/log2.h>
 #include <linux/jhash.h>
 #include <linux/netlink.h>
 #include <linux/vmalloc.h>
@@ -19,7 +20,7 @@
 #include <linux/netfilter/nf_tables.h>
 #include <net/netfilter/nf_tables.h>
 
-#define NFT_HASH_MIN_SIZE      4
+#define NFT_HASH_MIN_SIZE      4UL
 
 struct nft_hash {
        struct nft_hash_table __rcu     *tbl;
@@ -27,7 +28,6 @@ struct nft_hash {
 
 struct nft_hash_table {
        unsigned int                    size;
-       unsigned int                    elements;
        struct nft_hash_elem __rcu      *buckets[];
 };
 
@@ -76,10 +76,12 @@ static bool nft_hash_lookup(const struct nft_set *set,
 
 static void nft_hash_tbl_free(const struct nft_hash_table *tbl)
 {
-       if (is_vmalloc_addr(tbl))
-               vfree(tbl);
-       else
-               kfree(tbl);
+       kvfree(tbl);
+}
+
+static unsigned int nft_hash_tbl_size(unsigned int nelem)
+{
+       return max(roundup_pow_of_two(nelem * 4 / 3), NFT_HASH_MIN_SIZE);
 }
 
 static struct nft_hash_table *nft_hash_tbl_alloc(unsigned int nbuckets)
@@ -161,7 +163,6 @@ static int nft_hash_tbl_expand(const struct nft_set *set, struct nft_hash *priv)
                        break;
                }
        }
-       ntbl->elements = tbl->elements;
 
        /* Publish new table */
        rcu_assign_pointer(priv->tbl, ntbl);
@@ -201,7 +202,6 @@ static int nft_hash_tbl_shrink(const struct nft_set *set, struct nft_hash *priv)
                        ;
                RCU_INIT_POINTER(*pprev, tbl->buckets[i + ntbl->size]);
        }
-       ntbl->elements = tbl->elements;
 
        /* Publish new table */
        rcu_assign_pointer(priv->tbl, ntbl);
@@ -237,10 +237,9 @@ static int nft_hash_insert(const struct nft_set *set,
        h = nft_hash_data(&he->key, tbl->size, set->klen);
        RCU_INIT_POINTER(he->next, tbl->buckets[h]);
        rcu_assign_pointer(tbl->buckets[h], he);
-       tbl->elements++;
 
        /* Expand table when exceeding 75% load */
-       if (tbl->elements > tbl->size / 4 * 3)
+       if (set->nelems + 1 > tbl->size / 4 * 3)
                nft_hash_tbl_expand(set, priv);
 
        return 0;
@@ -268,10 +267,9 @@ static void nft_hash_remove(const struct nft_set *set,
        RCU_INIT_POINTER(*pprev, he->next);
        synchronize_rcu();
        kfree(he);
-       tbl->elements--;
 
        /* Shrink table beneath 30% load */
-       if (tbl->elements < tbl->size * 3 / 10 &&
+       if (set->nelems - 1 < tbl->size * 3 / 10 &&
            tbl->size > NFT_HASH_MIN_SIZE)
                nft_hash_tbl_shrink(set, priv);
 }
@@ -335,17 +333,23 @@ static unsigned int nft_hash_privsize(const struct nlattr * const nla[])
 }
 
 static int nft_hash_init(const struct nft_set *set,
+                        const struct nft_set_desc *desc,
                         const struct nlattr * const tb[])
 {
        struct nft_hash *priv = nft_set_priv(set);
        struct nft_hash_table *tbl;
+       unsigned int size;
 
        if (unlikely(!nft_hash_rnd_initted)) {
                get_random_bytes(&nft_hash_rnd, 4);
                nft_hash_rnd_initted = true;
        }
 
-       tbl = nft_hash_tbl_alloc(NFT_HASH_MIN_SIZE);
+       size = NFT_HASH_MIN_SIZE;
+       if (desc->size)
+               size = nft_hash_tbl_size(desc->size);
+
+       tbl = nft_hash_tbl_alloc(size);
        if (tbl == NULL)
                return -ENOMEM;
        RCU_INIT_POINTER(priv->tbl, tbl);
@@ -369,8 +373,37 @@ static void nft_hash_destroy(const struct nft_set *set)
        kfree(tbl);
 }
 
+static bool nft_hash_estimate(const struct nft_set_desc *desc, u32 features,
+                             struct nft_set_estimate *est)
+{
+       unsigned int esize;
+
+       esize = sizeof(struct nft_hash_elem);
+       if (features & NFT_SET_MAP)
+               esize += FIELD_SIZEOF(struct nft_hash_elem, data[0]);
+
+       if (desc->size) {
+               est->size = sizeof(struct nft_hash) +
+                           nft_hash_tbl_size(desc->size) *
+                           sizeof(struct nft_hash_elem *) +
+                           desc->size * esize;
+       } else {
+               /* Resizing happens when the load drops below 30% or goes
+                * above 75%. The average of 52.5% load (approximated by 50%)
+                * is used for the size estimation of the hash buckets,
+                * meaning we calculate two buckets per element.
+                */
+               est->size = esize + 2 * sizeof(struct nft_hash_elem *);
+       }
+
+       est->class = NFT_SET_CLASS_O_1;
+
+       return true;
+}
+
 static struct nft_set_ops nft_hash_ops __read_mostly = {
        .privsize       = nft_hash_privsize,
+       .estimate       = nft_hash_estimate,
        .init           = nft_hash_init,
        .destroy        = nft_hash_destroy,
        .get            = nft_hash_get,
index 7fd2bea8aa239f347dc461c7bc45869dac405573..6404a726d17b78fc6db6f411216195e68db63950 100644 (file)
@@ -56,8 +56,14 @@ static int nft_lookup_init(const struct nft_ctx *ctx,
                return -EINVAL;
 
        set = nf_tables_set_lookup(ctx->table, tb[NFTA_LOOKUP_SET]);
-       if (IS_ERR(set))
-               return PTR_ERR(set);
+       if (IS_ERR(set)) {
+               if (tb[NFTA_LOOKUP_SET_ID]) {
+                       set = nf_tables_set_lookup_byid(ctx->net,
+                                                       tb[NFTA_LOOKUP_SET_ID]);
+               }
+               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);
index 425cf39af8907f1d0618b0904121073ea4dc116a..852b178c6ae7fa2f7dbd6f7887404033da45971f 100644 (file)
 #include <net/sock.h>
 #include <net/tcp_states.h> /* for TCP_TIME_WAIT */
 #include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nft_meta.h>
 
-struct nft_meta {
-       enum nft_meta_keys      key:8;
-       union {
-               enum nft_registers      dreg:8;
-               enum nft_registers      sreg:8;
-       };
-};
-
-static void nft_meta_get_eval(const struct nft_expr *expr,
-                             struct nft_data data[NFT_REG_MAX + 1],
-                             const struct nft_pktinfo *pkt)
+void nft_meta_get_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;
@@ -140,10 +133,11 @@ static void nft_meta_get_eval(const struct nft_expr *expr,
 err:
        data[NFT_REG_VERDICT].verdict = NFT_BREAK;
 }
+EXPORT_SYMBOL_GPL(nft_meta_get_eval);
 
-static void nft_meta_set_eval(const struct nft_expr *expr,
-                             struct nft_data data[NFT_REG_MAX + 1],
-                             const struct nft_pktinfo *pkt)
+void nft_meta_set_eval(const struct nft_expr *expr,
+                      struct nft_data data[NFT_REG_MAX + 1],
+                      const struct nft_pktinfo *pkt)
 {
        const struct nft_meta *meta = nft_expr_priv(expr);
        struct sk_buff *skb = pkt->skb;
@@ -163,28 +157,24 @@ static void nft_meta_set_eval(const struct nft_expr *expr,
                WARN_ON(1);
        }
 }
+EXPORT_SYMBOL_GPL(nft_meta_set_eval);
 
-static const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = {
+const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = {
        [NFTA_META_DREG]        = { .type = NLA_U32 },
        [NFTA_META_KEY]         = { .type = NLA_U32 },
        [NFTA_META_SREG]        = { .type = NLA_U32 },
 };
+EXPORT_SYMBOL_GPL(nft_meta_policy);
 
-static int nft_meta_init_validate_set(uint32_t key)
+int nft_meta_get_init(const struct nft_ctx *ctx,
+                     const struct nft_expr *expr,
+                     const struct nlattr * const tb[])
 {
-       switch (key) {
-       case NFT_META_MARK:
-       case NFT_META_PRIORITY:
-       case NFT_META_NFTRACE:
-               return 0;
-       default:
-               return -EOPNOTSUPP;
-       }
-}
+       struct nft_meta *priv = nft_expr_priv(expr);
+       int err;
 
-static int nft_meta_init_validate_get(uint32_t key)
-{
-       switch (key) {
+       priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
+       switch (priv->key) {
        case NFT_META_LEN:
        case NFT_META_PROTOCOL:
        case NFT_META_NFPROTO:
@@ -205,39 +195,41 @@ static int nft_meta_init_validate_get(uint32_t key)
 #ifdef CONFIG_NETWORK_SECMARK
        case NFT_META_SECMARK:
 #endif
-               return 0;
+               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;
+
+       err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE);
+       if (err < 0)
+               return err;
+
+       return 0;
 }
+EXPORT_SYMBOL_GPL(nft_meta_get_init);
 
-static int nft_meta_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
-                        const struct nlattr * const tb[])
+int nft_meta_set_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;
 
        priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
-
-       if (tb[NFTA_META_DREG]) {
-               err = nft_meta_init_validate_get(priv->key);
-               if (err < 0)
-                       return err;
-
-               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);
+       switch (priv->key) {
+       case NFT_META_MARK:
+       case NFT_META_PRIORITY:
+       case NFT_META_NFTRACE:
+               break;
+       default:
+               return -EOPNOTSUPP;
        }
 
-       err = nft_meta_init_validate_set(priv->key);
-       if (err < 0)
-               return err;
-
        priv->sreg = ntohl(nla_get_be32(tb[NFTA_META_SREG]));
        err = nft_validate_input_register(priv->sreg);
        if (err < 0)
@@ -245,9 +237,10 @@ static int nft_meta_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(nft_meta_set_init);
 
-static int nft_meta_get_dump(struct sk_buff *skb,
-                            const struct nft_expr *expr)
+int nft_meta_get_dump(struct sk_buff *skb,
+                     const struct nft_expr *expr)
 {
        const struct nft_meta *priv = nft_expr_priv(expr);
 
@@ -260,9 +253,10 @@ static int nft_meta_get_dump(struct sk_buff *skb,
 nla_put_failure:
        return -1;
 }
+EXPORT_SYMBOL_GPL(nft_meta_get_dump);
 
-static int nft_meta_set_dump(struct sk_buff *skb,
-                            const struct nft_expr *expr)
+int nft_meta_set_dump(struct sk_buff *skb,
+                     const struct nft_expr *expr)
 {
        const struct nft_meta *priv = nft_expr_priv(expr);
 
@@ -276,13 +270,14 @@ static int nft_meta_set_dump(struct sk_buff *skb,
 nla_put_failure:
        return -1;
 }
+EXPORT_SYMBOL_GPL(nft_meta_set_dump);
 
 static struct nft_expr_type nft_meta_type;
 static const struct nft_expr_ops nft_meta_get_ops = {
        .type           = &nft_meta_type,
        .size           = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
        .eval           = nft_meta_get_eval,
-       .init           = nft_meta_init,
+       .init           = nft_meta_get_init,
        .dump           = nft_meta_get_dump,
 };
 
@@ -290,7 +285,7 @@ static const struct nft_expr_ops nft_meta_set_ops = {
        .type           = &nft_meta_type,
        .size           = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
        .eval           = nft_meta_set_eval,
-       .init           = nft_meta_init,
+       .init           = nft_meta_set_init,
        .dump           = nft_meta_set_dump,
 };
 
index e21d69d13506b95946820f24641fe7e48d885866..e1836ff881994dc08e957d414c6e14a0fbbff919 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/netfilter/nf_tables.h>
 #include <net/netfilter/nf_tables.h>
 
+static DEFINE_SPINLOCK(nft_rbtree_lock);
+
 struct nft_rbtree {
        struct rb_root          root;
 };
@@ -38,6 +40,7 @@ static bool nft_rbtree_lookup(const struct nft_set *set,
        const struct rb_node *parent = priv->root.rb_node;
        int d;
 
+       spin_lock_bh(&nft_rbtree_lock);
        while (parent != NULL) {
                rbe = rb_entry(parent, struct nft_rbtree_elem, node);
 
@@ -53,6 +56,8 @@ static bool nft_rbtree_lookup(const struct nft_set *set,
                                goto out;
                        if (set->flags & NFT_SET_MAP)
                                nft_data_copy(data, rbe->data);
+
+                       spin_unlock_bh(&nft_rbtree_lock);
                        return true;
                }
        }
@@ -62,6 +67,7 @@ static bool nft_rbtree_lookup(const struct nft_set *set,
                goto found;
        }
 out:
+       spin_unlock_bh(&nft_rbtree_lock);
        return false;
 }
 
@@ -124,9 +130,12 @@ static int nft_rbtree_insert(const struct nft_set *set,
            !(rbe->flags & NFT_SET_ELEM_INTERVAL_END))
                nft_data_copy(rbe->data, &elem->data);
 
+       spin_lock_bh(&nft_rbtree_lock);
        err = __nft_rbtree_insert(set, rbe);
        if (err < 0)
                kfree(rbe);
+
+       spin_unlock_bh(&nft_rbtree_lock);
        return err;
 }
 
@@ -136,7 +145,9 @@ static void nft_rbtree_remove(const struct nft_set *set,
        struct nft_rbtree *priv = nft_set_priv(set);
        struct nft_rbtree_elem *rbe = elem->cookie;
 
+       spin_lock_bh(&nft_rbtree_lock);
        rb_erase(&rbe->node, &priv->root);
+       spin_unlock_bh(&nft_rbtree_lock);
        kfree(rbe);
 }
 
@@ -147,6 +158,7 @@ static int nft_rbtree_get(const struct nft_set *set, struct nft_set_elem *elem)
        struct nft_rbtree_elem *rbe;
        int d;
 
+       spin_lock_bh(&nft_rbtree_lock);
        while (parent != NULL) {
                rbe = rb_entry(parent, struct nft_rbtree_elem, node);
 
@@ -161,9 +173,11 @@ static int nft_rbtree_get(const struct nft_set *set, struct nft_set_elem *elem)
                            !(rbe->flags & NFT_SET_ELEM_INTERVAL_END))
                                nft_data_copy(&elem->data, rbe->data);
                        elem->flags = rbe->flags;
+                       spin_unlock_bh(&nft_rbtree_lock);
                        return 0;
                }
        }
+       spin_unlock_bh(&nft_rbtree_lock);
        return -ENOENT;
 }
 
@@ -176,6 +190,7 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx,
        struct nft_set_elem elem;
        struct rb_node *node;
 
+       spin_lock_bh(&nft_rbtree_lock);
        for (node = rb_first(&priv->root); node != NULL; node = rb_next(node)) {
                if (iter->count < iter->skip)
                        goto cont;
@@ -188,11 +203,14 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx,
                elem.flags = rbe->flags;
 
                iter->err = iter->fn(ctx, set, iter, &elem);
-               if (iter->err < 0)
+               if (iter->err < 0) {
+                       spin_unlock_bh(&nft_rbtree_lock);
                        return;
+               }
 cont:
                iter->count++;
        }
+       spin_unlock_bh(&nft_rbtree_lock);
 }
 
 static unsigned int nft_rbtree_privsize(const struct nlattr * const nla[])
@@ -201,6 +219,7 @@ static unsigned int nft_rbtree_privsize(const struct nlattr * const nla[])
 }
 
 static int nft_rbtree_init(const struct nft_set *set,
+                          const struct nft_set_desc *desc,
                           const struct nlattr * const nla[])
 {
        struct nft_rbtree *priv = nft_set_priv(set);
@@ -215,15 +234,37 @@ static void nft_rbtree_destroy(const struct nft_set *set)
        struct nft_rbtree_elem *rbe;
        struct rb_node *node;
 
+       spin_lock_bh(&nft_rbtree_lock);
        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);
        }
+       spin_unlock_bh(&nft_rbtree_lock);
+}
+
+static bool nft_rbtree_estimate(const struct nft_set_desc *desc, u32 features,
+                               struct nft_set_estimate *est)
+{
+       unsigned int nsize;
+
+       nsize = sizeof(struct nft_rbtree_elem);
+       if (features & NFT_SET_MAP)
+               nsize += FIELD_SIZEOF(struct nft_rbtree_elem, data[0]);
+
+       if (desc->size)
+               est->size = sizeof(struct nft_rbtree) + desc->size * nsize;
+       else
+               est->size = nsize;
+
+       est->class = NFT_SET_CLASS_O_LOG_N;
+
+       return true;
 }
 
 static struct nft_set_ops nft_rbtree_ops __read_mostly = {
        .privsize       = nft_rbtree_privsize,
+       .estimate       = nft_rbtree_estimate,
        .init           = nft_rbtree_init,
        .destroy        = nft_rbtree_destroy,
        .insert         = nft_rbtree_insert,
index 12d4da8e6c7728ed6bcc723f5b583fbfd0dc895c..bbffdbdaf6031bef784042c064dded271d6a406f 100644 (file)
@@ -23,10 +23,11 @@ MODULE_ALIAS("ip6t_bpf");
 static int bpf_mt_check(const struct xt_mtchk_param *par)
 {
        struct xt_bpf_info *info = par->matchinfo;
-       struct sock_fprog program;
+       struct sock_fprog_kern program;
 
        program.len = info->bpf_program_num_elem;
-       program.filter = (struct sock_filter __user *) info->bpf_program;
+       program.filter = info->bpf_program;
+
        if (sk_unattached_filter_create(&info->filter, &program)) {
                pr_info("bpf: check failed: parse error\n");
                return -EINVAL;
index b3be0ef21f198ca8c7b025e1a775af9bd662d20e..8c646ed9c921bca1fbf507c1aa97c1dca60d8df1 100644 (file)
@@ -21,11 +21,14 @@ MODULE_ALIAS("ip6t_nfacct");
 
 static bool nfacct_mt(const struct sk_buff *skb, struct xt_action_param *par)
 {
+       int overquota;
        const struct xt_nfacct_match_info *info = par->targinfo;
 
        nfnl_acct_update(skb, info->nfacct);
 
-       return true;
+       overquota = nfnl_acct_overquota(skb, info->nfacct);
+
+       return overquota == NFACCT_UNDERQUOTA ? false : true;
 }
 
 static int
index 1e657cf715c478d9a9ec77411bf7536b14278d14..a9faae89f95533a53da4651efb2f737e3a0952b4 100644 (file)
@@ -313,10 +313,7 @@ recent_mt(const struct sk_buff *skb, struct xt_action_param *par)
 
 static void recent_table_free(void *addr)
 {
-       if (is_vmalloc_addr(addr))
-               vfree(addr);
-       else
-               kfree(addr);
+       kvfree(addr);
 }
 
 static int recent_mt_check(const struct xt_mtchk_param *par,
index f22757a29cd054e02e40372535a6407735a12ddc..15c731f03fa664a64f7bf3cdde36cf1a8e4150b6 100644 (file)
@@ -1206,7 +1206,8 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol,
        struct module *module = NULL;
        struct mutex *cb_mutex;
        struct netlink_sock *nlk;
-       void (*bind)(int group);
+       int (*bind)(int group);
+       void (*unbind)(int group);
        int err = 0;
 
        sock->state = SS_UNCONNECTED;
@@ -1232,6 +1233,7 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol,
                err = -EPROTONOSUPPORT;
        cb_mutex = nl_table[protocol].cb_mutex;
        bind = nl_table[protocol].bind;
+       unbind = nl_table[protocol].unbind;
        netlink_unlock_table();
 
        if (err < 0)
@@ -1248,6 +1250,7 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol,
        nlk = nlk_sk(sock->sk);
        nlk->module = module;
        nlk->netlink_bind = bind;
+       nlk->netlink_unbind = unbind;
 out:
        return err;
 
@@ -1301,6 +1304,7 @@ static int netlink_release(struct socket *sock)
                        kfree_rcu(old, rcu);
                        nl_table[sk->sk_protocol].module = NULL;
                        nl_table[sk->sk_protocol].bind = NULL;
+                       nl_table[sk->sk_protocol].unbind = NULL;
                        nl_table[sk->sk_protocol].flags = 0;
                        nl_table[sk->sk_protocol].registered = 0;
                }
@@ -1478,6 +1482,19 @@ static int netlink_realloc_groups(struct sock *sk)
        return err;
 }
 
+static void netlink_unbind(int group, long unsigned int groups,
+                          struct netlink_sock *nlk)
+{
+       int undo;
+
+       if (!nlk->netlink_unbind)
+               return;
+
+       for (undo = 0; undo < group; undo++)
+               if (test_bit(group, &groups))
+                       nlk->netlink_unbind(undo);
+}
+
 static int netlink_bind(struct socket *sock, struct sockaddr *addr,
                        int addr_len)
 {
@@ -1486,6 +1503,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr,
        struct netlink_sock *nlk = nlk_sk(sk);
        struct sockaddr_nl *nladdr = (struct sockaddr_nl *)addr;
        int err;
+       long unsigned int groups = nladdr->nl_groups;
 
        if (addr_len < sizeof(struct sockaddr_nl))
                return -EINVAL;
@@ -1494,7 +1512,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr,
                return -EINVAL;
 
        /* Only superuser is allowed to listen multicasts */
-       if (nladdr->nl_groups) {
+       if (groups) {
                if (!netlink_allowed(sock, NL_CFG_F_NONROOT_RECV))
                        return -EPERM;
                err = netlink_realloc_groups(sk);
@@ -1502,37 +1520,45 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr,
                        return err;
        }
 
-       if (nlk->portid) {
+       if (nlk->portid)
                if (nladdr->nl_pid != nlk->portid)
                        return -EINVAL;
-       } else {
+
+       if (nlk->netlink_bind && groups) {
+               int group;
+
+               for (group = 0; group < nlk->ngroups; group++) {
+                       if (!test_bit(group, &groups))
+                               continue;
+                       err = nlk->netlink_bind(group);
+                       if (!err)
+                               continue;
+                       netlink_unbind(group, groups, nlk);
+                       return err;
+               }
+       }
+
+       if (!nlk->portid) {
                err = nladdr->nl_pid ?
                        netlink_insert(sk, net, nladdr->nl_pid) :
                        netlink_autobind(sock);
-               if (err)
+               if (err) {
+                       netlink_unbind(nlk->ngroups - 1, groups, nlk);
                        return err;
+               }
        }
 
-       if (!nladdr->nl_groups && (nlk->groups == NULL || !(u32)nlk->groups[0]))
+       if (!groups && (nlk->groups == NULL || !(u32)nlk->groups[0]))
                return 0;
 
        netlink_table_grab();
        netlink_update_subscriptions(sk, nlk->subscriptions +
-                                        hweight32(nladdr->nl_groups) -
+                                        hweight32(groups) -
                                         hweight32(nlk->groups[0]));
-       nlk->groups[0] = (nlk->groups[0] & ~0xffffffffUL) | nladdr->nl_groups;
+       nlk->groups[0] = (nlk->groups[0] & ~0xffffffffUL) | groups;
        netlink_update_listeners(sk);
        netlink_table_ungrab();
 
-       if (nlk->netlink_bind && nlk->groups[0]) {
-               int i;
-
-               for (i = 0; i < nlk->ngroups; i++) {
-                       if (test_bit(i, nlk->groups))
-                               nlk->netlink_bind(i);
-               }
-       }
-
        return 0;
 }
 
@@ -2170,13 +2196,17 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname,
                        return err;
                if (!val || val - 1 >= nlk->ngroups)
                        return -EINVAL;
+               if (optname == NETLINK_ADD_MEMBERSHIP && nlk->netlink_bind) {
+                       err = nlk->netlink_bind(val);
+                       if (err)
+                               return err;
+               }
                netlink_table_grab();
                netlink_update_socket_mc(nlk, val,
                                         optname == NETLINK_ADD_MEMBERSHIP);
                netlink_table_ungrab();
-
-               if (nlk->netlink_bind)
-                       nlk->netlink_bind(val);
+               if (optname == NETLINK_DROP_MEMBERSHIP && nlk->netlink_unbind)
+                       nlk->netlink_unbind(val);
 
                err = 0;
                break;
index ed13a790b00e1684215e04c6a68f71f9491cb3c9..0b59d441f5b6bcff8d03b08211e5a901c170dfe8 100644 (file)
@@ -38,7 +38,8 @@ struct netlink_sock {
        struct mutex            *cb_mutex;
        struct mutex            cb_def_mutex;
        void                    (*netlink_rcv)(struct sk_buff *skb);
-       void                    (*netlink_bind)(int group);
+       int                     (*netlink_bind)(int group);
+       void                    (*netlink_unbind)(int group);
        struct module           *module;
 #ifdef CONFIG_NETLINK_MMAP
        struct mutex            pg_vec_lock;
@@ -74,7 +75,8 @@ struct netlink_table {
        unsigned int            groups;
        struct mutex            *cb_mutex;
        struct module           *module;
-       void                    (*bind)(int group);
+       int                     (*bind)(int group);
+       void                    (*unbind)(int group);
        bool                    (*compare)(struct net *net, struct sock *sock);
        int                     registered;
 };
index a3ba3ca0ff9281dec15c0b4d42002394583c1d8d..76393f2f4b225713b9c85ee5aae722e109881080 100644 (file)
@@ -317,7 +317,7 @@ static void genl_unregister_mc_groups(struct genl_family *family)
        }
 }
 
-static int genl_validate_ops(struct genl_family *family)
+static int genl_validate_ops(const struct genl_family *family)
 {
        const struct genl_ops *ops = family->ops;
        unsigned int n_ops = family->n_ops;
@@ -337,10 +337,6 @@ static int genl_validate_ops(struct genl_family *family)
                                return -EINVAL;
        }
 
-       /* family is not registered yet, so no locking needed */
-       family->ops = ops;
-       family->n_ops = n_ops;
-
        return 0;
 }
 
index 3759add68b1b8af4b4eae50af6eb4a3cd98fd345..71ad7eefddd4dfa69319ca06b3595e1bebca459f 100644 (file)
@@ -71,6 +71,7 @@ static inline int digital_in_send_cmd(struct nfc_digital_dev *ddev,
 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_sensb_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_in_send_iso15693_inv_req(struct nfc_digital_dev *ddev, u8 rf_tech);
 
index e01e15dbf1abe1541aeb2643cf5521e8e1419e75..a6ce3c627e4e40ff4a6fcdb0bb0a2704dd0872b0 100644 (file)
@@ -22,6 +22,8 @@
 #define DIGITAL_PROTO_NFCA_RF_TECH \
        (NFC_PROTO_JEWEL_MASK | NFC_PROTO_MIFARE_MASK | NFC_PROTO_NFC_DEP_MASK)
 
+#define DIGITAL_PROTO_NFCB_RF_TECH     NFC_PROTO_ISO14443_B_MASK
+
 #define DIGITAL_PROTO_NFCF_RF_TECH \
        (NFC_PROTO_FELICA_MASK | NFC_PROTO_NFC_DEP_MASK)
 
@@ -345,6 +347,12 @@ int digital_target_found(struct nfc_digital_dev *ddev,
                add_crc = digital_skb_add_crc_a;
                break;
 
+       case NFC_PROTO_ISO14443_B:
+               framing = NFC_DIGITAL_FRAMING_NFCB_T4T;
+               check_crc = digital_skb_check_crc_b;
+               add_crc = digital_skb_add_crc_b;
+               break;
+
        default:
                pr_err("Invalid protocol %d\n", protocol);
                return -EINVAL;
@@ -378,6 +386,8 @@ int digital_target_found(struct nfc_digital_dev *ddev,
 
 void digital_poll_next_tech(struct nfc_digital_dev *ddev)
 {
+       u8 rand_mod;
+
        digital_switch_rf(ddev, 0);
 
        mutex_lock(&ddev->poll_lock);
@@ -387,8 +397,8 @@ void digital_poll_next_tech(struct nfc_digital_dev *ddev)
                return;
        }
 
-       ddev->poll_tech_index = (ddev->poll_tech_index + 1) %
-                               ddev->poll_tech_count;
+       get_random_bytes(&rand_mod, sizeof(rand_mod));
+       ddev->poll_tech_index = rand_mod % ddev->poll_tech_count;
 
        mutex_unlock(&ddev->poll_lock);
 
@@ -475,6 +485,10 @@ static int digital_start_poll(struct nfc_dev *nfc_dev, __u32 im_protocols,
                digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_106A,
                                      digital_in_send_sens_req);
 
+       if (matching_im_protocols & DIGITAL_PROTO_NFCB_RF_TECH)
+               digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_106B,
+                                     digital_in_send_sensb_req);
+
        if (matching_im_protocols & DIGITAL_PROTO_NFCF_RF_TECH) {
                digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_212F,
                                      digital_in_send_sensf_req);
@@ -635,7 +649,8 @@ static void digital_in_send_complete(struct nfc_digital_dev *ddev, void *arg,
                goto done;
        }
 
-       if (ddev->curr_protocol == NFC_PROTO_ISO14443) {
+       if ((ddev->curr_protocol == NFC_PROTO_ISO14443) ||
+           (ddev->curr_protocol == NFC_PROTO_ISO14443_B)) {
                rc = digital_in_iso_dep_pull_sod(ddev, resp);
                if (rc)
                        goto done;
@@ -676,7 +691,8 @@ static int digital_in_send(struct nfc_dev *nfc_dev, struct nfc_target *target,
                goto exit;
        }
 
-       if (ddev->curr_protocol == NFC_PROTO_ISO14443) {
+       if ((ddev->curr_protocol == NFC_PROTO_ISO14443) ||
+           (ddev->curr_protocol == NFC_PROTO_ISO14443_B)) {
                rc = digital_in_iso_dep_push_sod(ddev, skb);
                if (rc)
                        goto exit;
@@ -747,6 +763,8 @@ struct nfc_digital_dev *nfc_digital_allocate_device(struct nfc_digital_ops *ops,
                ddev->protocols |= NFC_PROTO_ISO15693_MASK;
        if (supported_protocols & NFC_PROTO_ISO14443_MASK)
                ddev->protocols |= NFC_PROTO_ISO14443_MASK;
+       if (supported_protocols & NFC_PROTO_ISO14443_B_MASK)
+               ddev->protocols |= NFC_PROTO_ISO14443_B_MASK;
 
        ddev->tx_headroom = tx_headroom + DIGITAL_MAX_HEADER_LEN;
        ddev->tx_tailroom = tx_tailroom + DIGITAL_CRC_LEN;
index d4ed25ff723fd9ace06d03444d7fcc3f9b3b6b27..171cb9949ab58560bc3d1c997d963e2928cf5aa5 100644 (file)
@@ -224,9 +224,8 @@ int digital_in_send_atr_req(struct nfc_digital_dev *ddev,
 
        ddev->skb_add_crc(skb);
 
-       digital_in_send_cmd(ddev, skb, 500, digital_in_recv_atr_res, target);
-
-       return 0;
+       return digital_in_send_cmd(ddev, skb, 500, digital_in_recv_atr_res,
+                                  target);
 }
 
 static int digital_in_send_rtox(struct nfc_digital_dev *ddev,
index 278c3fed27e01f255374713ab2c0a545752671ee..c2c1c0189b7cbe931c279bc039df4d26801902d4 100644 (file)
 #define DIGITAL_MIFARE_READ_RES_LEN 16
 #define DIGITAL_MIFARE_ACK_RES 0x0A
 
+#define DIGITAL_CMD_SENSB_REQ                  0x05
+#define DIGITAL_SENSB_ADVANCED                 BIT(5)
+#define DIGITAL_SENSB_EXTENDED                 BIT(4)
+#define DIGITAL_SENSB_ALLB_REQ                 BIT(3)
+#define DIGITAL_SENSB_N(n)                     ((n) & 0x7)
+
+#define DIGITAL_CMD_SENSB_RES                  0x50
+
+#define DIGITAL_CMD_ATTRIB_REQ                 0x1D
+#define DIGITAL_ATTRIB_P1_TR0_DEFAULT          (0x0 << 6)
+#define DIGITAL_ATTRIB_P1_TR1_DEFAULT          (0x0 << 4)
+#define DIGITAL_ATTRIB_P1_SUPRESS_EOS          BIT(3)
+#define DIGITAL_ATTRIB_P1_SUPRESS_SOS          BIT(2)
+#define DIGITAL_ATTRIB_P2_LISTEN_POLL_1                (0x0 << 6)
+#define DIGITAL_ATTRIB_P2_POLL_LISTEN_1                (0x0 << 4)
+#define DIGITAL_ATTRIB_P2_MAX_FRAME_256                0x8
+#define DIGITAL_ATTRIB_P4_DID(n)               ((n) & 0xf)
+
 #define DIGITAL_CMD_SENSF_REQ  0x00
 #define DIGITAL_CMD_SENSF_RES  0x01
 
@@ -75,6 +93,7 @@ static const u8 digital_ats_fsc[] = {
 };
 
 #define DIGITAL_ATS_FSCI(t0) ((t0) & 0x0F)
+#define DIGITAL_SENSB_FSCI(pi2) (((pi2) & 0xF0) >> 4)
 #define DIGITAL_ATS_MAX_FSC  256
 
 #define DIGITAL_RATS_BYTE1 0xE0
@@ -92,6 +111,32 @@ struct digital_sel_req {
        u8 bcc;
 } __packed;
 
+struct digital_sensb_req {
+       u8 cmd;
+       u8 afi;
+       u8 param;
+} __packed;
+
+struct digital_sensb_res {
+       u8 cmd;
+       u8 nfcid0[4];
+       u8 app_data[4];
+       u8 proto_info[3];
+} __packed;
+
+struct digital_attrib_req {
+       u8 cmd;
+       u8 nfcid0[4];
+       u8 param1;
+       u8 param2;
+       u8 param3;
+       u8 param4;
+} __packed;
+
+struct digital_attrib_res {
+       u8 mbli_did;
+} __packed;
+
 struct digital_sensf_req {
        u8 cmd;
        u8 sc1;
@@ -531,6 +576,175 @@ int digital_in_recv_mifare_res(struct sk_buff *resp)
        return -EIO;
 }
 
+static void digital_in_recv_attrib_res(struct nfc_digital_dev *ddev, void *arg,
+                                      struct sk_buff *resp)
+{
+       struct nfc_target *target = arg;
+       struct digital_attrib_res *attrib_res;
+       int rc;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       if (resp->len < sizeof(*attrib_res)) {
+               PROTOCOL_ERR("12.6.2");
+               rc = -EIO;
+               goto exit;
+       }
+
+       attrib_res = (struct digital_attrib_res *)resp->data;
+
+       if (attrib_res->mbli_did & 0x0f) {
+               PROTOCOL_ERR("12.6.2.1");
+               rc = -EIO;
+               goto exit;
+       }
+
+       rc = digital_target_found(ddev, target, NFC_PROTO_ISO14443_B);
+
+exit:
+       dev_kfree_skb(resp);
+       kfree(target);
+
+       if (rc)
+               digital_poll_next_tech(ddev);
+}
+
+static int digital_in_send_attrib_req(struct nfc_digital_dev *ddev,
+                              struct nfc_target *target,
+                              struct digital_sensb_res *sensb_res)
+{
+       struct digital_attrib_req *attrib_req;
+       struct sk_buff *skb;
+       int rc;
+
+       skb = digital_skb_alloc(ddev, sizeof(*attrib_req));
+       if (!skb)
+               return -ENOMEM;
+
+       attrib_req = (struct digital_attrib_req *)skb_put(skb,
+                                                         sizeof(*attrib_req));
+
+       attrib_req->cmd = DIGITAL_CMD_ATTRIB_REQ;
+       memcpy(attrib_req->nfcid0, sensb_res->nfcid0,
+              sizeof(attrib_req->nfcid0));
+       attrib_req->param1 = DIGITAL_ATTRIB_P1_TR0_DEFAULT |
+                            DIGITAL_ATTRIB_P1_TR1_DEFAULT;
+       attrib_req->param2 = DIGITAL_ATTRIB_P2_LISTEN_POLL_1 |
+                            DIGITAL_ATTRIB_P2_POLL_LISTEN_1 |
+                            DIGITAL_ATTRIB_P2_MAX_FRAME_256;
+       attrib_req->param3 = sensb_res->proto_info[1] & 0x07;
+       attrib_req->param4 = DIGITAL_ATTRIB_P4_DID(0);
+
+       rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_attrib_res,
+                                target);
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
+}
+
+static void digital_in_recv_sensb_res(struct nfc_digital_dev *ddev, void *arg,
+                                     struct sk_buff *resp)
+{
+       struct nfc_target *target = NULL;
+       struct digital_sensb_res *sensb_res;
+       u8 fsci;
+       int rc;
+
+       if (IS_ERR(resp)) {
+               rc = PTR_ERR(resp);
+               resp = NULL;
+               goto exit;
+       }
+
+       if (resp->len != sizeof(*sensb_res)) {
+               PROTOCOL_ERR("5.6.2.1");
+               rc = -EIO;
+               goto exit;
+       }
+
+       sensb_res = (struct digital_sensb_res *)resp->data;
+
+       if (sensb_res->cmd != DIGITAL_CMD_SENSB_RES) {
+               PROTOCOL_ERR("5.6.2");
+               rc = -EIO;
+               goto exit;
+       }
+
+       if (!(sensb_res->proto_info[1] & BIT(0))) {
+               PROTOCOL_ERR("5.6.2.12");
+               rc = -EIO;
+               goto exit;
+       }
+
+       if (sensb_res->proto_info[1] & BIT(3)) {
+               PROTOCOL_ERR("5.6.2.16");
+               rc = -EIO;
+               goto exit;
+       }
+
+       fsci = DIGITAL_SENSB_FSCI(sensb_res->proto_info[1]);
+       if (fsci >= 8)
+               ddev->target_fsc = DIGITAL_ATS_MAX_FSC;
+       else
+               ddev->target_fsc = digital_ats_fsc[fsci];
+
+       target = kzalloc(sizeof(struct nfc_target), GFP_KERNEL);
+       if (!target) {
+               rc = -ENOMEM;
+               goto exit;
+       }
+
+       rc = digital_in_send_attrib_req(ddev, target, sensb_res);
+
+exit:
+       dev_kfree_skb(resp);
+
+       if (rc) {
+               kfree(target);
+               digital_poll_next_tech(ddev);
+       }
+}
+
+int digital_in_send_sensb_req(struct nfc_digital_dev *ddev, u8 rf_tech)
+{
+       struct digital_sensb_req *sensb_req;
+       struct sk_buff *skb;
+       int rc;
+
+       rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH,
+                                    NFC_DIGITAL_RF_TECH_106B);
+       if (rc)
+               return rc;
+
+       rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
+                                    NFC_DIGITAL_FRAMING_NFCB);
+       if (rc)
+               return rc;
+
+       skb = digital_skb_alloc(ddev, sizeof(*sensb_req));
+       if (!skb)
+               return -ENOMEM;
+
+       sensb_req = (struct digital_sensb_req *)skb_put(skb,
+                                                       sizeof(*sensb_req));
+
+       sensb_req->cmd = DIGITAL_CMD_SENSB_REQ;
+       sensb_req->afi = 0x00; /* All families and sub-families */
+       sensb_req->param = DIGITAL_SENSB_N(0);
+
+       rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_sensb_res,
+                                NULL);
+       if (rc)
+               kfree_skb(skb);
+
+       return rc;
+}
+
 static void digital_in_recv_sensf_res(struct nfc_digital_dev *ddev, void *arg,
                                   struct sk_buff *resp)
 {
@@ -877,6 +1091,18 @@ void digital_tg_recv_sens_req(struct nfc_digital_dev *ddev, void *arg,
        dev_kfree_skb(resp);
 }
 
+static void digital_tg_recv_atr_or_sensf_req(struct nfc_digital_dev *ddev,
+               void *arg, struct sk_buff *resp)
+{
+       if (!IS_ERR(resp) && (resp->len >= 2) &&
+                       (resp->data[1] == DIGITAL_CMD_SENSF_REQ))
+               digital_tg_recv_sensf_req(ddev, arg, resp);
+       else
+               digital_tg_recv_atr_req(ddev, arg, resp);
+
+       return;
+}
+
 static int digital_tg_send_sensf_res(struct nfc_digital_dev *ddev,
                              struct digital_sensf_req *sensf_req)
 {
@@ -887,7 +1113,7 @@ static int digital_tg_send_sensf_res(struct nfc_digital_dev *ddev,
 
        size = sizeof(struct digital_sensf_res);
 
-       if (sensf_req->rc != DIGITAL_SENSF_REQ_RC_NONE)
+       if (sensf_req->rc == DIGITAL_SENSF_REQ_RC_NONE)
                size -= sizeof(sensf_res->rd);
 
        skb = digital_skb_alloc(ddev, size);
@@ -922,7 +1148,7 @@ static int digital_tg_send_sensf_res(struct nfc_digital_dev *ddev,
                digital_skb_add_crc_f(skb);
 
        rc = digital_tg_send_cmd(ddev, skb, 300,
-                                digital_tg_recv_atr_req, NULL);
+                                digital_tg_recv_atr_or_sensf_req, NULL);
        if (rc)
                kfree_skb(skb);
 
index a9f4d2e62d8de267a94a689fe3fab53a1a79339d..677d24bb70f8af24569ef574404ba4b1b9e1c9f0 100644 (file)
@@ -26,6 +26,8 @@
 
 #include "hci.h"
 
+#define MAX_FWI 4949
+
 static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
                               const u8 *param, size_t param_len,
                               data_exchange_cb_t cb, void *cb_context)
@@ -37,7 +39,7 @@ static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
         * for all commands?
         */
        return nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_COMMAND, cmd,
-                                     param, param_len, cb, cb_context, 3000);
+                                     param, param_len, cb, cb_context, MAX_FWI);
 }
 
 /*
@@ -82,7 +84,7 @@ static int nfc_hci_execute_cmd(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
                                                    NFC_HCI_HCP_COMMAND, cmd,
                                                    param, param_len,
                                                    nfc_hci_execute_cb, &hcp_ew,
-                                                   3000);
+                                                   MAX_FWI);
        if (hcp_ew.exec_result < 0)
                return hcp_ew.exec_result;
 
index d45b638e77c78ec25b8228eacf2ea0ac754ad8d7..47403705197e85930d9f3e480d2e2d5dbd2012fa 100644 (file)
@@ -225,7 +225,7 @@ int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate)
                        goto exit;
                }
 
-               targets->sens_res = be16_to_cpu(*(u16 *)atqa_skb->data);
+               targets->sens_res = be16_to_cpu(*(__be16 *)atqa_skb->data);
                targets->sel_res = sak_skb->data[0];
 
                r = nfc_hci_get_param(hdev, NFC_HCI_RF_READER_A_GATE,
@@ -380,34 +380,31 @@ static int hci_dev_session_init(struct nfc_hci_dev *hdev)
        if (r < 0)
                goto disconnect_all;
 
-       if (skb->len && skb->len == strlen(hdev->init_data.session_id))
-               if (memcmp(hdev->init_data.session_id, skb->data,
-                          skb->len) == 0) {
-                       /* TODO ELa: restore gate<->pipe table from
-                        * some TBD location.
-                        * note: it doesn't seem possible to get the chip
-                        * currently open gate/pipe table.
-                        * It is only possible to obtain the supported
-                        * gate list.
-                        */
+       if (skb->len && skb->len == strlen(hdev->init_data.session_id) &&
+               (memcmp(hdev->init_data.session_id, skb->data,
+                          skb->len) == 0) && hdev->ops->load_session) {
+               /* Restore gate<->pipe table from some proprietary location. */
 
-                       /* goto exit
-                        * For now, always do a full initialization */
-               }
+               r = hdev->ops->load_session(hdev);
 
-       r = nfc_hci_disconnect_all_gates(hdev);
-       if (r < 0)
-               goto exit;
+               if (r < 0)
+                       goto disconnect_all;
+       } else {
 
-       r = hci_dev_connect_gates(hdev, hdev->init_data.gate_count,
-                                 hdev->init_data.gates);
-       if (r < 0)
-               goto disconnect_all;
+               r = nfc_hci_disconnect_all_gates(hdev);
+               if (r < 0)
+                       goto exit;
 
-       r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE,
-                             NFC_HCI_ADMIN_SESSION_IDENTITY,
-                             hdev->init_data.session_id,
-                             strlen(hdev->init_data.session_id));
+               r = hci_dev_connect_gates(hdev, hdev->init_data.gate_count,
+                                         hdev->init_data.gates);
+               if (r < 0)
+                       goto disconnect_all;
+
+               r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE,
+                               NFC_HCI_ADMIN_SESSION_IDENTITY,
+                               hdev->init_data.session_id,
+                               strlen(hdev->init_data.session_id));
+       }
        if (r == 0)
                goto exit;
 
index bec6ed15f5037ef9ffbc75f93e450e9ef1fd81b7..a3ad69a4c648c76779bf496153bfa7eb01f5354c 100644 (file)
@@ -387,7 +387,7 @@ int nfc_llcp_send_symm(struct nfc_dev *dev)
 
        __net_timestamp(skb);
 
-       nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_TX);
+       nfc_llcp_send_to_raw_sock(local, skb, NFC_DIRECTION_TX);
 
        return nfc_data_exchange(dev, local->target_idx, skb,
                                 nfc_llcp_recv, local);
index b4671958fcf935c3c3d984afc18f473bc882eae4..51e7887973171f999eb7c77984d7dddf081a5b75 100644 (file)
@@ -680,16 +680,17 @@ void nfc_llcp_send_to_raw_sock(struct nfc_llcp_local *local,
                        continue;
 
                if (skb_copy == NULL) {
-                       skb_copy = __pskb_copy(skb, NFC_LLCP_RAW_HEADER_SIZE,
-                                              GFP_ATOMIC);
+                       skb_copy = __pskb_copy_fclone(skb, NFC_RAW_HEADER_SIZE,
+                                                     GFP_ATOMIC, true);
 
                        if (skb_copy == NULL)
                                continue;
 
-                       data = skb_push(skb_copy, NFC_LLCP_RAW_HEADER_SIZE);
+                       data = skb_push(skb_copy, NFC_RAW_HEADER_SIZE);
 
                        data[0] = local->dev ? local->dev->idx : 0xFF;
-                       data[1] = direction;
+                       data[1] = direction & 0x01;
+                       data[1] |= (RAW_PAYLOAD_LLCP << 1);
                }
 
                nskb = skb_clone(skb_copy, GFP_ATOMIC);
@@ -747,7 +748,7 @@ static void nfc_llcp_tx_work(struct work_struct *work)
                        __net_timestamp(skb);
 
                        nfc_llcp_send_to_raw_sock(local, skb,
-                                                 NFC_LLCP_DIRECTION_TX);
+                                                 NFC_DIRECTION_TX);
 
                        ret = nfc_data_exchange(local->dev, local->target_idx,
                                                skb, nfc_llcp_recv, local);
@@ -1476,7 +1477,7 @@ static void nfc_llcp_rx_work(struct work_struct *work)
 
        __net_timestamp(skb);
 
-       nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_RX);
+       nfc_llcp_send_to_raw_sock(local, skb, NFC_DIRECTION_RX);
 
        nfc_llcp_rx_skb(local, skb);
 
index 6c34ac978501705a0c743289b34b2f62750bc441..2b400e1a869522b9ecbca787a0ff2938f6109c26 100644 (file)
@@ -861,6 +861,10 @@ static int nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb)
        /* Get rid of skb owner, prior to sending to the driver. */
        skb_orphan(skb);
 
+       /* Send copy to sniffer */
+       nfc_send_to_raw_sock(ndev->nfc_dev, skb,
+                            RAW_PAYLOAD_NCI, NFC_DIRECTION_TX);
+
        return ndev->ops->send(ndev, skb);
 }
 
@@ -935,6 +939,11 @@ static void nci_rx_work(struct work_struct *work)
        struct sk_buff *skb;
 
        while ((skb = skb_dequeue(&ndev->rx_q))) {
+
+               /* Send copy to sniffer */
+               nfc_send_to_raw_sock(ndev->nfc_dev, skb,
+                                    RAW_PAYLOAD_NCI, NFC_DIRECTION_RX);
+
                /* Process frame */
                switch (nci_mt(skb->data)) {
                case NCI_MT_RSP_PKT:
index 1e905097456b77501bc0a950aa932c45205a8eb1..f8f6af231381b336b111a9c01ce0ccb2dfc72856 100644 (file)
@@ -366,7 +366,6 @@ static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev,
                        struct nci_rf_intf_activated_ntf *ntf, __u8 *data)
 {
        struct activation_params_poll_nfc_dep *poll;
-       int i;
 
        switch (ntf->activation_rf_tech_and_mode) {
        case NCI_NFC_A_PASSIVE_POLL_MODE:
@@ -374,10 +373,8 @@ static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev,
                poll = &ntf->activation_params.poll_nfc_dep;
                poll->atr_res_len = min_t(__u8, *data++, 63);
                pr_debug("atr_res_len %d\n", poll->atr_res_len);
-               if (poll->atr_res_len > 0) {
-                       for (i = 0; i < poll->atr_res_len; i++)
-                               poll->atr_res[poll->atr_res_len-1-i] = data[i];
-               }
+               if (poll->atr_res_len > 0)
+                       memcpy(poll->atr_res, data, poll->atr_res_len);
                break;
 
        default:
index 9d6e74f7e6b34ba5ed0d9022a96fdc8061b479c7..88d60064890e3a9ca93db4c88fdcf840e077fabe 100644 (file)
@@ -40,6 +40,12 @@ struct nfc_rawsock {
        struct work_struct tx_work;
        bool tx_work_scheduled;
 };
+
+struct nfc_sock_list {
+       struct hlist_head head;
+       rwlock_t          lock;
+};
+
 #define nfc_rawsock(sk) ((struct nfc_rawsock *) sk)
 #define to_rawsock_sk(_tx_work) \
        ((struct sock *) container_of(_tx_work, struct nfc_rawsock, tx_work))
index c27a6e86cae459f0f70c2c4875614edac3518f98..11c3544ea5466f54f8038ca19f500a831d1ca79f 100644 (file)
 
 #include "nfc.h"
 
+static struct nfc_sock_list raw_sk_list = {
+       .lock = __RW_LOCK_UNLOCKED(raw_sk_list.lock)
+};
+
+static void nfc_sock_link(struct nfc_sock_list *l, struct sock *sk)
+{
+       write_lock(&l->lock);
+       sk_add_node(sk, &l->head);
+       write_unlock(&l->lock);
+}
+
+static void nfc_sock_unlink(struct nfc_sock_list *l, struct sock *sk)
+{
+       write_lock(&l->lock);
+       sk_del_node_init(sk);
+       write_unlock(&l->lock);
+}
+
 static void rawsock_write_queue_purge(struct sock *sk)
 {
        pr_debug("sk=%p\n", sk);
@@ -57,6 +75,9 @@ static int rawsock_release(struct socket *sock)
        if (!sk)
                return 0;
 
+       if (sock->type == SOCK_RAW)
+               nfc_sock_unlink(&raw_sk_list, sk);
+
        sock_orphan(sk);
        sock_put(sk);
 
@@ -275,6 +296,26 @@ static const struct proto_ops rawsock_ops = {
        .mmap           = sock_no_mmap,
 };
 
+static const struct proto_ops rawsock_raw_ops = {
+       .family         = PF_NFC,
+       .owner          = THIS_MODULE,
+       .release        = rawsock_release,
+       .bind           = sock_no_bind,
+       .connect        = sock_no_connect,
+       .socketpair     = sock_no_socketpair,
+       .accept         = sock_no_accept,
+       .getname        = sock_no_getname,
+       .poll           = datagram_poll,
+       .ioctl          = sock_no_ioctl,
+       .listen         = sock_no_listen,
+       .shutdown       = sock_no_shutdown,
+       .setsockopt     = sock_no_setsockopt,
+       .getsockopt     = sock_no_getsockopt,
+       .sendmsg        = sock_no_sendmsg,
+       .recvmsg        = rawsock_recvmsg,
+       .mmap           = sock_no_mmap,
+};
+
 static void rawsock_destruct(struct sock *sk)
 {
        pr_debug("sk=%p\n", sk);
@@ -300,10 +341,13 @@ static int rawsock_create(struct net *net, struct socket *sock,
 
        pr_debug("sock=%p\n", sock);
 
-       if (sock->type != SOCK_SEQPACKET)
+       if ((sock->type != SOCK_SEQPACKET) && (sock->type != SOCK_RAW))
                return -ESOCKTNOSUPPORT;
 
-       sock->ops = &rawsock_ops;
+       if (sock->type == SOCK_RAW)
+               sock->ops = &rawsock_raw_ops;
+       else
+               sock->ops = &rawsock_ops;
 
        sk = sk_alloc(net, PF_NFC, GFP_ATOMIC, nfc_proto->proto);
        if (!sk)
@@ -313,13 +357,53 @@ static int rawsock_create(struct net *net, struct socket *sock,
        sk->sk_protocol = nfc_proto->id;
        sk->sk_destruct = rawsock_destruct;
        sock->state = SS_UNCONNECTED;
-
-       INIT_WORK(&nfc_rawsock(sk)->tx_work, rawsock_tx_work);
-       nfc_rawsock(sk)->tx_work_scheduled = false;
+       if (sock->type == SOCK_RAW)
+               nfc_sock_link(&raw_sk_list, sk);
+       else {
+               INIT_WORK(&nfc_rawsock(sk)->tx_work, rawsock_tx_work);
+               nfc_rawsock(sk)->tx_work_scheduled = false;
+       }
 
        return 0;
 }
 
+void nfc_send_to_raw_sock(struct nfc_dev *dev, struct sk_buff *skb,
+                         u8 payload_type, u8 direction)
+{
+       struct sk_buff *skb_copy = NULL, *nskb;
+       struct sock *sk;
+       u8 *data;
+
+       read_lock(&raw_sk_list.lock);
+
+       sk_for_each(sk, &raw_sk_list.head) {
+               if (!skb_copy) {
+                       skb_copy = __pskb_copy_fclone(skb, NFC_RAW_HEADER_SIZE,
+                                                     GFP_ATOMIC, true);
+                       if (!skb_copy)
+                               continue;
+
+                       data = skb_push(skb_copy, NFC_RAW_HEADER_SIZE);
+
+                       data[0] = dev ? dev->idx : 0xFF;
+                       data[1] = direction & 0x01;
+                       data[1] |= (payload_type << 1);
+               }
+
+               nskb = skb_clone(skb_copy, GFP_ATOMIC);
+               if (!nskb)
+                       continue;
+
+               if (sock_queue_rcv_skb(sk, nskb))
+                       kfree_skb(nskb);
+       }
+
+       read_unlock(&raw_sk_list.lock);
+
+       kfree_skb(skb_copy);
+}
+EXPORT_SYMBOL(nfc_send_to_raw_sock);
+
 static struct proto rawsock_proto = {
        .name     = "NFC_RAW",
        .owner    = THIS_MODULE,
index 2c77e7b1a913241d85f11d19c25b5e182b5757f1..c36856a457ca963c735e89e36478a53ba60bb453 100644 (file)
@@ -134,8 +134,8 @@ static int set_eth_addr(struct sk_buff *skb,
 
        skb_postpull_rcsum(skb, eth_hdr(skb), ETH_ALEN * 2);
 
-       memcpy(eth_hdr(skb)->h_source, eth_key->eth_src, ETH_ALEN);
-       memcpy(eth_hdr(skb)->h_dest, eth_key->eth_dst, ETH_ALEN);
+       ether_addr_copy(eth_hdr(skb)->h_source, eth_key->eth_src);
+       ether_addr_copy(eth_hdr(skb)->h_dest, eth_key->eth_dst);
 
        ovs_skb_postpush_rcsum(skb, eth_hdr(skb), ETH_ALEN * 2);
 
index a3276e3c4feb065278b5195652378157619ff94c..0d407bca81e3573983bc47791dddf5561d4f8ee1 100644 (file)
 #include <linux/netfilter_ipv4.h>
 #include <linux/inetdevice.h>
 #include <linux/list.h>
-#include <linux/lockdep.h>
 #include <linux/openvswitch.h>
 #include <linux/rculist.h>
 #include <linux/dmi.h>
-#include <linux/workqueue.h>
+#include <linux/genetlink.h>
+#include <net/genetlink.h>
 #include <net/genetlink.h>
 #include <net/net_namespace.h>
 #include <net/netns/generic.h>
 
 int ovs_net_id __read_mostly;
 
+static struct genl_family dp_packet_genl_family;
+static struct genl_family dp_flow_genl_family;
+static struct genl_family dp_datapath_genl_family;
+
+static struct genl_multicast_group ovs_dp_flow_multicast_group = {
+       .name = OVS_FLOW_MCGROUP
+};
+
+static struct genl_multicast_group ovs_dp_datapath_multicast_group = {
+       .name = OVS_DATAPATH_MCGROUP
+};
+
+struct genl_multicast_group ovs_dp_vport_multicast_group = {
+       .name = OVS_VPORT_MCGROUP
+};
+
+/* Check if need to build a reply message.
+ * OVS userspace sets the NLM_F_ECHO flag if it needs the reply. */
+static bool ovs_must_notify(struct genl_info *info,
+                           const struct genl_multicast_group *grp)
+{
+       return info->nlhdr->nlmsg_flags & NLM_F_ECHO ||
+               netlink_has_listeners(genl_info_net(info)->genl_sock, 0);
+}
+
 static void ovs_notify(struct genl_family *family,
                       struct sk_buff *skb, struct genl_info *info)
 {
@@ -173,6 +198,7 @@ static struct hlist_head *vport_hash_bucket(const struct datapath *dp,
        return &dp->ports[port_no & (DP_VPORT_HASH_BUCKETS - 1)];
 }
 
+/* Called with ovs_mutex or RCU read lock. */
 struct vport *ovs_lookup_vport(const struct datapath *dp, u16 port_no)
 {
        struct vport *vport;
@@ -262,16 +288,6 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
        u64_stats_update_end(&stats->syncp);
 }
 
-static struct genl_family dp_packet_genl_family = {
-       .id = GENL_ID_GENERATE,
-       .hdrsize = sizeof(struct ovs_header),
-       .name = OVS_PACKET_FAMILY,
-       .version = OVS_PACKET_VERSION,
-       .maxattr = OVS_PACKET_ATTR_MAX,
-       .netnsok = true,
-       .parallel_ops = true,
-};
-
 int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
                  const struct dp_upcall_info *upcall_info)
 {
@@ -524,7 +540,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
                packet->protocol = htons(ETH_P_802_2);
 
        /* Build an sw_flow for sending this packet. */
-       flow = ovs_flow_alloc(false);
+       flow = ovs_flow_alloc();
        err = PTR_ERR(flow);
        if (IS_ERR(flow))
                goto err_kfree_skb;
@@ -590,6 +606,18 @@ static const struct genl_ops dp_packet_genl_ops[] = {
        }
 };
 
+static struct genl_family dp_packet_genl_family = {
+       .id = GENL_ID_GENERATE,
+       .hdrsize = sizeof(struct ovs_header),
+       .name = OVS_PACKET_FAMILY,
+       .version = OVS_PACKET_VERSION,
+       .maxattr = OVS_PACKET_ATTR_MAX,
+       .netnsok = true,
+       .parallel_ops = true,
+       .ops = dp_packet_genl_ops,
+       .n_ops = ARRAY_SIZE(dp_packet_genl_ops),
+};
+
 static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats,
                         struct ovs_dp_megaflow_stats *mega_stats)
 {
@@ -621,26 +649,6 @@ static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats,
        }
 }
 
-static const struct nla_policy flow_policy[OVS_FLOW_ATTR_MAX + 1] = {
-       [OVS_FLOW_ATTR_KEY] = { .type = NLA_NESTED },
-       [OVS_FLOW_ATTR_ACTIONS] = { .type = NLA_NESTED },
-       [OVS_FLOW_ATTR_CLEAR] = { .type = NLA_FLAG },
-};
-
-static struct genl_family dp_flow_genl_family = {
-       .id = GENL_ID_GENERATE,
-       .hdrsize = sizeof(struct ovs_header),
-       .name = OVS_FLOW_FAMILY,
-       .version = OVS_FLOW_VERSION,
-       .maxattr = OVS_FLOW_ATTR_MAX,
-       .netnsok = true,
-       .parallel_ops = true,
-};
-
-static struct genl_multicast_group ovs_dp_flow_multicast_group = {
-       .name = OVS_FLOW_MCGROUP
-};
-
 static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts)
 {
        return NLMSG_ALIGN(sizeof(struct ovs_header))
@@ -652,8 +660,8 @@ static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts)
                + nla_total_size(acts->actions_len); /* OVS_FLOW_ATTR_ACTIONS */
 }
 
-/* Called with ovs_mutex. */
-static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
+/* Called with ovs_mutex or RCU read lock. */
+static int ovs_flow_cmd_fill_info(const struct sw_flow *flow, int dp_ifindex,
                                  struct sk_buff *skb, u32 portid,
                                  u32 seq, u32 flags, u8 cmd)
 {
@@ -670,7 +678,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
        if (!ovs_header)
                return -EMSGSIZE;
 
-       ovs_header->dp_ifindex = get_dpifindex(dp);
+       ovs_header->dp_ifindex = dp_ifindex;
 
        /* Fill flow key. */
        nla = nla_nest_start(skb, OVS_FLOW_ATTR_KEY);
@@ -693,6 +701,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
        nla_nest_end(skb, nla);
 
        ovs_flow_stats_get(flow, &stats, &used, &tcp_flags);
+
        if (used &&
            nla_put_u64(skb, OVS_FLOW_ATTR_USED, ovs_flow_used_time(used)))
                goto nla_put_failure;
@@ -720,9 +729,9 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
                const struct sw_flow_actions *sf_acts;
 
                sf_acts = rcu_dereference_ovsl(flow->sf_acts);
-
                err = ovs_nla_put_actions(sf_acts->actions,
                                          sf_acts->actions_len, skb);
+
                if (!err)
                        nla_nest_end(skb, start);
                else {
@@ -743,113 +752,128 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
        return err;
 }
 
-static struct sk_buff *ovs_flow_cmd_alloc_info(struct sw_flow *flow,
-                                              struct genl_info *info)
+/* May not be called with RCU read lock. */
+static struct sk_buff *ovs_flow_cmd_alloc_info(const struct sw_flow_actions *acts,
+                                              struct genl_info *info,
+                                              bool always)
 {
-       size_t len;
+       struct sk_buff *skb;
+
+       if (!always && !ovs_must_notify(info, &ovs_dp_flow_multicast_group))
+               return NULL;
 
-       len = ovs_flow_cmd_msg_size(ovsl_dereference(flow->sf_acts));
+       skb = genlmsg_new_unicast(ovs_flow_cmd_msg_size(acts), info, GFP_KERNEL);
+       if (!skb)
+               return ERR_PTR(-ENOMEM);
 
-       return genlmsg_new_unicast(len, info, GFP_KERNEL);
+       return skb;
 }
 
-static struct sk_buff *ovs_flow_cmd_build_info(struct sw_flow *flow,
-                                              struct datapath *dp,
-                                              struct genl_info *info,
-                                              u8 cmd)
+/* Called with ovs_mutex. */
+static struct sk_buff *ovs_flow_cmd_build_info(const struct sw_flow *flow,
+                                              int dp_ifindex,
+                                              struct genl_info *info, u8 cmd,
+                                              bool always)
 {
        struct sk_buff *skb;
        int retval;
 
-       skb = ovs_flow_cmd_alloc_info(flow, info);
-       if (!skb)
-               return ERR_PTR(-ENOMEM);
+       skb = ovs_flow_cmd_alloc_info(ovsl_dereference(flow->sf_acts), info,
+                                     always);
+       if (!skb || IS_ERR(skb))
+               return skb;
 
-       retval = ovs_flow_cmd_fill_info(flow, dp, skb, info->snd_portid,
-                                       info->snd_seq, 0, cmd);
+       retval = ovs_flow_cmd_fill_info(flow, dp_ifindex, skb,
+                                       info->snd_portid, info->snd_seq, 0,
+                                       cmd);
        BUG_ON(retval < 0);
        return skb;
 }
 
-static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
+static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
 {
        struct nlattr **a = info->attrs;
        struct ovs_header *ovs_header = info->userhdr;
-       struct sw_flow_key key, masked_key;
-       struct sw_flow *flow = NULL;
+       struct sw_flow *flow, *new_flow;
        struct sw_flow_mask mask;
        struct sk_buff *reply;
        struct datapath *dp;
-       struct sw_flow_actions *acts = NULL;
+       struct sw_flow_actions *acts;
        struct sw_flow_match match;
-       bool exact_5tuple;
        int error;
 
-       /* Extract key. */
+       /* Must have key and actions. */
        error = -EINVAL;
        if (!a[OVS_FLOW_ATTR_KEY])
                goto error;
+       if (!a[OVS_FLOW_ATTR_ACTIONS])
+               goto error;
 
-       ovs_match_init(&match, &key, &mask);
-       error = ovs_nla_get_match(&match, &exact_5tuple,
+       /* Most of the time we need to allocate a new flow, do it before
+        * locking.
+        */
+       new_flow = ovs_flow_alloc();
+       if (IS_ERR(new_flow)) {
+               error = PTR_ERR(new_flow);
+               goto error;
+       }
+
+       /* Extract key. */
+       ovs_match_init(&match, &new_flow->unmasked_key, &mask);
+       error = ovs_nla_get_match(&match,
                                  a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK]);
        if (error)
-               goto error;
+               goto err_kfree_flow;
+
+       ovs_flow_mask_key(&new_flow->key, &new_flow->unmasked_key, &mask);
 
        /* Validate actions. */
-       if (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;
+       acts = ovs_nla_alloc_flow_actions(nla_len(a[OVS_FLOW_ATTR_ACTIONS]));
+       error = PTR_ERR(acts);
+       if (IS_ERR(acts))
+               goto err_kfree_flow;
 
-               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;
-               }
-       } else if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW) {
-               error = -EINVAL;
-               goto error;
+       error = ovs_nla_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], &new_flow->key,
+                                    0, &acts);
+       if (error) {
+               OVS_NLERR("Flow actions may not be safe on all matching packets.\n");
+               goto err_kfree_acts;
+       }
+
+       reply = ovs_flow_cmd_alloc_info(acts, info, false);
+       if (IS_ERR(reply)) {
+               error = PTR_ERR(reply);
+               goto err_kfree_acts;
        }
 
        ovs_lock();
        dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
-       error = -ENODEV;
-       if (!dp)
+       if (unlikely(!dp)) {
+               error = -ENODEV;
                goto err_unlock_ovs;
-
+       }
        /* Check if this is a duplicate flow */
-       flow = ovs_flow_tbl_lookup(&dp->table, &key);
-       if (!flow) {
-               /* 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;
-
-               /* Allocate flow. */
-               flow = ovs_flow_alloc(!exact_5tuple);
-               if (IS_ERR(flow)) {
-                       error = PTR_ERR(flow);
-                       goto err_unlock_ovs;
-               }
-
-               flow->key = masked_key;
-               flow->unmasked_key = key;
-               rcu_assign_pointer(flow->sf_acts, acts);
+       flow = ovs_flow_tbl_lookup(&dp->table, &new_flow->unmasked_key);
+       if (likely(!flow)) {
+               rcu_assign_pointer(new_flow->sf_acts, acts);
 
                /* Put flow in bucket. */
-               error = ovs_flow_tbl_insert(&dp->table, flow, &mask);
-               if (error) {
+               error = ovs_flow_tbl_insert(&dp->table, new_flow, &mask);
+               if (unlikely(error)) {
                        acts = NULL;
-                       goto err_flow_free;
+                       goto err_unlock_ovs;
                }
 
-               reply = ovs_flow_cmd_build_info(flow, dp, info, OVS_FLOW_CMD_NEW);
+               if (unlikely(reply)) {
+                       error = ovs_flow_cmd_fill_info(new_flow,
+                                                      ovs_header->dp_ifindex,
+                                                      reply, info->snd_portid,
+                                                      info->snd_seq, 0,
+                                                      OVS_FLOW_CMD_NEW);
+                       BUG_ON(error < 0);
+               }
+               ovs_unlock();
        } else {
-               /* We found a matching flow. */
                struct sw_flow_actions *old_acts;
 
                /* Bail out if we're not allowed to modify an existing flow.
@@ -858,40 +882,154 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                 * request.  We also accept NLM_F_EXCL in case that bug ever
                 * gets fixed.
                 */
-               error = -EEXIST;
-               if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW &&
-                   info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL))
+               if (unlikely(info->nlhdr->nlmsg_flags & (NLM_F_CREATE
+                                                        | NLM_F_EXCL))) {
+                       error = -EEXIST;
                        goto err_unlock_ovs;
-
+               }
                /* The unmasked key has to be the same for flow updates. */
-               if (!ovs_flow_cmp_unmasked_key(flow, &match))
+               if (unlikely(!ovs_flow_cmp_unmasked_key(flow, &match))) {
+                       error = -EEXIST;
                        goto err_unlock_ovs;
-
+               }
                /* Update actions. */
                old_acts = ovsl_dereference(flow->sf_acts);
                rcu_assign_pointer(flow->sf_acts, acts);
+
+               if (unlikely(reply)) {
+                       error = ovs_flow_cmd_fill_info(flow,
+                                                      ovs_header->dp_ifindex,
+                                                      reply, info->snd_portid,
+                                                      info->snd_seq, 0,
+                                                      OVS_FLOW_CMD_NEW);
+                       BUG_ON(error < 0);
+               }
+               ovs_unlock();
+
                ovs_nla_free_flow_actions(old_acts);
+               ovs_flow_free(new_flow, false);
+       }
+
+       if (reply)
+               ovs_notify(&dp_flow_genl_family, reply, info);
+       return 0;
+
+err_unlock_ovs:
+       ovs_unlock();
+       kfree_skb(reply);
+err_kfree_acts:
+       kfree(acts);
+err_kfree_flow:
+       ovs_flow_free(new_flow, false);
+error:
+       return error;
+}
+
+static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nlattr **a = info->attrs;
+       struct ovs_header *ovs_header = info->userhdr;
+       struct sw_flow_key key, masked_key;
+       struct sw_flow *flow;
+       struct sw_flow_mask mask;
+       struct sk_buff *reply = NULL;
+       struct datapath *dp;
+       struct sw_flow_actions *old_acts = NULL, *acts = NULL;
+       struct sw_flow_match match;
+       int error;
+
+       /* Extract key. */
+       error = -EINVAL;
+       if (!a[OVS_FLOW_ATTR_KEY])
+               goto error;
+
+       ovs_match_init(&match, &key, &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_nla_alloc_flow_actions(nla_len(a[OVS_FLOW_ATTR_ACTIONS]));
+               error = PTR_ERR(acts);
+               if (IS_ERR(acts))
+                       goto error;
+
+               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_acts;
+               }
+       }
+
+       /* Can allocate before locking if have acts. */
+       if (acts) {
+               reply = ovs_flow_cmd_alloc_info(acts, info, false);
+               if (IS_ERR(reply)) {
+                       error = PTR_ERR(reply);
+                       goto err_kfree_acts;
+               }
+       }
 
-               reply = ovs_flow_cmd_build_info(flow, dp, info, OVS_FLOW_CMD_NEW);
+       ovs_lock();
+       dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
+       if (unlikely(!dp)) {
+               error = -ENODEV;
+               goto err_unlock_ovs;
+       }
+       /* Check that the flow exists. */
+       flow = ovs_flow_tbl_lookup(&dp->table, &key);
+       if (unlikely(!flow)) {
+               error = -ENOENT;
+               goto err_unlock_ovs;
+       }
+       /* The unmasked key has to be the same for flow updates. */
+       if (unlikely(!ovs_flow_cmp_unmasked_key(flow, &match))) {
+               error = -EEXIST;
+               goto err_unlock_ovs;
+       }
+       /* Update actions, if present. */
+       if (likely(acts)) {
+               old_acts = ovsl_dereference(flow->sf_acts);
+               rcu_assign_pointer(flow->sf_acts, acts);
 
-               /* Clear stats. */
-               if (a[OVS_FLOW_ATTR_CLEAR])
-                       ovs_flow_stats_clear(flow);
+               if (unlikely(reply)) {
+                       error = ovs_flow_cmd_fill_info(flow,
+                                                      ovs_header->dp_ifindex,
+                                                      reply, info->snd_portid,
+                                                      info->snd_seq, 0,
+                                                      OVS_FLOW_CMD_NEW);
+                       BUG_ON(error < 0);
+               }
+       } else {
+               /* Could not alloc without acts before locking. */
+               reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex,
+                                               info, OVS_FLOW_CMD_NEW, false);
+               if (unlikely(IS_ERR(reply))) {
+                       error = PTR_ERR(reply);
+                       goto err_unlock_ovs;
+               }
        }
+
+       /* Clear stats. */
+       if (a[OVS_FLOW_ATTR_CLEAR])
+               ovs_flow_stats_clear(flow);
        ovs_unlock();
 
-       if (!IS_ERR(reply))
+       if (reply)
                ovs_notify(&dp_flow_genl_family, reply, info);
-       else
-               genl_set_err(&dp_flow_genl_family, sock_net(skb->sk), 0,
-                            0, PTR_ERR(reply));
+       if (old_acts)
+               ovs_nla_free_flow_actions(old_acts);
+
        return 0;
 
-err_flow_free:
-       ovs_flow_free(flow, false);
 err_unlock_ovs:
        ovs_unlock();
-err_kfree:
+       kfree_skb(reply);
+err_kfree_acts:
        kfree(acts);
 error:
        return error;
@@ -914,7 +1052,7 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
        }
 
        ovs_match_init(&match, &key, NULL);
-       err = ovs_nla_get_match(&match, NULL, a[OVS_FLOW_ATTR_KEY], NULL);
+       err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL);
        if (err)
                return err;
 
@@ -931,7 +1069,8 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
                goto unlock;
        }
 
-       reply = ovs_flow_cmd_build_info(flow, dp, info, OVS_FLOW_CMD_NEW);
+       reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex, info,
+                                       OVS_FLOW_CMD_NEW, true);
        if (IS_ERR(reply)) {
                err = PTR_ERR(reply);
                goto unlock;
@@ -955,45 +1094,53 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
        struct sw_flow_match match;
        int err;
 
+       if (likely(a[OVS_FLOW_ATTR_KEY])) {
+               ovs_match_init(&match, &key, NULL);
+               err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL);
+               if (unlikely(err))
+                       return err;
+       }
+
        ovs_lock();
        dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
-       if (!dp) {
+       if (unlikely(!dp)) {
                err = -ENODEV;
                goto unlock;
        }
 
-       if (!a[OVS_FLOW_ATTR_KEY]) {
+       if (unlikely(!a[OVS_FLOW_ATTR_KEY])) {
                err = ovs_flow_tbl_flush(&dp->table);
                goto unlock;
        }
 
-       ovs_match_init(&match, &key, NULL);
-       err = ovs_nla_get_match(&match, NULL, a[OVS_FLOW_ATTR_KEY], NULL);
-       if (err)
-               goto unlock;
-
        flow = ovs_flow_tbl_lookup(&dp->table, &key);
-       if (!flow || !ovs_flow_cmp_unmasked_key(flow, &match)) {
+       if (unlikely(!flow || !ovs_flow_cmp_unmasked_key(flow, &match))) {
                err = -ENOENT;
                goto unlock;
        }
 
-       reply = ovs_flow_cmd_alloc_info(flow, info);
-       if (!reply) {
-               err = -ENOMEM;
-               goto unlock;
-       }
-
        ovs_flow_tbl_remove(&dp->table, flow);
+       ovs_unlock();
 
-       err = ovs_flow_cmd_fill_info(flow, dp, reply, info->snd_portid,
-                                    info->snd_seq, 0, OVS_FLOW_CMD_DEL);
-       BUG_ON(err < 0);
+       reply = ovs_flow_cmd_alloc_info((const struct sw_flow_actions __force *) flow->sf_acts,
+                                       info, false);
+       if (likely(reply)) {
+               if (likely(!IS_ERR(reply))) {
+                       rcu_read_lock();        /*To keep RCU checker happy. */
+                       err = ovs_flow_cmd_fill_info(flow, ovs_header->dp_ifindex,
+                                                    reply, info->snd_portid,
+                                                    info->snd_seq, 0,
+                                                    OVS_FLOW_CMD_DEL);
+                       rcu_read_unlock();
+                       BUG_ON(err < 0);
+
+                       ovs_notify(&dp_flow_genl_family, reply, info);
+               } else {
+                       netlink_set_err(sock_net(skb->sk)->genl_sock, 0, 0, PTR_ERR(reply));
+               }
+       }
 
        ovs_flow_free(flow, true);
-       ovs_unlock();
-
-       ovs_notify(&dp_flow_genl_family, reply, info);
        return 0;
 unlock:
        ovs_unlock();
@@ -1024,7 +1171,7 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
                if (!flow)
                        break;
 
-               if (ovs_flow_cmd_fill_info(flow, dp, skb,
+               if (ovs_flow_cmd_fill_info(flow, ovs_header->dp_ifindex, skb,
                                           NETLINK_CB(cb->skb).portid,
                                           cb->nlh->nlmsg_seq, NLM_F_MULTI,
                                           OVS_FLOW_CMD_NEW) < 0)
@@ -1037,11 +1184,17 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
        return skb->len;
 }
 
-static const struct genl_ops dp_flow_genl_ops[] = {
+static const struct nla_policy flow_policy[OVS_FLOW_ATTR_MAX + 1] = {
+       [OVS_FLOW_ATTR_KEY] = { .type = NLA_NESTED },
+       [OVS_FLOW_ATTR_ACTIONS] = { .type = NLA_NESTED },
+       [OVS_FLOW_ATTR_CLEAR] = { .type = NLA_FLAG },
+};
+
+static struct genl_ops dp_flow_genl_ops[] = {
        { .cmd = OVS_FLOW_CMD_NEW,
          .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
          .policy = flow_policy,
-         .doit = ovs_flow_cmd_new_or_set
+         .doit = ovs_flow_cmd_new
        },
        { .cmd = OVS_FLOW_CMD_DEL,
          .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
@@ -1057,28 +1210,22 @@ static const struct genl_ops dp_flow_genl_ops[] = {
        { .cmd = OVS_FLOW_CMD_SET,
          .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
          .policy = flow_policy,
-         .doit = ovs_flow_cmd_new_or_set,
+         .doit = ovs_flow_cmd_set,
        },
 };
 
-static const struct nla_policy datapath_policy[OVS_DP_ATTR_MAX + 1] = {
-       [OVS_DP_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
-       [OVS_DP_ATTR_UPCALL_PID] = { .type = NLA_U32 },
-       [OVS_DP_ATTR_USER_FEATURES] = { .type = NLA_U32 },
-};
-
-static struct genl_family dp_datapath_genl_family = {
+static struct genl_family dp_flow_genl_family = {
        .id = GENL_ID_GENERATE,
        .hdrsize = sizeof(struct ovs_header),
-       .name = OVS_DATAPATH_FAMILY,
-       .version = OVS_DATAPATH_VERSION,
-       .maxattr = OVS_DP_ATTR_MAX,
+       .name = OVS_FLOW_FAMILY,
+       .version = OVS_FLOW_VERSION,
+       .maxattr = OVS_FLOW_ATTR_MAX,
        .netnsok = true,
        .parallel_ops = true,
-};
-
-static struct genl_multicast_group ovs_dp_datapath_multicast_group = {
-       .name = OVS_DATAPATH_MCGROUP
+       .ops = dp_flow_genl_ops,
+       .n_ops = ARRAY_SIZE(dp_flow_genl_ops),
+       .mcgrps = &ovs_dp_flow_multicast_group,
+       .n_mcgrps = 1,
 };
 
 static size_t ovs_dp_cmd_msg_size(void)
@@ -1093,6 +1240,7 @@ static size_t ovs_dp_cmd_msg_size(void)
        return msgsize;
 }
 
+/* Called with ovs_mutex or RCU read lock. */
 static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb,
                                u32 portid, u32 seq, u32 flags, u8 cmd)
 {
@@ -1108,9 +1256,7 @@ static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb,
 
        ovs_header->dp_ifindex = get_dpifindex(dp);
 
-       rcu_read_lock();
        err = nla_put_string(skb, OVS_DP_ATTR_NAME, ovs_dp_name(dp));
-       rcu_read_unlock();
        if (err)
                goto nla_put_failure;
 
@@ -1135,25 +1281,12 @@ static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb,
        return -EMSGSIZE;
 }
 
-static struct sk_buff *ovs_dp_cmd_build_info(struct datapath *dp,
-                                            struct genl_info *info, u8 cmd)
+static struct sk_buff *ovs_dp_cmd_alloc_info(struct genl_info *info)
 {
-       struct sk_buff *skb;
-       int retval;
-
-       skb = genlmsg_new_unicast(ovs_dp_cmd_msg_size(), info, GFP_KERNEL);
-       if (!skb)
-               return ERR_PTR(-ENOMEM);
-
-       retval = ovs_dp_cmd_fill_info(dp, skb, info->snd_portid, info->snd_seq, 0, cmd);
-       if (retval < 0) {
-               kfree_skb(skb);
-               return ERR_PTR(retval);
-       }
-       return skb;
+       return genlmsg_new_unicast(ovs_dp_cmd_msg_size(), info, GFP_KERNEL);
 }
 
-/* Called with ovs_mutex. */
+/* Called with rcu_read_lock or ovs_mutex. */
 static struct datapath *lookup_datapath(struct net *net,
                                        struct ovs_header *ovs_header,
                                        struct nlattr *a[OVS_DP_ATTR_MAX + 1])
@@ -1165,10 +1298,8 @@ static struct datapath *lookup_datapath(struct net *net,
        else {
                struct vport *vport;
 
-               rcu_read_lock();
                vport = ovs_vport_locate(net, nla_data(a[OVS_DP_ATTR_NAME]));
                dp = vport && vport->port_no == OVSP_LOCAL ? vport->dp : NULL;
-               rcu_read_unlock();
        }
        return dp ? dp : ERR_PTR(-ENODEV);
 }
@@ -1205,12 +1336,14 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
        if (!a[OVS_DP_ATTR_NAME] || !a[OVS_DP_ATTR_UPCALL_PID])
                goto err;
 
-       ovs_lock();
+       reply = ovs_dp_cmd_alloc_info(info);
+       if (!reply)
+               return -ENOMEM;
 
        err = -ENOMEM;
        dp = kzalloc(sizeof(*dp), GFP_KERNEL);
        if (dp == NULL)
-               goto err_unlock_ovs;
+               goto err_free_reply;
 
        ovs_dp_set_net(dp, hold_net(sock_net(skb->sk)));
 
@@ -1245,6 +1378,9 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
 
        ovs_dp_change(dp, a);
 
+       /* So far only local changes have been made, now need the lock. */
+       ovs_lock();
+
        vport = new_vport(&parms);
        if (IS_ERR(vport)) {
                err = PTR_ERR(vport);
@@ -1263,10 +1399,9 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
                goto err_destroy_ports_array;
        }
 
-       reply = ovs_dp_cmd_build_info(dp, info, OVS_DP_CMD_NEW);
-       err = PTR_ERR(reply);
-       if (IS_ERR(reply))
-               goto err_destroy_local_port;
+       err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid,
+                                  info->snd_seq, 0, OVS_DP_CMD_NEW);
+       BUG_ON(err < 0);
 
        ovs_net = net_generic(ovs_dp_get_net(dp), ovs_net_id);
        list_add_tail_rcu(&dp->list_node, &ovs_net->dps);
@@ -1276,9 +1411,8 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
        ovs_notify(&dp_datapath_genl_family, reply, info);
        return 0;
 
-err_destroy_local_port:
-       ovs_dp_detach_port(ovs_vport_ovsl(dp, OVSP_LOCAL));
 err_destroy_ports_array:
+       ovs_unlock();
        kfree(dp->ports);
 err_destroy_percpu:
        free_percpu(dp->stats_percpu);
@@ -1287,8 +1421,8 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
 err_free_dp:
        release_net(ovs_dp_get_net(dp));
        kfree(dp);
-err_unlock_ovs:
-       ovs_unlock();
+err_free_reply:
+       kfree_skb(reply);
 err:
        return err;
 }
@@ -1326,16 +1460,19 @@ static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info)
        struct datapath *dp;
        int err;
 
+       reply = ovs_dp_cmd_alloc_info(info);
+       if (!reply)
+               return -ENOMEM;
+
        ovs_lock();
        dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs);
        err = PTR_ERR(dp);
        if (IS_ERR(dp))
-               goto unlock;
+               goto err_unlock_free;
 
-       reply = ovs_dp_cmd_build_info(dp, info, OVS_DP_CMD_DEL);
-       err = PTR_ERR(reply);
-       if (IS_ERR(reply))
-               goto unlock;
+       err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid,
+                                  info->snd_seq, 0, OVS_DP_CMD_DEL);
+       BUG_ON(err < 0);
 
        __dp_destroy(dp);
        ovs_unlock();
@@ -1343,8 +1480,10 @@ static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info)
        ovs_notify(&dp_datapath_genl_family, reply, info);
 
        return 0;
-unlock:
+
+err_unlock_free:
        ovs_unlock();
+       kfree_skb(reply);
        return err;
 }
 
@@ -1354,29 +1493,30 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info)
        struct datapath *dp;
        int err;
 
+       reply = ovs_dp_cmd_alloc_info(info);
+       if (!reply)
+               return -ENOMEM;
+
        ovs_lock();
        dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs);
        err = PTR_ERR(dp);
        if (IS_ERR(dp))
-               goto unlock;
+               goto err_unlock_free;
 
        ovs_dp_change(dp, info->attrs);
 
-       reply = ovs_dp_cmd_build_info(dp, info, OVS_DP_CMD_NEW);
-       if (IS_ERR(reply)) {
-               err = PTR_ERR(reply);
-               genl_set_err(&dp_datapath_genl_family, sock_net(skb->sk), 0,
-                            0, err);
-               err = 0;
-               goto unlock;
-       }
+       err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid,
+                                  info->snd_seq, 0, OVS_DP_CMD_NEW);
+       BUG_ON(err < 0);
 
        ovs_unlock();
        ovs_notify(&dp_datapath_genl_family, reply, info);
 
        return 0;
-unlock:
+
+err_unlock_free:
        ovs_unlock();
+       kfree_skb(reply);
        return err;
 }
 
@@ -1386,24 +1526,26 @@ static int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info)
        struct datapath *dp;
        int err;
 
-       ovs_lock();
+       reply = ovs_dp_cmd_alloc_info(info);
+       if (!reply)
+               return -ENOMEM;
+
+       rcu_read_lock();
        dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs);
        if (IS_ERR(dp)) {
                err = PTR_ERR(dp);
-               goto unlock;
-       }
-
-       reply = ovs_dp_cmd_build_info(dp, info, OVS_DP_CMD_NEW);
-       if (IS_ERR(reply)) {
-               err = PTR_ERR(reply);
-               goto unlock;
+               goto err_unlock_free;
        }
+       err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid,
+                                  info->snd_seq, 0, OVS_DP_CMD_NEW);
+       BUG_ON(err < 0);
+       rcu_read_unlock();
 
-       ovs_unlock();
        return genlmsg_reply(reply, info);
 
-unlock:
-       ovs_unlock();
+err_unlock_free:
+       rcu_read_unlock();
+       kfree_skb(reply);
        return err;
 }
 
@@ -1430,7 +1572,13 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
        return skb->len;
 }
 
-static const struct genl_ops dp_datapath_genl_ops[] = {
+static const struct nla_policy datapath_policy[OVS_DP_ATTR_MAX + 1] = {
+       [OVS_DP_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
+       [OVS_DP_ATTR_UPCALL_PID] = { .type = NLA_U32 },
+       [OVS_DP_ATTR_USER_FEATURES] = { .type = NLA_U32 },
+};
+
+static struct genl_ops dp_datapath_genl_ops[] = {
        { .cmd = OVS_DP_CMD_NEW,
          .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
          .policy = datapath_policy,
@@ -1454,27 +1602,18 @@ static const struct genl_ops dp_datapath_genl_ops[] = {
        },
 };
 
-static const struct nla_policy vport_policy[OVS_VPORT_ATTR_MAX + 1] = {
-       [OVS_VPORT_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
-       [OVS_VPORT_ATTR_STATS] = { .len = sizeof(struct ovs_vport_stats) },
-       [OVS_VPORT_ATTR_PORT_NO] = { .type = NLA_U32 },
-       [OVS_VPORT_ATTR_TYPE] = { .type = NLA_U32 },
-       [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NLA_U32 },
-       [OVS_VPORT_ATTR_OPTIONS] = { .type = NLA_NESTED },
-};
-
-struct genl_family dp_vport_genl_family = {
+static struct genl_family dp_datapath_genl_family = {
        .id = GENL_ID_GENERATE,
        .hdrsize = sizeof(struct ovs_header),
-       .name = OVS_VPORT_FAMILY,
-       .version = OVS_VPORT_VERSION,
-       .maxattr = OVS_VPORT_ATTR_MAX,
+       .name = OVS_DATAPATH_FAMILY,
+       .version = OVS_DATAPATH_VERSION,
+       .maxattr = OVS_DP_ATTR_MAX,
        .netnsok = true,
        .parallel_ops = true,
-};
-
-static struct genl_multicast_group ovs_dp_vport_multicast_group = {
-       .name = OVS_VPORT_MCGROUP
+       .ops = dp_datapath_genl_ops,
+       .n_ops = ARRAY_SIZE(dp_datapath_genl_ops),
+       .mcgrps = &ovs_dp_datapath_multicast_group,
+       .n_mcgrps = 1,
 };
 
 /* Called with ovs_mutex or RCU read lock. */
@@ -1516,7 +1655,12 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
        return err;
 }
 
-/* Called with ovs_mutex or RCU read lock. */
+static struct sk_buff *ovs_vport_cmd_alloc_info(void)
+{
+       return nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+}
+
+/* Called with ovs_mutex, only via ovs_dp_notify_wq(). */
 struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, u32 portid,
                                         u32 seq, u8 cmd)
 {
@@ -1578,33 +1722,35 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
        u32 port_no;
        int err;
 
-       err = -EINVAL;
        if (!a[OVS_VPORT_ATTR_NAME] || !a[OVS_VPORT_ATTR_TYPE] ||
            !a[OVS_VPORT_ATTR_UPCALL_PID])
-               goto exit;
+               return -EINVAL;
+
+       port_no = a[OVS_VPORT_ATTR_PORT_NO]
+               ? nla_get_u32(a[OVS_VPORT_ATTR_PORT_NO]) : 0;
+       if (port_no >= DP_MAX_PORTS)
+               return -EFBIG;
+
+       reply = ovs_vport_cmd_alloc_info();
+       if (!reply)
+               return -ENOMEM;
 
        ovs_lock();
        dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
        err = -ENODEV;
        if (!dp)
-               goto exit_unlock;
-
-       if (a[OVS_VPORT_ATTR_PORT_NO]) {
-               port_no = nla_get_u32(a[OVS_VPORT_ATTR_PORT_NO]);
-
-               err = -EFBIG;
-               if (port_no >= DP_MAX_PORTS)
-                       goto exit_unlock;
+               goto exit_unlock_free;
 
+       if (port_no) {
                vport = ovs_vport_ovsl(dp, port_no);
                err = -EBUSY;
                if (vport)
-                       goto exit_unlock;
+                       goto exit_unlock_free;
        } else {
                for (port_no = 1; ; port_no++) {
                        if (port_no >= DP_MAX_PORTS) {
                                err = -EFBIG;
-                               goto exit_unlock;
+                               goto exit_unlock_free;
                        }
                        vport = ovs_vport_ovsl(dp, port_no);
                        if (!vport)
@@ -1622,22 +1768,19 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
        vport = new_vport(&parms);
        err = PTR_ERR(vport);
        if (IS_ERR(vport))
-               goto exit_unlock;
+               goto exit_unlock_free;
 
-       err = 0;
-       reply = ovs_vport_cmd_build_info(vport, info->snd_portid, info->snd_seq,
-                                        OVS_VPORT_CMD_NEW);
-       if (IS_ERR(reply)) {
-               err = PTR_ERR(reply);
-               ovs_dp_detach_port(vport);
-               goto exit_unlock;
-       }
+       err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid,
+                                     info->snd_seq, 0, OVS_VPORT_CMD_NEW);
+       BUG_ON(err < 0);
+       ovs_unlock();
 
        ovs_notify(&dp_vport_genl_family, reply, info);
+       return 0;
 
-exit_unlock:
+exit_unlock_free:
        ovs_unlock();
-exit:
+       kfree_skb(reply);
        return err;
 }
 
@@ -1648,28 +1791,26 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
        struct vport *vport;
        int err;
 
+       reply = ovs_vport_cmd_alloc_info();
+       if (!reply)
+               return -ENOMEM;
+
        ovs_lock();
        vport = lookup_vport(sock_net(skb->sk), info->userhdr, a);
        err = PTR_ERR(vport);
        if (IS_ERR(vport))
-               goto exit_unlock;
+               goto exit_unlock_free;
 
        if (a[OVS_VPORT_ATTR_TYPE] &&
            nla_get_u32(a[OVS_VPORT_ATTR_TYPE]) != vport->ops->type) {
                err = -EINVAL;
-               goto exit_unlock;
-       }
-
-       reply = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-       if (!reply) {
-               err = -ENOMEM;
-               goto exit_unlock;
+               goto exit_unlock_free;
        }
 
        if (a[OVS_VPORT_ATTR_OPTIONS]) {
                err = ovs_vport_set_options(vport, a[OVS_VPORT_ATTR_OPTIONS]);
                if (err)
-                       goto exit_free;
+                       goto exit_unlock_free;
        }
 
        if (a[OVS_VPORT_ATTR_UPCALL_PID])
@@ -1683,10 +1824,9 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
        ovs_notify(&dp_vport_genl_family, reply, info);
        return 0;
 
-exit_free:
-       kfree_skb(reply);
-exit_unlock:
+exit_unlock_free:
        ovs_unlock();
+       kfree_skb(reply);
        return err;
 }
 
@@ -1697,30 +1837,33 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info)
        struct vport *vport;
        int err;
 
+       reply = ovs_vport_cmd_alloc_info();
+       if (!reply)
+               return -ENOMEM;
+
        ovs_lock();
        vport = lookup_vport(sock_net(skb->sk), info->userhdr, a);
        err = PTR_ERR(vport);
        if (IS_ERR(vport))
-               goto exit_unlock;
+               goto exit_unlock_free;
 
        if (vport->port_no == OVSP_LOCAL) {
                err = -EINVAL;
-               goto exit_unlock;
+               goto exit_unlock_free;
        }
 
-       reply = ovs_vport_cmd_build_info(vport, info->snd_portid,
-                                        info->snd_seq, OVS_VPORT_CMD_DEL);
-       err = PTR_ERR(reply);
-       if (IS_ERR(reply))
-               goto exit_unlock;
-
-       err = 0;
+       err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid,
+                                     info->snd_seq, 0, OVS_VPORT_CMD_DEL);
+       BUG_ON(err < 0);
        ovs_dp_detach_port(vport);
+       ovs_unlock();
 
        ovs_notify(&dp_vport_genl_family, reply, info);
+       return 0;
 
-exit_unlock:
+exit_unlock_free:
        ovs_unlock();
+       kfree_skb(reply);
        return err;
 }
 
@@ -1732,24 +1875,25 @@ static int ovs_vport_cmd_get(struct sk_buff *skb, struct genl_info *info)
        struct vport *vport;
        int err;
 
+       reply = ovs_vport_cmd_alloc_info();
+       if (!reply)
+               return -ENOMEM;
+
        rcu_read_lock();
        vport = lookup_vport(sock_net(skb->sk), ovs_header, a);
        err = PTR_ERR(vport);
        if (IS_ERR(vport))
-               goto exit_unlock;
-
-       reply = ovs_vport_cmd_build_info(vport, info->snd_portid,
-                                        info->snd_seq, OVS_VPORT_CMD_NEW);
-       err = PTR_ERR(reply);
-       if (IS_ERR(reply))
-               goto exit_unlock;
-
+               goto exit_unlock_free;
+       err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid,
+                                     info->snd_seq, 0, OVS_VPORT_CMD_NEW);
+       BUG_ON(err < 0);
        rcu_read_unlock();
 
        return genlmsg_reply(reply, info);
 
-exit_unlock:
+exit_unlock_free:
        rcu_read_unlock();
+       kfree_skb(reply);
        return err;
 }
 
@@ -1792,7 +1936,16 @@ static int ovs_vport_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
        return skb->len;
 }
 
-static const struct genl_ops dp_vport_genl_ops[] = {
+static const struct nla_policy vport_policy[OVS_VPORT_ATTR_MAX + 1] = {
+       [OVS_VPORT_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
+       [OVS_VPORT_ATTR_STATS] = { .len = sizeof(struct ovs_vport_stats) },
+       [OVS_VPORT_ATTR_PORT_NO] = { .type = NLA_U32 },
+       [OVS_VPORT_ATTR_TYPE] = { .type = NLA_U32 },
+       [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NLA_U32 },
+       [OVS_VPORT_ATTR_OPTIONS] = { .type = NLA_NESTED },
+};
+
+static struct genl_ops dp_vport_genl_ops[] = {
        { .cmd = OVS_VPORT_CMD_NEW,
          .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
          .policy = vport_policy,
@@ -1816,26 +1969,25 @@ static const struct genl_ops dp_vport_genl_ops[] = {
        },
 };
 
-struct genl_family_and_ops {
-       struct genl_family *family;
-       const struct genl_ops *ops;
-       int n_ops;
-       const struct genl_multicast_group *group;
+struct genl_family dp_vport_genl_family = {
+       .id = GENL_ID_GENERATE,
+       .hdrsize = sizeof(struct ovs_header),
+       .name = OVS_VPORT_FAMILY,
+       .version = OVS_VPORT_VERSION,
+       .maxattr = OVS_VPORT_ATTR_MAX,
+       .netnsok = true,
+       .parallel_ops = true,
+       .ops = dp_vport_genl_ops,
+       .n_ops = ARRAY_SIZE(dp_vport_genl_ops),
+       .mcgrps = &ovs_dp_vport_multicast_group,
+       .n_mcgrps = 1,
 };
 
-static const struct genl_family_and_ops dp_genl_families[] = {
-       { &dp_datapath_genl_family,
-         dp_datapath_genl_ops, ARRAY_SIZE(dp_datapath_genl_ops),
-         &ovs_dp_datapath_multicast_group },
-       { &dp_vport_genl_family,
-         dp_vport_genl_ops, ARRAY_SIZE(dp_vport_genl_ops),
-         &ovs_dp_vport_multicast_group },
-       { &dp_flow_genl_family,
-         dp_flow_genl_ops, ARRAY_SIZE(dp_flow_genl_ops),
-         &ovs_dp_flow_multicast_group },
-       { &dp_packet_genl_family,
-         dp_packet_genl_ops, ARRAY_SIZE(dp_packet_genl_ops),
-         NULL },
+static struct genl_family * const dp_genl_families[] = {
+       &dp_datapath_genl_family,
+       &dp_vport_genl_family,
+       &dp_flow_genl_family,
+       &dp_packet_genl_family,
 };
 
 static void dp_unregister_genl(int n_families)
@@ -1843,33 +1995,25 @@ static void dp_unregister_genl(int n_families)
        int i;
 
        for (i = 0; i < n_families; i++)
-               genl_unregister_family(dp_genl_families[i].family);
+               genl_unregister_family(dp_genl_families[i]);
 }
 
 static int dp_register_genl(void)
 {
-       int n_registered;
        int err;
        int i;
 
-       n_registered = 0;
        for (i = 0; i < ARRAY_SIZE(dp_genl_families); i++) {
-               const struct genl_family_and_ops *f = &dp_genl_families[i];
 
-               f->family->ops = f->ops;
-               f->family->n_ops = f->n_ops;
-               f->family->mcgrps = f->group;
-               f->family->n_mcgrps = f->group ? 1 : 0;
-               err = genl_register_family(f->family);
+               err = genl_register_family(dp_genl_families[i]);
                if (err)
                        goto error;
-               n_registered++;
        }
 
        return 0;
 
 error:
-       dp_unregister_genl(n_registered);
+       dp_unregister_genl(i);
        return err;
 }
 
index 05317380fc03a03af708716f162738727cab5fff..7ede507500d7daa1cca3d352f9923122f5e63709 100644 (file)
@@ -194,7 +194,9 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq,
 int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb);
 void ovs_dp_notify_wq(struct work_struct *work);
 
-#define OVS_NLERR(fmt, ...) \
-       pr_info_once("netlink: " fmt, ##__VA_ARGS__)
-
+#define OVS_NLERR(fmt, ...)                                    \
+do {                                                           \
+       if (net_ratelimit())                                    \
+               pr_info("netlink: " fmt, ##__VA_ARGS__);        \
+} while (0)
 #endif /* datapath.h */
index 2998989e76db0a7ccb8e25ef11aa393180956593..334751cb15289c4f0ca00bedec960ee1cfe19ba0 100644 (file)
@@ -64,88 +64,110 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies)
 void ovs_flow_stats_update(struct sw_flow *flow, struct sk_buff *skb)
 {
        struct flow_stats *stats;
-       __be16 tcp_flags = 0;
-
-       if (!flow->stats.is_percpu)
-               stats = flow->stats.stat;
-       else
-               stats = this_cpu_ptr(flow->stats.cpu_stats);
-
-       if ((flow->key.eth.type == htons(ETH_P_IP) ||
-            flow->key.eth.type == htons(ETH_P_IPV6)) &&
-           flow->key.ip.frag != OVS_FRAG_TYPE_LATER &&
-           flow->key.ip.proto == IPPROTO_TCP &&
-           likely(skb->len >= skb_transport_offset(skb) + sizeof(struct tcphdr))) {
-               tcp_flags = TCP_FLAGS_BE16(tcp_hdr(skb));
+       __be16 tcp_flags = flow->key.tp.flags;
+       int node = numa_node_id();
+
+       stats = rcu_dereference(flow->stats[node]);
+
+       /* Check if already have node-specific stats. */
+       if (likely(stats)) {
+               spin_lock(&stats->lock);
+               /* Mark if we write on the pre-allocated stats. */
+               if (node == 0 && unlikely(flow->stats_last_writer != node))
+                       flow->stats_last_writer = node;
+       } else {
+               stats = rcu_dereference(flow->stats[0]); /* Pre-allocated. */
+               spin_lock(&stats->lock);
+
+               /* If the current NUMA-node is the only writer on the
+                * pre-allocated stats keep using them.
+                */
+               if (unlikely(flow->stats_last_writer != node)) {
+                       /* A previous locker may have already allocated the
+                        * stats, so we need to check again.  If node-specific
+                        * stats were already allocated, we update the pre-
+                        * allocated stats as we have already locked them.
+                        */
+                       if (likely(flow->stats_last_writer != NUMA_NO_NODE)
+                           && likely(!rcu_dereference(flow->stats[node]))) {
+                               /* Try to allocate node-specific stats. */
+                               struct flow_stats *new_stats;
+
+                               new_stats =
+                                       kmem_cache_alloc_node(flow_stats_cache,
+                                                             GFP_THISNODE |
+                                                             __GFP_NOMEMALLOC,
+                                                             node);
+                               if (likely(new_stats)) {
+                                       new_stats->used = jiffies;
+                                       new_stats->packet_count = 1;
+                                       new_stats->byte_count = skb->len;
+                                       new_stats->tcp_flags = tcp_flags;
+                                       spin_lock_init(&new_stats->lock);
+
+                                       rcu_assign_pointer(flow->stats[node],
+                                                          new_stats);
+                                       goto unlock;
+                               }
+                       }
+                       flow->stats_last_writer = node;
+               }
        }
 
-       spin_lock(&stats->lock);
        stats->used = jiffies;
        stats->packet_count++;
        stats->byte_count += skb->len;
        stats->tcp_flags |= tcp_flags;
+unlock:
        spin_unlock(&stats->lock);
 }
 
-static void stats_read(struct flow_stats *stats,
-                      struct ovs_flow_stats *ovs_stats,
-                      unsigned long *used, __be16 *tcp_flags)
-{
-       spin_lock(&stats->lock);
-       if (!*used || time_after(stats->used, *used))
-               *used = stats->used;
-       *tcp_flags |= stats->tcp_flags;
-       ovs_stats->n_packets += stats->packet_count;
-       ovs_stats->n_bytes += stats->byte_count;
-       spin_unlock(&stats->lock);
-}
-
-void ovs_flow_stats_get(struct sw_flow *flow, struct ovs_flow_stats *ovs_stats,
+/* Must be called with rcu_read_lock or ovs_mutex. */
+void ovs_flow_stats_get(const struct sw_flow *flow,
+                       struct ovs_flow_stats *ovs_stats,
                        unsigned long *used, __be16 *tcp_flags)
 {
-       int cpu;
+       int node;
 
        *used = 0;
        *tcp_flags = 0;
        memset(ovs_stats, 0, sizeof(*ovs_stats));
 
-       local_bh_disable();
-       if (!flow->stats.is_percpu) {
-               stats_read(flow->stats.stat, ovs_stats, used, tcp_flags);
-       } else {
-               for_each_possible_cpu(cpu) {
-                       struct flow_stats *stats;
+       for_each_node(node) {
+               struct flow_stats *stats = rcu_dereference_ovsl(flow->stats[node]);
 
-                       stats = per_cpu_ptr(flow->stats.cpu_stats, cpu);
-                       stats_read(stats, ovs_stats, used, tcp_flags);
+               if (stats) {
+                       /* Local CPU may write on non-local stats, so we must
+                        * block bottom-halves here.
+                        */
+                       spin_lock_bh(&stats->lock);
+                       if (!*used || time_after(stats->used, *used))
+                               *used = stats->used;
+                       *tcp_flags |= stats->tcp_flags;
+                       ovs_stats->n_packets += stats->packet_count;
+                       ovs_stats->n_bytes += stats->byte_count;
+                       spin_unlock_bh(&stats->lock);
                }
        }
-       local_bh_enable();
-}
-
-static void stats_reset(struct flow_stats *stats)
-{
-       spin_lock(&stats->lock);
-       stats->used = 0;
-       stats->packet_count = 0;
-       stats->byte_count = 0;
-       stats->tcp_flags = 0;
-       spin_unlock(&stats->lock);
 }
 
+/* Called with ovs_mutex. */
 void ovs_flow_stats_clear(struct sw_flow *flow)
 {
-       int cpu;
-
-       local_bh_disable();
-       if (!flow->stats.is_percpu) {
-               stats_reset(flow->stats.stat);
-       } else {
-               for_each_possible_cpu(cpu) {
-                       stats_reset(per_cpu_ptr(flow->stats.cpu_stats, cpu));
+       int node;
+
+       for_each_node(node) {
+               struct flow_stats *stats = ovsl_dereference(flow->stats[node]);
+
+               if (stats) {
+                       spin_lock_bh(&stats->lock);
+                       stats->used = 0;
+                       stats->packet_count = 0;
+                       stats->byte_count = 0;
+                       stats->tcp_flags = 0;
+                       spin_unlock_bh(&stats->lock);
                }
        }
-       local_bh_enable();
 }
 
 static int check_header(struct sk_buff *skb, int len)
@@ -332,8 +354,8 @@ static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key,
        /* The ICMPv6 type and code fields use the 16-bit transport port
         * fields, so we need to store them in 16-bit network byte order.
         */
-       key->ipv6.tp.src = htons(icmp->icmp6_type);
-       key->ipv6.tp.dst = htons(icmp->icmp6_code);
+       key->tp.src = htons(icmp->icmp6_type);
+       key->tp.dst = htons(icmp->icmp6_code);
 
        if (icmp->icmp6_code == 0 &&
            (icmp->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION ||
@@ -372,14 +394,14 @@ static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key,
                            && opt_len == 8) {
                                if (unlikely(!is_zero_ether_addr(key->ipv6.nd.sll)))
                                        goto invalid;
-                               memcpy(key->ipv6.nd.sll,
-                                   &nd->opt[offset+sizeof(*nd_opt)], ETH_ALEN);
+                               ether_addr_copy(key->ipv6.nd.sll,
+                                               &nd->opt[offset+sizeof(*nd_opt)]);
                        } else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LL_ADDR
                                   && opt_len == 8) {
                                if (unlikely(!is_zero_ether_addr(key->ipv6.nd.tll)))
                                        goto invalid;
-                               memcpy(key->ipv6.nd.tll,
-                                   &nd->opt[offset+sizeof(*nd_opt)], ETH_ALEN);
+                               ether_addr_copy(key->ipv6.nd.tll,
+                                               &nd->opt[offset+sizeof(*nd_opt)]);
                        }
 
                        icmp_len -= opt_len;
@@ -439,8 +461,8 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
         * header in the linear data area.
         */
        eth = eth_hdr(skb);
-       memcpy(key->eth.src, eth->h_source, ETH_ALEN);
-       memcpy(key->eth.dst, eth->h_dest, ETH_ALEN);
+       ether_addr_copy(key->eth.src, eth->h_source);
+       ether_addr_copy(key->eth.dst, eth->h_dest);
 
        __skb_pull(skb, 2 * ETH_ALEN);
        /* We are going to push all headers that we pull, so no need to
@@ -495,21 +517,21 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
                if (key->ip.proto == IPPROTO_TCP) {
                        if (tcphdr_ok(skb)) {
                                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);
+                               key->tp.src = tcp->source;
+                               key->tp.dst = tcp->dest;
+                               key->tp.flags = TCP_FLAGS_BE16(tcp);
                        }
                } else if (key->ip.proto == IPPROTO_UDP) {
                        if (udphdr_ok(skb)) {
                                struct udphdr *udp = udp_hdr(skb);
-                               key->ipv4.tp.src = udp->source;
-                               key->ipv4.tp.dst = udp->dest;
+                               key->tp.src = udp->source;
+                               key->tp.dst = udp->dest;
                        }
                } else if (key->ip.proto == IPPROTO_SCTP) {
                        if (sctphdr_ok(skb)) {
                                struct sctphdr *sctp = sctp_hdr(skb);
-                               key->ipv4.tp.src = sctp->source;
-                               key->ipv4.tp.dst = sctp->dest;
+                               key->tp.src = sctp->source;
+                               key->tp.dst = sctp->dest;
                        }
                } else if (key->ip.proto == IPPROTO_ICMP) {
                        if (icmphdr_ok(skb)) {
@@ -517,8 +539,8 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
                                /* The ICMP type and code fields use the 16-bit
                                 * transport port fields, so we need to store
                                 * them in 16-bit network byte order. */
-                               key->ipv4.tp.src = htons(icmp->type);
-                               key->ipv4.tp.dst = htons(icmp->code);
+                               key->tp.src = htons(icmp->type);
+                               key->tp.dst = htons(icmp->code);
                        }
                }
 
@@ -538,8 +560,8 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
                                key->ip.proto = ntohs(arp->ar_op);
                        memcpy(&key->ipv4.addr.src, arp->ar_sip, sizeof(key->ipv4.addr.src));
                        memcpy(&key->ipv4.addr.dst, arp->ar_tip, sizeof(key->ipv4.addr.dst));
-                       memcpy(key->ipv4.arp.sha, arp->ar_sha, ETH_ALEN);
-                       memcpy(key->ipv4.arp.tha, arp->ar_tha, ETH_ALEN);
+                       ether_addr_copy(key->ipv4.arp.sha, arp->ar_sha);
+                       ether_addr_copy(key->ipv4.arp.tha, arp->ar_tha);
                }
        } else if (key->eth.type == htons(ETH_P_IPV6)) {
                int nh_len;             /* IPv6 Header + Extensions */
@@ -564,21 +586,21 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
                if (key->ip.proto == NEXTHDR_TCP) {
                        if (tcphdr_ok(skb)) {
                                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);
+                               key->tp.src = tcp->source;
+                               key->tp.dst = tcp->dest;
+                               key->tp.flags = TCP_FLAGS_BE16(tcp);
                        }
                } else if (key->ip.proto == NEXTHDR_UDP) {
                        if (udphdr_ok(skb)) {
                                struct udphdr *udp = udp_hdr(skb);
-                               key->ipv6.tp.src = udp->source;
-                               key->ipv6.tp.dst = udp->dest;
+                               key->tp.src = udp->source;
+                               key->tp.dst = udp->dest;
                        }
                } else if (key->ip.proto == NEXTHDR_SCTP) {
                        if (sctphdr_ok(skb)) {
                                struct sctphdr *sctp = sctp_hdr(skb);
-                               key->ipv6.tp.src = sctp->source;
-                               key->ipv6.tp.dst = sctp->dest;
+                               key->tp.src = sctp->source;
+                               key->tp.dst = sctp->dest;
                        }
                } else if (key->ip.proto == NEXTHDR_ICMP) {
                        if (icmp6hdr_ok(skb)) {
index 2d770e28a3a396f7da0a4c8e7baf4b29709ceb0c..ac395d2cd821631898116b89438af7eba5d40b2f 100644 (file)
@@ -47,7 +47,7 @@ struct ovs_key_ipv4_tunnel {
        __be16 tun_flags;
        u8   ipv4_tos;
        u8   ipv4_ttl;
-};
+} __packed __aligned(4); /* Minimize padding. */
 
 static inline void ovs_flow_tun_key_init(struct ovs_key_ipv4_tunnel *tun_key,
                                         const struct iphdr *iph, __be64 tun_id,
@@ -71,7 +71,7 @@ struct sw_flow_key {
                u32     priority;       /* Packet QoS priority. */
                u32     skb_mark;       /* SKB mark. */
                u16     in_port;        /* Input switch port (or DP_MAX_PORTS). */
-       } phy;
+       } __packed phy; /* Safe when right after 'tun_key'. */
        struct {
                u8     src[ETH_ALEN];   /* Ethernet source address. */
                u8     dst[ETH_ALEN];   /* Ethernet destination address. */
@@ -84,23 +84,21 @@ struct sw_flow_key {
                u8     ttl;             /* IP TTL/hop limit. */
                u8     frag;            /* One of OVS_FRAG_TYPE_*. */
        } ip;
+       struct {
+               __be16 src;             /* TCP/UDP/SCTP source port. */
+               __be16 dst;             /* TCP/UDP/SCTP destination port. */
+               __be16 flags;           /* TCP flags. */
+       } tp;
        union {
                struct {
                        struct {
                                __be32 src;     /* IP source address. */
                                __be32 dst;     /* IP destination address. */
                        } addr;
-                       union {
-                               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. */
-                                       u8 tha[ETH_ALEN];       /* ARP target hardware address. */
-                               } arp;
-                       };
+                       struct {
+                               u8 sha[ETH_ALEN];       /* ARP source hardware address. */
+                               u8 tha[ETH_ALEN];       /* ARP target hardware address. */
+                       } arp;
                } ipv4;
                struct {
                        struct {
@@ -108,11 +106,6 @@ struct sw_flow_key {
                                struct in6_addr dst;    /* IPv6 destination address. */
                        } addr;
                        __be32 label;                   /* IPv6 flow label. */
-                       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. */
                                u8 sll[ETH_ALEN];       /* ND source link layer address. */
@@ -155,24 +148,22 @@ struct flow_stats {
        __be16 tcp_flags;               /* Union of seen TCP flags. */
 };
 
-struct sw_flow_stats {
-       bool is_percpu;
-       union {
-               struct flow_stats *stat;
-               struct flow_stats __percpu *cpu_stats;
-       };
-};
-
 struct sw_flow {
        struct rcu_head rcu;
        struct hlist_node hash_node[2];
        u32 hash;
-
+       int stats_last_writer;          /* NUMA-node id of the last writer on
+                                        * 'stats[0]'.
+                                        */
        struct sw_flow_key key;
        struct sw_flow_key unmasked_key;
        struct sw_flow_mask *mask;
        struct sw_flow_actions __rcu *sf_acts;
-       struct sw_flow_stats stats;
+       struct flow_stats __rcu *stats[]; /* One for each NUMA node.  First one
+                                          * is allocated at flow creation time,
+                                          * the rest are allocated on demand
+                                          * while holding the 'stats[0].lock'.
+                                          */
 };
 
 struct arp_eth_header {
@@ -189,10 +180,10 @@ struct arp_eth_header {
        unsigned char       ar_tip[4];          /* target IP address        */
 } __packed;
 
-void ovs_flow_stats_update(struct sw_flow *flow, struct sk_buff *skb);
-void ovs_flow_stats_get(struct sw_flow *flow, struct ovs_flow_stats *stats,
+void ovs_flow_stats_update(struct sw_flow *, struct sk_buff *);
+void ovs_flow_stats_get(const struct sw_flow *, struct ovs_flow_stats *,
                        unsigned long *used, __be16 *tcp_flags);
-void ovs_flow_stats_clear(struct sw_flow *flow);
+void ovs_flow_stats_clear(struct sw_flow *);
 u64 ovs_flow_used_time(unsigned long flow_jiffies);
 
 int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *);
index 4d000acaed0db5cc2052ae55f5b0cb7be6472b79..d757848da89ca734ac95cef806c2dd314f881bf6 100644 (file)
@@ -16,6 +16,8 @@
  * 02110-1301, USA
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include "flow.h"
 #include "datapath.h"
 #include <linux/uaccess.h>
@@ -202,11 +204,11 @@ static bool match_validate(const struct sw_flow_match *match,
                                if (match->mask && (match->mask->key.ip.proto == 0xff))
                                        mask_allowed |= 1 << OVS_KEY_ATTR_ICMPV6;
 
-                               if (match->key->ipv6.tp.src ==
+                               if (match->key->tp.src ==
                                                htons(NDISC_NEIGHBOUR_SOLICITATION) ||
-                                   match->key->ipv6.tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) {
+                                   match->key->tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) {
                                        key_expected |= 1 << OVS_KEY_ATTR_ND;
-                                       if (match->mask && (match->mask->key.ipv6.tp.src == htons(0xffff)))
+                                       if (match->mask && (match->mask->key.tp.src == htons(0xffff)))
                                                mask_allowed |= 1 << OVS_KEY_ATTR_ND;
                                }
                        }
@@ -216,14 +218,14 @@ static bool match_validate(const struct sw_flow_match *match,
        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);
+                               (unsigned long long)key_attrs, (unsigned long long)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);
+                               (unsigned long long)mask_attrs, (unsigned long long)mask_allowed);
                return false;
        }
 
@@ -266,20 +268,6 @@ static bool is_all_zero(const u8 *fp, size_t size)
        return true;
 }
 
-static bool is_all_set(const u8 *fp, size_t size)
-{
-       int i;
-
-       if (!fp)
-               return false;
-
-       for (i = 0; i < size; i++)
-               if (fp[i] != 0xff)
-                       return false;
-
-       return true;
-}
-
 static int __parse_flow_nlattrs(const struct nlattr *attr,
                                const struct nlattr *a[],
                                u64 *attrsp, bool nz)
@@ -501,9 +489,8 @@ static int metadata_from_nlattrs(struct sw_flow_match *match,  u64 *attrs,
        return 0;
 }
 
-static int ovs_key_from_nlattrs(struct sw_flow_match *match,  bool *exact_5tuple,
-                               u64 attrs, const struct nlattr **a,
-                               bool is_mask)
+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;
@@ -560,11 +547,6 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match,  bool *exact_5tuple
                SW_FLOW_KEY_PUT(match, eth.type, htons(ETH_P_802_2), is_mask);
        }
 
-       if (is_mask && exact_5tuple) {
-               if (match->mask->key.eth.type != htons(0xffff))
-                       *exact_5tuple = false;
-       }
-
        if (attrs & (1 << OVS_KEY_ATTR_IPV4)) {
                const struct ovs_key_ipv4 *ipv4_key;
 
@@ -587,13 +569,6 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match,  bool *exact_5tuple
                SW_FLOW_KEY_PUT(match, ipv4.addr.dst,
                                ipv4_key->ipv4_dst, is_mask);
                attrs &= ~(1 << OVS_KEY_ATTR_IPV4);
-
-               if (is_mask && exact_5tuple && *exact_5tuple) {
-                       if (ipv4_key->ipv4_proto != 0xff ||
-                           ipv4_key->ipv4_src != htonl(0xffffffff) ||
-                           ipv4_key->ipv4_dst != htonl(0xffffffff))
-                               *exact_5tuple = false;
-               }
        }
 
        if (attrs & (1 << OVS_KEY_ATTR_IPV6)) {
@@ -625,13 +600,6 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match,  bool *exact_5tuple
                                is_mask);
 
                attrs &= ~(1 << OVS_KEY_ATTR_IPV6);
-
-               if (is_mask && exact_5tuple && *exact_5tuple) {
-                       if (ipv6_key->ipv6_proto != 0xff ||
-                           !is_all_set((u8 *)ipv6_key->ipv6_src, sizeof(match->key->ipv6.addr.src)) ||
-                           !is_all_set((u8 *)ipv6_key->ipv6_dst, sizeof(match->key->ipv6.addr.dst)))
-                               *exact_5tuple = false;
-               }
        }
 
        if (attrs & (1 << OVS_KEY_ATTR_ARP)) {
@@ -662,32 +630,18 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match,  bool *exact_5tuple
                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);
-               }
+               SW_FLOW_KEY_PUT(match, tp.src, tcp_key->tcp_src, is_mask);
+               SW_FLOW_KEY_PUT(match, tp.dst, tcp_key->tcp_dst, is_mask);
                attrs &= ~(1 << OVS_KEY_ATTR_TCP);
-
-               if (is_mask && exact_5tuple && *exact_5tuple &&
-                   (tcp_key->tcp_src != htons(0xffff) ||
-                    tcp_key->tcp_dst != htons(0xffff)))
-                       *exact_5tuple = false;
        }
 
        if (attrs & (1 << OVS_KEY_ATTR_TCP_FLAGS)) {
                if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) {
-                       SW_FLOW_KEY_PUT(match, ipv4.tp.flags,
+                       SW_FLOW_KEY_PUT(match, tp.flags,
                                        nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]),
                                        is_mask);
                } else {
-                       SW_FLOW_KEY_PUT(match, ipv6.tp.flags,
+                       SW_FLOW_KEY_PUT(match, tp.flags,
                                        nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]),
                                        is_mask);
                }
@@ -698,40 +652,17 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match,  bool *exact_5tuple
                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);
-               }
+               SW_FLOW_KEY_PUT(match, tp.src, udp_key->udp_src, is_mask);
+               SW_FLOW_KEY_PUT(match, tp.dst, udp_key->udp_dst, is_mask);
                attrs &= ~(1 << OVS_KEY_ATTR_UDP);
-
-               if (is_mask && exact_5tuple && *exact_5tuple &&
-                   (udp_key->udp_src != htons(0xffff) ||
-                    udp_key->udp_dst != htons(0xffff)))
-                       *exact_5tuple = false;
        }
 
        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);
-               }
+               SW_FLOW_KEY_PUT(match, tp.src, sctp_key->sctp_src, is_mask);
+               SW_FLOW_KEY_PUT(match, tp.dst, sctp_key->sctp_dst, is_mask);
                attrs &= ~(1 << OVS_KEY_ATTR_SCTP);
        }
 
@@ -739,9 +670,9 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match,  bool *exact_5tuple
                const struct ovs_key_icmp *icmp_key;
 
                icmp_key = nla_data(a[OVS_KEY_ATTR_ICMP]);
-               SW_FLOW_KEY_PUT(match, ipv4.tp.src,
+               SW_FLOW_KEY_PUT(match, tp.src,
                                htons(icmp_key->icmp_type), is_mask);
-               SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
+               SW_FLOW_KEY_PUT(match, tp.dst,
                                htons(icmp_key->icmp_code), is_mask);
                attrs &= ~(1 << OVS_KEY_ATTR_ICMP);
        }
@@ -750,9 +681,9 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match,  bool *exact_5tuple
                const struct ovs_key_icmpv6 *icmpv6_key;
 
                icmpv6_key = nla_data(a[OVS_KEY_ATTR_ICMPV6]);
-               SW_FLOW_KEY_PUT(match, ipv6.tp.src,
+               SW_FLOW_KEY_PUT(match, tp.src,
                                htons(icmpv6_key->icmpv6_type), is_mask);
-               SW_FLOW_KEY_PUT(match, ipv6.tp.dst,
+               SW_FLOW_KEY_PUT(match, tp.dst,
                                htons(icmpv6_key->icmpv6_code), is_mask);
                attrs &= ~(1 << OVS_KEY_ATTR_ICMPV6);
        }
@@ -800,7 +731,6 @@ static void sw_flow_mask_set(struct sw_flow_mask *mask,
  * attribute specifies the mask field of the wildcarded flow.
  */
 int ovs_nla_get_match(struct sw_flow_match *match,
-                     bool *exact_5tuple,
                      const struct nlattr *key,
                      const struct nlattr *mask)
 {
@@ -848,13 +778,10 @@ int ovs_nla_get_match(struct sw_flow_match *match,
                }
        }
 
-       err = ovs_key_from_nlattrs(match, NULL, key_attrs, a, false);
+       err = ovs_key_from_nlattrs(match, key_attrs, a, false);
        if (err)
                return err;
 
-       if (exact_5tuple)
-               *exact_5tuple = true;
-
        if (mask) {
                err = parse_flow_mask_nlattrs(mask, a, &mask_attrs);
                if (err)
@@ -892,7 +819,7 @@ int ovs_nla_get_match(struct sw_flow_match *match,
                        }
                }
 
-               err = ovs_key_from_nlattrs(match, exact_5tuple, mask_attrs, a, true);
+               err = ovs_key_from_nlattrs(match, mask_attrs, a, true);
                if (err)
                        return err;
        } else {
@@ -982,8 +909,8 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey,
                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);
+       ether_addr_copy(eth_key->eth_src, output->eth.src);
+       ether_addr_copy(eth_key->eth_dst, output->eth.dst);
 
        if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q)) {
                __be16 eth_type;
@@ -1055,8 +982,8 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey,
                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);
+               ether_addr_copy(arp_key->arp_sha, output->ipv4.arp.sha);
+               ether_addr_copy(arp_key->arp_tha, output->ipv4.arp.tha);
        }
 
        if ((swkey->eth.type == htons(ETH_P_IP) ||
@@ -1070,19 +997,11 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey,
                        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;
-                       }
+                       tcp_key->tcp_src = output->tp.src;
+                       tcp_key->tcp_dst = output->tp.dst;
+                       if (nla_put_be16(skb, OVS_KEY_ATTR_TCP_FLAGS,
+                                        output->tp.flags))
+                               goto nla_put_failure;
                } else if (swkey->ip.proto == IPPROTO_UDP) {
                        struct ovs_key_udp *udp_key;
 
@@ -1090,13 +1009,8 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey,
                        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;
-                       }
+                       udp_key->udp_src = output->tp.src;
+                       udp_key->udp_dst = output->tp.dst;
                } else if (swkey->ip.proto == IPPROTO_SCTP) {
                        struct ovs_key_sctp *sctp_key;
 
@@ -1104,13 +1018,8 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey,
                        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;
-                       }
+                       sctp_key->sctp_src = output->tp.src;
+                       sctp_key->sctp_dst = output->tp.dst;
                } else if (swkey->eth.type == htons(ETH_P_IP) &&
                           swkey->ip.proto == IPPROTO_ICMP) {
                        struct ovs_key_icmp *icmp_key;
@@ -1119,8 +1028,8 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey,
                        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);
+                       icmp_key->icmp_type = ntohs(output->tp.src);
+                       icmp_key->icmp_code = ntohs(output->tp.dst);
                } else if (swkey->eth.type == htons(ETH_P_IPV6) &&
                           swkey->ip.proto == IPPROTO_ICMPV6) {
                        struct ovs_key_icmpv6 *icmpv6_key;
@@ -1130,8 +1039,8 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey,
                        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);
+                       icmpv6_key->icmpv6_type = ntohs(output->tp.src);
+                       icmpv6_key->icmpv6_code = ntohs(output->tp.dst);
 
                        if (icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_SOLICITATION ||
                            icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_ADVERTISEMENT) {
@@ -1143,8 +1052,8 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey,
                                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);
+                               ether_addr_copy(nd_key->nd_sll, output->ipv6.nd.sll);
+                               ether_addr_copy(nd_key->nd_tll, output->ipv6.nd.tll);
                        }
                }
        }
@@ -1309,13 +1218,10 @@ static int validate_and_copy_sample(const struct nlattr *attr,
 
 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;
-       }
+       if ((flow_key->eth.type == htons(ETH_P_IP) ||
+            flow_key->eth.type == htons(ETH_P_IPV6)) &&
+           (flow_key->tp.src || flow_key->tp.dst))
+               return 0;
 
        return -EINVAL;
 }
index b31fbe28bc7a81a0640546b6e55be8841c32f8a5..440151045d3946329bf01e4fd5a1c81f0fd4e906 100644 (file)
@@ -45,7 +45,6 @@ int ovs_nla_put_flow(const struct sw_flow_key *,
 int ovs_nla_get_flow_metadata(struct sw_flow *flow,
                              const struct nlattr *attr);
 int ovs_nla_get_match(struct sw_flow_match *match,
-                     bool *exact_5tuple,
                      const struct nlattr *,
                      const struct nlattr *);
 
index 3c268b3d71c34baa0a8c70888823b37da454fffd..574c3abc9b307ef6609f8f8dc09ade4b3253b814 100644 (file)
@@ -48,6 +48,7 @@
 #define REHASH_INTERVAL                (10 * 60 * HZ)
 
 static struct kmem_cache *flow_cache;
+struct kmem_cache *flow_stats_cache __read_mostly;
 
 static u16 range_n_bytes(const struct sw_flow_key_range *range)
 {
@@ -57,8 +58,10 @@ static u16 range_n_bytes(const struct sw_flow_key_range *range)
 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);
+       const long *m = (const long *)((const u8 *)&mask->key +
+                               mask->range.start);
+       const long *s = (const long *)((const u8 *)src +
+                               mask->range.start);
        long *d = (long *)((u8 *)dst + mask->range.start);
        int i;
 
@@ -70,10 +73,11 @@ void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src,
                *d++ = *s++ & *m++;
 }
 
-struct sw_flow *ovs_flow_alloc(bool percpu_stats)
+struct sw_flow *ovs_flow_alloc(void)
 {
        struct sw_flow *flow;
-       int cpu;
+       struct flow_stats *stats;
+       int node;
 
        flow = kmem_cache_alloc(flow_cache, GFP_KERNEL);
        if (!flow)
@@ -81,27 +85,22 @@ struct sw_flow *ovs_flow_alloc(bool percpu_stats)
 
        flow->sf_acts = NULL;
        flow->mask = NULL;
+       flow->stats_last_writer = NUMA_NO_NODE;
 
-       flow->stats.is_percpu = percpu_stats;
+       /* Initialize the default stat node. */
+       stats = kmem_cache_alloc_node(flow_stats_cache,
+                                     GFP_KERNEL | __GFP_ZERO, 0);
+       if (!stats)
+               goto err;
 
-       if (!percpu_stats) {
-               flow->stats.stat = kzalloc(sizeof(*flow->stats.stat), GFP_KERNEL);
-               if (!flow->stats.stat)
-                       goto err;
+       spin_lock_init(&stats->lock);
 
-               spin_lock_init(&flow->stats.stat->lock);
-       } else {
-               flow->stats.cpu_stats = alloc_percpu(struct flow_stats);
-               if (!flow->stats.cpu_stats)
-                       goto err;
+       RCU_INIT_POINTER(flow->stats[0], stats);
 
-               for_each_possible_cpu(cpu) {
-                       struct flow_stats *cpu_stats;
+       for_each_node(node)
+               if (node != 0)
+                       RCU_INIT_POINTER(flow->stats[node], NULL);
 
-                       cpu_stats = per_cpu_ptr(flow->stats.cpu_stats, cpu);
-                       spin_lock_init(&cpu_stats->lock);
-               }
-       }
        return flow;
 err:
        kmem_cache_free(flow_cache, flow);
@@ -138,11 +137,13 @@ static struct flex_array *alloc_buckets(unsigned int n_buckets)
 
 static void flow_free(struct sw_flow *flow)
 {
-       kfree((struct sf_flow_acts __force *)flow->sf_acts);
-       if (flow->stats.is_percpu)
-               free_percpu(flow->stats.cpu_stats);
-       else
-               kfree(flow->stats.stat);
+       int node;
+
+       kfree((struct sw_flow_actions __force *)flow->sf_acts);
+       for_each_node(node)
+               if (flow->stats[node])
+                       kmem_cache_free(flow_stats_cache,
+                                       (struct flow_stats __force *)flow->stats[node]);
        kmem_cache_free(flow_cache, flow);
 }
 
@@ -158,25 +159,6 @@ void ovs_flow_free(struct sw_flow *flow, bool deferred)
        if (!flow)
                return;
 
-       if (flow->mask) {
-               struct sw_flow_mask *mask = flow->mask;
-
-               /* ovs-lock is required to protect mask-refcount and
-                * mask list.
-                */
-               ASSERT_OVSL();
-               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);
-               }
-       }
-
        if (deferred)
                call_rcu(&flow->rcu, rcu_free_flow_callback);
        else
@@ -375,7 +357,7 @@ int ovs_flow_tbl_flush(struct flow_table *flow_table)
 static u32 flow_hash(const struct sw_flow_key *key, int key_start,
                     int key_end)
 {
-       u32 *hash_key = (u32 *)((u8 *)key + key_start);
+       const u32 *hash_key = (const u32 *)((const u8 *)key + key_start);
        int hash_u32s = (key_end - key_start) >> 2;
 
        /* Make sure number of hash bytes are multiple of u32. */
@@ -397,8 +379,8 @@ 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);
+       const long *cp1 = (const long *)((const u8 *)key1 + key_start);
+       const long *cp2 = (const long *)((const u8 *)key2 + key_start);
        long diffs = 0;
        int i;
 
@@ -490,6 +472,25 @@ static struct table_instance *table_instance_expand(struct table_instance *ti)
        return table_instance_rehash(ti, ti->n_buckets * 2);
 }
 
+/* Remove 'mask' from the mask list, if it is not needed any more. */
+static void flow_mask_remove(struct flow_table *tbl, struct sw_flow_mask *mask)
+{
+       if (mask) {
+               /* ovs-lock is required to protect mask-refcount and
+                * mask list.
+                */
+               ASSERT_OVSL();
+               BUG_ON(!mask->ref_count);
+               mask->ref_count--;
+
+               if (!mask->ref_count) {
+                       list_del_rcu(&mask->list);
+                       kfree_rcu(mask, rcu);
+               }
+       }
+}
+
+/* Must be called with OVS mutex held. */
 void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow)
 {
        struct table_instance *ti = ovsl_dereference(table->ti);
@@ -497,6 +498,11 @@ void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow)
        BUG_ON(table->count == 0);
        hlist_del_rcu(&flow->hash_node[ti->node_ver]);
        table->count--;
+
+       /* RCU delete the mask. 'flow->mask' is not NULLed, as it should be
+        * accessible as long as the RCU read lock is held.
+        */
+       flow_mask_remove(table, flow->mask);
 }
 
 static struct sw_flow_mask *mask_alloc(void)
@@ -513,8 +519,8 @@ static struct sw_flow_mask *mask_alloc(void)
 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;
+       const u8 *a_ = (const u8 *)&a->key + a->range.start;
+       const u8 *b_ = (const u8 *)&b->key + b->range.start;
 
        return  (a->range.end == b->range.end)
                && (a->range.start == b->range.start)
@@ -559,6 +565,7 @@ static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow,
        return 0;
 }
 
+/* Must be called with OVS mutex held. */
 int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow,
                        struct sw_flow_mask *mask)
 {
@@ -597,16 +604,28 @@ 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);
+       flow_cache = kmem_cache_create("sw_flow", sizeof(struct sw_flow)
+                                      + (num_possible_nodes()
+                                         * sizeof(struct flow_stats *)),
+                                      0, 0, NULL);
        if (flow_cache == NULL)
                return -ENOMEM;
 
+       flow_stats_cache
+               = kmem_cache_create("sw_flow_stats", sizeof(struct flow_stats),
+                                   0, SLAB_HWCACHE_ALIGN, NULL);
+       if (flow_stats_cache == NULL) {
+               kmem_cache_destroy(flow_cache);
+               flow_cache = NULL;
+               return -ENOMEM;
+       }
+
        return 0;
 }
 
 /* Uninitializes the flow module. */
 void ovs_flow_exit(void)
 {
+       kmem_cache_destroy(flow_stats_cache);
        kmem_cache_destroy(flow_cache);
 }
index baaeb101924d81a4beb373be93e07287ed9be020..ca8a5820f6153f67fb9ad987c4c156be882c92ea 100644 (file)
@@ -52,10 +52,12 @@ struct flow_table {
        unsigned int count;
 };
 
+extern struct kmem_cache *flow_stats_cache;
+
 int ovs_flow_init(void);
 void ovs_flow_exit(void);
 
-struct sw_flow *ovs_flow_alloc(bool percpu_stats);
+struct sw_flow *ovs_flow_alloc(void);
 void ovs_flow_free(struct sw_flow *, bool deferred);
 
 int ovs_flow_tbl_init(struct flow_table *);
index ebb6e2442554c89fa2b02f1f8f06b0a3b8acf39e..35ec4fed09e228c7e6fe889d2701cb3a3de7748a 100644 (file)
@@ -172,7 +172,7 @@ static int gre_tnl_send(struct vport *vport, struct sk_buff *skb)
        df = OVS_CB(skb)->tun_key->tun_flags & TUNNEL_DONT_FRAGMENT ?
                htons(IP_DF) : 0;
 
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        return iptunnel_xmit(skb->sk, rt, skb, fl.saddr,
                             OVS_CB(skb)->tun_key->ipv4_dst, IPPROTO_GRE,
@@ -256,7 +256,7 @@ static void gre_tnl_destroy(struct vport *vport)
 
        ovs_net = net_generic(net, ovs_net_id);
 
-       rcu_assign_pointer(ovs_net->vport_net.gre_vport, NULL);
+       RCU_INIT_POINTER(ovs_net->vport_net.gre_vport, NULL);
        ovs_vport_deferred_free(vport);
        gre_exit();
 }
index 729c68763fe70d150793e1f01a023d4d8f1b78c0..789af9280e77264b4d7f65ddb6c333e96fa4147f 100644 (file)
@@ -130,7 +130,7 @@ static void do_setup(struct net_device *netdev)
        netdev->priv_flags &= ~IFF_TX_SKB_SHARING;
        netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
        netdev->destructor = internal_dev_destructor;
-       SET_ETHTOOL_OPS(netdev, &internal_dev_ethtool_ops);
+       netdev->ethtool_ops = &internal_dev_ethtool_ops;
        netdev->tx_queue_len = 0;
 
        netdev->features = NETIF_F_LLTX | NETIF_F_SG | NETIF_F_FRAGLIST |
index e797a50ac2beec3a019bacf15e2e7c593355a46f..0edbd95c60e73abfba45a145978ac1d829bb1321 100644 (file)
@@ -122,7 +122,7 @@ static struct vport *vxlan_tnl_create(const struct vport_parms *parms)
        vxlan_port = vxlan_vport(vport);
        strncpy(vxlan_port->name, parms->name, IFNAMSIZ);
 
-       vs = vxlan_sock_add(net, htons(dst_port), vxlan_rcv, vport, true, false);
+       vs = vxlan_sock_add(net, htons(dst_port), vxlan_rcv, vport, true, 0);
        if (IS_ERR(vs)) {
                ovs_vport_free(vport);
                return (void *)vs;
@@ -170,7 +170,7 @@ static int vxlan_tnl_send(struct vport *vport, struct sk_buff *skb)
        df = OVS_CB(skb)->tun_key->tun_flags & TUNNEL_DONT_FRAGMENT ?
                htons(IP_DF) : 0;
 
-       skb->local_df = 1;
+       skb->ignore_df = 1;
 
        inet_get_local_port_range(net, &port_min, &port_max);
        src_port = vxlan_src_port(port_min, port_max, skb);
@@ -180,7 +180,8 @@ static int vxlan_tnl_send(struct vport *vport, struct sk_buff *skb)
                             OVS_CB(skb)->tun_key->ipv4_tos,
                             OVS_CB(skb)->tun_key->ipv4_ttl, df,
                             src_port, dst_port,
-                            htonl(be64_to_cpu(OVS_CB(skb)->tun_key->tun_id) << 8));
+                            htonl(be64_to_cpu(OVS_CB(skb)->tun_key->tun_id) << 8),
+                            false);
        if (err < 0)
                ip_rt_put(rt);
 error:
index d7e50a17396c5563c778ca9e0e07f9c9a730b56a..8d721e62f388d9990b7850942196440550a2a6bb 100644 (file)
@@ -172,7 +172,7 @@ void ovs_vport_deferred_free(struct vport *vport);
  */
 static inline void *vport_priv(const struct vport *vport)
 {
-       return (u8 *)vport + ALIGN(sizeof(struct vport), VPORT_ALIGN);
+       return (u8 *)(uintptr_t)vport + ALIGN(sizeof(struct vport), VPORT_ALIGN);
 }
 
 /**
@@ -185,9 +185,9 @@ static inline void *vport_priv(const struct vport *vport)
  * the result of a hash table lookup.  @priv must point to the start of the
  * private data area.
  */
-static inline struct vport *vport_from_priv(const void *priv)
+static inline struct vport *vport_from_priv(void *priv)
 {
-       return (struct vport *)(priv - ALIGN(sizeof(struct vport), VPORT_ALIGN));
+       return (struct vport *)((u8 *)priv - ALIGN(sizeof(struct vport), VPORT_ALIGN));
 }
 
 void ovs_vport_receive(struct vport *, struct sk_buff *,
index 37be6e226d1b46fefe8e0cf554580b1faf19cfb8..1dde91e3dc7033c575dcfc041a23402f98e52239 100644 (file)
@@ -298,7 +298,7 @@ void rds_ib_send_cq_comp_handler(struct ib_cq *cq, void *context)
                rds_ib_stats_inc(s_ib_tx_cq_event);
 
                if (wc.wr_id == RDS_IB_ACK_WR_ID) {
-                       if (ic->i_ack_queued + HZ/2 < jiffies)
+                       if (time_after(jiffies, ic->i_ack_queued + HZ/2))
                                rds_ib_stats_inc(s_ib_tx_stalled);
                        rds_ib_ack_send_complete(ic);
                        continue;
@@ -315,7 +315,7 @@ void rds_ib_send_cq_comp_handler(struct ib_cq *cq, void *context)
 
                        rm = rds_ib_send_unmap_op(ic, send, wc.status);
 
-                       if (send->s_queued + HZ/2 < jiffies)
+                       if (time_after(jiffies, send->s_queued + HZ/2))
                                rds_ib_stats_inc(s_ib_tx_stalled);
 
                        if (send->s_op) {
index e40c3c5db2c41e543abed12149c791fa9edd2ec6..9105ea03aec5dc05bad0221eb00c4794eb6e463a 100644 (file)
@@ -232,7 +232,7 @@ void rds_iw_send_cq_comp_handler(struct ib_cq *cq, void *context)
                }
 
                if (wc.wr_id == RDS_IW_ACK_WR_ID) {
-                       if (ic->i_ack_queued + HZ/2 < jiffies)
+                       if (time_after(jiffies, ic->i_ack_queued + HZ/2))
                                rds_iw_stats_inc(s_iw_tx_stalled);
                        rds_iw_ack_send_complete(ic);
                        continue;
@@ -267,7 +267,7 @@ void rds_iw_send_cq_comp_handler(struct ib_cq *cq, void *context)
 
                        send->s_wr.opcode = 0xdead;
                        send->s_wr.num_sge = 1;
-                       if (send->s_queued + HZ/2 < jiffies)
+                       if (time_after(jiffies, send->s_queued + HZ/2))
                                rds_iw_stats_inc(s_iw_tx_stalled);
 
                        /* If a RDMA operation produced an error, signal this right
index 89c91515ed0c605b6ee63996d0c0a9692ea8fe91..139239d2cb228438e29f347b33035da15d5396c0 100644 (file)
@@ -111,8 +111,7 @@ static struct ctl_table rds_iw_sysctl_table[] = {
 
 void rds_iw_sysctl_exit(void)
 {
-       if (rds_iw_sysctl_hdr)
-               unregister_net_sysctl_table(rds_iw_sysctl_hdr);
+       unregister_net_sysctl_table(rds_iw_sysctl_hdr);
 }
 
 int rds_iw_sysctl_init(void)
index c2be901d19ee133b60c1b77006f02c88d8b1ec42..6cd9d1deafc395d6573b7e3b801e666106d1f257 100644 (file)
@@ -168,7 +168,7 @@ static int rds_rdma_listen_init(void)
                return ret;
        }
 
-       sin.sin_family = AF_INET,
+       sin.sin_family = AF_INET;
        sin.sin_addr.s_addr = (__force u32)htonl(INADDR_ANY);
        sin.sin_port = (__force u16)htons(RDS_PORT);
 
index b5cb2aa08f33aa62ac5bff73684fc228f74dce7e..c3b0cd43eb56689e395581c4757402bad531e271 100644 (file)
@@ -94,8 +94,7 @@ static struct ctl_table rds_sysctl_rds_table[] = {
 
 void rds_sysctl_exit(void)
 {
-       if (rds_sysctl_reg_table)
-               unregister_net_sysctl_table(rds_sysctl_reg_table);
+       unregister_net_sysctl_table(rds_sysctl_reg_table);
 }
 
 int rds_sysctl_init(void)
index 4e638f85118595d59c888917a9c3f072050c5780..23ab4dcd1d9f03942aa4d70bc7f6d9aa401f7707 100644 (file)
@@ -153,7 +153,7 @@ int rds_tcp_listen_init(void)
        sock->sk->sk_data_ready = rds_tcp_listen_data_ready;
        write_unlock_bh(&sock->sk->sk_callback_lock);
 
-       sin.sin_family = PF_INET,
+       sin.sin_family = PF_INET;
        sin.sin_addr.s_addr = (__force u32)htonl(INADDR_ANY);
        sin.sin_port = (__force u16)htons(RDS_TCP_PORT);
 
index bd2a5b90400cdf2688f249c1bcaaf382a4e918fd..14c98e48f261ee49656eabb198d62648890b63b3 100644 (file)
@@ -36,8 +36,6 @@ struct rfkill_gpio_data {
        struct gpio_desc        *shutdown_gpio;
 
        struct rfkill           *rfkill_dev;
-       char                    *reset_name;
-       char                    *shutdown_name;
        struct clk              *clk;
 
        bool                    clk_enabled;
@@ -47,17 +45,14 @@ static int rfkill_gpio_set_power(void *data, bool blocked)
 {
        struct rfkill_gpio_data *rfkill = data;
 
-       if (blocked) {
-               gpiod_set_value(rfkill->shutdown_gpio, 0);
-               gpiod_set_value(rfkill->reset_gpio, 0);
-               if (!IS_ERR(rfkill->clk) && rfkill->clk_enabled)
-                       clk_disable(rfkill->clk);
-       } else {
-               if (!IS_ERR(rfkill->clk) && !rfkill->clk_enabled)
-                       clk_enable(rfkill->clk);
-               gpiod_set_value(rfkill->reset_gpio, 1);
-               gpiod_set_value(rfkill->shutdown_gpio, 1);
-       }
+       if (!blocked && !IS_ERR(rfkill->clk) && !rfkill->clk_enabled)
+               clk_enable(rfkill->clk);
+
+       gpiod_set_value_cansleep(rfkill->shutdown_gpio, !blocked);
+       gpiod_set_value_cansleep(rfkill->reset_gpio, !blocked);
+
+       if (blocked && !IS_ERR(rfkill->clk) && rfkill->clk_enabled)
+               clk_disable(rfkill->clk);
 
        rfkill->clk_enabled = blocked;
 
@@ -87,10 +82,8 @@ static int rfkill_gpio_probe(struct platform_device *pdev)
 {
        struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data;
        struct rfkill_gpio_data *rfkill;
-       const char *clk_name = NULL;
        struct gpio_desc *gpio;
        int ret;
-       int len;
 
        rfkill = devm_kzalloc(&pdev->dev, sizeof(*rfkill), GFP_KERNEL);
        if (!rfkill)
@@ -101,28 +94,15 @@ static int rfkill_gpio_probe(struct platform_device *pdev)
                if (ret)
                        return ret;
        } else if (pdata) {
-               clk_name = pdata->power_clk_name;
                rfkill->name = pdata->name;
                rfkill->type = pdata->type;
        } else {
                return -ENODEV;
        }
 
-       len = strlen(rfkill->name);
-       rfkill->reset_name = devm_kzalloc(&pdev->dev, len + 7, GFP_KERNEL);
-       if (!rfkill->reset_name)
-               return -ENOMEM;
-
-       rfkill->shutdown_name = devm_kzalloc(&pdev->dev, len + 10, GFP_KERNEL);
-       if (!rfkill->shutdown_name)
-               return -ENOMEM;
+       rfkill->clk = devm_clk_get(&pdev->dev, NULL);
 
-       snprintf(rfkill->reset_name, len + 6 , "%s_reset", rfkill->name);
-       snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", rfkill->name);
-
-       rfkill->clk = devm_clk_get(&pdev->dev, clk_name);
-
-       gpio = devm_gpiod_get_index(&pdev->dev, rfkill->reset_name, 0);
+       gpio = devm_gpiod_get_index(&pdev->dev, "reset", 0);
        if (!IS_ERR(gpio)) {
                ret = gpiod_direction_output(gpio, 0);
                if (ret)
@@ -130,7 +110,7 @@ static int rfkill_gpio_probe(struct platform_device *pdev)
                rfkill->reset_gpio = gpio;
        }
 
-       gpio = devm_gpiod_get_index(&pdev->dev, rfkill->shutdown_name, 1);
+       gpio = devm_gpiod_get_index(&pdev->dev, "shutdown", 1);
        if (!IS_ERR(gpio)) {
                ret = gpiod_direction_output(gpio, 0);
                if (ret)
@@ -146,14 +126,6 @@ static int rfkill_gpio_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       if (pdata && pdata->gpio_runtime_setup) {
-               ret = pdata->gpio_runtime_setup(pdev);
-               if (ret) {
-                       dev_err(&pdev->dev, "can't set up gpio\n");
-                       return ret;
-               }
-       }
-
        rfkill->rfkill_dev = rfkill_alloc(rfkill->name, &pdev->dev,
                                          rfkill->type, &rfkill_gpio_ops,
                                          rfkill);
@@ -174,20 +146,23 @@ static int rfkill_gpio_probe(struct platform_device *pdev)
 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 && pdata->gpio_runtime_close)
-               pdata->gpio_runtime_close(pdev);
        rfkill_unregister(rfkill->rfkill_dev);
        rfkill_destroy(rfkill->rfkill_dev);
 
        return 0;
 }
 
+#ifdef CONFIG_ACPI
 static const struct acpi_device_id rfkill_acpi_match[] = {
+       { "BCM2E1A", RFKILL_TYPE_BLUETOOTH },
+       { "BCM2E39", RFKILL_TYPE_BLUETOOTH },
+       { "BCM2E3D", RFKILL_TYPE_BLUETOOTH },
        { "BCM4752", RFKILL_TYPE_GPS },
+       { "LNV4752", RFKILL_TYPE_GPS },
        { },
 };
+#endif
 
 static struct platform_driver rfkill_gpio_driver = {
        .probe = rfkill_gpio_probe,
index bdbdb1a7920af99c1829ae62716caddafb114fef..45527e6b52dbf396cbb7415bb0613152a8320096 100644 (file)
@@ -134,7 +134,8 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n)
        int err;
        int tp_created = 0;
 
-       if ((n->nlmsg_type != RTM_GETTFILTER) && !netlink_capable(skb, CAP_NET_ADMIN))
+       if ((n->nlmsg_type != RTM_GETTFILTER) &&
+           !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
                return -EPERM;
 
 replay:
@@ -317,7 +318,8 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n)
                }
        }
 
-       err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh);
+       err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh,
+                             n->nlmsg_flags & NLM_F_CREATE ? TCA_ACT_NOREPLACE : TCA_ACT_REPLACE);
        if (err == 0) {
                if (tp_created) {
                        spin_lock_bh(root_lock);
@@ -504,7 +506,7 @@ void tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts)
 EXPORT_SYMBOL(tcf_exts_destroy);
 
 int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
-                 struct nlattr *rate_tlv, struct tcf_exts *exts)
+                 struct nlattr *rate_tlv, struct tcf_exts *exts, bool ovr)
 {
 #ifdef CONFIG_NET_CLS_ACT
        {
@@ -513,7 +515,7 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
                INIT_LIST_HEAD(&exts->actions);
                if (exts->police && tb[exts->police]) {
                        act = tcf_action_init_1(net, tb[exts->police], rate_tlv,
-                                               "police", TCA_ACT_NOREPLACE,
+                                               "police", ovr,
                                                TCA_ACT_BIND);
                        if (IS_ERR(act))
                                return PTR_ERR(act);
@@ -523,7 +525,7 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
                } else if (exts->action && tb[exts->action]) {
                        int err;
                        err = tcf_action_init(net, tb[exts->action], rate_tlv,
-                                             NULL, TCA_ACT_NOREPLACE,
+                                             NULL, ovr,
                                              TCA_ACT_BIND, &exts->actions);
                        if (err)
                                return err;
@@ -543,14 +545,12 @@ void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst,
                     struct tcf_exts *src)
 {
 #ifdef CONFIG_NET_CLS_ACT
-       if (!list_empty(&src->actions)) {
-               LIST_HEAD(tmp);
-               tcf_tree_lock(tp);
-               list_splice_init(&dst->actions, &tmp);
-               list_splice(&src->actions, &dst->actions);
-               tcf_tree_unlock(tp);
-               tcf_action_destroy(&tmp, TCA_ACT_UNBIND);
-       }
+       LIST_HEAD(tmp);
+       tcf_tree_lock(tp);
+       list_splice_init(&dst->actions, &tmp);
+       list_splice(&src->actions, &dst->actions);
+       tcf_tree_unlock(tp);
+       tcf_action_destroy(&tmp, TCA_ACT_UNBIND);
 #endif
 }
 EXPORT_SYMBOL(tcf_exts_change);
index e98ca99c202bb5af6db77260f0996e6cacb098cf..0ae1813e3e90d55a1bf5993364502cebd58c1b22 100644 (file)
@@ -130,14 +130,14 @@ static const struct nla_policy basic_policy[TCA_BASIC_MAX + 1] = {
 static int basic_set_parms(struct net *net, struct tcf_proto *tp,
                           struct basic_filter *f, unsigned long base,
                           struct nlattr **tb,
-                          struct nlattr *est)
+                          struct nlattr *est, bool ovr)
 {
        int err;
        struct tcf_exts e;
        struct tcf_ematch_tree t;
 
        tcf_exts_init(&e, TCA_BASIC_ACT, TCA_BASIC_POLICE);
-       err = tcf_exts_validate(net, tp, tb, est, &e);
+       err = tcf_exts_validate(net, tp, tb, est, &e, ovr);
        if (err < 0)
                return err;
 
@@ -161,7 +161,7 @@ static int basic_set_parms(struct net *net, struct tcf_proto *tp,
 
 static int basic_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 nlattr **tca, unsigned long *arg, bool ovr)
 {
        int err;
        struct basic_head *head = tp->root;
@@ -179,7 +179,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
        if (f != NULL) {
                if (handle && f->handle != handle)
                        return -EINVAL;
-               return basic_set_parms(net, tp, f, base, tb, tca[TCA_RATE]);
+               return basic_set_parms(net, tp, f, base, tb, tca[TCA_RATE], ovr);
        }
 
        err = -ENOBUFS;
@@ -206,7 +206,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
                f->handle = head->hgenerator;
        }
 
-       err = basic_set_parms(net, tp, f, base, tb, tca[TCA_RATE]);
+       err = basic_set_parms(net, tp, f, base, tb, tca[TCA_RATE], ovr);
        if (err < 0)
                goto errout;
 
index 8e3cf49118e3a2297214de12313bb499cbfe9054..13f64df2c710663f2753df92632c5575bc2fe317 100644 (file)
@@ -156,11 +156,11 @@ 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 nlattr *est, bool ovr)
 {
        struct sock_filter *bpf_ops, *bpf_old;
        struct tcf_exts exts;
-       struct sock_fprog tmp;
+       struct sock_fprog_kern tmp;
        struct sk_filter *fp, *fp_old;
        u16 bpf_size, bpf_len;
        u32 classid;
@@ -170,7 +170,7 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp,
                return -EINVAL;
 
        tcf_exts_init(&exts, TCA_BPF_ACT, TCA_BPF_POLICE);
-       ret = tcf_exts_validate(net, tp, tb, est, &exts);
+       ret = tcf_exts_validate(net, tp, tb, est, &exts, ovr);
        if (ret < 0)
                return ret;
 
@@ -191,7 +191,7 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp,
        memcpy(bpf_ops, nla_data(tb[TCA_BPF_OPS]), bpf_size);
 
        tmp.len = bpf_len;
-       tmp.filter = (struct sock_filter __user *) bpf_ops;
+       tmp.filter = bpf_ops;
 
        ret = sk_unattached_filter_create(&fp, &tmp);
        if (ret)
@@ -242,7 +242,7 @@ static u32 cls_bpf_grab_new_handle(struct tcf_proto *tp,
 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)
+                         unsigned long *arg, bool ovr)
 {
        struct cls_bpf_head *head = tp->root;
        struct cls_bpf_prog *prog = (struct cls_bpf_prog *) *arg;
@@ -260,7 +260,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
                if (handle && prog->handle != handle)
                        return -EINVAL;
                return cls_bpf_modify_existing(net, tp, prog, base, tb,
-                                              tca[TCA_RATE]);
+                                              tca[TCA_RATE], ovr);
        }
 
        prog = kzalloc(sizeof(*prog), GFP_KERNEL);
@@ -277,7 +277,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
                goto errout;
        }
 
-       ret = cls_bpf_modify_existing(net, tp, prog, base, tb, tca[TCA_RATE]);
+       ret = cls_bpf_modify_existing(net, tp, prog, base, tb, tca[TCA_RATE], ovr);
        if (ret < 0)
                goto errout;
 
index 8e2158ab551c0c9d7258ffae3e474788bfc64565..cacf01bd04f0a96660050c0e71da0840c8af0f95 100644 (file)
@@ -83,7 +83,7 @@ static const struct nla_policy cgroup_policy[TCA_CGROUP_MAX + 1] = {
 static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb,
                             struct tcf_proto *tp, unsigned long base,
                             u32 handle, struct nlattr **tca,
-                            unsigned long *arg)
+                            unsigned long *arg, bool ovr)
 {
        struct nlattr *tb[TCA_CGROUP_MAX + 1];
        struct cls_cgroup_head *head = tp->root;
@@ -119,7 +119,7 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb,
                return err;
 
        tcf_exts_init(&e, TCA_CGROUP_ACT, TCA_CGROUP_POLICE);
-       err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e);
+       err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
        if (err < 0)
                return err;
 
index 257029c5433298f62101533ada3a81c246eb0d7a..35be16f7c192dc8d5d2ebdd38c2b27a144c41076 100644 (file)
@@ -349,7 +349,7 @@ static const struct nla_policy flow_policy[TCA_FLOW_MAX + 1] = {
 static int flow_change(struct net *net, struct sk_buff *in_skb,
                       struct tcf_proto *tp, unsigned long base,
                       u32 handle, struct nlattr **tca,
-                      unsigned long *arg)
+                      unsigned long *arg, bool ovr)
 {
        struct flow_head *head = tp->root;
        struct flow_filter *f;
@@ -393,7 +393,7 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,
        }
 
        tcf_exts_init(&e, TCA_FLOW_ACT, TCA_FLOW_POLICE);
-       err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e);
+       err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
        if (err < 0)
                return err;
 
index 63a3ce75c02ee959fe67d6b203e01420d86b03ad..861b03ccfed0a55007ceb001a297b05906b36ed3 100644 (file)
@@ -169,7 +169,7 @@ static const struct nla_policy fw_policy[TCA_FW_MAX + 1] = {
 
 static int
 fw_change_attrs(struct net *net, struct tcf_proto *tp, struct fw_filter *f,
-       struct nlattr **tb, struct nlattr **tca, unsigned long base)
+       struct nlattr **tb, struct nlattr **tca, unsigned long base, bool ovr)
 {
        struct fw_head *head = tp->root;
        struct tcf_exts e;
@@ -177,7 +177,7 @@ fw_change_attrs(struct net *net, struct tcf_proto *tp, struct fw_filter *f,
        int err;
 
        tcf_exts_init(&e, TCA_FW_ACT, TCA_FW_POLICE);
-       err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e);
+       err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
        if (err < 0)
                return err;
 
@@ -218,7 +218,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb,
                     struct tcf_proto *tp, unsigned long base,
                     u32 handle,
                     struct nlattr **tca,
-                    unsigned long *arg)
+                    unsigned long *arg, bool ovr)
 {
        struct fw_head *head = tp->root;
        struct fw_filter *f = (struct fw_filter *) *arg;
@@ -236,7 +236,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb,
        if (f != NULL) {
                if (f->id != handle && handle)
                        return -EINVAL;
-               return fw_change_attrs(net, tp, f, tb, tca, base);
+               return fw_change_attrs(net, tp, f, tb, tca, base, ovr);
        }
 
        if (!handle)
@@ -264,7 +264,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb,
        tcf_exts_init(&f->exts, TCA_FW_ACT, TCA_FW_POLICE);
        f->id = handle;
 
-       err = fw_change_attrs(net, tp, f, tb, tca, base);
+       err = fw_change_attrs(net, tp, f, tb, tca, base, ovr);
        if (err < 0)
                goto errout;
 
index 1ad3068f2ce16e2c6ba15985c40cf899a7030ba7..dd9fc2523c76a2b0b9fe4c5225328cc29c1649f6 100644 (file)
@@ -333,7 +333,8 @@ static const struct nla_policy route4_policy[TCA_ROUTE4_MAX + 1] = {
 static int route4_set_parms(struct net *net, struct tcf_proto *tp,
                            unsigned long base, struct route4_filter *f,
                            u32 handle, struct route4_head *head,
-                           struct nlattr **tb, struct nlattr *est, int new)
+                           struct nlattr **tb, struct nlattr *est, int new,
+                           bool ovr)
 {
        int err;
        u32 id = 0, to = 0, nhandle = 0x8000;
@@ -343,7 +344,7 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp,
        struct tcf_exts e;
 
        tcf_exts_init(&e, TCA_ROUTE4_ACT, TCA_ROUTE4_POLICE);
-       err = tcf_exts_validate(net, tp, tb, est, &e);
+       err = tcf_exts_validate(net, tp, tb, est, &e, ovr);
        if (err < 0)
                return err;
 
@@ -428,7 +429,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,
                       struct tcf_proto *tp, unsigned long base,
                       u32 handle,
                       struct nlattr **tca,
-                      unsigned long *arg)
+                      unsigned long *arg, bool ovr)
 {
        struct route4_head *head = tp->root;
        struct route4_filter *f, *f1, **fp;
@@ -455,7 +456,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,
                        old_handle = f->handle;
 
                err = route4_set_parms(net, tp, base, f, handle, head, tb,
-                       tca[TCA_RATE], 0);
+                       tca[TCA_RATE], 0, ovr);
                if (err < 0)
                        return err;
 
@@ -479,7 +480,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,
 
        tcf_exts_init(&f->exts, TCA_ROUTE4_ACT, TCA_ROUTE4_POLICE);
        err = route4_set_parms(net, tp, base, f, handle, head, tb,
-               tca[TCA_RATE], 1);
+               tca[TCA_RATE], 1, ovr);
        if (err < 0)
                goto errout;
 
index 19f8e5dfa8bdaebcd9ab903050047e7e49690530..1020e233a5d6c74092fb153133b1bfed7f4177a9 100644 (file)
@@ -415,7 +415,7 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb,
                       struct tcf_proto *tp, unsigned long base,
                       u32 handle,
                       struct nlattr **tca,
-                      unsigned long *arg)
+                      unsigned long *arg, bool ovr)
 {
        struct rsvp_head *data = tp->root;
        struct rsvp_filter *f, **fp;
@@ -436,7 +436,7 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb,
                return err;
 
        tcf_exts_init(&e, TCA_RSVP_ACT, TCA_RSVP_POLICE);
-       err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e);
+       err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
        if (err < 0)
                return err;
 
index f435a88d899afff7c132085ccece5427461e5490..c721cd4a469fe39f42186d82ec7ab04f17bcba26 100644 (file)
@@ -198,7 +198,7 @@ static int
 tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
                  u32 handle, struct tcindex_data *p,
                  struct tcindex_filter_result *r, struct nlattr **tb,
-                struct nlattr *est)
+                 struct nlattr *est, bool ovr)
 {
        int err, balloc = 0;
        struct tcindex_filter_result new_filter_result, *old_r = r;
@@ -208,7 +208,7 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
        struct tcf_exts e;
 
        tcf_exts_init(&e, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE);
-       err = tcf_exts_validate(net, tp, tb, est, &e);
+       err = tcf_exts_validate(net, tp, tb, est, &e, ovr);
        if (err < 0)
                return err;
 
@@ -341,7 +341,7 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
 static int
 tcindex_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 nlattr **tca, unsigned long *arg, bool ovr)
 {
        struct nlattr *opt = tca[TCA_OPTIONS];
        struct nlattr *tb[TCA_TCINDEX_MAX + 1];
@@ -361,7 +361,7 @@ tcindex_change(struct net *net, struct sk_buff *in_skb,
                return err;
 
        return tcindex_set_parms(net, tp, base, handle, p, r, tb,
-                                tca[TCA_RATE]);
+                                tca[TCA_RATE], ovr);
 }
 
 
index 84c28daff8484f643e5bed4176f6b225eec34e66..c39b583ace3229d4bae6a7b3774593e5eebd7141 100644 (file)
@@ -486,13 +486,13 @@ static const struct nla_policy u32_policy[TCA_U32_MAX + 1] = {
 static int u32_set_parms(struct net *net, struct tcf_proto *tp,
                         unsigned long base, struct tc_u_hnode *ht,
                         struct tc_u_knode *n, struct nlattr **tb,
-                        struct nlattr *est)
+                        struct nlattr *est, bool ovr)
 {
        int err;
        struct tcf_exts e;
 
        tcf_exts_init(&e, TCA_U32_ACT, TCA_U32_POLICE);
-       err = tcf_exts_validate(net, tp, tb, est, &e);
+       err = tcf_exts_validate(net, tp, tb, est, &e, ovr);
        if (err < 0)
                return err;
 
@@ -545,7 +545,7 @@ static int u32_set_parms(struct net *net, struct tcf_proto *tp,
 static int u32_change(struct net *net, struct sk_buff *in_skb,
                      struct tcf_proto *tp, unsigned long base, u32 handle,
                      struct nlattr **tca,
-                     unsigned long *arg)
+                     unsigned long *arg, bool ovr)
 {
        struct tc_u_common *tp_c = tp->data;
        struct tc_u_hnode *ht;
@@ -569,7 +569,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
                        return -EINVAL;
 
                return u32_set_parms(net, tp, base, n->ht_up, n, tb,
-                                    tca[TCA_RATE]);
+                                    tca[TCA_RATE], ovr);
        }
 
        if (tb[TCA_U32_DIVISOR]) {
@@ -656,7 +656,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
        }
 #endif
 
-       err = u32_set_parms(net, tp, base, ht, n, tb, tca[TCA_RATE]);
+       err = u32_set_parms(net, tp, base, ht, n, tb, tca[TCA_RATE], ovr);
        if (err == 0) {
                struct tc_u_knode **ins;
                for (ins = &ht->ht[TC_U32_HASH(handle)]; *ins; ins = &(*ins)->next)
index 400769014bbde8d4e9032600524e97f549dcff31..58bed7599db7d9f5538f01ff5057e7aa79d0a915 100644 (file)
@@ -563,7 +563,7 @@ void __qdisc_calculate_pkt_len(struct sk_buff *skb, const struct qdisc_size_tabl
 }
 EXPORT_SYMBOL(__qdisc_calculate_pkt_len);
 
-void qdisc_warn_nonwc(char *txt, struct Qdisc *qdisc)
+void qdisc_warn_nonwc(const char *txt, struct Qdisc *qdisc)
 {
        if (!(qdisc->flags & TCQ_F_WARN_NONWC)) {
                pr_warn("%s: %s qdisc %X: is non-work-conserving?\n",
@@ -1084,7 +1084,8 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n)
        struct Qdisc *p = NULL;
        int err;
 
-       if ((n->nlmsg_type != RTM_GETQDISC) && !netlink_capable(skb, CAP_NET_ADMIN))
+       if ((n->nlmsg_type != RTM_GETQDISC) &&
+           !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
                return -EPERM;
 
        err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL);
@@ -1151,7 +1152,7 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n)
        struct Qdisc *q, *p;
        int err;
 
-       if (!netlink_capable(skb, CAP_NET_ADMIN))
+       if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
                return -EPERM;
 
 replay:
@@ -1490,7 +1491,8 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n)
        u32 qid;
        int err;
 
-       if ((n->nlmsg_type != RTM_GETTCLASS) && !netlink_capable(skb, CAP_NET_ADMIN))
+       if ((n->nlmsg_type != RTM_GETTCLASS) &&
+           !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
                return -EPERM;
 
        err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL);
index 2aee02802c2760fafabb47d6bf4229d08d8ef907..ed30e436128bff7c556e0c334e4cb5ae8d088e5e 100644 (file)
@@ -391,12 +391,7 @@ static const struct nla_policy choke_policy[TCA_CHOKE_MAX + 1] = {
 
 static void choke_free(void *addr)
 {
-       if (addr) {
-               if (is_vmalloc_addr(addr))
-                       vfree(addr);
-               else
-                       kfree(addr);
-       }
+       kvfree(addr);
 }
 
 static int choke_change(struct Qdisc *sch, struct nlattr *opt)
index 8302717ea3034247bc27597e067ac8771dc973b5..7bbbfe1121920a126537d43a0443ab4b98e340fa 100644 (file)
@@ -391,8 +391,10 @@ static struct sk_buff *drr_dequeue(struct Qdisc *sch)
        while (1) {
                cl = list_first_entry(&q->active, struct drr_class, alist);
                skb = cl->qdisc->ops->peek(cl->qdisc);
-               if (skb == NULL)
+               if (skb == NULL) {
+                       qdisc_warn_nonwc(__func__, cl->qdisc);
                        goto out;
+               }
 
                len = qdisc_pkt_len(skb);
                if (len <= cl->deficit) {
index 23c682b42f99ecb86ca8a103005a52d692a21d38..ba32c2b005d0821788f656e8ae05d4d47a862351 100644 (file)
@@ -591,10 +591,7 @@ static void *fq_alloc_node(size_t sz, int node)
 
 static void fq_free(void *addr)
 {
-       if (addr && is_vmalloc_addr(addr))
-               vfree(addr);
-       else
-               kfree(addr);
+       kvfree(addr);
 }
 
 static int fq_resize(struct Qdisc *sch, u32 log)
index 0bf432c782c1f17cdb8bbb1b8ece16848e63c27e..063b726bf1f8636e3529a2e61d1b43fced918d9c 100644 (file)
@@ -365,12 +365,7 @@ static void *fq_codel_zalloc(size_t sz)
 
 static void fq_codel_free(void *addr)
 {
-       if (addr) {
-               if (is_vmalloc_addr(addr))
-                       vfree(addr);
-               else
-                       kfree(addr);
-       }
+       kvfree(addr);
 }
 
 static void fq_codel_destroy(struct Qdisc *sch)
index 6e957c3b9854acc025e43d9028b94f0af7f4d6c1..d85b6812a7d4c6bd0794decd872406e6b810e779 100644 (file)
@@ -414,7 +414,7 @@ static int hhf_enqueue(struct sk_buff *skb, struct Qdisc *sch)
                }
                bucket->deficit = weight * q->quantum;
        }
-       if (++sch->q.qlen < sch->limit)
+       if (++sch->q.qlen <= sch->limit)
                return NET_XMIT_SUCCESS;
 
        q->drop_overlimit++;
@@ -494,12 +494,7 @@ static void *hhf_zalloc(size_t sz)
 
 static void hhf_free(void *addr)
 {
-       if (addr) {
-               if (is_vmalloc_addr(addr))
-                       vfree(addr);
-               else
-                       kfree(addr);
-       }
+       kvfree(addr);
 }
 
 static void hhf_destroy(struct Qdisc *sch)
index f1669a00f5710c297310f4a1da4a1b4e72cd3cab..111d70fddaea97242ac4a85115e969efcd11dfcd 100644 (file)
@@ -648,12 +648,7 @@ static void netem_reset(struct Qdisc *sch)
 
 static void dist_free(struct disttable *d)
 {
-       if (d) {
-               if (is_vmalloc_addr(d))
-                       vfree(d);
-               else
-                       kfree(d);
-       }
+       kvfree(d);
 }
 
 /*
index 87317ff0b4ec4699dae7898405666d2bde6c9897..1af2f73906d07aa5bf6f428ffe37b81523178ee4 100644 (file)
@@ -716,12 +716,7 @@ static void *sfq_alloc(size_t sz)
 
 static void sfq_free(void *addr)
 {
-       if (addr) {
-               if (is_vmalloc_addr(addr))
-                       vfree(addr);
-               else
-                       kfree(addr);
-       }
+       kvfree(addr);
 }
 
 static void sfq_destroy(struct Qdisc *sch)
index 39579c3e0d14c12f165e420be064d7fbf248c0ad..9de23a222d3f7b9f33e650483d749d3341c51dc0 100644 (file)
@@ -55,6 +55,7 @@
 #include <net/sctp/sm.h>
 
 /* Forward declarations for internal functions. */
+static void sctp_select_active_and_retran_path(struct sctp_association *asoc);
 static void sctp_assoc_bh_rcv(struct work_struct *work);
 static void sctp_assoc_free_asconf_acks(struct sctp_association *asoc);
 static void sctp_assoc_free_asconf_queue(struct sctp_association *asoc);
@@ -330,7 +331,7 @@ void sctp_association_free(struct sctp_association *asoc)
        /* Only real associations count against the endpoint, so
         * don't bother for if this is a temporary association.
         */
-       if (!asoc->temp) {
+       if (!list_empty(&asoc->asocs)) {
                list_del(&asoc->asocs);
 
                /* Decrement the backlog value for a TCP-style listening
@@ -774,9 +775,6 @@ void sctp_assoc_control_transport(struct sctp_association *asoc,
                                  sctp_transport_cmd_t command,
                                  sctp_sn_error_t error)
 {
-       struct sctp_transport *t = NULL;
-       struct sctp_transport *first;
-       struct sctp_transport *second;
        struct sctp_ulpevent *event;
        struct sockaddr_storage addr;
        int spc_state = 0;
@@ -829,13 +827,14 @@ void sctp_assoc_control_transport(struct sctp_association *asoc,
                return;
        }
 
-       /* Generate and send a SCTP_PEER_ADDR_CHANGE notification to the
-        * user.
+       /* Generate and send a SCTP_PEER_ADDR_CHANGE notification
+        * to the user.
         */
        if (ulp_notify) {
                memset(&addr, 0, sizeof(struct sockaddr_storage));
                memcpy(&addr, &transport->ipaddr,
                       transport->af_specific->sockaddr_len);
+
                event = sctp_ulpevent_make_peer_addr_change(asoc, &addr,
                                        0, spc_state, error, GFP_ATOMIC);
                if (event)
@@ -843,60 +842,7 @@ void sctp_assoc_control_transport(struct sctp_association *asoc,
        }
 
        /* Select new active and retran paths. */
-
-       /* Look for the two most recently used active transports.
-        *
-        * This code produces the wrong ordering whenever jiffies
-        * rolls over, but we still get usable transports, so we don't
-        * worry about it.
-        */
-       first = NULL; second = NULL;
-
-       list_for_each_entry(t, &asoc->peer.transport_addr_list,
-                       transports) {
-
-               if ((t->state == SCTP_INACTIVE) ||
-                   (t->state == SCTP_UNCONFIRMED) ||
-                   (t->state == SCTP_PF))
-                       continue;
-               if (!first || t->last_time_heard > first->last_time_heard) {
-                       second = first;
-                       first = t;
-               } else if (!second ||
-                          t->last_time_heard > second->last_time_heard)
-                       second = t;
-       }
-
-       /* RFC 2960 6.4 Multi-Homed SCTP Endpoints
-        *
-        * By default, an endpoint should always transmit to the
-        * primary path, unless the SCTP user explicitly specifies the
-        * destination transport address (and possibly source
-        * transport address) to use.
-        *
-        * [If the primary is active but not most recent, bump the most
-        * recently used transport.]
-        */
-       if (((asoc->peer.primary_path->state == SCTP_ACTIVE) ||
-            (asoc->peer.primary_path->state == SCTP_UNKNOWN)) &&
-           first != asoc->peer.primary_path) {
-               second = first;
-               first = asoc->peer.primary_path;
-       }
-
-       if (!second)
-               second = first;
-       /* If we failed to find a usable transport, just camp on the
-        * primary, even if it is inactive.
-        */
-       if (!first) {
-               first = asoc->peer.primary_path;
-               second = asoc->peer.primary_path;
-       }
-
-       /* Set the active and retran transports.  */
-       asoc->peer.active_path = first;
-       asoc->peer.retran_path = second;
+       sctp_select_active_and_retran_path(asoc);
 }
 
 /* Hold a reference to an association. */
@@ -1090,7 +1036,7 @@ static void sctp_assoc_bh_rcv(struct work_struct *work)
                }
 
                if (chunk->transport)
-                       chunk->transport->last_time_heard = jiffies;
+                       chunk->transport->last_time_heard = ktime_get();
 
                /* Run through the state machine. */
                error = sctp_do_sm(net, SCTP_EVENT_T_CHUNK, subtype,
@@ -1278,13 +1224,41 @@ static u8 sctp_trans_score(const struct sctp_transport *trans)
        return sctp_trans_state_to_prio_map[trans->state];
 }
 
+static struct sctp_transport *sctp_trans_elect_tie(struct sctp_transport *trans1,
+                                                  struct sctp_transport *trans2)
+{
+       if (trans1->error_count > trans2->error_count) {
+               return trans2;
+       } else if (trans1->error_count == trans2->error_count &&
+                  ktime_after(trans2->last_time_heard,
+                              trans1->last_time_heard)) {
+               return trans2;
+       } else {
+               return trans1;
+       }
+}
+
 static struct sctp_transport *sctp_trans_elect_best(struct sctp_transport *curr,
                                                    struct sctp_transport *best)
 {
+       u8 score_curr, score_best;
+
        if (best == NULL)
                return curr;
 
-       return sctp_trans_score(curr) > sctp_trans_score(best) ? curr : best;
+       score_curr = sctp_trans_score(curr);
+       score_best = sctp_trans_score(best);
+
+       /* First, try a score-based selection if both transport states
+        * differ. If we're in a tie, lets try to make a more clever
+        * decision here based on error counts and last time heard.
+        */
+       if (score_curr > score_best)
+               return curr;
+       else if (score_curr == score_best)
+               return sctp_trans_elect_tie(curr, best);
+       else
+               return best;
 }
 
 void sctp_assoc_update_retran_path(struct sctp_association *asoc)
@@ -1325,6 +1299,76 @@ void sctp_assoc_update_retran_path(struct sctp_association *asoc)
                 __func__, asoc, &asoc->peer.retran_path->ipaddr.sa);
 }
 
+static void sctp_select_active_and_retran_path(struct sctp_association *asoc)
+{
+       struct sctp_transport *trans, *trans_pri = NULL, *trans_sec = NULL;
+       struct sctp_transport *trans_pf = NULL;
+
+       /* Look for the two most recently used active transports. */
+       list_for_each_entry(trans, &asoc->peer.transport_addr_list,
+                           transports) {
+               /* Skip uninteresting transports. */
+               if (trans->state == SCTP_INACTIVE ||
+                   trans->state == SCTP_UNCONFIRMED)
+                       continue;
+               /* Keep track of the best PF transport from our
+                * list in case we don't find an active one.
+                */
+               if (trans->state == SCTP_PF) {
+                       trans_pf = sctp_trans_elect_best(trans, trans_pf);
+                       continue;
+               }
+               /* For active transports, pick the most recent ones. */
+               if (trans_pri == NULL ||
+                   ktime_after(trans->last_time_heard,
+                               trans_pri->last_time_heard)) {
+                       trans_sec = trans_pri;
+                       trans_pri = trans;
+               } else if (trans_sec == NULL ||
+                          ktime_after(trans->last_time_heard,
+                                      trans_sec->last_time_heard)) {
+                       trans_sec = trans;
+               }
+       }
+
+       /* RFC 2960 6.4 Multi-Homed SCTP Endpoints
+        *
+        * By default, an endpoint should always transmit to the primary
+        * path, unless the SCTP user explicitly specifies the
+        * destination transport address (and possibly source transport
+        * address) to use. [If the primary is active but not most recent,
+        * bump the most recently used transport.]
+        */
+       if ((asoc->peer.primary_path->state == SCTP_ACTIVE ||
+            asoc->peer.primary_path->state == SCTP_UNKNOWN) &&
+            asoc->peer.primary_path != trans_pri) {
+               trans_sec = trans_pri;
+               trans_pri = asoc->peer.primary_path;
+       }
+
+       /* We did not find anything useful for a possible retransmission
+        * path; either primary path that we found is the the same as
+        * the current one, or we didn't generally find an active one.
+        */
+       if (trans_sec == NULL)
+               trans_sec = trans_pri;
+
+       /* If we failed to find a usable transport, just camp on the
+        * primary or retran, even if they are inactive, if possible
+        * pick a PF iff it's the better choice.
+        */
+       if (trans_pri == NULL) {
+               trans_pri = sctp_trans_elect_best(asoc->peer.primary_path,
+                                                 asoc->peer.retran_path);
+               trans_pri = sctp_trans_elect_best(trans_pri, trans_pf);
+               trans_sec = asoc->peer.primary_path;
+       }
+
+       /* Set the active and retran transports. */
+       asoc->peer.active_path = trans_pri;
+       asoc->peer.retran_path = trans_sec;
+}
+
 struct sctp_transport *
 sctp_assoc_choose_alter_transport(struct sctp_association *asoc,
                                  struct sctp_transport *last_sent_to)
@@ -1547,7 +1591,7 @@ int sctp_assoc_lookup_laddr(struct sctp_association *asoc,
 /* Set an association id for a given association */
 int sctp_assoc_set_id(struct sctp_association *asoc, gfp_t gfp)
 {
-       bool preload = gfp & __GFP_WAIT;
+       bool preload = !!(gfp & __GFP_WAIT);
        int ret;
 
        /* If the id is already assigned, keep it. */
index 3d9f429858dca24f6262996bbec03ca59491b0c0..9da76ba4d10fe316884973448c19e5b9b8310a79 100644 (file)
@@ -481,7 +481,7 @@ static void sctp_endpoint_bh_rcv(struct work_struct *work)
                }
 
                if (chunk->transport)
-                       chunk->transport->last_time_heard = jiffies;
+                       chunk->transport->last_time_heard = ktime_get();
 
                error = sctp_do_sm(net, SCTP_EVENT_T_CHUNK, subtype, state,
                                   ep, asoc, chunk, GFP_ATOMIC);
index 2b1738ef9394537589b403f7d299181e18fb2315..1999592ba88c9f3d574428e1ce076c10f724c13a 100644 (file)
@@ -216,7 +216,7 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport)
        IP6_ECN_flow_xmit(sk, fl6->flowlabel);
 
        if (!(transport->param_flags & SPP_PMTUD_ENABLE))
-               skb->local_df = 1;
+               skb->ignore_df = 1;
 
        SCTP_INC_STATS(sock_net(sk), SCTP_MIB_OUTSCTPPACKS);
 
@@ -943,7 +943,6 @@ static struct inet_protosw sctpv6_seqpacket_protosw = {
        .protocol      = IPPROTO_SCTP,
        .prot          = &sctpv6_prot,
        .ops           = &inet6_seqpacket_ops,
-       .no_check      = 0,
        .flags         = SCTP_PROTOSW_FLAG
 };
 static struct inet_protosw sctpv6_stream_protosw = {
@@ -951,7 +950,6 @@ static struct inet_protosw sctpv6_stream_protosw = {
        .protocol      = IPPROTO_SCTP,
        .prot          = &sctpv6_prot,
        .ops           = &inet6_seqpacket_ops,
-       .no_check      = 0,
        .flags         = SCTP_PROTOSW_FLAG,
 };
 
index 0f4d15fc2627bcccb546ee2da883f812daa4e4a1..01ab8e0723f04ea845ba81045228786ef07c97ad 100644 (file)
@@ -591,7 +591,7 @@ int sctp_packet_transmit(struct sctp_packet *packet)
 
        pr_debug("***sctp_transmit_packet*** skb->len:%d\n", nskb->len);
 
-       nskb->local_df = packet->ipfragok;
+       nskb->ignore_df = packet->ipfragok;
        tp->af_specific->sctp_xmit(nskb, tp);
 
 out:
index 0947f1e15eb88a0381434a05da082bf3d6fd9797..34229ee7f379902b16a7659f82545f10e5908b17 100644 (file)
@@ -78,7 +78,7 @@ static int sctp_snmp_seq_show(struct seq_file *seq, void *v)
 
        for (i = 0; sctp_snmp_list[i].name != NULL; i++)
                seq_printf(seq, "%-32s\t%ld\n", sctp_snmp_list[i].name,
-                          snmp_fold_field((void __percpu **)net->sctp.sctp_statistics,
+                          snmp_fold_field(net->sctp.sctp_statistics,
                                      sctp_snmp_list[i].entry));
 
        return 0;
index 44cbb54c85746a586407015c952e7f8d60ab4d91..6789d785e698325afc7c8cfd65d1b69c2e50047a 100644 (file)
@@ -1017,7 +1017,6 @@ static struct inet_protosw sctp_seqpacket_protosw = {
        .protocol   = IPPROTO_SCTP,
        .prot       = &sctp_prot,
        .ops        = &inet_seqpacket_ops,
-       .no_check   = 0,
        .flags      = SCTP_PROTOSW_FLAG
 };
 static struct inet_protosw sctp_stream_protosw = {
@@ -1025,7 +1024,6 @@ static struct inet_protosw sctp_stream_protosw = {
        .protocol   = IPPROTO_SCTP,
        .prot       = &sctp_prot,
        .ops        = &inet_seqpacket_ops,
-       .no_check   = 0,
        .flags      = SCTP_PROTOSW_FLAG
 };
 
@@ -1105,14 +1103,15 @@ int sctp_register_pf(struct sctp_pf *pf, sa_family_t family)
 
 static inline int init_sctp_mibs(struct net *net)
 {
-       return snmp_mib_init((void __percpu **)net->sctp.sctp_statistics,
-                            sizeof(struct sctp_mib),
-                            __alignof__(struct sctp_mib));
+       net->sctp.sctp_statistics = alloc_percpu(struct sctp_mib);
+       if (!net->sctp.sctp_statistics)
+               return -ENOMEM;
+       return 0;
 }
 
 static inline void cleanup_sctp_mibs(struct net *net)
 {
-       snmp_mib_free((void __percpu **)net->sctp.sctp_statistics);
+       free_percpu(net->sctp.sctp_statistics);
 }
 
 static void sctp_v4_pf_init(void)
index fee5552ddf929e40f702e1d0b86ead2cb0630f97..ae0e616a7ca5ed64ee9e7e09cab08a13caf7bff7 100644 (file)
@@ -1782,7 +1782,7 @@ struct sctp_association *sctp_unpack_cookie(
        else
                kt = ktime_get();
 
-       if (!asoc && ktime_compare(bear_cookie->expiration, kt) < 0) {
+       if (!asoc && ktime_before(bear_cookie->expiration, kt)) {
                /*
                 * Section 3.3.10.3 Stale Cookie Error (3)
                 *
index fee06b99a4da8dab4d2b094196af08e23d1f22d4..429899689408caec64cf5c36b4eb0b00369f8e48 100644 (file)
@@ -71,6 +71,7 @@
 #include <net/route.h>
 #include <net/ipv6.h>
 #include <net/inet_common.h>
+#include <net/busy_poll.h>
 
 #include <linux/socket.h> /* for sa_family_t */
 #include <linux/export.h>
@@ -5945,8 +5946,9 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
                /* Search for an available port. */
                int low, high, remaining, index;
                unsigned int rover;
+               struct net *net = sock_net(sk);
 
-               inet_get_local_port_range(sock_net(sk), &low, &high);
+               inet_get_local_port_range(net, &low, &high);
                remaining = (high - low) + 1;
                rover = prandom_u32() % remaining + low;
 
@@ -5954,7 +5956,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
                        rover++;
                        if ((rover < low) || (rover > high))
                                rover = low;
-                       if (inet_is_reserved_local_port(rover))
+                       if (inet_is_local_reserved_port(net, rover))
                                continue;
                        index = sctp_phashfn(sock_net(sk), rover);
                        head = &sctp_port_hashtable[index];
@@ -6557,6 +6559,10 @@ static struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags,
                if (sk->sk_shutdown & RCV_SHUTDOWN)
                        break;
 
+               if (sk_can_busy_loop(sk) &&
+                   sk_busy_loop(sk, noblock))
+                       continue;
+
                /* User doesn't want to wait.  */
                error = -EAGAIN;
                if (!timeo)
@@ -6940,7 +6946,8 @@ void sctp_copy_sock(struct sock *newsk, struct sock *sk,
        newsk->sk_type = sk->sk_type;
        newsk->sk_bound_dev_if = sk->sk_bound_dev_if;
        newsk->sk_flags = sk->sk_flags;
-       newsk->sk_no_check = sk->sk_no_check;
+       newsk->sk_no_check_tx = sk->sk_no_check_tx;
+       newsk->sk_no_check_rx = sk->sk_no_check_rx;
        newsk->sk_reuse = sk->sk_reuse;
 
        newsk->sk_shutdown = sk->sk_shutdown;
index c82fdc1eab7c359dbee3812db3f3707fa5c65560..7e5eb75549902eeeb2c0c0889daca8ee53317ccf 100644 (file)
@@ -436,20 +436,21 @@ static int proc_sctp_do_auth(struct ctl_table *ctl, int write,
 
 int sctp_sysctl_net_register(struct net *net)
 {
-       struct ctl_table *table = sctp_net_table;
-
-       if (!net_eq(net, &init_net)) {
-               int i;
+       struct ctl_table *table;
+       int i;
 
-               table = kmemdup(sctp_net_table, sizeof(sctp_net_table), GFP_KERNEL);
-               if (!table)
-                       return -ENOMEM;
+       table = kmemdup(sctp_net_table, sizeof(sctp_net_table), GFP_KERNEL);
+       if (!table)
+               return -ENOMEM;
 
-               for (i = 0; table[i].data; i++)
-                       table[i].data += (char *)(&net->sctp) - (char *)&init_net.sctp;
-       }
+       for (i = 0; table[i].data; i++)
+               table[i].data += (char *)(&net->sctp) - (char *)&init_net.sctp;
 
        net->sctp.sysctl_header = register_net_sysctl(net, "net/sctp", table);
+       if (net->sctp.sysctl_header == NULL) {
+               kfree(table);
+               return -ENOMEM;
+       }
        return 0;
 }
 
index 1d348d15b33de4c3b20f7f071442c05058e7dcc3..7dd672fa651f979ae6b93578fc678254a7317b6b 100644 (file)
@@ -72,7 +72,7 @@ static struct sctp_transport *sctp_transport_init(struct net *net,
         */
        peer->rto = msecs_to_jiffies(net->sctp.rto_initial);
 
-       peer->last_time_heard = jiffies;
+       peer->last_time_heard = ktime_get();
        peer->last_time_ecne_reduced = jiffies;
 
        peer->param_flags = SPP_HB_DISABLE |
index 7144eb6a1b95ccad39f9376f723756e0b2c75a2c..d49dc2ed30adb97a809eb37902b9956c366a2862 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/types.h>
 #include <linux/skbuff.h>
 #include <net/sock.h>
+#include <net/busy_poll.h>
 #include <net/sctp/structs.h>
 #include <net/sctp/sctp.h>
 #include <net/sctp/sm.h>
@@ -204,6 +205,9 @@ int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event)
        if (sock_flag(sk, SOCK_DEAD) || (sk->sk_shutdown & RCV_SHUTDOWN))
                goto out_free;
 
+       if (!sctp_ulpevent_is_notification(event))
+               sk_mark_napi_id(sk, skb);
+
        /* Check if the user wishes to receive this event.  */
        if (!sctp_ulpevent_is_enabled(event, &sctp_sk(sk)->subscribe))
                goto out_free;
index 0a648c502fc35e1f905fdd81667b4553d255932d..2df87f78e518eab4f4fedd37eb38de1ae6c8aaea 100644 (file)
@@ -173,7 +173,8 @@ int csum_partial_copy_to_xdr(struct xdr_buf *xdr, struct sk_buff *skb)
                return -1;
        if (csum_fold(desc.csum))
                return -1;
-       if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE))
+       if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
+           !skb->csum_complete_sw)
                netdev_rx_csum_fault(skb->dev);
        return 0;
 no_checksum:
index 402a7e9a16b7cdf05aa5d23462b3113b1b96c4d3..be8bbd5d65ec6914f6178816556ce31ee7ef0883 100644 (file)
@@ -866,8 +866,6 @@ static void xs_reset_transport(struct sock_xprt *transport)
        xs_restore_old_callbacks(transport, sk);
        write_unlock_bh(&sk->sk_callback_lock);
 
-       sk->sk_no_check = 0;
-
        trace_rpc_socket_close(&transport->xprt, sock);
        sock_release(sock);
 }
@@ -2046,7 +2044,6 @@ static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
                sk->sk_user_data = xprt;
                sk->sk_data_ready = xs_udp_data_ready;
                sk->sk_write_space = xs_udp_write_space;
-               sk->sk_no_check = UDP_CSUM_NORCV;
                sk->sk_allocation = GFP_ATOMIC;
 
                xprt_set_connected(xprt);
index b282f7130d2bb51f0dee12e4800a9a0ad54a33a7..a080c66d819a032233a963512d849f757cc979e2 100644 (file)
@@ -5,7 +5,7 @@
 obj-$(CONFIG_TIPC) := tipc.o
 
 tipc-y += addr.o bcast.o bearer.o config.o \
-          core.o handler.o link.o discover.o msg.o  \
+          core.o link.o discover.o msg.o  \
           name_distr.o  subscr.o name_table.o net.o  \
           netlink.o node.o node_subscr.o port.o ref.o  \
           socket.o log.o eth_media.o server.o
index 95ab5ef92920fddf34c02478973b98ebbe96ba59..26631679a1faa4bb7b8c720dda1e48b5c68ab40e 100644 (file)
@@ -71,7 +71,7 @@ struct tipc_bcbearer_pair {
  * Note: The fields labelled "temporary" are incorporated into the bearer
  * to avoid consuming potentially limited stack space through the use of
  * large local variables within multicast routines.  Concurrent access is
- * prevented through use of the spinlock "bc_lock".
+ * prevented through use of the spinlock "bclink_lock".
  */
 struct tipc_bcbearer {
        struct tipc_bearer bearer;
@@ -84,34 +84,64 @@ struct tipc_bcbearer {
 
 /**
  * struct tipc_bclink - link used for broadcast messages
+ * @lock: spinlock governing access to structure
  * @link: (non-standard) broadcast link structure
  * @node: (non-standard) node structure representing b'cast link's peer node
+ * @flags: represent bclink states
  * @bcast_nodes: map of broadcast-capable nodes
  * @retransmit_to: node that most recently requested a retransmit
  *
  * Handles sequence numbering, fragmentation, bundling, etc.
  */
 struct tipc_bclink {
+       spinlock_t lock;
        struct tipc_link link;
        struct tipc_node node;
+       unsigned int flags;
        struct tipc_node_map bcast_nodes;
        struct tipc_node *retransmit_to;
 };
 
-static struct tipc_bcbearer bcast_bearer;
-static struct tipc_bclink bcast_link;
-
-static struct tipc_bcbearer *bcbearer = &bcast_bearer;
-static struct tipc_bclink *bclink = &bcast_link;
-static struct tipc_link *bcl = &bcast_link.link;
-
-static DEFINE_SPINLOCK(bc_lock);
+static struct tipc_bcbearer *bcbearer;
+static struct tipc_bclink *bclink;
+static struct tipc_link *bcl;
 
 const char tipc_bclink_name[] = "broadcast-link";
 
 static void tipc_nmap_diff(struct tipc_node_map *nm_a,
                           struct tipc_node_map *nm_b,
                           struct tipc_node_map *nm_diff);
+static void tipc_nmap_add(struct tipc_node_map *nm_ptr, u32 node);
+static void tipc_nmap_remove(struct tipc_node_map *nm_ptr, u32 node);
+
+static void tipc_bclink_lock(void)
+{
+       spin_lock_bh(&bclink->lock);
+}
+
+static void tipc_bclink_unlock(void)
+{
+       struct tipc_node *node = NULL;
+
+       if (likely(!bclink->flags)) {
+               spin_unlock_bh(&bclink->lock);
+               return;
+       }
+
+       if (bclink->flags & TIPC_BCLINK_RESET) {
+               bclink->flags &= ~TIPC_BCLINK_RESET;
+               node = tipc_bclink_retransmit_to();
+       }
+       spin_unlock_bh(&bclink->lock);
+
+       if (node)
+               tipc_link_reset_all(node);
+}
+
+void tipc_bclink_set_flags(unsigned int flags)
+{
+       bclink->flags |= flags;
+}
 
 static u32 bcbuf_acks(struct sk_buff *buf)
 {
@@ -130,16 +160,16 @@ static void bcbuf_decr_acks(struct sk_buff *buf)
 
 void tipc_bclink_add_node(u32 addr)
 {
-       spin_lock_bh(&bc_lock);
+       tipc_bclink_lock();
        tipc_nmap_add(&bclink->bcast_nodes, addr);
-       spin_unlock_bh(&bc_lock);
+       tipc_bclink_unlock();
 }
 
 void tipc_bclink_remove_node(u32 addr)
 {
-       spin_lock_bh(&bc_lock);
+       tipc_bclink_lock();
        tipc_nmap_remove(&bclink->bcast_nodes, addr);
-       spin_unlock_bh(&bc_lock);
+       tipc_bclink_unlock();
 }
 
 static void bclink_set_last_sent(void)
@@ -165,7 +195,7 @@ static void bclink_update_last_sent(struct tipc_node *node, u32 seqno)
 /**
  * tipc_bclink_retransmit_to - get most recent node to request retransmission
  *
- * Called with bc_lock locked
+ * Called with bclink_lock locked
  */
 struct tipc_node *tipc_bclink_retransmit_to(void)
 {
@@ -177,7 +207,7 @@ struct tipc_node *tipc_bclink_retransmit_to(void)
  * @after: sequence number of last packet to *not* retransmit
  * @to: sequence number of last packet to retransmit
  *
- * Called with bc_lock locked
+ * Called with bclink_lock locked
  */
 static void bclink_retransmit_pkt(u32 after, u32 to)
 {
@@ -194,7 +224,7 @@ static void bclink_retransmit_pkt(u32 after, u32 to)
  * @n_ptr: node that sent acknowledgement info
  * @acked: broadcast sequence # that has been acknowledged
  *
- * Node is locked, bc_lock unlocked.
+ * Node is locked, bclink_lock unlocked.
  */
 void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked)
 {
@@ -202,8 +232,7 @@ void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked)
        struct sk_buff *next;
        unsigned int released = 0;
 
-       spin_lock_bh(&bc_lock);
-
+       tipc_bclink_lock();
        /* Bail out if tx queue is empty (no clean up is required) */
        crs = bcl->first_out;
        if (!crs)
@@ -267,13 +296,13 @@ void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked)
        if (unlikely(released && !list_empty(&bcl->waiting_ports)))
                tipc_link_wakeup_ports(bcl, 0);
 exit:
-       spin_unlock_bh(&bc_lock);
+       tipc_bclink_unlock();
 }
 
 /**
  * tipc_bclink_update_link_state - update broadcast link state
  *
- * tipc_net_lock and node lock set
+ * RCU and node lock set
  */
 void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent)
 {
@@ -320,10 +349,10 @@ void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent)
                                 ? buf_seqno(n_ptr->bclink.deferred_head) - 1
                                 : n_ptr->bclink.last_sent);
 
-               spin_lock_bh(&bc_lock);
-               tipc_bearer_send(&bcbearer->bearer, buf, NULL);
+               tipc_bclink_lock();
+               tipc_bearer_send(MAX_BEARERS, buf, NULL);
                bcl->stats.sent_nacks++;
-               spin_unlock_bh(&bc_lock);
+               tipc_bclink_unlock();
                kfree_skb(buf);
 
                n_ptr->bclink.oos_state++;
@@ -335,8 +364,6 @@ void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent)
  *
  * Delay any upcoming NACK by this node if another node has already
  * requested the first message this node is going to ask for.
- *
- * Only tipc_net_lock set.
  */
 static void bclink_peek_nack(struct tipc_msg *msg)
 {
@@ -362,7 +389,7 @@ int tipc_bclink_xmit(struct sk_buff *buf)
 {
        int res;
 
-       spin_lock_bh(&bc_lock);
+       tipc_bclink_lock();
 
        if (!bclink->bcast_nodes.count) {
                res = msg_data_sz(buf_msg(buf));
@@ -377,14 +404,14 @@ int tipc_bclink_xmit(struct sk_buff *buf)
                bcl->stats.accu_queue_sz += bcl->out_queue_size;
        }
 exit:
-       spin_unlock_bh(&bc_lock);
+       tipc_bclink_unlock();
        return res;
 }
 
 /**
  * bclink_accept_pkt - accept an incoming, in-sequence broadcast packet
  *
- * Called with both sending node's lock and bc_lock taken.
+ * Called with both sending node's lock and bclink_lock taken.
  */
 static void bclink_accept_pkt(struct tipc_node *node, u32 seqno)
 {
@@ -408,7 +435,7 @@ static void bclink_accept_pkt(struct tipc_node *node, u32 seqno)
 /**
  * tipc_bclink_rcv - receive a broadcast packet, and deliver upwards
  *
- * tipc_net_lock is read_locked, no other locks set
+ * RCU is locked, no other locks set
  */
 void tipc_bclink_rcv(struct sk_buff *buf)
 {
@@ -439,12 +466,12 @@ void tipc_bclink_rcv(struct sk_buff *buf)
                if (msg_destnode(msg) == tipc_own_addr) {
                        tipc_bclink_acknowledge(node, msg_bcast_ack(msg));
                        tipc_node_unlock(node);
-                       spin_lock_bh(&bc_lock);
+                       tipc_bclink_lock();
                        bcl->stats.recv_nacks++;
                        bclink->retransmit_to = node;
                        bclink_retransmit_pkt(msg_bcgap_after(msg),
                                              msg_bcgap_to(msg));
-                       spin_unlock_bh(&bc_lock);
+                       tipc_bclink_unlock();
                } else {
                        tipc_node_unlock(node);
                        bclink_peek_nack(msg);
@@ -462,51 +489,47 @@ void tipc_bclink_rcv(struct sk_buff *buf)
                /* Deliver message to destination */
 
                if (likely(msg_isdata(msg))) {
-                       spin_lock_bh(&bc_lock);
+                       tipc_bclink_lock();
                        bclink_accept_pkt(node, seqno);
-                       spin_unlock_bh(&bc_lock);
+                       tipc_bclink_unlock();
                        tipc_node_unlock(node);
                        if (likely(msg_mcast(msg)))
                                tipc_port_mcast_rcv(buf, NULL);
                        else
                                kfree_skb(buf);
                } else if (msg_user(msg) == MSG_BUNDLER) {
-                       spin_lock_bh(&bc_lock);
+                       tipc_bclink_lock();
                        bclink_accept_pkt(node, seqno);
                        bcl->stats.recv_bundles++;
                        bcl->stats.recv_bundled += msg_msgcnt(msg);
-                       spin_unlock_bh(&bc_lock);
+                       tipc_bclink_unlock();
                        tipc_node_unlock(node);
                        tipc_link_bundle_rcv(buf);
                } else if (msg_user(msg) == MSG_FRAGMENTER) {
-                       int ret;
-                       ret = tipc_link_frag_rcv(&node->bclink.reasm_head,
-                                                &node->bclink.reasm_tail,
-                                                &buf);
-                       if (ret == LINK_REASM_ERROR)
+                       tipc_buf_append(&node->bclink.reasm_buf, &buf);
+                       if (unlikely(!buf && !node->bclink.reasm_buf))
                                goto unlock;
-                       spin_lock_bh(&bc_lock);
+                       tipc_bclink_lock();
                        bclink_accept_pkt(node, seqno);
                        bcl->stats.recv_fragments++;
-                       if (ret == LINK_REASM_COMPLETE) {
+                       if (buf) {
                                bcl->stats.recv_fragmented++;
-                               /* Point msg to inner header */
                                msg = buf_msg(buf);
-                               spin_unlock_bh(&bc_lock);
+                               tipc_bclink_unlock();
                                goto receive;
                        }
-                       spin_unlock_bh(&bc_lock);
+                       tipc_bclink_unlock();
                        tipc_node_unlock(node);
                } else if (msg_user(msg) == NAME_DISTRIBUTOR) {
-                       spin_lock_bh(&bc_lock);
+                       tipc_bclink_lock();
                        bclink_accept_pkt(node, seqno);
-                       spin_unlock_bh(&bc_lock);
+                       tipc_bclink_unlock();
                        tipc_node_unlock(node);
                        tipc_named_rcv(buf);
                } else {
-                       spin_lock_bh(&bc_lock);
+                       tipc_bclink_lock();
                        bclink_accept_pkt(node, seqno);
-                       spin_unlock_bh(&bc_lock);
+                       tipc_bclink_unlock();
                        tipc_node_unlock(node);
                        kfree_skb(buf);
                }
@@ -552,14 +575,14 @@ void tipc_bclink_rcv(struct sk_buff *buf)
        } else
                deferred = 0;
 
-       spin_lock_bh(&bc_lock);
+       tipc_bclink_lock();
 
        if (deferred)
                bcl->stats.deferred_recv++;
        else
                bcl->stats.duplicates++;
 
-       spin_unlock_bh(&bc_lock);
+       tipc_bclink_unlock();
 
 unlock:
        tipc_node_unlock(node);
@@ -627,13 +650,13 @@ static int tipc_bcbearer_send(struct sk_buff *buf, struct tipc_bearer *unused1,
 
                if (bp_index == 0) {
                        /* Use original buffer for first bearer */
-                       tipc_bearer_send(b, buf, &b->bcast_addr);
+                       tipc_bearer_send(b->identity, buf, &b->bcast_addr);
                } else {
                        /* Avoid concurrent buffer access */
-                       tbuf = pskb_copy(buf, GFP_ATOMIC);
+                       tbuf = pskb_copy_for_clone(buf, GFP_ATOMIC);
                        if (!tbuf)
                                break;
-                       tipc_bearer_send(b, tbuf, &b->bcast_addr);
+                       tipc_bearer_send(b->identity, tbuf, &b->bcast_addr);
                        kfree_skb(tbuf); /* Bearer keeps a clone */
                }
 
@@ -655,20 +678,27 @@ static int tipc_bcbearer_send(struct sk_buff *buf, struct tipc_bearer *unused1,
 /**
  * tipc_bcbearer_sort - create sets of bearer pairs used by broadcast bearer
  */
-void tipc_bcbearer_sort(void)
+void tipc_bcbearer_sort(struct tipc_node_map *nm_ptr, u32 node, bool action)
 {
        struct tipc_bcbearer_pair *bp_temp = bcbearer->bpairs_temp;
        struct tipc_bcbearer_pair *bp_curr;
+       struct tipc_bearer *b;
        int b_index;
        int pri;
 
-       spin_lock_bh(&bc_lock);
+       tipc_bclink_lock();
+
+       if (action)
+               tipc_nmap_add(nm_ptr, node);
+       else
+               tipc_nmap_remove(nm_ptr, node);
 
        /* Group bearers by priority (can assume max of two per priority) */
        memset(bp_temp, 0, sizeof(bcbearer->bpairs_temp));
 
+       rcu_read_lock();
        for (b_index = 0; b_index < MAX_BEARERS; b_index++) {
-               struct tipc_bearer *b = bearer_list[b_index];
+               b = rcu_dereference_rtnl(bearer_list[b_index]);
                if (!b || !b->nodes.count)
                        continue;
 
@@ -677,6 +707,7 @@ void tipc_bcbearer_sort(void)
                else
                        bp_temp[b->priority].secondary = b;
        }
+       rcu_read_unlock();
 
        /* Create array of bearer pairs for broadcasting */
        bp_curr = bcbearer->bpairs;
@@ -702,7 +733,7 @@ void tipc_bcbearer_sort(void)
                bp_curr++;
        }
 
-       spin_unlock_bh(&bc_lock);
+       tipc_bclink_unlock();
 }
 
 
@@ -714,7 +745,7 @@ int tipc_bclink_stats(char *buf, const u32 buf_size)
        if (!bcl)
                return 0;
 
-       spin_lock_bh(&bc_lock);
+       tipc_bclink_lock();
 
        s = &bcl->stats;
 
@@ -743,7 +774,7 @@ int tipc_bclink_stats(char *buf, const u32 buf_size)
                             s->queue_sz_counts ?
                             (s->accu_queue_sz / s->queue_sz_counts) : 0);
 
-       spin_unlock_bh(&bc_lock);
+       tipc_bclink_unlock();
        return ret;
 }
 
@@ -752,9 +783,9 @@ int tipc_bclink_reset_stats(void)
        if (!bcl)
                return -ENOPROTOOPT;
 
-       spin_lock_bh(&bc_lock);
+       tipc_bclink_lock();
        memset(&bcl->stats, 0, sizeof(bcl->stats));
-       spin_unlock_bh(&bc_lock);
+       tipc_bclink_unlock();
        return 0;
 }
 
@@ -765,46 +796,59 @@ int tipc_bclink_set_queue_limits(u32 limit)
        if ((limit < TIPC_MIN_LINK_WIN) || (limit > TIPC_MAX_LINK_WIN))
                return -EINVAL;
 
-       spin_lock_bh(&bc_lock);
+       tipc_bclink_lock();
        tipc_link_set_queue_limits(bcl, limit);
-       spin_unlock_bh(&bc_lock);
+       tipc_bclink_unlock();
        return 0;
 }
 
-void tipc_bclink_init(void)
+int tipc_bclink_init(void)
 {
+       bcbearer = kzalloc(sizeof(*bcbearer), GFP_ATOMIC);
+       if (!bcbearer)
+               return -ENOMEM;
+
+       bclink = kzalloc(sizeof(*bclink), GFP_ATOMIC);
+       if (!bclink) {
+               kfree(bcbearer);
+               return -ENOMEM;
+       }
+
+       bcl = &bclink->link;
        bcbearer->bearer.media = &bcbearer->media;
        bcbearer->media.send_msg = tipc_bcbearer_send;
        sprintf(bcbearer->media.name, "tipc-broadcast");
 
+       spin_lock_init(&bclink->lock);
        INIT_LIST_HEAD(&bcl->waiting_ports);
        bcl->next_out_no = 1;
        spin_lock_init(&bclink->node.lock);
        bcl->owner = &bclink->node;
        bcl->max_pkt = MAX_PKT_DEFAULT_MCAST;
        tipc_link_set_queue_limits(bcl, BCLINK_WIN_DEFAULT);
-       bcl->b_ptr = &bcbearer->bearer;
-       bearer_list[BCBEARER] = &bcbearer->bearer;
+       bcl->bearer_id = MAX_BEARERS;
+       rcu_assign_pointer(bearer_list[MAX_BEARERS], &bcbearer->bearer);
        bcl->state = WORKING_WORKING;
        strlcpy(bcl->name, tipc_bclink_name, TIPC_MAX_LINK_NAME);
+       return 0;
 }
 
 void tipc_bclink_stop(void)
 {
-       spin_lock_bh(&bc_lock);
+       tipc_bclink_lock();
        tipc_link_purge_queues(bcl);
-       spin_unlock_bh(&bc_lock);
+       tipc_bclink_unlock();
 
-       bearer_list[BCBEARER] = NULL;
-       memset(bclink, 0, sizeof(*bclink));
-       memset(bcbearer, 0, sizeof(*bcbearer));
+       RCU_INIT_POINTER(bearer_list[BCBEARER], NULL);
+       synchronize_net();
+       kfree(bcbearer);
+       kfree(bclink);
 }
 
-
 /**
  * tipc_nmap_add - add a node to a node map
  */
-void tipc_nmap_add(struct tipc_node_map *nm_ptr, u32 node)
+static void tipc_nmap_add(struct tipc_node_map *nm_ptr, u32 node)
 {
        int n = tipc_node(node);
        int w = n / WSIZE;
@@ -819,7 +863,7 @@ void tipc_nmap_add(struct tipc_node_map *nm_ptr, u32 node)
 /**
  * tipc_nmap_remove - remove a node from a node map
  */
-void tipc_nmap_remove(struct tipc_node_map *nm_ptr, u32 node)
+static void tipc_nmap_remove(struct tipc_node_map *nm_ptr, u32 node)
 {
        int n = tipc_node(node);
        int w = n / WSIZE;
index a80ef54b818e221a98bd9bd69a3adf28d0001f88..00330c45df3e04d03626a31d5f2ee6ba66569298 100644 (file)
@@ -39,6 +39,7 @@
 
 #define MAX_NODES 4096
 #define WSIZE 32
+#define TIPC_BCLINK_RESET 1
 
 /**
  * struct tipc_node_map - set of node identifiers
@@ -69,9 +70,6 @@ struct tipc_node;
 
 extern const char tipc_bclink_name[];
 
-void tipc_nmap_add(struct tipc_node_map *nm_ptr, u32 node);
-void tipc_nmap_remove(struct tipc_node_map *nm_ptr, u32 node);
-
 /**
  * tipc_nmap_equal - test for equality of node maps
  */
@@ -84,8 +82,9 @@ static inline int tipc_nmap_equal(struct tipc_node_map *nm_a,
 void tipc_port_list_add(struct tipc_port_list *pl_ptr, u32 port);
 void tipc_port_list_free(struct tipc_port_list *pl_ptr);
 
-void tipc_bclink_init(void);
+int tipc_bclink_init(void);
 void tipc_bclink_stop(void);
+void tipc_bclink_set_flags(unsigned int flags);
 void tipc_bclink_add_node(u32 addr);
 void tipc_bclink_remove_node(u32 addr);
 struct tipc_node *tipc_bclink_retransmit_to(void);
@@ -98,6 +97,6 @@ void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent);
 int  tipc_bclink_stats(char *stats_buf, const u32 buf_size);
 int  tipc_bclink_reset_stats(void);
 int  tipc_bclink_set_queue_limits(u32 limit);
-void tipc_bcbearer_sort(void);
+void tipc_bcbearer_sort(struct tipc_node_map *nm_ptr, u32 node, bool action);
 
 #endif
index 3fef7eb776dc12934654b2bfa7500cfa75138ad4..264474394f9f75994205b751e734b632d21fbc02 100644 (file)
@@ -49,7 +49,7 @@ static struct tipc_media * const media_info_array[] = {
        NULL
 };
 
-struct tipc_bearer *bearer_list[MAX_BEARERS + 1];
+struct tipc_bearer __rcu *bearer_list[MAX_BEARERS + 1];
 
 static void bearer_disable(struct tipc_bearer *b_ptr, bool shutting_down);
 
@@ -178,7 +178,7 @@ struct tipc_bearer *tipc_bearer_find(const char *name)
        u32 i;
 
        for (i = 0; i < MAX_BEARERS; i++) {
-               b_ptr = bearer_list[i];
+               b_ptr = rtnl_dereference(bearer_list[i]);
                if (b_ptr && (!strcmp(b_ptr->name, name)))
                        return b_ptr;
        }
@@ -198,10 +198,9 @@ struct sk_buff *tipc_bearer_get_names(void)
        if (!buf)
                return NULL;
 
-       read_lock_bh(&tipc_net_lock);
        for (i = 0; media_info_array[i] != NULL; i++) {
                for (j = 0; j < MAX_BEARERS; j++) {
-                       b = bearer_list[j];
+                       b = rtnl_dereference(bearer_list[j]);
                        if (!b)
                                continue;
                        if (b->media == media_info_array[i]) {
@@ -211,22 +210,33 @@ struct sk_buff *tipc_bearer_get_names(void)
                        }
                }
        }
-       read_unlock_bh(&tipc_net_lock);
        return buf;
 }
 
-void tipc_bearer_add_dest(struct tipc_bearer *b_ptr, u32 dest)
+void tipc_bearer_add_dest(u32 bearer_id, u32 dest)
 {
-       tipc_nmap_add(&b_ptr->nodes, dest);
-       tipc_bcbearer_sort();
-       tipc_disc_add_dest(b_ptr->link_req);
+       struct tipc_bearer *b_ptr;
+
+       rcu_read_lock();
+       b_ptr = rcu_dereference_rtnl(bearer_list[bearer_id]);
+       if (b_ptr) {
+               tipc_bcbearer_sort(&b_ptr->nodes, dest, true);
+               tipc_disc_add_dest(b_ptr->link_req);
+       }
+       rcu_read_unlock();
 }
 
-void tipc_bearer_remove_dest(struct tipc_bearer *b_ptr, u32 dest)
+void tipc_bearer_remove_dest(u32 bearer_id, u32 dest)
 {
-       tipc_nmap_remove(&b_ptr->nodes, dest);
-       tipc_bcbearer_sort();
-       tipc_disc_remove_dest(b_ptr->link_req);
+       struct tipc_bearer *b_ptr;
+
+       rcu_read_lock();
+       b_ptr = rcu_dereference_rtnl(bearer_list[bearer_id]);
+       if (b_ptr) {
+               tipc_bcbearer_sort(&b_ptr->nodes, dest, false);
+               tipc_disc_remove_dest(b_ptr->link_req);
+       }
+       rcu_read_unlock();
 }
 
 /**
@@ -271,13 +281,11 @@ int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority)
                return -EINVAL;
        }
 
-       write_lock_bh(&tipc_net_lock);
-
        m_ptr = tipc_media_find(b_names.media_name);
        if (!m_ptr) {
                pr_warn("Bearer <%s> rejected, media <%s> not registered\n",
                        name, b_names.media_name);
-               goto exit;
+               return -EINVAL;
        }
 
        if (priority == TIPC_MEDIA_LINK_PRI)
@@ -287,7 +295,7 @@ int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority)
        bearer_id = MAX_BEARERS;
        with_this_prio = 1;
        for (i = MAX_BEARERS; i-- != 0; ) {
-               b_ptr = bearer_list[i];
+               b_ptr = rtnl_dereference(bearer_list[i]);
                if (!b_ptr) {
                        bearer_id = i;
                        continue;
@@ -295,14 +303,14 @@ int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority)
                if (!strcmp(name, b_ptr->name)) {
                        pr_warn("Bearer <%s> rejected, already enabled\n",
                                name);
-                       goto exit;
+                       return -EINVAL;
                }
                if ((b_ptr->priority == priority) &&
                    (++with_this_prio > 2)) {
                        if (priority-- == 0) {
                                pr_warn("Bearer <%s> rejected, duplicate priority\n",
                                        name);
-                               goto exit;
+                               return -EINVAL;
                        }
                        pr_warn("Bearer <%s> priority adjustment required %u->%u\n",
                                name, priority + 1, priority);
@@ -312,21 +320,20 @@ int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority)
        if (bearer_id >= MAX_BEARERS) {
                pr_warn("Bearer <%s> rejected, bearer limit reached (%u)\n",
                        name, MAX_BEARERS);
-               goto exit;
+               return -EINVAL;
        }
 
        b_ptr = kzalloc(sizeof(*b_ptr), GFP_ATOMIC);
-       if (!b_ptr) {
-               res = -ENOMEM;
-               goto exit;
-       }
+       if (!b_ptr)
+               return -ENOMEM;
+
        strcpy(b_ptr->name, name);
        b_ptr->media = m_ptr;
        res = m_ptr->enable_media(b_ptr);
        if (res) {
                pr_warn("Bearer <%s> rejected, enable failure (%d)\n",
                        name, -res);
-               goto exit;
+               return -EINVAL;
        }
 
        b_ptr->identity = bearer_id;
@@ -341,16 +348,14 @@ int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority)
                bearer_disable(b_ptr, false);
                pr_warn("Bearer <%s> rejected, discovery object creation failed\n",
                        name);
-               goto exit;
+               return -EINVAL;
        }
 
-       bearer_list[bearer_id] = b_ptr;
+       rcu_assign_pointer(bearer_list[bearer_id], b_ptr);
 
        pr_info("Enabled bearer <%s>, discovery domain %s, priority %u\n",
                name,
                tipc_addr_string_fill(addr_string, disc_domain), priority);
-exit:
-       write_unlock_bh(&tipc_net_lock);
        return res;
 }
 
@@ -359,19 +364,16 @@ int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority)
  */
 static int tipc_reset_bearer(struct tipc_bearer *b_ptr)
 {
-       read_lock_bh(&tipc_net_lock);
        pr_info("Resetting bearer <%s>\n", b_ptr->name);
-       tipc_disc_delete(b_ptr->link_req);
        tipc_link_reset_list(b_ptr->identity);
-       tipc_disc_create(b_ptr, &b_ptr->bcast_addr);
-       read_unlock_bh(&tipc_net_lock);
+       tipc_disc_reset(b_ptr);
        return 0;
 }
 
 /**
  * bearer_disable
  *
- * Note: This routine assumes caller holds tipc_net_lock.
+ * Note: This routine assumes caller holds RTNL lock.
  */
 static void bearer_disable(struct tipc_bearer *b_ptr, bool shutting_down)
 {
@@ -385,12 +387,12 @@ static void bearer_disable(struct tipc_bearer *b_ptr, bool shutting_down)
                tipc_disc_delete(b_ptr->link_req);
 
        for (i = 0; i < MAX_BEARERS; i++) {
-               if (b_ptr == bearer_list[i]) {
-                       bearer_list[i] = NULL;
+               if (b_ptr == rtnl_dereference(bearer_list[i])) {
+                       RCU_INIT_POINTER(bearer_list[i], NULL);
                        break;
                }
        }
-       kfree(b_ptr);
+       kfree_rcu(b_ptr, rcu);
 }
 
 int tipc_disable_bearer(const char *name)
@@ -398,7 +400,6 @@ int tipc_disable_bearer(const char *name)
        struct tipc_bearer *b_ptr;
        int res;
 
-       write_lock_bh(&tipc_net_lock);
        b_ptr = tipc_bearer_find(name);
        if (b_ptr == NULL) {
                pr_warn("Attempt to disable unknown bearer <%s>\n", name);
@@ -407,32 +408,9 @@ int tipc_disable_bearer(const char *name)
                bearer_disable(b_ptr, false);
                res = 0;
        }
-       write_unlock_bh(&tipc_net_lock);
        return res;
 }
 
-
-/* tipc_l2_media_addr_set - initialize Ethernet media address structure
- *
- * Media-dependent "value" field stores MAC address in first 6 bytes
- * and zeroes out the remaining bytes.
- */
-void tipc_l2_media_addr_set(const struct tipc_bearer *b,
-                           struct tipc_media_addr *a, char *mac)
-{
-       int len = b->media->hwaddr_len;
-
-       if (unlikely(sizeof(a->value) < len)) {
-               WARN_ONCE(1, "Media length invalid\n");
-               return;
-       }
-
-       memcpy(a->value, mac, len);
-       memset(a->value + len, 0, sizeof(a->value) - len);
-       a->media_id = b->media->type_id;
-       a->broadcast = !memcmp(mac, b->bcast_addr.value, len);
-}
-
 int tipc_enable_l2_media(struct tipc_bearer *b)
 {
        struct net_device *dev;
@@ -443,33 +421,37 @@ int tipc_enable_l2_media(struct tipc_bearer *b)
        if (!dev)
                return -ENODEV;
 
-       /* Associate TIPC bearer with Ethernet bearer */
-       b->media_ptr = dev;
-       memset(b->bcast_addr.value, 0, sizeof(b->bcast_addr.value));
+       /* Associate TIPC bearer with L2 bearer */
+       rcu_assign_pointer(b->media_ptr, dev);
+       memset(&b->bcast_addr, 0, sizeof(b->bcast_addr));
        memcpy(b->bcast_addr.value, dev->broadcast, b->media->hwaddr_len);
        b->bcast_addr.media_id = b->media->type_id;
        b->bcast_addr.broadcast = 1;
        b->mtu = dev->mtu;
-       tipc_l2_media_addr_set(b, &b->addr, (char *)dev->dev_addr);
+       b->media->raw2addr(b, &b->addr, (char *)dev->dev_addr);
        rcu_assign_pointer(dev->tipc_ptr, b);
        return 0;
 }
 
-/* tipc_disable_l2_media - detach TIPC bearer from an Ethernet interface
+/* tipc_disable_l2_media - detach TIPC bearer from an L2 interface
  *
- * Mark Ethernet bearer as inactive so that incoming buffers are thrown away,
+ * Mark L2 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.)
  */
 void tipc_disable_l2_media(struct tipc_bearer *b)
 {
-       struct net_device *dev = (struct net_device *)b->media_ptr;
+       struct net_device *dev;
+
+       dev = (struct net_device *)rtnl_dereference(b->media_ptr);
+       RCU_INIT_POINTER(b->media_ptr, NULL);
        RCU_INIT_POINTER(dev->tipc_ptr, NULL);
+       synchronize_net();
        dev_put(dev);
 }
 
 /**
- * tipc_l2_send_msg - send a TIPC packet out over an Ethernet interface
+ * tipc_l2_send_msg - send a TIPC packet out over an L2 interface
  * @buf: the packet to be sent
  * @b_ptr: the bearer through which the packet is to be sent
  * @dest: peer destination address
@@ -478,8 +460,12 @@ int tipc_l2_send_msg(struct sk_buff *buf, struct tipc_bearer *b,
                     struct tipc_media_addr *dest)
 {
        struct sk_buff *clone;
+       struct net_device *dev;
        int delta;
-       struct net_device *dev = (struct net_device *)b->media_ptr;
+
+       dev = (struct net_device *)rcu_dereference_rtnl(b->media_ptr);
+       if (!dev)
+               return 0;
 
        clone = skb_clone(buf, GFP_ATOMIC);
        if (!clone)
@@ -507,10 +493,16 @@ int tipc_l2_send_msg(struct sk_buff *buf, struct tipc_bearer *b,
  * The media send routine must not alter the buffer being passed in
  * as it may be needed for later retransmission!
  */
-void tipc_bearer_send(struct tipc_bearer *b, struct sk_buff *buf,
+void tipc_bearer_send(u32 bearer_id, struct sk_buff *buf,
                      struct tipc_media_addr *dest)
 {
-       b->media->send_msg(buf, b, dest);
+       struct tipc_bearer *b_ptr;
+
+       rcu_read_lock();
+       b_ptr = rcu_dereference_rtnl(bearer_list[bearer_id]);
+       if (likely(b_ptr))
+               b_ptr->media->send_msg(buf, b_ptr, dest);
+       rcu_read_unlock();
 }
 
 /**
@@ -535,7 +527,7 @@ static int tipc_l2_rcv_msg(struct sk_buff *buf, struct net_device *dev,
        }
 
        rcu_read_lock();
-       b_ptr = rcu_dereference(dev->tipc_ptr);
+       b_ptr = rcu_dereference_rtnl(dev->tipc_ptr);
        if (likely(b_ptr)) {
                if (likely(buf->pkt_type <= PACKET_BROADCAST)) {
                        buf->next = NULL;
@@ -568,12 +560,9 @@ static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt,
        if (!net_eq(dev_net(dev), &init_net))
                return NOTIFY_DONE;
 
-       rcu_read_lock();
-       b_ptr = rcu_dereference(dev->tipc_ptr);
-       if (!b_ptr) {
-               rcu_read_unlock();
+       b_ptr = rtnl_dereference(dev->tipc_ptr);
+       if (!b_ptr)
                return NOTIFY_DONE;
-       }
 
        b_ptr->mtu = dev->mtu;
 
@@ -586,17 +575,15 @@ static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt,
                tipc_reset_bearer(b_ptr);
                break;
        case NETDEV_CHANGEADDR:
-               tipc_l2_media_addr_set(b_ptr, &b_ptr->addr,
+               b_ptr->media->raw2addr(b_ptr, &b_ptr->addr,
                                       (char *)dev->dev_addr);
                tipc_reset_bearer(b_ptr);
                break;
        case NETDEV_UNREGISTER:
        case NETDEV_CHANGENAME:
-               tipc_disable_bearer(b_ptr->name);
+               bearer_disable(b_ptr, false);
                break;
        }
-       rcu_read_unlock();
-
        return NOTIFY_OK;
 }
 
@@ -633,7 +620,7 @@ void tipc_bearer_stop(void)
        u32 i;
 
        for (i = 0; i < MAX_BEARERS; i++) {
-               b_ptr = bearer_list[i];
+               b_ptr = rtnl_dereference(bearer_list[i]);
                if (b_ptr) {
                        bearer_disable(b_ptr, true);
                        bearer_list[i] = NULL;
index ba48145e871dd8dcd357a193b61e9234e5ad7f0d..78fccc49de23c1e6dc20c1799d629c4083f6f80c 100644 (file)
 #define MAX_BEARERS    2
 #define MAX_MEDIA      2
 
-/*
- * Identifiers associated with TIPC message header media address info
- *
- * - address info field is 20 bytes long
- * - media type identifier located at offset 3
- * - remaining bytes vary according to media type
+/* Identifiers associated with TIPC message header media address info
+ * - address info field is 32 bytes long
+ * - the field's actual content and length is defined per media
+ * - remaining unused bytes in the field are set to zero
  */
-#define TIPC_MEDIA_ADDR_SIZE   20
+#define TIPC_MEDIA_ADDR_SIZE   32
 #define TIPC_MEDIA_TYPE_OFFSET 3
 
 /*
@@ -77,9 +75,10 @@ struct tipc_bearer;
  * @send_msg: routine which handles buffer transmission
  * @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
+ * @addr2str: convert media address format to string
+ * @addr2msg: convert from media addr format to discovery msg addr format
+ * @msg2addr: convert from discovery msg addr format to media addr format
+ * @raw2addr: convert from raw addr format to media addr format
  * @priority: default link (and bearer) priority
  * @tolerance: default time (in ms) before declaring link failure
  * @window: default window (in packets) before declaring link congestion
@@ -93,10 +92,16 @@ struct tipc_media {
                        struct tipc_media_addr *dest);
        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,
-                       struct tipc_media_addr *a, char *msg_area);
+       int (*addr2str)(struct tipc_media_addr *addr,
+                       char *strbuf,
+                       int bufsz);
+       int (*addr2msg)(char *msg, struct tipc_media_addr *addr);
+       int (*msg2addr)(struct tipc_bearer *b,
+                       struct tipc_media_addr *addr,
+                       char *msg);
+       int (*raw2addr)(struct tipc_bearer *b,
+                       struct tipc_media_addr *addr,
+                       char *raw);
        u32 priority;
        u32 tolerance;
        u32 window;
@@ -113,6 +118,7 @@ struct tipc_media {
  * @name: bearer name (format = media:interface)
  * @media: ptr to media structure associated with bearer
  * @bcast_addr: media address used in broadcasting
+ * @rcu: rcu struct for tipc_bearer
  * @priority: default link priority for bearer
  * @window: default window size for bearer
  * @tolerance: default link tolerance for bearer
@@ -127,12 +133,13 @@ struct tipc_media {
  * care of initializing all other fields.
  */
 struct tipc_bearer {
-       void *media_ptr;                        /* initalized by media */
+       void __rcu *media_ptr;                  /* initalized by media */
        u32 mtu;                                /* initalized by media */
        struct tipc_media_addr addr;            /* initalized by media */
        char name[TIPC_MAX_BEARER_NAME];
        struct tipc_media *media;
        struct tipc_media_addr bcast_addr;
+       struct rcu_head rcu;
        u32 priority;
        u32 window;
        u32 tolerance;
@@ -150,7 +157,7 @@ struct tipc_bearer_names {
 
 struct tipc_link;
 
-extern struct tipc_bearer *bearer_list[];
+extern struct tipc_bearer __rcu *bearer_list[];
 
 /*
  * TIPC routines available to supported media types
@@ -173,22 +180,20 @@ int tipc_media_set_priority(const char *name, u32 new_value);
 int tipc_media_set_window(const char *name, u32 new_value);
 void tipc_media_addr_printf(char *buf, int len, struct tipc_media_addr *a);
 struct sk_buff *tipc_media_get_names(void);
-void tipc_l2_media_addr_set(const struct tipc_bearer *b,
-                           struct tipc_media_addr *a, char *mac);
 int tipc_enable_l2_media(struct tipc_bearer *b);
 void tipc_disable_l2_media(struct tipc_bearer *b);
 int tipc_l2_send_msg(struct sk_buff *buf, struct tipc_bearer *b,
                     struct tipc_media_addr *dest);
 
 struct sk_buff *tipc_bearer_get_names(void);
-void tipc_bearer_add_dest(struct tipc_bearer *b_ptr, u32 dest);
-void tipc_bearer_remove_dest(struct tipc_bearer *b_ptr, u32 dest);
+void tipc_bearer_add_dest(u32 bearer_id, u32 dest);
+void tipc_bearer_remove_dest(u32 bearer_id, u32 dest);
 struct tipc_bearer *tipc_bearer_find(const char *name);
 struct tipc_media *tipc_media_find(const char *name);
 int tipc_bearer_setup(void);
 void tipc_bearer_cleanup(void);
 void tipc_bearer_stop(void);
-void tipc_bearer_send(struct tipc_bearer *b, struct sk_buff *buf,
+void tipc_bearer_send(u32 bearer_id, struct sk_buff *buf,
                      struct tipc_media_addr *dest);
 
 #endif /* _TIPC_BEARER_H */
index 4b981c053823e90cc31963277aedd8c3682bc1a0..2b42403ad33a690221456ff25fb4be50a2235255 100644 (file)
@@ -42,8 +42,6 @@
 
 #define REPLY_TRUNCATED "<truncated>\n"
 
-static DEFINE_MUTEX(config_mutex);
-
 static const void *req_tlv_area;       /* request message TLV area */
 static int req_tlv_space;              /* request message TLV area size */
 static int rep_headroom;               /* reply message headroom to use */
@@ -179,8 +177,10 @@ static struct sk_buff *cfg_set_own_addr(void)
        if (tipc_own_addr)
                return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED
                                                   " (cannot change node address once assigned)");
-       tipc_net_start(addr);
-       return tipc_cfg_reply_none();
+       if (!tipc_net_start(addr))
+               return tipc_cfg_reply_none();
+
+       return tipc_cfg_reply_error_string("cannot change to network mode");
 }
 
 static struct sk_buff *cfg_set_max_ports(void)
@@ -223,7 +223,7 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area
 {
        struct sk_buff *rep_tlv_buf;
 
-       mutex_lock(&config_mutex);
+       rtnl_lock();
 
        /* Save request and reply details in a well-known location */
        req_tlv_area = request_area;
@@ -337,6 +337,6 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area
 
        /* Return reply buffer */
 exit:
-       mutex_unlock(&config_mutex);
+       rtnl_unlock();
        return rep_tlv_buf;
 }
index 50d57429ebcaf82b8d36bcf49f6fa1585664180a..676d18015dd82efa0346f6bed2bf0d7f5489f1f6 100644 (file)
@@ -80,7 +80,6 @@ struct sk_buff *tipc_buf_acquire(u32 size)
  */
 static void tipc_core_stop(void)
 {
-       tipc_handler_stop();
        tipc_net_stop();
        tipc_bearer_cleanup();
        tipc_netlink_stop();
@@ -100,10 +99,6 @@ static int tipc_core_start(void)
 
        get_random_bytes(&tipc_random, sizeof(tipc_random));
 
-       err = tipc_handler_start();
-       if (err)
-               goto out_handler;
-
        err = tipc_ref_table_init(tipc_max_ports, tipc_random);
        if (err)
                goto out_reftbl;
@@ -146,8 +141,6 @@ static int tipc_core_start(void)
 out_nametbl:
        tipc_ref_table_stop();
 out_reftbl:
-       tipc_handler_stop();
-out_handler:
        return err;
 }
 
@@ -161,10 +154,11 @@ static int __init tipc_init(void)
        tipc_max_ports = CONFIG_TIPC_PORTS;
        tipc_net_id = 4711;
 
-       sysctl_tipc_rmem[0] = CONN_OVERLOAD_LIMIT >> 4 << TIPC_LOW_IMPORTANCE;
-       sysctl_tipc_rmem[1] = CONN_OVERLOAD_LIMIT >> 4 <<
+       sysctl_tipc_rmem[0] = TIPC_CONN_OVERLOAD_LIMIT >> 4 <<
+                             TIPC_LOW_IMPORTANCE;
+       sysctl_tipc_rmem[1] = TIPC_CONN_OVERLOAD_LIMIT >> 4 <<
                              TIPC_CRITICAL_IMPORTANCE;
-       sysctl_tipc_rmem[2] = CONN_OVERLOAD_LIMIT;
+       sysctl_tipc_rmem[2] = TIPC_CONN_OVERLOAD_LIMIT;
 
        res = tipc_core_start();
        if (res)
index 8985bbcb942bdb3d6ef839c3249d4e547c2f75ce..bb26ed1ee966c84c66fc7322877d80f763047e5a 100644 (file)
@@ -56,7 +56,8 @@
 #include <linux/list.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
-
+#include <linux/rtnetlink.h>
+#include <linux/etherdevice.h>
 
 #define TIPC_MOD_VER "2.0.0"
 
@@ -89,8 +90,6 @@ extern int tipc_random __read_mostly;
 /*
  * Routines available to privileged subsystems
  */
-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);
@@ -109,12 +108,10 @@ void tipc_unregister_sysctl(void);
 #endif
 
 /*
- * TIPC timer and signal code
+ * TIPC timer code
  */
 typedef void (*Handler) (unsigned long);
 
-u32 tipc_k_signal(Handler routine, unsigned long argument);
-
 /**
  * k_init_timer - initialize a timer
  * @timer: pointer to timer structure
@@ -191,6 +188,7 @@ static inline void k_term_timer(struct timer_list *timer)
 struct tipc_skb_cb {
        void *handle;
        bool deferred;
+       struct sk_buff *tail;
 };
 
 #define TIPC_SKB_CB(__skb) ((struct tipc_skb_cb *)&((__skb)->cb[0]))
index 542fe3413dc4e8d06d97eb2d5a409d2a7b26459a..aa722a42ef8b03b4d840e31bbf7582d51948fbae 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * net/tipc/discover.c
  *
- * Copyright (c) 2003-2006, Ericsson AB
+ * Copyright (c) 2003-2006, 2014, Ericsson AB
  * Copyright (c) 2005-2006, 2010-2011, Wind River Systems
  * All rights reserved.
  *
@@ -46,8 +46,9 @@
 
 /**
  * struct tipc_link_req - information about an ongoing link setup request
- * @bearer: bearer issuing requests
+ * @bearer_id: identity of bearer issuing requests
  * @dest: destination address for request messages
+ * @domain: network domain to which links can be established
  * @num_nodes: number of nodes currently discovered (i.e. with an active link)
  * @lock: spinlock for controlling access to requests
  * @buf: request message to be (repeatedly) sent
@@ -55,8 +56,9 @@
  * @timer_intv: current interval between requests (in ms)
  */
 struct tipc_link_req {
-       struct tipc_bearer *bearer;
+       u32 bearer_id;
        struct tipc_media_addr dest;
+       u32 domain;
        int num_nodes;
        spinlock_t lock;
        struct sk_buff *buf;
@@ -69,22 +71,19 @@ struct tipc_link_req {
  * @type: message type (request or response)
  * @b_ptr: ptr to bearer issuing message
  */
-static struct sk_buff *tipc_disc_init_msg(u32 type, struct tipc_bearer *b_ptr)
+static void tipc_disc_init_msg(struct sk_buff *buf, u32 type,
+                              struct tipc_bearer *b_ptr)
 {
-       struct sk_buff *buf = tipc_buf_acquire(INT_H_SIZE);
        struct tipc_msg *msg;
        u32 dest_domain = b_ptr->domain;
 
-       if (buf) {
-               msg = buf_msg(buf);
-               tipc_msg_init(msg, LINK_CONFIG, type, INT_H_SIZE, dest_domain);
-               msg_set_non_seq(msg, 1);
-               msg_set_node_sig(msg, tipc_random);
-               msg_set_dest_domain(msg, dest_domain);
-               msg_set_bc_netid(msg, tipc_net_id);
-               b_ptr->media->addr2msg(&b_ptr->addr, msg_media_addr(msg));
-       }
-       return buf;
+       msg = buf_msg(buf);
+       tipc_msg_init(msg, LINK_CONFIG, type, INT_H_SIZE, dest_domain);
+       msg_set_non_seq(msg, 1);
+       msg_set_node_sig(msg, tipc_random);
+       msg_set_dest_domain(msg, dest_domain);
+       msg_set_bc_netid(msg, tipc_net_id);
+       b_ptr->media->addr2msg(msg_media_addr(msg), &b_ptr->addr);
 }
 
 /**
@@ -107,146 +106,150 @@ static void disc_dupl_alert(struct tipc_bearer *b_ptr, u32 node_addr,
 }
 
 /**
- * tipc_disc_rcv - handle incoming link setup message (request or response)
+ * tipc_disc_rcv - handle incoming discovery message (request or response)
  * @buf: buffer containing message
- * @b_ptr: bearer that message arrived on
+ * @bearer: bearer that message arrived on
  */
-void tipc_disc_rcv(struct sk_buff *buf, struct tipc_bearer *b_ptr)
+void tipc_disc_rcv(struct sk_buff *buf, struct tipc_bearer *bearer)
 {
-       struct tipc_node *n_ptr;
+       struct tipc_node *node;
        struct tipc_link *link;
-       struct tipc_media_addr media_addr;
+       struct tipc_media_addr maddr;
        struct sk_buff *rbuf;
        struct tipc_msg *msg = buf_msg(buf);
-       u32 dest = msg_dest_domain(msg);
-       u32 orig = msg_prevnode(msg);
+       u32 ddom = msg_dest_domain(msg);
+       u32 onode = msg_prevnode(msg);
        u32 net_id = msg_bc_netid(msg);
-       u32 type = msg_type(msg);
+       u32 mtyp = msg_type(msg);
        u32 signature = msg_node_sig(msg);
-       int addr_mismatch;
-       int link_fully_up;
-
-       media_addr.broadcast = 1;
-       b_ptr->media->msg2addr(b_ptr, &media_addr, msg_media_addr(msg));
+       bool addr_match = false;
+       bool sign_match = false;
+       bool link_up = false;
+       bool accept_addr = false;
+       bool accept_sign = false;
+       bool respond = false;
+
+       bearer->media->msg2addr(bearer, &maddr, msg_media_addr(msg));
        kfree_skb(buf);
 
        /* Ensure message from node is valid and communication is permitted */
        if (net_id != tipc_net_id)
                return;
-       if (media_addr.broadcast)
+       if (maddr.broadcast)
                return;
-       if (!tipc_addr_domain_valid(dest))
+       if (!tipc_addr_domain_valid(ddom))
                return;
-       if (!tipc_addr_node_valid(orig))
+       if (!tipc_addr_node_valid(onode))
                return;
-       if (orig == tipc_own_addr) {
-               if (memcmp(&media_addr, &b_ptr->addr, sizeof(media_addr)))
-                       disc_dupl_alert(b_ptr, tipc_own_addr, &media_addr);
+
+       if (in_own_node(onode)) {
+               if (memcmp(&maddr, &bearer->addr, sizeof(maddr)))
+                       disc_dupl_alert(bearer, tipc_own_addr, &maddr);
                return;
        }
-       if (!tipc_in_scope(dest, tipc_own_addr))
+       if (!tipc_in_scope(ddom, tipc_own_addr))
                return;
-       if (!tipc_in_scope(b_ptr->domain, orig))
+       if (!tipc_in_scope(bearer->domain, onode))
                return;
 
-       /* Locate structure corresponding to requesting node */
-       n_ptr = tipc_node_find(orig);
-       if (!n_ptr) {
-               n_ptr = tipc_node_create(orig);
-               if (!n_ptr)
-                       return;
-       }
-       tipc_node_lock(n_ptr);
+       /* Locate, or if necessary, create, node: */
+       node = tipc_node_find(onode);
+       if (!node)
+               node = tipc_node_create(onode);
+       if (!node)
+               return;
 
-       /* Prepare to validate requesting node's signature and media address */
-       link = n_ptr->links[b_ptr->identity];
-       addr_mismatch = (link != NULL) &&
-               memcmp(&link->media_addr, &media_addr, sizeof(media_addr));
+       tipc_node_lock(node);
+       link = node->links[bearer->identity];
 
-       /*
-        * Ensure discovery message's signature is correct
-        *
-        * If signature is incorrect and there is no working link to the node,
-        * accept the new signature but invalidate all existing links to the
-        * node so they won't re-activate without a new discovery message.
-        *
-        * If signature is incorrect and the requested link to the node is
-        * working, accept the new signature. (This is an instance of delayed
-        * rediscovery, where a link endpoint was able to re-establish contact
-        * with its peer endpoint on a node that rebooted before receiving a
-        * discovery message from that node.)
-        *
-        * If signature is incorrect and there is a working link to the node
-        * that is not the requested link, reject the request (must be from
-        * a duplicate node).
-        */
-       if (signature != n_ptr->signature) {
-               if (n_ptr->working_links == 0) {
-                       struct tipc_link *curr_link;
-                       int i;
-
-                       for (i = 0; i < MAX_BEARERS; i++) {
-                               curr_link = n_ptr->links[i];
-                               if (curr_link) {
-                                       memset(&curr_link->media_addr, 0,
-                                              sizeof(media_addr));
-                                       tipc_link_reset(curr_link);
-                               }
-                       }
-                       addr_mismatch = (link != NULL);
-               } else if (tipc_link_is_up(link) && !addr_mismatch) {
-                       /* delayed rediscovery */
-               } else {
-                       disc_dupl_alert(b_ptr, orig, &media_addr);
-                       tipc_node_unlock(n_ptr);
-                       return;
-               }
-               n_ptr->signature = signature;
+       /* Prepare to validate requesting node's signature and media address */
+       sign_match = (signature == node->signature);
+       addr_match = link && !memcmp(&link->media_addr, &maddr, sizeof(maddr));
+       link_up = link && tipc_link_is_up(link);
+
+
+       /* These three flags give us eight permutations: */
+
+       if (sign_match && addr_match && link_up) {
+               /* All is fine. Do nothing. */
+       } else if (sign_match && addr_match && !link_up) {
+               /* Respond. The link will come up in due time */
+               respond = true;
+       } else if (sign_match && !addr_match && link_up) {
+               /* Peer has changed i/f address without rebooting.
+                * If so, the link will reset soon, and the next
+                * discovery will be accepted. So we can ignore it.
+                * It may also be an cloned or malicious peer having
+                * chosen the same node address and signature as an
+                * existing one.
+                * Ignore requests until the link goes down, if ever.
+                */
+               disc_dupl_alert(bearer, onode, &maddr);
+       } else if (sign_match && !addr_match && !link_up) {
+               /* Peer link has changed i/f address without rebooting.
+                * It may also be a cloned or malicious peer; we can't
+                * distinguish between the two.
+                * The signature is correct, so we must accept.
+                */
+               accept_addr = true;
+               respond = true;
+       } else if (!sign_match && addr_match && link_up) {
+               /* Peer node rebooted. Two possibilities:
+                *  - Delayed re-discovery; this link endpoint has already
+                *    reset and re-established contact with the peer, before
+                *    receiving a discovery message from that node.
+                *    (The peer happened to receive one from this node first).
+                *  - The peer came back so fast that our side has not
+                *    discovered it yet. Probing from this side will soon
+                *    reset the link, since there can be no working link
+                *    endpoint at the peer end, and the link will re-establish.
+                *  Accept the signature, since it comes from a known peer.
+                */
+               accept_sign = true;
+       } else if (!sign_match && addr_match && !link_up) {
+               /*  The peer node has rebooted.
+                *  Accept signature, since it is a known peer.
+                */
+               accept_sign = true;
+               respond = true;
+       } else if (!sign_match && !addr_match && link_up) {
+               /* Peer rebooted with new address, or a new/duplicate peer.
+                * Ignore until the link goes down, if ever.
+                */
+               disc_dupl_alert(bearer, onode, &maddr);
+       } else if (!sign_match && !addr_match && !link_up) {
+               /* Peer rebooted with new address, or it is a new peer.
+                * Accept signature and address.
+               */
+               accept_sign = true;
+               accept_addr = true;
+               respond = true;
        }
 
-       /*
-        * Ensure requesting node's media address is correct
-        *
-        * If media address doesn't match and the link is working, reject the
-        * request (must be from a duplicate node).
-        *
-        * If media address doesn't match and the link is not working, accept
-        * the new media address and reset the link to ensure it starts up
-        * cleanly.
-        */
-       if (addr_mismatch) {
-               if (tipc_link_is_up(link)) {
-                       disc_dupl_alert(b_ptr, orig, &media_addr);
-                       tipc_node_unlock(n_ptr);
-                       return;
-               } else {
-                       memcpy(&link->media_addr, &media_addr,
-                              sizeof(media_addr));
-                       tipc_link_reset(link);
-               }
-       }
+       if (accept_sign)
+               node->signature = signature;
 
-       /* Create a link endpoint for this bearer, if necessary */
-       if (!link) {
-               link = tipc_link_create(n_ptr, b_ptr, &media_addr);
-               if (!link) {
-                       tipc_node_unlock(n_ptr);
-                       return;
+       if (accept_addr) {
+               if (!link)
+                       link = tipc_link_create(node, bearer, &maddr);
+               if (link) {
+                       memcpy(&link->media_addr, &maddr, sizeof(maddr));
+                       tipc_link_reset(link);
+               } else {
+                       respond = false;
                }
        }
 
-       /* Accept discovery message & send response, if necessary */
-       link_fully_up = link_working_working(link);
-
-       if ((type == DSC_REQ_MSG) && !link_fully_up) {
-               rbuf = tipc_disc_init_msg(DSC_RESP_MSG, b_ptr);
+       /* Send response, if necessary */
+       if (respond && (mtyp == DSC_REQ_MSG)) {
+               rbuf = tipc_buf_acquire(INT_H_SIZE);
                if (rbuf) {
-                       tipc_bearer_send(b_ptr, rbuf, &media_addr);
+                       tipc_disc_init_msg(rbuf, DSC_RESP_MSG, bearer);
+                       tipc_bearer_send(bearer->identity, rbuf, &maddr);
                        kfree_skb(rbuf);
                }
        }
-
-       tipc_node_unlock(n_ptr);
+       tipc_node_unlock(node);
 }
 
 /**
@@ -303,7 +306,7 @@ static void disc_timeout(struct tipc_link_req *req)
        spin_lock_bh(&req->lock);
 
        /* Stop searching if only desired node has been found */
-       if (tipc_node(req->bearer->domain) && req->num_nodes) {
+       if (tipc_node(req->domain) && req->num_nodes) {
                req->timer_intv = TIPC_LINK_REQ_INACTIVE;
                goto exit;
        }
@@ -315,7 +318,7 @@ static void disc_timeout(struct tipc_link_req *req)
         * hold at fast polling rate if don't have any associated nodes,
         * otherwise hold at slow polling rate
         */
-       tipc_bearer_send(req->bearer, req->buf, &req->dest);
+       tipc_bearer_send(req->bearer_id, req->buf, &req->dest);
 
 
        req->timer_intv *= 2;
@@ -347,21 +350,23 @@ int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest)
        if (!req)
                return -ENOMEM;
 
-       req->buf = tipc_disc_init_msg(DSC_REQ_MSG, b_ptr);
+       req->buf = tipc_buf_acquire(INT_H_SIZE);
        if (!req->buf) {
                kfree(req);
-               return -ENOMSG;
+               return -ENOMEM;
        }
 
+       tipc_disc_init_msg(req->buf, DSC_REQ_MSG, b_ptr);
        memcpy(&req->dest, dest, sizeof(*dest));
-       req->bearer = b_ptr;
+       req->bearer_id = b_ptr->identity;
+       req->domain = b_ptr->domain;
        req->num_nodes = 0;
        req->timer_intv = TIPC_LINK_REQ_INIT;
        spin_lock_init(&req->lock);
        k_init_timer(&req->timer, (Handler)disc_timeout, (unsigned long)req);
        k_start_timer(&req->timer, req->timer_intv);
        b_ptr->link_req = req;
-       tipc_bearer_send(req->bearer, req->buf, &req->dest);
+       tipc_bearer_send(req->bearer_id, req->buf, &req->dest);
        return 0;
 }
 
@@ -376,3 +381,23 @@ void tipc_disc_delete(struct tipc_link_req *req)
        kfree_skb(req->buf);
        kfree(req);
 }
+
+/**
+ * tipc_disc_reset - reset object to send periodic link setup requests
+ * @b_ptr: ptr to bearer issuing requests
+ * @dest_domain: network domain to which links can be established
+ */
+void tipc_disc_reset(struct tipc_bearer *b_ptr)
+{
+       struct tipc_link_req *req = b_ptr->link_req;
+
+       spin_lock_bh(&req->lock);
+       tipc_disc_init_msg(req->buf, DSC_REQ_MSG, b_ptr);
+       req->bearer_id = b_ptr->identity;
+       req->domain = b_ptr->domain;
+       req->num_nodes = 0;
+       req->timer_intv = TIPC_LINK_REQ_INIT;
+       k_start_timer(&req->timer, req->timer_intv);
+       tipc_bearer_send(req->bearer_id, req->buf, &req->dest);
+       spin_unlock_bh(&req->lock);
+}
index 07f34729459dcacb93b71d8a56c69263db5f563b..515b57392f4d881b567d6d29cc7677bfece5e4c7 100644 (file)
@@ -41,6 +41,7 @@ struct tipc_link_req;
 
 int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest);
 void tipc_disc_delete(struct tipc_link_req *req);
+void tipc_disc_reset(struct tipc_bearer *b_ptr);
 void tipc_disc_add_dest(struct tipc_link_req *req);
 void tipc_disc_remove_dest(struct tipc_link_req *req);
 void tipc_disc_rcv(struct sk_buff *buf, struct tipc_bearer *b_ptr);
index 67cf3f935dba0a9e4d0141fc0406a93b1aeb60d6..5e1426f1751f146cf3350983e9d0c04d218da850 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * net/tipc/eth_media.c: Ethernet bearer support for TIPC
  *
- * Copyright (c) 2001-2007, 2013, Ericsson AB
+ * Copyright (c) 2001-2007, 2013-2014, Ericsson AB
  * Copyright (c) 2005-2008, 2011-2013, Wind River Systems
  * All rights reserved.
  *
 #include "core.h"
 #include "bearer.h"
 
-#define ETH_ADDR_OFFSET        4       /* message header offset of MAC address */
+#define ETH_ADDR_OFFSET  4  /* MAC addr position inside address field */
 
-/* convert Ethernet address to string */
-static int tipc_eth_addr2str(struct tipc_media_addr *a, char *str_buf,
-                            int str_size)
+/* Convert Ethernet address (media address format) to string */
+static int tipc_eth_addr2str(struct tipc_media_addr *addr,
+                            char *strbuf, int bufsz)
 {
-       if (str_size < 18)      /* 18 = strlen("aa:bb:cc:dd:ee:ff\0") */
+       if (bufsz < 18) /* 18 = strlen("aa:bb:cc:dd:ee:ff\0") */
                return 1;
 
-       sprintf(str_buf, "%pM", a->value);
+       sprintf(strbuf, "%pM", addr->value);
        return 0;
 }
 
-/* convert Ethernet address format to message header format */
-static int tipc_eth_addr2msg(struct tipc_media_addr *a, char *msg_area)
+/* Convert from media address format to discovery message addr format */
+static int tipc_eth_addr2msg(char *msg, struct tipc_media_addr *addr)
 {
-       memset(msg_area, 0, TIPC_MEDIA_ADDR_SIZE);
-       msg_area[TIPC_MEDIA_TYPE_OFFSET] = TIPC_MEDIA_TYPE_ETH;
-       memcpy(msg_area + ETH_ADDR_OFFSET, a->value, ETH_ALEN);
+       memset(msg, 0, TIPC_MEDIA_ADDR_SIZE);
+       msg[TIPC_MEDIA_TYPE_OFFSET] = TIPC_MEDIA_TYPE_ETH;
+       memcpy(msg + ETH_ADDR_OFFSET, addr->value, ETH_ALEN);
        return 0;
 }
 
-/* convert message header address format to Ethernet format */
-static int tipc_eth_msg2addr(const struct tipc_bearer *tb_ptr,
-                            struct tipc_media_addr *a, char *msg_area)
+/* Convert raw mac address format to media addr format */
+static int tipc_eth_raw2addr(struct tipc_bearer *b,
+                            struct tipc_media_addr *addr,
+                            char *msg)
 {
-       if (msg_area[TIPC_MEDIA_TYPE_OFFSET] != TIPC_MEDIA_TYPE_ETH)
-               return 1;
+       char bcast_mac[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
 
-       tipc_l2_media_addr_set(tb_ptr, a, msg_area + ETH_ADDR_OFFSET);
+       memset(addr, 0, sizeof(*addr));
+       ether_addr_copy(addr->value, msg);
+       addr->media_id = TIPC_MEDIA_TYPE_ETH;
+       addr->broadcast = !memcmp(addr->value, bcast_mac, ETH_ALEN);
        return 0;
 }
 
+/* Convert discovery msg addr format to Ethernet media addr format */
+static int tipc_eth_msg2addr(struct tipc_bearer *b,
+                            struct tipc_media_addr *addr,
+                            char *msg)
+{
+       /* Skip past preamble: */
+       msg += ETH_ADDR_OFFSET;
+       return tipc_eth_raw2addr(b, addr, msg);
+}
+
 /* Ethernet media registration info */
 struct tipc_media eth_media_info = {
        .send_msg       = tipc_l2_send_msg,
@@ -78,6 +91,7 @@ struct tipc_media eth_media_info = {
        .addr2str       = tipc_eth_addr2str,
        .addr2msg       = tipc_eth_addr2msg,
        .msg2addr       = tipc_eth_msg2addr,
+       .raw2addr       = tipc_eth_raw2addr,
        .priority       = TIPC_DEF_LINK_PRI,
        .tolerance      = TIPC_DEF_LINK_TOL,
        .window         = TIPC_DEF_LINK_WIN,
@@ -85,4 +99,3 @@ struct tipc_media eth_media_info = {
        .hwaddr_len     = ETH_ALEN,
        .name           = "eth"
 };
-
diff --git a/net/tipc/handler.c b/net/tipc/handler.c
deleted file mode 100644 (file)
index 1fabf16..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * net/tipc/handler.c: TIPC signal handling
- *
- * Copyright (c) 2000-2006, Ericsson AB
- * Copyright (c) 2005, Wind River Systems
- * 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 names of the copyright holders nor the names of its
- *    contributors may be used to endorse or promote products derived from
- *    this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * 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.
- */
-
-#include "core.h"
-
-struct queue_item {
-       struct list_head next_signal;
-       void (*handler) (unsigned long);
-       unsigned long data;
-};
-
-static struct kmem_cache *tipc_queue_item_cache;
-static struct list_head signal_queue_head;
-static DEFINE_SPINLOCK(qitem_lock);
-static int handler_enabled __read_mostly;
-
-static void process_signal_queue(unsigned long dummy);
-
-static DECLARE_TASKLET_DISABLED(tipc_tasklet, process_signal_queue, 0);
-
-
-unsigned int tipc_k_signal(Handler routine, unsigned long argument)
-{
-       struct queue_item *item;
-
-       spin_lock_bh(&qitem_lock);
-       if (!handler_enabled) {
-               spin_unlock_bh(&qitem_lock);
-               return -ENOPROTOOPT;
-       }
-
-       item = kmem_cache_alloc(tipc_queue_item_cache, GFP_ATOMIC);
-       if (!item) {
-               pr_err("Signal queue out of memory\n");
-               spin_unlock_bh(&qitem_lock);
-               return -ENOMEM;
-       }
-       item->handler = routine;
-       item->data = argument;
-       list_add_tail(&item->next_signal, &signal_queue_head);
-       spin_unlock_bh(&qitem_lock);
-       tasklet_schedule(&tipc_tasklet);
-       return 0;
-}
-
-static void process_signal_queue(unsigned long dummy)
-{
-       struct queue_item *__volatile__ item;
-       struct list_head *l, *n;
-
-       spin_lock_bh(&qitem_lock);
-       list_for_each_safe(l, n, &signal_queue_head) {
-               item = list_entry(l, struct queue_item, next_signal);
-               list_del(&item->next_signal);
-               spin_unlock_bh(&qitem_lock);
-               item->handler(item->data);
-               spin_lock_bh(&qitem_lock);
-               kmem_cache_free(tipc_queue_item_cache, item);
-       }
-       spin_unlock_bh(&qitem_lock);
-}
-
-int tipc_handler_start(void)
-{
-       tipc_queue_item_cache =
-               kmem_cache_create("tipc_queue_items", sizeof(struct queue_item),
-                                 0, SLAB_HWCACHE_ALIGN, NULL);
-       if (!tipc_queue_item_cache)
-               return -ENOMEM;
-
-       INIT_LIST_HEAD(&signal_queue_head);
-       tasklet_enable(&tipc_tasklet);
-       handler_enabled = 1;
-       return 0;
-}
-
-void tipc_handler_stop(void)
-{
-       struct list_head *l, *n;
-       struct queue_item *item;
-
-       spin_lock_bh(&qitem_lock);
-       if (!handler_enabled) {
-               spin_unlock_bh(&qitem_lock);
-               return;
-       }
-       handler_enabled = 0;
-       spin_unlock_bh(&qitem_lock);
-
-       tasklet_kill(&tipc_tasklet);
-
-       spin_lock_bh(&qitem_lock);
-       list_for_each_safe(l, n, &signal_queue_head) {
-               item = list_entry(l, struct queue_item, next_signal);
-               list_del(&item->next_signal);
-               kmem_cache_free(tipc_queue_item_cache, item);
-       }
-       spin_unlock_bh(&qitem_lock);
-
-       kmem_cache_destroy(tipc_queue_item_cache);
-}
index 844a77e2582856ae8cff4618c5e1de23f4220e0b..8522eef9c136bc25d39e166b32dfc459881d77c9 100644 (file)
@@ -42,7 +42,7 @@
 #include "core.h"
 #include "bearer.h"
 
-/* convert InfiniBand address to string */
+/* convert InfiniBand address (media address format) media address to string */
 static int tipc_ib_addr2str(struct tipc_media_addr *a, char *str_buf,
                            int str_size)
 {
@@ -54,23 +54,35 @@ static int tipc_ib_addr2str(struct tipc_media_addr *a, char *str_buf,
        return 0;
 }
 
-/* convert InfiniBand address format to message header format */
-static int tipc_ib_addr2msg(struct tipc_media_addr *a, char *msg_area)
+/* Convert from media address format to discovery message addr format */
+static int tipc_ib_addr2msg(char *msg, struct tipc_media_addr *addr)
 {
-       memset(msg_area, 0, TIPC_MEDIA_ADDR_SIZE);
-       msg_area[TIPC_MEDIA_TYPE_OFFSET] = TIPC_MEDIA_TYPE_IB;
-       memcpy(msg_area, a->value, INFINIBAND_ALEN);
+       memset(msg, 0, TIPC_MEDIA_ADDR_SIZE);
+       memcpy(msg, addr->value, INFINIBAND_ALEN);
        return 0;
 }
 
-/* convert message header address format to InfiniBand format */
-static int tipc_ib_msg2addr(const struct tipc_bearer *tb_ptr,
-                           struct tipc_media_addr *a, char *msg_area)
+/* Convert raw InfiniBand address format to media addr format */
+static int tipc_ib_raw2addr(struct tipc_bearer *b,
+                           struct tipc_media_addr *addr,
+                           char *msg)
 {
-       tipc_l2_media_addr_set(tb_ptr, a, msg_area);
+       memset(addr, 0, sizeof(*addr));
+       memcpy(addr->value, msg, INFINIBAND_ALEN);
+       addr->media_id = TIPC_MEDIA_TYPE_IB;
+       addr->broadcast = !memcmp(msg, b->bcast_addr.value,
+                                 INFINIBAND_ALEN);
        return 0;
 }
 
+/* Convert discovery msg addr format to InfiniBand media addr format */
+static int tipc_ib_msg2addr(struct tipc_bearer *b,
+                           struct tipc_media_addr *addr,
+                           char *msg)
+{
+       return tipc_ib_raw2addr(b, addr, msg);
+}
+
 /* InfiniBand media registration info */
 struct tipc_media ib_media_info = {
        .send_msg       = tipc_l2_send_msg,
@@ -79,6 +91,7 @@ struct tipc_media ib_media_info = {
        .addr2str       = tipc_ib_addr2str,
        .addr2msg       = tipc_ib_addr2msg,
        .msg2addr       = tipc_ib_msg2addr,
+       .raw2addr       = tipc_ib_raw2addr,
        .priority       = TIPC_DEF_LINK_PRI,
        .tolerance      = TIPC_DEF_LINK_TOL,
        .window         = TIPC_DEF_LINK_WIN,
@@ -86,4 +99,3 @@ struct tipc_media ib_media_info = {
        .hwaddr_len     = INFINIBAND_ALEN,
        .name           = "ib"
 };
-
index c5190ab75290d04202b99a3e923a69fe1a9dad38..ad2c57f5868dafe28fbf4204f9fc2189c1156d25 100644 (file)
@@ -37,6 +37,7 @@
 #include "core.h"
 #include "link.h"
 #include "port.h"
+#include "socket.h"
 #include "name_distr.h"
 #include "discover.h"
 #include "config.h"
@@ -101,9 +102,18 @@ static unsigned int align(unsigned int i)
 
 static void link_init_max_pkt(struct tipc_link *l_ptr)
 {
+       struct tipc_bearer *b_ptr;
        u32 max_pkt;
 
-       max_pkt = (l_ptr->b_ptr->mtu & ~3);
+       rcu_read_lock();
+       b_ptr = rcu_dereference_rtnl(bearer_list[l_ptr->bearer_id]);
+       if (!b_ptr) {
+               rcu_read_unlock();
+               return;
+       }
+       max_pkt = (b_ptr->mtu & ~3);
+       rcu_read_unlock();
+
        if (max_pkt > MAX_MSG_SIZE)
                max_pkt = MAX_MSG_SIZE;
 
@@ -248,7 +258,7 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr,
        l_ptr->owner = n_ptr;
        l_ptr->checkpoint = 1;
        l_ptr->peer_session = INVALID_SESSION;
-       l_ptr->b_ptr = b_ptr;
+       l_ptr->bearer_id = b_ptr->identity;
        link_set_supervision_props(l_ptr, b_ptr->tolerance);
        l_ptr->state = RESET_UNKNOWN;
 
@@ -263,6 +273,7 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr,
        l_ptr->priority = b_ptr->priority;
        tipc_link_set_queue_limits(l_ptr, b_ptr->window);
 
+       l_ptr->net_plane = b_ptr->net_plane;
        link_init_max_pkt(l_ptr);
 
        l_ptr->next_out_no = 1;
@@ -287,14 +298,14 @@ void tipc_link_delete_list(unsigned int bearer_id, bool shutting_down)
 
        rcu_read_lock();
        list_for_each_entry_rcu(n_ptr, &tipc_node_list, list) {
-               spin_lock_bh(&n_ptr->lock);
+               tipc_node_lock(n_ptr);
                l_ptr = n_ptr->links[bearer_id];
                if (l_ptr) {
                        tipc_link_reset(l_ptr);
                        if (shutting_down || !tipc_node_is_up(n_ptr)) {
                                tipc_node_detach_link(l_ptr->owner, l_ptr);
                                tipc_link_reset_fragments(l_ptr);
-                               spin_unlock_bh(&n_ptr->lock);
+                               tipc_node_unlock(n_ptr);
 
                                /* Nobody else can access this link now: */
                                del_timer_sync(&l_ptr->timer);
@@ -302,12 +313,12 @@ void tipc_link_delete_list(unsigned int bearer_id, bool shutting_down)
                        } else {
                                /* Detach/delete when failover is finished: */
                                l_ptr->flags |= LINK_STOPPED;
-                               spin_unlock_bh(&n_ptr->lock);
+                               tipc_node_unlock(n_ptr);
                                del_timer_sync(&l_ptr->timer);
                        }
                        continue;
                }
-               spin_unlock_bh(&n_ptr->lock);
+               tipc_node_unlock(n_ptr);
        }
        rcu_read_unlock();
 }
@@ -388,9 +399,8 @@ static void link_release_outqueue(struct tipc_link *l_ptr)
  */
 void tipc_link_reset_fragments(struct tipc_link *l_ptr)
 {
-       kfree_skb(l_ptr->reasm_head);
-       l_ptr->reasm_head = NULL;
-       l_ptr->reasm_tail = NULL;
+       kfree_skb(l_ptr->reasm_buf);
+       l_ptr->reasm_buf = NULL;
 }
 
 /**
@@ -426,7 +436,7 @@ void tipc_link_reset(struct tipc_link *l_ptr)
                return;
 
        tipc_node_link_down(l_ptr->owner, l_ptr);
-       tipc_bearer_remove_dest(l_ptr->b_ptr, l_ptr->addr);
+       tipc_bearer_remove_dest(l_ptr->bearer_id, l_ptr->addr);
 
        if (was_active_link && tipc_node_active_links(l_ptr->owner)) {
                l_ptr->reset_checkpoint = checkpoint;
@@ -464,11 +474,11 @@ void tipc_link_reset_list(unsigned int bearer_id)
 
        rcu_read_lock();
        list_for_each_entry_rcu(n_ptr, &tipc_node_list, list) {
-               spin_lock_bh(&n_ptr->lock);
+               tipc_node_lock(n_ptr);
                l_ptr = n_ptr->links[bearer_id];
                if (l_ptr)
                        tipc_link_reset(l_ptr);
-               spin_unlock_bh(&n_ptr->lock);
+               tipc_node_unlock(n_ptr);
        }
        rcu_read_unlock();
 }
@@ -477,7 +487,7 @@ static void link_activate(struct tipc_link *l_ptr)
 {
        l_ptr->next_in_no = l_ptr->stats.recv_info = 1;
        tipc_node_link_up(l_ptr->owner, l_ptr);
-       tipc_bearer_add_dest(l_ptr->b_ptr, l_ptr->addr);
+       tipc_bearer_add_dest(l_ptr->bearer_id, l_ptr->addr);
 }
 
 /**
@@ -777,7 +787,7 @@ int __tipc_link_xmit(struct tipc_link *l_ptr, struct sk_buff *buf)
        if (likely(!link_congested(l_ptr))) {
                link_add_to_outqueue(l_ptr, buf, msg);
 
-               tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr);
+               tipc_bearer_send(l_ptr->bearer_id, buf, &l_ptr->media_addr);
                l_ptr->unacked_window = 0;
                return dsz;
        }
@@ -825,7 +835,6 @@ int tipc_link_xmit(struct sk_buff *buf, u32 dest, u32 selector)
        struct tipc_node *n_ptr;
        int res = -ELINKCONG;
 
-       read_lock_bh(&tipc_net_lock);
        n_ptr = tipc_node_find(dest);
        if (n_ptr) {
                tipc_node_lock(n_ptr);
@@ -838,7 +847,6 @@ int tipc_link_xmit(struct sk_buff *buf, u32 dest, u32 selector)
        } else {
                kfree_skb(buf);
        }
-       read_unlock_bh(&tipc_net_lock);
        return res;
 }
 
@@ -902,7 +910,6 @@ void tipc_link_names_xmit(struct list_head *message_list, u32 dest)
        if (list_empty(message_list))
                return;
 
-       read_lock_bh(&tipc_net_lock);
        n_ptr = tipc_node_find(dest);
        if (n_ptr) {
                tipc_node_lock(n_ptr);
@@ -917,7 +924,6 @@ void tipc_link_names_xmit(struct list_head *message_list, u32 dest)
                }
                tipc_node_unlock(n_ptr);
        }
-       read_unlock_bh(&tipc_net_lock);
 
        /* discard the messages if they couldn't be sent */
        list_for_each_safe(buf, temp_buf, ((struct sk_buff *)message_list)) {
@@ -941,7 +947,7 @@ static int tipc_link_xmit_fast(struct tipc_link *l_ptr, struct sk_buff *buf,
        if (likely(!link_congested(l_ptr))) {
                if (likely(msg_size(msg) <= l_ptr->max_pkt)) {
                        link_add_to_outqueue(l_ptr, buf, msg);
-                       tipc_bearer_send(l_ptr->b_ptr, buf,
+                       tipc_bearer_send(l_ptr->bearer_id, buf,
                                         &l_ptr->media_addr);
                        l_ptr->unacked_window = 0;
                        return res;
@@ -979,7 +985,6 @@ int tipc_link_iovec_xmit_fast(struct tipc_port *sender,
        if (unlikely(res < 0))
                return res;
 
-       read_lock_bh(&tipc_net_lock);
        node = tipc_node_find(destaddr);
        if (likely(node)) {
                tipc_node_lock(node);
@@ -990,7 +995,6 @@ int tipc_link_iovec_xmit_fast(struct tipc_port *sender,
                                                          &sender->max_pkt);
 exit:
                                tipc_node_unlock(node);
-                               read_unlock_bh(&tipc_net_lock);
                                return res;
                        }
 
@@ -1007,7 +1011,6 @@ int tipc_link_iovec_xmit_fast(struct tipc_port *sender,
                         */
                        sender->max_pkt = l_ptr->max_pkt;
                        tipc_node_unlock(node);
-                       read_unlock_bh(&tipc_net_lock);
 
 
                        if ((msg_hdr_sz(hdr) + res) <= sender->max_pkt)
@@ -1018,7 +1021,6 @@ int tipc_link_iovec_xmit_fast(struct tipc_port *sender,
                }
                tipc_node_unlock(node);
        }
-       read_unlock_bh(&tipc_net_lock);
 
        /* Couldn't find a link to the destination node */
        kfree_skb(buf);
@@ -1204,7 +1206,7 @@ static u32 tipc_link_push_packet(struct tipc_link *l_ptr)
        if (r_q_size && buf) {
                msg_set_ack(buf_msg(buf), mod(l_ptr->next_in_no - 1));
                msg_set_bcast_ack(buf_msg(buf), l_ptr->owner->bclink.last_in);
-               tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr);
+               tipc_bearer_send(l_ptr->bearer_id, buf, &l_ptr->media_addr);
                l_ptr->retransm_queue_head = mod(++r_q_head);
                l_ptr->retransm_queue_size = --r_q_size;
                l_ptr->stats.retransmitted++;
@@ -1216,7 +1218,7 @@ static u32 tipc_link_push_packet(struct tipc_link *l_ptr)
        if (buf) {
                msg_set_ack(buf_msg(buf), mod(l_ptr->next_in_no - 1));
                msg_set_bcast_ack(buf_msg(buf), l_ptr->owner->bclink.last_in);
-               tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr);
+               tipc_bearer_send(l_ptr->bearer_id, buf, &l_ptr->media_addr);
                l_ptr->unacked_window = 0;
                kfree_skb(buf);
                l_ptr->proto_msg_queue = NULL;
@@ -1233,7 +1235,8 @@ static u32 tipc_link_push_packet(struct tipc_link *l_ptr)
                if (mod(next - first) < l_ptr->queue_limit[0]) {
                        msg_set_ack(msg, mod(l_ptr->next_in_no - 1));
                        msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in);
-                       tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr);
+                       tipc_bearer_send(l_ptr->bearer_id, buf,
+                                        &l_ptr->media_addr);
                        if (msg_user(msg) == MSG_BUNDLER)
                                msg_set_type(msg, CLOSED_MSG);
                        l_ptr->next_out = buf->next;
@@ -1256,33 +1259,24 @@ void tipc_link_push_queue(struct tipc_link *l_ptr)
        } while (!res);
 }
 
-static void link_reset_all(unsigned long addr)
+void tipc_link_reset_all(struct tipc_node *node)
 {
-       struct tipc_node *n_ptr;
        char addr_string[16];
        u32 i;
 
-       read_lock_bh(&tipc_net_lock);
-       n_ptr = tipc_node_find((u32)addr);
-       if (!n_ptr) {
-               read_unlock_bh(&tipc_net_lock);
-               return; /* node no longer exists */
-       }
-
-       tipc_node_lock(n_ptr);
+       tipc_node_lock(node);
 
        pr_warn("Resetting all links to %s\n",
-               tipc_addr_string_fill(addr_string, n_ptr->addr));
+               tipc_addr_string_fill(addr_string, node->addr));
 
        for (i = 0; i < MAX_BEARERS; i++) {
-               if (n_ptr->links[i]) {
-                       link_print(n_ptr->links[i], "Resetting link\n");
-                       tipc_link_reset(n_ptr->links[i]);
+               if (node->links[i]) {
+                       link_print(node->links[i], "Resetting link\n");
+                       tipc_link_reset(node->links[i]);
                }
        }
 
-       tipc_node_unlock(n_ptr);
-       read_unlock_bh(&tipc_net_lock);
+       tipc_node_unlock(node);
 }
 
 static void link_retransmit_failure(struct tipc_link *l_ptr,
@@ -1319,10 +1313,9 @@ static void link_retransmit_failure(struct tipc_link *l_ptr,
                        n_ptr->bclink.oos_state,
                        n_ptr->bclink.last_sent);
 
-               tipc_k_signal((Handler)link_reset_all, (unsigned long)n_ptr->addr);
-
                tipc_node_unlock(n_ptr);
 
+               tipc_bclink_set_flags(TIPC_BCLINK_RESET);
                l_ptr->stale_count = 0;
        }
 }
@@ -1352,7 +1345,7 @@ void tipc_link_retransmit(struct tipc_link *l_ptr, struct sk_buff *buf,
                msg = buf_msg(buf);
                msg_set_ack(msg, mod(l_ptr->next_in_no - 1));
                msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in);
-               tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr);
+               tipc_bearer_send(l_ptr->bearer_id, buf, &l_ptr->media_addr);
                buf = buf->next;
                retransmits--;
                l_ptr->stats.retransmitted++;
@@ -1440,14 +1433,13 @@ static int link_recv_buf_validate(struct sk_buff *buf)
 /**
  * tipc_rcv - process TIPC packets/messages arriving from off-node
  * @head: pointer to message buffer chain
- * @tb_ptr: pointer to bearer message arrived on
+ * @b_ptr: pointer to bearer message arrived on
  *
  * Invoked with no locks held.  Bearer pointer must point to a valid bearer
  * structure (i.e. cannot be NULL), but bearer can be inactive.
  */
 void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr)
 {
-       read_lock_bh(&tipc_net_lock);
        while (head) {
                struct tipc_node *n_ptr;
                struct tipc_link *l_ptr;
@@ -1497,14 +1489,14 @@ void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr)
                        goto unlock_discard;
 
                /* Verify that communication with node is currently allowed */
-               if ((n_ptr->block_setup & WAIT_PEER_DOWN) &&
-                       msg_user(msg) == LINK_PROTOCOL &&
-                       (msg_type(msg) == RESET_MSG ||
-                        msg_type(msg) == ACTIVATE_MSG) &&
-                       !msg_redundant_link(msg))
-                       n_ptr->block_setup &= ~WAIT_PEER_DOWN;
-
-               if (n_ptr->block_setup)
+               if ((n_ptr->action_flags & TIPC_WAIT_PEER_LINKS_DOWN) &&
+                   msg_user(msg) == LINK_PROTOCOL &&
+                   (msg_type(msg) == RESET_MSG ||
+                   msg_type(msg) == ACTIVATE_MSG) &&
+                   !msg_redundant_link(msg))
+                       n_ptr->action_flags &= ~TIPC_WAIT_PEER_LINKS_DOWN;
+
+               if (tipc_node_blocked(n_ptr))
                        goto unlock_discard;
 
                /* Validate message sequence number info */
@@ -1581,17 +1573,12 @@ void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr)
                        }
                        msg = buf_msg(buf);
                } else if (msg_user(msg) == MSG_FRAGMENTER) {
-                       int rc;
-
                        l_ptr->stats.recv_fragments++;
-                       rc = tipc_link_frag_rcv(&l_ptr->reasm_head,
-                                               &l_ptr->reasm_tail,
-                                               &buf);
-                       if (rc == LINK_REASM_COMPLETE) {
+                       if (tipc_buf_append(&l_ptr->reasm_buf, &buf)) {
                                l_ptr->stats.recv_fragmented++;
                                msg = buf_msg(buf);
                        } else {
-                               if (rc == LINK_REASM_ERROR)
+                               if (!l_ptr->reasm_buf)
                                        tipc_link_reset(l_ptr);
                                tipc_node_unlock(n_ptr);
                                continue;
@@ -1604,7 +1591,7 @@ void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr)
                case TIPC_HIGH_IMPORTANCE:
                case TIPC_CRITICAL_IMPORTANCE:
                        tipc_node_unlock(n_ptr);
-                       tipc_port_rcv(buf);
+                       tipc_sk_rcv(buf);
                        continue;
                case MSG_BUNDLER:
                        l_ptr->stats.recv_bundles++;
@@ -1635,7 +1622,6 @@ void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr)
 discard:
                kfree_skb(buf);
        }
-       read_unlock_bh(&tipc_net_lock);
 }
 
 /**
@@ -1747,12 +1733,12 @@ void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int probe_msg,
                return;
 
        /* Abort non-RESET send if communication with node is prohibited */
-       if ((l_ptr->owner->block_setup) && (msg_typ != RESET_MSG))
+       if ((tipc_node_blocked(l_ptr->owner)) && (msg_typ != RESET_MSG))
                return;
 
        /* Create protocol message with "out-of-sequence" sequence number */
        msg_set_type(msg, msg_typ);
-       msg_set_net_plane(msg, l_ptr->b_ptr->net_plane);
+       msg_set_net_plane(msg, l_ptr->net_plane);
        msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in);
        msg_set_last_bcast(msg, tipc_bclink_get_last_sent());
 
@@ -1818,7 +1804,7 @@ void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int probe_msg,
        skb_copy_to_linear_data(buf, msg, sizeof(l_ptr->proto_msg));
        buf->priority = TC_PRIO_CONTROL;
 
-       tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr);
+       tipc_bearer_send(l_ptr->bearer_id, buf, &l_ptr->media_addr);
        l_ptr->unacked_window = 0;
        kfree_skb(buf);
 }
@@ -1840,12 +1826,9 @@ static void tipc_link_proto_rcv(struct tipc_link *l_ptr, struct sk_buff *buf)
        if (l_ptr->exp_msg_count)
                goto exit;
 
-       /* record unnumbered packet arrival (force mismatch on next timeout) */
-       l_ptr->checkpoint--;
-
-       if (l_ptr->b_ptr->net_plane != msg_net_plane(msg))
+       if (l_ptr->net_plane != msg_net_plane(msg))
                if (tipc_own_addr > msg_prevnode(msg))
-                       l_ptr->b_ptr->net_plane = msg_net_plane(msg);
+                       l_ptr->net_plane = msg_net_plane(msg);
 
        switch (msg_type(msg)) {
 
@@ -1862,7 +1845,7 @@ static void tipc_link_proto_rcv(struct tipc_link *l_ptr, struct sk_buff *buf)
                         * peer has lost contact -- don't allow peer's links
                         * to reactivate before we recognize loss & clean up
                         */
-                       l_ptr->owner->block_setup = WAIT_NODE_DOWN;
+                       l_ptr->owner->action_flags |= TIPC_WAIT_OWN_LINKS_DOWN;
                }
 
                link_state_event(l_ptr, RESET_MSG);
@@ -1918,6 +1901,10 @@ static void tipc_link_proto_rcv(struct tipc_link *l_ptr, struct sk_buff *buf)
                        tipc_link_reset(l_ptr); /* Enforce change to take effect */
                        break;
                }
+
+               /* Record reception; force mismatch at next timeout: */
+               l_ptr->checkpoint--;
+
                link_state_event(l_ptr, TRAFFIC_MSG_EVT);
                l_ptr->stats.recv_states++;
                if (link_reset_unknown(l_ptr))
@@ -2177,9 +2164,7 @@ static struct sk_buff *tipc_link_failover_rcv(struct tipc_link *l_ptr,
                }
                if (msg_user(msg) == MSG_FRAGMENTER) {
                        l_ptr->stats.recv_fragments++;
-                       tipc_link_frag_rcv(&l_ptr->reasm_head,
-                                          &l_ptr->reasm_tail,
-                                          &buf);
+                       tipc_buf_append(&l_ptr->reasm_buf, &buf);
                }
        }
 exit:
@@ -2317,53 +2302,6 @@ static int tipc_link_frag_xmit(struct tipc_link *l_ptr, struct sk_buff *buf)
        return dsz;
 }
 
-/* tipc_link_frag_rcv(): Called with node lock on. Returns
- * the reassembled buffer if message is complete.
- */
-int tipc_link_frag_rcv(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);
-               *fbuf = NULL;
-               return 0;
-       } else if (*head &&
-                  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;
-       }
-       *fbuf = NULL;
-       return 0;
-out_free:
-       pr_warn_ratelimited("Link unable to reassemble fragmented message\n");
-       kfree_skb(*fbuf);
-       *fbuf = NULL;
-       return LINK_REASM_ERROR;
-}
-
 static void link_set_supervision_props(struct tipc_link *l_ptr, u32 tolerance)
 {
        if ((tolerance < TIPC_MIN_LINK_TOL) || (tolerance > TIPC_MAX_LINK_TOL))
@@ -2397,8 +2335,6 @@ void tipc_link_set_queue_limits(struct tipc_link *l_ptr, u32 window)
 /* tipc_link_find_owner - locate owner node of link by link's name
  * @name: pointer to link name string
  * @bearer_id: pointer to index in 'node->links' array where the link was found.
- * Caller must hold 'tipc_net_lock' to ensure node and bearer are not deleted;
- * this also prevents link deletion.
  *
  * Returns pointer to node owning the link, or 0 if no matching link is found.
  */
@@ -2460,7 +2396,7 @@ static int link_value_is_valid(u16 cmd, u32 new_value)
  * @new_value: new value of link, bearer, or media setting
  * @cmd: which link, bearer, or media attribute to set (TIPC_CMD_SET_LINK_*)
  *
- * Caller must hold 'tipc_net_lock' to ensure link/bearer/media is not deleted.
+ * Caller must hold RTNL lock to ensure link/bearer/media is not deleted.
  *
  * Returns 0 if value updated and negative value on error.
  */
@@ -2566,9 +2502,7 @@ struct sk_buff *tipc_link_cmd_config(const void *req_tlv_area, int req_tlv_space
                                                   " (cannot change setting on broadcast link)");
        }
 
-       read_lock_bh(&tipc_net_lock);
        res = link_cmd_set_value(args->name, new_value, cmd);
-       read_unlock_bh(&tipc_net_lock);
        if (res)
                return tipc_cfg_reply_error_string("cannot change link setting");
 
@@ -2602,22 +2536,18 @@ struct sk_buff *tipc_link_cmd_reset_stats(const void *req_tlv_area, int req_tlv_
                        return tipc_cfg_reply_error_string("link not found");
                return tipc_cfg_reply_none();
        }
-       read_lock_bh(&tipc_net_lock);
        node = tipc_link_find_owner(link_name, &bearer_id);
-       if (!node) {
-               read_unlock_bh(&tipc_net_lock);
+       if (!node)
                return tipc_cfg_reply_error_string("link not found");
-       }
+
        tipc_node_lock(node);
        l_ptr = node->links[bearer_id];
        if (!l_ptr) {
                tipc_node_unlock(node);
-               read_unlock_bh(&tipc_net_lock);
                return tipc_cfg_reply_error_string("link not found");
        }
        link_reset_statistics(l_ptr);
        tipc_node_unlock(node);
-       read_unlock_bh(&tipc_net_lock);
        return tipc_cfg_reply_none();
 }
 
@@ -2650,18 +2580,15 @@ static int tipc_link_stats(const char *name, char *buf, const u32 buf_size)
        if (!strcmp(name, tipc_bclink_name))
                return tipc_bclink_stats(buf, buf_size);
 
-       read_lock_bh(&tipc_net_lock);
        node = tipc_link_find_owner(name, &bearer_id);
-       if (!node) {
-               read_unlock_bh(&tipc_net_lock);
+       if (!node)
                return 0;
-       }
+
        tipc_node_lock(node);
 
        l = node->links[bearer_id];
        if (!l) {
                tipc_node_unlock(node);
-               read_unlock_bh(&tipc_net_lock);
                return 0;
        }
 
@@ -2727,7 +2654,6 @@ static int tipc_link_stats(const char *name, char *buf, const u32 buf_size)
                             (s->accu_queue_sz / s->queue_sz_counts) : 0);
 
        tipc_node_unlock(node);
-       read_unlock_bh(&tipc_net_lock);
        return ret;
 }
 
@@ -2778,7 +2704,6 @@ u32 tipc_link_get_max_pkt(u32 dest, u32 selector)
        if (dest == tipc_own_addr)
                return MAX_MSG_SIZE;
 
-       read_lock_bh(&tipc_net_lock);
        n_ptr = tipc_node_find(dest);
        if (n_ptr) {
                tipc_node_lock(n_ptr);
@@ -2787,13 +2712,18 @@ u32 tipc_link_get_max_pkt(u32 dest, u32 selector)
                        res = l_ptr->max_pkt;
                tipc_node_unlock(n_ptr);
        }
-       read_unlock_bh(&tipc_net_lock);
        return res;
 }
 
 static void link_print(struct tipc_link *l_ptr, const char *str)
 {
-       pr_info("%s Link %x<%s>:", str, l_ptr->addr, l_ptr->b_ptr->name);
+       struct tipc_bearer *b_ptr;
+
+       rcu_read_lock();
+       b_ptr = rcu_dereference_rtnl(bearer_list[l_ptr->bearer_id]);
+       if (b_ptr)
+               pr_info("%s Link %x<%s>:", str, l_ptr->addr, b_ptr->name);
+       rcu_read_unlock();
 
        if (link_working_unknown(l_ptr))
                pr_cont(":WU\n");
index 8c0b49b5b2ee6b0751f248cf740254a33e424c6a..200d518b218ede4e0ad7c210c863c546f2362947 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
  */
 #define INVALID_LINK_SEQ 0x10000
@@ -107,7 +102,7 @@ struct tipc_stats {
  * @checkpoint: reference point for triggering link continuity checking
  * @peer_session: link session # being used by peer end of link
  * @peer_bearer_id: bearer id used by link's peer endpoint
- * @b_ptr: pointer to bearer used by link
+ * @bearer_id: local bearer id used by link
  * @tolerance: minimum link continuity loss needed to reset link [in ms]
  * @continuity_interval: link continuity testing interval [in ms]
  * @abort_limit: # of unacknowledged continuity probes needed to reset link
@@ -116,6 +111,7 @@ struct tipc_stats {
  * @proto_msg: template for control messages generated by link
  * @pmsg: convenience pointer to "proto_msg" field
  * @priority: current link priority
+ * @net_plane: current link network plane ('A' through 'H')
  * @queue_limit: outbound message queue congestion thresholds (indexed by user)
  * @exp_msg_count: # of tunnelled messages expected during link changeover
  * @reset_checkpoint: seq # of last acknowledged message at time of link reset
@@ -139,8 +135,7 @@ 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
- * @reasm_head: list head of partially reassembled inbound message fragments
- * @reasm_tail: last fragment received
+ * @reasm_buf: head of partially reassembled inbound message fragments
  * @stats: collects statistics regarding link activity
  */
 struct tipc_link {
@@ -155,7 +150,7 @@ struct tipc_link {
        u32 checkpoint;
        u32 peer_session;
        u32 peer_bearer_id;
-       struct tipc_bearer *b_ptr;
+       u32 bearer_id;
        u32 tolerance;
        u32 continuity_interval;
        u32 abort_limit;
@@ -167,6 +162,7 @@ struct tipc_link {
        } proto_msg;
        struct tipc_msg *pmsg;
        u32 priority;
+       char net_plane;
        u32 queue_limit[15];    /* queue_limit[0]==window limit */
 
        /* Changeover */
@@ -202,8 +198,7 @@ struct tipc_link {
 
        /* Fragmentation/reassembly */
        u32 long_msg_seq_no;
-       struct sk_buff *reasm_head;
-       struct sk_buff *reasm_tail;
+       struct sk_buff *reasm_buf;
 
        /* Statistics */
        struct tipc_stats stats;
@@ -228,6 +223,7 @@ struct sk_buff *tipc_link_cmd_show_stats(const void *req_tlv_area,
                                         int req_tlv_space);
 struct sk_buff *tipc_link_cmd_reset_stats(const void *req_tlv_area,
                                          int req_tlv_space);
+void tipc_link_reset_all(struct tipc_node *node);
 void tipc_link_reset(struct tipc_link *l_ptr);
 void tipc_link_reset_list(unsigned int bearer_id);
 int tipc_link_xmit(struct sk_buff *buf, u32 dest, u32 selector);
@@ -239,9 +235,6 @@ int tipc_link_iovec_xmit_fast(struct tipc_port *sender,
                              struct iovec const *msg_sect,
                              unsigned int len, u32 destnode);
 void tipc_link_bundle_rcv(struct sk_buff *buf);
-int tipc_link_frag_rcv(struct sk_buff **reasm_head,
-                      struct sk_buff **reasm_tail,
-                      struct sk_buff **fbuf);
 void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int prob,
                          u32 gap, u32 tolerance, u32 priority, u32 acked_mtu);
 void tipc_link_push_queue(struct tipc_link *l_ptr);
index e525f8ce1dee09ce0d9baf214bd3dd8e13daa9d8..8be6e94a1ca9790dbbde757b6bd70fe9c5abb428 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * net/tipc/msg.c: TIPC message header routines
  *
- * Copyright (c) 2000-2006, Ericsson AB
+ * Copyright (c) 2000-2006, 2014, Ericsson AB
  * Copyright (c) 2005, 2010-2011, Wind River Systems
  * All rights reserved.
  *
@@ -99,3 +99,56 @@ int tipc_msg_build(struct tipc_msg *hdr, struct iovec const *msg_sect,
        }
        return dsz;
 }
+
+/* tipc_buf_append(): Append a buffer to the fragment list of another buffer
+ * Let first buffer become head buffer
+ * Returns 1 and sets *buf to headbuf if chain is complete, otherwise 0
+ * Leaves headbuf pointer at NULL if failure
+ */
+int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf)
+{
+       struct sk_buff *head = *headbuf;
+       struct sk_buff *frag = *buf;
+       struct sk_buff *tail;
+       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 = *headbuf = frag;
+               skb_frag_list_init(head);
+               return 0;
+       }
+       if (!head)
+               goto out_free;
+       tail = TIPC_SKB_CB(head)->tail;
+       if (skb_try_coalesce(head, frag, &headstolen, &delta)) {
+               kfree_skb_partial(frag, headstolen);
+       } else {
+               if (!skb_has_frag_list(head))
+                       skb_shinfo(head)->frag_list = frag;
+               else
+                       tail->next = frag;
+               head->truesize += frag->truesize;
+               head->data_len += frag->len;
+               head->len += frag->len;
+               TIPC_SKB_CB(head)->tail = frag;
+       }
+       if (fragid == LAST_FRAGMENT) {
+               *buf = head;
+               TIPC_SKB_CB(head)->tail = NULL;
+               *headbuf = NULL;
+               return 1;
+       }
+       *buf = NULL;
+       return 0;
+out_free:
+       pr_warn_ratelimited("Unable to build fragment list\n");
+       kfree_skb(*buf);
+       return 0;
+}
index 76d1269b944361076855876219d6e59b16c3b23e..503511903d1d25c9c4106f669da0a7def7c9dd46 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * net/tipc/msg.h: Include file for TIPC message header routines
  *
- * Copyright (c) 2000-2007, Ericsson AB
+ * Copyright (c) 2000-2007, 2014, Ericsson AB
  * Copyright (c) 2005-2008, 2010-2011, Wind River Systems
  * All rights reserved.
  *
@@ -711,4 +711,7 @@ 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,
                   unsigned int len, int max_size, struct sk_buff **buf);
+
+int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf);
+
 #endif
index aff8041dc1573e3fea829e286e2675e322350b2e..8ce730984aa1f0d429b2325d58862e1ef7799d4d 100644 (file)
 #include "link.h"
 #include "name_distr.h"
 
-#define ITEM_SIZE sizeof(struct distr_item)
-
-/**
- * struct distr_item - publication info distributed to other nodes
- * @type: name sequence type
- * @lower: name sequence lower bound
- * @upper: name sequence upper bound
- * @ref: publishing port reference
- * @key: publication key
- *
- * ===> All fields are stored in network byte order. <===
- *
- * First 3 fields identify (name or) name sequence being published.
- * Reference field uniquely identifies port that published name sequence.
- * Key field uniquely identifies publication, in the event a port has
- * multiple publications of the same name sequence.
- *
- * Note: There is no field that identifies the publishing node because it is
- * the same for all items contained within a publication message.
- */
-struct distr_item {
-       __be32 type;
-       __be32 lower;
-       __be32 upper;
-       __be32 ref;
-       __be32 key;
-};
-
 /**
  * struct publ_list - list of publications made by this node
  * @list: circular list of publications
@@ -127,7 +99,7 @@ static struct sk_buff *named_prepare_buf(u32 type, u32 size, u32 dest)
        return buf;
 }
 
-static void named_cluster_distribute(struct sk_buff *buf)
+void named_cluster_distribute(struct sk_buff *buf)
 {
        struct sk_buff *buf_copy;
        struct tipc_node *n_ptr;
@@ -135,18 +107,18 @@ static void named_cluster_distribute(struct sk_buff *buf)
 
        rcu_read_lock();
        list_for_each_entry_rcu(n_ptr, &tipc_node_list, list) {
-               spin_lock_bh(&n_ptr->lock);
+               tipc_node_lock(n_ptr);
                l_ptr = n_ptr->active_links[n_ptr->addr & 1];
                if (l_ptr) {
                        buf_copy = skb_copy(buf, GFP_ATOMIC);
                        if (!buf_copy) {
-                               spin_unlock_bh(&n_ptr->lock);
+                               tipc_node_unlock(n_ptr);
                                break;
                        }
                        msg_set_destnode(buf_msg(buf_copy), n_ptr->addr);
                        __tipc_link_xmit(l_ptr, buf_copy);
                }
-               spin_unlock_bh(&n_ptr->lock);
+               tipc_node_unlock(n_ptr);
        }
        rcu_read_unlock();
 
@@ -156,7 +128,7 @@ static void named_cluster_distribute(struct sk_buff *buf)
 /**
  * tipc_named_publish - tell other nodes about a new publication by this node
  */
-void tipc_named_publish(struct publication *publ)
+struct sk_buff *tipc_named_publish(struct publication *publ)
 {
        struct sk_buff *buf;
        struct distr_item *item;
@@ -165,23 +137,23 @@ void tipc_named_publish(struct publication *publ)
        publ_lists[publ->scope]->size++;
 
        if (publ->scope == TIPC_NODE_SCOPE)
-               return;
+               return NULL;
 
        buf = named_prepare_buf(PUBLICATION, ITEM_SIZE, 0);
        if (!buf) {
                pr_warn("Publication distribution failure\n");
-               return;
+               return NULL;
        }
 
        item = (struct distr_item *)msg_data(buf_msg(buf));
        publ_to_item(item, publ);
-       named_cluster_distribute(buf);
+       return buf;
 }
 
 /**
  * tipc_named_withdraw - tell other nodes about a withdrawn publication by this node
  */
-void tipc_named_withdraw(struct publication *publ)
+struct sk_buff *tipc_named_withdraw(struct publication *publ)
 {
        struct sk_buff *buf;
        struct distr_item *item;
@@ -190,17 +162,17 @@ void tipc_named_withdraw(struct publication *publ)
        publ_lists[publ->scope]->size--;
 
        if (publ->scope == TIPC_NODE_SCOPE)
-               return;
+               return NULL;
 
        buf = named_prepare_buf(WITHDRAWAL, ITEM_SIZE, 0);
        if (!buf) {
                pr_warn("Withdrawal distribution failure\n");
-               return;
+               return NULL;
        }
 
        item = (struct distr_item *)msg_data(buf_msg(buf));
        publ_to_item(item, publ);
-       named_cluster_distribute(buf);
+       return buf;
 }
 
 /*
@@ -239,31 +211,9 @@ static void named_distribute(struct list_head *message_list, u32 node,
 /**
  * tipc_named_node_up - tell specified node about all publications by this node
  */
-void tipc_named_node_up(unsigned long nodearg)
+void tipc_named_node_up(u32 max_item_buf, u32 node)
 {
-       struct tipc_node *n_ptr;
-       struct tipc_link *l_ptr;
-       struct list_head message_list;
-       u32 node = (u32)nodearg;
-       u32 max_item_buf = 0;
-
-       /* compute maximum amount of publication data to send per message */
-       read_lock_bh(&tipc_net_lock);
-       n_ptr = tipc_node_find(node);
-       if (n_ptr) {
-               tipc_node_lock(n_ptr);
-               l_ptr = n_ptr->active_links[0];
-               if (l_ptr)
-                       max_item_buf = ((l_ptr->max_pkt - INT_H_SIZE) /
-                               ITEM_SIZE) * ITEM_SIZE;
-               tipc_node_unlock(n_ptr);
-       }
-       read_unlock_bh(&tipc_net_lock);
-       if (!max_item_buf)
-               return;
-
-       /* create list of publication messages, then send them as a unit */
-       INIT_LIST_HEAD(&message_list);
+       LIST_HEAD(message_list);
 
        read_lock_bh(&tipc_nametbl_lock);
        named_distribute(&message_list, node, &publ_cluster, max_item_buf);
index 9b312ccfd43e7da41bcab4ca33d5f0f4d5be86cf..b2eed4ec1526a34efd8b191e4b952bf9718f7a2e 100644 (file)
 
 #include "name_table.h"
 
-void tipc_named_publish(struct publication *publ);
-void tipc_named_withdraw(struct publication *publ);
-void tipc_named_node_up(unsigned long node);
+#define ITEM_SIZE sizeof(struct distr_item)
+
+/**
+ * struct distr_item - publication info distributed to other nodes
+ * @type: name sequence type
+ * @lower: name sequence lower bound
+ * @upper: name sequence upper bound
+ * @ref: publishing port reference
+ * @key: publication key
+ *
+ * ===> All fields are stored in network byte order. <===
+ *
+ * First 3 fields identify (name or) name sequence being published.
+ * Reference field uniquely identifies port that published name sequence.
+ * Key field uniquely identifies publication, in the event a port has
+ * multiple publications of the same name sequence.
+ *
+ * Note: There is no field that identifies the publishing node because it is
+ * the same for all items contained within a publication message.
+ */
+struct distr_item {
+       __be32 type;
+       __be32 lower;
+       __be32 upper;
+       __be32 ref;
+       __be32 key;
+};
+
+struct sk_buff *tipc_named_publish(struct publication *publ);
+struct sk_buff *tipc_named_withdraw(struct publication *publ);
+void named_cluster_distribute(struct sk_buff *buf);
+void tipc_named_node_up(u32 max_item_buf, u32 node);
 void tipc_named_rcv(struct sk_buff *buf);
 void tipc_named_reinit(void);
 
index 042e8e3cabc09f84aa5dce626c57a30faf3ca32d..9d7d37d95187c77d9d7490ce7aec4de147a9f2fd 100644 (file)
@@ -664,6 +664,7 @@ struct publication *tipc_nametbl_publish(u32 type, u32 lower, u32 upper,
                                         u32 scope, u32 port_ref, u32 key)
 {
        struct publication *publ;
+       struct sk_buff *buf = NULL;
 
        if (table.local_publ_count >= TIPC_MAX_PUBLICATIONS) {
                pr_warn("Publication failed, local publication limit reached (%u)\n",
@@ -676,9 +677,12 @@ struct publication *tipc_nametbl_publish(u32 type, u32 lower, u32 upper,
                                   tipc_own_addr, port_ref, key);
        if (likely(publ)) {
                table.local_publ_count++;
-               tipc_named_publish(publ);
+               buf = tipc_named_publish(publ);
        }
        write_unlock_bh(&tipc_nametbl_lock);
+
+       if (buf)
+               named_cluster_distribute(buf);
        return publ;
 }
 
@@ -688,15 +692,19 @@ struct publication *tipc_nametbl_publish(u32 type, u32 lower, u32 upper,
 int tipc_nametbl_withdraw(u32 type, u32 lower, u32 ref, u32 key)
 {
        struct publication *publ;
+       struct sk_buff *buf;
 
        write_lock_bh(&tipc_nametbl_lock);
        publ = tipc_nametbl_remove_publ(type, lower, tipc_own_addr, ref, key);
        if (likely(publ)) {
                table.local_publ_count--;
-               tipc_named_withdraw(publ);
+               buf = tipc_named_withdraw(publ);
                write_unlock_bh(&tipc_nametbl_lock);
                list_del_init(&publ->pport_list);
                kfree(publ);
+
+               if (buf)
+                       named_cluster_distribute(buf);
                return 1;
        }
        write_unlock_bh(&tipc_nametbl_lock);
@@ -961,6 +969,7 @@ static void tipc_purge_publications(struct name_seq *seq)
        list_for_each_entry_safe(publ, safe, &info->zone_list, zone_list) {
                tipc_nametbl_remove_publ(publ->type, publ->lower, publ->node,
                                         publ->ref, publ->key);
+               kfree(publ);
        }
 }
 
@@ -982,7 +991,6 @@ void tipc_nametbl_stop(void)
                hlist_for_each_entry_safe(seq, safe, seq_head, ns_list) {
                        tipc_purge_publications(seq);
                }
-               continue;
        }
        kfree(table.types);
        table.types = NULL;
index 4c564eb69e1ad9bd2695e78eb91464a0112e663c..f64375e7f99fa4081ce2a30629071ca3e546aa05 100644 (file)
 #include "name_distr.h"
 #include "subscr.h"
 #include "port.h"
+#include "socket.h"
 #include "node.h"
 #include "config.h"
 
 /*
  * The TIPC locking policy is designed to ensure a very fine locking
  * granularity, permitting complete parallel access to individual
- * port and node/link instances. The code consists of three major
+ * port and node/link instances. The code consists of four major
  * locking domains, each protected with their own disjunct set of locks.
  *
- * 1: The routing hierarchy.
- *    Comprises the structures 'zone', 'cluster', 'node', 'link'
- *    and 'bearer'. The whole hierarchy is protected by a big
- *    read/write lock, tipc_net_lock, to enssure that nothing is added
- *    or removed while code is accessing any of these structures.
- *    This layer must not be called from the two others while they
- *    hold any of their own locks.
- *    Neither must it itself do any upcalls to the other two before
- *    it has released tipc_net_lock and other protective locks.
+ * 1: The bearer level.
+ *    RTNL lock is used to serialize the process of configuring bearer
+ *    on update side, and RCU lock is applied on read side to make
+ *    bearer instance valid on both paths of message transmission and
+ *    reception.
  *
- *   Within the tipc_net_lock domain there are two sub-domains;'node' and
- *   'bearer', where local write operations are permitted,
- *   provided that those are protected by individual spin_locks
- *   per instance. Code holding tipc_net_lock(read) and a node spin_lock
- *   is permitted to poke around in both the node itself and its
- *   subordinate links. I.e, it can update link counters and queues,
- *   change link state, send protocol messages, and alter the
- *   "active_links" array in the node; but it can _not_ remove a link
- *   or a node from the overall structure.
- *   Correspondingly, individual bearers may change status within a
- *   tipc_net_lock(read), protected by an individual spin_lock ber bearer
- *   instance, but it needs tipc_net_lock(write) to remove/add any bearers.
+ * 2: The node and link level.
+ *    All node instances are saved into two tipc_node_list and node_htable
+ *    lists. The two lists are protected by node_list_lock on write side,
+ *    and they are guarded with RCU lock on read side. Especially node
+ *    instance is destroyed only when TIPC module is removed, and we can
+ *    confirm that there has no any user who is accessing the node at the
+ *    moment. Therefore, Except for iterating the two lists within RCU
+ *    protection, it's no needed to hold RCU that we access node instance
+ *    in other places.
  *
+ *    In addition, all members in node structure including link instances
+ *    are protected by node spin lock.
  *
- *  2: The transport level of the protocol.
- *     This consists of the structures port, (and its user level
- *     representations, such as user_port and tipc_sock), reference and
- *     tipc_user (port.c, reg.c, socket.c).
+ * 3: The transport level of the protocol.
+ *    This consists of the structures port, (and its user level
+ *    representations, such as user_port and tipc_sock), reference and
+ *    tipc_user (port.c, reg.c, socket.c).
  *
- *     This layer has four different locks:
+ *    This layer has four different locks:
  *     - The tipc_port spin_lock. This is protecting each port instance
  *       from parallel data access and removal. Since we can not place
  *       this lock in the port itself, it has been placed in the
@@ -96,7 +92,7 @@
  *       There are two such lists; 'port_list', which is used for management,
  *       and 'wait_list', which is used to queue ports during congestion.
  *
- *  3: The name table (name_table.c, name_distr.c, subscription.c)
+ *  4: The name table (name_table.c, name_distr.c, subscription.c)
  *     - There is one big read/write-lock (tipc_nametbl_lock) protecting the
  *       overall name table structure. Nothing must be added/removed to
  *       this structure without holding write access to it.
  *     - A local spin_lock protecting the queue of subscriber events.
 */
 
-DEFINE_RWLOCK(tipc_net_lock);
-
 static void net_route_named_msg(struct sk_buff *buf)
 {
        struct tipc_msg *msg = buf_msg(buf);
@@ -148,7 +142,7 @@ void tipc_net_route_msg(struct sk_buff *buf)
                        if (msg_mcast(msg))
                                tipc_port_mcast_rcv(buf, NULL);
                        else if (msg_destport(msg))
-                               tipc_port_rcv(buf);
+                               tipc_sk_rcv(buf);
                        else
                                net_route_named_msg(buf);
                        return;
@@ -171,22 +165,25 @@ void tipc_net_route_msg(struct sk_buff *buf)
        tipc_link_xmit(buf, dnode, msg_link_selector(msg));
 }
 
-void tipc_net_start(u32 addr)
+int tipc_net_start(u32 addr)
 {
        char addr_string[16];
+       int res;
 
-       write_lock_bh(&tipc_net_lock);
        tipc_own_addr = addr;
        tipc_named_reinit();
        tipc_port_reinit();
-       tipc_bclink_init();
-       write_unlock_bh(&tipc_net_lock);
+       res = tipc_bclink_init();
+       if (res)
+               return res;
 
        tipc_nametbl_publish(TIPC_CFG_SRV, tipc_own_addr, tipc_own_addr,
                             TIPC_ZONE_SCOPE, 0, tipc_own_addr);
+
        pr_info("Started in network mode\n");
        pr_info("Own node address %s, network identity %u\n",
                tipc_addr_string_fill(addr_string, tipc_own_addr), tipc_net_id);
+       return 0;
 }
 
 void tipc_net_stop(void)
@@ -195,11 +192,11 @@ void tipc_net_stop(void)
                return;
 
        tipc_nametbl_withdraw(TIPC_CFG_SRV, tipc_own_addr, 0, tipc_own_addr);
-       write_lock_bh(&tipc_net_lock);
+       rtnl_lock();
        tipc_bearer_stop();
        tipc_bclink_stop();
        tipc_node_stop();
-       write_unlock_bh(&tipc_net_lock);
+       rtnl_unlock();
 
        pr_info("Left network mode\n");
 }
index 079daadb3f7286471cd5146798f6b06328bf99ad..c6c2b46f7c283095c4e29c7e0b11c5cdea2bcc01 100644 (file)
 #ifndef _TIPC_NET_H
 #define _TIPC_NET_H
 
-extern rwlock_t tipc_net_lock;
-
 void tipc_net_route_msg(struct sk_buff *buf);
 
-void tipc_net_start(u32 addr);
+int tipc_net_start(u32 addr);
 void tipc_net_stop(void);
 
 #endif
index 1d3a4999a70ff96a751f3908d0d8e274af63a962..5b44c3041be431955094de87f1815122deeea369 100644 (file)
@@ -108,7 +108,7 @@ struct tipc_node *tipc_node_create(u32 addr)
                        break;
        }
        list_add_tail_rcu(&n_ptr->list, &temp_node->list);
-       n_ptr->block_setup = WAIT_PEER_DOWN;
+       n_ptr->action_flags = TIPC_WAIT_PEER_LINKS_DOWN;
        n_ptr->signature = INVALID_NODE_SIG;
 
        tipc_num_nodes++;
@@ -144,11 +144,13 @@ void tipc_node_stop(void)
 void tipc_node_link_up(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
 {
        struct tipc_link **active = &n_ptr->active_links[0];
+       u32 addr = n_ptr->addr;
 
        n_ptr->working_links++;
-
+       tipc_nametbl_publish(TIPC_LINK_STATE, addr, addr, TIPC_NODE_SCOPE,
+                            l_ptr->bearer_id, addr);
        pr_info("Established link <%s> on network plane %c\n",
-               l_ptr->name, l_ptr->b_ptr->net_plane);
+               l_ptr->name, l_ptr->net_plane);
 
        if (!active[0]) {
                active[0] = active[1] = l_ptr;
@@ -203,16 +205,18 @@ static void node_select_active_links(struct tipc_node *n_ptr)
 void tipc_node_link_down(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
 {
        struct tipc_link **active;
+       u32 addr = n_ptr->addr;
 
        n_ptr->working_links--;
+       tipc_nametbl_withdraw(TIPC_LINK_STATE, addr, l_ptr->bearer_id, addr);
 
        if (!tipc_link_is_active(l_ptr)) {
                pr_info("Lost standby link <%s> on network plane %c\n",
-                       l_ptr->name, l_ptr->b_ptr->net_plane);
+                       l_ptr->name, l_ptr->net_plane);
                return;
        }
        pr_info("Lost link <%s> on network plane %c\n",
-               l_ptr->name, l_ptr->b_ptr->net_plane);
+               l_ptr->name, l_ptr->net_plane);
 
        active = &n_ptr->active_links[0];
        if (active[0] == l_ptr)
@@ -239,7 +243,7 @@ int tipc_node_is_up(struct tipc_node *n_ptr)
 
 void tipc_node_attach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
 {
-       n_ptr->links[l_ptr->b_ptr->identity] = l_ptr;
+       n_ptr->links[l_ptr->bearer_id] = l_ptr;
        spin_lock_bh(&node_list_lock);
        tipc_num_links++;
        spin_unlock_bh(&node_list_lock);
@@ -263,26 +267,12 @@ void tipc_node_detach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
 
 static void node_established_contact(struct tipc_node *n_ptr)
 {
-       tipc_k_signal((Handler)tipc_named_node_up, n_ptr->addr);
+       n_ptr->action_flags |= TIPC_NOTIFY_NODE_UP;
        n_ptr->bclink.oos_state = 0;
        n_ptr->bclink.acked = tipc_bclink_get_last_sent();
        tipc_bclink_add_node(n_ptr->addr);
 }
 
-static void node_name_purge_complete(unsigned long node_addr)
-{
-       struct tipc_node *n_ptr;
-
-       read_lock_bh(&tipc_net_lock);
-       n_ptr = tipc_node_find(node_addr);
-       if (n_ptr) {
-               tipc_node_lock(n_ptr);
-               n_ptr->block_setup &= ~WAIT_NAMES_GONE;
-               tipc_node_unlock(n_ptr);
-       }
-       read_unlock_bh(&tipc_net_lock);
-}
-
 static void node_lost_contact(struct tipc_node *n_ptr)
 {
        char addr_string[16];
@@ -296,10 +286,9 @@ static void node_lost_contact(struct tipc_node *n_ptr)
                kfree_skb_list(n_ptr->bclink.deferred_head);
                n_ptr->bclink.deferred_size = 0;
 
-               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;
+               if (n_ptr->bclink.reasm_buf) {
+                       kfree_skb(n_ptr->bclink.reasm_buf);
+                       n_ptr->bclink.reasm_buf = NULL;
                }
 
                tipc_bclink_remove_node(n_ptr->addr);
@@ -318,12 +307,13 @@ static void node_lost_contact(struct tipc_node *n_ptr)
                tipc_link_reset_fragments(l_ptr);
        }
 
-       /* Notify subscribers */
-       tipc_nodesub_notify(n_ptr);
+       n_ptr->action_flags &= ~TIPC_WAIT_OWN_LINKS_DOWN;
 
-       /* Prevent re-contact with node until cleanup is done */
-       n_ptr->block_setup = WAIT_PEER_DOWN | WAIT_NAMES_GONE;
-       tipc_k_signal((Handler)node_name_purge_complete, n_ptr->addr);
+       /* Notify subscribers and prevent re-contact with node until
+        * cleanup is done.
+        */
+       n_ptr->action_flags |= TIPC_WAIT_PEER_LINKS_DOWN |
+                              TIPC_NOTIFY_NODE_DOWN;
 }
 
 struct sk_buff *tipc_node_get_nodes(const void *req_tlv_area, int req_tlv_space)
@@ -436,3 +426,63 @@ struct sk_buff *tipc_node_get_links(const void *req_tlv_area, int req_tlv_space)
        rcu_read_unlock();
        return buf;
 }
+
+/**
+ * tipc_node_get_linkname - get the name of a link
+ *
+ * @bearer_id: id of the bearer
+ * @node: peer node address
+ * @linkname: link name output buffer
+ *
+ * Returns 0 on success
+ */
+int tipc_node_get_linkname(u32 bearer_id, u32 addr, char *linkname, size_t len)
+{
+       struct tipc_link *link;
+       struct tipc_node *node = tipc_node_find(addr);
+
+       if ((bearer_id >= MAX_BEARERS) || !node)
+               return -EINVAL;
+       tipc_node_lock(node);
+       link = node->links[bearer_id];
+       if (link) {
+               strncpy(linkname, link->name, len);
+               tipc_node_unlock(node);
+               return 0;
+       }
+       tipc_node_unlock(node);
+       return -EINVAL;
+}
+
+void tipc_node_unlock(struct tipc_node *node)
+{
+       LIST_HEAD(nsub_list);
+       struct tipc_link *link;
+       int pkt_sz = 0;
+       u32 addr = 0;
+
+       if (likely(!node->action_flags)) {
+               spin_unlock_bh(&node->lock);
+               return;
+       }
+
+       if (node->action_flags & TIPC_NOTIFY_NODE_DOWN) {
+               list_replace_init(&node->nsub, &nsub_list);
+               node->action_flags &= ~TIPC_NOTIFY_NODE_DOWN;
+       }
+       if (node->action_flags & TIPC_NOTIFY_NODE_UP) {
+               link = node->active_links[0];
+               node->action_flags &= ~TIPC_NOTIFY_NODE_UP;
+               if (link) {
+                       pkt_sz = ((link->max_pkt - INT_H_SIZE) / ITEM_SIZE) *
+                                 ITEM_SIZE;
+                       addr = node->addr;
+               }
+       }
+       spin_unlock_bh(&node->lock);
+
+       if (!list_empty(&nsub_list))
+               tipc_nodesub_notify(&nsub_list);
+       if (pkt_sz)
+               tipc_named_node_up(pkt_sz, addr);
+}
index 7cbb8cec1a932f881cd636a71edb0342070530ae..9087063793f26eb352f9b848a14b27545c8b02be 100644 (file)
  */
 #define INVALID_NODE_SIG 0x10000
 
-/* Flags used to block (re)establishment of contact with a neighboring node */
-#define WAIT_PEER_DOWN 0x0001  /* wait to see that peer's links are down */
-#define WAIT_NAMES_GONE        0x0002  /* wait for peer's publications to be purged */
-#define WAIT_NODE_DOWN 0x0004  /* wait until peer node is declared down */
+/* Flags used to take different actions according to flag type
+ * TIPC_WAIT_PEER_LINKS_DOWN: wait to see that peer's links are down
+ * TIPC_WAIT_OWN_LINKS_DOWN: wait until peer node is declared down
+ * TIPC_NOTIFY_NODE_DOWN: notify node is down
+ * TIPC_NOTIFY_NODE_UP: notify node is up
+ */
+enum {
+       TIPC_WAIT_PEER_LINKS_DOWN       = (1 << 1),
+       TIPC_WAIT_OWN_LINKS_DOWN        = (1 << 2),
+       TIPC_NOTIFY_NODE_DOWN           = (1 << 3),
+       TIPC_NOTIFY_NODE_UP             = (1 << 4)
+};
+
+/**
+ * struct tipc_node_bclink - TIPC node bclink structure
+ * @acked: sequence # of last outbound b'cast message acknowledged by node
+ * @last_in: sequence # of last in-sequence b'cast message received from node
+ * @last_sent: sequence # of last b'cast message sent by node
+ * @oos_state: state tracker for handling OOS b'cast messages
+ * @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
+ * @reasm_buf: broadcast reassembly queue head from node
+ * @recv_permitted: true if node is allowed to receive b'cast messages
+ */
+struct tipc_node_bclink {
+       u32 acked;
+       u32 last_in;
+       u32 last_sent;
+       u32 oos_state;
+       u32 deferred_size;
+       struct sk_buff *deferred_head;
+       struct sk_buff *deferred_tail;
+       struct sk_buff *reasm_buf;
+       bool recv_permitted;
+};
 
 /**
  * struct tipc_node - TIPC node structure
  * @addr: network address of node
  * @lock: spinlock governing access to structure
  * @hash: links to adjacent nodes in unsorted hash chain
- * @list: links to adjacent nodes in sorted list of cluster's nodes
- * @nsub: list of "node down" subscriptions monitoring node
  * @active_links: pointers to active links to node
  * @links: pointers to all links to node
+ * @action_flags: bit mask of different types of node actions
+ * @bclink: broadcast-related info
+ * @list: links to adjacent nodes in sorted list of cluster's nodes
  * @working_links: number of working links to node (both active and standby)
- * @block_setup: bit mask of conditions preventing link establishment to node
  * @link_cnt: number of links to node
  * @signature: node instance identifier
- * @bclink: broadcast-related info
+ * @nsub: list of "node down" subscriptions monitoring node
  * @rcu: rcu struct for tipc_node
- *    @acked: sequence # of last outbound b'cast message acknowledged by node
- *    @last_in: sequence # of last in-sequence b'cast message received from node
- *    @last_sent: sequence # of last b'cast message sent by node
- *    @oos_state: state tracker for handling OOS b'cast messages
- *    @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
- *    @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 {
        u32 addr;
        spinlock_t lock;
        struct hlist_node hash;
-       struct list_head list;
-       struct list_head nsub;
        struct tipc_link *active_links[2];
        struct tipc_link *links[MAX_BEARERS];
+       unsigned int action_flags;
+       struct tipc_node_bclink bclink;
+       struct list_head list;
        int link_cnt;
        int working_links;
-       int block_setup;
        u32 signature;
+       struct list_head nsub;
        struct rcu_head rcu;
-       struct {
-               u32 acked;
-               u32 last_in;
-               u32 last_sent;
-               u32 oos_state;
-               u32 deferred_size;
-               struct sk_buff *deferred_head;
-               struct sk_buff *deferred_tail;
-               struct sk_buff *reasm_head;
-               struct sk_buff *reasm_tail;
-               bool recv_permitted;
-       } bclink;
 };
 
 extern struct list_head tipc_node_list;
@@ -118,15 +129,18 @@ int tipc_node_active_links(struct tipc_node *n_ptr);
 int tipc_node_is_up(struct tipc_node *n_ptr);
 struct sk_buff *tipc_node_get_links(const void *req_tlv_area, int req_tlv_space);
 struct sk_buff *tipc_node_get_nodes(const void *req_tlv_area, int req_tlv_space);
+int tipc_node_get_linkname(u32 bearer_id, u32 node, char *linkname, size_t len);
+void tipc_node_unlock(struct tipc_node *node);
 
-static inline void tipc_node_lock(struct tipc_node *n_ptr)
+static inline void tipc_node_lock(struct tipc_node *node)
 {
-       spin_lock_bh(&n_ptr->lock);
+       spin_lock_bh(&node->lock);
 }
 
-static inline void tipc_node_unlock(struct tipc_node *n_ptr)
+static inline bool tipc_node_blocked(struct tipc_node *node)
 {
-       spin_unlock_bh(&n_ptr->lock);
+       return (node->action_flags & (TIPC_WAIT_PEER_LINKS_DOWN |
+               TIPC_NOTIFY_NODE_DOWN | TIPC_WAIT_OWN_LINKS_DOWN));
 }
 
 #endif
index 8a7384c04add4bdc6db6ae4451ebb2232e4b338b..7c59ab1d6ecb3dc26c4efb77cd243a00341c7b5b 100644 (file)
@@ -81,14 +81,13 @@ void tipc_nodesub_unsubscribe(struct tipc_node_subscr *node_sub)
  *
  * Note: node is locked by caller
  */
-void tipc_nodesub_notify(struct tipc_node *node)
+void tipc_nodesub_notify(struct list_head *nsub_list)
 {
-       struct tipc_node_subscr *ns;
+       struct tipc_node_subscr *ns, *safe;
 
-       list_for_each_entry(ns, &node->nsub, nodesub_list) {
+       list_for_each_entry_safe(ns, safe, nsub_list, nodesub_list) {
                if (ns->handle_node_down) {
-                       tipc_k_signal((Handler)ns->handle_node_down,
-                                     (unsigned long)ns->usr_handle);
+                       ns->handle_node_down(ns->usr_handle);
                        ns->handle_node_down = NULL;
                }
        }
index c95d20727ded3ea70cf9532a82cd8ff20fde415d..d91b8cc81e3d786948bd4ed9ac622689c4218ea1 100644 (file)
@@ -58,6 +58,6 @@ struct tipc_node_subscr {
 void tipc_nodesub_subscribe(struct tipc_node_subscr *node_sub, u32 addr,
                            void *usr_handle, net_ev_handler handle_down);
 void tipc_nodesub_unsubscribe(struct tipc_node_subscr *node_sub);
-void tipc_nodesub_notify(struct tipc_node *node);
+void tipc_nodesub_notify(struct list_head *nsub_list);
 
 #endif
index 5c14c7801ee65095d809d502cab9f78d33d51e5e..5fd7acce01ea339b7ffe2873956e9513eb40bb49 100644 (file)
@@ -165,7 +165,7 @@ void tipc_port_mcast_rcv(struct sk_buff *buf, struct tipc_port_list *dp)
                msg_set_destnode(msg, tipc_own_addr);
                if (dp->count == 1) {
                        msg_set_destport(msg, dp->ports[0]);
-                       tipc_port_rcv(buf);
+                       tipc_sk_rcv(buf);
                        tipc_port_list_free(dp);
                        return;
                }
@@ -180,7 +180,7 @@ void tipc_port_mcast_rcv(struct sk_buff *buf, struct tipc_port_list *dp)
                        if ((index == 0) && (cnt != 0))
                                item = item->next;
                        msg_set_destport(buf_msg(b), item->ports[index]);
-                       tipc_port_rcv(b);
+                       tipc_sk_rcv(b);
                }
        }
 exit:
@@ -343,7 +343,7 @@ int tipc_reject_msg(struct sk_buff *buf, u32 err)
        /* send returned message & dispose of rejected message */
        src_node = msg_prevnode(msg);
        if (in_own_node(src_node))
-               tipc_port_rcv(rbuf);
+               tipc_sk_rcv(rbuf);
        else
                tipc_link_xmit(rbuf, src_node, msg_link_selector(rmsg));
 exit:
@@ -754,37 +754,6 @@ int tipc_port_shutdown(u32 ref)
        return tipc_port_disconnect(ref);
 }
 
-/**
- * tipc_port_rcv - receive message from lower layer and deliver to port user
- */
-int tipc_port_rcv(struct sk_buff *buf)
-{
-       struct tipc_port *p_ptr;
-       struct tipc_msg *msg = buf_msg(buf);
-       u32 destport = msg_destport(msg);
-       u32 dsz = msg_data_sz(msg);
-       u32 err;
-
-       /* forward unresolved named message */
-       if (unlikely(!destport)) {
-               tipc_net_route_msg(buf);
-               return dsz;
-       }
-
-       /* validate destination & pass to port, otherwise reject message */
-       p_ptr = tipc_port_lock(destport);
-       if (likely(p_ptr)) {
-               err = tipc_sk_rcv(&tipc_port_to_sock(p_ptr)->sk, buf);
-               tipc_port_unlock(p_ptr);
-               if (likely(!err))
-                       return dsz;
-       } else {
-               err = TIPC_ERR_NO_PORT;
-       }
-
-       return tipc_reject_msg(buf, err);
-}
-
 /*
  *  tipc_port_iovec_rcv: Concatenate and deliver sectioned
  *                       message for this node.
@@ -798,7 +767,7 @@ static int tipc_port_iovec_rcv(struct tipc_port *sender,
 
        res = tipc_msg_build(&sender->phdr, msg_sect, len, MAX_MSG_SIZE, &buf);
        if (likely(buf))
-               tipc_port_rcv(buf);
+               tipc_sk_rcv(buf);
        return res;
 }
 
index a00397393bd1d9179bd779339500e34bd710aa5b..cf4ca5b1d9a48ae7752f9f476cad079e3f115da8 100644 (file)
 #include "msg.h"
 #include "node_subscr.h"
 
-#define TIPC_FLOW_CONTROL_WIN 512
-#define CONN_OVERLOAD_LIMIT    ((TIPC_FLOW_CONTROL_WIN * 2 + 1) * \
-                               SKB_TRUESIZE(TIPC_MAX_USER_MSG_SIZE))
+#define TIPC_CONNACK_INTV         256
+#define TIPC_FLOWCTRL_WIN        (TIPC_CONNACK_INTV * 2)
+#define TIPC_CONN_OVERLOAD_LIMIT ((TIPC_FLOWCTRL_WIN * 2 + 1) * \
+                                 SKB_TRUESIZE(TIPC_MAX_USER_MSG_SIZE))
 
 /**
  * struct tipc_port - TIPC port structure
@@ -134,7 +135,6 @@ int tipc_port_peer_msg(struct tipc_port *p_ptr, struct tipc_msg *msg);
 /*
  * TIPC messaging routines
  */
-int tipc_port_rcv(struct sk_buff *buf);
 
 int tipc_send(struct tipc_port *port,
              struct iovec const *msg_sect,
@@ -187,7 +187,7 @@ static inline void tipc_port_unlock(struct tipc_port *p_ptr)
 
 static inline int tipc_port_congested(struct tipc_port *p_ptr)
 {
-       return (p_ptr->sent - p_ptr->acked) >= (TIPC_FLOW_CONTROL_WIN * 2);
+       return ((p_ptr->sent - p_ptr->acked) >= TIPC_FLOWCTRL_WIN);
 }
 
 
index 3c0256962f7dafa4ee3b11d69aed963822c888c2..ef0475568f9e39cfa3aa24d1d4d7a7132ea059a4 100644 (file)
@@ -36,6 +36,7 @@
 
 #include "core.h"
 #include "port.h"
+#include "node.h"
 
 #include <linux/export.h>
 
@@ -44,7 +45,7 @@
 
 #define CONN_TIMEOUT_DEFAULT   8000    /* default connect timeout = 8s */
 
-static int backlog_rcv(struct sock *sk, struct sk_buff *skb);
+static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *skb);
 static void tipc_data_ready(struct sock *sk);
 static void tipc_write_space(struct sock *sk);
 static int tipc_release(struct socket *sock);
@@ -195,11 +196,12 @@ static int tipc_sk_create(struct net *net, struct socket *sock,
        sock->state = state;
 
        sock_init_data(sock, sk);
-       sk->sk_backlog_rcv = backlog_rcv;
+       sk->sk_backlog_rcv = tipc_backlog_rcv;
        sk->sk_rcvbuf = sysctl_tipc_rmem[1];
        sk->sk_data_ready = tipc_data_ready;
        sk->sk_write_space = tipc_write_space;
-       tipc_sk(sk)->conn_timeout = CONN_TIMEOUT_DEFAULT;
+       tsk->conn_timeout = CONN_TIMEOUT_DEFAULT;
+       atomic_set(&tsk->dupl_rcvcnt, 0);
        tipc_port_unlock(port);
 
        if (sock->state == SS_READY) {
@@ -983,10 +985,11 @@ static int anc_data_recv(struct msghdr *m, struct tipc_msg *msg,
        return 0;
 }
 
-static int tipc_wait_for_rcvmsg(struct socket *sock, long timeo)
+static int tipc_wait_for_rcvmsg(struct socket *sock, long *timeop)
 {
        struct sock *sk = sock->sk;
        DEFINE_WAIT(wait);
+       long timeo = *timeop;
        int err;
 
        for (;;) {
@@ -1011,6 +1014,7 @@ static int tipc_wait_for_rcvmsg(struct socket *sock, long timeo)
                        break;
        }
        finish_wait(sk_sleep(sk), &wait);
+       *timeop = timeo;
        return err;
 }
 
@@ -1054,7 +1058,7 @@ static int tipc_recvmsg(struct kiocb *iocb, struct socket *sock,
 restart:
 
        /* Look for a message in receive queue; wait if necessary */
-       res = tipc_wait_for_rcvmsg(sock, timeo);
+       res = tipc_wait_for_rcvmsg(sock, &timeo);
        if (res)
                goto exit;
 
@@ -1100,7 +1104,7 @@ static int tipc_recvmsg(struct kiocb *iocb, struct socket *sock,
        /* Consume received message (optional) */
        if (likely(!(flags & MSG_PEEK))) {
                if ((sock->state != SS_READY) &&
-                   (++port->conn_unacked >= TIPC_FLOW_CONTROL_WIN))
+                   (++port->conn_unacked >= TIPC_CONNACK_INTV))
                        tipc_acknowledge(port->ref, port->conn_unacked);
                advance_rx_queue(sk);
        }
@@ -1152,7 +1156,7 @@ static int tipc_recv_stream(struct kiocb *iocb, struct socket *sock,
 
 restart:
        /* Look for a message in receive queue; wait if necessary */
-       res = tipc_wait_for_rcvmsg(sock, timeo);
+       res = tipc_wait_for_rcvmsg(sock, &timeo);
        if (res)
                goto exit;
 
@@ -1209,7 +1213,7 @@ static int tipc_recv_stream(struct kiocb *iocb, struct socket *sock,
 
        /* Consume received message (optional) */
        if (likely(!(flags & MSG_PEEK))) {
-               if (unlikely(++port->conn_unacked >= TIPC_FLOW_CONTROL_WIN))
+               if (unlikely(++port->conn_unacked >= TIPC_CONNACK_INTV))
                        tipc_acknowledge(port->ref, port->conn_unacked);
                advance_rx_queue(sk);
        }
@@ -1415,7 +1419,7 @@ static u32 filter_rcv(struct sock *sk, struct sk_buff *buf)
 }
 
 /**
- * backlog_rcv - handle incoming message from backlog queue
+ * tipc_backlog_rcv - handle incoming message from backlog queue
  * @sk: socket
  * @buf: message
  *
@@ -1423,47 +1427,74 @@ static u32 filter_rcv(struct sock *sk, struct sk_buff *buf)
  *
  * Returns 0
  */
-static int backlog_rcv(struct sock *sk, struct sk_buff *buf)
+static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *buf)
 {
        u32 res;
+       struct tipc_sock *tsk = tipc_sk(sk);
+       uint truesize = buf->truesize;
 
        res = filter_rcv(sk, buf);
-       if (res)
+       if (unlikely(res))
                tipc_reject_msg(buf, res);
+
+       if (atomic_read(&tsk->dupl_rcvcnt) < TIPC_CONN_OVERLOAD_LIMIT)
+               atomic_add(truesize, &tsk->dupl_rcvcnt);
+
        return 0;
 }
 
 /**
  * tipc_sk_rcv - handle incoming message
- * @sk:  socket receiving message
- * @buf: message
- *
- * Called with port lock already taken.
- *
- * Returns TIPC error status code (TIPC_OK if message is not to be rejected)
+ * @buf: buffer containing arriving message
+ * Consumes buffer
+ * Returns 0 if success, or errno: -EHOSTUNREACH
  */
-u32 tipc_sk_rcv(struct sock *sk, struct sk_buff *buf)
+int tipc_sk_rcv(struct sk_buff *buf)
 {
-       u32 res;
+       struct tipc_sock *tsk;
+       struct tipc_port *port;
+       struct sock *sk;
+       u32 dport = msg_destport(buf_msg(buf));
+       int err = TIPC_OK;
+       uint limit;
 
-       /*
-        * Process message if socket is unlocked; otherwise add to backlog queue
-        *
-        * This code is based on sk_receive_skb(), but must be distinct from it
-        * since a TIPC-specific filter/reject mechanism is utilized
-        */
+       /* Forward unresolved named message */
+       if (unlikely(!dport)) {
+               tipc_net_route_msg(buf);
+               return 0;
+       }
+
+       /* Validate destination */
+       port = tipc_port_lock(dport);
+       if (unlikely(!port)) {
+               err = TIPC_ERR_NO_PORT;
+               goto exit;
+       }
+
+       tsk = tipc_port_to_sock(port);
+       sk = &tsk->sk;
+
+       /* Queue message */
        bh_lock_sock(sk);
+
        if (!sock_owned_by_user(sk)) {
-               res = filter_rcv(sk, buf);
+               err = filter_rcv(sk, buf);
        } else {
-               if (sk_add_backlog(sk, buf, rcvbuf_limit(sk, buf)))
-                       res = TIPC_ERR_OVERLOAD;
-               else
-                       res = TIPC_OK;
+               if (sk->sk_backlog.len == 0)
+                       atomic_set(&tsk->dupl_rcvcnt, 0);
+               limit = rcvbuf_limit(sk, buf) + atomic_read(&tsk->dupl_rcvcnt);
+               if (sk_add_backlog(sk, buf, limit))
+                       err = TIPC_ERR_OVERLOAD;
        }
+
        bh_unlock_sock(sk);
+       tipc_port_unlock(port);
 
-       return res;
+       if (likely(!err))
+               return 0;
+exit:
+       tipc_reject_msg(buf, err);
+       return -EHOSTUNREACH;
 }
 
 static int tipc_wait_for_connect(struct socket *sock, long *timeo_p)
@@ -1905,6 +1936,28 @@ static int tipc_getsockopt(struct socket *sock, int lvl, int opt,
        return put_user(sizeof(value), ol);
 }
 
+int tipc_ioctl(struct socket *sk, unsigned int cmd, unsigned long arg)
+{
+       struct tipc_sioc_ln_req lnr;
+       void __user *argp = (void __user *)arg;
+
+       switch (cmd) {
+       case SIOCGETLINKNAME:
+               if (copy_from_user(&lnr, argp, sizeof(lnr)))
+                       return -EFAULT;
+               if (!tipc_node_get_linkname(lnr.bearer_id, lnr.peer,
+                                           lnr.linkname, TIPC_MAX_LINK_NAME)) {
+                       if (copy_to_user(argp, &lnr, sizeof(lnr)))
+                               return -EFAULT;
+                       return 0;
+               }
+               return -EADDRNOTAVAIL;
+               break;
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+
 /* Protocol switches for the various types of TIPC sockets */
 
 static const struct proto_ops msg_ops = {
@@ -1917,7 +1970,7 @@ static const struct proto_ops msg_ops = {
        .accept         = sock_no_accept,
        .getname        = tipc_getname,
        .poll           = tipc_poll,
-       .ioctl          = sock_no_ioctl,
+       .ioctl          = tipc_ioctl,
        .listen         = sock_no_listen,
        .shutdown       = tipc_shutdown,
        .setsockopt     = tipc_setsockopt,
@@ -1938,7 +1991,7 @@ static const struct proto_ops packet_ops = {
        .accept         = tipc_accept,
        .getname        = tipc_getname,
        .poll           = tipc_poll,
-       .ioctl          = sock_no_ioctl,
+       .ioctl          = tipc_ioctl,
        .listen         = tipc_listen,
        .shutdown       = tipc_shutdown,
        .setsockopt     = tipc_setsockopt,
@@ -1959,7 +2012,7 @@ static const struct proto_ops stream_ops = {
        .accept         = tipc_accept,
        .getname        = tipc_getname,
        .poll           = tipc_poll,
-       .ioctl          = sock_no_ioctl,
+       .ioctl          = tipc_ioctl,
        .listen         = tipc_listen,
        .shutdown       = tipc_shutdown,
        .setsockopt     = tipc_setsockopt,
index 74e5c7f195a660d6a1e88d1b506cf4e7371566f1..3afcd2a70b313c21d67752606b839e613d3cf9df 100644 (file)
  * @port: port - interacts with 'sk' and with the rest of the TIPC stack
  * @peer_name: the peer of the connection, if any
  * @conn_timeout: the time we can wait for an unresponded setup request
+ * @dupl_rcvcnt: number of bytes counted twice, in both backlog and rcv queue
  */
 
 struct tipc_sock {
        struct sock sk;
        struct tipc_port port;
        unsigned int conn_timeout;
+       atomic_t dupl_rcvcnt;
 };
 
 static inline struct tipc_sock *tipc_sk(const struct sock *sk)
@@ -67,6 +69,6 @@ static inline void tipc_sock_wakeup(struct tipc_sock *tsk)
        tsk->sk.sk_write_space(&tsk->sk);
 }
 
-u32 tipc_sk_rcv(struct sock *sk, struct sk_buff *buf);
+int tipc_sk_rcv(struct sk_buff *buf);
 
 #endif
index 749f80c21e220b4222a57c376499a3ad18f12ad9..e968843807320ea66a8b6421974fa182ab9d58aa 100644 (file)
@@ -1492,10 +1492,14 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,
        if (len > sk->sk_sndbuf - 32)
                goto out;
 
-       if (len > SKB_MAX_ALLOC)
+       if (len > SKB_MAX_ALLOC) {
                data_len = min_t(size_t,
                                 len - SKB_MAX_ALLOC,
                                 MAX_SKB_FRAGS * PAGE_SIZE);
+               data_len = PAGE_ALIGN(data_len);
+
+               BUILD_BUG_ON(SKB_MAX_ALLOC < PAGE_SIZE);
+       }
 
        skb = sock_alloc_send_pskb(sk, len - data_len, data_len,
                                   msg->msg_flags & MSG_DONTWAIT, &err,
@@ -1670,6 +1674,8 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
 
                data_len = max_t(int, 0, size - SKB_MAX_HEAD(0));
 
+               data_len = min_t(size_t, size, PAGE_ALIGN(data_len));
+
                skb = sock_alloc_send_pskb(sk, size - data_len, data_len,
                                           msg->msg_flags & MSG_DONTWAIT, &err,
                                           get_order(UNIX_SKB_FRAGS_SZ));
index 16d08b39921071479456e2033c5e371d381175b7..405f3c4cf70ca3617a4101e1bad278b93d7ae1b7 100644 (file)
@@ -95,6 +95,43 @@ config CFG80211_CERTIFICATION_ONUS
          you are a wireless researcher and are working in a controlled
          and approved environment by your local regulatory agency.
 
+config CFG80211_REG_CELLULAR_HINTS
+       bool "cfg80211 regulatory support for cellular base station hints"
+       depends on CFG80211_CERTIFICATION_ONUS
+       ---help---
+         This option enables support for parsing regulatory hints
+         from cellular base stations. If enabled and at least one driver
+         claims support for parsing cellular base station hints the
+         regulatory core will allow and parse these regulatory hints.
+         The regulatory core will only apply these regulatory hints on
+         drivers that support this feature. You should only enable this
+         feature if you have tested and validated this feature on your
+         systems.
+
+config CFG80211_REG_RELAX_NO_IR
+       bool "cfg80211 support for NO_IR relaxation"
+       depends on CFG80211_CERTIFICATION_ONUS
+       ---help---
+        This option enables support for relaxation of the NO_IR flag for
+        situations that certain regulatory bodies have provided clarifications
+        on how relaxation can occur. This feature has an inherent dependency on
+        userspace features which must have been properly tested and as such is
+        not enabled by default.
+
+        A relaxation feature example is allowing the operation of a P2P group
+        owner (GO) on channels marked with NO_IR if there is an additional BSS
+        interface which associated to an AP which userspace assumes or confirms
+        to be an authorized master, i.e., with radar detection support and DFS
+        capabilities. However, note that in order to not create daisy chain
+        scenarios, this relaxation is not allowed in cases that the BSS client
+        is associated to P2P GO and in addition the P2P GO instantiated on
+        a channel due to this relaxation should not allow connection from
+        non P2P clients.
+
+        The regulatory core will apply these relaxations only for drivers that
+        support this feature by declaring the appropriate channel flags and
+        capabilities in their registration flow.
+
 config CFG80211_DEFAULT_PS
        bool "enable powersave by default"
        depends on CFG80211
index 3e02ade508d8804d4158a03ad5de474d64bf7594..bdad1f951561b3d8b7c75d8992e1c3c1a623f146 100644 (file)
@@ -6,8 +6,8 @@
 #include "rdev-ops.h"
 
 
-static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
-                             struct net_device *dev, bool notify)
+int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
+                      struct net_device *dev, bool notify)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        int err;
index 9c9501a35fb5c6a43a142132306998405e086774..992b34070bcb16f9fe4323bae293996524f1373c 100644 (file)
@@ -326,28 +326,57 @@ static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
 
 
 int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
-                                 const struct cfg80211_chan_def *chandef)
+                                 const struct cfg80211_chan_def *chandef,
+                                 enum nl80211_iftype iftype)
 {
        int width;
-       int r;
+       int ret;
 
        if (WARN_ON(!cfg80211_chandef_valid(chandef)))
                return -EINVAL;
 
-       width = cfg80211_chandef_get_width(chandef);
-       if (width < 0)
-               return -EINVAL;
+       switch (iftype) {
+       case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_P2P_GO:
+       case NL80211_IFTYPE_MESH_POINT:
+               width = cfg80211_chandef_get_width(chandef);
+               if (width < 0)
+                       return -EINVAL;
 
-       r = cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq1,
-                                           width);
-       if (r)
-               return r;
+               ret = cfg80211_get_chans_dfs_required(wiphy,
+                                                     chandef->center_freq1,
+                                                     width);
+               if (ret < 0)
+                       return ret;
+               else if (ret > 0)
+                       return BIT(chandef->width);
 
-       if (!chandef->center_freq2)
-               return 0;
+               if (!chandef->center_freq2)
+                       return 0;
+
+               ret = cfg80211_get_chans_dfs_required(wiphy,
+                                                     chandef->center_freq2,
+                                                     width);
+               if (ret < 0)
+                       return ret;
+               else if (ret > 0)
+                       return BIT(chandef->width);
 
-       return cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq2,
-                                              width);
+               break;
+       case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_P2P_CLIENT:
+       case NL80211_IFTYPE_MONITOR:
+       case NL80211_IFTYPE_AP_VLAN:
+       case NL80211_IFTYPE_WDS:
+       case NL80211_IFTYPE_P2P_DEVICE:
+               break;
+       case NL80211_IFTYPE_UNSPECIFIED:
+       case NUM_NL80211_IFTYPES:
+               WARN_ON(1);
+       }
+
+       return 0;
 }
 EXPORT_SYMBOL(cfg80211_chandef_dfs_required);
 
@@ -587,12 +616,14 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
                width = 5;
                break;
        case NL80211_CHAN_WIDTH_10:
+               prohibited_flags |= IEEE80211_CHAN_NO_10MHZ;
                width = 10;
                break;
        case NL80211_CHAN_WIDTH_20:
                if (!ht_cap->ht_supported)
                        return false;
        case NL80211_CHAN_WIDTH_20_NOHT:
+               prohibited_flags |= IEEE80211_CHAN_NO_20MHZ;
                width = 20;
                break;
        case NL80211_CHAN_WIDTH_40:
@@ -661,17 +692,111 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
 }
 EXPORT_SYMBOL(cfg80211_chandef_usable);
 
+/*
+ * For GO only, check if the channel can be used under permissive conditions
+ * mandated by the some regulatory bodies, i.e., the channel is marked with
+ * IEEE80211_CHAN_GO_CONCURRENT and there is an additional station interface
+ * associated to an AP on the same channel or on the same UNII band
+ * (assuming that the AP is an authorized master).
+ * In addition allow the GO to operate on a channel on which indoor operation is
+ * allowed, iff we are currently operating in an indoor environment.
+ */
+static bool cfg80211_go_permissive_chan(struct cfg80211_registered_device *rdev,
+                                       struct ieee80211_channel *chan)
+{
+       struct wireless_dev *wdev_iter;
+       struct wiphy *wiphy = wiphy_idx_to_wiphy(rdev->wiphy_idx);
+
+       ASSERT_RTNL();
+
+       if (!config_enabled(CONFIG_CFG80211_REG_RELAX_NO_IR) ||
+           !(wiphy->regulatory_flags & REGULATORY_ENABLE_RELAX_NO_IR))
+               return false;
+
+       if (regulatory_indoor_allowed() &&
+           (chan->flags & IEEE80211_CHAN_INDOOR_ONLY))
+               return true;
+
+       if (!(chan->flags & IEEE80211_CHAN_GO_CONCURRENT))
+               return false;
+
+       /*
+        * Generally, it is possible to rely on another device/driver to allow
+        * the GO concurrent relaxation, however, since the device can further
+        * enforce the relaxation (by doing a similar verifications as this),
+        * and thus fail the GO instantiation, consider only the interfaces of
+        * the current registered device.
+        */
+       list_for_each_entry(wdev_iter, &rdev->wdev_list, list) {
+               struct ieee80211_channel *other_chan = NULL;
+               int r1, r2;
+
+               if (wdev_iter->iftype != NL80211_IFTYPE_STATION ||
+                   !netif_running(wdev_iter->netdev))
+                       continue;
+
+               wdev_lock(wdev_iter);
+               if (wdev_iter->current_bss)
+                       other_chan = wdev_iter->current_bss->pub.channel;
+               wdev_unlock(wdev_iter);
+
+               if (!other_chan)
+                       continue;
+
+               if (chan == other_chan)
+                       return true;
+
+               if (chan->band != IEEE80211_BAND_5GHZ)
+                       continue;
+
+               r1 = cfg80211_get_unii(chan->center_freq);
+               r2 = cfg80211_get_unii(other_chan->center_freq);
+
+               if (r1 != -EINVAL && r1 == r2) {
+                       /*
+                        * At some locations channels 149-165 are considered a
+                        * bundle, but at other locations, e.g., Indonesia,
+                        * channels 149-161 are considered a bundle while
+                        * channel 165 is left out and considered to be in a
+                        * different bundle. Thus, in case that there is a
+                        * station interface connected to an AP on channel 165,
+                        * it is assumed that channels 149-161 are allowed for
+                        * GO operations. However, having a station interface
+                        * connected to an AP on channels 149-161, does not
+                        * allow GO operation on channel 165.
+                        */
+                       if (chan->center_freq == 5825 &&
+                           other_chan->center_freq != 5825)
+                               continue;
+                       return true;
+               }
+       }
+
+       return false;
+}
+
 bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
-                            struct cfg80211_chan_def *chandef)
+                            struct cfg80211_chan_def *chandef,
+                            enum nl80211_iftype iftype)
 {
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        bool res;
        u32 prohibited_flags = IEEE80211_CHAN_DISABLED |
-                              IEEE80211_CHAN_NO_IR |
                               IEEE80211_CHAN_RADAR;
 
-       trace_cfg80211_reg_can_beacon(wiphy, chandef);
+       trace_cfg80211_reg_can_beacon(wiphy, chandef, iftype);
 
-       if (cfg80211_chandef_dfs_required(wiphy, chandef) > 0 &&
+       /*
+        * Under certain conditions suggested by the some regulatory bodies
+        * a GO can operate on channels marked with IEEE80211_NO_IR
+        * so set this flag only if such relaxations are not enabled and
+        * the conditions are not met.
+        */
+       if (iftype != NL80211_IFTYPE_P2P_GO ||
+           !cfg80211_go_permissive_chan(rdev, chandef->chan))
+               prohibited_flags |= IEEE80211_CHAN_NO_IR;
+
+       if (cfg80211_chandef_dfs_required(wiphy, chandef, iftype) > 0 &&
            cfg80211_chandef_dfs_available(wiphy, chandef)) {
                /* We can skip IEEE80211_CHAN_NO_IR if chandef dfs available */
                prohibited_flags = IEEE80211_CHAN_DISABLED;
@@ -701,6 +826,8 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
                        enum cfg80211_chan_mode *chanmode,
                        u8 *radar_detect)
 {
+       int ret;
+
        *chan = NULL;
        *chanmode = CHAN_MODE_UNDEFINED;
 
@@ -743,8 +870,11 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
                        *chan = wdev->chandef.chan;
                        *chanmode = CHAN_MODE_SHARED;
 
-                       if (cfg80211_chandef_dfs_required(wdev->wiphy,
-                                                         &wdev->chandef))
+                       ret = cfg80211_chandef_dfs_required(wdev->wiphy,
+                                                           &wdev->chandef,
+                                                           wdev->iftype);
+                       WARN_ON(ret < 0);
+                       if (ret > 0)
                                *radar_detect |= BIT(wdev->chandef.width);
                }
                return;
@@ -753,8 +883,11 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
                        *chan = wdev->chandef.chan;
                        *chanmode = CHAN_MODE_SHARED;
 
-                       if (cfg80211_chandef_dfs_required(wdev->wiphy,
-                                                         &wdev->chandef))
+                       ret = cfg80211_chandef_dfs_required(wdev->wiphy,
+                                                           &wdev->chandef,
+                                                           wdev->iftype);
+                       WARN_ON(ret < 0);
+                       if (ret > 0)
                                *radar_detect |= BIT(wdev->chandef.width);
                }
                return;
index 086cddd03ba6edd79d1609ecf713146bc756c1ff..a1c40654dd9b1ca9b47bbaef6682ee6d49147e66 100644 (file)
@@ -69,7 +69,7 @@ struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx)
 
 int get_wiphy_idx(struct wiphy *wiphy)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        return rdev->wiphy_idx;
 }
@@ -130,7 +130,7 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
                            newname))
                pr_err("failed to rename debugfs dir to %s!\n", newname);
 
-       nl80211_notify_dev_rename(rdev);
+       nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
 
        return 0;
 }
@@ -210,15 +210,12 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
        }
 }
 
-static int cfg80211_rfkill_set_block(void *data, bool blocked)
+void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy)
 {
-       struct cfg80211_registered_device *rdev = data;
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct wireless_dev *wdev;
 
-       if (!blocked)
-               return 0;
-
-       rtnl_lock();
+       ASSERT_RTNL();
 
        list_for_each_entry(wdev, &rdev->wdev_list, list) {
                if (wdev->netdev) {
@@ -234,7 +231,18 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked)
                        break;
                }
        }
+}
+EXPORT_SYMBOL_GPL(cfg80211_shutdown_all_interfaces);
+
+static int cfg80211_rfkill_set_block(void *data, bool blocked)
+{
+       struct cfg80211_registered_device *rdev = data;
+
+       if (!blocked)
+               return 0;
 
+       rtnl_lock();
+       cfg80211_shutdown_all_interfaces(&rdev->wiphy);
        rtnl_unlock();
 
        return 0;
@@ -260,6 +268,45 @@ static void cfg80211_event_work(struct work_struct *work)
        rtnl_unlock();
 }
 
+void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev)
+{
+       struct cfg80211_iface_destroy *item;
+
+       ASSERT_RTNL();
+
+       spin_lock_irq(&rdev->destroy_list_lock);
+       while ((item = list_first_entry_or_null(&rdev->destroy_list,
+                                               struct cfg80211_iface_destroy,
+                                               list))) {
+               struct wireless_dev *wdev, *tmp;
+               u32 nlportid = item->nlportid;
+
+               list_del(&item->list);
+               kfree(item);
+               spin_unlock_irq(&rdev->destroy_list_lock);
+
+               list_for_each_entry_safe(wdev, tmp, &rdev->wdev_list, list) {
+                       if (nlportid == wdev->owner_nlportid)
+                               rdev_del_virtual_intf(rdev, wdev);
+               }
+
+               spin_lock_irq(&rdev->destroy_list_lock);
+       }
+       spin_unlock_irq(&rdev->destroy_list_lock);
+}
+
+static void cfg80211_destroy_iface_wk(struct work_struct *work)
+{
+       struct cfg80211_registered_device *rdev;
+
+       rdev = container_of(work, struct cfg80211_registered_device,
+                           destroy_work);
+
+       rtnl_lock();
+       cfg80211_destroy_ifaces(rdev);
+       rtnl_unlock();
+}
+
 /* exported functions */
 
 struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
@@ -318,6 +365,10 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
        rdev->wiphy.dev.class = &ieee80211_class;
        rdev->wiphy.dev.platform_data = rdev;
 
+       INIT_LIST_HEAD(&rdev->destroy_list);
+       spin_lock_init(&rdev->destroy_list_lock);
+       INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk);
+
 #ifdef CONFIG_CFG80211_DEFAULT_PS
        rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
 #endif
@@ -351,6 +402,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
        rdev->wiphy.rts_threshold = (u32) -1;
        rdev->wiphy.coverage_class = 0;
 
+       rdev->wiphy.max_num_csa_counters = 1;
+
        return &rdev->wiphy;
 }
 EXPORT_SYMBOL(wiphy_new);
@@ -396,10 +449,7 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)
                for (j = 0; j < c->n_limits; j++) {
                        u16 types = c->limits[j].types;
 
-                       /*
-                        * interface types shouldn't overlap, this is
-                        * used in cfg80211_can_change_interface()
-                        */
+                       /* interface types shouldn't overlap */
                        if (WARN_ON(types & all_iftypes))
                                return -EINVAL;
                        all_iftypes |= types;
@@ -435,7 +485,7 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)
 
 int wiphy_register(struct wiphy *wiphy)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        int res;
        enum ieee80211_band band;
        struct ieee80211_supported_band *sband;
@@ -610,13 +660,15 @@ int wiphy_register(struct wiphy *wiphy)
                return res;
        }
 
+       nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
+
        return 0;
 }
 EXPORT_SYMBOL(wiphy_register);
 
 void wiphy_rfkill_start_polling(struct wiphy *wiphy)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        if (!rdev->ops->rfkill_poll)
                return;
@@ -627,7 +679,7 @@ EXPORT_SYMBOL(wiphy_rfkill_start_polling);
 
 void wiphy_rfkill_stop_polling(struct wiphy *wiphy)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        rfkill_pause_polling(rdev->rfkill);
 }
@@ -635,7 +687,7 @@ EXPORT_SYMBOL(wiphy_rfkill_stop_polling);
 
 void wiphy_unregister(struct wiphy *wiphy)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        wait_event(rdev->dev_wait, ({
                int __count;
@@ -648,9 +700,10 @@ void wiphy_unregister(struct wiphy *wiphy)
                rfkill_unregister(rdev->rfkill);
 
        rtnl_lock();
+       nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY);
        rdev->wiphy.registered = false;
 
-       BUG_ON(!list_empty(&rdev->wdev_list));
+       WARN_ON(!list_empty(&rdev->wdev_list));
 
        /*
         * First remove the hardware from everywhere, this makes
@@ -675,6 +728,7 @@ void wiphy_unregister(struct wiphy *wiphy)
        cancel_work_sync(&rdev->conn_work);
        flush_work(&rdev->event_work);
        cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
+       flush_work(&rdev->destroy_work);
 
 #ifdef CONFIG_PM
        if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup)
@@ -707,7 +761,7 @@ EXPORT_SYMBOL(wiphy_free);
 
 void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        if (rfkill_set_hw_state(rdev->rfkill, blocked))
                schedule_work(&rdev->rfkill_sync);
@@ -716,7 +770,7 @@ EXPORT_SYMBOL(wiphy_rfkill_set_hw_state);
 
 void cfg80211_unregister_wdev(struct wireless_dev *wdev)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 
        ASSERT_RTNL();
 
@@ -751,23 +805,23 @@ void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
                rdev->num_running_monitor_ifaces += num;
 }
 
-void cfg80211_leave(struct cfg80211_registered_device *rdev,
-                   struct wireless_dev *wdev)
+void __cfg80211_leave(struct cfg80211_registered_device *rdev,
+                     struct wireless_dev *wdev)
 {
        struct net_device *dev = wdev->netdev;
 
        ASSERT_RTNL();
+       ASSERT_WDEV_LOCK(wdev);
 
        switch (wdev->iftype) {
        case NL80211_IFTYPE_ADHOC:
-               cfg80211_leave_ibss(rdev, dev, true);
+               __cfg80211_leave_ibss(rdev, dev, true);
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_STATION:
                if (rdev->sched_scan_req && dev == rdev->sched_scan_req->dev)
                        __cfg80211_stop_sched_scan(rdev, false);
 
-               wdev_lock(wdev);
 #ifdef CONFIG_CFG80211_WEXT
                kfree(wdev->wext.ie);
                wdev->wext.ie = NULL;
@@ -776,32 +830,60 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev,
 #endif
                cfg80211_disconnect(rdev, dev,
                                    WLAN_REASON_DEAUTH_LEAVING, true);
-               wdev_unlock(wdev);
                break;
        case NL80211_IFTYPE_MESH_POINT:
-               cfg80211_leave_mesh(rdev, dev);
+               __cfg80211_leave_mesh(rdev, dev);
                break;
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_P2P_GO:
-               cfg80211_stop_ap(rdev, dev, true);
+               __cfg80211_stop_ap(rdev, dev, true);
                break;
        default:
                break;
        }
 }
 
+void cfg80211_leave(struct cfg80211_registered_device *rdev,
+                   struct wireless_dev *wdev)
+{
+       wdev_lock(wdev);
+       __cfg80211_leave(rdev, wdev);
+       wdev_unlock(wdev);
+}
+
+void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev,
+                        gfp_t gfp)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+       struct cfg80211_event *ev;
+       unsigned long flags;
+
+       trace_cfg80211_stop_iface(wiphy, wdev);
+
+       ev = kzalloc(sizeof(*ev), gfp);
+       if (!ev)
+               return;
+
+       ev->type = EVENT_STOPPED;
+
+       spin_lock_irqsave(&wdev->event_lock, flags);
+       list_add_tail(&ev->list, &wdev->event_list);
+       spin_unlock_irqrestore(&wdev->event_lock, flags);
+       queue_work(cfg80211_wq, &rdev->event_work);
+}
+EXPORT_SYMBOL(cfg80211_stop_iface);
+
 static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                                         unsigned long state, void *ptr)
 {
        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_registered_device *rdev;
-       int ret;
 
        if (!wdev)
                return NOTIFY_DONE;
 
-       rdev = wiphy_to_dev(wdev->wiphy);
+       rdev = wiphy_to_rdev(wdev->wiphy);
 
        WARN_ON(wdev->iftype == NL80211_IFTYPE_UNSPECIFIED);
 
@@ -959,13 +1041,14 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
        case NETDEV_PRE_UP:
                if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype)))
                        return notifier_from_errno(-EOPNOTSUPP);
-               ret = cfg80211_can_add_interface(rdev, wdev->iftype);
-               if (ret)
-                       return notifier_from_errno(ret);
+               if (rfkill_blocked(rdev->rfkill))
+                       return notifier_from_errno(-ERFKILL);
                break;
+       default:
+               return NOTIFY_DONE;
        }
 
-       return NOTIFY_DONE;
+       return NOTIFY_OK;
 }
 
 static struct notifier_block cfg80211_netdev_notifier = {
index 5b1fdcadd46985548f4a04f4f64ddaccbc935661..e9afbf10e756bd3a1ec81a6b6b7aa229d0688be7 100644 (file)
@@ -80,13 +80,17 @@ struct cfg80211_registered_device {
 
        struct cfg80211_coalesce *coalesce;
 
+       spinlock_t destroy_list_lock;
+       struct list_head destroy_list;
+       struct work_struct destroy_work;
+
        /* must be last because of the way we do wiphy_priv(),
         * and it should at least be aligned to NETDEV_ALIGN */
        struct wiphy wiphy __aligned(NETDEV_ALIGN);
 };
 
 static inline
-struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy)
+struct cfg80211_registered_device *wiphy_to_rdev(struct wiphy *wiphy)
 {
        BUG_ON(!wiphy);
        return container_of(wiphy, struct cfg80211_registered_device, wiphy);
@@ -181,6 +185,7 @@ enum cfg80211_event_type {
        EVENT_ROAMED,
        EVENT_DISCONNECTED,
        EVENT_IBSS_JOINED,
+       EVENT_STOPPED,
 };
 
 struct cfg80211_event {
@@ -232,6 +237,13 @@ struct cfg80211_beacon_registration {
        u32 nlportid;
 };
 
+struct cfg80211_iface_destroy {
+       struct list_head list;
+       u32 nlportid;
+};
+
+void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev);
+
 /* free object */
 void cfg80211_dev_free(struct cfg80211_registered_device *rdev);
 
@@ -240,8 +252,8 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
 
 void ieee80211_set_bitrate_flags(struct wiphy *wiphy);
 
-void cfg80211_bss_expire(struct cfg80211_registered_device *dev);
-void cfg80211_bss_age(struct cfg80211_registered_device *dev,
+void cfg80211_bss_expire(struct cfg80211_registered_device *rdev);
+void cfg80211_bss_age(struct cfg80211_registered_device *rdev,
                       unsigned long age_secs);
 
 /* IBSS */
@@ -270,6 +282,8 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
                       struct net_device *dev,
                       struct mesh_setup *setup,
                       const struct mesh_config *conf);
+int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
+                         struct net_device *dev);
 int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
                        struct net_device *dev);
 int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
@@ -277,6 +291,8 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
                              struct cfg80211_chan_def *chandef);
 
 /* AP */
+int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
+                      struct net_device *dev, bool notify);
 int cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
                     struct net_device *dev, bool notify);
 
@@ -401,35 +417,6 @@ unsigned int
 cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
                              const struct cfg80211_chan_def *chandef);
 
-static inline int
-cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
-                             struct wireless_dev *wdev,
-                             enum nl80211_iftype iftype)
-{
-       return cfg80211_can_use_iftype_chan(rdev, wdev, iftype, NULL,
-                                           CHAN_MODE_UNDEFINED, 0);
-}
-
-static inline int
-cfg80211_can_add_interface(struct cfg80211_registered_device *rdev,
-                          enum nl80211_iftype iftype)
-{
-       if (rfkill_blocked(rdev->rfkill))
-               return -ERFKILL;
-
-       return cfg80211_can_change_interface(rdev, NULL, iftype);
-}
-
-static inline int
-cfg80211_can_use_chan(struct cfg80211_registered_device *rdev,
-                     struct wireless_dev *wdev,
-                     struct ieee80211_channel *chan,
-                     enum cfg80211_chan_mode chanmode)
-{
-       return cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
-                                           chan, chanmode, 0);
-}
-
 static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
 {
        unsigned long end = jiffies;
@@ -459,6 +446,8 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
 void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
                               enum nl80211_iftype iftype, int num);
 
+void __cfg80211_leave(struct cfg80211_registered_device *rdev,
+                     struct wireless_dev *wdev);
 void cfg80211_leave(struct cfg80211_registered_device *rdev,
                    struct wireless_dev *wdev);
 
index e37862f1b1270d8e2056fb6289f01bf69b583720..d4860bfc020e5a1c43758e8cca6e9508908344be 100644 (file)
@@ -43,7 +43,7 @@ static void cfg80211_get_ringparam(struct net_device *dev,
                                   struct ethtool_ringparam *rp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 
        memset(rp, 0, sizeof(*rp));
 
@@ -56,7 +56,7 @@ static int cfg80211_set_ringparam(struct net_device *dev,
                                  struct ethtool_ringparam *rp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 
        if (rp->rx_mini_pending != 0 || rp->rx_jumbo_pending != 0)
                return -EINVAL;
@@ -70,7 +70,7 @@ static int cfg80211_set_ringparam(struct net_device *dev,
 static int cfg80211_get_sset_count(struct net_device *dev, int sset)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        if (rdev->ops->get_et_sset_count)
                return rdev_get_et_sset_count(rdev, dev, sset);
        return -EOPNOTSUPP;
@@ -80,7 +80,7 @@ static void cfg80211_get_stats(struct net_device *dev,
                               struct ethtool_stats *stats, u64 *data)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        if (rdev->ops->get_et_stats)
                rdev_get_et_stats(rdev, dev, stats, data);
 }
@@ -88,7 +88,7 @@ static void cfg80211_get_stats(struct net_device *dev,
 static void cfg80211_get_strings(struct net_device *dev, u32 sset, u8 *data)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        if (rdev->ops->get_et_strings)
                rdev_get_et_strings(rdev, dev, sset, data);
 }
index b35da8dc85deff4640ecb0975cc829326b68034c..40c37fc5b67cb76325dcd4f0041c1ad3cf5ebf66 100644 (file)
@@ -68,17 +68,7 @@ function parse_reg_rule()
        sub(/,/, "", units)
        dfs_cac = $9
        if (units == "mW") {
-               if (power == 100) {
-                       power = 20
-               } else if (power == 200) {
-                       power = 23
-               } else if (power == 500) {
-                       power = 27
-               } else if (power == 1000) {
-                       power = 30
-               } else {
-                       print "Unknown power value in database!"
-               }
+               power = 10 * log(power)/log(10)
        } else {
                dfs_cac = $8
        }
@@ -117,7 +107,7 @@ function parse_reg_rule()
 
        }
        flags = flags "0"
-       printf "\t\tREG_RULE_EXT(%d, %d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, dfs_cac, flags
+       printf "\t\tREG_RULE_EXT(%d, %d, %d, %d, %.0f, %d, %s),\n", start, end, bw, gain, power, dfs_cac, flags
        rules++
 }
 
index a6b5bdad039c7450f276d1e56994661e2e41952c..8f345da3ea5f4e0767ae6b1de539f18954707148 100644 (file)
@@ -45,7 +45,7 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
 
        cfg80211_upload_connect_keys(wdev);
 
-       nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid,
+       nl80211_send_ibss_bssid(wiphy_to_rdev(wdev->wiphy), dev, bssid,
                                GFP_KERNEL);
 #ifdef CONFIG_CFG80211_WEXT
        memset(&wrqu, 0, sizeof(wrqu));
@@ -58,7 +58,7 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
                          struct ieee80211_channel *channel, gfp_t gfp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_event *ev;
        unsigned long flags;
 
@@ -88,8 +88,6 @@ static 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);
@@ -126,28 +124,6 @@ static int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
 #ifdef CONFIG_CFG80211_WEXT
        wdev->wext.ibss.chandef = params->chandef;
 #endif
-       check_chan = params->chandef.chan;
-       if (params->userspace_handles_dfs) {
-               /* 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);
-       }
-
-       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);
-
-       if (err) {
-               wdev->connect_keys = NULL;
-               return err;
-       }
-
        err = rdev_join_ibss(rdev, dev, params);
        if (err) {
                wdev->connect_keys = NULL;
@@ -180,7 +156,7 @@ int cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
 static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        int i;
 
        ASSERT_WDEV_LOCK(wdev);
@@ -335,7 +311,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
                               struct iw_freq *wextfreq, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct ieee80211_channel *chan = NULL;
        int err, freq;
 
@@ -346,7 +322,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
        if (!rdev->ops->join_ibss)
                return -EOPNOTSUPP;
 
-       freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
+       freq = cfg80211_wext_freq(wextfreq);
        if (freq < 0)
                return freq;
 
@@ -420,7 +396,7 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev,
                                struct iw_point *data, char *ssid)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        size_t len = data->length;
        int err;
 
@@ -444,8 +420,8 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev,
        if (len > 0 && ssid[len - 1] == '\0')
                len--;
 
+       memcpy(wdev->ssid, ssid, len);
        wdev->wext.ibss.ssid = wdev->ssid;
-       memcpy(wdev->wext.ibss.ssid, ssid, len);
        wdev->wext.ibss.ssid_len = len;
 
        wdev_lock(wdev);
@@ -487,7 +463,7 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev,
                             struct sockaddr *ap_addr, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        u8 *bssid = ap_addr->sa_data;
        int err;
 
@@ -505,6 +481,9 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev,
        if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
                bssid = NULL;
 
+       if (bssid && !is_valid_ether_addr(bssid))
+               return -EINVAL;
+
        /* both automatic */
        if (!bssid && !wdev->wext.ibss.bssid)
                return 0;
index 5af5cc6b2c4c2406475a3063a69eef80cc14691f..092300b30c372ddc03115638a83bc65ce7a49997 100644 (file)
@@ -99,7 +99,6 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
                         const struct mesh_config *conf)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       u8 radar_detect_width = 0;
        int err;
 
        BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != IEEE80211_MAX_MESH_ID_LEN);
@@ -175,22 +174,10 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
                                                               scan_width);
        }
 
-       if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef))
+       if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef,
+                                    NL80211_IFTYPE_MESH_POINT))
                return -EINVAL;
 
-       err = cfg80211_chandef_dfs_required(wdev->wiphy, &setup->chandef);
-       if (err < 0)
-               return err;
-       if (err)
-               radar_detect_width = BIT(setup->chandef.width);
-
-       err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
-                                          setup->chandef.chan,
-                                          CHAN_MODE_SHARED,
-                                          radar_detect_width);
-       if (err)
-               return err;
-
        err = rdev_join_mesh(rdev, dev, conf, setup);
        if (!err) {
                memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len);
@@ -236,17 +223,6 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
                if (!netif_running(wdev->netdev))
                        return -ENETDOWN;
 
-               /* cfg80211_can_use_chan() calls
-                * cfg80211_can_use_iftype_chan() with no radar
-                * detection, so if we're trying to use a radar
-                * channel here, something is wrong.
-                */
-               WARN_ON_ONCE(chandef->chan->flags & IEEE80211_CHAN_RADAR);
-               err = cfg80211_can_use_chan(rdev, wdev, chandef->chan,
-                                           CHAN_MODE_SHARED);
-               if (err)
-                       return err;
-
                err = rdev_libertas_set_mesh_channel(rdev, wdev->netdev,
                                                     chandef->chan);
                if (!err)
@@ -262,8 +238,8 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
        return 0;
 }
 
-static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
-                                struct net_device *dev)
+int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
+                         struct net_device *dev)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        int err;
index c52ff59a3e96d7cabb892bff220b86c580069a43..266766b8d80b61455565cc43779a6e229ed7710d 100644 (file)
@@ -23,7 +23,7 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss,
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
        u8 *ie = mgmt->u.assoc_resp.variable;
        int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
@@ -54,7 +54,7 @@ EXPORT_SYMBOL(cfg80211_rx_assoc_resp);
 static void cfg80211_process_auth(struct wireless_dev *wdev,
                                  const u8 *buf, size_t len)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 
        nl80211_send_rx_auth(rdev, wdev->netdev, buf, len, GFP_KERNEL);
        cfg80211_sme_rx_auth(wdev, buf, len);
@@ -63,7 +63,7 @@ static void cfg80211_process_auth(struct wireless_dev *wdev,
 static void cfg80211_process_deauth(struct wireless_dev *wdev,
                                    const u8 *buf, size_t len)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
        const u8 *bssid = mgmt->bssid;
        u16 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
@@ -82,7 +82,7 @@ static void cfg80211_process_deauth(struct wireless_dev *wdev,
 static void cfg80211_process_disassoc(struct wireless_dev *wdev,
                                      const u8 *buf, size_t len)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
        const u8 *bssid = mgmt->bssid;
        u16 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
@@ -123,7 +123,7 @@ void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        trace_cfg80211_send_auth_timeout(dev, addr);
 
@@ -136,7 +136,7 @@ void cfg80211_assoc_timeout(struct net_device *dev, struct cfg80211_bss *bss)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        trace_cfg80211_send_assoc_timeout(dev, bss->bssid);
 
@@ -172,7 +172,7 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
                                  const u8 *tsc, gfp_t gfp)
 {
        struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 #ifdef CONFIG_CFG80211_WEXT
        union iwreq_data wrqu;
        char *buf = kmalloc(128, gfp);
@@ -233,14 +233,8 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
        if (!req.bss)
                return -ENOENT;
 
-       err = cfg80211_can_use_chan(rdev, wdev, req.bss->channel,
-                                   CHAN_MODE_SHARED);
-       if (err)
-               goto out;
-
        err = rdev_auth(rdev, dev, &req);
 
-out:
        cfg80211_put_bss(&rdev->wiphy, req.bss);
        return err;
 }
@@ -306,16 +300,10 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
        if (!req->bss)
                return -ENOENT;
 
-       err = cfg80211_can_use_chan(rdev, wdev, chan, CHAN_MODE_SHARED);
-       if (err)
-               goto out;
-
        err = rdev_assoc(rdev, dev, req);
        if (!err)
                cfg80211_hold_bss(bss_from_pub(req->bss));
-
-out:
-       if (err)
+       else
                cfg80211_put_bss(&rdev->wiphy, req->bss);
 
        return err;
@@ -414,7 +402,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
                                int match_len)
 {
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct cfg80211_mgmt_registration *reg, *nreg;
        int err = 0;
        u16 mgmt_type;
@@ -473,7 +461,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
 void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
 {
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct cfg80211_mgmt_registration *reg, *tmp;
 
        spin_lock_bh(&wdev->mgmt_registrations_lock);
@@ -620,7 +608,7 @@ bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm,
                      const u8 *buf, size_t len, u32 flags, gfp_t gfp)
 {
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct cfg80211_mgmt_registration *reg;
        const struct ieee80211_txrx_stypes *stypes =
                &wiphy->mgmt_stypes[wdev->iftype];
@@ -739,7 +727,7 @@ void cfg80211_radar_event(struct wiphy *wiphy,
                          struct cfg80211_chan_def *chandef,
                          gfp_t gfp)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        unsigned long timeout;
 
        trace_cfg80211_radar_event(wiphy, chandef);
@@ -764,7 +752,7 @@ void cfg80211_cac_event(struct net_device *netdev,
 {
        struct wireless_dev *wdev = netdev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        unsigned long timeout;
 
        trace_cfg80211_cac_event(netdev, event);
index 052c1bf8ffaceb92d3f117231a46fca78ed30216..ba4f1723c83ad2eb094c15a8794fedb7b6f7a404 100644 (file)
@@ -168,8 +168,8 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
                netdev = __dev_get_by_index(netns, ifindex);
                if (netdev) {
                        if (netdev->ieee80211_ptr)
-                               tmp = wiphy_to_dev(
-                                               netdev->ieee80211_ptr->wiphy);
+                               tmp = wiphy_to_rdev(
+                                       netdev->ieee80211_ptr->wiphy);
                        else
                                tmp = NULL;
 
@@ -371,8 +371,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_CH_SWITCH_COUNT] = { .type = NLA_U32 },
        [NL80211_ATTR_CH_SWITCH_BLOCK_TX] = { .type = NLA_FLAG },
        [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_CSA_C_OFF_BEACON] = { .type = NLA_BINARY },
+       [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_BINARY },
        [NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY },
        [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY },
        [NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG },
@@ -385,6 +385,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_MAC_HINT] = { .len = ETH_ALEN },
        [NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 },
        [NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 },
+       [NL80211_ATTR_IFACE_SOCKET_OWNER] = { .type = NLA_FLAG },
+       [NL80211_ATTR_CSA_C_OFFSETS_TX] = { .type = NLA_BINARY },
 };
 
 /* policy for the key attributes */
@@ -484,7 +486,7 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
                        err = PTR_ERR(*wdev);
                        goto out_unlock;
                }
-               *rdev = wiphy_to_dev((*wdev)->wiphy);
+               *rdev = wiphy_to_rdev((*wdev)->wiphy);
                /* 0 is the first index - add 1 to parse only once */
                cb->args[0] = (*rdev)->wiphy_idx + 1;
                cb->args[1] = (*wdev)->identifier;
@@ -497,7 +499,7 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
                        err = -ENODEV;
                        goto out_unlock;
                }
-               *rdev = wiphy_to_dev(wiphy);
+               *rdev = wiphy_to_rdev(wiphy);
                *wdev = NULL;
 
                list_for_each_entry(tmp, &(*rdev)->wdev_list, list) {
@@ -566,6 +568,13 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,
                                   struct ieee80211_channel *chan,
                                   bool large)
 {
+       /* Some channels must be completely excluded from the
+        * list to protect old user-space tools from breaking
+        */
+       if (!large && chan->flags &
+           (IEEE80211_CHAN_NO_10MHZ | IEEE80211_CHAN_NO_20MHZ))
+               return 0;
+
        if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_FREQ,
                        chan->center_freq))
                goto nla_put_failure;
@@ -613,6 +622,18 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,
                if ((chan->flags & IEEE80211_CHAN_NO_160MHZ) &&
                    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_160MHZ))
                        goto nla_put_failure;
+               if ((chan->flags & IEEE80211_CHAN_INDOOR_ONLY) &&
+                   nla_put_flag(msg, NL80211_FREQUENCY_ATTR_INDOOR_ONLY))
+                       goto nla_put_failure;
+               if ((chan->flags & IEEE80211_CHAN_GO_CONCURRENT) &&
+                   nla_put_flag(msg, NL80211_FREQUENCY_ATTR_GO_CONCURRENT))
+                       goto nla_put_failure;
+               if ((chan->flags & IEEE80211_CHAN_NO_20MHZ) &&
+                   nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_20MHZ))
+                       goto nla_put_failure;
+               if ((chan->flags & IEEE80211_CHAN_NO_10MHZ) &&
+                   nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_10MHZ))
+                       goto nla_put_failure;
        }
 
        if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
@@ -950,8 +971,10 @@ static int nl80211_put_iface_combinations(struct wiphy *wiphy,
                                c->max_interfaces))
                        goto nla_put_failure;
                if (large &&
-                   nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
-                               c->radar_detect_widths))
+                   (nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
+                               c->radar_detect_widths) ||
+                    nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_REGIONS,
+                               c->radar_detect_regions)))
                        goto nla_put_failure;
 
                nla_nest_end(msg, nl_combi);
@@ -1006,42 +1029,42 @@ static int nl80211_send_wowlan_tcp_caps(struct cfg80211_registered_device *rdev,
 }
 
 static int nl80211_send_wowlan(struct sk_buff *msg,
-                              struct cfg80211_registered_device *dev,
+                              struct cfg80211_registered_device *rdev,
                               bool large)
 {
        struct nlattr *nl_wowlan;
 
-       if (!dev->wiphy.wowlan)
+       if (!rdev->wiphy.wowlan)
                return 0;
 
        nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
        if (!nl_wowlan)
                return -ENOBUFS;
 
-       if (((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_ANY) &&
+       if (((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_ANY) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
-           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_DISCONNECT) &&
+           ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_DISCONNECT) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
-           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT) &&
+           ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
-           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
+           ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) ||
-           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
+           ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
-           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
+           ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
-           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
+           ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
-           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
+           ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
                return -ENOBUFS;
 
-       if (dev->wiphy.wowlan->n_patterns) {
+       if (rdev->wiphy.wowlan->n_patterns) {
                struct nl80211_pattern_support pat = {
-                       .max_patterns = dev->wiphy.wowlan->n_patterns,
-                       .min_pattern_len = dev->wiphy.wowlan->pattern_min_len,
-                       .max_pattern_len = dev->wiphy.wowlan->pattern_max_len,
-                       .max_pkt_offset = dev->wiphy.wowlan->max_pkt_offset,
+                       .max_patterns = rdev->wiphy.wowlan->n_patterns,
+                       .min_pattern_len = rdev->wiphy.wowlan->pattern_min_len,
+                       .max_pattern_len = rdev->wiphy.wowlan->pattern_max_len,
+                       .max_pkt_offset = rdev->wiphy.wowlan->max_pkt_offset,
                };
 
                if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
@@ -1049,7 +1072,7 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
                        return -ENOBUFS;
        }
 
-       if (large && nl80211_send_wowlan_tcp_caps(dev, msg))
+       if (large && nl80211_send_wowlan_tcp_caps(rdev, msg))
                return -ENOBUFS;
 
        nla_nest_end(msg, nl_wowlan);
@@ -1059,19 +1082,19 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
 #endif
 
 static int nl80211_send_coalesce(struct sk_buff *msg,
-                                struct cfg80211_registered_device *dev)
+                                struct cfg80211_registered_device *rdev)
 {
        struct nl80211_coalesce_rule_support rule;
 
-       if (!dev->wiphy.coalesce)
+       if (!rdev->wiphy.coalesce)
                return 0;
 
-       rule.max_rules = dev->wiphy.coalesce->n_rules;
-       rule.max_delay = dev->wiphy.coalesce->max_delay;
-       rule.pat.max_patterns = dev->wiphy.coalesce->n_patterns;
-       rule.pat.min_pattern_len = dev->wiphy.coalesce->pattern_min_len;
-       rule.pat.max_pattern_len = dev->wiphy.coalesce->pattern_max_len;
-       rule.pat.max_pkt_offset = dev->wiphy.coalesce->max_pkt_offset;
+       rule.max_rules = rdev->wiphy.coalesce->n_rules;
+       rule.max_delay = rdev->wiphy.coalesce->max_delay;
+       rule.pat.max_patterns = rdev->wiphy.coalesce->n_patterns;
+       rule.pat.min_pattern_len = rdev->wiphy.coalesce->pattern_min_len;
+       rule.pat.max_pattern_len = rdev->wiphy.coalesce->pattern_max_len;
+       rule.pat.max_pkt_offset = rdev->wiphy.coalesce->max_pkt_offset;
 
        if (nla_put(msg, NL80211_ATTR_COALESCE_RULE, sizeof(rule), &rule))
                return -ENOBUFS;
@@ -1202,7 +1225,8 @@ struct nl80211_dump_wiphy_state {
        bool split;
 };
 
-static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
+static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
+                             enum nl80211_commands cmd,
                              struct sk_buff *msg, u32 portid, u32 seq,
                              int flags, struct nl80211_dump_wiphy_state *state)
 {
@@ -1214,63 +1238,66 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
        struct ieee80211_channel *chan;
        int i;
        const struct ieee80211_txrx_stypes *mgmt_stypes =
-                               dev->wiphy.mgmt_stypes;
+                               rdev->wiphy.mgmt_stypes;
        u32 features;
 
-       hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY);
+       hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
        if (!hdr)
                return -ENOBUFS;
 
        if (WARN_ON(!state))
                return -EINVAL;
 
-       if (nla_put_u32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx) ||
+       if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
            nla_put_string(msg, NL80211_ATTR_WIPHY_NAME,
-                          wiphy_name(&dev->wiphy)) ||
+                          wiphy_name(&rdev->wiphy)) ||
            nla_put_u32(msg, NL80211_ATTR_GENERATION,
                        cfg80211_rdev_list_generation))
                goto nla_put_failure;
 
+       if (cmd != NL80211_CMD_NEW_WIPHY)
+               goto finish;
+
        switch (state->split_start) {
        case 0:
                if (nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
-                              dev->wiphy.retry_short) ||
+                              rdev->wiphy.retry_short) ||
                    nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
-                              dev->wiphy.retry_long) ||
+                              rdev->wiphy.retry_long) ||
                    nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
-                               dev->wiphy.frag_threshold) ||
+                               rdev->wiphy.frag_threshold) ||
                    nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
-                               dev->wiphy.rts_threshold) ||
+                               rdev->wiphy.rts_threshold) ||
                    nla_put_u8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
-                              dev->wiphy.coverage_class) ||
+                              rdev->wiphy.coverage_class) ||
                    nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
-                              dev->wiphy.max_scan_ssids) ||
+                              rdev->wiphy.max_scan_ssids) ||
                    nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
-                              dev->wiphy.max_sched_scan_ssids) ||
+                              rdev->wiphy.max_sched_scan_ssids) ||
                    nla_put_u16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
-                               dev->wiphy.max_scan_ie_len) ||
+                               rdev->wiphy.max_scan_ie_len) ||
                    nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
-                               dev->wiphy.max_sched_scan_ie_len) ||
+                               rdev->wiphy.max_sched_scan_ie_len) ||
                    nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS,
-                              dev->wiphy.max_match_sets))
+                              rdev->wiphy.max_match_sets))
                        goto nla_put_failure;
 
-               if ((dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) &&
+               if ((rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) &&
                    nla_put_flag(msg, NL80211_ATTR_SUPPORT_IBSS_RSN))
                        goto nla_put_failure;
-               if ((dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&
+               if ((rdev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&
                    nla_put_flag(msg, NL80211_ATTR_SUPPORT_MESH_AUTH))
                        goto nla_put_failure;
-               if ((dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
+               if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
                    nla_put_flag(msg, NL80211_ATTR_SUPPORT_AP_UAPSD))
                        goto nla_put_failure;
-               if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&
+               if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&
                    nla_put_flag(msg, NL80211_ATTR_ROAM_SUPPORT))
                        goto nla_put_failure;
-               if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
+               if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
                    nla_put_flag(msg, NL80211_ATTR_TDLS_SUPPORT))
                        goto nla_put_failure;
-               if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
+               if ((rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
                    nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
                        goto nla_put_failure;
                state->split_start++;
@@ -1278,35 +1305,35 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                        break;
        case 1:
                if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES,
-                           sizeof(u32) * dev->wiphy.n_cipher_suites,
-                           dev->wiphy.cipher_suites))
+                           sizeof(u32) * rdev->wiphy.n_cipher_suites,
+                           rdev->wiphy.cipher_suites))
                        goto nla_put_failure;
 
                if (nla_put_u8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
-                              dev->wiphy.max_num_pmkids))
+                              rdev->wiphy.max_num_pmkids))
                        goto nla_put_failure;
 
-               if ((dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
+               if ((rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
                    nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE))
                        goto nla_put_failure;
 
                if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
-                               dev->wiphy.available_antennas_tx) ||
+                               rdev->wiphy.available_antennas_tx) ||
                    nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
-                               dev->wiphy.available_antennas_rx))
+                               rdev->wiphy.available_antennas_rx))
                        goto nla_put_failure;
 
-               if ((dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) &&
+               if ((rdev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) &&
                    nla_put_u32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD,
-                               dev->wiphy.probe_resp_offload))
+                               rdev->wiphy.probe_resp_offload))
                        goto nla_put_failure;
 
-               if ((dev->wiphy.available_antennas_tx ||
-                    dev->wiphy.available_antennas_rx) &&
-                   dev->ops->get_antenna) {
+               if ((rdev->wiphy.available_antennas_tx ||
+                    rdev->wiphy.available_antennas_rx) &&
+                   rdev->ops->get_antenna) {
                        u32 tx_ant = 0, rx_ant = 0;
                        int res;
-                       res = rdev_get_antenna(dev, &tx_ant, &rx_ant);
+                       res = rdev_get_antenna(rdev, &tx_ant, &rx_ant);
                        if (!res) {
                                if (nla_put_u32(msg,
                                                NL80211_ATTR_WIPHY_ANTENNA_TX,
@@ -1323,7 +1350,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                        break;
        case 2:
                if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
-                                       dev->wiphy.interface_modes))
+                                       rdev->wiphy.interface_modes))
                                goto nla_put_failure;
                state->split_start++;
                if (state->split)
@@ -1337,7 +1364,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                     band < IEEE80211_NUM_BANDS; band++) {
                        struct ieee80211_supported_band *sband;
 
-                       sband = dev->wiphy.bands[band];
+                       sband = rdev->wiphy.bands[band];
 
                        if (!sband)
                                continue;
@@ -1414,7 +1441,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                i = 0;
 #define CMD(op, n)                                                     \
                 do {                                                   \
-                       if (dev->ops->op) {                             \
+                       if (rdev->ops->op) {                            \
                                i++;                                    \
                                if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) \
                                        goto nla_put_failure;           \
@@ -1438,32 +1465,32 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                CMD(set_pmksa, SET_PMKSA);
                CMD(del_pmksa, DEL_PMKSA);
                CMD(flush_pmksa, FLUSH_PMKSA);
-               if (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
+               if (rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
                        CMD(remain_on_channel, REMAIN_ON_CHANNEL);
                CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
                CMD(mgmt_tx, FRAME);
                CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
-               if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
+               if (rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
                        i++;
                        if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS))
                                goto nla_put_failure;
                }
-               if (dev->ops->set_monitor_channel || dev->ops->start_ap ||
-                   dev->ops->join_mesh) {
+               if (rdev->ops->set_monitor_channel || rdev->ops->start_ap ||
+                   rdev->ops->join_mesh) {
                        i++;
                        if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL))
                                goto nla_put_failure;
                }
                CMD(set_wds_peer, SET_WDS_PEER);
-               if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
+               if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
                        CMD(tdls_mgmt, TDLS_MGMT);
                        CMD(tdls_oper, TDLS_OPER);
                }
-               if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
+               if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
                        CMD(sched_scan_start, START_SCHED_SCAN);
                CMD(probe_client, PROBE_CLIENT);
                CMD(set_noack_map, SET_NOACK_MAP);
-               if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
+               if (rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
                        i++;
                        if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS))
                                goto nla_put_failure;
@@ -1473,7 +1500,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                if (state->split) {
                        CMD(crit_proto_start, CRIT_PROTOCOL_START);
                        CMD(crit_proto_stop, CRIT_PROTOCOL_STOP);
-                       if (dev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
+                       if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
                                CMD(channel_switch, CHANNEL_SWITCH);
                }
                CMD(set_qos_map, SET_QOS_MAP);
@@ -1484,13 +1511,13 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
 
 #undef CMD
 
-               if (dev->ops->connect || dev->ops->auth) {
+               if (rdev->ops->connect || rdev->ops->auth) {
                        i++;
                        if (nla_put_u32(msg, i, NL80211_CMD_CONNECT))
                                goto nla_put_failure;
                }
 
-               if (dev->ops->disconnect || dev->ops->deauth) {
+               if (rdev->ops->disconnect || rdev->ops->deauth) {
                        i++;
                        if (nla_put_u32(msg, i, NL80211_CMD_DISCONNECT))
                                goto nla_put_failure;
@@ -1501,14 +1528,14 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                if (state->split)
                        break;
        case 5:
-               if (dev->ops->remain_on_channel &&
-                   (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) &&
+               if (rdev->ops->remain_on_channel &&
+                   (rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) &&
                    nla_put_u32(msg,
                                NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
-                               dev->wiphy.max_remain_on_channel_duration))
+                               rdev->wiphy.max_remain_on_channel_duration))
                        goto nla_put_failure;
 
-               if ((dev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) &&
+               if ((rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) &&
                    nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK))
                        goto nla_put_failure;
 
@@ -1519,7 +1546,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                        break;
        case 6:
 #ifdef CONFIG_PM
-               if (nl80211_send_wowlan(msg, dev, state->split))
+               if (nl80211_send_wowlan(msg, rdev, state->split))
                        goto nla_put_failure;
                state->split_start++;
                if (state->split)
@@ -1529,10 +1556,10 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
 #endif
        case 7:
                if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
-                                       dev->wiphy.software_iftypes))
+                                       rdev->wiphy.software_iftypes))
                        goto nla_put_failure;
 
-               if (nl80211_put_iface_combinations(&dev->wiphy, msg,
+               if (nl80211_put_iface_combinations(&rdev->wiphy, msg,
                                                   state->split))
                        goto nla_put_failure;
 
@@ -1540,12 +1567,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                if (state->split)
                        break;
        case 8:
-               if ((dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
+               if ((rdev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
                    nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME,
-                               dev->wiphy.ap_sme_capa))
+                               rdev->wiphy.ap_sme_capa))
                        goto nla_put_failure;
 
-               features = dev->wiphy.features;
+               features = rdev->wiphy.features;
                /*
                 * We can only add the per-channel limit information if the
                 * dump is split, otherwise it makes it too big. Therefore
@@ -1556,16 +1583,16 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS, features))
                        goto nla_put_failure;
 
-               if (dev->wiphy.ht_capa_mod_mask &&
+               if (rdev->wiphy.ht_capa_mod_mask &&
                    nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK,
-                           sizeof(*dev->wiphy.ht_capa_mod_mask),
-                           dev->wiphy.ht_capa_mod_mask))
+                           sizeof(*rdev->wiphy.ht_capa_mod_mask),
+                           rdev->wiphy.ht_capa_mod_mask))
                        goto nla_put_failure;
 
-               if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME &&
-                   dev->wiphy.max_acl_mac_addrs &&
+               if (rdev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME &&
+                   rdev->wiphy.max_acl_mac_addrs &&
                    nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX,
-                               dev->wiphy.max_acl_mac_addrs))
+                               rdev->wiphy.max_acl_mac_addrs))
                        goto nla_put_failure;
 
                /*
@@ -1581,41 +1608,41 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                state->split_start++;
                break;
        case 9:
-               if (dev->wiphy.extended_capabilities &&
+               if (rdev->wiphy.extended_capabilities &&
                    (nla_put(msg, NL80211_ATTR_EXT_CAPA,
-                            dev->wiphy.extended_capabilities_len,
-                            dev->wiphy.extended_capabilities) ||
+                            rdev->wiphy.extended_capabilities_len,
+                            rdev->wiphy.extended_capabilities) ||
                     nla_put(msg, NL80211_ATTR_EXT_CAPA_MASK,
-                            dev->wiphy.extended_capabilities_len,
-                            dev->wiphy.extended_capabilities_mask)))
+                            rdev->wiphy.extended_capabilities_len,
+                            rdev->wiphy.extended_capabilities_mask)))
                        goto nla_put_failure;
 
-               if (dev->wiphy.vht_capa_mod_mask &&
+               if (rdev->wiphy.vht_capa_mod_mask &&
                    nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK,
-                           sizeof(*dev->wiphy.vht_capa_mod_mask),
-                           dev->wiphy.vht_capa_mod_mask))
+                           sizeof(*rdev->wiphy.vht_capa_mod_mask),
+                           rdev->wiphy.vht_capa_mod_mask))
                        goto nla_put_failure;
 
                state->split_start++;
                break;
        case 10:
-               if (nl80211_send_coalesce(msg, dev))
+               if (nl80211_send_coalesce(msg, rdev))
                        goto nla_put_failure;
 
-               if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) &&
+               if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) &&
                    (nla_put_flag(msg, NL80211_ATTR_SUPPORT_5_MHZ) ||
                     nla_put_flag(msg, NL80211_ATTR_SUPPORT_10_MHZ)))
                        goto nla_put_failure;
 
-               if (dev->wiphy.max_ap_assoc_sta &&
+               if (rdev->wiphy.max_ap_assoc_sta &&
                    nla_put_u32(msg, NL80211_ATTR_MAX_AP_ASSOC_STA,
-                               dev->wiphy.max_ap_assoc_sta))
+                               rdev->wiphy.max_ap_assoc_sta))
                        goto nla_put_failure;
 
                state->split_start++;
                break;
        case 11:
-               if (dev->wiphy.n_vendor_commands) {
+               if (rdev->wiphy.n_vendor_commands) {
                        const struct nl80211_vendor_cmd_info *info;
                        struct nlattr *nested;
 
@@ -1623,15 +1650,15 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                        if (!nested)
                                goto nla_put_failure;
 
-                       for (i = 0; i < dev->wiphy.n_vendor_commands; i++) {
-                               info = &dev->wiphy.vendor_commands[i].info;
+                       for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) {
+                               info = &rdev->wiphy.vendor_commands[i].info;
                                if (nla_put(msg, i + 1, sizeof(*info), info))
                                        goto nla_put_failure;
                        }
                        nla_nest_end(msg, nested);
                }
 
-               if (dev->wiphy.n_vendor_events) {
+               if (rdev->wiphy.n_vendor_events) {
                        const struct nl80211_vendor_cmd_info *info;
                        struct nlattr *nested;
 
@@ -1640,18 +1667,26 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                        if (!nested)
                                goto nla_put_failure;
 
-                       for (i = 0; i < dev->wiphy.n_vendor_events; i++) {
-                               info = &dev->wiphy.vendor_events[i];
+                       for (i = 0; i < rdev->wiphy.n_vendor_events; i++) {
+                               info = &rdev->wiphy.vendor_events[i];
                                if (nla_put(msg, i + 1, sizeof(*info), info))
                                        goto nla_put_failure;
                        }
                        nla_nest_end(msg, nested);
                }
+               state->split_start++;
+               break;
+       case 12:
+               if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH &&
+                   nla_put_u8(msg, NL80211_ATTR_MAX_CSA_COUNTERS,
+                              rdev->wiphy.max_num_csa_counters))
+                       goto nla_put_failure;
 
                /* done */
                state->split_start = 0;
                break;
        }
+ finish:
        return genlmsg_end(msg, hdr);
 
  nla_put_failure:
@@ -1684,7 +1719,7 @@ static int nl80211_dump_wiphy_parse(struct sk_buff *skb,
                if (!netdev)
                        return -ENODEV;
                if (netdev->ieee80211_ptr) {
-                       rdev = wiphy_to_dev(
+                       rdev = wiphy_to_rdev(
                                netdev->ieee80211_ptr->wiphy);
                        state->filter_wiphy = rdev->wiphy_idx;
                }
@@ -1697,7 +1732,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
 {
        int idx = 0, ret;
        struct nl80211_dump_wiphy_state *state = (void *)cb->args[0];
-       struct cfg80211_registered_device *dev;
+       struct cfg80211_registered_device *rdev;
 
        rtnl_lock();
        if (!state) {
@@ -1716,17 +1751,18 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
                cb->args[0] = (long)state;
        }
 
-       list_for_each_entry(dev, &cfg80211_rdev_list, list) {
-               if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
+       list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+               if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
                        continue;
                if (++idx <= state->start)
                        continue;
                if (state->filter_wiphy != -1 &&
-                   state->filter_wiphy != dev->wiphy_idx)
+                   state->filter_wiphy != rdev->wiphy_idx)
                        continue;
                /* attempt to fit multiple wiphy data chunks into the skb */
                do {
-                       ret = nl80211_send_wiphy(dev, skb,
+                       ret = nl80211_send_wiphy(rdev, NL80211_CMD_NEW_WIPHY,
+                                                skb,
                                                 NETLINK_CB(cb->skb).portid,
                                                 cb->nlh->nlmsg_seq,
                                                 NLM_F_MULTI, state);
@@ -1774,14 +1810,15 @@ static int nl80211_dump_wiphy_done(struct netlink_callback *cb)
 static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
 {
        struct sk_buff *msg;
-       struct cfg80211_registered_device *dev = info->user_ptr[0];
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct nl80211_dump_wiphy_state state = {};
 
        msg = nlmsg_new(4096, GFP_KERNEL);
        if (!msg)
                return -ENOMEM;
 
-       if (nl80211_send_wiphy(dev, msg, info->snd_portid, info->snd_seq, 0,
+       if (nl80211_send_wiphy(rdev, NL80211_CMD_NEW_WIPHY, msg,
+                              info->snd_portid, info->snd_seq, 0,
                               &state) < 0) {
                nlmsg_free(msg);
                return -ENOBUFS;
@@ -1908,18 +1945,20 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
 }
 
 static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
-                                struct wireless_dev *wdev,
+                                struct net_device *dev,
                                 struct genl_info *info)
 {
        struct cfg80211_chan_def chandef;
        int result;
        enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR;
+       struct wireless_dev *wdev = NULL;
 
-       if (wdev)
-               iftype = wdev->iftype;
-
+       if (dev)
+               wdev = dev->ieee80211_ptr;
        if (!nl80211_can_set_dev_channel(wdev))
                return -EOPNOTSUPP;
+       if (wdev)
+               iftype = wdev->iftype;
 
        result = nl80211_parse_chandef(rdev, info, &chandef);
        if (result)
@@ -1928,14 +1967,27 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
        switch (iftype) {
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_P2P_GO:
-               if (wdev->beacon_interval) {
-                       result = -EBUSY;
-                       break;
-               }
-               if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef)) {
+               if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, iftype)) {
                        result = -EINVAL;
                        break;
                }
+               if (wdev->beacon_interval) {
+                       if (!dev || !rdev->ops->set_ap_chanwidth ||
+                           !(rdev->wiphy.features &
+                             NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)) {
+                               result = -EBUSY;
+                               break;
+                       }
+
+                       /* Only allow dynamic channel width changes */
+                       if (chandef.chan != wdev->preset_chandef.chan) {
+                               result = -EBUSY;
+                               break;
+                       }
+                       result = rdev_set_ap_chanwidth(rdev, dev, &chandef);
+                       if (result)
+                               break;
+               }
                wdev->preset_chandef = chandef;
                result = 0;
                break;
@@ -1957,7 +2009,7 @@ static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *netdev = info->user_ptr[1];
 
-       return __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info);
+       return __nl80211_set_channel(rdev, netdev, info);
 }
 
 static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)
@@ -2013,7 +2065,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 
                netdev = __dev_get_by_index(genl_info_net(info), ifindex);
                if (netdev && netdev->ieee80211_ptr)
-                       rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
+                       rdev = wiphy_to_rdev(netdev->ieee80211_ptr->wiphy);
                else
                        netdev = NULL;
        }
@@ -2079,9 +2131,10 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
-               result = __nl80211_set_channel(rdev,
-                               nl80211_can_set_dev_channel(wdev) ? wdev : NULL,
-                               info);
+               result = __nl80211_set_channel(
+                       rdev,
+                       nl80211_can_set_dev_channel(wdev) ? netdev : NULL,
+                       info);
                if (result)
                        return result;
        }
@@ -2229,7 +2282,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 static inline u64 wdev_id(struct wireless_dev *wdev)
 {
        return (u64)wdev->identifier |
-              ((u64)wiphy_to_dev(wdev->wiphy)->wiphy_idx << 32);
+              ((u64)wiphy_to_rdev(wdev->wiphy)->wiphy_idx << 32);
 }
 
 static int nl80211_send_chandef(struct sk_buff *msg,
@@ -2355,7 +2408,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
 static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
 {
        struct sk_buff *msg;
-       struct cfg80211_registered_device *dev = info->user_ptr[0];
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct wireless_dev *wdev = info->user_ptr[1];
 
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
@@ -2363,7 +2416,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
                return -ENOMEM;
 
        if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
-                              dev, wdev) < 0) {
+                              rdev, wdev) < 0) {
                nlmsg_free(msg);
                return -ENOBUFS;
        }
@@ -2514,6 +2567,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
        enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
        u32 flags;
 
+       /* to avoid failing a new interface creation due to pending removal */
+       cfg80211_destroy_ifaces(rdev);
+
        memset(&params, 0, sizeof(params));
 
        if (!info->attrs[NL80211_ATTR_IFNAME])
@@ -2563,6 +2619,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
                return PTR_ERR(wdev);
        }
 
+       if (info->attrs[NL80211_ATTR_IFACE_SOCKET_OWNER])
+               wdev->owner_nlportid = info->snd_portid;
+
        switch (type) {
        case NL80211_IFTYPE_MESH_POINT:
                if (!info->attrs[NL80211_ATTR_MESH_ID])
@@ -3142,7 +3201,6 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_ap_settings params;
        int err;
-       u8 radar_detect_width = 0;
 
        if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
            dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
@@ -3258,24 +3316,10 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
        } else if (!nl80211_get_ap_channel(rdev, &params))
                return -EINVAL;
 
-       if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
+       if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef,
+                                    wdev->iftype))
                return -EINVAL;
 
-       err = cfg80211_chandef_dfs_required(wdev->wiphy, &params.chandef);
-       if (err < 0)
-               return err;
-       if (err) {
-               radar_detect_width = BIT(params.chandef.width);
-               params.radar_required = true;
-       }
-
-       err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
-                                          params.chandef.chan,
-                                          CHAN_MODE_SHARED,
-                                          radar_detect_width);
-       if (err)
-               return err;
-
        if (info->attrs[NL80211_ATTR_ACL_POLICY]) {
                params.acl = parse_acl_data(&rdev->wiphy, info);
                if (IS_ERR(params.acl))
@@ -3613,6 +3657,10 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
            nla_put_u32(msg, NL80211_STA_INFO_TX_FAILED,
                        sinfo->tx_failed))
                goto nla_put_failure;
+       if ((sinfo->filled & STATION_INFO_EXPECTED_THROUGHPUT) &&
+           nla_put_u32(msg, NL80211_STA_INFO_EXPECTED_THROUGHPUT,
+                       sinfo->expected_throughput))
+               goto nla_put_failure;
        if ((sinfo->filled & STATION_INFO_BEACON_LOSS_COUNT) &&
            nla_put_u32(msg, NL80211_STA_INFO_BEACON_LOSS,
                        sinfo->beacon_loss_count))
@@ -3675,13 +3723,13 @@ static int nl80211_dump_station(struct sk_buff *skb,
                                struct netlink_callback *cb)
 {
        struct station_info sinfo;
-       struct cfg80211_registered_device *dev;
+       struct cfg80211_registered_device *rdev;
        struct wireless_dev *wdev;
        u8 mac_addr[ETH_ALEN];
        int sta_idx = cb->args[2];
        int err;
 
-       err = nl80211_prepare_wdev_dump(skb, cb, &dev, &wdev);
+       err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
        if (err)
                return err;
 
@@ -3690,14 +3738,14 @@ static int nl80211_dump_station(struct sk_buff *skb,
                goto out_err;
        }
 
-       if (!dev->ops->dump_station) {
+       if (!rdev->ops->dump_station) {
                err = -EOPNOTSUPP;
                goto out_err;
        }
 
        while (1) {
                memset(&sinfo, 0, sizeof(sinfo));
-               err = rdev_dump_station(dev, wdev->netdev, sta_idx,
+               err = rdev_dump_station(rdev, wdev->netdev, sta_idx,
                                        mac_addr, &sinfo);
                if (err == -ENOENT)
                        break;
@@ -3707,7 +3755,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
                if (nl80211_send_station(skb,
                                NETLINK_CB(cb->skb).portid,
                                cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                               dev, wdev->netdev, mac_addr,
+                               rdev, wdev->netdev, mac_addr,
                                &sinfo) < 0)
                        goto out;
 
@@ -3719,7 +3767,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
        cb->args[2] = sta_idx;
        err = skb->len;
  out_err:
-       nl80211_finish_wdev_dump(dev);
+       nl80211_finish_wdev_dump(rdev);
 
        return err;
 }
@@ -4380,18 +4428,18 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
                              struct netlink_callback *cb)
 {
        struct mpath_info pinfo;
-       struct cfg80211_registered_device *dev;
+       struct cfg80211_registered_device *rdev;
        struct wireless_dev *wdev;
        u8 dst[ETH_ALEN];
        u8 next_hop[ETH_ALEN];
        int path_idx = cb->args[2];
        int err;
 
-       err = nl80211_prepare_wdev_dump(skb, cb, &dev, &wdev);
+       err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
        if (err)
                return err;
 
-       if (!dev->ops->dump_mpath) {
+       if (!rdev->ops->dump_mpath) {
                err = -EOPNOTSUPP;
                goto out_err;
        }
@@ -4402,7 +4450,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
        }
 
        while (1) {
-               err = rdev_dump_mpath(dev, wdev->netdev, path_idx, dst,
+               err = rdev_dump_mpath(rdev, wdev->netdev, path_idx, dst,
                                      next_hop, &pinfo);
                if (err == -ENOENT)
                        break;
@@ -4423,7 +4471,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
        cb->args[2] = path_idx;
        err = skb->len;
  out_err:
-       nl80211_finish_wdev_dump(dev);
+       nl80211_finish_wdev_dump(rdev);
        return err;
 }
 
@@ -4663,7 +4711,6 @@ static int parse_reg_rule(struct nlattr *tb[],
 
 static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
 {
-       int r;
        char *data = NULL;
        enum nl80211_user_reg_hint_type user_reg_hint_type;
 
@@ -4676,11 +4723,6 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
        if (unlikely(!rcu_access_pointer(cfg80211_regdomain)))
                return -EINPROGRESS;
 
-       if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
-               return -EINVAL;
-
-       data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
-
        if (info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE])
                user_reg_hint_type =
                  nla_get_u32(info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]);
@@ -4690,14 +4732,16 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
        switch (user_reg_hint_type) {
        case NL80211_USER_REG_HINT_USER:
        case NL80211_USER_REG_HINT_CELL_BASE:
-               break;
+               if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
+                       return -EINVAL;
+
+               data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
+               return regulatory_hint_user(data, user_reg_hint_type);
+       case NL80211_USER_REG_HINT_INDOOR:
+               return regulatory_hint_indoor_user();
        default:
                return -EINVAL;
        }
-
-       r = regulatory_hint_user(data, user_reg_hint_type);
-
-       return r;
 }
 
 static int nl80211_get_mesh_config(struct sk_buff *skb,
@@ -5796,7 +5840,8 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
        if (wdev->cac_started)
                return -EBUSY;
 
-       err = cfg80211_chandef_dfs_required(wdev->wiphy, &chandef);
+       err = cfg80211_chandef_dfs_required(wdev->wiphy, &chandef,
+                                           wdev->iftype);
        if (err < 0)
                return err;
 
@@ -5809,12 +5854,6 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
        if (!rdev->ops->start_radar_detection)
                return -EOPNOTSUPP;
 
-       err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
-                                          chandef.chan, CHAN_MODE_SHARED,
-                                          BIT(chandef.width));
-       if (err)
-               return err;
-
        cac_time_ms = cfg80211_chandef_dfs_cac_time(&rdev->wiphy, &chandef);
        if (WARN_ON(!cac_time_ms))
                cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
@@ -5843,6 +5882,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
        u8 radar_detect_width = 0;
        int err;
        bool need_new_beacon = false;
+       int len, i;
 
        if (!rdev->ops->channel_switch ||
            !(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))
@@ -5901,26 +5941,55 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
        if (!csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON])
                return -EINVAL;
 
-       params.counter_offset_beacon =
-               nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
-       if (params.counter_offset_beacon >= params.beacon_csa.tail_len)
+       len = nla_len(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
+       if (!len || (len % sizeof(u16)))
                return -EINVAL;
 
-       /* sanity check - counters should be the same */
-       if (params.beacon_csa.tail[params.counter_offset_beacon] !=
-           params.count)
+       params.n_counter_offsets_beacon = len / sizeof(u16);
+       if (rdev->wiphy.max_num_csa_counters &&
+           (params.n_counter_offsets_beacon >
+            rdev->wiphy.max_num_csa_counters))
                return -EINVAL;
 
+       params.counter_offsets_beacon =
+               nla_data(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
+
+       /* sanity checks - counters should fit and be the same */
+       for (i = 0; i < params.n_counter_offsets_beacon; i++) {
+               u16 offset = params.counter_offsets_beacon[i];
+
+               if (offset >= params.beacon_csa.tail_len)
+                       return -EINVAL;
+
+               if (params.beacon_csa.tail[offset] != params.count)
+                       return -EINVAL;
+       }
+
        if (csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]) {
-               params.counter_offset_presp =
-                       nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
-               if (params.counter_offset_presp >=
-                   params.beacon_csa.probe_resp_len)
+               len = nla_len(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
+               if (!len || (len % sizeof(u16)))
                        return -EINVAL;
 
-               if (params.beacon_csa.probe_resp[params.counter_offset_presp] !=
-                   params.count)
+               params.n_counter_offsets_presp = len / sizeof(u16);
+               if (rdev->wiphy.max_num_csa_counters &&
+                   (params.n_counter_offsets_beacon >
+                    rdev->wiphy.max_num_csa_counters))
                        return -EINVAL;
+
+               params.counter_offsets_presp =
+                       nla_data(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
+
+               /* sanity checks - counters should fit and be the same */
+               for (i = 0; i < params.n_counter_offsets_presp; i++) {
+                       u16 offset = params.counter_offsets_presp[i];
+
+                       if (offset >= params.beacon_csa.probe_resp_len)
+                               return -EINVAL;
+
+                       if (params.beacon_csa.probe_resp[offset] !=
+                           params.count)
+                               return -EINVAL;
+               }
        }
 
 skip_beacons:
@@ -5928,27 +5997,25 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
        if (err)
                return err;
 
-       if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
+       if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef,
+                                    wdev->iftype))
                return -EINVAL;
 
-       switch (dev->ieee80211_ptr->iftype) {
-       case NL80211_IFTYPE_AP:
-       case NL80211_IFTYPE_P2P_GO:
-       case NL80211_IFTYPE_ADHOC:
-       case NL80211_IFTYPE_MESH_POINT:
-               err = cfg80211_chandef_dfs_required(wdev->wiphy,
-                                                   &params.chandef);
-               if (err < 0)
-                       return err;
-               if (err) {
-                       radar_detect_width = BIT(params.chandef.width);
-                       params.radar_required = true;
-               }
-               break;
-       default:
-               break;
+       err = cfg80211_chandef_dfs_required(wdev->wiphy,
+                                           &params.chandef,
+                                           wdev->iftype);
+       if (err < 0)
+               return err;
+
+       if (err > 0) {
+               radar_detect_width = BIT(params.chandef.width);
+               params.radar_required = true;
        }
 
+       /* TODO: I left this here for now.  With channel switch, the
+        * verification is a bit more complicated, because we only do
+        * it later when the channel switch really happens.
+        */
        err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
                                           params.chandef.chan,
                                           CHAN_MODE_SHARED,
@@ -6175,12 +6242,12 @@ static int nl80211_dump_survey(struct sk_buff *skb,
                        struct netlink_callback *cb)
 {
        struct survey_info survey;
-       struct cfg80211_registered_device *dev;
+       struct cfg80211_registered_device *rdev;
        struct wireless_dev *wdev;
        int survey_idx = cb->args[2];
        int res;
 
-       res = nl80211_prepare_wdev_dump(skb, cb, &dev, &wdev);
+       res = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
        if (res)
                return res;
 
@@ -6189,7 +6256,7 @@ static int nl80211_dump_survey(struct sk_buff *skb,
                goto out_err;
        }
 
-       if (!dev->ops->dump_survey) {
+       if (!rdev->ops->dump_survey) {
                res = -EOPNOTSUPP;
                goto out_err;
        }
@@ -6197,7 +6264,7 @@ static int nl80211_dump_survey(struct sk_buff *skb,
        while (1) {
                struct ieee80211_channel *chan;
 
-               res = rdev_dump_survey(dev, wdev->netdev, survey_idx, &survey);
+               res = rdev_dump_survey(rdev, wdev->netdev, survey_idx, &survey);
                if (res == -ENOENT)
                        break;
                if (res)
@@ -6209,7 +6276,7 @@ static int nl80211_dump_survey(struct sk_buff *skb,
                        goto out;
                }
 
-               chan = ieee80211_get_channel(&dev->wiphy,
+               chan = ieee80211_get_channel(&rdev->wiphy,
                                             survey.channel->center_freq);
                if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
                        survey_idx++;
@@ -6228,7 +6295,7 @@ static int nl80211_dump_survey(struct sk_buff *skb,
        cb->args[2] = survey_idx;
        res = skb->len;
  out_err:
-       nl80211_finish_wdev_dump(dev);
+       nl80211_finish_wdev_dump(rdev);
        return res;
 }
 
@@ -6704,7 +6771,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
        if (err)
                return err;
 
-       if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef))
+       if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef,
+                                    NL80211_IFTYPE_ADHOC))
                return -EINVAL;
 
        switch (ibss.chandef.width) {
@@ -6879,7 +6947,7 @@ struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy,
                                           int vendor_event_idx,
                                           int approxlen, gfp_t gfp)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        const struct nl80211_vendor_cmd_info *info;
 
        switch (cmd) {
@@ -7767,6 +7835,27 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
        if (!chandef.chan && params.offchan)
                return -EINVAL;
 
+       params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]);
+       params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]);
+
+       if (info->attrs[NL80211_ATTR_CSA_C_OFFSETS_TX]) {
+               int len = nla_len(info->attrs[NL80211_ATTR_CSA_C_OFFSETS_TX]);
+               int i;
+
+               if (len % sizeof(u16))
+                       return -EINVAL;
+
+               params.n_csa_offsets = len / sizeof(u16);
+               params.csa_offsets =
+                       nla_data(info->attrs[NL80211_ATTR_CSA_C_OFFSETS_TX]);
+
+               /* check that all the offsets fit the frame */
+               for (i = 0; i < params.n_csa_offsets; i++) {
+                       if (params.csa_offsets[i] >= params.len)
+                               return -EINVAL;
+               }
+       }
+
        if (!params.dont_wait_for_ack) {
                msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
                if (!msg)
@@ -7780,8 +7869,6 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
                }
        }
 
-       params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]);
-       params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]);
        params.chan = chandef.chan;
        err = cfg80211_mlme_mgmt_tx(rdev, wdev, &params, &cookie);
        if (err)
@@ -8478,6 +8565,8 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
 
                nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
                                    rem) {
+                       u8 *mask_pat;
+
                        nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
                                  nla_len(pat), NULL);
                        err = -EINVAL;
@@ -8501,19 +8590,18 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
                                goto error;
                        new_triggers.patterns[i].pkt_offset = pkt_offset;
 
-                       new_triggers.patterns[i].mask =
-                               kmalloc(mask_len + pat_len, GFP_KERNEL);
-                       if (!new_triggers.patterns[i].mask) {
+                       mask_pat = kmalloc(mask_len + pat_len, GFP_KERNEL);
+                       if (!mask_pat) {
                                err = -ENOMEM;
                                goto error;
                        }
-                       new_triggers.patterns[i].pattern =
-                               new_triggers.patterns[i].mask + mask_len;
-                       memcpy(new_triggers.patterns[i].mask,
-                              nla_data(pat_tb[NL80211_PKTPAT_MASK]),
+                       new_triggers.patterns[i].mask = mask_pat;
+                       memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_MASK]),
                               mask_len);
+                       mask_pat += mask_len;
+                       new_triggers.patterns[i].pattern = mask_pat;
                        new_triggers.patterns[i].pattern_len = pat_len;
-                       memcpy(new_triggers.patterns[i].pattern,
+                       memcpy(mask_pat,
                               nla_data(pat_tb[NL80211_PKTPAT_PATTERN]),
                               pat_len);
                        i++;
@@ -8705,6 +8793,8 @@ static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev,
 
        nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
                            rem) {
+               u8 *mask_pat;
+
                nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
                          nla_len(pat), NULL);
                if (!pat_tb[NL80211_PKTPAT_MASK] ||
@@ -8726,17 +8816,19 @@ static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev,
                        return -EINVAL;
                new_rule->patterns[i].pkt_offset = pkt_offset;
 
-               new_rule->patterns[i].mask =
-                       kmalloc(mask_len + pat_len, GFP_KERNEL);
-               if (!new_rule->patterns[i].mask)
+               mask_pat = kmalloc(mask_len + pat_len, GFP_KERNEL);
+               if (!mask_pat)
                        return -ENOMEM;
-               new_rule->patterns[i].pattern =
-                       new_rule->patterns[i].mask + mask_len;
-               memcpy(new_rule->patterns[i].mask,
-                      nla_data(pat_tb[NL80211_PKTPAT_MASK]), mask_len);
+
+               new_rule->patterns[i].mask = mask_pat;
+               memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_MASK]),
+                      mask_len);
+
+               mask_pat += mask_len;
+               new_rule->patterns[i].pattern = mask_pat;
                new_rule->patterns[i].pattern_len = pat_len;
-               memcpy(new_rule->patterns[i].pattern,
-                      nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), pat_len);
+               memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_PATTERN]),
+                      pat_len);
                i++;
        }
 
@@ -8981,9 +9073,8 @@ static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info)
        if (wdev->p2p_started)
                return 0;
 
-       err = cfg80211_can_add_interface(rdev, wdev->iftype);
-       if (err)
-               return err;
+       if (rfkill_blocked(rdev->rfkill))
+               return -ERFKILL;
 
        err = rdev_start_p2p_device(rdev, wdev);
        if (err)
@@ -9192,7 +9283,7 @@ struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy,
                                           enum nl80211_attrs attr,
                                           int approxlen)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        if (WARN_ON(!rdev->cur_cmd_info))
                return NULL;
@@ -9316,7 +9407,7 @@ static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
                }
 
                dev = wdev->netdev;
-               rdev = wiphy_to_dev(wdev->wiphy);
+               rdev = wiphy_to_rdev(wdev->wiphy);
 
                if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
                        if (!dev) {
@@ -10017,16 +10108,20 @@ static const struct genl_ops nl80211_ops[] = {
 
 /* notification functions */
 
-void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
+void nl80211_notify_wiphy(struct cfg80211_registered_device *rdev,
+                         enum nl80211_commands cmd)
 {
        struct sk_buff *msg;
        struct nl80211_dump_wiphy_state state = {};
 
+       WARN_ON(cmd != NL80211_CMD_NEW_WIPHY &&
+               cmd != NL80211_CMD_DEL_WIPHY);
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return;
 
-       if (nl80211_send_wiphy(rdev, msg, 0, 0, 0, &state) < 0) {
+       if (nl80211_send_wiphy(rdev, cmd, msg, 0, 0, 0, &state) < 0) {
                nlmsg_free(msg);
                return;
        }
@@ -10345,7 +10440,7 @@ void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf,
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        const struct ieee80211_mgmt *mgmt = (void *)buf;
        u32 cmd;
 
@@ -10567,7 +10662,7 @@ void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,
                                        const u8* ie, u8 ie_len, gfp_t gfp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
 
@@ -10747,7 +10842,7 @@ void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie,
                               unsigned int duration, gfp_t gfp)
 {
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        trace_cfg80211_ready_on_channel(wdev, cookie, chan, duration);
        nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
@@ -10761,7 +10856,7 @@ void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie,
                                        gfp_t gfp)
 {
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan);
        nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
@@ -10773,7 +10868,7 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
                      struct station_info *sinfo, gfp_t gfp)
 {
        struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct sk_buff *msg;
 
        trace_cfg80211_new_sta(dev, mac_addr, sinfo);
@@ -10796,7 +10891,7 @@ EXPORT_SYMBOL(cfg80211_new_sta);
 void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)
 {
        struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct sk_buff *msg;
        void *hdr;
 
@@ -10833,7 +10928,7 @@ void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
                          gfp_t gfp)
 {
        struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct sk_buff *msg;
        void *hdr;
 
@@ -10868,7 +10963,7 @@ static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
                                       const u8 *addr, gfp_t gfp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
        u32 nlportid = ACCESS_ONCE(wdev->ap_unexpected_nlportid);
@@ -10988,7 +11083,7 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
                             const u8 *buf, size_t len, bool ack, gfp_t gfp)
 {
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct net_device *netdev = wdev->netdev;
        struct sk_buff *msg;
        void *hdr;
@@ -11032,7 +11127,7 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct sk_buff *msg;
        struct nlattr *pinfoattr;
        void *hdr;
@@ -11124,7 +11219,7 @@ void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        trace_cfg80211_gtk_rekey_notify(dev, bssid);
        nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp);
@@ -11182,7 +11277,7 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        trace_cfg80211_pmksa_candidate_notify(dev, index, bssid, preauth);
        nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);
@@ -11229,7 +11324,7 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        ASSERT_WDEV_LOCK(wdev);
 
@@ -11253,7 +11348,7 @@ void cfg80211_cqm_txe_notify(struct net_device *dev,
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct sk_buff *msg;
        struct nlattr *pinfoattr;
        void *hdr;
@@ -11353,7 +11448,7 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev,
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct sk_buff *msg;
        struct nlattr *pinfoattr;
        void *hdr;
@@ -11400,7 +11495,7 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
                           u64 cookie, bool acked, gfp_t gfp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
 
@@ -11440,7 +11535,7 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy,
                                 const u8 *frame, size_t len,
                                 int freq, int sig_dbm)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct sk_buff *msg;
        void *hdr;
        struct cfg80211_beacon_registration *reg;
@@ -11487,7 +11582,7 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
                                   struct cfg80211_wowlan_wakeup *wakeup,
                                   gfp_t gfp)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
        int size = 200;
@@ -11597,7 +11692,7 @@ void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
                                u16 reason_code, gfp_t gfp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
 
@@ -11649,9 +11744,15 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
        rcu_read_lock();
 
        list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
-               list_for_each_entry_rcu(wdev, &rdev->wdev_list, list)
+               bool schedule_destroy_work = false;
+
+               list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) {
                        cfg80211_mlme_unregister_socket(wdev, notify->portid);
 
+                       if (wdev->owner_nlportid == notify->portid)
+                               schedule_destroy_work = true;
+               }
+
                spin_lock_bh(&rdev->beacon_registrations_lock);
                list_for_each_entry_safe(reg, tmp, &rdev->beacon_registrations,
                                         list) {
@@ -11662,11 +11763,24 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
                        }
                }
                spin_unlock_bh(&rdev->beacon_registrations_lock);
+
+               if (schedule_destroy_work) {
+                       struct cfg80211_iface_destroy *destroy;
+
+                       destroy = kzalloc(sizeof(*destroy), GFP_ATOMIC);
+                       if (destroy) {
+                               destroy->nlportid = notify->portid;
+                               spin_lock(&rdev->destroy_list_lock);
+                               list_add(&destroy->list, &rdev->destroy_list);
+                               spin_unlock(&rdev->destroy_list_lock);
+                               schedule_work(&rdev->destroy_work);
+                       }
+               }
        }
 
        rcu_read_unlock();
 
-       return NOTIFY_DONE;
+       return NOTIFY_OK;
 }
 
 static struct notifier_block nl80211_netlink_notifier = {
@@ -11677,7 +11791,7 @@ void cfg80211_ft_event(struct net_device *netdev,
                       struct cfg80211_ft_event_params *ft_event)
 {
        struct wiphy *wiphy = netdev->ieee80211_ptr->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct sk_buff *msg;
        void *hdr;
 
@@ -11724,7 +11838,7 @@ void cfg80211_crit_proto_stopped(struct wireless_dev *wdev, gfp_t gfp)
        void *hdr;
        u32 nlportid;
 
-       rdev = wiphy_to_dev(wdev->wiphy);
+       rdev = wiphy_to_rdev(wdev->wiphy);
        if (!rdev->crit_proto_nlportid)
                return;
 
@@ -11759,7 +11873,7 @@ EXPORT_SYMBOL(cfg80211_crit_proto_stopped);
 void nl80211_send_ap_stopped(struct wireless_dev *wdev)
 {
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct sk_buff *msg;
        void *hdr;
 
index 1e6df9630f42f11f815578a4bbc73fc7b037017d..49c9a482dd1248565f0b7f333ecacf5f027ff650 100644 (file)
@@ -5,7 +5,8 @@
 
 int nl80211_init(void);
 void nl80211_exit(void);
-void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev);
+void nl80211_notify_wiphy(struct cfg80211_registered_device *rdev,
+                         enum nl80211_commands cmd);
 void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
                             struct wireless_dev *wdev);
 struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev,
index 74d97d33c938e8250ef300c2c3fb82d2a39b63b6..d95bbe34813833d04ed618fcd6ca85700718216b 100644 (file)
@@ -199,7 +199,7 @@ static inline int rdev_change_station(struct cfg80211_registered_device *rdev,
 }
 
 static inline int rdev_get_station(struct cfg80211_registered_device *rdev,
-                                  struct net_device *dev, u8 *mac,
+                                  struct net_device *dev, const u8 *mac,
                                   struct station_info *sinfo)
 {
        int ret;
@@ -950,4 +950,17 @@ static inline int rdev_set_qos_map(struct cfg80211_registered_device *rdev,
        return ret;
 }
 
+static inline int
+rdev_set_ap_chanwidth(struct cfg80211_registered_device *rdev,
+                     struct net_device *dev, struct cfg80211_chan_def *chandef)
+{
+       int ret;
+
+       trace_rdev_set_ap_chanwidth(&rdev->wiphy, dev, chandef);
+       ret = rdev->ops->set_ap_chanwidth(&rdev->wiphy, dev, chandef);
+       trace_rdev_return_int(&rdev->wiphy, ret);
+
+       return ret;
+}
+
 #endif /* __CFG80211_RDEV_OPS */
index f59aaac586f8cf10905135324c3913646910a662..558b0e3a02d8284c49de58d14833c13b444db5a2 100644 (file)
 #define REG_DBG_PRINT(args...)
 #endif
 
+/**
+ * enum reg_request_treatment - regulatory request treatment
+ *
+ * @REG_REQ_OK: continue processing the regulatory request
+ * @REG_REQ_IGNORE: ignore the regulatory request
+ * @REG_REQ_INTERSECT: the regulatory domain resulting from this request should
+ *     be intersected with the current one.
+ * @REG_REQ_ALREADY_SET: the regulatory request will not change the current
+ *     regulatory settings, and no further processing is required.
+ * @REG_REQ_USER_HINT_HANDLED: a non alpha2  user hint was handled and no
+ *     further processing is required, i.e., not need to update last_request
+ *     etc. This should be used for user hints that do not provide an alpha2
+ *     but some other type of regulatory hint, i.e., indoor operation.
+ */
 enum reg_request_treatment {
        REG_REQ_OK,
        REG_REQ_IGNORE,
        REG_REQ_INTERSECT,
        REG_REQ_ALREADY_SET,
+       REG_REQ_USER_HINT_HANDLED,
 };
 
 static struct regulatory_request core_request_world = {
@@ -106,6 +121,14 @@ const struct ieee80211_regdomain __rcu *cfg80211_regdomain;
  */
 static int reg_num_devs_support_basehint;
 
+/*
+ * State variable indicating if the platform on which the devices
+ * are attached is operating in an indoor environment. The state variable
+ * is relevant for all registered devices.
+ * (protected by RTNL)
+ */
+static bool reg_is_indoor;
+
 static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
 {
        return rtnl_dereference(cfg80211_regdomain);
@@ -240,8 +263,16 @@ static char user_alpha2[2];
 module_param(ieee80211_regdom, charp, 0444);
 MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
 
-static void reg_free_request(struct regulatory_request *lr)
+static void reg_free_request(struct regulatory_request *request)
 {
+       if (request != get_last_request())
+               kfree(request);
+}
+
+static void reg_free_last_request(void)
+{
+       struct regulatory_request *lr = get_last_request();
+
        if (lr != &core_request_world && lr)
                kfree_rcu(lr, rcu_head);
 }
@@ -254,7 +285,7 @@ static void reg_update_last_request(struct regulatory_request *request)
        if (lr == request)
                return;
 
-       reg_free_request(lr);
+       reg_free_last_request();
        rcu_assign_pointer(last_request, request);
 }
 
@@ -873,6 +904,8 @@ static u32 map_regdom_flags(u32 rd_flags)
                channel_flags |= IEEE80211_CHAN_RADAR;
        if (rd_flags & NL80211_RRF_NO_OFDM)
                channel_flags |= IEEE80211_CHAN_NO_OFDM;
+       if (rd_flags & NL80211_RRF_NO_OUTDOOR)
+               channel_flags |= IEEE80211_CHAN_INDOOR_ONLY;
        return channel_flags;
 }
 
@@ -902,7 +935,7 @@ freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
                if (!band_rule_found)
                        band_rule_found = freq_in_rule_band(fr, center_freq);
 
-               bw_fits = reg_does_bw_fit(fr, center_freq, MHZ_TO_KHZ(20));
+               bw_fits = reg_does_bw_fit(fr, center_freq, MHZ_TO_KHZ(5));
 
                if (band_rule_found && bw_fits)
                        return rr;
@@ -986,10 +1019,10 @@ static void chan_reg_rule_print_dbg(const struct ieee80211_regdomain *regd,
 }
 #endif
 
-/*
- * Note that right now we assume the desired channel bandwidth
- * is always 20 MHz for each individual channel (HT40 uses 20 MHz
- * per channel, the primary and the extension channel).
+/* Find an ieee80211_reg_rule such that a 5MHz channel with frequency
+ * chan->center_freq fits there.
+ * If there is no such reg_rule, disable the channel, otherwise set the
+ * flags corresponding to the bandwidths allowed in the particular reg_rule
  */
 static void handle_channel(struct wiphy *wiphy,
                           enum nl80211_reg_initiator initiator,
@@ -1050,8 +1083,12 @@ static void handle_channel(struct wiphy *wiphy,
        if (reg_rule->flags & NL80211_RRF_AUTO_BW)
                max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
 
+       if (max_bandwidth_khz < MHZ_TO_KHZ(10))
+               bw_flags = IEEE80211_CHAN_NO_10MHZ;
+       if (max_bandwidth_khz < MHZ_TO_KHZ(20))
+               bw_flags |= IEEE80211_CHAN_NO_20MHZ;
        if (max_bandwidth_khz < MHZ_TO_KHZ(40))
-               bw_flags = IEEE80211_CHAN_NO_HT40;
+               bw_flags |= IEEE80211_CHAN_NO_HT40;
        if (max_bandwidth_khz < MHZ_TO_KHZ(80))
                bw_flags |= IEEE80211_CHAN_NO_80MHZ;
        if (max_bandwidth_khz < MHZ_TO_KHZ(160))
@@ -1071,6 +1108,13 @@ static void handle_channel(struct wiphy *wiphy,
                        (int) MBI_TO_DBI(power_rule->max_antenna_gain);
                chan->max_reg_power = chan->max_power = chan->orig_mpwr =
                        (int) MBM_TO_DBM(power_rule->max_eirp);
+
+               if (chan->flags & IEEE80211_CHAN_RADAR) {
+                       chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
+                       if (reg_rule->dfs_cac_ms)
+                               chan->dfs_cac_ms = reg_rule->dfs_cac_ms;
+               }
+
                return;
        }
 
@@ -1126,12 +1170,19 @@ static bool reg_request_cell_base(struct regulatory_request *request)
        return request->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE;
 }
 
+static bool reg_request_indoor(struct regulatory_request *request)
+{
+       if (request->initiator != NL80211_REGDOM_SET_BY_USER)
+               return false;
+       return request->user_reg_hint_type == NL80211_USER_REG_HINT_INDOOR;
+}
+
 bool reg_last_request_cell_base(void)
 {
        return reg_request_cell_base(get_last_request());
 }
 
-#ifdef CONFIG_CFG80211_CERTIFICATION_ONUS
+#ifdef CONFIG_CFG80211_REG_CELLULAR_HINTS
 /* Core specific check */
 static enum reg_request_treatment
 reg_ignore_cell_hint(struct regulatory_request *pending_request)
@@ -1471,8 +1522,12 @@ static void handle_channel_custom(struct wiphy *wiphy,
        if (reg_rule->flags & NL80211_RRF_AUTO_BW)
                max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
 
+       if (max_bandwidth_khz < MHZ_TO_KHZ(10))
+               bw_flags = IEEE80211_CHAN_NO_10MHZ;
+       if (max_bandwidth_khz < MHZ_TO_KHZ(20))
+               bw_flags |= IEEE80211_CHAN_NO_20MHZ;
        if (max_bandwidth_khz < MHZ_TO_KHZ(40))
-               bw_flags = IEEE80211_CHAN_NO_HT40;
+               bw_flags |= IEEE80211_CHAN_NO_HT40;
        if (max_bandwidth_khz < MHZ_TO_KHZ(80))
                bw_flags |= IEEE80211_CHAN_NO_80MHZ;
        if (max_bandwidth_khz < MHZ_TO_KHZ(160))
@@ -1568,6 +1623,11 @@ __reg_process_hint_user(struct regulatory_request *user_request)
 {
        struct regulatory_request *lr = get_last_request();
 
+       if (reg_request_indoor(user_request)) {
+               reg_is_indoor = true;
+               return REG_REQ_USER_HINT_HANDLED;
+       }
+
        if (reg_request_cell_base(user_request))
                return reg_ignore_cell_hint(user_request);
 
@@ -1615,8 +1675,9 @@ reg_process_hint_user(struct regulatory_request *user_request)
 
        treatment = __reg_process_hint_user(user_request);
        if (treatment == REG_REQ_IGNORE ||
-           treatment == REG_REQ_ALREADY_SET) {
-               kfree(user_request);
+           treatment == REG_REQ_ALREADY_SET ||
+           treatment == REG_REQ_USER_HINT_HANDLED) {
+               reg_free_request(user_request);
                return treatment;
        }
 
@@ -1676,14 +1737,15 @@ reg_process_hint_driver(struct wiphy *wiphy,
        case REG_REQ_OK:
                break;
        case REG_REQ_IGNORE:
-               kfree(driver_request);
+       case REG_REQ_USER_HINT_HANDLED:
+               reg_free_request(driver_request);
                return treatment;
        case REG_REQ_INTERSECT:
                /* fall through */
        case REG_REQ_ALREADY_SET:
                regd = reg_copy_regd(get_cfg80211_regdom());
                if (IS_ERR(regd)) {
-                       kfree(driver_request);
+                       reg_free_request(driver_request);
                        return REG_REQ_IGNORE;
                }
                rcu_assign_pointer(wiphy->regd, regd);
@@ -1775,12 +1837,13 @@ reg_process_hint_country_ie(struct wiphy *wiphy,
        case REG_REQ_OK:
                break;
        case REG_REQ_IGNORE:
+       case REG_REQ_USER_HINT_HANDLED:
                /* fall through */
        case REG_REQ_ALREADY_SET:
-               kfree(country_ie_request);
+               reg_free_request(country_ie_request);
                return treatment;
        case REG_REQ_INTERSECT:
-               kfree(country_ie_request);
+               reg_free_request(country_ie_request);
                /*
                 * This doesn't happen yet, not sure we
                 * ever want to support it for this case.
@@ -1813,7 +1876,8 @@ static void reg_process_hint(struct regulatory_request *reg_request)
        case NL80211_REGDOM_SET_BY_USER:
                treatment = reg_process_hint_user(reg_request);
                if (treatment == REG_REQ_IGNORE ||
-                   treatment == REG_REQ_ALREADY_SET)
+                   treatment == REG_REQ_ALREADY_SET ||
+                   treatment == REG_REQ_USER_HINT_HANDLED)
                        return;
                queue_delayed_work(system_power_efficient_wq,
                                   &reg_timeout, msecs_to_jiffies(3142));
@@ -1841,7 +1905,7 @@ static void reg_process_hint(struct regulatory_request *reg_request)
        return;
 
 out_free:
-       kfree(reg_request);
+       reg_free_request(reg_request);
 }
 
 /*
@@ -1857,7 +1921,7 @@ static void reg_process_pending_hints(void)
 
        /* When last_request->processed becomes true this will be rescheduled */
        if (lr && !lr->processed) {
-               REG_DBG_PRINT("Pending regulatory request, waiting for it to be processed...\n");
+               reg_process_hint(lr);
                return;
        }
 
@@ -1967,6 +2031,22 @@ int regulatory_hint_user(const char *alpha2,
        return 0;
 }
 
+int regulatory_hint_indoor_user(void)
+{
+       struct regulatory_request *request;
+
+       request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
+       if (!request)
+               return -ENOMEM;
+
+       request->wiphy_idx = WIPHY_IDX_INVALID;
+       request->initiator = NL80211_REGDOM_SET_BY_USER;
+       request->user_reg_hint_type = NL80211_USER_REG_HINT_INDOOR;
+       queue_regulatory_request(request);
+
+       return 0;
+}
+
 /* Driver hints */
 int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
 {
@@ -2134,6 +2214,8 @@ static void restore_regulatory_settings(bool reset_user)
 
        ASSERT_RTNL();
 
+       reg_is_indoor = false;
+
        reset_regdomains(true, &world_regdom);
        restore_alpha2(alpha2, reset_user);
 
@@ -2594,7 +2676,7 @@ void wiphy_regulatory_deregister(struct wiphy *wiphy)
                reg_num_devs_support_basehint--;
 
        rcu_free_regdom(get_wiphy_regdom(wiphy));
-       rcu_assign_pointer(wiphy->regd, NULL);
+       RCU_INIT_POINTER(wiphy->regd, NULL);
 
        if (lr)
                request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
@@ -2614,6 +2696,40 @@ static void reg_timeout_work(struct work_struct *work)
        rtnl_unlock();
 }
 
+/*
+ * See http://www.fcc.gov/document/5-ghz-unlicensed-spectrum-unii, for
+ * UNII band definitions
+ */
+int cfg80211_get_unii(int freq)
+{
+       /* UNII-1 */
+       if (freq >= 5150 && freq <= 5250)
+               return 0;
+
+       /* UNII-2A */
+       if (freq > 5250 && freq <= 5350)
+               return 1;
+
+       /* UNII-2B */
+       if (freq > 5350 && freq <= 5470)
+               return 2;
+
+       /* UNII-2C */
+       if (freq > 5470 && freq <= 5725)
+               return 3;
+
+       /* UNII-3 */
+       if (freq > 5725 && freq <= 5825)
+               return 4;
+
+       return -EINVAL;
+}
+
+bool regulatory_indoor_allowed(void)
+{
+       return reg_is_indoor;
+}
+
 int __init regulatory_init(void)
 {
        int err = 0;
index 37c180df34b72a1195aacb6d72b7b07ddc44a9ef..5e48031ccb9afc33a41c36221e3bef924625ebe8 100644 (file)
@@ -25,6 +25,7 @@ enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy);
 
 int regulatory_hint_user(const char *alpha2,
                         enum nl80211_user_reg_hint_type user_reg_hint_type);
+int regulatory_hint_indoor_user(void);
 
 void wiphy_regulatory_register(struct wiphy *wiphy);
 void wiphy_regulatory_deregister(struct wiphy *wiphy);
@@ -104,4 +105,21 @@ void regulatory_hint_country_ie(struct wiphy *wiphy,
  */
 void regulatory_hint_disconnect(void);
 
+/**
+ * cfg80211_get_unii - get the U-NII band for the frequency
+ * @freq: the frequency for which we want to get the UNII band.
+
+ * Get a value specifying the U-NII band frequency belongs to.
+ * U-NII bands are defined by the FCC in C.F.R 47 part 15.
+ *
+ * Returns -EINVAL if freq is invalid, 0 for UNII-1, 1 for UNII-2A,
+ * 2 for UNII-2B, 3 for UNII-2C and 4 for UNII-3.
+ */
+int cfg80211_get_unii(int freq);
+
+/**
+ * regulatory_indoor_allowed - is indoor operation allowed
+ */
+bool regulatory_indoor_allowed(void);
+
 #endif  /* __NET_WIRELESS_REG_H */
index 88f108edfb586ef3b2d17d15cf66b00da84f93f0..0798c62e60858cb81d1df3d4829414665808268b 100644 (file)
@@ -81,10 +81,10 @@ static void bss_free(struct cfg80211_internal_bss *bss)
        kfree(bss);
 }
 
-static inline void bss_ref_get(struct cfg80211_registered_device *dev,
+static inline void bss_ref_get(struct cfg80211_registered_device *rdev,
                               struct cfg80211_internal_bss *bss)
 {
-       lockdep_assert_held(&dev->bss_lock);
+       lockdep_assert_held(&rdev->bss_lock);
 
        bss->refcount++;
        if (bss->pub.hidden_beacon_bss) {
@@ -95,10 +95,10 @@ static inline void bss_ref_get(struct cfg80211_registered_device *dev,
        }
 }
 
-static inline void bss_ref_put(struct cfg80211_registered_device *dev,
+static inline void bss_ref_put(struct cfg80211_registered_device *rdev,
                               struct cfg80211_internal_bss *bss)
 {
-       lockdep_assert_held(&dev->bss_lock);
+       lockdep_assert_held(&rdev->bss_lock);
 
        if (bss->pub.hidden_beacon_bss) {
                struct cfg80211_internal_bss *hbss;
@@ -114,10 +114,10 @@ static inline void bss_ref_put(struct cfg80211_registered_device *dev,
                bss_free(bss);
 }
 
-static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *dev,
+static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *rdev,
                                  struct cfg80211_internal_bss *bss)
 {
-       lockdep_assert_held(&dev->bss_lock);
+       lockdep_assert_held(&rdev->bss_lock);
 
        if (!list_empty(&bss->hidden_list)) {
                /*
@@ -134,31 +134,31 @@ static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *dev,
        }
 
        list_del_init(&bss->list);
-       rb_erase(&bss->rbn, &dev->bss_tree);
-       bss_ref_put(dev, bss);
+       rb_erase(&bss->rbn, &rdev->bss_tree);
+       bss_ref_put(rdev, bss);
        return true;
 }
 
-static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev,
+static void __cfg80211_bss_expire(struct cfg80211_registered_device *rdev,
                                  unsigned long expire_time)
 {
        struct cfg80211_internal_bss *bss, *tmp;
        bool expired = false;
 
-       lockdep_assert_held(&dev->bss_lock);
+       lockdep_assert_held(&rdev->bss_lock);
 
-       list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) {
+       list_for_each_entry_safe(bss, tmp, &rdev->bss_list, list) {
                if (atomic_read(&bss->hold))
                        continue;
                if (!time_after(expire_time, bss->ts))
                        continue;
 
-               if (__cfg80211_unlink_bss(dev, bss))
+               if (__cfg80211_unlink_bss(rdev, bss))
                        expired = true;
        }
 
        if (expired)
-               dev->bss_generation++;
+               rdev->bss_generation++;
 }
 
 void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
@@ -238,11 +238,11 @@ void __cfg80211_scan_done(struct work_struct *wk)
 void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
 {
        trace_cfg80211_scan_done(request, aborted);
-       WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req);
+       WARN_ON(request != wiphy_to_rdev(request->wiphy)->scan_req);
 
        request->aborted = aborted;
        request->notified = true;
-       queue_work(cfg80211_wq, &wiphy_to_dev(request->wiphy)->scan_done_wk);
+       queue_work(cfg80211_wq, &wiphy_to_rdev(request->wiphy)->scan_done_wk);
 }
 EXPORT_SYMBOL(cfg80211_scan_done);
 
@@ -278,15 +278,15 @@ void cfg80211_sched_scan_results(struct wiphy *wiphy)
 {
        trace_cfg80211_sched_scan_results(wiphy);
        /* ignore if we're not scanning */
-       if (wiphy_to_dev(wiphy)->sched_scan_req)
+       if (wiphy_to_rdev(wiphy)->sched_scan_req)
                queue_work(cfg80211_wq,
-                          &wiphy_to_dev(wiphy)->sched_scan_results_wk);
+                          &wiphy_to_rdev(wiphy)->sched_scan_results_wk);
 }
 EXPORT_SYMBOL(cfg80211_sched_scan_results);
 
 void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
        ASSERT_RTNL();
 
@@ -330,21 +330,21 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
        return 0;
 }
 
-void cfg80211_bss_age(struct cfg80211_registered_device *dev,
+void cfg80211_bss_age(struct cfg80211_registered_device *rdev,
                       unsigned long age_secs)
 {
        struct cfg80211_internal_bss *bss;
        unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC);
 
-       spin_lock_bh(&dev->bss_lock);
-       list_for_each_entry(bss, &dev->bss_list, list)
+       spin_lock_bh(&rdev->bss_lock);
+       list_for_each_entry(bss, &rdev->bss_list, list)
                bss->ts -= age_jiffies;
-       spin_unlock_bh(&dev->bss_lock);
+       spin_unlock_bh(&rdev->bss_lock);
 }
 
-void cfg80211_bss_expire(struct cfg80211_registered_device *dev)
+void cfg80211_bss_expire(struct cfg80211_registered_device *rdev)
 {
-       __cfg80211_bss_expire(dev, jiffies - IEEE80211_SCAN_RESULT_EXPIRE);
+       __cfg80211_bss_expire(rdev, jiffies - IEEE80211_SCAN_RESULT_EXPIRE);
 }
 
 const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len)
@@ -534,32 +534,34 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
                                      const u8 *ssid, size_t ssid_len,
                                      u16 capa_mask, u16 capa_val)
 {
-       struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct cfg80211_internal_bss *bss, *res = NULL;
        unsigned long now = jiffies;
 
        trace_cfg80211_get_bss(wiphy, channel, bssid, ssid, ssid_len, capa_mask,
                               capa_val);
 
-       spin_lock_bh(&dev->bss_lock);
+       spin_lock_bh(&rdev->bss_lock);
 
-       list_for_each_entry(bss, &dev->bss_list, list) {
+       list_for_each_entry(bss, &rdev->bss_list, list) {
                if ((bss->pub.capability & capa_mask) != capa_val)
                        continue;
                if (channel && bss->pub.channel != channel)
                        continue;
+               if (!is_valid_ether_addr(bss->pub.bssid))
+                       continue;
                /* Don't get expired BSS structs */
                if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) &&
                    !atomic_read(&bss->hold))
                        continue;
                if (is_bss(&bss->pub, bssid, ssid, ssid_len)) {
                        res = bss;
-                       bss_ref_get(dev, res);
+                       bss_ref_get(rdev, res);
                        break;
                }
        }
 
-       spin_unlock_bh(&dev->bss_lock);
+       spin_unlock_bh(&rdev->bss_lock);
        if (!res)
                return NULL;
        trace_cfg80211_return_bss(&res->pub);
@@ -567,10 +569,10 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
 }
 EXPORT_SYMBOL(cfg80211_get_bss);
 
-static void rb_insert_bss(struct cfg80211_registered_device *dev,
+static void rb_insert_bss(struct cfg80211_registered_device *rdev,
                          struct cfg80211_internal_bss *bss)
 {
-       struct rb_node **p = &dev->bss_tree.rb_node;
+       struct rb_node **p = &rdev->bss_tree.rb_node;
        struct rb_node *parent = NULL;
        struct cfg80211_internal_bss *tbss;
        int cmp;
@@ -593,15 +595,15 @@ static void rb_insert_bss(struct cfg80211_registered_device *dev,
        }
 
        rb_link_node(&bss->rbn, parent, p);
-       rb_insert_color(&bss->rbn, &dev->bss_tree);
+       rb_insert_color(&bss->rbn, &rdev->bss_tree);
 }
 
 static struct cfg80211_internal_bss *
-rb_find_bss(struct cfg80211_registered_device *dev,
+rb_find_bss(struct cfg80211_registered_device *rdev,
            struct cfg80211_internal_bss *res,
            enum bss_compare_mode mode)
 {
-       struct rb_node *n = dev->bss_tree.rb_node;
+       struct rb_node *n = rdev->bss_tree.rb_node;
        struct cfg80211_internal_bss *bss;
        int r;
 
@@ -620,7 +622,7 @@ rb_find_bss(struct cfg80211_registered_device *dev,
        return NULL;
 }
 
-static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev,
+static bool cfg80211_combine_bsses(struct cfg80211_registered_device *rdev,
                                   struct cfg80211_internal_bss *new)
 {
        const struct cfg80211_bss_ies *ies;
@@ -650,7 +652,7 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev,
 
        /* This is the bad part ... */
 
-       list_for_each_entry(bss, &dev->bss_list, list) {
+       list_for_each_entry(bss, &rdev->bss_list, list) {
                if (!ether_addr_equal(bss->pub.bssid, new->pub.bssid))
                        continue;
                if (bss->pub.channel != new->pub.channel)
@@ -684,7 +686,7 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev,
 
 /* Returned bss is reference counted and must be cleaned up appropriately. */
 static struct cfg80211_internal_bss *
-cfg80211_bss_update(struct cfg80211_registered_device *dev,
+cfg80211_bss_update(struct cfg80211_registered_device *rdev,
                    struct cfg80211_internal_bss *tmp,
                    bool signal_valid)
 {
@@ -695,14 +697,14 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
 
        tmp->ts = jiffies;
 
-       spin_lock_bh(&dev->bss_lock);
+       spin_lock_bh(&rdev->bss_lock);
 
        if (WARN_ON(!rcu_access_pointer(tmp->pub.ies))) {
-               spin_unlock_bh(&dev->bss_lock);
+               spin_unlock_bh(&rdev->bss_lock);
                return NULL;
        }
 
-       found = rb_find_bss(dev, tmp, BSS_CMP_REGULAR);
+       found = rb_find_bss(rdev, tmp, BSS_CMP_REGULAR);
 
        if (found) {
                /* Update IEs */
@@ -789,7 +791,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
                 * is allocated on the stack since it's not needed in the
                 * more common case of an update
                 */
-               new = kzalloc(sizeof(*new) + dev->wiphy.bss_priv_size,
+               new = kzalloc(sizeof(*new) + rdev->wiphy.bss_priv_size,
                              GFP_ATOMIC);
                if (!new) {
                        ies = (void *)rcu_dereference(tmp->pub.beacon_ies);
@@ -805,9 +807,9 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
                INIT_LIST_HEAD(&new->hidden_list);
 
                if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
-                       hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_ZLEN);
+                       hidden = rb_find_bss(rdev, tmp, BSS_CMP_HIDE_ZLEN);
                        if (!hidden)
-                               hidden = rb_find_bss(dev, tmp,
+                               hidden = rb_find_bss(rdev, tmp,
                                                     BSS_CMP_HIDE_NUL);
                        if (hidden) {
                                new->pub.hidden_beacon_bss = &hidden->pub;
@@ -824,24 +826,24 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
                         * expensive search for any probe responses that should
                         * be grouped with this beacon for updates ...
                         */
-                       if (!cfg80211_combine_bsses(dev, new)) {
+                       if (!cfg80211_combine_bsses(rdev, new)) {
                                kfree(new);
                                goto drop;
                        }
                }
 
-               list_add_tail(&new->list, &dev->bss_list);
-               rb_insert_bss(dev, new);
+               list_add_tail(&new->list, &rdev->bss_list);
+               rb_insert_bss(rdev, new);
                found = new;
        }
 
-       dev->bss_generation++;
-       bss_ref_get(dev, found);
-       spin_unlock_bh(&dev->bss_lock);
+       rdev->bss_generation++;
+       bss_ref_get(rdev, found);
+       spin_unlock_bh(&rdev->bss_lock);
 
        return found;
  drop:
-       spin_unlock_bh(&dev->bss_lock);
+       spin_unlock_bh(&rdev->bss_lock);
        return NULL;
 }
 
@@ -889,6 +891,7 @@ cfg80211_inform_bss_width(struct wiphy *wiphy,
        struct cfg80211_bss_ies *ies;
        struct ieee80211_channel *channel;
        struct cfg80211_internal_bss tmp = {}, *res;
+       bool signal_valid;
 
        if (WARN_ON(!wiphy))
                return NULL;
@@ -925,8 +928,9 @@ cfg80211_inform_bss_width(struct wiphy *wiphy,
        rcu_assign_pointer(tmp.pub.beacon_ies, ies);
        rcu_assign_pointer(tmp.pub.ies, ies);
 
-       res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp,
-                                 rx_channel == channel);
+       signal_valid = abs(rx_channel->center_freq - channel->center_freq) <=
+               wiphy->max_adj_channel_rssi_comp;
+       res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid);
        if (!res)
                return NULL;
 
@@ -950,6 +954,7 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
        struct cfg80211_internal_bss tmp = {}, *res;
        struct cfg80211_bss_ies *ies;
        struct ieee80211_channel *channel;
+       bool signal_valid;
        size_t ielen = len - offsetof(struct ieee80211_mgmt,
                                      u.probe_resp.variable);
 
@@ -997,8 +1002,9 @@ cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
        tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
        tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
 
-       res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp,
-                                 rx_channel == channel);
+       signal_valid = abs(rx_channel->center_freq - channel->center_freq) <=
+               wiphy->max_adj_channel_rssi_comp;
+       res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid);
        if (!res)
                return NULL;
 
@@ -1013,7 +1019,7 @@ EXPORT_SYMBOL(cfg80211_inform_bss_width_frame);
 
 void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
 {
-       struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct cfg80211_internal_bss *bss;
 
        if (!pub)
@@ -1021,15 +1027,15 @@ void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
 
        bss = container_of(pub, struct cfg80211_internal_bss, pub);
 
-       spin_lock_bh(&dev->bss_lock);
-       bss_ref_get(dev, bss);
-       spin_unlock_bh(&dev->bss_lock);
+       spin_lock_bh(&rdev->bss_lock);
+       bss_ref_get(rdev, bss);
+       spin_unlock_bh(&rdev->bss_lock);
 }
 EXPORT_SYMBOL(cfg80211_ref_bss);
 
 void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
 {
-       struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct cfg80211_internal_bss *bss;
 
        if (!pub)
@@ -1037,15 +1043,15 @@ void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
 
        bss = container_of(pub, struct cfg80211_internal_bss, pub);
 
-       spin_lock_bh(&dev->bss_lock);
-       bss_ref_put(dev, bss);
-       spin_unlock_bh(&dev->bss_lock);
+       spin_lock_bh(&rdev->bss_lock);
+       bss_ref_put(rdev, bss);
+       spin_unlock_bh(&rdev->bss_lock);
 }
 EXPORT_SYMBOL(cfg80211_put_bss);
 
 void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
 {
-       struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct cfg80211_internal_bss *bss;
 
        if (WARN_ON(!pub))
@@ -1053,12 +1059,12 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
 
        bss = container_of(pub, struct cfg80211_internal_bss, pub);
 
-       spin_lock_bh(&dev->bss_lock);
+       spin_lock_bh(&rdev->bss_lock);
        if (!list_empty(&bss->list)) {
-               if (__cfg80211_unlink_bss(dev, bss))
-                       dev->bss_generation++;
+               if (__cfg80211_unlink_bss(rdev, bss))
+                       rdev->bss_generation++;
        }
-       spin_unlock_bh(&dev->bss_lock);
+       spin_unlock_bh(&rdev->bss_lock);
 }
 EXPORT_SYMBOL(cfg80211_unlink_bss);
 
@@ -1075,7 +1081,7 @@ cfg80211_get_dev_from_ifindex(struct net *net, int ifindex)
        if (!dev)
                return ERR_PTR(-ENODEV);
        if (dev->ieee80211_ptr)
-               rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy);
+               rdev = wiphy_to_rdev(dev->ieee80211_ptr->wiphy);
        else
                rdev = ERR_PTR(-ENODEV);
        dev_put(dev);
@@ -1155,7 +1161,11 @@ int cfg80211_wext_siwscan(struct net_device *dev,
                                int k;
                                int wiphy_freq = wiphy->bands[band]->channels[j].center_freq;
                                for (k = 0; k < wreq->num_channels; k++) {
-                                       int wext_freq = cfg80211_wext_freq(wiphy, &wreq->channel_list[k]);
+                                       struct iw_freq *freq =
+                                               &wreq->channel_list[k];
+                                       int wext_freq =
+                                               cfg80211_wext_freq(freq);
+
                                        if (wext_freq == wiphy_freq)
                                                goto wext_freq_found;
                                }
@@ -1467,7 +1477,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
 }
 
 
-static int ieee80211_scan_results(struct cfg80211_registered_device *dev,
+static int ieee80211_scan_results(struct cfg80211_registered_device *rdev,
                                  struct iw_request_info *info,
                                  char *buf, size_t len)
 {
@@ -1475,18 +1485,18 @@ static int ieee80211_scan_results(struct cfg80211_registered_device *dev,
        char *end_buf = buf + len;
        struct cfg80211_internal_bss *bss;
 
-       spin_lock_bh(&dev->bss_lock);
-       cfg80211_bss_expire(dev);
+       spin_lock_bh(&rdev->bss_lock);
+       cfg80211_bss_expire(rdev);
 
-       list_for_each_entry(bss, &dev->bss_list, list) {
+       list_for_each_entry(bss, &rdev->bss_list, list) {
                if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
-                       spin_unlock_bh(&dev->bss_lock);
+                       spin_unlock_bh(&rdev->bss_lock);
                        return -E2BIG;
                }
-               current_ev = ieee80211_bss(&dev->wiphy, info, bss,
+               current_ev = ieee80211_bss(&rdev->wiphy, info, bss,
                                           current_ev, end_buf);
        }
-       spin_unlock_bh(&dev->bss_lock);
+       spin_unlock_bh(&rdev->bss_lock);
        return current_ev - buf;
 }
 
index 3546a77033de30d0d2593c6ac4fbac8f58659025..8bbeeb302216223a260085634bf6b5627c209783 100644 (file)
@@ -59,7 +59,7 @@ static void cfg80211_sme_free(struct wireless_dev *wdev)
 
 static int cfg80211_conn_scan(struct wireless_dev *wdev)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_scan_request *request;
        int n_channels, err;
 
@@ -130,7 +130,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
 
 static int cfg80211_conn_do_work(struct wireless_dev *wdev)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_connect_params *params;
        struct cfg80211_assoc_request req = {};
        int err;
@@ -149,7 +149,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
        case CFG80211_CONN_SCAN_AGAIN:
                return cfg80211_conn_scan(wdev);
        case CFG80211_CONN_AUTHENTICATE_NEXT:
-               BUG_ON(!rdev->ops->auth);
+               if (WARN_ON(!rdev->ops->auth))
+                       return -EOPNOTSUPP;
                wdev->conn->state = CFG80211_CONN_AUTHENTICATING;
                return cfg80211_mlme_auth(rdev, wdev->netdev,
                                          params->channel, params->auth_type,
@@ -161,7 +162,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
        case CFG80211_CONN_AUTH_FAILED:
                return -ENOTCONN;
        case CFG80211_CONN_ASSOCIATE_NEXT:
-               BUG_ON(!rdev->ops->assoc);
+               if (WARN_ON(!rdev->ops->assoc))
+                       return -EOPNOTSUPP;
                wdev->conn->state = CFG80211_CONN_ASSOCIATING;
                if (wdev->conn->prev_bssid_valid)
                        req.prev_bssid = wdev->conn->prev_bssid;
@@ -244,7 +246,7 @@ void cfg80211_conn_work(struct work_struct *work)
 /* Returned bss is reference counted and must be cleaned up appropriately. */
 static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_bss *bss;
        u16 capa = WLAN_CAPABILITY_ESS;
 
@@ -274,7 +276,7 @@ static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev)
 static void __cfg80211_sme_scan_done(struct net_device *dev)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_bss *bss;
 
        ASSERT_WDEV_LOCK(wdev);
@@ -305,7 +307,7 @@ void cfg80211_sme_scan_done(struct net_device *dev)
 void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len)
 {
        struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
        u16 status_code = le16_to_cpu(mgmt->u.auth.status_code);
 
@@ -351,7 +353,7 @@ void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len)
 
 bool cfg80211_sme_rx_assoc_resp(struct wireless_dev *wdev, u16 status)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 
        if (!wdev->conn)
                return false;
@@ -385,7 +387,7 @@ void cfg80211_sme_deauth(struct wireless_dev *wdev)
 
 void cfg80211_sme_auth_timeout(struct wireless_dev *wdev)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 
        if (!wdev->conn)
                return;
@@ -396,7 +398,7 @@ void cfg80211_sme_auth_timeout(struct wireless_dev *wdev)
 
 void cfg80211_sme_disassoc(struct wireless_dev *wdev)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 
        if (!wdev->conn)
                return;
@@ -407,7 +409,7 @@ void cfg80211_sme_disassoc(struct wireless_dev *wdev)
 
 void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 
        if (!wdev->conn)
                return;
@@ -420,7 +422,7 @@ static int cfg80211_sme_connect(struct wireless_dev *wdev,
                                struct cfg80211_connect_params *connect,
                                const u8 *prev_bssid)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_bss *bss;
        int err;
 
@@ -467,7 +469,7 @@ static int cfg80211_sme_connect(struct wireless_dev *wdev,
        }
 
        wdev->conn->params.ssid = wdev->ssid;
-       wdev->conn->params.ssid_len = connect->ssid_len;
+       wdev->conn->params.ssid_len = wdev->ssid_len;
 
        /* see if we have the bss already */
        bss = cfg80211_get_conn_bss(wdev);
@@ -479,7 +481,6 @@ static int cfg80211_sme_connect(struct wireless_dev *wdev,
 
        /* we're good if we have a matching bss struct */
        if (bss) {
-               wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
                err = cfg80211_conn_do_work(wdev);
                cfg80211_put_bss(wdev->wiphy, bss);
        } else {
@@ -505,7 +506,7 @@ static int cfg80211_sme_connect(struct wireless_dev *wdev,
 
 static int cfg80211_sme_disconnect(struct wireless_dev *wdev, u16 reason)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        int err;
 
        if (!wdev->conn)
@@ -593,7 +594,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
                return;
        }
 
-       nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev,
+       nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev,
                                    bssid, req_ie, req_ie_len,
                                    resp_ie, resp_ie_len,
                                    status, GFP_KERNEL);
@@ -624,7 +625,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
 #endif
 
        if (!bss && (status == WLAN_STATUS_SUCCESS)) {
-               WARN_ON_ONCE(!wiphy_to_dev(wdev->wiphy)->ops->connect);
+               WARN_ON_ONCE(!wiphy_to_rdev(wdev->wiphy)->ops->connect);
                bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
                                       wdev->ssid, wdev->ssid_len,
                                       WLAN_CAPABILITY_ESS,
@@ -687,7 +688,7 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
                             u16 status, gfp_t gfp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_event *ev;
        unsigned long flags;
 
@@ -742,7 +743,8 @@ void __cfg80211_roamed(struct wireless_dev *wdev,
        cfg80211_hold_bss(bss_from_pub(bss));
        wdev->current_bss = bss_from_pub(bss);
 
-       nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bss->bssid,
+       nl80211_send_roamed(wiphy_to_rdev(wdev->wiphy),
+                           wdev->netdev, bss->bssid,
                            req_ie, req_ie_len, resp_ie, resp_ie_len,
                            GFP_KERNEL);
 
@@ -801,7 +803,7 @@ void cfg80211_roamed_bss(struct net_device *dev,
                         size_t resp_ie_len, gfp_t gfp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_event *ev;
        unsigned long flags;
 
@@ -834,7 +836,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
                             size_t ie_len, u16 reason, bool from_ap)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        int i;
 #ifdef CONFIG_CFG80211_WEXT
        union iwreq_data wrqu;
@@ -877,10 +879,10 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
 }
 
 void cfg80211_disconnected(struct net_device *dev, u16 reason,
-                          u8 *ie, size_t ie_len, gfp_t gfp)
+                          const u8 *ie, size_t ie_len, gfp_t gfp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_event *ev;
        unsigned long flags;
 
index aabccf13e07b6860ef92ddc637a7879a8f961aab..560ed77084e92b52cae0f299ca383eef240a42e6 100644 (file)
@@ -1876,29 +1876,33 @@ TRACE_EVENT(rdev_channel_switch,
                WIPHY_ENTRY
                NETDEV_ENTRY
                CHAN_DEF_ENTRY
-               __field(u16, counter_offset_beacon)
-               __field(u16, counter_offset_presp)
                __field(bool, radar_required)
                __field(bool, block_tx)
                __field(u8, count)
+               __dynamic_array(u16, bcn_ofs, params->n_counter_offsets_beacon)
+               __dynamic_array(u16, pres_ofs, params->n_counter_offsets_presp)
        ),
        TP_fast_assign(
                WIPHY_ASSIGN;
                NETDEV_ASSIGN;
                CHAN_DEF_ASSIGN(&params->chandef);
-               __entry->counter_offset_beacon = params->counter_offset_beacon;
-               __entry->counter_offset_presp = params->counter_offset_presp;
                __entry->radar_required = params->radar_required;
                __entry->block_tx = params->block_tx;
                __entry->count = params->count;
+               memcpy(__get_dynamic_array(bcn_ofs),
+                      params->counter_offsets_beacon,
+                      params->n_counter_offsets_beacon * sizeof(u16));
+
+               /* probe response offsets are optional */
+               if (params->n_counter_offsets_presp)
+                       memcpy(__get_dynamic_array(pres_ofs),
+                              params->counter_offsets_presp,
+                              params->n_counter_offsets_presp * sizeof(u16));
        ),
        TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT
-                 ", block_tx: %d, count: %u, radar_required: %d"
-                 ", counter offsets (beacon/presp): %u/%u",
+                 ", block_tx: %d, count: %u, radar_required: %d",
                  WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG,
-                 __entry->block_tx, __entry->count, __entry->radar_required,
-                 __entry->counter_offset_beacon,
-                 __entry->counter_offset_presp)
+                 __entry->block_tx, __entry->count, __entry->radar_required)
 );
 
 TRACE_EVENT(rdev_set_qos_map,
@@ -1919,6 +1923,24 @@ TRACE_EVENT(rdev_set_qos_map,
                  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->num_des)
 );
 
+TRACE_EVENT(rdev_set_ap_chanwidth,
+       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+                struct cfg80211_chan_def *chandef),
+       TP_ARGS(wiphy, netdev, chandef),
+       TP_STRUCT__entry(
+               WIPHY_ENTRY
+               NETDEV_ENTRY
+               CHAN_DEF_ENTRY
+       ),
+       TP_fast_assign(
+               WIPHY_ASSIGN;
+               NETDEV_ASSIGN;
+               CHAN_DEF_ASSIGN(chandef);
+       ),
+       TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT,
+                 WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG)
+);
+
 /*************************************************************
  *          cfg80211 exported functions traces              *
  *************************************************************/
@@ -2193,18 +2215,21 @@ TRACE_EVENT(cfg80211_cqm_rssi_notify,
 );
 
 TRACE_EVENT(cfg80211_reg_can_beacon,
-       TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
-       TP_ARGS(wiphy, chandef),
+       TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef,
+                enum nl80211_iftype iftype),
+       TP_ARGS(wiphy, chandef, iftype),
        TP_STRUCT__entry(
                WIPHY_ENTRY
                CHAN_DEF_ENTRY
+               __field(enum nl80211_iftype, iftype)
        ),
        TP_fast_assign(
                WIPHY_ASSIGN;
                CHAN_DEF_ASSIGN(chandef);
+               __entry->iftype = iftype;
        ),
-       TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
-                 WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
+       TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", iftype=%d",
+                 WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->iftype)
 );
 
 TRACE_EVENT(cfg80211_chandef_dfs_required,
@@ -2615,6 +2640,21 @@ TRACE_EVENT(cfg80211_ft_event,
                  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(target_ap))
 );
 
+TRACE_EVENT(cfg80211_stop_iface,
+       TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+       TP_ARGS(wiphy, wdev),
+       TP_STRUCT__entry(
+               WIPHY_ENTRY
+               WDEV_ENTRY
+       ),
+       TP_fast_assign(
+               WIPHY_ASSIGN;
+               WDEV_ASSIGN;
+       ),
+       TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT,
+                 WIPHY_PR_ARG, WDEV_PR_ARG)
+);
+
 #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
 
 #undef TRACE_INCLUDE_PATH
index e5872ff2c27ca8989ca6da7cfdf4d7041c29a72e..728f1c0dc70dbdb7c85558b84b83dc9b66e4083f 100644 (file)
@@ -476,7 +476,8 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
 EXPORT_SYMBOL(ieee80211_data_to_8023);
 
 int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
-                            enum nl80211_iftype iftype, u8 *bssid, bool qos)
+                            enum nl80211_iftype iftype,
+                            const u8 *bssid, bool qos)
 {
        struct ieee80211_hdr hdr;
        u16 hdrlen, ethertype;
@@ -770,7 +771,7 @@ EXPORT_SYMBOL(ieee80211_bss_get_ie);
 
 void cfg80211_upload_connect_keys(struct wireless_dev *wdev)
 {
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct net_device *dev = wdev->netdev;
        int i;
 
@@ -839,6 +840,9 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev)
                        __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid,
                                               ev->ij.channel);
                        break;
+               case EVENT_STOPPED:
+                       __cfg80211_leave(wiphy_to_rdev(wdev->wiphy), wdev);
+                       break;
                }
                wdev_unlock(wdev);
 
@@ -888,11 +892,6 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
                return -EBUSY;
 
        if (ntype != otype && netif_running(dev)) {
-               err = cfg80211_can_change_interface(rdev, dev->ieee80211_ptr,
-                                                   ntype);
-               if (err)
-                       return err;
-
                dev->ieee80211_ptr->use_4addr = false;
                dev->ieee80211_ptr->mesh_id_up_len = 0;
                wdev_lock(dev->ieee80211_ptr);
@@ -1268,6 +1267,120 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
        return res;
 }
 
+int cfg80211_iter_combinations(struct wiphy *wiphy,
+                              const int num_different_channels,
+                              const u8 radar_detect,
+                              const int iftype_num[NUM_NL80211_IFTYPES],
+                              void (*iter)(const struct ieee80211_iface_combination *c,
+                                           void *data),
+                              void *data)
+{
+       const struct ieee80211_regdomain *regdom;
+       enum nl80211_dfs_regions region = 0;
+       int i, j, iftype;
+       int num_interfaces = 0;
+       u32 used_iftypes = 0;
+
+       if (radar_detect) {
+               rcu_read_lock();
+               regdom = rcu_dereference(cfg80211_regdomain);
+               if (regdom)
+                       region = regdom->dfs_region;
+               rcu_read_unlock();
+       }
+
+       for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) {
+               num_interfaces += iftype_num[iftype];
+               if (iftype_num[iftype] > 0 &&
+                   !(wiphy->software_iftypes & BIT(iftype)))
+                       used_iftypes |= BIT(iftype);
+       }
+
+       for (i = 0; i < wiphy->n_iface_combinations; i++) {
+               const struct ieee80211_iface_combination *c;
+               struct ieee80211_iface_limit *limits;
+               u32 all_iftypes = 0;
+
+               c = &wiphy->iface_combinations[i];
+
+               if (num_interfaces > c->max_interfaces)
+                       continue;
+               if (num_different_channels > c->num_different_channels)
+                       continue;
+
+               limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits,
+                                GFP_KERNEL);
+               if (!limits)
+                       return -ENOMEM;
+
+               for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) {
+                       if (wiphy->software_iftypes & BIT(iftype))
+                               continue;
+                       for (j = 0; j < c->n_limits; j++) {
+                               all_iftypes |= limits[j].types;
+                               if (!(limits[j].types & BIT(iftype)))
+                                       continue;
+                               if (limits[j].max < iftype_num[iftype])
+                                       goto cont;
+                               limits[j].max -= iftype_num[iftype];
+                       }
+               }
+
+               if (radar_detect != (c->radar_detect_widths & radar_detect))
+                       goto cont;
+
+               if (radar_detect && c->radar_detect_regions &&
+                   !(c->radar_detect_regions & BIT(region)))
+                       goto cont;
+
+               /* Finally check that all iftypes that we're currently
+                * using are actually part of this combination. If they
+                * aren't then we can't use this combination and have
+                * to continue to the next.
+                */
+               if ((all_iftypes & used_iftypes) != used_iftypes)
+                       goto cont;
+
+               /* This combination covered all interface types and
+                * supported the requested numbers, so we're good.
+                */
+
+               (*iter)(c, data);
+ cont:
+               kfree(limits);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(cfg80211_iter_combinations);
+
+static void
+cfg80211_iter_sum_ifcombs(const struct ieee80211_iface_combination *c,
+                         void *data)
+{
+       int *num = data;
+       (*num)++;
+}
+
+int cfg80211_check_combinations(struct wiphy *wiphy,
+                               const int num_different_channels,
+                               const u8 radar_detect,
+                               const int iftype_num[NUM_NL80211_IFTYPES])
+{
+       int err, num = 0;
+
+       err = cfg80211_iter_combinations(wiphy, num_different_channels,
+                                        radar_detect, iftype_num,
+                                        cfg80211_iter_sum_ifcombs, &num);
+       if (err)
+               return err;
+       if (num == 0)
+               return -EBUSY;
+
+       return 0;
+}
+EXPORT_SYMBOL(cfg80211_check_combinations);
+
 int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
                                 struct wireless_dev *wdev,
                                 enum nl80211_iftype iftype,
@@ -1276,7 +1389,6 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
                                 u8 radar_detect)
 {
        struct wireless_dev *wdev_iter;
-       u32 used_iftypes = BIT(iftype);
        int num[NUM_NL80211_IFTYPES];
        struct ieee80211_channel
                        *used_channels[CFG80211_MAX_NUM_DIFFERENT_CHANNELS];
@@ -1284,7 +1396,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;
-       int i, j;
+       int i;
 
        ASSERT_RTNL();
 
@@ -1306,6 +1418,11 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
 
        num[iftype] = 1;
 
+       /* TODO: We'll probably not need this anymore, since this
+        * should only be called with CHAN_MODE_UNDEFINED. There are
+        * still a couple of pending calls where other chanmodes are
+        * used, but we should get rid of them.
+        */
        switch (chanmode) {
        case CHAN_MODE_UNDEFINED:
                break;
@@ -1369,65 +1486,13 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
 
                num[wdev_iter->iftype]++;
                total++;
-               used_iftypes |= BIT(wdev_iter->iftype);
        }
 
        if (total == 1 && !radar_detect)
                return 0;
 
-       for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) {
-               const struct ieee80211_iface_combination *c;
-               struct ieee80211_iface_limit *limits;
-               u32 all_iftypes = 0;
-
-               c = &rdev->wiphy.iface_combinations[i];
-
-               if (total > c->max_interfaces)
-                       continue;
-               if (num_different_channels > c->num_different_channels)
-                       continue;
-
-               limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits,
-                                GFP_KERNEL);
-               if (!limits)
-                       return -ENOMEM;
-
-               for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) {
-                       if (rdev->wiphy.software_iftypes & BIT(iftype))
-                               continue;
-                       for (j = 0; j < c->n_limits; j++) {
-                               all_iftypes |= limits[j].types;
-                               if (!(limits[j].types & BIT(iftype)))
-                                       continue;
-                               if (limits[j].max < num[iftype])
-                                       goto cont;
-                               limits[j].max -= num[iftype];
-                       }
-               }
-
-               if (radar_detect && !(c->radar_detect_widths & radar_detect))
-                       goto cont;
-
-               /*
-                * Finally check that all iftypes that we're currently
-                * using are actually part of this combination. If they
-                * aren't then we can't use this combination and have
-                * to continue to the next.
-                */
-               if ((all_iftypes & used_iftypes) != used_iftypes)
-                       goto cont;
-
-               /*
-                * This combination covered all interface types and
-                * supported the requested numbers, so we're good.
-                */
-               kfree(limits);
-               return 0;
- cont:
-               kfree(limits);
-       }
-
-       return -EBUSY;
+       return cfg80211_check_combinations(&rdev->wiphy, num_different_channels,
+                                          radar_detect, num);
 }
 
 int ieee80211_get_ratemask(struct ieee80211_supported_band *sband,
@@ -1481,6 +1546,24 @@ unsigned int ieee80211_get_num_supported_channels(struct wiphy *wiphy)
 }
 EXPORT_SYMBOL(ieee80211_get_num_supported_channels);
 
+int cfg80211_get_station(struct net_device *dev, const u8 *mac_addr,
+                        struct station_info *sinfo)
+{
+       struct cfg80211_registered_device *rdev;
+       struct wireless_dev *wdev;
+
+       wdev = dev->ieee80211_ptr;
+       if (!wdev)
+               return -EOPNOTSUPP;
+
+       rdev = wiphy_to_rdev(wdev->wiphy);
+       if (!rdev->ops->get_station)
+               return -EOPNOTSUPP;
+
+       return rdev_get_station(rdev, dev, mac_addr, sinfo);
+}
+EXPORT_SYMBOL(cfg80211_get_station);
+
 /* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
 /* Ethernet-II snap header (RFC1042 for most EtherTypes) */
 const unsigned char rfc1042_header[] __aligned(2) =
index 5661a54ac7ee4ed1c1865d855e1b2681c67d07cc..11120bb14162505043579628bed2ad131ba41f7d 100644 (file)
@@ -73,7 +73,7 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
        struct vif_params vifparams;
        enum nl80211_iftype type;
 
-       rdev = wiphy_to_dev(wdev->wiphy);
+       rdev = wiphy_to_rdev(wdev->wiphy);
 
        switch (*mode) {
        case IW_MODE_INFRA:
@@ -253,12 +253,12 @@ EXPORT_SYMBOL_GPL(cfg80211_wext_giwrange);
 
 /**
  * cfg80211_wext_freq - get wext frequency for non-"auto"
- * @wiphy: the wiphy
+ * @dev: the net device
  * @freq: the wext freq encoding
  *
  * Returns a frequency, or a negative error code, or 0 for auto.
  */
-int cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq)
+int cfg80211_wext_freq(struct iw_freq *freq)
 {
        /*
         * Parse frequency - return 0 for auto and
@@ -286,7 +286,7 @@ int cfg80211_wext_siwrts(struct net_device *dev,
                         struct iw_param *rts, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        u32 orts = wdev->wiphy->rts_threshold;
        int err;
 
@@ -324,7 +324,7 @@ int cfg80211_wext_siwfrag(struct net_device *dev,
                          struct iw_param *frag, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        u32 ofrag = wdev->wiphy->frag_threshold;
        int err;
 
@@ -364,7 +364,7 @@ static int cfg80211_wext_siwretry(struct net_device *dev,
                                  struct iw_param *retry, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        u32 changed = 0;
        u8 olong = wdev->wiphy->retry_long;
        u8 oshort = wdev->wiphy->retry_short;
@@ -587,7 +587,7 @@ static int cfg80211_wext_siwencode(struct net_device *dev,
                                   struct iw_point *erq, char *keybuf)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        int idx, err;
        bool remove = false;
        struct key_params params;
@@ -647,7 +647,7 @@ static int cfg80211_wext_siwencodeext(struct net_device *dev,
                                      struct iw_point *erq, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct iw_encode_ext *ext = (struct iw_encode_ext *) extra;
        const u8 *addr;
        int idx;
@@ -775,7 +775,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
                                 struct iw_freq *wextfreq, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_chan_def chandef = {
                .width = NL80211_CHAN_WIDTH_20_NOHT,
        };
@@ -787,7 +787,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
        case NL80211_IFTYPE_ADHOC:
                return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra);
        case NL80211_IFTYPE_MONITOR:
-               freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
+               freq = cfg80211_wext_freq(wextfreq);
                if (freq < 0)
                        return freq;
                if (freq == 0)
@@ -798,7 +798,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
                        return -EINVAL;
                return cfg80211_set_monitor_channel(rdev, &chandef);
        case NL80211_IFTYPE_MESH_POINT:
-               freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
+               freq = cfg80211_wext_freq(wextfreq);
                if (freq < 0)
                        return freq;
                if (freq == 0)
@@ -818,7 +818,7 @@ static int cfg80211_wext_giwfreq(struct net_device *dev,
                                 struct iw_freq *freq, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_chan_def chandef;
        int ret;
 
@@ -847,7 +847,7 @@ static int cfg80211_wext_siwtxpower(struct net_device *dev,
                                    union iwreq_data *data, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        enum nl80211_tx_power_setting type;
        int dbm = 0;
 
@@ -899,7 +899,7 @@ static int cfg80211_wext_giwtxpower(struct net_device *dev,
                                    union iwreq_data *data, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        int err, val;
 
        if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM)
@@ -1119,7 +1119,7 @@ static int cfg80211_wext_siwpower(struct net_device *dev,
                                  struct iw_param *wrq, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        bool ps = wdev->ps;
        int timeout = wdev->ps_timeout;
        int err;
@@ -1177,7 +1177,7 @@ static int cfg80211_wds_wext_siwap(struct net_device *dev,
                                   struct sockaddr *addr, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        int err;
 
        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_WDS))
@@ -1221,7 +1221,7 @@ static int cfg80211_wext_siwrate(struct net_device *dev,
                                 struct iw_param *rate, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_bitrate_mask mask;
        u32 fixed, maxrate;
        struct ieee80211_supported_band *sband;
@@ -1272,7 +1272,7 @@ static int cfg80211_wext_giwrate(struct net_device *dev,
                                 struct iw_param *rate, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        /* we are under RTNL - globally locked - so can use a static struct */
        static struct station_info sinfo;
        u8 addr[ETH_ALEN];
@@ -1310,7 +1310,7 @@ static int cfg80211_wext_giwrate(struct net_device *dev,
 static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        /* we are under RTNL - globally locked - so can use static structs */
        static struct iw_statistics wstats;
        static struct station_info sinfo;
@@ -1449,7 +1449,7 @@ static int cfg80211_wext_siwpmksa(struct net_device *dev,
                                  struct iw_point *data, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_pmksa cfg_pmksa;
        struct iw_pmksa *pmksa = (struct iw_pmksa *)extra;
 
index 5d766b0118e81969ff4f24c59b88bffdaa6496ff..ebcacca2f731941123efb22c8067a6c34aaa9ea1 100644 (file)
@@ -50,7 +50,7 @@ int cfg80211_wext_siwgenie(struct net_device *dev,
                           struct iw_point *data, char *extra);
 
 
-int cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq);
+int cfg80211_wext_freq(struct iw_freq *freq);
 
 
 extern const struct iw_handler_def cfg80211_wext_handler;
index 86c331a65664a77bfe6c083224b7eae2c91f5b9c..c7e5c8eb4f24708a27d90ae298a85c825ee81e38 100644 (file)
@@ -67,7 +67,7 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
                              struct iw_freq *wextfreq, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct ieee80211_channel *chan = NULL;
        int err, freq;
 
@@ -75,7 +75,7 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
                return -EINVAL;
 
-       freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
+       freq = cfg80211_wext_freq(wextfreq);
        if (freq < 0)
                return freq;
 
@@ -169,7 +169,7 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev,
                               struct iw_point *data, char *ssid)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        size_t len = data->length;
        int err;
 
@@ -260,7 +260,7 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev,
                            struct sockaddr *ap_addr, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        u8 *bssid = ap_addr->sa_data;
        int err;
 
@@ -333,7 +333,7 @@ int cfg80211_wext_siwgenie(struct net_device *dev,
                           struct iw_point *data, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        u8 *ie = extra;
        int ie_len = data->length, err;
 
@@ -390,7 +390,7 @@ int cfg80211_wext_siwmlme(struct net_device *dev,
        if (!wdev)
                return -EOPNOTSUPP;
 
-       rdev = wiphy_to_dev(wdev->wiphy);
+       rdev = wiphy_to_rdev(wdev->wiphy);
 
        if (wdev->iftype != NL80211_IFTYPE_STATION)
                return -EINVAL;
index 3bb2cdc13b46e1468743e1130d6c4c25e08921e0..c51e8f7b8653cb167aba13f61c2d7520d615c31c 100644 (file)
@@ -199,6 +199,7 @@ int xfrm_output(struct sk_buff *skb)
 
        return xfrm_output2(skb);
 }
+EXPORT_SYMBOL_GPL(xfrm_output);
 
 int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb)
 {
@@ -213,6 +214,7 @@ int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb)
                return -EAFNOSUPPORT;
        return inner_mode->afinfo->extract_output(x, skb);
 }
+EXPORT_SYMBOL_GPL(xfrm_inner_extract_output);
 
 void xfrm_local_error(struct sk_buff *skb, int mtu)
 {
@@ -233,7 +235,4 @@ void xfrm_local_error(struct sk_buff *skb, int mtu)
        afinfo->local_error(skb, mtu);
        xfrm_state_put_afinfo(afinfo);
 }
-
-EXPORT_SYMBOL_GPL(xfrm_output);
-EXPORT_SYMBOL_GPL(xfrm_inner_extract_output);
 EXPORT_SYMBOL_GPL(xfrm_local_error);
index c08fbd11ceff52ee145316a33758feb30667f02d..a8ef5108e0d86cbc5c411f3db378fde5a0d54f18 100644 (file)
@@ -769,7 +769,7 @@ EXPORT_SYMBOL(xfrm_policy_byid);
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
 static inline int
-xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audit_info)
+xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid)
 {
        int dir, err = 0;
 
@@ -783,10 +783,7 @@ xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audi
                                continue;
                        err = security_xfrm_policy_delete(pol->security);
                        if (err) {
-                               xfrm_audit_policy_delete(pol, 0,
-                                                        audit_info->loginuid,
-                                                        audit_info->sessionid,
-                                                        audit_info->secid);
+                               xfrm_audit_policy_delete(pol, 0, task_valid);
                                return err;
                        }
                }
@@ -800,9 +797,7 @@ xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audi
                                                                pol->security);
                                if (err) {
                                        xfrm_audit_policy_delete(pol, 0,
-                                                       audit_info->loginuid,
-                                                       audit_info->sessionid,
-                                                       audit_info->secid);
+                                                                task_valid);
                                        return err;
                                }
                        }
@@ -812,19 +807,19 @@ xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audi
 }
 #else
 static inline int
-xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audit_info)
+xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid)
 {
        return 0;
 }
 #endif
 
-int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info)
+int xfrm_policy_flush(struct net *net, u8 type, bool task_valid)
 {
        int dir, err = 0, cnt = 0;
 
        write_lock_bh(&net->xfrm.xfrm_policy_lock);
 
-       err = xfrm_policy_flush_secctx_check(net, type, audit_info);
+       err = xfrm_policy_flush_secctx_check(net, type, task_valid);
        if (err)
                goto out;
 
@@ -841,9 +836,7 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info)
                        write_unlock_bh(&net->xfrm.xfrm_policy_lock);
                        cnt++;
 
-                       xfrm_audit_policy_delete(pol, 1, audit_info->loginuid,
-                                                audit_info->sessionid,
-                                                audit_info->secid);
+                       xfrm_audit_policy_delete(pol, 1, task_valid);
 
                        xfrm_policy_kill(pol);
 
@@ -862,10 +855,7 @@ int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info)
                                write_unlock_bh(&net->xfrm.xfrm_policy_lock);
                                cnt++;
 
-                               xfrm_audit_policy_delete(pol, 1,
-                                                        audit_info->loginuid,
-                                                        audit_info->sessionid,
-                                                        audit_info->secid);
+                               xfrm_audit_policy_delete(pol, 1, task_valid);
                                xfrm_policy_kill(pol);
 
                                write_lock_bh(&net->xfrm.xfrm_policy_lock);
@@ -2783,21 +2773,19 @@ static struct notifier_block xfrm_dev_notifier = {
 static int __net_init xfrm_statistics_init(struct net *net)
 {
        int rv;
-
-       if (snmp_mib_init((void __percpu **)net->mib.xfrm_statistics,
-                         sizeof(struct linux_xfrm_mib),
-                         __alignof__(struct linux_xfrm_mib)) < 0)
+       net->mib.xfrm_statistics = alloc_percpu(struct linux_xfrm_mib);
+       if (!net->mib.xfrm_statistics)
                return -ENOMEM;
        rv = xfrm_proc_init(net);
        if (rv < 0)
-               snmp_mib_free((void __percpu **)net->mib.xfrm_statistics);
+               free_percpu(net->mib.xfrm_statistics);
        return rv;
 }
 
 static void xfrm_statistics_fini(struct net *net)
 {
        xfrm_proc_fini(net);
-       snmp_mib_free((void __percpu **)net->mib.xfrm_statistics);
+       free_percpu(net->mib.xfrm_statistics);
 }
 #else
 static int __net_init xfrm_statistics_init(struct net *net)
@@ -2862,21 +2850,14 @@ static int __net_init xfrm_policy_init(struct net *net)
 
 static void xfrm_policy_fini(struct net *net)
 {
-       struct xfrm_audit audit_info;
        unsigned int sz;
        int dir;
 
        flush_work(&net->xfrm.policy_hash_work);
 #ifdef CONFIG_XFRM_SUB_POLICY
-       audit_info.loginuid = INVALID_UID;
-       audit_info.sessionid = (unsigned int)-1;
-       audit_info.secid = 0;
-       xfrm_policy_flush(net, XFRM_POLICY_TYPE_SUB, &audit_info);
+       xfrm_policy_flush(net, XFRM_POLICY_TYPE_SUB, false);
 #endif
-       audit_info.loginuid = INVALID_UID;
-       audit_info.sessionid = (unsigned int)-1;
-       audit_info.secid = 0;
-       xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, &audit_info);
+       xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, false);
 
        WARN_ON(!list_empty(&net->xfrm.policy_all));
 
@@ -2991,15 +2972,14 @@ static void xfrm_audit_common_policyinfo(struct xfrm_policy *xp,
        }
 }
 
-void xfrm_audit_policy_add(struct xfrm_policy *xp, int result,
-                          kuid_t auid, unsigned int sessionid, u32 secid)
+void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, bool task_valid)
 {
        struct audit_buffer *audit_buf;
 
        audit_buf = xfrm_audit_start("SPD-add");
        if (audit_buf == NULL)
                return;
-       xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
+       xfrm_audit_helper_usrinfo(task_valid, audit_buf);
        audit_log_format(audit_buf, " res=%u", result);
        xfrm_audit_common_policyinfo(xp, audit_buf);
        audit_log_end(audit_buf);
@@ -3007,14 +2987,14 @@ void xfrm_audit_policy_add(struct xfrm_policy *xp, int result,
 EXPORT_SYMBOL_GPL(xfrm_audit_policy_add);
 
 void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result,
-                             kuid_t auid, unsigned int sessionid, u32 secid)
+                             bool task_valid)
 {
        struct audit_buffer *audit_buf;
 
        audit_buf = xfrm_audit_start("SPD-delete");
        if (audit_buf == NULL)
                return;
-       xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
+       xfrm_audit_helper_usrinfo(task_valid, audit_buf);
        audit_log_format(audit_buf, " res=%u", result);
        xfrm_audit_common_policyinfo(xp, audit_buf);
        audit_log_end(audit_buf);
index fc5abd0b456f3a3abf8163821f22577b8d700f6f..9c4fbd8935f48e28c3c86d9da443904f55f93ec3 100644 (file)
@@ -54,8 +54,7 @@ static int xfrm_statistics_seq_show(struct seq_file *seq, void *v)
        int i;
        for (i = 0; xfrm_mib_list[i].name; i++)
                seq_printf(seq, "%-24s\t%lu\n", xfrm_mib_list[i].name,
-                          snmp_fold_field((void __percpu **)
-                                          net->mib.xfrm_statistics,
+                          snmp_fold_field(net->mib.xfrm_statistics,
                                           xfrm_mib_list[i].entry));
        return 0;
 }
index 8e9c781a6bbaaba83e4af4a31ac7a07a70dd6c71..0ab54134bb40b84cf825df6035a9acf7cb3cf760 100644 (file)
@@ -463,9 +463,7 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
        if (!err)
                km_state_expired(x, 1, 0);
 
-       xfrm_audit_state_delete(x, err ? 0 : 1,
-                               audit_get_loginuid(current),
-                               audit_get_sessionid(current), 0);
+       xfrm_audit_state_delete(x, err ? 0 : 1, true);
 
 out:
        spin_unlock(&x->lock);
@@ -562,7 +560,7 @@ EXPORT_SYMBOL(xfrm_state_delete);
 
 #ifdef CONFIG_SECURITY_NETWORK_XFRM
 static inline int
-xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audit_info)
+xfrm_state_flush_secctx_check(struct net *net, u8 proto, bool task_valid)
 {
        int i, err = 0;
 
@@ -572,10 +570,7 @@ xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audi
                hlist_for_each_entry(x, net->xfrm.state_bydst+i, bydst) {
                        if (xfrm_id_proto_match(x->id.proto, proto) &&
                           (err = security_xfrm_state_delete(x)) != 0) {
-                               xfrm_audit_state_delete(x, 0,
-                                                       audit_info->loginuid,
-                                                       audit_info->sessionid,
-                                                       audit_info->secid);
+                               xfrm_audit_state_delete(x, 0, task_valid);
                                return err;
                        }
                }
@@ -585,18 +580,18 @@ xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audi
 }
 #else
 static inline int
-xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audit_info)
+xfrm_state_flush_secctx_check(struct net *net, u8 proto, bool task_valid)
 {
        return 0;
 }
 #endif
 
-int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info)
+int xfrm_state_flush(struct net *net, u8 proto, bool task_valid)
 {
        int i, err = 0, cnt = 0;
 
        spin_lock_bh(&net->xfrm.xfrm_state_lock);
-       err = xfrm_state_flush_secctx_check(net, proto, audit_info);
+       err = xfrm_state_flush_secctx_check(net, proto, task_valid);
        if (err)
                goto out;
 
@@ -612,9 +607,7 @@ int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info)
 
                                err = xfrm_state_delete(x);
                                xfrm_audit_state_delete(x, err ? 0 : 1,
-                                                       audit_info->loginuid,
-                                                       audit_info->sessionid,
-                                                       audit_info->secid);
+                                                       task_valid);
                                xfrm_state_put(x);
                                if (!err)
                                        cnt++;
@@ -2128,14 +2121,10 @@ int __net_init xfrm_state_init(struct net *net)
 
 void xfrm_state_fini(struct net *net)
 {
-       struct xfrm_audit audit_info;
        unsigned int sz;
 
        flush_work(&net->xfrm.state_hash_work);
-       audit_info.loginuid = INVALID_UID;
-       audit_info.sessionid = (unsigned int)-1;
-       audit_info.secid = 0;
-       xfrm_state_flush(net, IPSEC_PROTO_ANY, &audit_info);
+       xfrm_state_flush(net, IPSEC_PROTO_ANY, false);
        flush_work(&net->xfrm.state_gc_work);
 
        WARN_ON(!list_empty(&net->xfrm.state_all));
@@ -2198,30 +2187,28 @@ static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,
        }
 }
 
-void xfrm_audit_state_add(struct xfrm_state *x, int result,
-                         kuid_t auid, unsigned int sessionid, u32 secid)
+void xfrm_audit_state_add(struct xfrm_state *x, int result, bool task_valid)
 {
        struct audit_buffer *audit_buf;
 
        audit_buf = xfrm_audit_start("SAD-add");
        if (audit_buf == NULL)
                return;
-       xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
+       xfrm_audit_helper_usrinfo(task_valid, audit_buf);
        xfrm_audit_helper_sainfo(x, audit_buf);
        audit_log_format(audit_buf, " res=%u", result);
        audit_log_end(audit_buf);
 }
 EXPORT_SYMBOL_GPL(xfrm_audit_state_add);
 
-void xfrm_audit_state_delete(struct xfrm_state *x, int result,
-                            kuid_t auid, unsigned int sessionid, u32 secid)
+void xfrm_audit_state_delete(struct xfrm_state *x, int result, bool task_valid)
 {
        struct audit_buffer *audit_buf;
 
        audit_buf = xfrm_audit_start("SAD-delete");
        if (audit_buf == NULL)
                return;
-       xfrm_audit_helper_usrinfo(auid, sessionid, secid, audit_buf);
+       xfrm_audit_helper_usrinfo(task_valid, audit_buf);
        xfrm_audit_helper_sainfo(x, audit_buf);
        audit_log_format(audit_buf, " res=%u", result);
        audit_log_end(audit_buf);
index 51398ae6cda85000b5edc416e9569659495a6614..412d9dc3a8731ec580d4b6596fec491fb10a2a48 100644 (file)
@@ -597,9 +597,6 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
        struct xfrm_state *x;
        int err;
        struct km_event c;
-       kuid_t loginuid = audit_get_loginuid(current);
-       unsigned int sessionid = audit_get_sessionid(current);
-       u32 sid;
 
        err = verify_newsa_info(p, attrs);
        if (err)
@@ -615,8 +612,7 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
        else
                err = xfrm_state_update(x);
 
-       security_task_getsecid(current, &sid);
-       xfrm_audit_state_add(x, err ? 0 : 1, loginuid, sessionid, sid);
+       xfrm_audit_state_add(x, err ? 0 : 1, true);
 
        if (err < 0) {
                x->km.state = XFRM_STATE_DEAD;
@@ -676,9 +672,6 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
        int err = -ESRCH;
        struct km_event c;
        struct xfrm_usersa_id *p = nlmsg_data(nlh);
-       kuid_t loginuid = audit_get_loginuid(current);
-       unsigned int sessionid = audit_get_sessionid(current);
-       u32 sid;
 
        x = xfrm_user_state_lookup(net, p, attrs, &err);
        if (x == NULL)
@@ -703,8 +696,7 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
        km_state_notify(x, &c);
 
 out:
-       security_task_getsecid(current, &sid);
-       xfrm_audit_state_delete(x, err ? 0 : 1, loginuid, sessionid, sid);
+       xfrm_audit_state_delete(x, err ? 0 : 1, true);
        xfrm_state_put(x);
        return err;
 }
@@ -955,6 +947,20 @@ static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb,
        return skb;
 }
 
+/* A wrapper for nlmsg_multicast() checking that nlsk is still available.
+ * Must be called with RCU read lock.
+ */
+static inline int xfrm_nlmsg_multicast(struct net *net, struct sk_buff *skb,
+                                      u32 pid, unsigned int group)
+{
+       struct sock *nlsk = rcu_dereference(net->xfrm.nlsk);
+
+       if (nlsk)
+               return nlmsg_multicast(nlsk, skb, pid, group, GFP_ATOMIC);
+       else
+               return -1;
+}
+
 static inline size_t xfrm_spdinfo_msgsize(void)
 {
        return NLMSG_ALIGN(4)
@@ -1414,9 +1420,6 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
        struct km_event c;
        int err;
        int excl;
-       kuid_t loginuid = audit_get_loginuid(current);
-       unsigned int sessionid = audit_get_sessionid(current);
-       u32 sid;
 
        err = verify_newpolicy_info(p);
        if (err)
@@ -1435,8 +1438,7 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
         * a type XFRM_MSG_UPDPOLICY - JHS */
        excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY;
        err = xfrm_policy_insert(p->dir, xp, excl);
-       security_task_getsecid(current, &sid);
-       xfrm_audit_policy_add(xp, err ? 0 : 1, loginuid, sessionid, sid);
+       xfrm_audit_policy_add(xp, err ? 0 : 1, true);
 
        if (err) {
                security_xfrm_policy_free(xp->security);
@@ -1673,13 +1675,7 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
                                            NETLINK_CB(skb).portid);
                }
        } else {
-               kuid_t loginuid = audit_get_loginuid(current);
-               unsigned int sessionid = audit_get_sessionid(current);
-               u32 sid;
-
-               security_task_getsecid(current, &sid);
-               xfrm_audit_policy_delete(xp, err ? 0 : 1, loginuid, sessionid,
-                                        sid);
+               xfrm_audit_policy_delete(xp, err ? 0 : 1, true);
 
                if (err != 0)
                        goto out;
@@ -1704,13 +1700,9 @@ static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
        struct net *net = sock_net(skb->sk);
        struct km_event c;
        struct xfrm_usersa_flush *p = nlmsg_data(nlh);
-       struct xfrm_audit audit_info;
        int err;
 
-       audit_info.loginuid = audit_get_loginuid(current);
-       audit_info.sessionid = audit_get_sessionid(current);
-       security_task_getsecid(current, &audit_info.secid);
-       err = xfrm_state_flush(net, p->proto, &audit_info);
+       err = xfrm_state_flush(net, p->proto, true);
        if (err) {
                if (err == -ESRCH) /* empty table */
                        return 0;
@@ -1894,16 +1886,12 @@ static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
        struct km_event c;
        u8 type = XFRM_POLICY_TYPE_MAIN;
        int err;
-       struct xfrm_audit audit_info;
 
        err = copy_from_user_policy_type(&type, attrs);
        if (err)
                return err;
 
-       audit_info.loginuid = audit_get_loginuid(current);
-       audit_info.sessionid = audit_get_sessionid(current);
-       security_task_getsecid(current, &audit_info.secid);
-       err = xfrm_policy_flush(net, type, &audit_info);
+       err = xfrm_policy_flush(net, type, true);
        if (err) {
                if (err == -ESRCH) /* empty table */
                        return 0;
@@ -1969,14 +1957,8 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
 
        err = 0;
        if (up->hard) {
-               kuid_t loginuid = audit_get_loginuid(current);
-               unsigned int sessionid = audit_get_sessionid(current);
-               u32 sid;
-
-               security_task_getsecid(current, &sid);
                xfrm_policy_delete(xp, p->dir);
-               xfrm_audit_policy_delete(xp, 1, loginuid, sessionid, sid);
-
+               xfrm_audit_policy_delete(xp, 1, true);
        } else {
                // reset the timers here?
                WARN(1, "Dont know what to do with soft policy expire\n");
@@ -2012,13 +1994,8 @@ static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
        km_state_expired(x, ue->hard, nlh->nlmsg_pid);
 
        if (ue->hard) {
-               kuid_t loginuid = audit_get_loginuid(current);
-               unsigned int sessionid = audit_get_sessionid(current);
-               u32 sid;
-
-               security_task_getsecid(current, &sid);
                __xfrm_state_delete(x);
-               xfrm_audit_state_delete(x, 1, loginuid, sessionid, sid);
+               xfrm_audit_state_delete(x, 1, true);
        }
        err = 0;
 out:
@@ -2265,7 +2242,7 @@ static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
        if (build_migrate(skb, m, num_migrate, k, sel, dir, type) < 0)
                BUG();
 
-       return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MIGRATE, GFP_ATOMIC);
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_MIGRATE);
 }
 #else
 static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
@@ -2456,7 +2433,7 @@ static int xfrm_exp_state_notify(struct xfrm_state *x, const struct km_event *c)
                return -EMSGSIZE;
        }
 
-       return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC);
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE);
 }
 
 static int xfrm_aevent_state_notify(struct xfrm_state *x, const struct km_event *c)
@@ -2471,7 +2448,7 @@ static int xfrm_aevent_state_notify(struct xfrm_state *x, const struct km_event
        if (build_aevent(skb, x, c) < 0)
                BUG();
 
-       return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_AEVENTS, GFP_ATOMIC);
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_AEVENTS);
 }
 
 static int xfrm_notify_sa_flush(const struct km_event *c)
@@ -2497,7 +2474,7 @@ static int xfrm_notify_sa_flush(const struct km_event *c)
 
        nlmsg_end(skb, nlh);
 
-       return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC);
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_SA);
 }
 
 static inline size_t xfrm_sa_len(struct xfrm_state *x)
@@ -2584,7 +2561,7 @@ static int xfrm_notify_sa(struct xfrm_state *x, const struct km_event *c)
 
        nlmsg_end(skb, nlh);
 
-       return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC);
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_SA);
 
 out_free_skb:
        kfree_skb(skb);
@@ -2675,7 +2652,7 @@ static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt,
        if (build_acquire(skb, x, xt, xp) < 0)
                BUG();
 
-       return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_ACQUIRE, GFP_ATOMIC);
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_ACQUIRE);
 }
 
 /* User gives us xfrm_user_policy_info followed by an array of 0
@@ -2789,7 +2766,7 @@ static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, const struct
        if (build_polexpire(skb, xp, dir, c) < 0)
                BUG();
 
-       return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC);
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE);
 }
 
 static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_event *c)
@@ -2851,7 +2828,7 @@ static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_e
 
        nlmsg_end(skb, nlh);
 
-       return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC);
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_POLICY);
 
 out_free_skb:
        kfree_skb(skb);
@@ -2879,7 +2856,7 @@ static int xfrm_notify_policy_flush(const struct km_event *c)
 
        nlmsg_end(skb, nlh);
 
-       return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC);
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_POLICY);
 
 out_free_skb:
        kfree_skb(skb);
@@ -2948,7 +2925,7 @@ static int xfrm_send_report(struct net *net, u8 proto,
        if (build_report(skb, proto, sel, addr) < 0)
                BUG();
 
-       return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_REPORT, GFP_ATOMIC);
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_REPORT);
 }
 
 static inline size_t xfrm_mapping_msgsize(void)
@@ -3000,7 +2977,7 @@ static int xfrm_send_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr,
        if (build_mapping(skb, x, ipaddr, sport) < 0)
                BUG();
 
-       return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MAPPING, GFP_ATOMIC);
+       return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_MAPPING);
 }
 
 static bool xfrm_is_alive(const struct km_event *c)
index 1d07860f6c423661417d11fcfde21a8207f6de43..890df5c6adfbc16769a4abcb27bb3860fc625e71 100644 (file)
@@ -39,4 +39,4 @@ subdir-$(CONFIG_SECURITY_SELINUX) += selinux
 subdir-$(CONFIG_DTC)         += dtc
 
 # Let clean descend into subdirs
-subdir-        += basic kconfig package selinux
+subdir-        += basic kconfig package
index d17e0ea911edd568a039c905330420810223d07f..045e0098e96244be631ae8272c1d94968da3d7e9 100644 (file)
@@ -21,4 +21,3 @@ all: $(patsubst %, $(obj)/%, $(generic-y))
 
 $(obj)/%.h:
        $(call cmd,wrap)
-
index 003bc263105ae3fc5a92e671faa8654ac6809fa5..bf3e6778cd71addc1884b895a65cde06af11525e 100644 (file)
@@ -50,67 +50,6 @@ ifeq ($(KBUILD_NOPEDANTIC),)
         endif
 endif
 
-#
-# make W=... settings
-#
-# W=1 - warnings that may be relevant and does not occur too often
-# W=2 - warnings that occur quite often but may still be relevant
-# W=3 - the more obscure warnings, can most likely be ignored
-#
-# $(call cc-option, -W...) handles gcc -W.. options which
-# are not supported by all versions of the compiler
-ifdef KBUILD_ENABLE_EXTRA_GCC_CHECKS
-warning-  := $(empty)
-
-warning-1 := -Wextra -Wunused -Wno-unused-parameter
-warning-1 += -Wmissing-declarations
-warning-1 += -Wmissing-format-attribute
-warning-1 += $(call cc-option, -Wmissing-prototypes)
-warning-1 += -Wold-style-definition
-warning-1 += $(call cc-option, -Wmissing-include-dirs)
-warning-1 += $(call cc-option, -Wunused-but-set-variable)
-warning-1 += $(call cc-disable-warning, missing-field-initializers)
-
-# Clang
-warning-1 += $(call cc-disable-warning, initializer-overrides)
-warning-1 += $(call cc-disable-warning, unused-value)
-warning-1 += $(call cc-disable-warning, format)
-warning-1 += $(call cc-disable-warning, unknown-warning-option)
-warning-1 += $(call cc-disable-warning, sign-compare)
-warning-1 += $(call cc-disable-warning, format-zero-length)
-warning-1 += $(call cc-disable-warning, uninitialized)
-warning-1 += $(call cc-option, -fcatch-undefined-behavior)
-
-warning-2 := -Waggregate-return
-warning-2 += -Wcast-align
-warning-2 += -Wdisabled-optimization
-warning-2 += -Wnested-externs
-warning-2 += -Wshadow
-warning-2 += $(call cc-option, -Wlogical-op)
-warning-2 += $(call cc-option, -Wmissing-field-initializers)
-
-warning-3 := -Wbad-function-cast
-warning-3 += -Wcast-qual
-warning-3 += -Wconversion
-warning-3 += -Wpacked
-warning-3 += -Wpadded
-warning-3 += -Wpointer-arith
-warning-3 += -Wredundant-decls
-warning-3 += -Wswitch-default
-warning-3 += $(call cc-option, -Wpacked-bitfield-compat)
-warning-3 += $(call cc-option, -Wvla)
-
-warning := $(warning-$(findstring 1, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS)))
-warning += $(warning-$(findstring 2, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS)))
-warning += $(warning-$(findstring 3, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS)))
-
-ifeq ("$(strip $(warning))","")
-        $(error W=$(KBUILD_ENABLE_EXTRA_GCC_CHECKS) is unknown)
-endif
-
-KBUILD_CFLAGS += $(warning)
-endif
-
 include scripts/Makefile.lib
 
 ifdef host-progs
@@ -342,7 +281,7 @@ $(real-objs-m)      : modkern_aflags := $(KBUILD_AFLAGS_MODULE) $(AFLAGS_MODULE)
 $(real-objs-m:.o=.s): modkern_aflags := $(KBUILD_AFLAGS_MODULE) $(AFLAGS_MODULE)
 
 quiet_cmd_as_s_S = CPP $(quiet_modtag) $@
-cmd_as_s_S       = $(CPP) $(a_flags)   -o $@ $< 
+cmd_as_s_S       = $(CPP) $(a_flags)   -o $@ $<
 
 $(obj)/%.s: $(src)/%.S FORCE
        $(call if_changed_dep,as_s_S)
@@ -436,7 +375,7 @@ link_multi_deps =                     \
 $(filter $(addprefix $(obj)/,         \
 $($(subst $(obj)/,,$(@:.o=-objs)))    \
 $($(subst $(obj)/,,$(@:.o=-y)))), $^)
+
 quiet_cmd_link_multi-y = LD      $@
 cmd_link_multi-y = $(LD) $(ld_flags) -r -o $@ $(link_multi_deps) $(cmd_secanalysis)
 
diff --git a/scripts/Makefile.extrawarn b/scripts/Makefile.extrawarn
new file mode 100644 (file)
index 0000000..6564350
--- /dev/null
@@ -0,0 +1,67 @@
+# ==========================================================================
+#
+# make W=... settings
+#
+# W=1 - warnings that may be relevant and does not occur too often
+# W=2 - warnings that occur quite often but may still be relevant
+# W=3 - the more obscure warnings, can most likely be ignored
+#
+# $(call cc-option, -W...) handles gcc -W.. options which
+# are not supported by all versions of the compiler
+# ==========================================================================
+
+ifeq ("$(origin W)", "command line")
+  export KBUILD_ENABLE_EXTRA_GCC_CHECKS := $(W)
+endif
+
+ifdef KBUILD_ENABLE_EXTRA_GCC_CHECKS
+warning-  := $(empty)
+
+warning-1 := -Wextra -Wunused -Wno-unused-parameter
+warning-1 += -Wmissing-declarations
+warning-1 += -Wmissing-format-attribute
+warning-1 += $(call cc-option, -Wmissing-prototypes)
+warning-1 += -Wold-style-definition
+warning-1 += $(call cc-option, -Wmissing-include-dirs)
+warning-1 += $(call cc-option, -Wunused-but-set-variable)
+warning-1 += $(call cc-disable-warning, missing-field-initializers)
+
+# Clang
+warning-1 += $(call cc-disable-warning, initializer-overrides)
+warning-1 += $(call cc-disable-warning, unused-value)
+warning-1 += $(call cc-disable-warning, format)
+warning-1 += $(call cc-disable-warning, unknown-warning-option)
+warning-1 += $(call cc-disable-warning, sign-compare)
+warning-1 += $(call cc-disable-warning, format-zero-length)
+warning-1 += $(call cc-disable-warning, uninitialized)
+warning-1 += $(call cc-option, -fcatch-undefined-behavior)
+
+warning-2 := -Waggregate-return
+warning-2 += -Wcast-align
+warning-2 += -Wdisabled-optimization
+warning-2 += -Wnested-externs
+warning-2 += -Wshadow
+warning-2 += $(call cc-option, -Wlogical-op)
+warning-2 += $(call cc-option, -Wmissing-field-initializers)
+
+warning-3 := -Wbad-function-cast
+warning-3 += -Wcast-qual
+warning-3 += -Wconversion
+warning-3 += -Wpacked
+warning-3 += -Wpadded
+warning-3 += -Wpointer-arith
+warning-3 += -Wredundant-decls
+warning-3 += -Wswitch-default
+warning-3 += $(call cc-option, -Wpacked-bitfield-compat)
+warning-3 += $(call cc-option, -Wvla)
+
+warning := $(warning-$(findstring 1, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS)))
+warning += $(warning-$(findstring 2, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS)))
+warning += $(warning-$(findstring 3, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS)))
+
+ifeq ("$(strip $(warning))","")
+        $(error W=$(KBUILD_ENABLE_EXTRA_GCC_CHECKS) is unknown)
+endif
+
+KBUILD_CFLAGS += $(warning)
+endif
index 4d908d16c035c4e6c582e254e531998e7f6156ae..d8e335eed22632a495c341c21d995a076ef7cb57 100644 (file)
@@ -18,31 +18,29 @@ include $(srctree)/$(obj)/Makefile
 include scripts/Makefile.host
 
 mod-fw := $(fw-shipped-m)
-# If CONFIG_FIRMWARE_IN_KERNEL isn't set, then install the 
+# If CONFIG_FIRMWARE_IN_KERNEL isn't set, then install the
 # firmware for in-kernel drivers too.
 ifndef CONFIG_FIRMWARE_IN_KERNEL
 mod-fw += $(fw-shipped-y)
 endif
 
+ifneq ($(KBUILD_SRC),)
+# Create output directory if not already present
+_dummy := $(shell [ -d $(obj) ] || mkdir -p $(obj))
+
+firmware-dirs := $(sort $(addprefix $(objtree)/$(obj)/,$(dir $(fw-external-y) $(fw-shipped-all))))
+# Create directories for firmware in subdirectories
+_dummy := $(foreach d,$(firmware-dirs), $(shell [ -d $(d) ] || mkdir -p $(d)))
+endif
+
 installed-mod-fw := $(addprefix $(INSTALL_FW_PATH)/,$(mod-fw))
 
 installed-fw := $(addprefix $(INSTALL_FW_PATH)/,$(fw-shipped-all))
-installed-fw-dirs := $(sort $(dir $(installed-fw))) $(INSTALL_FW_PATH)/./
-
-# Workaround for make < 3.81, where .SECONDEXPANSION doesn't work.
-PHONY += $(INSTALL_FW_PATH)/$$(%) install-all-dirs
-$(INSTALL_FW_PATH)/$$(%): install-all-dirs
-       @true
-install-all-dirs: $(installed-fw-dirs)
-       @true
 
 quiet_cmd_install = INSTALL $(subst $(srctree)/,,$@)
-      cmd_install = $(INSTALL) -m0644 $< $@
-
-$(installed-fw-dirs):
-       $(call cmd,mkdir)
+      cmd_install = mkdir -p $(@D); $(INSTALL) -m0644 $< $@
 
-$(installed-fw): $(INSTALL_FW_PATH)/%: $(obj)/% | $(INSTALL_FW_PATH)/$$(dir %)
+$(installed-fw): $(INSTALL_FW_PATH)/%: $(obj)/%
        $(call cmd,install)
 
 PHONY +=  __fw_install __fw_modinst FORCE
index 1ac414fd50300384663248c0ac651c9887bfe45d..66893643fd7d14a6f59bb52a17b0393180e7051d 100644 (file)
@@ -166,5 +166,4 @@ $(host-cshlib): $(obj)/%: $(host-cshobjs) FORCE
        $(call if_changed,host-cshlib)
 
 targets += $(host-csingle)  $(host-cmulti) $(host-cobjs)\
-          $(host-cxxmulti) $(host-cxxobjs) $(host-cshlib) $(host-cshobjs) 
-
+          $(host-cxxmulti) $(host-cxxobjs) $(host-cshlib) $(host-cshobjs)
index 6a5b0decb797227326acb4f8511256afb003b989..260bf8acfce96cfcbae0eb2c3c6754e96e649317 100644 (file)
@@ -27,7 +27,7 @@ lib-y := $(filter-out $(obj-y), $(sort $(lib-y) $(lib-m)))
 # ---------------------------------------------------------------------------
 # o if we encounter foo/ in $(obj-y), replace it by foo/built-in.o
 #   and add the directory to the list of dirs to descend into: $(subdir-y)
-# o if we encounter foo/ in $(obj-m), remove it from $(obj-m) 
+# o if we encounter foo/ in $(obj-m), remove it from $(obj-m)
 #   and add the directory to the list of dirs to descend into: $(subdir-m)
 
 # Determine modorder.
@@ -46,7 +46,7 @@ obj-m         := $(filter-out %/, $(obj-m))
 
 subdir-ym      := $(sort $(subdir-y) $(subdir-m))
 
-# if $(foo-objs) exists, foo.o is a composite object 
+# if $(foo-objs) exists, foo.o is a composite object
 multi-used-y := $(sort $(foreach m,$(obj-y), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))), $(m))))
 multi-used-m := $(sort $(foreach m,$(obj-m), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))), $(m))))
 multi-used   := $(multi-used-y) $(multi-used-m)
@@ -91,7 +91,7 @@ obj-dirs      := $(addprefix $(obj)/,$(obj-dirs))
 
 # These flags are needed for modversions and compiling, so we define them here
 # already
-# $(modname_flags) #defines KBUILD_MODNAME as the name of the module it will 
+# $(modname_flags) #defines KBUILD_MODNAME as the name of the module it will
 # end up in (or would, if it gets compiled in)
 # Note: Files that end up in two or more modules are compiled without the
 #       KBUILD_MODNAME definition. The reason is that any made-up name would
@@ -212,7 +212,7 @@ $(obj)/%: $(src)/%_shipped
 
 # Commands useful for building a boot image
 # ===========================================================================
-# 
+#
 #      Use as following:
 #
 #      target: source(s) FORCE
@@ -226,7 +226,7 @@ $(obj)/%: $(src)/%_shipped
 
 quiet_cmd_ld = LD      $@
 cmd_ld = $(LD) $(LDFLAGS) $(ldflags-y) $(LDFLAGS_$(@F)) \
-              $(filter-out FORCE,$^) -o $@ 
+              $(filter-out FORCE,$^) -o $@
 
 # Objcopy
 # ---------------------------------------------------------------------------
index 078fe1d64e7df3e38e94dcd595bb834e5bd9da9c..b30406860b7397881e4e6ffeedcda1f5b2959cc0 100644 (file)
@@ -409,10 +409,10 @@ static void print_deps(void)
                exit(2);
        }
        if (fstat(fd, &st) < 0) {
-                fprintf(stderr, "fixdep: error fstat'ing depfile: ");
-                perror(depfile);
-                exit(2);
-        }
+               fprintf(stderr, "fixdep: error fstat'ing depfile: ");
+               perror(depfile);
+               exit(2);
+       }
        if (st.st_size == 0) {
                fprintf(stderr,"fixdep: %s is empty\n",depfile);
                close(fd);
index 544aa56b6200a0610a1b0841ce34ed312955c3f0..c05d586b1feeddd92d9d030d77e0b31b9ea0eb58 100755 (executable)
@@ -173,4 +173,3 @@ while (my $line = <STDIN>) {
 
 # Sort output by size (last field)
 print sort { ($b =~ /:\t*(\d+)$/)[0] <=> ($a =~ /:\t*(\d+)$/)[0] } @stack;
-
diff --git a/scripts/coccinelle/misc/of_table.cocci b/scripts/coccinelle/misc/of_table.cocci
new file mode 100644 (file)
index 0000000..3c93404
--- /dev/null
@@ -0,0 +1,62 @@
+/// Make sure of_device_id tables are NULL terminated
+//
+// Keywords: of_table
+// Confidence: Medium
+// Options: --include-headers
+
+virtual patch
+virtual context
+virtual org
+virtual report
+
+@depends on context@
+identifier var, arr;
+expression E;
+@@
+struct of_device_id arr[] = {
+       ...,
+       {
+       .var = E,
+*      }
+};
+
+@depends on patch@
+identifier var, arr;
+expression E;
+@@
+struct of_device_id arr[] = {
+       ...,
+       {
+       .var = E,
+-      }
++      },
++      { }
+};
+
+@r depends on org || report@
+position p1;
+identifier var, arr;
+expression E;
+@@
+struct of_device_id arr[] = {
+       ...,
+       {
+       .var = E,
+       }
+       @p1
+};
+
+@script:python depends on org@
+p1 << r.p1;
+arr << r.arr;
+@@
+
+cocci.print_main(arr,p1)
+
+@script:python depends on report@
+p1 << r.p1;
+arr << r.arr;
+@@
+
+msg = "%s is not NULL terminated at line %s" % (arr, p1[0].line)
+coccilib.report.print_report(p1[0],msg)
diff --git a/scripts/coccinelle/misc/returnvar.cocci b/scripts/coccinelle/misc/returnvar.cocci
new file mode 100644 (file)
index 0000000..605955a
--- /dev/null
@@ -0,0 +1,66 @@
+///
+/// Removes unneeded variable used to store return value.
+///
+// Confidence: Moderate
+// Copyright: (C) 2012 Peter Senna Tschudin, INRIA/LIP6.  GPLv2.
+// URL: http://coccinelle.lip6.fr/
+// Comments: Comments on code can be deleted if near code that is removed.
+//           "when strict" can be removed to get more hits, but adds false
+//           positives
+// Options: --no-includes --include-headers
+
+virtual patch
+virtual report
+virtual context
+virtual org
+
+@depends on patch@
+type T;
+constant C;
+identifier ret;
+@@
+- T ret = C;
+... when != ret
+    when strict
+return
+- ret
++ C
+;
+
+@depends on context@
+type T;
+constant C;
+identifier ret;
+@@
+* T ret = C;
+... when != ret
+    when strict
+* return ret;
+
+@r1 depends on report || org@
+type T;
+constant C;
+identifier ret;
+position p1, p2;
+@@
+T ret@p1 = C;
+... when != ret
+    when strict
+return ret@p2;
+
+@script:python depends on report@
+p1 << r1.p1;
+p2 << r1.p2;
+C << r1.C;
+ret << r1.ret;
+@@
+coccilib.report.print_report(p1[0], "Unneeded variable: \"" + ret + "\". Return \"" + C + "\" on line " + p2[0].line)
+
+@script:python depends on org@
+p1 << r1.p1;
+p2 << r1.p2;
+C << r1.C;
+ret << r1.ret;
+@@
+cocci.print_main("unneeded \"" + ret + "\" variable", p1)
+cocci.print_sec("return " + C + " here", p2)
index 68041793698c5f22099782129b03c81b8b4bca54..026aeb4f32ee3fa056e57c84c1283e2d04181f15 100755 (executable)
@@ -223,4 +223,3 @@ while [ "$1" != "" ] ; do
                ;;
        esac
 done
-
index 263a44d57fa95dea88411e7df0527eb1528cb474..61bbda54cf13ead802ba096fe8dad55bd5241488 100644 (file)
@@ -104,7 +104,7 @@ int main(int argc, char *argv[])
        }
     }
 
-  /* For now we assume the default font is always 256 characters. */    
+  /* For now we assume the default font is always 256 characters. */
   fontlen = 256;
 
   /* Initialize table */
@@ -236,15 +236,15 @@ int main(int argc, char *argv[])
     }
 
   /* Okay, we hit EOF, now output hash table */
-  
+
   fclose(ctbl);
-  
+
 
   /* Compute total size of Unicode list */
   nuni = 0;
   for ( i = 0 ; i < fontlen ; i++ )
     nuni += unicount[i];
-  
+
   printf("\
 /*\n\
  * Do not edit this file; it was automatically generated by\n\
@@ -268,9 +268,9 @@ u8 dfont_unicount[%d] = \n\
       else
         printf(", ");
     }
-  
+
   printf("\nu16 dfont_unitable[%d] = \n{\n\t", nuni);
-  
+
   fp0 = 0;
   nent = 0;
   for ( i = 0 ; i < nuni ; i++ )
index 2b69eaf5b646e309d95fc9fda3c5a94efce236f6..e267e621431a6b83768ac5efa5d37eba6fb2f55c 100644 (file)
@@ -154,7 +154,7 @@ int symfilecnt = 0;
 static void add_new_symbol(struct symfile *sym, char * symname)
 {
        sym->symbollist =
-          realloc(sym->symbollist, (sym->symbolcnt + 1) * sizeof(char *));
+         realloc(sym->symbollist, (sym->symbolcnt + 1) * sizeof(char *));
        sym->symbollist[sym->symbolcnt++].name = strdup(symname);
 }
 
@@ -215,7 +215,7 @@ static void find_export_symbols(char * filename)
                        char *p;
                        char *e;
                        if (((p = strstr(line, "EXPORT_SYMBOL_GPL")) != NULL) ||
-                            ((p = strstr(line, "EXPORT_SYMBOL")) != NULL)) {
+                           ((p = strstr(line, "EXPORT_SYMBOL")) != NULL)) {
                                /* Skip EXPORT_SYMBOL{_GPL} */
                                while (isalnum(*p) || *p == '_')
                                        p++;
@@ -291,28 +291,28 @@ static void extfunc(char * filename) { docfunctions(filename, FUNCTION);   }
 static void singfunc(char * filename, char * line)
 {
        char *vec[200]; /* Enough for specific functions */
-        int i, idx = 0;
-        int startofsym = 1;
+       int i, idx = 0;
+       int startofsym = 1;
        vec[idx++] = KERNELDOC;
        vec[idx++] = DOCBOOK;
        vec[idx++] = SHOWNOTFOUND;
 
-        /* Split line up in individual parameters preceded by FUNCTION */
-        for (i=0; line[i]; i++) {
-                if (isspace(line[i])) {
-                        line[i] = '\0';
-                        startofsym = 1;
-                        continue;
-                }
-                if (startofsym) {
-                        startofsym = 0;
-                        vec[idx++] = FUNCTION;
-                        vec[idx++] = &line[i];
-                }
-        }
+       /* Split line up in individual parameters preceded by FUNCTION */
+       for (i=0; line[i]; i++) {
+               if (isspace(line[i])) {
+                       line[i] = '\0';
+                       startofsym = 1;
+                       continue;
+               }
+               if (startofsym) {
+                       startofsym = 0;
+                       vec[idx++] = FUNCTION;
+                       vec[idx++] = &line[i];
+               }
+       }
        for (i = 0; i < idx; i++) {
-               if (strcmp(vec[i], FUNCTION))
-                       continue;
+               if (strcmp(vec[i], FUNCTION))
+                       continue;
                consume_symbol(vec[i + 1]);
        }
        vec[idx++] = filename;
@@ -460,14 +460,14 @@ static void parse_file(FILE *infile)
                                        break;
                                case 'D':
                                        while (*s && !isspace(*s)) s++;
-                                        *s = '\0';
-                                        symbolsonly(line+2);
-                                        break;
+                                       *s = '\0';
+                                       symbolsonly(line+2);
+                                       break;
                                case 'F':
                                        /* filename */
                                        while (*s && !isspace(*s)) s++;
                                        *s++ = '\0';
-                                        /* function names */
+                                       /* function names */
                                        while (isspace(*s))
                                                s++;
                                        singlefunctions(line +2, s);
@@ -515,11 +515,11 @@ int main(int argc, char *argv[])
        }
        /* Open file, exit on error */
        infile = fopen(argv[2], "r");
-        if (infile == NULL) {
-                fprintf(stderr, "docproc: ");
-                perror(argv[2]);
-                exit(2);
-        }
+       if (infile == NULL) {
+               fprintf(stderr, "docproc: ");
+               perror(argv[2]);
+               exit(2);
+       }
 
        if (strcmp("doc", argv[1]) == 0) {
                /* Need to do this in two passes.
index 095acb49a374bb228efc6c10665bde0640b7bca7..cdabdc95a6e741b506aedccb5cc2d014c8ad348b 100644 (file)
@@ -2,4 +2,3 @@ dtc
 dtc-lexer.lex.c
 dtc-parser.tab.c
 dtc-parser.tab.h
-
index f3774530170ab6ac7465ae31cb5777bbe48614d0..e464727c8808cbcd06e184b004fb7261c4472288 100644 (file)
@@ -88,4 +88,3 @@ struct boot_info *dt_from_fs(const char *dirname)
 
        return build_boot_info(NULL, tree, guess_boot_cpuid(tree));
 }
-
index f72d13b1d19c0bce7a27658e18a5f9fe26b2453c..f2ae9b77c285733e50b4b496407471149650d290 100644 (file)
@@ -81,4 +81,3 @@ int fdt_create_empty_tree(void *buf, int bufsize)
 
        return fdt_open_into(buf, buf, bufsize);
 }
-
index 33eeba55fb4dd6482193902e784dbb1a62368f3e..5740e6992d37c157d5395572341a8d53cd5d10d8 100644 (file)
@@ -281,4 +281,3 @@ void dt_to_source(FILE *f, struct boot_info *bi)
 
        write_tree_source_node(f, bi->dt, 0);
 }
-
index 978b42b3acd7e59519bdc74540a180da25e8c607..95ece06599a58b78f5f4e3955c33acecda34af10 100755 (executable)
@@ -28,5 +28,3 @@ for arch in ${archs}; do
                ;;
        esac
 done
-
-
index 1237dd7fb4cac7c2f863b413399df17a9e0c8beb..dc7aa45e80ce5e1acd4031e9774aabff7217b492 100644 (file)
@@ -123,7 +123,7 @@ static int read_symbol(FILE *in, struct sym_entry *s)
        }
        if (strlen(str) > KSYM_NAME_LEN) {
                fprintf(stderr, "Symbol %s too long for kallsyms (%zu vs %d).\n"
-                                "Please increase KSYM_NAME_LEN both in kernel and kallsyms.c\n",
+                               "Please increase KSYM_NAME_LEN both in kernel and kallsyms.c\n",
                        str, strlen(str), KSYM_NAME_LEN);
                return -1;
        }
index 844bc9da08dacf866e9f769318faa15f90e5c136..9c4d2412fb724e8b8d93cd02b70488ba3f779cb9 100644 (file)
@@ -33,11 +33,11 @@ oldconfig: $(obj)/conf
        $< --$@ $(Kconfig)
 
 silentoldconfig: $(obj)/conf
-       $(Q)mkdir -p include/generated
+       $(Q)mkdir -p include/config include/generated
        $< --$@ $(Kconfig)
 
 localyesconfig localmodconfig: $(obj)/streamline_config.pl $(obj)/conf
-       $(Q)mkdir -p include/generated
+       $(Q)mkdir -p include/config include/generated
        $(Q)perl $< --$@ $(srctree) $(Kconfig) > .tmp.config
        $(Q)if [ -f .config ]; then                                     \
                        cmp -s .tmp.config .config ||                   \
@@ -319,4 +319,3 @@ $(obj)/%.moc: $(src)/%.h $(obj)/.tmp_qtcheck
 $(obj)/gconf.glade.h: $(obj)/gconf.glade
        $(Q)intltool-extract --type=gettext/glade --srcdir=$(srctree) \
        $(obj)/gconf.glade
-
index 854d9c7c675ca4782702e83e1fa04b17aa47d6f8..55b79ba1ba2a5e4c84cb6cc3bf5f25dbb92a3e89 100755 (executable)
@@ -11,4 +11,3 @@ EOF
 if [ ! "$?" -eq "0"  ]; then
        echo -DKBUILD_NO_NLS;
 fi
-
index d19944f9c3acb9e6004ca13a1f6e4444174a5359..fef75fc756f40bfa5ae89c942e49f80719204bb9 100644 (file)
@@ -696,7 +696,7 @@ int main(int ac, char **av)
        } else if (input_mode == savedefconfig) {
                if (conf_write_defconfig(defconfig_file)) {
                        fprintf(stderr, _("n*** Error while saving defconfig to: %s\n\n"),
-                               defconfig_file);
+                               defconfig_file);
                        return 1;
                }
        } else if (input_mode != listnewconfig) {
index f2bee70e26f4940061c97add6dafbf273b272dcd..d0a35b21f3087283c286885c26caa58630925231 100644 (file)
@@ -1404,7 +1404,7 @@ static void display_tree(struct menu *menu)
                    && (tree == tree2))
                        continue;
 /*
-                if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT))
+               if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT))
                    || (view_mode == FULL_VIEW)
                    || (view_mode == SPLIT_VIEW))*/
 
index 3b15c08ec1fac369f9b6e0b01c738e46fa3efb2d..8d016faa28d704be886cb1c6195a3bdca78d29b0 100644 (file)
@@ -168,13 +168,13 @@ int dialog_checklist(const char *title, const char *prompt, int height,
 
        /* create new window for the list */
        list = subwin(dialog, list_height, list_width, y + box_y + 1,
-                     x + box_x + 1);
+                     x + box_x + 1);
 
        keypad(list, TRUE);
 
        /* draw a box around the list items */
        draw_box(dialog, box_y, box_x, list_height + 2, list_width + 2,
-                dlg.menubox_border.atr, dlg.menubox.atr);
+                dlg.menubox_border.atr, dlg.menubox.atr);
 
        /* Find length of longest item in order to center checklist */
        check_x = 0;
index 447a582198c9bc70c37c4d2d034b128b5a62080d..d58de1dc5360dacf90fce2c177e3fb6c3ed8c83e 100644 (file)
@@ -42,7 +42,7 @@ static void print_buttons(WINDOW * dialog, int height, int width, int selected)
  * Display a dialog box for inputing a string
  */
 int dialog_inputbox(const char *title, const char *prompt, int height, int width,
-                    const char *init)
+                   const char *init)
 {
        int i, x, y, box_y, box_x, box_width;
        int input_x = 0, key = 0, button = -1;
index c93de0b2faca28525588bceb023f99bdd8a14acf..11ae9ad7ac7b992e2244ae88c0f576977157c254 100644 (file)
@@ -64,7 +64,7 @@ static int menu_width, item_x;
  * Print menu item
  */
 static void do_print_item(WINDOW * win, const char *item, int line_y,
-                          int selected, int hotkey)
+                         int selected, int hotkey)
 {
        int j;
        char *menu_item = malloc(menu_width + 1);
@@ -182,7 +182,7 @@ static void do_scroll(WINDOW *win, int *scroll, int n)
  * Display a menu for choosing among a number of options
  */
 int dialog_menu(const char *title, const char *prompt,
-                const void *selected, int *s_scroll)
+               const void *selected, int *s_scroll)
 {
        int i, j, x, y, box_x, box_y;
        int height, width, menu_height;
index 58a8289dd650281c29f8e34827c153e9b4d4ac74..f7abdeb92af02188a48712ce48ab76f6b32f1d27 100644 (file)
@@ -623,7 +623,7 @@ void item_make(const char *fmt, ...)
 void item_add_str(const char *fmt, ...)
 {
        va_list ap;
-        size_t avail;
+       size_t avail;
 
        avail = sizeof(item_cur->node.str) - strlen(item_cur->node.str);
 
index 59184bb41ef81054384bcd641097f95e9e5426a2..14cea7463a621b33c6c0b8487781db107d764841 100644 (file)
@@ -299,7 +299,7 @@ static void set_config_filename(const char *config_filename)
        int size;
 
        size = snprintf(menu_backtitle, sizeof(menu_backtitle),
-                       "%s - %s", config_filename, rootmenu.prompt->text);
+                       "%s - %s", config_filename, rootmenu.prompt->text);
        if (size >= sizeof(menu_backtitle))
                menu_backtitle[sizeof(menu_backtitle)-1] = '\0';
        set_dialog_backtitle(menu_backtitle);
@@ -1034,4 +1034,3 @@ int main(int ac, char **av)
 
        return res;
 }
-
index 3ac2c9c6e280300275920d2ea1cadb0c7be66746..a26cc5d2a9b0217d9c3d52bf0dab21b7337e95d2 100644 (file)
@@ -258,8 +258,8 @@ static void sym_check_prop(struct symbol *sym)
                                    "config symbol '%s' uses select, but is "
                                    "not boolean or tristate", sym->name);
                        else if (sym2->type != S_UNKNOWN &&
-                                sym2->type != S_BOOLEAN &&
-                                sym2->type != S_TRISTATE)
+                                sym2->type != S_BOOLEAN &&
+                                sym2->type != S_TRISTATE)
                                prop_warn(prop,
                                    "'%s' has wrong type. 'select' only "
                                    "accept arguments of boolean and "
@@ -268,7 +268,7 @@ static void sym_check_prop(struct symbol *sym)
                case P_RANGE:
                        if (sym->type != S_INT && sym->type != S_HEX)
                                prop_warn(prop, "range is only allowed "
-                                               "for int or hex symbols");
+                                               "for int or hex symbols");
                        if (!menu_validate_number(sym, prop->expr->left.sym) ||
                            !menu_validate_number(sym, prop->expr->right.sym))
                                prop_warn(prop, "range is invalid");
index 4fbecd2473bc51904629056eaa8bacb7b42fb660..984489ef2b46e12549eedd74a2d8691ba0589489 100644 (file)
@@ -1554,4 +1554,3 @@ int main(int ac, char **av)
        endwin();
        return 0;
 }
-
index 31331723e810746d08f46c01928c20b2555018a8..9cb8522d8d22a826caaec968e82c77516339ca01 100644 (file)
@@ -589,7 +589,7 @@ while ($repeat) {
 
     # Now we need to see if we have to check selects;
     loop_select;
-}          
+}
 
 my %setconfigs;
 
index 6e7fbf1968090463f37ec1af70d65fb0af28a391..94f9c83e324f4a72b79b6fbf8ca6b3999d046702 100644 (file)
@@ -155,5 +155,3 @@ void *xcalloc(size_t nmemb, size_t size)
        fprintf(stderr, "Out of memory.\n");
        exit(1);
 }
-
-
index 1a9f53e535ca8ba87adc9b1af2bc761f128e56a0..6c62d93b4ffbd018807993788fb78848d176d67d 100644 (file)
@@ -27,8 +27,8 @@ static char *text;
 static int text_size, text_asize;
 
 struct buffer {
-        struct buffer *parent;
-        YY_BUFFER_STATE state;
+       struct buffer *parent;
+       YY_BUFFER_STATE state;
 };
 
 struct buffer *current_buf;
index a0521aa5974b514e7bfcb1811746c283702862f2..349a7f24315b1d1c4887962626365fd749fdae02 100644 (file)
@@ -789,8 +789,8 @@ static char *text;
 static int text_size, text_asize;
 
 struct buffer {
-        struct buffer *parent;
-        YY_BUFFER_STATE state;
+       struct buffer *parent;
+       YY_BUFFER_STATE state;
 };
 
 struct buffer *current_buf;
index 25ae16ac75c85853e24733eb754bc514ba6eb419..de5e84ed3f96f824b63a1e5bd2e4da597996747f 100644 (file)
@@ -2314,7 +2314,7 @@ void conf_parse(const char *name)
        for_all_symbols(i, sym) {
                if (sym_check_deps(sym))
                        zconfnerrs++;
-        }
+       }
        if (zconfnerrs)
                exit(1);
        sym_set_change_count(1);
index 0653886fac4810b0989af918708f4dbfd39f145b..0f683cfa53e9abf9aecc032e03334fca182c0d20 100644 (file)
@@ -510,7 +510,7 @@ void conf_parse(const char *name)
        for_all_symbols(i, sym) {
                if (sym_check_deps(sym))
                        zconfnerrs++;
-        }
+       }
        if (zconfnerrs)
                exit(1);
        sym_set_change_count(1);
index 827896f56501aaebba885e70adc24a477fd31884..c21d16328d3f2bae0ccaba40029a7e1f83229b6a 100644 (file)
@@ -367,4 +367,3 @@ OPTION:
 EOT
        exit;
 }
-
index cfb8440cc0b2cf5611db59014d4b83fd27af2e9d..6fdc97ef6023da95f708dcbe9b1cc0634a99f0f2 100755 (executable)
@@ -68,7 +68,7 @@ UTS_TRUNCATE="cut -b -$UTS_LEN"
 
 ( echo /\* This file is auto generated, version $VERSION \*/
   if [ -n "$CONFIG_FLAGS" ] ; then echo "/* $CONFIG_FLAGS */"; fi
-  
+
   echo \#define UTS_MACHINE \"$ARCH\"
 
   echo \#define UTS_VERSION \"`echo $UTS_VERSION | $UTS_TRUNCATE`\"
@@ -84,7 +84,7 @@ UTS_TRUNCATE="cut -b -$UTS_LEN"
 # recompilations.
 # We don't consider the file changed if only the date/time changed.
 # A kernel config change will increase the generation number, thus
-# causing compile.h to be updated (including date/time) due to the 
+# causing compile.h to be updated (including date/time) due to the
 # changed comment in the
 # first line.
 
index 0cc04426074403458583b73339754c2a588bfa13..84af27bf0f99fc8983564eebaf63aed7797e7a30 100644 (file)
@@ -42,18 +42,11 @@ MAKEARGS += O=\$(if \$(patsubst /%,,\$(makedir)),\$(CURDIR)/)\$(patsubst %/,%,\$
 
 MAKEFLAGS += --no-print-directory
 
-.PHONY: all \$(MAKECMDGOALS)
+.PHONY: __sub-make \$(MAKECMDGOALS)
 
-all    := \$(filter-out all Makefile,\$(MAKECMDGOALS))
+__sub-make:
+       \$(Q)\$(MAKE) \$(MAKEARGS) \$(MAKECMDGOALS)
 
-all:
-       \$(Q)\$(MAKE) \$(MAKEARGS) \$(all)
-
-Makefile:;
-
-\$(all): all
-       @:
-
-%/: all
+\$(filter-out __sub-make, \$(MAKECMDGOALS)): __sub-make
        @:
 EOF
index c1b6191ef8792a48da0805f711a49b56aaefd3c9..7ada35a0f47802e64ea360a04d0b5b0afd7aae6f 100644 (file)
@@ -42,4 +42,3 @@
 # (At least sparc64 has __crc_ in the middle).
 
 $NM -n $1 | grep -v '\( [aNUw] \)\|\(__crc_\)\|\( \$[adt]\)' > $2
-
index 33bae0df4de5bb5750e75247c641e12c8098d736..3bd11b6031734a0bf0e12b9427387a74149cdfbf 100644 (file)
@@ -2,4 +2,3 @@ elfconfig.h
 mk_elfconfig
 modpost
 devicetable-offsets.h
-
index 1924990a737f36c61820bd35470c9adf90f49888..e614ef689eee1f16dc19daf1c00be4460a184f63 100644 (file)
@@ -644,28 +644,26 @@ ADD_TO_DEVTABLE("pcmcia", pcmcia_device_id, do_pcmcia_entry);
 
 static int do_of_entry (const char *filename, void *symval, char *alias)
 {
-    int len;
-    char *tmp;
-    DEF_FIELD_ADDR(symval, of_device_id, name);
-    DEF_FIELD_ADDR(symval, of_device_id, type);
-    DEF_FIELD_ADDR(symval, of_device_id, compatible);
-
-    len = sprintf (alias, "of:N%sT%s",
-                    (*name)[0] ? *name : "*",
-                    (*type)[0] ? *type : "*");
-
-    if (compatible[0])
-        sprintf (&alias[len], "%sC%s",
-                     (*type)[0] ? "*" : "",
-                     *compatible);
-
-    /* Replace all whitespace with underscores */
-    for (tmp = alias; tmp && *tmp; tmp++)
-        if (isspace (*tmp))
-            *tmp = '_';
-
-    add_wildcard(alias);
-    return 1;
+       int len;
+       char *tmp;
+       DEF_FIELD_ADDR(symval, of_device_id, name);
+       DEF_FIELD_ADDR(symval, of_device_id, type);
+       DEF_FIELD_ADDR(symval, of_device_id, compatible);
+
+       len = sprintf(alias, "of:N%sT%s", (*name)[0] ? *name : "*",
+                     (*type)[0] ? *type : "*");
+
+       if (compatible[0])
+               sprintf(&alias[len], "%sC%s", (*type)[0] ? "*" : "",
+                       *compatible);
+
+       /* Replace all whitespace with underscores */
+       for (tmp = alias; tmp && *tmp; tmp++)
+               if (isspace (*tmp))
+                       *tmp = '_';
+
+       add_wildcard(alias);
+       return 1;
 }
 ADD_TO_DEVTABLE("of", of_device_id, do_of_entry);
 
index 639bca7ba559198f4b3013e72556e0e24ba557c6..a4fd71d71d65951756f476dceef3fefa7c1d465c 100644 (file)
@@ -54,4 +54,3 @@ main(int argc, char **argv)
 
        return 0;
 }
-
index 026543ba8d8687b6218192d5dd4dd212c442e329..9d9c5b905b359e58b9eb98eac457ef81d7f8179b 100644 (file)
@@ -862,7 +862,7 @@ static const char *section_white_list[] =
  * without "ax" / "aw".
  */
 static void check_section(const char *modname, struct elf_info *elf,
-                          Elf_Shdr *sechdr)
+                         Elf_Shdr *sechdr)
 {
        const char *sec = sech_name(elf, sechdr);
 
@@ -1296,12 +1296,12 @@ static void print_section_list(const char * const list[20])
  */
 static void report_sec_mismatch(const char *modname,
                                const struct sectioncheck *mismatch,
-                                const char *fromsec,
-                                unsigned long long fromaddr,
-                                const char *fromsym,
-                                int from_is_func,
-                                const char *tosec, const char *tosym,
-                                int to_is_func)
+                               const char *fromsec,
+                               unsigned long long fromaddr,
+                               const char *fromsym,
+                               int from_is_func,
+                               const char *tosec, const char *tosym,
+                               int to_is_func)
 {
        const char *from, *from_p;
        const char *to, *to_p;
@@ -1441,7 +1441,7 @@ static void report_sec_mismatch(const char *modname,
 }
 
 static void check_section_mismatch(const char *modname, struct elf_info *elf,
-                                   Elf_Rela *r, Elf_Sym *sym, const char *fromsec)
+                                  Elf_Rela *r, Elf_Sym *sym, const char *fromsec)
 {
        const char *tosec;
        const struct sectioncheck *mismatch;
@@ -1528,7 +1528,7 @@ static int addend_arm_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r)
        case R_ARM_ABS32:
                /* From ARM ABI: (S + A) | T */
                r->r_addend = (int)(long)
-                             (elf->symtab_start + ELF_R_SYM(r->r_info));
+                             (elf->symtab_start + ELF_R_SYM(r->r_info));
                break;
        case R_ARM_PC24:
        case R_ARM_CALL:
@@ -1538,8 +1538,8 @@ static int addend_arm_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r)
        case R_ARM_THM_JUMP19:
                /* From ARM ABI: ((S + A) | T) - P */
                r->r_addend = (int)(long)(elf->hdr +
-                             sechdr->sh_offset +
-                             (r->r_offset - sechdr->sh_addr));
+                             sechdr->sh_offset +
+                             (r->r_offset - sechdr->sh_addr));
                break;
        default:
                return 1;
@@ -1571,7 +1571,7 @@ static int addend_mips_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r)
 }
 
 static void section_rela(const char *modname, struct elf_info *elf,
-                         Elf_Shdr *sechdr)
+                        Elf_Shdr *sechdr)
 {
        Elf_Sym  *sym;
        Elf_Rela *rela;
@@ -1615,7 +1615,7 @@ static void section_rela(const char *modname, struct elf_info *elf,
 }
 
 static void section_rel(const char *modname, struct elf_info *elf,
-                        Elf_Shdr *sechdr)
+                       Elf_Shdr *sechdr)
 {
        Elf_Sym *sym;
        Elf_Rel *rel;
@@ -1685,7 +1685,7 @@ static void section_rel(const char *modname, struct elf_info *elf,
  * be discarded and warns about it.
  **/
 static void check_sec_ref(struct module *mod, const char *modname,
-                          struct elf_info *elf)
+                         struct elf_info *elf)
 {
        int i;
        Elf_Shdr *sechdrs = elf->sechdrs;
@@ -1945,7 +1945,7 @@ static int add_versions(struct buffer *b, struct module *mod)
                                             s->name, mod->name);
                                } else {
                                        merror("\"%s\" [%s.ko] undefined!\n",
-                                                 s->name, mod->name);
+                                              s->name, mod->name);
                                        err = 1;
                                }
                        }
index deb2994b04c4952d48300e8c4c38a43cf462828b..944418da9fe3369efd596e44fd53ffaffc0f80bd 100644 (file)
@@ -214,7 +214,7 @@ static void md4_final_ascii(struct md4_ctx *mctx, char *out, unsigned int len)
        mctx->block[14] = mctx->byte_count << 3;
        mctx->block[15] = mctx->byte_count >> 29;
        le32_to_cpu_array(mctx->block, (sizeof(mctx->block) -
-                         sizeof(uint64_t)) / sizeof(uint32_t));
+                         sizeof(uint64_t)) / sizeof(uint32_t));
        md4_transform(mctx->hash, mctx->block);
        cpu_to_le32_array(mctx->hash, sizeof(mctx->hash) / sizeof(uint32_t));
 
@@ -367,7 +367,7 @@ static int parse_source_files(const char *objfile, struct md4_ctx *md)
                        break;
                /* Terminate line at first space, to get rid of final ' \' */
                while (*p) {
-                       if (isspace(*p)) {
+                       if (isspace(*p)) {
                                *p = '\0';
                                break;
                        }
index b3e4f10bfc3edac5a5119d31f432371f8e75feee..62e51dae2138db450555f3d15dbd14cc53c53652 100755 (executable)
 #
 # Note: 'make mrproper' will also remove .tmp_objdiff
 
-GIT_DIR="`git rev-parse --git-dir`"
+SRCTREE=$(cd $(git rev-parse --show-toplevel 2>/dev/null); pwd)
 
-if [ -d "$GIT_DIR" ]; then
-       TMPD="${GIT_DIR%git}tmp_objdiff"
-
-       [ -d "$TMPD" ] || mkdir "$TMPD"
-else
-       echo "ERROR: git directory not found."
+if [ -z "$SRCTREE" ]; then
+       echo >&2 "ERROR: Not a git repository."
        exit 1
 fi
 
+TMPD=$SRCTREE/.tmp_objdiff
+
 usage() {
-       echo "Usage: $0 <command> <args>"
-       echo "  record    <list of object files>"
-       echo "  diff      <commitA> <commitB>"
-       echo "  clean     all | <commit>"
+       echo >&2 "Usage: $0 <command> <args>"
+       echo >&2 "  record    <list of object files or directories>"
+       echo >&2 "  diff      <commitA> <commitB>"
+       echo >&2 "  clean     all | <commit>"
        exit 1
 }
 
+get_output_dir() {
+       dir=${1%/*}
+
+       if [ "$dir" = "$1" ]; then
+               dir=.
+       fi
+
+       dir=$(cd $dir; pwd)
+
+       echo $TMPD/$CMT${dir#$SRCTREE}
+}
+
+do_objdump() {
+       dir=$(get_output_dir $1)
+       base=${1##*/}
+       dis=$dir/${base%.o}.dis
+
+       [ ! -d "$dir" ] && mkdir -p $dir
+
+       # remove addresses for a cleaner diff
+       # http://dummdida.tumblr.com/post/60924060451/binary-diff-between-libc-from-scientificlinux-and
+       $OBJDUMP -D $1 | sed "s/^[[:space:]]\+[0-9a-f]\+//" > $dis
+}
+
 dorecord() {
        [ $# -eq 0 ] && usage
 
@@ -52,20 +74,16 @@ dorecord() {
        CMT="`git rev-parse --short HEAD`"
 
        OBJDUMP="${CROSS_COMPILE}objdump"
-       OBJDIFFD="$TMPD/$CMT"
-
-       [ ! -d "$OBJDIFFD" ] && mkdir -p "$OBJDIFFD"
 
-       for f in $FILES; do
-               dn="${f%/*}"
-               bn="${f##*/}"
-
-               [ ! -d "$OBJDIFFD/$dn" ] && mkdir -p "$OBJDIFFD/$dn"
-
-               # remove addresses for a more clear diff
-               # http://dummdida.tumblr.com/post/60924060451/binary-diff-between-libc-from-scientificlinux-and
-               $OBJDUMP -D "$f" | sed "s/^[[:space:]]\+[0-9a-f]\+//" \
-                       >"$OBJDIFFD/$dn/$bn"
+       for d in $FILES; do
+               if [ -d "$d" ]; then
+                       for f in $(find $d -name '*.o')
+                       do
+                               do_objdump $f
+                       done
+               else
+                       do_objdump $d
+               fi
        done
 }
 
@@ -90,12 +108,12 @@ dodiff() {
        DSTD="$TMPD/$DST"
 
        if [ ! -d "$SRCD" ]; then
-               echo "ERROR: $SRCD doesn't exist"
+               echo >&2 "ERROR: $SRCD doesn't exist"
                exit 1
        fi
 
        if [ ! -d "$DSTD" ]; then
-               echo "ERROR: $DSTD doesn't exist"
+               echo >&2 "ERROR: $DSTD doesn't exist"
                exit 1
        fi
 
@@ -114,7 +132,7 @@ doclean() {
                if [ -d "$TMPD/$CMT" ]; then
                        rm -rf $TMPD/$CMT
                else
-                       echo "$CMT not found"
+                       echo >&2 "$CMT not found"
                fi
        fi
 }
@@ -135,7 +153,7 @@ case "$1" in
                doclean $*
                ;;
        *)
-               echo "Unrecognized command '$1'"
+               echo >&2 "Unrecognized command '$1'"
                exit 1
                ;;
 esac
index c5d473393816f971ec43135bd7dd3a27cc660beb..99ca6e76eb0a532ffafd0918dca8fa217c88f571 100644 (file)
@@ -143,4 +143,3 @@ help: FORCE
        @echo '  perf-targz-src-pkg  - Build $(perf-tar).tar.gz source tarball'
        @echo '  perf-tarbz2-src-pkg - Build $(perf-tar).tar.bz2 source tarball'
        @echo '  perf-tarxz-src-pkg  - Build $(perf-tar).tar.xz source tarball'
-
index f46e4dd0558da4582b38c75d4342456cceefd920..b5f08f7278686c4b66ba9a581b8e70f13f0fed8d 100644 (file)
@@ -35,13 +35,15 @@ create_package() {
        sparc*)
                debarch=sparc ;;
        s390*)
-               debarch=s390 ;;
+               debarch=s390$(grep -q CONFIG_64BIT=y $KCONFIG_CONFIG && echo x || true) ;;
        ppc*)
                debarch=powerpc ;;
        parisc*)
                debarch=hppa ;;
        mips*)
                debarch=mips$(grep -q CPU_LITTLE_ENDIAN=y $KCONFIG_CONFIG && echo el || true) ;;
+       arm64)
+               debarch=arm64 ;;
        arm*)
                debarch=arm$(grep -q CONFIG_AEABI=y $KCONFIG_CONFIG && echo el || true) ;;
        *)
@@ -130,7 +132,7 @@ if [ "$ARCH" = "um" ] ; then
        cp System.map "$tmpdir/usr/lib/uml/modules/$version/System.map"
        cp $KCONFIG_CONFIG "$tmpdir/usr/share/doc/$packagename/config"
        gzip "$tmpdir/usr/share/doc/$packagename/config"
-else 
+else
        cp System.map "$tmpdir/boot/System.map-$version"
        cp $KCONFIG_CONFIG "$tmpdir/boot/config-$version"
 fi
@@ -155,11 +157,11 @@ if grep -q '^CONFIG_MODULES=y' $KCONFIG_CONFIG ; then
                        for module in $(find lib/modules/ -name *.ko); do
                                mkdir -p $(dirname $dbg_dir/usr/lib/debug/$module)
                                # only keep debug symbols in the debug file
-                               objcopy --only-keep-debug $module $dbg_dir/usr/lib/debug/$module
+                               $OBJCOPY --only-keep-debug $module $dbg_dir/usr/lib/debug/$module
                                # strip original module from debug symbols
-                               objcopy --strip-debug $module
+                               $OBJCOPY --strip-debug $module
                                # then add a link to those
-                               objcopy --add-gnu-debuglink=$dbg_dir/usr/lib/debug/$module $module
+                               $OBJCOPY --add-gnu-debuglink=$dbg_dir/usr/lib/debug/$module $module
                        done
                )
        fi
index aa22f9447ddc2492bfee4241a1380bc4fb91a735..995c1eafaff6156862ad340aa11ff79b3dc96f46 100644 (file)
@@ -136,4 +136,3 @@ esac
 echo "Tarball successfully created in ${tarball}${file_ext}"
 
 exit 0
-
index d000ea3a41fdb7f202a34e439cf954bd5353b74f..49b4241e814a90c159cc272e655daa6294c7a2ea 100755 (executable)
@@ -27,7 +27,7 @@
 #       Nick Holloway <Nick.Holloway@alfie.demon.co.uk>, 2nd January 1995.
 #
 # Added support for handling multiple types of compression. What includes
-# gzip, bzip, bzip2, zip, compress, and plaintext. 
+# gzip, bzip, bzip2, zip, compress, and plaintext.
 #
 #       Adam Sulmicki <adam@cfar.umd.edu>, 1st January 1997.
 #
@@ -159,7 +159,7 @@ applyPatch () {
   fi
   # Remove backup files
   find $sourcedir/ '(' -name '*.orig' -o -name '.*.orig' ')' -exec rm -f {} \;
+
   return 0;
 }
 
index 68bb4efc5af429a2f5d1ec359a06d3f2e7af8bb4..4718d7895f0b451e4a529a4e1fcce6a85ec5a4c5 100644 (file)
@@ -512,4 +512,3 @@ int main(int argc, char *argv[])
     }
     exit(0);
 }
-
index e11aa4a156d2d26df48706d0da0a7970813e9798..650ecc83d7d72f89b012ec2f1ee41ef895aa398f 100644 (file)
@@ -487,5 +487,3 @@ main(int argc, char *argv[])
        }
        return !!n_error;
 }
-
-
index 43098afe74311669fd7cea7ca342415ba47d5bae..6b5c83baf1488729cf386ff56211e55b20e589fa 100644 (file)
@@ -19,4 +19,3 @@ testit t3-l2-pi.tst
 testit t4-l2-pi-deboost.tst
 testit t5-l4-pi-boost-deboost.tst
 testit t5-l4-pi-boost-deboost-setsched.tst
-
index 34186cac1d2ffaff30bca6e0065be69f9e50c7f5..6d916c2a45a550e925efd0bd0f99cb7cdca742ad 100644 (file)
@@ -216,5 +216,3 @@ while 1:
 # Normal exit pass
 print "Pass"
 sys.exit(0)
-
-
index 7b9ccf61f8f96fd9d6f6fd3e20f25f61738717a2..f6a0ce71015faa4109597ff002e2b6d31e028a77 100644 (file)
@@ -66,4 +66,3 @@ if [ "eq$dodev" != "eq" ]; then
        $SF file_contexts /dev
        mount --move /mnt /dev
 fi
-
index e25732b5d701127d904da3b608b825a520fce3ca..5b365009e6a398bed2aa9c13b575bf651aff21eb 100755 (executable)
@@ -126,4 +126,3 @@ def main():
                print (convert_line(line, base_time),)
 
 main()
-
index f2c5b006a3d73534d12fcb8c213ae140db4bae4e..e6b011fe1d0d1dfd850f345f55f0a7f057d6435d 100755 (executable)
@@ -25,6 +25,9 @@ else
        tree=${srctree}/
 fi
 
+# ignore userspace tools
+ignore="$ignore ( -path ${tree}tools ) -prune -o"
+
 # Find all available archs
 find_all_archs()
 {
@@ -47,7 +50,8 @@ find_arch_sources()
        for i in $archincludedir; do
                prune="$prune -wholename $i -prune -o"
        done
-       find ${tree}arch/$1 $ignore $subarchprune $prune -name "$2" -print;
+       find ${tree}arch/$1 $ignore $subarchprune $prune -name "$2" \
+               -not -type l -print;
 }
 
 # find sources in arch/$1/include
@@ -57,14 +61,15 @@ find_arch_include_sources()
                                        -name include -type d -print);
        if [ -n "$include" ]; then
                archincludedir="$archincludedir $include"
-               find $include $ignore -name "$2" -print;
+               find $include $ignore -name "$2" -not -type l -print;
        fi
 }
 
 # find sources in include/
 find_include_sources()
 {
-       find ${tree}include $ignore -name config -prune -o -name "$1" -print;
+       find ${tree}include $ignore -name config -prune -o -name "$1" \
+               -not -type l -print;
 }
 
 # find sources in rest of tree
@@ -73,7 +78,7 @@ find_other_sources()
 {
        find ${tree}* $ignore \
             \( -name include -o -name arch -o -name '.tmp_*' \) -prune -o \
-              -name "$1" -print;
+              -name "$1" -not -type l -print;
 }
 
 find_sources()
@@ -187,6 +192,10 @@ exuberant()
        --regex-c++='/TESTCLEARFLAG_FALSE\(([^,)]*).*/TestClearPage\1/' \
        --regex-c++='/__TESTCLEARFLAG_FALSE\(([^,)]*).*/__TestClearPage\1/' \
        --regex-c++='/_PE\(([^,)]*).*/PEVENT_ERRNO__\1/'                \
+       --regex-c++='/TESTPCGFLAG\(([^,)]*).*/PageCgroup\1/'            \
+       --regex-c++='/SETPCGFLAG\(([^,)]*).*/SetPageCgroup\1/'          \
+       --regex-c++='/CLEARPCGFLAG\(([^,)]*).*/ClearPageCgroup\1/'      \
+       --regex-c++='/TESTCLEARPCGFLAG\(([^,)]*).*/TestClearPageCgroup\1/' \
        --regex-c='/PCI_OP_READ\((\w*).*[1-4]\)/pci_bus_read_config_\1/' \
        --regex-c='/PCI_OP_WRITE\((\w*).*[1-4]\)/pci_bus_write_config_\1/' \
        --regex-c='/DEFINE_(MUTEX|SEMAPHORE|SPINLOCK)\((\w*)/\2/v/'     \
@@ -201,7 +210,8 @@ exuberant()
        --regex-c='/DECLARE_(TASKLET|WORK|DELAYED_WORK)\((\w*)/\2/v/'   \
        --regex-c='/DEFINE_PCI_DEVICE_TABLE\((\w*)/\1/v/'               \
        --regex-c='/(^\s)OFFSET\((\w*)/\2/v/'                           \
-       --regex-c='/(^\s)DEFINE\((\w*)/\2/v/'
+       --regex-c='/(^\s)DEFINE\((\w*)/\2/v/'                           \
+       --regex-c='/DEFINE_HASHTABLE\((\w*)/\1/v/'
 
        all_kconfigs | xargs $1 -a                              \
        --langdef=kconfig --language-force=kconfig              \
@@ -244,9 +254,14 @@ emacs()
        --regex='/__CLEARPAGEFLAG_NOOP(\([^,)]*\).*/__ClearPage\1/' \
        --regex='/TESTCLEARFLAG_FALSE(\([^,)]*\).*/TestClearPage\1/' \
        --regex='/__TESTCLEARFLAG_FALSE(\([^,)]*\).*/__TestClearPage\1/' \
+       --regex='/TESTPCGFLAG\(([^,)]*).*/PageCgroup\1/'        \
+       --regex='/SETPCGFLAG\(([^,)]*).*/SetPageCgroup\1/'      \
+       --regex='/CLEARPCGFLAG\(([^,)]*).*/ClearPageCgroup\1/'  \
+       --regex='/TESTCLEARPCGFLAG\(([^,)]*).*/TestClearPageCgroup\1/' \
        --regex='/_PE(\([^,)]*\).*/PEVENT_ERRNO__\1/'           \
        --regex='/PCI_OP_READ(\([a-z]*[a-z]\).*[1-4])/pci_bus_read_config_\1/' \
-       --regex='/PCI_OP_WRITE(\([a-z]*[a-z]\).*[1-4])/pci_bus_write_config_\1/'
+       --regex='/PCI_OP_WRITE(\([a-z]*[a-z]\).*[1-4])/pci_bus_write_config_\1/'\
+       --regex='/DEFINE_HASHTABLE\((\w*)/\1/v/'
 
        all_kconfigs | xargs $1 -a                              \
        --regex='/^[ \t]*\(\(menu\)*config\)[ \t]+\([a-zA-Z0-9_]+\)/\3/'
@@ -266,7 +281,7 @@ xtags()
                emacs $1
        else
                all_target_sources | xargs $1 -a
-        fi
+       fi
 }
 
 # Support um (which uses SUBARCH)
index 14d04e63b1f0e09ef15b4cf64057bd7cc861ed1d..be491a74c1edc4a279f76121dab615305c3a47f2 100644 (file)
@@ -147,7 +147,7 @@ struct security_class_mapping secclass_map[] = {
        { "peer", { "recv", NULL } },
        { "capability2",
          { "mac_override", "mac_admin", "syslog", "wake_alarm", "block_suspend",
-           NULL } },
+           "audit_read", NULL } },
        { "kernel_service", { "use_as_override", "create_files_as", NULL } },
        { "tun_socket",
          { COMMON_SOCK_PERMS, "attach_queue", NULL } },
index 5b5eb788996e1de2f105eeedffdc1bb0cb72caba..c1b49c36a951d74b1c12b417b20428b0f4326e70 100644 (file)
@@ -1,8 +1,10 @@
 /* TODO merge/factor in debugfs.c here */
 
+#include <ctype.h>
 #include <errno.h>
 #include <stdbool.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <sys/vfs.h>
 
@@ -96,12 +98,51 @@ static bool fs__check_mounts(struct fs *fs)
        return false;
 }
 
+static void mem_toupper(char *f, size_t len)
+{
+       while (len) {
+               *f = toupper(*f);
+               f++;
+               len--;
+       }
+}
+
+/*
+ * Check for "NAME_PATH" environment variable to override fs location (for
+ * testing). This matches the recommendation in Documentation/sysfs-rules.txt
+ * for SYSFS_PATH.
+ */
+static bool fs__env_override(struct fs *fs)
+{
+       char *override_path;
+       size_t name_len = strlen(fs->name);
+       /* name + "_PATH" + '\0' */
+       char upper_name[name_len + 5 + 1];
+       memcpy(upper_name, fs->name, name_len);
+       mem_toupper(upper_name, name_len);
+       strcpy(&upper_name[name_len], "_PATH");
+
+       override_path = getenv(upper_name);
+       if (!override_path)
+               return false;
+
+       fs->found = true;
+       strncpy(fs->path, override_path, sizeof(fs->path));
+       return true;
+}
+
 static const char *fs__get_mountpoint(struct fs *fs)
 {
+       if (fs__env_override(fs))
+               return fs->path;
+
        if (fs__check_mounts(fs))
                return fs->path;
 
-       return fs__read_mounts(fs) ? fs->path : NULL;
+       if (fs__read_mounts(fs))
+               return fs->path;
+
+       return NULL;
 }
 
 static const char *fs__mountpoint(int idx)
index bf7be77ddd621238aa2a26ca2103b309bdcd089f..833a96611da6d042e089294990801817d0f1ade9 100644 (file)
@@ -92,6 +92,7 @@ extern void yyerror(const char *str);
 "#"?("cpu")    { return K_CPU; }
 "#"?("vlan_tci") { return K_VLANT; }
 "#"?("vlan_pr")        { return K_VLANP; }
+"#"?("rand")   { return K_RAND; }
 
 ":"            { return ':'; }
 ","            { return ','; }
index d15efc989ef500ac3f9998cf660d8e2bd22627e7..e6306c51c26f9e7cb53342ad088a9db1e4e32423 100644 (file)
@@ -56,7 +56,7 @@ static void bpf_set_jmp_label(char *label, enum jmp_type type);
 %token OP_LDXI
 
 %token K_PKT_LEN K_PROTO K_TYPE K_NLATTR K_NLATTR_NEST K_MARK K_QUEUE K_HATYPE
-%token K_RXHASH K_CPU K_IFIDX K_VLANT K_VLANP K_POFF
+%token K_RXHASH K_CPU K_IFIDX K_VLANT K_VLANP K_POFF K_RAND
 
 %token ':' ',' '[' ']' '(' ')' 'x' 'a' '+' 'M' '*' '&' '#' '%'
 
@@ -164,6 +164,9 @@ ldb
        | OP_LDB K_POFF {
                bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0,
                                   SKF_AD_OFF + SKF_AD_PAY_OFFSET); }
+       | OP_LDB K_RAND {
+               bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0,
+                                  SKF_AD_OFF + SKF_AD_RANDOM); }
        ;
 
 ldh
@@ -212,6 +215,9 @@ ldh
        | OP_LDH K_POFF {
                bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0,
                                   SKF_AD_OFF + SKF_AD_PAY_OFFSET); }
+       | OP_LDH K_RAND {
+               bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0,
+                                  SKF_AD_OFF + SKF_AD_RANDOM); }
        ;
 
 ldi
@@ -265,6 +271,9 @@ ld
        | OP_LD K_POFF {
                bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0,
                                   SKF_AD_OFF + SKF_AD_PAY_OFFSET); }
+       | OP_LD K_RAND {
+               bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0,
+                                  SKF_AD_OFF + SKF_AD_RANDOM); }
        | OP_LD 'M' '[' number ']' {
                bpf_set_curr_instr(BPF_LD | BPF_MEM, 0, 0, $4); }
        | OP_LD '[' 'x' '+' number ']' {
index cfe0cdcda3de990793b70d128e2267f53baac5ba..c5baf9c591b7bb5a2c280e173f4e0a2b561285fa 100644 (file)
@@ -43,8 +43,7 @@ static void get_exec_path(char *tpath, size_t size)
        free(path);
 }
 
-static void get_asm_insns(uint8_t *image, size_t len, unsigned long base,
-                         int opcodes)
+static void get_asm_insns(uint8_t *image, size_t len, int opcodes)
 {
        int count, i, pc = 0;
        char tpath[256];
@@ -107,13 +106,13 @@ static void put_klog_buff(char *buff)
 }
 
 static int get_last_jit_image(char *haystack, size_t hlen,
-                             uint8_t *image, size_t ilen,
-                             unsigned long *base)
+                             uint8_t *image, size_t ilen)
 {
        char *ptr, *pptr, *tmp;
        off_t off = 0;
        int ret, flen, proglen, pass, ulen = 0;
        regmatch_t pmatch[1];
+       unsigned long base;
        regex_t regex;
 
        if (hlen == 0)
@@ -136,7 +135,7 @@ static int get_last_jit_image(char *haystack, size_t hlen,
 
        ptr = haystack + off - (pmatch[0].rm_eo - pmatch[0].rm_so);
        ret = sscanf(ptr, "flen=%d proglen=%d pass=%d image=%lx",
-                    &flen, &proglen, &pass, base);
+                    &flen, &proglen, &pass, &base);
        if (ret != 4)
                return 0;
 
@@ -162,7 +161,7 @@ static int get_last_jit_image(char *haystack, size_t hlen,
        assert(ulen == proglen);
        printf("%d bytes emitted from JIT compiler (pass:%d, flen:%d)\n",
               proglen, pass, flen);
-       printf("%lx + <x>:\n", *base);
+       printf("%lx + <x>:\n", base);
 
        regfree(&regex);
        return ulen;
@@ -172,8 +171,7 @@ int main(int argc, char **argv)
 {
        int len, klen, opcodes = 0;
        char *kbuff;
-       unsigned long base;
-       uint8_t image[4096];
+       static uint8_t image[32768];
 
        if (argc > 1) {
                if (!strncmp("-o", argv[argc - 1], 2)) {
@@ -189,9 +187,9 @@ int main(int argc, char **argv)
 
        kbuff = get_klog_buff(&klen);
 
-       len = get_last_jit_image(kbuff, klen, image, sizeof(image), &base);
-       if (len > 0 && base > 0)
-               get_asm_insns(image, len, base, opcodes);
+       len = get_last_jit_image(kbuff, klen, image, sizeof(image));
+       if (len > 0)
+               get_asm_insns(image, len, opcodes);
 
        put_klog_buff(kbuff);
 
index c71b0f36d9e8d45285a5b2b1256876f4d6f27799..d460049cae8e7c798dcad7bcd8b0cea795dfb29b 100644 (file)
@@ -184,9 +184,10 @@ following filters are defined:
        - in_tx: only when the target is in a hardware transaction
        - no_tx: only when the target is not in a hardware transaction
        - abort_tx: only when the target is a hardware transaction abort
+       - cond: conditional branches
 
 +
-The option requires at least one branch type among any, any_call, any_ret, ind_call.
+The option requires at least one branch type among any, any_call, any_ret, ind_call, cond.
 The privilege levels may be omitted, in which case, the privilege levels of the associated
 event are applied to the branch filter. Both kernel (k) and hypervisor (hv) privilege
 levels are subject to permissions.  When sampling on multiple events, branch stack sampling
index a1b5185402d5d2721a9c5aefe7fd9c26630d1a32..cefdf430d1b4066624431fb57f1efc25c04e3fd4 100644 (file)
@@ -111,7 +111,7 @@ OPTIONS
 --fields=::
        Specify output field - multiple keys can be specified in CSV format.
        Following fields are available:
-       overhead, overhead_sys, overhead_us, sample and period.
+       overhead, overhead_sys, overhead_us, overhead_children, sample and period.
        Also it can contain any sort key(s).
 
        By default, every sort keys not specified in -F will be appended
@@ -163,6 +163,11 @@ OPTIONS
 
        Default: fractal,0.5,callee,function.
 
+--children::
+       Accumulate callchain of children to parent entry so that then can
+       show up in the output.  The output will have a new "Children" column
+       and will be sorted on the data.  It requires callchains are recorded.
+
 --max-stack::
        Set the stack depth limit when parsing the callchain, anything
        beyond the specified depth will be ignored. This is a trade-off
index dcfa54c851e9d6ee1dcd1b0ffed15a6863ee881e..180ae02137a519acf1b3d779a92eb651db6b4aff 100644 (file)
@@ -119,7 +119,7 @@ Default is to monitor all CPUS.
 --fields=::
        Specify output field - multiple keys can be specified in CSV format.
        Following fields are available:
-       overhead, overhead_sys, overhead_us, sample and period.
+       overhead, overhead_sys, overhead_us, overhead_children, sample and period.
        Also it can contain any sort key(s).
 
        By default, every sort keys not specified in --field will be appended
@@ -161,6 +161,12 @@ Default is to monitor all CPUS.
        Setup and enable call-graph (stack chain/backtrace) recording,
        implies -g.
 
+--children::
+       Accumulate callchain of children to parent entry so that then can
+       show up in the output.  The output will have a new "Children" column
+       and will be sorted on the data.  It requires -g/--call-graph option
+       enabled.
+
 --max-stack::
        Set the stack depth limit when parsing the callchain, anything
        beyond the specified depth will be ignored. This is a trade-off
index 02f0a4dd1a80f3d06a9e532503b75cd3230d0aa2..ae20edfcc3f7e3a61b683a60882ed4239a4bb581 100644 (file)
@@ -400,6 +400,7 @@ LIB_OBJS += $(OUTPUT)tests/hists_common.o
 LIB_OBJS += $(OUTPUT)tests/hists_link.o
 LIB_OBJS += $(OUTPUT)tests/hists_filter.o
 LIB_OBJS += $(OUTPUT)tests/hists_output.o
+LIB_OBJS += $(OUTPUT)tests/hists_cumulate.o
 LIB_OBJS += $(OUTPUT)tests/python-use.o
 LIB_OBJS += $(OUTPUT)tests/bp_signal.o
 LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o
@@ -788,8 +789,8 @@ help:
        @echo ''
        @echo 'Perf install targets:'
        @echo '  NOTE: documentation build requires asciidoc, xmlto packages to be installed'
-       @echo '  HINT: use "make prefix=<path> <install target>" to install to a particular'
-       @echo '        path like make prefix=/usr/local install install-doc'
+       @echo '  HINT: use "prefix" or "DESTDIR" to install to a particular'
+       @echo '        path like "make prefix=/usr/local install install-doc"'
        @echo '  install        - install compiled binaries'
        @echo '  install-doc    - install *all* documentation'
        @echo '  install-man    - install manpage documentation'
@@ -814,17 +815,20 @@ INSTALL_DOC_TARGETS += quick-install-doc quick-install-man quick-install-html
 $(DOC_TARGETS):
        $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) $(@:doc=all)
 
+TAG_FOLDERS= . ../lib/traceevent ../lib/api ../lib/symbol
+TAG_FILES= ../../include/uapi/linux/perf_event.h
+
 TAGS:
        $(RM) TAGS
-       $(FIND) . -name '*.[hcS]' -print | xargs etags -a
+       $(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs etags -a $(TAG_FILES)
 
 tags:
        $(RM) tags
-       $(FIND) . -name '*.[hcS]' -print | xargs ctags -a
+       $(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs ctags -a $(TAG_FILES)
 
 cscope:
        $(RM) cscope*
-       $(FIND) . -name '*.[hcS]' -print | xargs cscope -b
+       $(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs cscope -b $(TAG_FILES)
 
 ### Detect prefix changes
 TRACK_CFLAGS = $(subst ','\'',$(CFLAGS)):\
index d30d2c2e2a7a30d79846afde2487656aa8cdd92f..1ec429fef2be9354d79400efe074e707229bef2f 100644 (file)
@@ -65,12 +65,13 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel,
                return 0;
        }
 
-       he = __hists__add_entry(&evsel->hists, al, NULL, NULL, NULL, 1, 1, 0);
+       he = __hists__add_entry(&evsel->hists, al, NULL, NULL, NULL, 1, 1, 0,
+                               true);
        if (he == NULL)
                return -ENOMEM;
 
        ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
-       hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
+       hists__inc_nr_samples(&evsel->hists, true);
        return ret;
 }
 
index 8bff543acaab7a093ed3579b42b24bc15e8d14ab..9a5a035cb4262afcf1e74986276df8763f1036b9 100644 (file)
@@ -315,7 +315,7 @@ static int hists__add_entry(struct hists *hists,
                            u64 weight, u64 transaction)
 {
        if (__hists__add_entry(hists, al, NULL, NULL, NULL, period, weight,
-                              transaction) != NULL)
+                              transaction, true) != NULL)
                return 0;
        return -ENOMEM;
 }
index e4c85b8f46c29fd7526e0bea3ef419ba7884acd3..378b85b731a72fede65d41318107272649edfbe5 100644 (file)
@@ -454,7 +454,11 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
                        if (done)
                                break;
                        err = poll(rec->evlist->pollfd, rec->evlist->nr_fds, -1);
-                       if (err < 0 && errno == EINTR)
+                       /*
+                        * Propagate error, only if there's any. Ignore positive
+                        * number of returned events and interrupt error.
+                        */
+                       if (err > 0 || (err < 0 && errno == EINTR))
                                err = 0;
                        waking++;
                }
@@ -544,6 +548,7 @@ static const struct branch_mode branch_modes[] = {
        BRANCH_OPT("abort_tx", PERF_SAMPLE_BRANCH_ABORT_TX),
        BRANCH_OPT("in_tx", PERF_SAMPLE_BRANCH_IN_TX),
        BRANCH_OPT("no_tx", PERF_SAMPLE_BRANCH_NO_TX),
+       BRANCH_OPT("cond", PERF_SAMPLE_BRANCH_COND),
        BRANCH_END
 };
 
index bc0eec1ce4beaba37509f731c79f8e1855796c08..21d830bafff32aaa4b38ec0eb2e63ec66dfc00f8 100644 (file)
@@ -72,6 +72,10 @@ static int report__config(const char *var, const char *value, void *cb)
                rep->min_percent = strtof(value, NULL);
                return 0;
        }
+       if (!strcmp(var, "report.children")) {
+               symbol_conf.cumulate_callchain = perf_config_bool(var, value);
+               return 0;
+       }
 
        return perf_default_config(var, value, cb);
 }
@@ -85,156 +89,52 @@ static void report__inc_stats(struct report *rep, struct hist_entry *he)
         */
        if (he->stat.nr_events == 1)
                rep->nr_entries++;
-
-       /*
-        * Only counts number of samples at this stage as it's more
-        * natural to do it here and non-sample events are also
-        * counted in perf_session_deliver_event().  The dump_trace
-        * requires this info is ready before going to the output tree.
-        */
-       hists__inc_nr_events(he->hists, PERF_RECORD_SAMPLE);
-       if (!he->filtered)
-               he->hists->stats.nr_non_filtered_samples++;
 }
 
-static int report__add_mem_hist_entry(struct report *rep, struct addr_location *al,
-                                     struct perf_sample *sample, struct perf_evsel *evsel)
+static int hist_iter__report_callback(struct hist_entry_iter *iter,
+                                     struct addr_location *al, bool single,
+                                     void *arg)
 {
-       struct symbol *parent = NULL;
-       struct hist_entry *he;
-       struct mem_info *mi, *mx;
-       uint64_t cost;
-       int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack);
-
-       if (err)
-               return err;
+       int err = 0;
+       struct report *rep = arg;
+       struct hist_entry *he = iter->he;
+       struct perf_evsel *evsel = iter->evsel;
+       struct mem_info *mi;
+       struct branch_info *bi;
 
-       mi = sample__resolve_mem(sample, al);
-       if (!mi)
-               return -ENOMEM;
+       report__inc_stats(rep, he);
 
-       if (rep->hide_unresolved && !al->sym)
+       if (!ui__has_annotation())
                return 0;
 
-       cost = sample->weight;
-       if (!cost)
-               cost = 1;
-
-       /*
-        * must pass period=weight in order to get the correct
-        * sorting from hists__collapse_resort() which is solely
-        * based on periods. We want sorting be done on nr_events * weight
-        * and this is indirectly achieved by passing period=weight here
-        * and the he_stat__add_period() function.
-        */
-       he = __hists__add_entry(&evsel->hists, al, parent, NULL, mi,
-                               cost, cost, 0);
-       if (!he)
-               return -ENOMEM;
-
-       if (ui__has_annotation()) {
-               err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
-               if (err)
-                       goto out;
-
-               mx = he->mem_info;
-               err = addr_map_symbol__inc_samples(&mx->daddr, evsel->idx);
+       if (sort__mode == SORT_MODE__BRANCH) {
+               bi = he->branch_info;
+               err = addr_map_symbol__inc_samples(&bi->from, evsel->idx);
                if (err)
                        goto out;
-       }
-
-       report__inc_stats(rep, he);
-
-       err = hist_entry__append_callchain(he, sample);
-out:
-       return err;
-}
-
-static int report__add_branch_hist_entry(struct report *rep, struct addr_location *al,
-                                        struct perf_sample *sample, struct perf_evsel *evsel)
-{
-       struct symbol *parent = NULL;
-       unsigned i;
-       struct hist_entry *he;
-       struct branch_info *bi, *bx;
-       int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack);
 
-       if (err)
-               return err;
-
-       bi = sample__resolve_bstack(sample, al);
-       if (!bi)
-               return -ENOMEM;
-
-       for (i = 0; i < sample->branch_stack->nr; i++) {
-               if (rep->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym))
-                       continue;
+               err = addr_map_symbol__inc_samples(&bi->to, evsel->idx);
 
-               err = -ENOMEM;
-
-               /* overwrite the 'al' to branch-to info */
-               al->map = bi[i].to.map;
-               al->sym = bi[i].to.sym;
-               al->addr = bi[i].to.addr;
-               /*
-                * The report shows the percentage of total branches captured
-                * and not events sampled. Thus we use a pseudo period of 1.
-                */
-               he = __hists__add_entry(&evsel->hists, al, parent, &bi[i], NULL,
-                                       1, 1, 0);
-               if (he) {
-                       if (ui__has_annotation()) {
-                               bx = he->branch_info;
-                               err = addr_map_symbol__inc_samples(&bx->from,
-                                                                  evsel->idx);
-                               if (err)
-                                       goto out;
-
-                               err = addr_map_symbol__inc_samples(&bx->to,
-                                                                  evsel->idx);
-                               if (err)
-                                       goto out;
-                       }
-                       report__inc_stats(rep, he);
-               } else
+       } else if (rep->mem_mode) {
+               mi = he->mem_info;
+               err = addr_map_symbol__inc_samples(&mi->daddr, evsel->idx);
+               if (err)
                        goto out;
-       }
-       err = 0;
-out:
-       free(bi);
-       return err;
-}
-
-static int report__add_hist_entry(struct report *rep, struct perf_evsel *evsel,
-                                 struct addr_location *al, struct perf_sample *sample)
-{
-       struct symbol *parent = NULL;
-       struct hist_entry *he;
-       int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack);
-
-       if (err)
-               return err;
 
-       he = __hists__add_entry(&evsel->hists, al, parent, NULL, NULL,
-                               sample->period, sample->weight,
-                               sample->transaction);
-       if (he == NULL)
-               return -ENOMEM;
-
-       err = hist_entry__append_callchain(he, sample);
-       if (err)
-               goto out;
-
-       if (ui__has_annotation())
                err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
 
-       report__inc_stats(rep, he);
+       } else if (symbol_conf.cumulate_callchain) {
+               if (single)
+                       err = hist_entry__inc_addr_samples(he, evsel->idx,
+                                                          al->addr);
+       } else {
+               err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
+       }
 
 out:
        return err;
 }
 
-
 static int process_sample_event(struct perf_tool *tool,
                                union perf_event *event,
                                struct perf_sample *sample,
@@ -243,6 +143,10 @@ static int process_sample_event(struct perf_tool *tool,
 {
        struct report *rep = container_of(tool, struct report, tool);
        struct addr_location al;
+       struct hist_entry_iter iter = {
+               .hide_unresolved = rep->hide_unresolved,
+               .add_entry_cb = hist_iter__report_callback,
+       };
        int ret;
 
        if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
@@ -257,22 +161,23 @@ static int process_sample_event(struct perf_tool *tool,
        if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap))
                return 0;
 
-       if (sort__mode == SORT_MODE__BRANCH) {
-               ret = report__add_branch_hist_entry(rep, &al, sample, evsel);
-               if (ret < 0)
-                       pr_debug("problem adding lbr entry, skipping event\n");
-       } else if (rep->mem_mode == 1) {
-               ret = report__add_mem_hist_entry(rep, &al, sample, evsel);
-               if (ret < 0)
-                       pr_debug("problem adding mem entry, skipping event\n");
-       } else {
-               if (al.map != NULL)
-                       al.map->dso->hit = 1;
+       if (sort__mode == SORT_MODE__BRANCH)
+               iter.ops = &hist_iter_branch;
+       else if (rep->mem_mode)
+               iter.ops = &hist_iter_mem;
+       else if (symbol_conf.cumulate_callchain)
+               iter.ops = &hist_iter_cumulative;
+       else
+               iter.ops = &hist_iter_normal;
+
+       if (al.map != NULL)
+               al.map->dso->hit = 1;
+
+       ret = hist_entry_iter__add(&iter, &al, evsel, sample, rep->max_stack,
+                                  rep);
+       if (ret < 0)
+               pr_debug("problem adding hist entry, skipping event\n");
 
-               ret = report__add_hist_entry(rep, evsel, &al, sample);
-               if (ret < 0)
-                       pr_debug("problem incrementing symbol period, skipping event\n");
-       }
        return ret;
 }
 
@@ -329,6 +234,14 @@ static int report__setup_sample_type(struct report *rep)
                        }
        }
 
+       if (symbol_conf.cumulate_callchain) {
+               /* Silently ignore if callchain is missing */
+               if (!(sample_type & PERF_SAMPLE_CALLCHAIN)) {
+                       symbol_conf.cumulate_callchain = false;
+                       perf_hpp__cancel_cumulate();
+               }
+       }
+
        if (sort__mode == SORT_MODE__BRANCH) {
                if (!is_pipe &&
                    !(sample_type & PERF_SAMPLE_BRANCH_STACK)) {
@@ -712,6 +625,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
        OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order",
                     "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). "
                     "Default: fractal,0.5,callee,function", &report_parse_callchain_opt, callchain_default_opt),
+       OPT_BOOLEAN(0, "children", &symbol_conf.cumulate_callchain,
+                   "Accumulate callchains of children and show total overhead as well"),
        OPT_INTEGER(0, "max-stack", &report.max_stack,
                    "Set the maximum stack depth when parsing the callchain, "
                    "anything beyond the specified depth will be ignored. "
@@ -804,8 +719,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
        has_br_stack = perf_header__has_feat(&session->header,
                                             HEADER_BRANCH_STACK);
 
-       if (branch_mode == -1 && has_br_stack)
+       if (branch_mode == -1 && has_br_stack) {
                sort__mode = SORT_MODE__BRANCH;
+               symbol_conf.cumulate_callchain = false;
+       }
 
        if (report.mem_mode) {
                if (sort__mode == SORT_MODE__BRANCH) {
@@ -813,6 +730,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
                        goto error;
                }
                sort__mode = SORT_MODE__MEMORY;
+               symbol_conf.cumulate_callchain = false;
        }
 
        if (setup_sorting() < 0) {
index d7176830b9b2fb789a61565acf113987ac59fd99..c38d06c047757281b03992522723af980b54412b 100644 (file)
@@ -1428,7 +1428,7 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_
        int err = 0;
 
        evsel->hists.stats.total_period += sample->period;
-       hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
+       hists__inc_nr_samples(&evsel->hists, true);
 
        if (evsel->handler != NULL) {
                tracepoint_handler f = evsel->handler;
index 5b389ce4cd15d03b656185f83d58f10aca94fd81..377971dc89a3b26ffcd25b9eb3e8c0c01338ea24 100644 (file)
@@ -196,6 +196,12 @@ static void perf_top__record_precise_ip(struct perf_top *top,
 
        pthread_mutex_unlock(&notes->lock);
 
+       /*
+        * This function is now called with he->hists->lock held.
+        * Release it before going to sleep.
+        */
+       pthread_mutex_unlock(&he->hists->lock);
+
        if (err == -ERANGE && !he->ms.map->erange_warned)
                ui__warn_map_erange(he->ms.map, sym, ip);
        else if (err == -ENOMEM) {
@@ -203,6 +209,8 @@ static void perf_top__record_precise_ip(struct perf_top *top,
                       sym->name);
                sleep(1);
        }
+
+       pthread_mutex_lock(&he->hists->lock);
 }
 
 static void perf_top__show_details(struct perf_top *top)
@@ -238,27 +246,6 @@ static void perf_top__show_details(struct perf_top *top)
        pthread_mutex_unlock(&notes->lock);
 }
 
-static struct hist_entry *perf_evsel__add_hist_entry(struct perf_evsel *evsel,
-                                                    struct addr_location *al,
-                                                    struct perf_sample *sample)
-{
-       struct hist_entry *he;
-
-       pthread_mutex_lock(&evsel->hists.lock);
-       he = __hists__add_entry(&evsel->hists, al, NULL, NULL, NULL,
-                               sample->period, sample->weight,
-                               sample->transaction);
-       pthread_mutex_unlock(&evsel->hists.lock);
-       if (he == NULL)
-               return NULL;
-
-       hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
-       if (!he->filtered)
-               evsel->hists.stats.nr_non_filtered_samples++;
-
-       return he;
-}
-
 static void perf_top__print_sym_table(struct perf_top *top)
 {
        char bf[160];
@@ -662,6 +649,26 @@ static int symbol_filter(struct map *map __maybe_unused, struct symbol *sym)
        return 0;
 }
 
+static int hist_iter__top_callback(struct hist_entry_iter *iter,
+                                  struct addr_location *al, bool single,
+                                  void *arg)
+{
+       struct perf_top *top = arg;
+       struct hist_entry *he = iter->he;
+       struct perf_evsel *evsel = iter->evsel;
+
+       if (sort__has_sym && single) {
+               u64 ip = al->addr;
+
+               if (al->map)
+                       ip = al->map->unmap_ip(al->map, ip);
+
+               perf_top__record_precise_ip(top, he, evsel->idx, ip);
+       }
+
+       return 0;
+}
+
 static void perf_event__process_sample(struct perf_tool *tool,
                                       const union perf_event *event,
                                       struct perf_evsel *evsel,
@@ -669,8 +676,6 @@ static void perf_event__process_sample(struct perf_tool *tool,
                                       struct machine *machine)
 {
        struct perf_top *top = container_of(tool, struct perf_top, tool);
-       struct symbol *parent = NULL;
-       u64 ip = sample->ip;
        struct addr_location al;
        int err;
 
@@ -745,25 +750,23 @@ static void perf_event__process_sample(struct perf_tool *tool,
        }
 
        if (al.sym == NULL || !al.sym->ignore) {
-               struct hist_entry *he;
+               struct hist_entry_iter iter = {
+                       .add_entry_cb = hist_iter__top_callback,
+               };
 
-               err = sample__resolve_callchain(sample, &parent, evsel, &al,
-                                               top->max_stack);
-               if (err)
-                       return;
+               if (symbol_conf.cumulate_callchain)
+                       iter.ops = &hist_iter_cumulative;
+               else
+                       iter.ops = &hist_iter_normal;
 
-               he = perf_evsel__add_hist_entry(evsel, &al, sample);
-               if (he == NULL) {
-                       pr_err("Problem incrementing symbol period, skipping event\n");
-                       return;
-               }
+               pthread_mutex_lock(&evsel->hists.lock);
 
-               err = hist_entry__append_callchain(he, sample);
-               if (err)
-                       return;
+               err = hist_entry_iter__add(&iter, &al, evsel, sample,
+                                          top->max_stack, top);
+               if (err < 0)
+                       pr_err("Problem incrementing symbol period, skipping event\n");
 
-               if (sort__has_sym)
-                       perf_top__record_precise_ip(top, he, evsel->idx, ip);
+               pthread_mutex_unlock(&evsel->hists.lock);
        }
 
        return;
@@ -1001,6 +1004,10 @@ static int perf_top_config(const char *var, const char *value, void *cb)
 
        if (!strcmp(var, "top.call-graph"))
                return record_parse_callchain(value, &top->record_opts);
+       if (!strcmp(var, "top.children")) {
+               symbol_conf.cumulate_callchain = perf_config_bool(var, value);
+               return 0;
+       }
 
        return perf_default_config(var, value, cb);
 }
@@ -1095,6 +1102,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
        OPT_CALLBACK(0, "call-graph", &top.record_opts,
                     "mode[,dump_size]", record_callchain_help,
                     &parse_callchain_opt),
+       OPT_BOOLEAN(0, "children", &symbol_conf.cumulate_callchain,
+                   "Accumulate callchains of children and show total overhead as well"),
        OPT_INTEGER(0, "max-stack", &top.max_stack,
                    "Set the maximum stack depth when parsing the callchain. "
                    "Default: " __stringify(PERF_MAX_STACK_DEPTH)),
@@ -1200,6 +1209,11 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
 
        top.sym_evsel = perf_evlist__first(top.evlist);
 
+       if (!symbol_conf.use_callchain) {
+               symbol_conf.cumulate_callchain = false;
+               perf_hpp__cancel_cumulate();
+       }
+
        symbol_conf.priv_size = sizeof(struct annotation);
 
        symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
index 729bbdf5cec7c663cda5d678c9960e471bb17c77..4f100b54ba8bf0a352c9db0ac2f9d7b6f719343d 100644 (file)
@@ -447,6 +447,7 @@ else
   ifneq ($(feature-libperl), 1)
     CFLAGS += -DNO_LIBPERL
     NO_LIBPERL := 1
+    msg := $(warning Missing perl devel files. Disabling perl scripting support, consider installing perl-ExtUtils-Embed);
   else
     LDFLAGS += $(PERL_EMBED_LDFLAGS)
     EXTLIBS += $(PERL_EMBED_LIBADD)
@@ -599,7 +600,7 @@ endif
 
 # Make the path relative to DESTDIR, not to prefix
 ifndef DESTDIR
-prefix = $(HOME)
+prefix ?= $(HOME)
 endif
 bindir_relative = bin
 bindir = $(prefix)/$(bindir_relative)
index 431798a4110d6a63a9e802ddeef2b2a701bea2a2..78f7b920e5483a1982dd4d164588b689e1b29e6d 100644 (file)
@@ -481,14 +481,18 @@ int main(int argc, const char **argv)
                fprintf(stderr, "cannot handle %s internally", cmd);
                goto out;
        }
-#ifdef HAVE_LIBAUDIT_SUPPORT
        if (!prefixcmp(cmd, "trace")) {
+#ifdef HAVE_LIBAUDIT_SUPPORT
                set_buildid_dir();
                setup_path();
                argv[0] = "trace";
                return cmd_trace(argc, argv, NULL);
-       }
+#else
+               fprintf(stderr,
+                       "trace command not available: missing audit-libs devel package at build time.\n");
+               goto out;
 #endif
+       }
        /* Look for flags.. */
        argv++;
        argc--;
index 831f52cae1972eb8d019808d505ca2e9dfd4a423..802e3cd50f6fb7ecb7100b2dc5ef577e48846918 100644 (file)
@@ -139,6 +139,10 @@ static struct test {
                .desc = "Test output sorting of hist entries",
                .func = test__hists_output,
        },
+       {
+               .desc = "Test cumulation of child hist entries",
+               .func = test__hists_cumulate,
+       },
        {
                .func = NULL,
        },
index e4e01aadc3be4c4b6f13b291006f4eef86625b8f..a62c091345163f70ea72ad2cb9dbc2297ae2da1a 100644 (file)
@@ -12,9 +12,9 @@ static struct {
        u32 pid;
        const char *comm;
 } fake_threads[] = {
-       { 100, "perf" },
-       { 200, "perf" },
-       { 300, "bash" },
+       { FAKE_PID_PERF1, "perf" },
+       { FAKE_PID_PERF2, "perf" },
+       { FAKE_PID_BASH,  "bash" },
 };
 
 static struct {
@@ -22,15 +22,15 @@ static struct {
        u64 start;
        const char *filename;
 } fake_mmap_info[] = {
-       { 100, 0x40000, "perf" },
-       { 100, 0x50000, "libc" },
-       { 100, 0xf0000, "[kernel]" },
-       { 200, 0x40000, "perf" },
-       { 200, 0x50000, "libc" },
-       { 200, 0xf0000, "[kernel]" },
-       { 300, 0x40000, "bash" },
-       { 300, 0x50000, "libc" },
-       { 300, 0xf0000, "[kernel]" },
+       { FAKE_PID_PERF1, FAKE_MAP_PERF,   "perf" },
+       { FAKE_PID_PERF1, FAKE_MAP_LIBC,   "libc" },
+       { FAKE_PID_PERF1, FAKE_MAP_KERNEL, "[kernel]" },
+       { FAKE_PID_PERF2, FAKE_MAP_PERF,   "perf" },
+       { FAKE_PID_PERF2, FAKE_MAP_LIBC,   "libc" },
+       { FAKE_PID_PERF2, FAKE_MAP_KERNEL, "[kernel]" },
+       { FAKE_PID_BASH,  FAKE_MAP_BASH,   "bash" },
+       { FAKE_PID_BASH,  FAKE_MAP_LIBC,   "libc" },
+       { FAKE_PID_BASH,  FAKE_MAP_KERNEL, "[kernel]" },
 };
 
 struct fake_sym {
@@ -40,27 +40,30 @@ struct fake_sym {
 };
 
 static struct fake_sym perf_syms[] = {
-       { 700, 100, "main" },
-       { 800, 100, "run_command" },
-       { 900, 100, "cmd_record" },
+       { FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "main" },
+       { FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "run_command" },
+       { FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "cmd_record" },
 };
 
 static struct fake_sym bash_syms[] = {
-       { 700, 100, "main" },
-       { 800, 100, "xmalloc" },
-       { 900, 100, "xfree" },
+       { FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "main" },
+       { FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "xmalloc" },
+       { FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "xfree" },
 };
 
 static struct fake_sym libc_syms[] = {
        { 700, 100, "malloc" },
        { 800, 100, "free" },
        { 900, 100, "realloc" },
+       { FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "malloc" },
+       { FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "free" },
+       { FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "realloc" },
 };
 
 static struct fake_sym kernel_syms[] = {
-       { 700, 100, "schedule" },
-       { 800, 100, "page_fault" },
-       { 900, 100, "sys_perf_event_open" },
+       { FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "schedule" },
+       { FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "page_fault" },
+       { FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "sys_perf_event_open" },
 };
 
 static struct {
@@ -102,7 +105,7 @@ struct machine *setup_fake_machine(struct machines *machines)
                                .pid = fake_mmap_info[i].pid,
                                .tid = fake_mmap_info[i].pid,
                                .start = fake_mmap_info[i].start,
-                               .len = 0x1000ULL,
+                               .len = FAKE_MAP_LENGTH,
                                .pgoff = 0ULL,
                        },
                };
@@ -193,10 +196,11 @@ void print_hists_out(struct hists *hists)
                he = rb_entry(node, struct hist_entry, rb_node);
 
                if (!he->filtered) {
-                       pr_info("%2d: entry: %8s:%5d [%-8s] %20s: period = %"PRIu64"\n",
+                       pr_info("%2d: entry: %8s:%5d [%-8s] %20s: period = %"PRIu64"/%"PRIu64"\n",
                                i, thread__comm_str(he->thread), he->thread->tid,
                                he->ms.map->dso->short_name,
-                               he->ms.sym->name, he->stat.period);
+                               he->ms.sym->name, he->stat.period,
+                               he->stat_acc ? he->stat_acc->period : 0);
                }
 
                i++;
index 1415ae69d7b6fa40235cce03a8e02874da39aa10..888254e8665c41ce0c904f28857047c69e816467 100644 (file)
@@ -4,6 +4,34 @@
 struct machine;
 struct machines;
 
+#define FAKE_PID_PERF1  100
+#define FAKE_PID_PERF2  200
+#define FAKE_PID_BASH   300
+
+#define FAKE_MAP_PERF    0x400000
+#define FAKE_MAP_BASH    0x400000
+#define FAKE_MAP_LIBC    0x500000
+#define FAKE_MAP_KERNEL  0xf00000
+#define FAKE_MAP_LENGTH  0x100000
+
+#define FAKE_SYM_OFFSET1  700
+#define FAKE_SYM_OFFSET2  800
+#define FAKE_SYM_OFFSET3  900
+#define FAKE_SYM_LENGTH   100
+
+#define FAKE_IP_PERF_MAIN  FAKE_MAP_PERF + FAKE_SYM_OFFSET1
+#define FAKE_IP_PERF_RUN_COMMAND  FAKE_MAP_PERF + FAKE_SYM_OFFSET2
+#define FAKE_IP_PERF_CMD_RECORD  FAKE_MAP_PERF + FAKE_SYM_OFFSET3
+#define FAKE_IP_BASH_MAIN  FAKE_MAP_BASH + FAKE_SYM_OFFSET1
+#define FAKE_IP_BASH_XMALLOC  FAKE_MAP_BASH + FAKE_SYM_OFFSET2
+#define FAKE_IP_BASH_XFREE  FAKE_MAP_BASH + FAKE_SYM_OFFSET3
+#define FAKE_IP_LIBC_MALLOC  FAKE_MAP_LIBC + FAKE_SYM_OFFSET1
+#define FAKE_IP_LIBC_FREE  FAKE_MAP_LIBC + FAKE_SYM_OFFSET2
+#define FAKE_IP_LIBC_REALLOC  FAKE_MAP_LIBC + FAKE_SYM_OFFSET3
+#define FAKE_IP_KERNEL_SCHEDULE  FAKE_MAP_KERNEL + FAKE_SYM_OFFSET1
+#define FAKE_IP_KERNEL_PAGE_FAULT  FAKE_MAP_KERNEL + FAKE_SYM_OFFSET2
+#define FAKE_IP_KERNEL_SYS_PERF_EVENT_OPEN  FAKE_MAP_KERNEL + FAKE_SYM_OFFSET3
+
 /*
  * The setup_fake_machine() provides a test environment which consists
  * of 3 processes that have 3 mappings and in turn, have 3 symbols
@@ -13,7 +41,7 @@ struct machines;
  * .............  .............  ...................
  *    perf:  100           perf  main
  *    perf:  100           perf  run_command
- *    perf:  100           perf  comd_record
+ *    perf:  100           perf  cmd_record
  *    perf:  100           libc  malloc
  *    perf:  100           libc  free
  *    perf:  100           libc  realloc
@@ -22,7 +50,7 @@ struct machines;
  *    perf:  100       [kernel]  sys_perf_event_open
  *    perf:  200           perf  main
  *    perf:  200           perf  run_command
- *    perf:  200           perf  comd_record
+ *    perf:  200           perf  cmd_record
  *    perf:  200           libc  malloc
  *    perf:  200           libc  free
  *    perf:  200           libc  realloc
diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c
new file mode 100644 (file)
index 0000000..0ac240d
--- /dev/null
@@ -0,0 +1,726 @@
+#include "perf.h"
+#include "util/debug.h"
+#include "util/symbol.h"
+#include "util/sort.h"
+#include "util/evsel.h"
+#include "util/evlist.h"
+#include "util/machine.h"
+#include "util/thread.h"
+#include "util/parse-events.h"
+#include "tests/tests.h"
+#include "tests/hists_common.h"
+
+struct sample {
+       u32 pid;
+       u64 ip;
+       struct thread *thread;
+       struct map *map;
+       struct symbol *sym;
+};
+
+/* For the numbers, see hists_common.c */
+static struct sample fake_samples[] = {
+       /* perf [kernel] schedule() */
+       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, },
+       /* perf [perf]   main() */
+       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_MAIN, },
+       /* perf [perf]   cmd_record() */
+       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_CMD_RECORD, },
+       /* perf [libc]   malloc() */
+       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, },
+       /* perf [libc]   free() */
+       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_FREE, },
+       /* perf [perf]   main() */
+       { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, },
+       /* perf [kernel] page_fault() */
+       { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
+       /* bash [bash]   main() */
+       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_MAIN, },
+       /* bash [bash]   xmalloc() */
+       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_XMALLOC, },
+       /* bash [kernel] page_fault() */
+       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
+};
+
+/*
+ * Will be casted to struct ip_callchain which has all 64 bit entries
+ * of nr and ips[].
+ */
+static u64 fake_callchains[][10] = {
+       /*   schedule => run_command => main */
+       { 3, FAKE_IP_KERNEL_SCHEDULE, FAKE_IP_PERF_RUN_COMMAND, FAKE_IP_PERF_MAIN, },
+       /*   main  */
+       { 1, FAKE_IP_PERF_MAIN, },
+       /*   cmd_record => run_command => main */
+       { 3, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND, FAKE_IP_PERF_MAIN, },
+       /*   malloc => cmd_record => run_command => main */
+       { 4, FAKE_IP_LIBC_MALLOC, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND,
+            FAKE_IP_PERF_MAIN, },
+       /*   free => cmd_record => run_command => main */
+       { 4, FAKE_IP_LIBC_FREE, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND,
+            FAKE_IP_PERF_MAIN, },
+       /*   main */
+       { 1, FAKE_IP_PERF_MAIN, },
+       /*   page_fault => sys_perf_event_open => run_command => main */
+       { 4, FAKE_IP_KERNEL_PAGE_FAULT, FAKE_IP_KERNEL_SYS_PERF_EVENT_OPEN,
+            FAKE_IP_PERF_RUN_COMMAND, FAKE_IP_PERF_MAIN, },
+       /*   main */
+       { 1, FAKE_IP_BASH_MAIN, },
+       /*   xmalloc => malloc => xmalloc => malloc => xmalloc => main */
+       { 6, FAKE_IP_BASH_XMALLOC, FAKE_IP_LIBC_MALLOC, FAKE_IP_BASH_XMALLOC,
+            FAKE_IP_LIBC_MALLOC, FAKE_IP_BASH_XMALLOC, FAKE_IP_BASH_MAIN, },
+       /*   page_fault => malloc => main */
+       { 3, FAKE_IP_KERNEL_PAGE_FAULT, FAKE_IP_LIBC_MALLOC, FAKE_IP_BASH_MAIN, },
+};
+
+static int add_hist_entries(struct hists *hists, struct machine *machine)
+{
+       struct addr_location al;
+       struct perf_evsel *evsel = hists_to_evsel(hists);
+       struct perf_sample sample = { .period = 1000, };
+       size_t i;
+
+       for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {
+               const union perf_event event = {
+                       .header = {
+                               .misc = PERF_RECORD_MISC_USER,
+                       },
+               };
+               struct hist_entry_iter iter = {
+                       .hide_unresolved = false,
+               };
+
+               if (symbol_conf.cumulate_callchain)
+                       iter.ops = &hist_iter_cumulative;
+               else
+                       iter.ops = &hist_iter_normal;
+
+               sample.pid = fake_samples[i].pid;
+               sample.tid = fake_samples[i].pid;
+               sample.ip = fake_samples[i].ip;
+               sample.callchain = (struct ip_callchain *)fake_callchains[i];
+
+               if (perf_event__preprocess_sample(&event, machine, &al,
+                                                 &sample) < 0)
+                       goto out;
+
+               if (hist_entry_iter__add(&iter, &al, evsel, &sample,
+                                        PERF_MAX_STACK_DEPTH, NULL) < 0)
+                       goto out;
+
+               fake_samples[i].thread = al.thread;
+               fake_samples[i].map = al.map;
+               fake_samples[i].sym = al.sym;
+       }
+
+       return TEST_OK;
+
+out:
+       pr_debug("Not enough memory for adding a hist entry\n");
+       return TEST_FAIL;
+}
+
+static void del_hist_entries(struct hists *hists)
+{
+       struct hist_entry *he;
+       struct rb_root *root_in;
+       struct rb_root *root_out;
+       struct rb_node *node;
+
+       if (sort__need_collapse)
+               root_in = &hists->entries_collapsed;
+       else
+               root_in = hists->entries_in;
+
+       root_out = &hists->entries;
+
+       while (!RB_EMPTY_ROOT(root_out)) {
+               node = rb_first(root_out);
+
+               he = rb_entry(node, struct hist_entry, rb_node);
+               rb_erase(node, root_out);
+               rb_erase(&he->rb_node_in, root_in);
+               hist_entry__free(he);
+       }
+}
+
+typedef int (*test_fn_t)(struct perf_evsel *, struct machine *);
+
+#define COMM(he)  (thread__comm_str(he->thread))
+#define DSO(he)   (he->ms.map->dso->short_name)
+#define SYM(he)   (he->ms.sym->name)
+#define CPU(he)   (he->cpu)
+#define PID(he)   (he->thread->tid)
+#define DEPTH(he) (he->callchain->max_depth)
+#define CDSO(cl)  (cl->ms.map->dso->short_name)
+#define CSYM(cl)  (cl->ms.sym->name)
+
+struct result {
+       u64 children;
+       u64 self;
+       const char *comm;
+       const char *dso;
+       const char *sym;
+};
+
+struct callchain_result {
+       u64 nr;
+       struct {
+               const char *dso;
+               const char *sym;
+       } node[10];
+};
+
+static int do_test(struct hists *hists, struct result *expected, size_t nr_expected,
+                  struct callchain_result *expected_callchain, size_t nr_callchain)
+{
+       char buf[32];
+       size_t i, c;
+       struct hist_entry *he;
+       struct rb_root *root;
+       struct rb_node *node;
+       struct callchain_node *cnode;
+       struct callchain_list *clist;
+
+       /*
+        * adding and deleting hist entries must be done outside of this
+        * function since TEST_ASSERT_VAL() returns in case of failure.
+        */
+       hists__collapse_resort(hists, NULL);
+       hists__output_resort(hists);
+
+       if (verbose > 2) {
+               pr_info("use callchain: %d, cumulate callchain: %d\n",
+                       symbol_conf.use_callchain,
+                       symbol_conf.cumulate_callchain);
+               print_hists_out(hists);
+       }
+
+       root = &hists->entries;
+       for (node = rb_first(root), i = 0;
+            node && (he = rb_entry(node, struct hist_entry, rb_node));
+            node = rb_next(node), i++) {
+               scnprintf(buf, sizeof(buf), "Invalid hist entry #%zd", i);
+
+               TEST_ASSERT_VAL("Incorrect number of hist entry",
+                               i < nr_expected);
+               TEST_ASSERT_VAL(buf, he->stat.period == expected[i].self &&
+                               !strcmp(COMM(he), expected[i].comm) &&
+                               !strcmp(DSO(he), expected[i].dso) &&
+                               !strcmp(SYM(he), expected[i].sym));
+
+               if (symbol_conf.cumulate_callchain)
+                       TEST_ASSERT_VAL(buf, he->stat_acc->period == expected[i].children);
+
+               if (!symbol_conf.use_callchain)
+                       continue;
+
+               /* check callchain entries */
+               root = &he->callchain->node.rb_root;
+               cnode = rb_entry(rb_first(root), struct callchain_node, rb_node);
+
+               c = 0;
+               list_for_each_entry(clist, &cnode->val, list) {
+                       scnprintf(buf, sizeof(buf), "Invalid callchain entry #%zd/%zd", i, c);
+
+                       TEST_ASSERT_VAL("Incorrect number of callchain entry",
+                                       c < expected_callchain[i].nr);
+                       TEST_ASSERT_VAL(buf,
+                               !strcmp(CDSO(clist), expected_callchain[i].node[c].dso) &&
+                               !strcmp(CSYM(clist), expected_callchain[i].node[c].sym));
+                       c++;
+               }
+               /* TODO: handle multiple child nodes properly */
+               TEST_ASSERT_VAL("Incorrect number of callchain entry",
+                               c <= expected_callchain[i].nr);
+       }
+       TEST_ASSERT_VAL("Incorrect number of hist entry",
+                       i == nr_expected);
+       TEST_ASSERT_VAL("Incorrect number of callchain entry",
+                       !symbol_conf.use_callchain || nr_expected == nr_callchain);
+       return 0;
+}
+
+/* NO callchain + NO children */
+static int test1(struct perf_evsel *evsel, struct machine *machine)
+{
+       int err;
+       struct hists *hists = &evsel->hists;
+       /*
+        * expected output:
+        *
+        * Overhead  Command  Shared Object          Symbol
+        * ========  =======  =============  ==============
+        *   20.00%     perf  perf           [.] main
+        *   10.00%     bash  [kernel]       [k] page_fault
+        *   10.00%     bash  bash           [.] main
+        *   10.00%     bash  bash           [.] xmalloc
+        *   10.00%     perf  [kernel]       [k] page_fault
+        *   10.00%     perf  [kernel]       [k] schedule
+        *   10.00%     perf  libc           [.] free
+        *   10.00%     perf  libc           [.] malloc
+        *   10.00%     perf  perf           [.] cmd_record
+        */
+       struct result expected[] = {
+               { 0, 2000, "perf", "perf",     "main" },
+               { 0, 1000, "bash", "[kernel]", "page_fault" },
+               { 0, 1000, "bash", "bash",     "main" },
+               { 0, 1000, "bash", "bash",     "xmalloc" },
+               { 0, 1000, "perf", "[kernel]", "page_fault" },
+               { 0, 1000, "perf", "[kernel]", "schedule" },
+               { 0, 1000, "perf", "libc",     "free" },
+               { 0, 1000, "perf", "libc",     "malloc" },
+               { 0, 1000, "perf", "perf",     "cmd_record" },
+       };
+
+       symbol_conf.use_callchain = false;
+       symbol_conf.cumulate_callchain = false;
+
+       setup_sorting();
+       callchain_register_param(&callchain_param);
+
+       err = add_hist_entries(hists, machine);
+       if (err < 0)
+               goto out;
+
+       err = do_test(hists, expected, ARRAY_SIZE(expected), NULL, 0);
+
+out:
+       del_hist_entries(hists);
+       reset_output_field();
+       return err;
+}
+
+/* callcain + NO children */
+static int test2(struct perf_evsel *evsel, struct machine *machine)
+{
+       int err;
+       struct hists *hists = &evsel->hists;
+       /*
+        * expected output:
+        *
+        * Overhead  Command  Shared Object          Symbol
+        * ========  =======  =============  ==============
+        *   20.00%     perf  perf           [.] main
+        *              |
+        *              --- main
+        *
+        *   10.00%     bash  [kernel]       [k] page_fault
+        *              |
+        *              --- page_fault
+        *                  malloc
+        *                  main
+        *
+        *   10.00%     bash  bash           [.] main
+        *              |
+        *              --- main
+        *
+        *   10.00%     bash  bash           [.] xmalloc
+        *              |
+        *              --- xmalloc
+        *                  malloc
+        *                  xmalloc     <--- NOTE: there's a cycle
+        *                  malloc
+        *                  xmalloc
+        *                  main
+        *
+        *   10.00%     perf  [kernel]       [k] page_fault
+        *              |
+        *              --- page_fault
+        *                  sys_perf_event_open
+        *                  run_command
+        *                  main
+        *
+        *   10.00%     perf  [kernel]       [k] schedule
+        *              |
+        *              --- schedule
+        *                  run_command
+        *                  main
+        *
+        *   10.00%     perf  libc           [.] free
+        *              |
+        *              --- free
+        *                  cmd_record
+        *                  run_command
+        *                  main
+        *
+        *   10.00%     perf  libc           [.] malloc
+        *              |
+        *              --- malloc
+        *                  cmd_record
+        *                  run_command
+        *                  main
+        *
+        *   10.00%     perf  perf           [.] cmd_record
+        *              |
+        *              --- cmd_record
+        *                  run_command
+        *                  main
+        *
+        */
+       struct result expected[] = {
+               { 0, 2000, "perf", "perf",     "main" },
+               { 0, 1000, "bash", "[kernel]", "page_fault" },
+               { 0, 1000, "bash", "bash",     "main" },
+               { 0, 1000, "bash", "bash",     "xmalloc" },
+               { 0, 1000, "perf", "[kernel]", "page_fault" },
+               { 0, 1000, "perf", "[kernel]", "schedule" },
+               { 0, 1000, "perf", "libc",     "free" },
+               { 0, 1000, "perf", "libc",     "malloc" },
+               { 0, 1000, "perf", "perf",     "cmd_record" },
+       };
+       struct callchain_result expected_callchain[] = {
+               {
+                       1, {    { "perf",     "main" }, },
+               },
+               {
+                       3, {    { "[kernel]", "page_fault" },
+                               { "libc",     "malloc" },
+                               { "bash",     "main" }, },
+               },
+               {
+                       1, {    { "bash",     "main" }, },
+               },
+               {
+                       6, {    { "bash",     "xmalloc" },
+                               { "libc",     "malloc" },
+                               { "bash",     "xmalloc" },
+                               { "libc",     "malloc" },
+                               { "bash",     "xmalloc" },
+                               { "bash",     "main" }, },
+               },
+               {
+                       4, {    { "[kernel]", "page_fault" },
+                               { "[kernel]", "sys_perf_event_open" },
+                               { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+               {
+                       3, {    { "[kernel]", "schedule" },
+                               { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+               {
+                       4, {    { "libc",     "free" },
+                               { "perf",     "cmd_record" },
+                               { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+               {
+                       4, {    { "libc",     "malloc" },
+                               { "perf",     "cmd_record" },
+                               { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+               {
+                       3, {    { "perf",     "cmd_record" },
+                               { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+       };
+
+       symbol_conf.use_callchain = true;
+       symbol_conf.cumulate_callchain = false;
+
+       setup_sorting();
+       callchain_register_param(&callchain_param);
+
+       err = add_hist_entries(hists, machine);
+       if (err < 0)
+               goto out;
+
+       err = do_test(hists, expected, ARRAY_SIZE(expected),
+                     expected_callchain, ARRAY_SIZE(expected_callchain));
+
+out:
+       del_hist_entries(hists);
+       reset_output_field();
+       return err;
+}
+
+/* NO callchain + children */
+static int test3(struct perf_evsel *evsel, struct machine *machine)
+{
+       int err;
+       struct hists *hists = &evsel->hists;
+       /*
+        * expected output:
+        *
+        * Children      Self  Command  Shared Object                   Symbol
+        * ========  ========  =======  =============  =======================
+        *   70.00%    20.00%     perf  perf           [.] main
+        *   50.00%     0.00%     perf  perf           [.] run_command
+        *   30.00%    10.00%     bash  bash           [.] main
+        *   30.00%    10.00%     perf  perf           [.] cmd_record
+        *   20.00%     0.00%     bash  libc           [.] malloc
+        *   10.00%    10.00%     bash  [kernel]       [k] page_fault
+        *   10.00%    10.00%     perf  [kernel]       [k] schedule
+        *   10.00%     0.00%     perf  [kernel]       [k] sys_perf_event_open
+        *   10.00%    10.00%     perf  [kernel]       [k] page_fault
+        *   10.00%    10.00%     perf  libc           [.] free
+        *   10.00%    10.00%     perf  libc           [.] malloc
+        *   10.00%    10.00%     bash  bash           [.] xmalloc
+        */
+       struct result expected[] = {
+               { 7000, 2000, "perf", "perf",     "main" },
+               { 5000,    0, "perf", "perf",     "run_command" },
+               { 3000, 1000, "bash", "bash",     "main" },
+               { 3000, 1000, "perf", "perf",     "cmd_record" },
+               { 2000,    0, "bash", "libc",     "malloc" },
+               { 1000, 1000, "bash", "[kernel]", "page_fault" },
+               { 1000, 1000, "perf", "[kernel]", "schedule" },
+               { 1000,    0, "perf", "[kernel]", "sys_perf_event_open" },
+               { 1000, 1000, "perf", "[kernel]", "page_fault" },
+               { 1000, 1000, "perf", "libc",     "free" },
+               { 1000, 1000, "perf", "libc",     "malloc" },
+               { 1000, 1000, "bash", "bash",     "xmalloc" },
+       };
+
+       symbol_conf.use_callchain = false;
+       symbol_conf.cumulate_callchain = true;
+
+       setup_sorting();
+       callchain_register_param(&callchain_param);
+
+       err = add_hist_entries(hists, machine);
+       if (err < 0)
+               goto out;
+
+       err = do_test(hists, expected, ARRAY_SIZE(expected), NULL, 0);
+
+out:
+       del_hist_entries(hists);
+       reset_output_field();
+       return err;
+}
+
+/* callchain + children */
+static int test4(struct perf_evsel *evsel, struct machine *machine)
+{
+       int err;
+       struct hists *hists = &evsel->hists;
+       /*
+        * expected output:
+        *
+        * Children      Self  Command  Shared Object                   Symbol
+        * ========  ========  =======  =============  =======================
+        *   70.00%    20.00%     perf  perf           [.] main
+        *              |
+        *              --- main
+        *
+        *   50.00%     0.00%     perf  perf           [.] run_command
+        *              |
+        *              --- run_command
+        *                  main
+        *
+        *   30.00%    10.00%     bash  bash           [.] main
+        *              |
+        *              --- main
+        *
+        *   30.00%    10.00%     perf  perf           [.] cmd_record
+        *              |
+        *              --- cmd_record
+        *                  run_command
+        *                  main
+        *
+        *   20.00%     0.00%     bash  libc           [.] malloc
+        *              |
+        *              --- malloc
+        *                 |
+        *                 |--50.00%-- xmalloc
+        *                 |           main
+        *                  --50.00%-- main
+        *
+        *   10.00%    10.00%     bash  [kernel]       [k] page_fault
+        *              |
+        *              --- page_fault
+        *                  malloc
+        *                  main
+        *
+        *   10.00%    10.00%     perf  [kernel]       [k] schedule
+        *              |
+        *              --- schedule
+        *                  run_command
+        *                  main
+        *
+        *   10.00%     0.00%     perf  [kernel]       [k] sys_perf_event_open
+        *              |
+        *              --- sys_perf_event_open
+        *                  run_command
+        *                  main
+        *
+        *   10.00%    10.00%     perf  [kernel]       [k] page_fault
+        *              |
+        *              --- page_fault
+        *                  sys_perf_event_open
+        *                  run_command
+        *                  main
+        *
+        *   10.00%    10.00%     perf  libc           [.] free
+        *              |
+        *              --- free
+        *                  cmd_record
+        *                  run_command
+        *                  main
+        *
+        *   10.00%    10.00%     perf  libc           [.] malloc
+        *              |
+        *              --- malloc
+        *                  cmd_record
+        *                  run_command
+        *                  main
+        *
+        *   10.00%    10.00%     bash  bash           [.] xmalloc
+        *              |
+        *              --- xmalloc
+        *                  malloc
+        *                  xmalloc     <--- NOTE: there's a cycle
+        *                  malloc
+        *                  xmalloc
+        *                  main
+        *
+        */
+       struct result expected[] = {
+               { 7000, 2000, "perf", "perf",     "main" },
+               { 5000,    0, "perf", "perf",     "run_command" },
+               { 3000, 1000, "bash", "bash",     "main" },
+               { 3000, 1000, "perf", "perf",     "cmd_record" },
+               { 2000,    0, "bash", "libc",     "malloc" },
+               { 1000, 1000, "bash", "[kernel]", "page_fault" },
+               { 1000, 1000, "perf", "[kernel]", "schedule" },
+               { 1000,    0, "perf", "[kernel]", "sys_perf_event_open" },
+               { 1000, 1000, "perf", "[kernel]", "page_fault" },
+               { 1000, 1000, "perf", "libc",     "free" },
+               { 1000, 1000, "perf", "libc",     "malloc" },
+               { 1000, 1000, "bash", "bash",     "xmalloc" },
+       };
+       struct callchain_result expected_callchain[] = {
+               {
+                       1, {    { "perf",     "main" }, },
+               },
+               {
+                       2, {    { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+               {
+                       1, {    { "bash",     "main" }, },
+               },
+               {
+                       3, {    { "perf",     "cmd_record" },
+                               { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+               {
+                       4, {    { "libc",     "malloc" },
+                               { "bash",     "xmalloc" },
+                               { "bash",     "main" },
+                               { "bash",     "main" }, },
+               },
+               {
+                       3, {    { "[kernel]", "page_fault" },
+                               { "libc",     "malloc" },
+                               { "bash",     "main" }, },
+               },
+               {
+                       3, {    { "[kernel]", "schedule" },
+                               { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+               {
+                       3, {    { "[kernel]", "sys_perf_event_open" },
+                               { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+               {
+                       4, {    { "[kernel]", "page_fault" },
+                               { "[kernel]", "sys_perf_event_open" },
+                               { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+               {
+                       4, {    { "libc",     "free" },
+                               { "perf",     "cmd_record" },
+                               { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+               {
+                       4, {    { "libc",     "malloc" },
+                               { "perf",     "cmd_record" },
+                               { "perf",     "run_command" },
+                               { "perf",     "main" }, },
+               },
+               {
+                       6, {    { "bash",     "xmalloc" },
+                               { "libc",     "malloc" },
+                               { "bash",     "xmalloc" },
+                               { "libc",     "malloc" },
+                               { "bash",     "xmalloc" },
+                               { "bash",     "main" }, },
+               },
+       };
+
+       symbol_conf.use_callchain = true;
+       symbol_conf.cumulate_callchain = true;
+
+       setup_sorting();
+       callchain_register_param(&callchain_param);
+
+       err = add_hist_entries(hists, machine);
+       if (err < 0)
+               goto out;
+
+       err = do_test(hists, expected, ARRAY_SIZE(expected),
+                     expected_callchain, ARRAY_SIZE(expected_callchain));
+
+out:
+       del_hist_entries(hists);
+       reset_output_field();
+       return err;
+}
+
+int test__hists_cumulate(void)
+{
+       int err = TEST_FAIL;
+       struct machines machines;
+       struct machine *machine;
+       struct perf_evsel *evsel;
+       struct perf_evlist *evlist = perf_evlist__new();
+       size_t i;
+       test_fn_t testcases[] = {
+               test1,
+               test2,
+               test3,
+               test4,
+       };
+
+       TEST_ASSERT_VAL("No memory", evlist);
+
+       err = parse_events(evlist, "cpu-clock");
+       if (err)
+               goto out;
+
+       machines__init(&machines);
+
+       /* setup threads/dso/map/symbols also */
+       machine = setup_fake_machine(&machines);
+       if (!machine)
+               goto out;
+
+       if (verbose > 1)
+               machine__fprintf(machine, stderr);
+
+       evsel = perf_evlist__first(evlist);
+
+       for (i = 0; i < ARRAY_SIZE(testcases); i++) {
+               err = testcases[i](evsel, machine);
+               if (err < 0)
+                       break;
+       }
+
+out:
+       /* tear down everything */
+       perf_evlist__delete(evlist);
+       machines__exit(&machines);
+
+       return err;
+}
index c5ba924a3581cbda11e86b4627025e7f1a53a227..821f581fd9303623ed5cf5730030381ccce66263 100644 (file)
@@ -21,33 +21,33 @@ struct sample {
 /* For the numbers, see hists_common.c */
 static struct sample fake_samples[] = {
        /* perf [kernel] schedule() */
-       { .pid = 100, .ip = 0xf0000 + 700, },
+       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, },
        /* perf [perf]   main() */
-       { .pid = 100, .ip = 0x40000 + 700, },
+       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_MAIN, },
        /* perf [libc]   malloc() */
-       { .pid = 100, .ip = 0x50000 + 700, },
+       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, },
        /* perf [perf]   main() */
-       { .pid = 200, .ip = 0x40000 + 700, }, /* will be merged */
+       { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, }, /* will be merged */
        /* perf [perf]   cmd_record() */
-       { .pid = 200, .ip = 0x40000 + 900, },
+       { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_CMD_RECORD, },
        /* perf [kernel] page_fault() */
-       { .pid = 200, .ip = 0xf0000 + 800, },
+       { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
        /* bash [bash]   main() */
-       { .pid = 300, .ip = 0x40000 + 700, },
+       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_MAIN, },
        /* bash [bash]   xmalloc() */
-       { .pid = 300, .ip = 0x40000 + 800, },
+       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_XMALLOC, },
        /* bash [libc]   malloc() */
-       { .pid = 300, .ip = 0x50000 + 700, },
+       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_LIBC_MALLOC, },
        /* bash [kernel] page_fault() */
-       { .pid = 300, .ip = 0xf0000 + 800, },
+       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
 };
 
-static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
+static int add_hist_entries(struct perf_evlist *evlist,
+                           struct machine *machine __maybe_unused)
 {
        struct perf_evsel *evsel;
        struct addr_location al;
-       struct hist_entry *he;
-       struct perf_sample sample = { .cpu = 0, };
+       struct perf_sample sample = { .period = 100, };
        size_t i;
 
        /*
@@ -62,6 +62,10 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
                                        .misc = PERF_RECORD_MISC_USER,
                                },
                        };
+                       struct hist_entry_iter iter = {
+                               .ops = &hist_iter_normal,
+                               .hide_unresolved = false,
+                       };
 
                        /* make sure it has no filter at first */
                        evsel->hists.thread_filter = NULL;
@@ -76,18 +80,13 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
                                                          &sample) < 0)
                                goto out;
 
-                       he = __hists__add_entry(&evsel->hists, &al, NULL,
-                                               NULL, NULL, 100, 1, 0);
-                       if (he == NULL)
+                       if (hist_entry_iter__add(&iter, &al, evsel, &sample,
+                                                PERF_MAX_STACK_DEPTH, NULL) < 0)
                                goto out;
 
                        fake_samples[i].thread = al.thread;
                        fake_samples[i].map = al.map;
                        fake_samples[i].sym = al.sym;
-
-                       hists__inc_nr_events(he->hists, PERF_RECORD_SAMPLE);
-                       if (!he->filtered)
-                               he->hists->stats.nr_non_filtered_samples++;
                }
        }
 
index 5ffa2c3eb77d69b68f4d01b96375775613718c6a..d4b34b0f50a228afe694813359b0ca33fef9e83f 100644 (file)
@@ -21,41 +21,41 @@ struct sample {
 /* For the numbers, see hists_common.c */
 static struct sample fake_common_samples[] = {
        /* perf [kernel] schedule() */
-       { .pid = 100, .ip = 0xf0000 + 700, },
+       { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, },
        /* perf [perf]   main() */
-       { .pid = 200, .ip = 0x40000 + 700, },
+       { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, },
        /* perf [perf]   cmd_record() */
-       { .pid = 200, .ip = 0x40000 + 900, },
+       { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_CMD_RECORD, },
        /* bash [bash]   xmalloc() */
-       { .pid = 300, .ip = 0x40000 + 800, },
+       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_XMALLOC, },
        /* bash [libc]   malloc() */
-       { .pid = 300, .ip = 0x50000 + 700, },
+       { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_LIBC_MALLOC, },
 };
 
 static struct sample fake_samples[][5] = {
        {
                /* perf [perf]   run_command() */
-               { .pid = 100, .ip = 0x40000 + 800, },
+               { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_RUN_COMMAND, },
                /* perf [libc]   malloc() */
-               { .pid = 100, .ip = 0x50000 + 700, },
+               { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, },
                /* perf [kernel] page_fault() */
-               { .pid = 100, .ip = 0xf0000 + 800, },
+               { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
                /* perf [kernel] sys_perf_event_open() */
-               { .pid = 200, .ip = 0xf0000 + 900, },
+               { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_SYS_PERF_EVENT_OPEN, },
                /* bash [libc]   free() */
-               { .pid = 300, .ip = 0x50000 + 800, },
+               { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_LIBC_FREE, },
        },
        {
                /* perf [libc]   free() */
-               { .pid = 200, .ip = 0x50000 + 800, },
+               { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_LIBC_FREE, },
                /* bash [libc]   malloc() */
-               { .pid = 300, .ip = 0x50000 + 700, }, /* will be merged */
+               { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_LIBC_MALLOC, }, /* will be merged */
                /* bash [bash]   xfee() */
-               { .pid = 300, .ip = 0x40000 + 900, },
+               { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_XFREE, },
                /* bash [libc]   realloc() */
-               { .pid = 300, .ip = 0x50000 + 900, },
+               { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_LIBC_REALLOC, },
                /* bash [kernel] page_fault() */
-               { .pid = 300, .ip = 0xf0000 + 800, },
+               { .pid = FAKE_PID_BASH,  .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
        },
 };
 
@@ -64,7 +64,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
        struct perf_evsel *evsel;
        struct addr_location al;
        struct hist_entry *he;
-       struct perf_sample sample = { .cpu = 0, };
+       struct perf_sample sample = { .period = 1, };
        size_t i = 0, k;
 
        /*
@@ -88,7 +88,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
                                goto out;
 
                        he = __hists__add_entry(&evsel->hists, &al, NULL,
-                                               NULL, NULL, 1, 1, 0);
+                                               NULL, NULL, 1, 1, 0, true);
                        if (he == NULL)
                                goto out;
 
@@ -112,7 +112,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
                                goto out;
 
                        he = __hists__add_entry(&evsel->hists, &al, NULL,
-                                               NULL, NULL, 1, 1, 0);
+                                               NULL, NULL, 1, 1, 0, true);
                        if (he == NULL)
                                goto out;
 
index a16850551797ea13fbf5d0dfc9cb93a136c5ec7d..e3bbd6c54c1b023f7f7168ab8ed77dae33c8c8d3 100644 (file)
@@ -22,31 +22,31 @@ struct sample {
 /* For the numbers, see hists_common.c */
 static struct sample fake_samples[] = {
        /* perf [kernel] schedule() */
-       { .cpu = 0, .pid = 100, .ip = 0xf0000 + 700, },
+       { .cpu = 0, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, },
        /* perf [perf]   main() */
-       { .cpu = 1, .pid = 100, .ip = 0x40000 + 700, },
+       { .cpu = 1, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_MAIN, },
        /* perf [perf]   cmd_record() */
-       { .cpu = 1, .pid = 100, .ip = 0x40000 + 900, },
+       { .cpu = 1, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_CMD_RECORD, },
        /* perf [libc]   malloc() */
-       { .cpu = 1, .pid = 100, .ip = 0x50000 + 700, },
+       { .cpu = 1, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, },
        /* perf [libc]   free() */
-       { .cpu = 2, .pid = 100, .ip = 0x50000 + 800, },
+       { .cpu = 2, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_FREE, },
        /* perf [perf]   main() */
-       { .cpu = 2, .pid = 200, .ip = 0x40000 + 700, },
+       { .cpu = 2, .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, },
        /* perf [kernel] page_fault() */
-       { .cpu = 2, .pid = 200, .ip = 0xf0000 + 800, },
+       { .cpu = 2, .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
        /* bash [bash]   main() */
-       { .cpu = 3, .pid = 300, .ip = 0x40000 + 700, },
+       { .cpu = 3, .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_MAIN, },
        /* bash [bash]   xmalloc() */
-       { .cpu = 0, .pid = 300, .ip = 0x40000 + 800, },
+       { .cpu = 0, .pid = FAKE_PID_BASH,  .ip = FAKE_IP_BASH_XMALLOC, },
        /* bash [kernel] page_fault() */
-       { .cpu = 1, .pid = 300, .ip = 0xf0000 + 800, },
+       { .cpu = 1, .pid = FAKE_PID_BASH,  .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
 };
 
 static int add_hist_entries(struct hists *hists, struct machine *machine)
 {
        struct addr_location al;
-       struct hist_entry *he;
+       struct perf_evsel *evsel = hists_to_evsel(hists);
        struct perf_sample sample = { .period = 100, };
        size_t i;
 
@@ -56,6 +56,10 @@ static int add_hist_entries(struct hists *hists, struct machine *machine)
                                .misc = PERF_RECORD_MISC_USER,
                        },
                };
+               struct hist_entry_iter iter = {
+                       .ops = &hist_iter_normal,
+                       .hide_unresolved = false,
+               };
 
                sample.cpu = fake_samples[i].cpu;
                sample.pid = fake_samples[i].pid;
@@ -66,9 +70,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine)
                                                  &sample) < 0)
                        goto out;
 
-               he = __hists__add_entry(hists, &al, NULL, NULL, NULL,
-                                       sample.period, 1, 0);
-               if (he == NULL)
+               if (hist_entry_iter__add(&iter, &al, evsel, &sample,
+                                        PERF_MAX_STACK_DEPTH, NULL) < 0)
                        goto out;
 
                fake_samples[i].thread = al.thread;
index d76c0e2e6635024afd2c31b2f20cb8179268175b..022bb68fd9c75350e35b48e26523563595eb66d8 100644 (file)
@@ -45,6 +45,7 @@ int test__hists_filter(void);
 int test__mmap_thread_lookup(void);
 int test__thread_mg_share(void);
 int test__hists_output(void);
+int test__hists_cumulate(void);
 
 #if defined(__x86_64__) || defined(__i386__) || defined(__arm__)
 #ifdef HAVE_DWARF_UNWIND_SUPPORT
index d11541d4d7d7ffcddad8d093b2e79f697967292a..3ccf6e14f89bfe425fa9467c02f028e932642b72 100644 (file)
@@ -194,7 +194,7 @@ int ui_browser__warning(struct ui_browser *browser, int timeout,
                ui_helpline__vpush(format, args);
                va_end(args);
        } else {
-               while ((key == ui__question_window("Warning!", text,
+               while ((key = ui__question_window("Warning!", text,
                                                   "Press any key...",
                                                   timeout)) == K_RESIZE)
                        ui_browser__handle_resize(browser);
index 1c331b934ffc50747c1b6df0ba6658e81fe8971d..52c03fbbba1774b7eb8b4a4695dcf90b439a59e9 100644 (file)
@@ -37,7 +37,6 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size,
 static void hist_browser__update_nr_entries(struct hist_browser *hb);
 
 static struct rb_node *hists__filter_entries(struct rb_node *nd,
-                                            struct hists *hists,
                                             float min_pcnt);
 
 static bool hist_browser__has_filter(struct hist_browser *hb)
@@ -319,7 +318,7 @@ __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
        struct hists *hists = browser->hists;
 
        for (nd = rb_first(&hists->entries);
-            (nd = hists__filter_entries(nd, hists, browser->min_pcnt)) != NULL;
+            (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
             nd = rb_next(nd)) {
                struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
                hist_entry__set_folding(he, unfold);
@@ -651,13 +650,36 @@ hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
                          __hpp__slsmg_color_printf, true);             \
 }
 
+#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)                      \
+static u64 __hpp_get_acc_##_field(struct hist_entry *he)               \
+{                                                                      \
+       return he->stat_acc->_field;                                    \
+}                                                                      \
+                                                                       \
+static int                                                             \
+hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
+                               struct perf_hpp *hpp,                   \
+                               struct hist_entry *he)                  \
+{                                                                      \
+       if (!symbol_conf.cumulate_callchain) {                          \
+               int ret = scnprintf(hpp->buf, hpp->size, "%8s", "N/A"); \
+               slsmg_printf("%s", hpp->buf);                           \
+                                                                       \
+               return ret;                                             \
+       }                                                               \
+       return __hpp__fmt(hpp, he, __hpp_get_acc_##_field, " %6.2f%%",  \
+                         __hpp__slsmg_color_printf, true);             \
+}
+
 __HPP_COLOR_PERCENT_FN(overhead, period)
 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
+__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
 
 #undef __HPP_COLOR_PERCENT_FN
+#undef __HPP_COLOR_ACC_PERCENT_FN
 
 void hist_browser__init_hpp(void)
 {
@@ -671,6 +693,8 @@ void hist_browser__init_hpp(void)
                                hist_browser__hpp_color_overhead_guest_sys;
        perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
                                hist_browser__hpp_color_overhead_guest_us;
+       perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
+                               hist_browser__hpp_color_overhead_acc;
 }
 
 static int hist_browser__show_entry(struct hist_browser *browser,
@@ -783,15 +807,12 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser)
 
        for (nd = browser->top; nd; nd = rb_next(nd)) {
                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
-               u64 total = hists__total_period(h->hists);
-               float percent = 0.0;
+               float percent;
 
                if (h->filtered)
                        continue;
 
-               if (total)
-                       percent = h->stat.period * 100.0 / total;
-
+               percent = hist_entry__get_percent_limit(h);
                if (percent < hb->min_pcnt)
                        continue;
 
@@ -804,16 +825,11 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser)
 }
 
 static struct rb_node *hists__filter_entries(struct rb_node *nd,
-                                            struct hists *hists,
                                             float min_pcnt)
 {
        while (nd != NULL) {
                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
-               u64 total = hists__total_period(hists);
-               float percent = 0.0;
-
-               if (total)
-                       percent = h->stat.period * 100.0 / total;
+               float percent = hist_entry__get_percent_limit(h);
 
                if (!h->filtered && percent >= min_pcnt)
                        return nd;
@@ -825,16 +841,11 @@ static struct rb_node *hists__filter_entries(struct rb_node *nd,
 }
 
 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
-                                                 struct hists *hists,
                                                  float min_pcnt)
 {
        while (nd != NULL) {
                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
-               u64 total = hists__total_period(hists);
-               float percent = 0.0;
-
-               if (total)
-                       percent = h->stat.period * 100.0 / total;
+               float percent = hist_entry__get_percent_limit(h);
 
                if (!h->filtered && percent >= min_pcnt)
                        return nd;
@@ -863,14 +874,14 @@ static void ui_browser__hists_seek(struct ui_browser *browser,
        switch (whence) {
        case SEEK_SET:
                nd = hists__filter_entries(rb_first(browser->entries),
-                                          hb->hists, hb->min_pcnt);
+                                          hb->min_pcnt);
                break;
        case SEEK_CUR:
                nd = browser->top;
                goto do_offset;
        case SEEK_END:
                nd = hists__filter_prev_entries(rb_last(browser->entries),
-                                               hb->hists, hb->min_pcnt);
+                                               hb->min_pcnt);
                first = false;
                break;
        default:
@@ -913,8 +924,7 @@ static void ui_browser__hists_seek(struct ui_browser *browser,
                                        break;
                                }
                        }
-                       nd = hists__filter_entries(rb_next(nd), hb->hists,
-                                                  hb->min_pcnt);
+                       nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
                        if (nd == NULL)
                                break;
                        --offset;
@@ -947,7 +957,7 @@ static void ui_browser__hists_seek(struct ui_browser *browser,
                                }
                        }
 
-                       nd = hists__filter_prev_entries(rb_prev(nd), hb->hists,
+                       nd = hists__filter_prev_entries(rb_prev(nd),
                                                        hb->min_pcnt);
                        if (nd == NULL)
                                break;
@@ -1126,7 +1136,6 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,
 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
 {
        struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
-                                                  browser->hists,
                                                   browser->min_pcnt);
        int printed = 0;
 
@@ -1134,8 +1143,7 @@ static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 
                printed += hist_browser__fprintf_entry(browser, h, fp);
-               nd = hists__filter_entries(rb_next(nd), browser->hists,
-                                          browser->min_pcnt);
+               nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
        }
 
        return printed;
@@ -1372,8 +1380,7 @@ static void hist_browser__update_nr_entries(struct hist_browser *hb)
                return;
        }
 
-       while ((nd = hists__filter_entries(nd, hb->hists,
-                                          hb->min_pcnt)) != NULL) {
+       while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
                nr_entries++;
                nd = rb_next(nd);
        }
@@ -1699,14 +1706,14 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
 zoom_out_dso:
                                ui_helpline__pop();
                                browser->hists->dso_filter = NULL;
-                               sort_dso.elide = false;
+                               perf_hpp__set_elide(HISTC_DSO, false);
                        } else {
                                if (dso == NULL)
                                        continue;
                                ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
                                                   dso->kernel ? "the Kernel" : dso->short_name);
                                browser->hists->dso_filter = dso;
-                               sort_dso.elide = true;
+                               perf_hpp__set_elide(HISTC_DSO, true);
                                pstack__push(fstack, &browser->hists->dso_filter);
                        }
                        hists__filter_by_dso(hists);
@@ -1718,13 +1725,13 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
 zoom_out_thread:
                                ui_helpline__pop();
                                browser->hists->thread_filter = NULL;
-                               sort_thread.elide = false;
+                               perf_hpp__set_elide(HISTC_THREAD, false);
                        } else {
                                ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
                                                   thread->comm_set ? thread__comm_str(thread) : "",
                                                   thread->tid);
                                browser->hists->thread_filter = thread;
-                               sort_thread.elide = true;
+                               perf_hpp__set_elide(HISTC_THREAD, false);
                                pstack__push(fstack, &browser->hists->thread_filter);
                        }
                        hists__filter_by_thread(hists);
index 9d90683914d46cc28c1aa88db2e2d54e863f5ae9..6ca60e482cdc2594382466bbaecdde12462dfbec 100644 (file)
@@ -47,11 +47,26 @@ static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,
                          __percent_color_snprintf, true);                      \
 }
 
+#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)                              \
+static u64 he_get_acc_##_field(struct hist_entry *he)                          \
+{                                                                              \
+       return he->stat_acc->_field;                                            \
+}                                                                              \
+                                                                               \
+static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,        \
+                                      struct perf_hpp *hpp,                    \
+                                      struct hist_entry *he)                   \
+{                                                                              \
+       return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, " %6.2f%%",         \
+                             __percent_color_snprintf, true);                  \
+}
+
 __HPP_COLOR_PERCENT_FN(overhead, period)
 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
+__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
 
 #undef __HPP_COLOR_PERCENT_FN
 
@@ -68,6 +83,8 @@ void perf_gtk__init_hpp(void)
                                perf_gtk__hpp_color_overhead_guest_sys;
        perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
                                perf_gtk__hpp_color_overhead_guest_us;
+       perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
+                               perf_gtk__hpp_color_overhead_acc;
 }
 
 static void callchain_list__sym_name(struct callchain_list *cl,
@@ -181,6 +198,13 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
                if (perf_hpp__should_skip(fmt))
                        continue;
 
+               /*
+                * XXX no way to determine where symcol column is..
+                *     Just use last column for now.
+                */
+               if (perf_hpp__is_sort_entry(fmt))
+                       sym_col = col_idx;
+
                fmt->header(fmt, &hpp, hists_to_evsel(hists));
 
                gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
@@ -209,14 +233,12 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
                GtkTreeIter iter;
                u64 total = hists__total_period(h->hists);
-               float percent = 0.0;
+               float percent;
 
                if (h->filtered)
                        continue;
 
-               if (total)
-                       percent = h->stat.period * 100.0 / total;
-
+               percent = hist_entry__get_percent_limit(h);
                if (percent < min_pcnt)
                        continue;
 
@@ -238,7 +260,8 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
 
                if (symbol_conf.use_callchain && sort__has_sym) {
                        if (callchain_param.mode == CHAIN_GRAPH_REL)
-                               total = h->stat.period;
+                               total = symbol_conf.cumulate_callchain ?
+                                       h->stat_acc->period : h->stat.period;
 
                        perf_gtk__add_callchain(&h->sorted_chain, store, &iter,
                                                sym_col, total);
index 4484f5bd1b14a6065637605b745b3aff2e08bbbc..498adb23c02ef9992f82c840aca5aa31220af9bf 100644 (file)
@@ -104,6 +104,18 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
        return ret;
 }
 
+int __hpp__fmt_acc(struct perf_hpp *hpp, struct hist_entry *he,
+                  hpp_field_fn get_field, const char *fmt,
+                  hpp_snprint_fn print_fn, bool fmt_percent)
+{
+       if (!symbol_conf.cumulate_callchain) {
+               return snprintf(hpp->buf, hpp->size, "%*s",
+                               fmt_percent ? 8 : 12, "N/A");
+       }
+
+       return __hpp__fmt(hpp, he, get_field, fmt, print_fn, fmt_percent);
+}
+
 static int field_cmp(u64 field_a, u64 field_b)
 {
        if (field_a > field_b)
@@ -160,6 +172,24 @@ static int __hpp__sort(struct hist_entry *a, struct hist_entry *b,
        return ret;
 }
 
+static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b,
+                          hpp_field_fn get_field)
+{
+       s64 ret = 0;
+
+       if (symbol_conf.cumulate_callchain) {
+               /*
+                * Put caller above callee when they have equal period.
+                */
+               ret = field_cmp(get_field(a), get_field(b));
+               if (ret)
+                       return ret;
+
+               ret = b->callchain->max_depth - a->callchain->max_depth;
+       }
+       return ret;
+}
+
 #define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width)          \
 static int hpp__header_##_type(struct perf_hpp_fmt *fmt __maybe_unused,        \
                               struct perf_hpp *hpp,                    \
@@ -242,6 +272,34 @@ static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b)       \
        return __hpp__sort(a, b, he_get_##_field);                              \
 }
 
+#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)                              \
+static u64 he_get_acc_##_field(struct hist_entry *he)                          \
+{                                                                              \
+       return he->stat_acc->_field;                                            \
+}                                                                              \
+                                                                               \
+static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,         \
+                             struct perf_hpp *hpp, struct hist_entry *he)      \
+{                                                                              \
+       return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, " %6.2f%%",         \
+                             hpp_color_scnprintf, true);                       \
+}
+
+#define __HPP_ENTRY_ACC_PERCENT_FN(_type, _field)                              \
+static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused,                \
+                             struct perf_hpp *hpp, struct hist_entry *he)      \
+{                                                                              \
+       const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%";         \
+       return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, fmt,                \
+                             hpp_entry_scnprintf, true);                       \
+}
+
+#define __HPP_SORT_ACC_FN(_type, _field)                                       \
+static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b)   \
+{                                                                              \
+       return __hpp__sort_acc(a, b, he_get_acc_##_field);                      \
+}
+
 #define __HPP_ENTRY_RAW_FN(_type, _field)                                      \
 static u64 he_get_raw_##_field(struct hist_entry *he)                          \
 {                                                                              \
@@ -270,18 +328,27 @@ __HPP_COLOR_PERCENT_FN(_type, _field)                                     \
 __HPP_ENTRY_PERCENT_FN(_type, _field)                                  \
 __HPP_SORT_FN(_type, _field)
 
+#define HPP_PERCENT_ACC_FNS(_type, _str, _field, _min_width, _unit_width)\
+__HPP_HEADER_FN(_type, _str, _min_width, _unit_width)                  \
+__HPP_WIDTH_FN(_type, _min_width, _unit_width)                         \
+__HPP_COLOR_ACC_PERCENT_FN(_type, _field)                              \
+__HPP_ENTRY_ACC_PERCENT_FN(_type, _field)                              \
+__HPP_SORT_ACC_FN(_type, _field)
+
 #define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width)      \
 __HPP_HEADER_FN(_type, _str, _min_width, _unit_width)                  \
 __HPP_WIDTH_FN(_type, _min_width, _unit_width)                         \
 __HPP_ENTRY_RAW_FN(_type, _field)                                      \
 __HPP_SORT_RAW_FN(_type, _field)
 
+__HPP_HEADER_FN(overhead_self, "Self", 8, 8)
 
 HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8)
 HPP_PERCENT_FNS(overhead_sys, "sys", period_sys, 8, 8)
 HPP_PERCENT_FNS(overhead_us, "usr", period_us, 8, 8)
 HPP_PERCENT_FNS(overhead_guest_sys, "guest sys", period_guest_sys, 9, 8)
 HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8)
+HPP_PERCENT_ACC_FNS(overhead_acc, "Children", period, 8, 8)
 
 HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12)
 HPP_RAW_FNS(period, "Period", period, 12, 12)
@@ -303,6 +370,17 @@ static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused,
                .sort   = hpp__sort_ ## _name,          \
        }
 
+#define HPP__COLOR_ACC_PRINT_FNS(_name)                        \
+       {                                               \
+               .header = hpp__header_ ## _name,        \
+               .width  = hpp__width_ ## _name,         \
+               .color  = hpp__color_ ## _name,         \
+               .entry  = hpp__entry_ ## _name,         \
+               .cmp    = hpp__nop_cmp,                 \
+               .collapse = hpp__nop_cmp,               \
+               .sort   = hpp__sort_ ## _name,          \
+       }
+
 #define HPP__PRINT_FNS(_name)                          \
        {                                               \
                .header = hpp__header_ ## _name,        \
@@ -319,6 +397,7 @@ struct perf_hpp_fmt perf_hpp__format[] = {
        HPP__COLOR_PRINT_FNS(overhead_us),
        HPP__COLOR_PRINT_FNS(overhead_guest_sys),
        HPP__COLOR_PRINT_FNS(overhead_guest_us),
+       HPP__COLOR_ACC_PRINT_FNS(overhead_acc),
        HPP__PRINT_FNS(samples),
        HPP__PRINT_FNS(period)
 };
@@ -328,16 +407,23 @@ LIST_HEAD(perf_hpp__sort_list);
 
 
 #undef HPP__COLOR_PRINT_FNS
+#undef HPP__COLOR_ACC_PRINT_FNS
 #undef HPP__PRINT_FNS
 
 #undef HPP_PERCENT_FNS
+#undef HPP_PERCENT_ACC_FNS
 #undef HPP_RAW_FNS
 
 #undef __HPP_HEADER_FN
 #undef __HPP_WIDTH_FN
 #undef __HPP_COLOR_PERCENT_FN
 #undef __HPP_ENTRY_PERCENT_FN
+#undef __HPP_COLOR_ACC_PERCENT_FN
+#undef __HPP_ENTRY_ACC_PERCENT_FN
 #undef __HPP_ENTRY_RAW_FN
+#undef __HPP_SORT_FN
+#undef __HPP_SORT_ACC_FN
+#undef __HPP_SORT_RAW_FN
 
 
 void perf_hpp__init(void)
@@ -361,6 +447,13 @@ void perf_hpp__init(void)
        if (field_order)
                return;
 
+       if (symbol_conf.cumulate_callchain) {
+               perf_hpp__column_enable(PERF_HPP__OVERHEAD_ACC);
+
+               perf_hpp__format[PERF_HPP__OVERHEAD].header =
+                                               hpp__header_overhead_self;
+       }
+
        perf_hpp__column_enable(PERF_HPP__OVERHEAD);
 
        if (symbol_conf.show_cpu_utilization) {
@@ -383,6 +476,12 @@ void perf_hpp__init(void)
        list = &perf_hpp__format[PERF_HPP__OVERHEAD].sort_list;
        if (list_empty(list))
                list_add(list, &perf_hpp__sort_list);
+
+       if (symbol_conf.cumulate_callchain) {
+               list = &perf_hpp__format[PERF_HPP__OVERHEAD_ACC].sort_list;
+               if (list_empty(list))
+                       list_add(list, &perf_hpp__sort_list);
+       }
 }
 
 void perf_hpp__column_register(struct perf_hpp_fmt *format)
@@ -390,6 +489,11 @@ void perf_hpp__column_register(struct perf_hpp_fmt *format)
        list_add_tail(&format->list, &perf_hpp__list);
 }
 
+void perf_hpp__column_unregister(struct perf_hpp_fmt *format)
+{
+       list_del(&format->list);
+}
+
 void perf_hpp__register_sort_field(struct perf_hpp_fmt *format)
 {
        list_add_tail(&format->sort_list, &perf_hpp__sort_list);
@@ -401,6 +505,21 @@ void perf_hpp__column_enable(unsigned col)
        perf_hpp__column_register(&perf_hpp__format[col]);
 }
 
+void perf_hpp__column_disable(unsigned col)
+{
+       BUG_ON(col >= PERF_HPP__MAX_INDEX);
+       perf_hpp__column_unregister(&perf_hpp__format[col]);
+}
+
+void perf_hpp__cancel_cumulate(void)
+{
+       if (field_order)
+               return;
+
+       perf_hpp__column_disable(PERF_HPP__OVERHEAD_ACC);
+       perf_hpp__format[PERF_HPP__OVERHEAD].header = hpp__header_overhead;
+}
+
 void perf_hpp__setup_output_field(void)
 {
        struct perf_hpp_fmt *fmt;
index 9f57991025a90fd2648f6d54832dedd9a4c8ab10..90122abd372133fdb0358488e09810030c234b6c 100644 (file)
@@ -271,7 +271,9 @@ static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
 {
        switch (callchain_param.mode) {
        case CHAIN_GRAPH_REL:
-               return callchain__fprintf_graph(fp, &he->sorted_chain, he->stat.period,
+               return callchain__fprintf_graph(fp, &he->sorted_chain,
+                                               symbol_conf.cumulate_callchain ?
+                                               he->stat_acc->period : he->stat.period,
                                                left_margin);
                break;
        case CHAIN_GRAPH_ABS:
@@ -461,12 +463,12 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
 
        for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
                struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
-               float percent = h->stat.period * 100.0 /
-                                       hists->stats.total_period;
+               float percent;
 
                if (h->filtered)
                        continue;
 
+               percent = hist_entry__get_percent_limit(h);
                if (percent < min_pcnt)
                        continue;
 
index 9a42382b3921a76a531d97481a32df2e8f29542f..48b6d3f500123162df296ff63514351c4aa1b991 100644 (file)
@@ -616,7 +616,8 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent
        if (sample->callchain == NULL)
                return 0;
 
-       if (symbol_conf.use_callchain || sort__has_parent) {
+       if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain ||
+           sort__has_parent) {
                return machine__resolve_callchain(al->machine, evsel, al->thread,
                                                  sample, parent, al, max_stack);
        }
@@ -629,3 +630,45 @@ int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *samp
                return 0;
        return callchain_append(he->callchain, &callchain_cursor, sample->period);
 }
+
+int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node,
+                       bool hide_unresolved)
+{
+       al->map = node->map;
+       al->sym = node->sym;
+       if (node->map)
+               al->addr = node->map->map_ip(node->map, node->ip);
+       else
+               al->addr = node->ip;
+
+       if (al->sym == NULL) {
+               if (hide_unresolved)
+                       return 0;
+               if (al->map == NULL)
+                       goto out;
+       }
+
+       if (al->map->groups == &al->machine->kmaps) {
+               if (machine__is_host(al->machine)) {
+                       al->cpumode = PERF_RECORD_MISC_KERNEL;
+                       al->level = 'k';
+               } else {
+                       al->cpumode = PERF_RECORD_MISC_GUEST_KERNEL;
+                       al->level = 'g';
+               }
+       } else {
+               if (machine__is_host(al->machine)) {
+                       al->cpumode = PERF_RECORD_MISC_USER;
+                       al->level = '.';
+               } else if (perf_guest) {
+                       al->cpumode = PERF_RECORD_MISC_GUEST_USER;
+                       al->level = 'u';
+               } else {
+                       al->cpumode = PERF_RECORD_MISC_HYPERVISOR;
+                       al->level = 'H';
+               }
+       }
+
+out:
+       return 1;
+}
index bde2b0cc24cf473c463cc4a030d7ff50036d08dd..8f84423a75da74db5695c060cb13d15a8b503203 100644 (file)
@@ -162,7 +162,18 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent
                              struct perf_evsel *evsel, struct addr_location *al,
                              int max_stack);
 int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample);
+int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node,
+                       bool hide_unresolved);
 
 extern const char record_callchain_help[];
 int parse_callchain_report_opt(const char *arg);
+
+static inline void callchain_cursor_snapshot(struct callchain_cursor *dest,
+                                            struct callchain_cursor *src)
+{
+       *dest = *src;
+
+       dest->first = src->curr;
+       dest->nr -= src->pos;
+}
 #endif /* __PERF_CALLCHAIN_H */
index b262b44b7a656d8dca4fcab7488e43e3c49ebb8c..5a0a4b2cadc4574dce4c23239ac9a14e4e13b6fe 100644 (file)
@@ -4,6 +4,7 @@
 #include "session.h"
 #include "sort.h"
 #include "evsel.h"
+#include "annotate.h"
 #include <math.h>
 
 static bool hists__filter_entry_by_dso(struct hists *hists,
@@ -231,6 +232,8 @@ static bool hists__decay_entry(struct hists *hists, struct hist_entry *he)
                return true;
 
        he_stat__decay(&he->stat);
+       if (symbol_conf.cumulate_callchain)
+               he_stat__decay(he->stat_acc);
 
        diff = prev_period - he->stat.period;
 
@@ -276,14 +279,31 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel)
  * histogram, sorted on item, collects periods
  */
 
-static struct hist_entry *hist_entry__new(struct hist_entry *template)
+static struct hist_entry *hist_entry__new(struct hist_entry *template,
+                                         bool sample_self)
 {
-       size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0;
-       struct hist_entry *he = zalloc(sizeof(*he) + callchain_size);
+       size_t callchain_size = 0;
+       struct hist_entry *he;
+
+       if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain)
+               callchain_size = sizeof(struct callchain_root);
+
+       he = zalloc(sizeof(*he) + callchain_size);
 
        if (he != NULL) {
                *he = *template;
 
+               if (symbol_conf.cumulate_callchain) {
+                       he->stat_acc = malloc(sizeof(he->stat));
+                       if (he->stat_acc == NULL) {
+                               free(he);
+                               return NULL;
+                       }
+                       memcpy(he->stat_acc, &he->stat, sizeof(he->stat));
+                       if (!sample_self)
+                               memset(&he->stat, 0, sizeof(he->stat));
+               }
+
                if (he->ms.map)
                        he->ms.map->referenced = true;
 
@@ -295,6 +315,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
                         */
                        he->branch_info = malloc(sizeof(*he->branch_info));
                        if (he->branch_info == NULL) {
+                               free(he->stat_acc);
                                free(he);
                                return NULL;
                        }
@@ -333,7 +354,8 @@ static u8 symbol__parent_filter(const struct symbol *parent)
 
 static struct hist_entry *add_hist_entry(struct hists *hists,
                                         struct hist_entry *entry,
-                                        struct addr_location *al)
+                                        struct addr_location *al,
+                                        bool sample_self)
 {
        struct rb_node **p;
        struct rb_node *parent = NULL;
@@ -357,7 +379,10 @@ static struct hist_entry *add_hist_entry(struct hists *hists,
                cmp = hist_entry__cmp(he, entry);
 
                if (!cmp) {
-                       he_stat__add_period(&he->stat, period, weight);
+                       if (sample_self)
+                               he_stat__add_period(&he->stat, period, weight);
+                       if (symbol_conf.cumulate_callchain)
+                               he_stat__add_period(he->stat_acc, period, weight);
 
                        /*
                         * This mem info was allocated from sample__resolve_mem
@@ -385,14 +410,17 @@ static struct hist_entry *add_hist_entry(struct hists *hists,
                        p = &(*p)->rb_right;
        }
 
-       he = hist_entry__new(entry);
+       he = hist_entry__new(entry, sample_self);
        if (!he)
                return NULL;
 
        rb_link_node(&he->rb_node_in, parent, p);
        rb_insert_color(&he->rb_node_in, hists->entries_in);
 out:
-       he_stat__add_cpumode_period(&he->stat, al->cpumode, period);
+       if (sample_self)
+               he_stat__add_cpumode_period(&he->stat, al->cpumode, period);
+       if (symbol_conf.cumulate_callchain)
+               he_stat__add_cpumode_period(he->stat_acc, al->cpumode, period);
        return he;
 }
 
@@ -401,7 +429,8 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
                                      struct symbol *sym_parent,
                                      struct branch_info *bi,
                                      struct mem_info *mi,
-                                     u64 period, u64 weight, u64 transaction)
+                                     u64 period, u64 weight, u64 transaction,
+                                     bool sample_self)
 {
        struct hist_entry entry = {
                .thread = al->thread,
@@ -426,7 +455,429 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
                .transaction = transaction,
        };
 
-       return add_hist_entry(hists, &entry, al);
+       return add_hist_entry(hists, &entry, al, sample_self);
+}
+
+static int
+iter_next_nop_entry(struct hist_entry_iter *iter __maybe_unused,
+                   struct addr_location *al __maybe_unused)
+{
+       return 0;
+}
+
+static int
+iter_add_next_nop_entry(struct hist_entry_iter *iter __maybe_unused,
+                       struct addr_location *al __maybe_unused)
+{
+       return 0;
+}
+
+static int
+iter_prepare_mem_entry(struct hist_entry_iter *iter, struct addr_location *al)
+{
+       struct perf_sample *sample = iter->sample;
+       struct mem_info *mi;
+
+       mi = sample__resolve_mem(sample, al);
+       if (mi == NULL)
+               return -ENOMEM;
+
+       iter->priv = mi;
+       return 0;
+}
+
+static int
+iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al)
+{
+       u64 cost;
+       struct mem_info *mi = iter->priv;
+       struct hist_entry *he;
+
+       if (mi == NULL)
+               return -EINVAL;
+
+       cost = iter->sample->weight;
+       if (!cost)
+               cost = 1;
+
+       /*
+        * must pass period=weight in order to get the correct
+        * sorting from hists__collapse_resort() which is solely
+        * based on periods. We want sorting be done on nr_events * weight
+        * and this is indirectly achieved by passing period=weight here
+        * and the he_stat__add_period() function.
+        */
+       he = __hists__add_entry(&iter->evsel->hists, al, iter->parent, NULL, mi,
+                               cost, cost, 0, true);
+       if (!he)
+               return -ENOMEM;
+
+       iter->he = he;
+       return 0;
+}
+
+static int
+iter_finish_mem_entry(struct hist_entry_iter *iter,
+                     struct addr_location *al __maybe_unused)
+{
+       struct perf_evsel *evsel = iter->evsel;
+       struct hist_entry *he = iter->he;
+       int err = -EINVAL;
+
+       if (he == NULL)
+               goto out;
+
+       hists__inc_nr_samples(&evsel->hists, he->filtered);
+
+       err = hist_entry__append_callchain(he, iter->sample);
+
+out:
+       /*
+        * We don't need to free iter->priv (mem_info) here since
+        * the mem info was either already freed in add_hist_entry() or
+        * passed to a new hist entry by hist_entry__new().
+        */
+       iter->priv = NULL;
+
+       iter->he = NULL;
+       return err;
+}
+
+static int
+iter_prepare_branch_entry(struct hist_entry_iter *iter, struct addr_location *al)
+{
+       struct branch_info *bi;
+       struct perf_sample *sample = iter->sample;
+
+       bi = sample__resolve_bstack(sample, al);
+       if (!bi)
+               return -ENOMEM;
+
+       iter->curr = 0;
+       iter->total = sample->branch_stack->nr;
+
+       iter->priv = bi;
+       return 0;
+}
+
+static int
+iter_add_single_branch_entry(struct hist_entry_iter *iter __maybe_unused,
+                            struct addr_location *al __maybe_unused)
+{
+       /* to avoid calling callback function */
+       iter->he = NULL;
+
+       return 0;
+}
+
+static int
+iter_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al)
+{
+       struct branch_info *bi = iter->priv;
+       int i = iter->curr;
+
+       if (bi == NULL)
+               return 0;
+
+       if (iter->curr >= iter->total)
+               return 0;
+
+       al->map = bi[i].to.map;
+       al->sym = bi[i].to.sym;
+       al->addr = bi[i].to.addr;
+       return 1;
+}
+
+static int
+iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al)
+{
+       struct branch_info *bi;
+       struct perf_evsel *evsel = iter->evsel;
+       struct hist_entry *he = NULL;
+       int i = iter->curr;
+       int err = 0;
+
+       bi = iter->priv;
+
+       if (iter->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym))
+               goto out;
+
+       /*
+        * The report shows the percentage of total branches captured
+        * and not events sampled. Thus we use a pseudo period of 1.
+        */
+       he = __hists__add_entry(&evsel->hists, al, iter->parent, &bi[i], NULL,
+                               1, 1, 0, true);
+       if (he == NULL)
+               return -ENOMEM;
+
+       hists__inc_nr_samples(&evsel->hists, he->filtered);
+
+out:
+       iter->he = he;
+       iter->curr++;
+       return err;
+}
+
+static int
+iter_finish_branch_entry(struct hist_entry_iter *iter,
+                        struct addr_location *al __maybe_unused)
+{
+       zfree(&iter->priv);
+       iter->he = NULL;
+
+       return iter->curr >= iter->total ? 0 : -1;
+}
+
+static int
+iter_prepare_normal_entry(struct hist_entry_iter *iter __maybe_unused,
+                         struct addr_location *al __maybe_unused)
+{
+       return 0;
+}
+
+static int
+iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location *al)
+{
+       struct perf_evsel *evsel = iter->evsel;
+       struct perf_sample *sample = iter->sample;
+       struct hist_entry *he;
+
+       he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL,
+                               sample->period, sample->weight,
+                               sample->transaction, true);
+       if (he == NULL)
+               return -ENOMEM;
+
+       iter->he = he;
+       return 0;
+}
+
+static int
+iter_finish_normal_entry(struct hist_entry_iter *iter,
+                        struct addr_location *al __maybe_unused)
+{
+       struct hist_entry *he = iter->he;
+       struct perf_evsel *evsel = iter->evsel;
+       struct perf_sample *sample = iter->sample;
+
+       if (he == NULL)
+               return 0;
+
+       iter->he = NULL;
+
+       hists__inc_nr_samples(&evsel->hists, he->filtered);
+
+       return hist_entry__append_callchain(he, sample);
+}
+
+static int
+iter_prepare_cumulative_entry(struct hist_entry_iter *iter __maybe_unused,
+                             struct addr_location *al __maybe_unused)
+{
+       struct hist_entry **he_cache;
+
+       callchain_cursor_commit(&callchain_cursor);
+
+       /*
+        * This is for detecting cycles or recursions so that they're
+        * cumulated only one time to prevent entries more than 100%
+        * overhead.
+        */
+       he_cache = malloc(sizeof(*he_cache) * (PERF_MAX_STACK_DEPTH + 1));
+       if (he_cache == NULL)
+               return -ENOMEM;
+
+       iter->priv = he_cache;
+       iter->curr = 0;
+
+       return 0;
+}
+
+static int
+iter_add_single_cumulative_entry(struct hist_entry_iter *iter,
+                                struct addr_location *al)
+{
+       struct perf_evsel *evsel = iter->evsel;
+       struct perf_sample *sample = iter->sample;
+       struct hist_entry **he_cache = iter->priv;
+       struct hist_entry *he;
+       int err = 0;
+
+       he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL,
+                               sample->period, sample->weight,
+                               sample->transaction, true);
+       if (he == NULL)
+               return -ENOMEM;
+
+       iter->he = he;
+       he_cache[iter->curr++] = he;
+
+       callchain_append(he->callchain, &callchain_cursor, sample->period);
+
+       /*
+        * We need to re-initialize the cursor since callchain_append()
+        * advanced the cursor to the end.
+        */
+       callchain_cursor_commit(&callchain_cursor);
+
+       hists__inc_nr_samples(&evsel->hists, he->filtered);
+
+       return err;
+}
+
+static int
+iter_next_cumulative_entry(struct hist_entry_iter *iter,
+                          struct addr_location *al)
+{
+       struct callchain_cursor_node *node;
+
+       node = callchain_cursor_current(&callchain_cursor);
+       if (node == NULL)
+               return 0;
+
+       return fill_callchain_info(al, node, iter->hide_unresolved);
+}
+
+static int
+iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
+                              struct addr_location *al)
+{
+       struct perf_evsel *evsel = iter->evsel;
+       struct perf_sample *sample = iter->sample;
+       struct hist_entry **he_cache = iter->priv;
+       struct hist_entry *he;
+       struct hist_entry he_tmp = {
+               .cpu = al->cpu,
+               .thread = al->thread,
+               .comm = thread__comm(al->thread),
+               .ip = al->addr,
+               .ms = {
+                       .map = al->map,
+                       .sym = al->sym,
+               },
+               .parent = iter->parent,
+       };
+       int i;
+       struct callchain_cursor cursor;
+
+       callchain_cursor_snapshot(&cursor, &callchain_cursor);
+
+       callchain_cursor_advance(&callchain_cursor);
+
+       /*
+        * Check if there's duplicate entries in the callchain.
+        * It's possible that it has cycles or recursive calls.
+        */
+       for (i = 0; i < iter->curr; i++) {
+               if (hist_entry__cmp(he_cache[i], &he_tmp) == 0) {
+                       /* to avoid calling callback function */
+                       iter->he = NULL;
+                       return 0;
+               }
+       }
+
+       he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL,
+                               sample->period, sample->weight,
+                               sample->transaction, false);
+       if (he == NULL)
+               return -ENOMEM;
+
+       iter->he = he;
+       he_cache[iter->curr++] = he;
+
+       callchain_append(he->callchain, &cursor, sample->period);
+       return 0;
+}
+
+static int
+iter_finish_cumulative_entry(struct hist_entry_iter *iter,
+                            struct addr_location *al __maybe_unused)
+{
+       zfree(&iter->priv);
+       iter->he = NULL;
+
+       return 0;
+}
+
+const struct hist_iter_ops hist_iter_mem = {
+       .prepare_entry          = iter_prepare_mem_entry,
+       .add_single_entry       = iter_add_single_mem_entry,
+       .next_entry             = iter_next_nop_entry,
+       .add_next_entry         = iter_add_next_nop_entry,
+       .finish_entry           = iter_finish_mem_entry,
+};
+
+const struct hist_iter_ops hist_iter_branch = {
+       .prepare_entry          = iter_prepare_branch_entry,
+       .add_single_entry       = iter_add_single_branch_entry,
+       .next_entry             = iter_next_branch_entry,
+       .add_next_entry         = iter_add_next_branch_entry,
+       .finish_entry           = iter_finish_branch_entry,
+};
+
+const struct hist_iter_ops hist_iter_normal = {
+       .prepare_entry          = iter_prepare_normal_entry,
+       .add_single_entry       = iter_add_single_normal_entry,
+       .next_entry             = iter_next_nop_entry,
+       .add_next_entry         = iter_add_next_nop_entry,
+       .finish_entry           = iter_finish_normal_entry,
+};
+
+const struct hist_iter_ops hist_iter_cumulative = {
+       .prepare_entry          = iter_prepare_cumulative_entry,
+       .add_single_entry       = iter_add_single_cumulative_entry,
+       .next_entry             = iter_next_cumulative_entry,
+       .add_next_entry         = iter_add_next_cumulative_entry,
+       .finish_entry           = iter_finish_cumulative_entry,
+};
+
+int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
+                        struct perf_evsel *evsel, struct perf_sample *sample,
+                        int max_stack_depth, void *arg)
+{
+       int err, err2;
+
+       err = sample__resolve_callchain(sample, &iter->parent, evsel, al,
+                                       max_stack_depth);
+       if (err)
+               return err;
+
+       iter->evsel = evsel;
+       iter->sample = sample;
+
+       err = iter->ops->prepare_entry(iter, al);
+       if (err)
+               goto out;
+
+       err = iter->ops->add_single_entry(iter, al);
+       if (err)
+               goto out;
+
+       if (iter->he && iter->add_entry_cb) {
+               err = iter->add_entry_cb(iter, al, true, arg);
+               if (err)
+                       goto out;
+       }
+
+       while (iter->ops->next_entry(iter, al)) {
+               err = iter->ops->add_next_entry(iter, al);
+               if (err)
+                       break;
+
+               if (iter->he && iter->add_entry_cb) {
+                       err = iter->add_entry_cb(iter, al, false, arg);
+                       if (err)
+                               goto out;
+               }
+       }
+
+out:
+       err2 = iter->ops->finish_entry(iter, al);
+       if (!err)
+               err = err2;
+
+       return err;
 }
 
 int64_t
@@ -469,6 +920,7 @@ void hist_entry__free(struct hist_entry *he)
 {
        zfree(&he->branch_info);
        zfree(&he->mem_info);
+       zfree(&he->stat_acc);
        free_srcline(he->srcline);
        free(he);
 }
@@ -494,6 +946,8 @@ static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused,
 
                if (!cmp) {
                        he_stat__add_stat(&iter->stat, &he->stat);
+                       if (symbol_conf.cumulate_callchain)
+                               he_stat__add_stat(iter->stat_acc, he->stat_acc);
 
                        if (symbol_conf.use_callchain) {
                                callchain_cursor_reset(&callchain_cursor);
@@ -800,6 +1254,13 @@ void hists__inc_nr_events(struct hists *hists, u32 type)
        events_stats__inc(&hists->stats, type);
 }
 
+void hists__inc_nr_samples(struct hists *hists, bool filtered)
+{
+       events_stats__inc(&hists->stats, PERF_RECORD_SAMPLE);
+       if (!filtered)
+               hists->stats.nr_non_filtered_samples++;
+}
+
 static struct hist_entry *hists__add_dummy_entry(struct hists *hists,
                                                 struct hist_entry *pair)
 {
@@ -831,7 +1292,7 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists,
                        p = &(*p)->rb_right;
        }
 
-       he = hist_entry__new(pair);
+       he = hist_entry__new(pair, true);
        if (he) {
                memset(&he->stat, 0, sizeof(he->stat));
                he->hists = hists;
index a8418d19808dc29e50cc683ea6da088062af55a4..d2bf03575d5f08ccb22f6c550ac5f18b0fdf5e08 100644 (file)
@@ -96,12 +96,50 @@ struct hists {
        u16                     col_len[HISTC_NR_COLS];
 };
 
+struct hist_entry_iter;
+
+struct hist_iter_ops {
+       int (*prepare_entry)(struct hist_entry_iter *, struct addr_location *);
+       int (*add_single_entry)(struct hist_entry_iter *, struct addr_location *);
+       int (*next_entry)(struct hist_entry_iter *, struct addr_location *);
+       int (*add_next_entry)(struct hist_entry_iter *, struct addr_location *);
+       int (*finish_entry)(struct hist_entry_iter *, struct addr_location *);
+};
+
+struct hist_entry_iter {
+       int total;
+       int curr;
+
+       bool hide_unresolved;
+
+       struct perf_evsel *evsel;
+       struct perf_sample *sample;
+       struct hist_entry *he;
+       struct symbol *parent;
+       void *priv;
+
+       const struct hist_iter_ops *ops;
+       /* user-defined callback function (optional) */
+       int (*add_entry_cb)(struct hist_entry_iter *iter,
+                           struct addr_location *al, bool single, void *arg);
+};
+
+extern const struct hist_iter_ops hist_iter_normal;
+extern const struct hist_iter_ops hist_iter_branch;
+extern const struct hist_iter_ops hist_iter_mem;
+extern const struct hist_iter_ops hist_iter_cumulative;
+
 struct hist_entry *__hists__add_entry(struct hists *hists,
                                      struct addr_location *al,
                                      struct symbol *parent,
                                      struct branch_info *bi,
                                      struct mem_info *mi, u64 period,
-                                     u64 weight, u64 transaction);
+                                     u64 weight, u64 transaction,
+                                     bool sample_self);
+int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
+                        struct perf_evsel *evsel, struct perf_sample *sample,
+                        int max_stack_depth, void *arg);
+
 int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right);
 int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right);
 int hist_entry__transaction_len(void);
@@ -119,6 +157,7 @@ u64 hists__total_period(struct hists *hists);
 void hists__reset_stats(struct hists *hists);
 void hists__inc_stats(struct hists *hists, struct hist_entry *h);
 void hists__inc_nr_events(struct hists *hists, u32 type);
+void hists__inc_nr_samples(struct hists *hists, bool filtered);
 void events_stats__inc(struct events_stats *stats, u32 type);
 size_t events_stats__fprintf(struct events_stats *stats, FILE *fp);
 
@@ -166,6 +205,7 @@ struct perf_hpp_fmt {
 
        struct list_head list;
        struct list_head sort_list;
+       bool elide;
 };
 
 extern struct list_head perf_hpp__list;
@@ -192,6 +232,7 @@ enum {
        PERF_HPP__OVERHEAD_US,
        PERF_HPP__OVERHEAD_GUEST_SYS,
        PERF_HPP__OVERHEAD_GUEST_US,
+       PERF_HPP__OVERHEAD_ACC,
        PERF_HPP__SAMPLES,
        PERF_HPP__PERIOD,
 
@@ -200,7 +241,11 @@ enum {
 
 void perf_hpp__init(void);
 void perf_hpp__column_register(struct perf_hpp_fmt *format);
+void perf_hpp__column_unregister(struct perf_hpp_fmt *format);
 void perf_hpp__column_enable(unsigned col);
+void perf_hpp__column_disable(unsigned col);
+void perf_hpp__cancel_cumulate(void);
+
 void perf_hpp__register_sort_field(struct perf_hpp_fmt *format);
 void perf_hpp__setup_output_field(void);
 void perf_hpp__reset_output_field(void);
@@ -208,7 +253,12 @@ void perf_hpp__append_sort_keys(void);
 
 bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format);
 bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b);
-bool perf_hpp__should_skip(struct perf_hpp_fmt *format);
+
+static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format)
+{
+       return format->elide;
+}
+
 void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists);
 
 typedef u64 (*hpp_field_fn)(struct hist_entry *he);
@@ -218,6 +268,9 @@ typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...);
 int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
               hpp_field_fn get_field, const char *fmt,
               hpp_snprint_fn print_fn, bool fmt_percent);
+int __hpp__fmt_acc(struct perf_hpp *hpp, struct hist_entry *he,
+                  hpp_field_fn get_field, const char *fmt,
+                  hpp_snprint_fn print_fn, bool fmt_percent);
 
 static inline void advance_hpp(struct perf_hpp *hpp, int inc)
 {
index 901b9bece2ee439ebbc9e9100471cd03883b55d8..45512baaab672706d1c0fd4c858630fed4176364 100644 (file)
@@ -1061,6 +1061,7 @@ static struct hpp_dimension hpp_sort_dimensions[] = {
        DIM(PERF_HPP__OVERHEAD_US, "overhead_us"),
        DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"),
        DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"),
+       DIM(PERF_HPP__OVERHEAD_ACC, "overhead_children"),
        DIM(PERF_HPP__SAMPLES, "sample"),
        DIM(PERF_HPP__PERIOD, "period"),
 };
@@ -1156,6 +1157,7 @@ __sort_dimension__alloc_hpp(struct sort_dimension *sd)
 
        INIT_LIST_HEAD(&hse->hpp.list);
        INIT_LIST_HEAD(&hse->hpp.sort_list);
+       hse->hpp.elide = false;
 
        return hse;
 }
@@ -1363,27 +1365,64 @@ static int __setup_sorting(void)
        return ret;
 }
 
-bool perf_hpp__should_skip(struct perf_hpp_fmt *format)
+void perf_hpp__set_elide(int idx, bool elide)
 {
-       if (perf_hpp__is_sort_entry(format)) {
-               struct hpp_sort_entry *hse;
+       struct perf_hpp_fmt *fmt;
+       struct hpp_sort_entry *hse;
+
+       perf_hpp__for_each_format(fmt) {
+               if (!perf_hpp__is_sort_entry(fmt))
+                       continue;
 
-               hse = container_of(format, struct hpp_sort_entry, hpp);
-               return hse->se->elide;
+               hse = container_of(fmt, struct hpp_sort_entry, hpp);
+               if (hse->se->se_width_idx == idx) {
+                       fmt->elide = elide;
+                       break;
+               }
        }
-       return false;
 }
 
-static void sort_entry__setup_elide(struct sort_entry *se,
-                                   struct strlist *list,
-                                   const char *list_name, FILE *fp)
+static bool __get_elide(struct strlist *list, const char *list_name, FILE *fp)
 {
        if (list && strlist__nr_entries(list) == 1) {
                if (fp != NULL)
                        fprintf(fp, "# %s: %s\n", list_name,
                                strlist__entry(list, 0)->s);
-               se->elide = true;
+               return true;
        }
+       return false;
+}
+
+static bool get_elide(int idx, FILE *output)
+{
+       switch (idx) {
+       case HISTC_SYMBOL:
+               return __get_elide(symbol_conf.sym_list, "symbol", output);
+       case HISTC_DSO:
+               return __get_elide(symbol_conf.dso_list, "dso", output);
+       case HISTC_COMM:
+               return __get_elide(symbol_conf.comm_list, "comm", output);
+       default:
+               break;
+       }
+
+       if (sort__mode != SORT_MODE__BRANCH)
+               return false;
+
+       switch (idx) {
+       case HISTC_SYMBOL_FROM:
+               return __get_elide(symbol_conf.sym_from_list, "sym_from", output);
+       case HISTC_SYMBOL_TO:
+               return __get_elide(symbol_conf.sym_to_list, "sym_to", output);
+       case HISTC_DSO_FROM:
+               return __get_elide(symbol_conf.dso_from_list, "dso_from", output);
+       case HISTC_DSO_TO:
+               return __get_elide(symbol_conf.dso_to_list, "dso_to", output);
+       default:
+               break;
+       }
+
+       return false;
 }
 
 void sort__setup_elide(FILE *output)
@@ -1391,39 +1430,12 @@ void sort__setup_elide(FILE *output)
        struct perf_hpp_fmt *fmt;
        struct hpp_sort_entry *hse;
 
-       sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
-                               "dso", output);
-       sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list,
-                               "comm", output);
-       sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list,
-                               "symbol", output);
-
-       if (sort__mode == SORT_MODE__BRANCH) {
-               sort_entry__setup_elide(&sort_dso_from,
-                                       symbol_conf.dso_from_list,
-                                       "dso_from", output);
-               sort_entry__setup_elide(&sort_dso_to,
-                                       symbol_conf.dso_to_list,
-                                       "dso_to", output);
-               sort_entry__setup_elide(&sort_sym_from,
-                                       symbol_conf.sym_from_list,
-                                       "sym_from", output);
-               sort_entry__setup_elide(&sort_sym_to,
-                                       symbol_conf.sym_to_list,
-                                       "sym_to", output);
-       } else if (sort__mode == SORT_MODE__MEMORY) {
-               sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
-                                       "symbol_daddr", output);
-               sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
-                                       "dso_daddr", output);
-               sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
-                                       "mem", output);
-               sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
-                                       "local_weight", output);
-               sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
-                                       "tlb", output);
-               sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
-                                       "snoop", output);
+       perf_hpp__for_each_format(fmt) {
+               if (!perf_hpp__is_sort_entry(fmt))
+                       continue;
+
+               hse = container_of(fmt, struct hpp_sort_entry, hpp);
+               fmt->elide = get_elide(hse->se->se_width_idx, output);
        }
 
        /*
@@ -1434,8 +1446,7 @@ void sort__setup_elide(FILE *output)
                if (!perf_hpp__is_sort_entry(fmt))
                        continue;
 
-               hse = container_of(fmt, struct hpp_sort_entry, hpp);
-               if (!hse->se->elide)
+               if (!fmt->elide)
                        return;
        }
 
@@ -1443,8 +1454,7 @@ void sort__setup_elide(FILE *output)
                if (!perf_hpp__is_sort_entry(fmt))
                        continue;
 
-               hse = container_of(fmt, struct hpp_sort_entry, hpp);
-               hse->se->elide = false;
+               fmt->elide = false;
        }
 }
 
@@ -1581,6 +1591,9 @@ void reset_output_field(void)
        sort__has_sym = 0;
        sort__has_dso = 0;
 
+       field_order = NULL;
+       sort_order = NULL;
+
        reset_dimensions();
        perf_hpp__reset_output_field();
 }
index 5f38d925e92f4f20917c11c44a9bb6274b7762fd..5bf0098d6b068921190c4b98256bf355cb092333 100644 (file)
@@ -20,7 +20,7 @@
 
 #include "parse-options.h"
 #include "parse-events.h"
-
+#include "hist.h"
 #include "thread.h"
 
 extern regex_t parent_regex;
@@ -82,6 +82,7 @@ struct hist_entry {
                struct list_head head;
        } pairs;
        struct he_stat          stat;
+       struct he_stat          *stat_acc;
        struct map_symbol       ms;
        struct thread           *thread;
        struct comm             *comm;
@@ -130,6 +131,21 @@ static inline void hist_entry__add_pair(struct hist_entry *pair,
        list_add_tail(&pair->pairs.node, &he->pairs.head);
 }
 
+static inline float hist_entry__get_percent_limit(struct hist_entry *he)
+{
+       u64 period = he->stat.period;
+       u64 total_period = hists__total_period(he->hists);
+
+       if (unlikely(total_period == 0))
+               return 0;
+
+       if (symbol_conf.cumulate_callchain)
+               period = he->stat_acc->period;
+
+       return period * 100.0 / total_period;
+}
+
+
 enum sort_mode {
        SORT_MODE__NORMAL,
        SORT_MODE__BRANCH,
@@ -186,7 +202,6 @@ struct sort_entry {
        int     (*se_snprintf)(struct hist_entry *he, char *bf, size_t size,
                               unsigned int width);
        u8      se_width_idx;
-       bool    elide;
 };
 
 extern struct sort_entry sort_thread;
@@ -197,6 +212,7 @@ int setup_output_field(void);
 void reset_output_field(void);
 extern int sort_dimension__add(const char *);
 void sort__setup_elide(FILE *fp);
+void perf_hpp__set_elide(int idx, bool elide);
 
 int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset);
 
index 95e249779931216f5cbeb5b09c26ea0655a0fcf7..7b9096f29cdbf7f8c2afabb041e2563b0e70c0da 100644 (file)
@@ -29,11 +29,12 @@ int vmlinux_path__nr_entries;
 char **vmlinux_path;
 
 struct symbol_conf symbol_conf = {
-       .use_modules      = true,
-       .try_vmlinux_path = true,
-       .annotate_src     = true,
-       .demangle         = true,
-       .symfs            = "",
+       .use_modules            = true,
+       .try_vmlinux_path       = true,
+       .annotate_src           = true,
+       .demangle               = true,
+       .cumulate_callchain     = true,
+       .symfs                  = "",
 };
 
 static enum dso_binary_type binary_type_symtab[] = {
index 33ede53fa6b9944940bec3047ec1789c375f11a0..615c752dd7673600b33f8e051f35ba85ce1cd866 100644 (file)
@@ -109,6 +109,7 @@ struct symbol_conf {
                        show_nr_samples,
                        show_total_period,
                        use_callchain,
+                       cumulate_callchain,
                        exclude_other,
                        show_cpu_utilization,
                        initialized,
index 750512ba2c8846d2d1275abb796b425fdd4a987d..c7493b8f9b0ee5b951d73d64e7502e809be2d5ed 100644 (file)
@@ -14,6 +14,12 @@ all: $(NET_PROGS)
 run_tests: all
        @/bin/sh ./run_netsocktests || echo "sockettests: [FAIL]"
        @/bin/sh ./run_afpackettests || echo "afpackettests: [FAIL]"
-
+       @if /sbin/modprobe test_bpf ; then \
+               /sbin/rmmod test_bpf; \
+               echo "test_bpf: ok"; \
+       else \
+               echo "test_bpf: [FAIL]"; \
+               exit 1; \
+       fi
 clean:
        $(RM) $(NET_PROGS)
index b3dbe9ef1a40ac0580905ed2812d22026850f449..54833a791a441d11b30c6fbb8bbcb8afafe5aa48 100644 (file)
@@ -13,7 +13,7 @@ CFLAGS := -Wall -O2 -flto -Wall -Werror -DGIT_VERSION='"$(GIT_VERSION)"' -I$(CUR
 
 export CC CFLAGS
 
-TARGETS = pmu copyloops mm
+TARGETS = pmu copyloops mm tm
 
 endif
 
index e80c42a584fe52d6cd73d3c39ee43174057bccb8..8ebc58a09311da9a591a7c8efd1fb2025049c2fe 100644 (file)
@@ -30,12 +30,15 @@ int run_test(int (test_function)(void), char *name)
 
        pid = fork();
        if (pid == 0) {
+               setpgid(0, 0);
                exit(test_function());
        } else if (pid == -1) {
                perror("fork");
                return 1;
        }
 
+       setpgid(pid, pid);
+
        /* Wake us up in timeout seconds */
        alarm(TIMEOUT);
        terminated = false;
@@ -50,17 +53,20 @@ int run_test(int (test_function)(void), char *name)
 
                if (terminated) {
                        printf("!! force killing %s\n", name);
-                       kill(pid, SIGKILL);
+                       kill(-pid, SIGKILL);
                        return 1;
                } else {
                        printf("!! killing %s\n", name);
-                       kill(pid, SIGTERM);
+                       kill(-pid, SIGTERM);
                        terminated = true;
                        alarm(KILL_TIMEOUT);
                        goto wait;
                }
        }
 
+       /* Kill anything else in the process group that is still running */
+       kill(-pid, SIGTERM);
+
        if (WIFEXITED(status))
                status = WEXITSTATUS(status);
        else {
@@ -99,7 +105,10 @@ int test_harness(int (test_function)(void), char *name)
 
        rc = run_test(test_function, name);
 
-       test_finish(name, rc);
+       if (rc == MAGIC_SKIP_RETURN_VALUE)
+               test_skip(name);
+       else
+               test_finish(name, rc);
 
        return rc;
 }
index 7216f009165537053a198bf905de14e80bab3b2e..b9ff0db42c79a1e35ce9116bb73103b154419411 100644 (file)
@@ -4,7 +4,7 @@ noarg:
 PROGS := count_instructions
 EXTRA_SOURCES := ../harness.c event.c
 
-all: $(PROGS)
+all: $(PROGS) sub_all
 
 $(PROGS): $(EXTRA_SOURCES)
 
@@ -12,12 +12,30 @@ $(PROGS): $(EXTRA_SOURCES)
 count_instructions: loop.S count_instructions.c $(EXTRA_SOURCES)
        $(CC) $(CFLAGS) -m64 -o $@ $^
 
-run_tests: all
+run_tests: all sub_run_tests
        @-for PROG in $(PROGS); do \
                ./$$PROG; \
        done;
 
-clean:
+clean: sub_clean
        rm -f $(PROGS) loop.o
 
-.PHONY: all run_tests clean
+
+SUB_TARGETS = ebb
+
+sub_all:
+       @for TARGET in $(SUB_TARGETS); do \
+               $(MAKE) -C $$TARGET all; \
+       done;
+
+sub_run_tests: all
+       @for TARGET in $(SUB_TARGETS); do \
+               $(MAKE) -C $$TARGET run_tests; \
+       done;
+
+sub_clean:
+       @for TARGET in $(SUB_TARGETS); do \
+               $(MAKE) -C $$TARGET clean; \
+       done;
+
+.PHONY: all run_tests clean sub_all sub_run_tests sub_clean
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/Makefile b/tools/testing/selftests/powerpc/pmu/ebb/Makefile
new file mode 100644 (file)
index 0000000..edbba2a
--- /dev/null
@@ -0,0 +1,32 @@
+noarg:
+       $(MAKE) -C ../../
+
+# The EBB handler is 64-bit code and everything links against it
+CFLAGS += -m64
+
+PROGS := reg_access_test event_attributes_test cycles_test     \
+        cycles_with_freeze_test pmc56_overflow_test            \
+        ebb_vs_cpu_event_test cpu_event_vs_ebb_test            \
+        cpu_event_pinned_vs_ebb_test task_event_vs_ebb_test    \
+        task_event_pinned_vs_ebb_test multi_ebb_procs_test     \
+        multi_counter_test pmae_handling_test                  \
+        close_clears_pmcc_test instruction_count_test          \
+        fork_cleanup_test ebb_on_child_test                    \
+        ebb_on_willing_child_test back_to_back_ebbs_test       \
+        lost_exception_test no_handler_test
+
+all: $(PROGS)
+
+$(PROGS): ../../harness.c ../event.c ../lib.c ebb.c ebb_handler.S trace.c
+
+instruction_count_test: ../loop.S
+
+lost_exception_test: ../lib.c
+
+run_tests: all
+       @-for PROG in $(PROGS); do \
+               ./$$PROG; \
+       done;
+
+clean:
+       rm -f $(PROGS)
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c b/tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c
new file mode 100644 (file)
index 0000000..66ea765
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ebb.h"
+
+
+#define NUMBER_OF_EBBS 50
+
+/*
+ * Test that if we overflow the counter while in the EBB handler, we take
+ * another EBB on exiting from the handler.
+ *
+ * We do this by counting with a stupidly low sample period, causing us to
+ * overflow the PMU while we're still in the EBB handler, leading to another
+ * EBB.
+ *
+ * We get out of what would otherwise be an infinite loop by leaving the
+ * counter frozen once we've taken enough EBBs.
+ */
+
+static void ebb_callee(void)
+{
+       uint64_t siar, val;
+
+       val = mfspr(SPRN_BESCR);
+       if (!(val & BESCR_PMEO)) {
+               ebb_state.stats.spurious++;
+               goto out;
+       }
+
+       ebb_state.stats.ebb_count++;
+       trace_log_counter(ebb_state.trace, ebb_state.stats.ebb_count);
+
+       /* Resets the PMC */
+       count_pmc(1, sample_period);
+
+out:
+       if (ebb_state.stats.ebb_count == NUMBER_OF_EBBS)
+               /* Reset but leave counters frozen */
+               reset_ebb_with_clear_mask(MMCR0_PMAO);
+       else
+               /* Unfreezes */
+               reset_ebb();
+
+       /* Do some stuff to chew some cycles and pop the counter */
+       siar = mfspr(SPRN_SIAR);
+       trace_log_reg(ebb_state.trace, SPRN_SIAR, siar);
+
+       val = mfspr(SPRN_PMC1);
+       trace_log_reg(ebb_state.trace, SPRN_PMC1, val);
+
+       val = mfspr(SPRN_MMCR0);
+       trace_log_reg(ebb_state.trace, SPRN_MMCR0, val);
+}
+
+int back_to_back_ebbs(void)
+{
+       struct event event;
+
+       event_init_named(&event, 0x1001e, "cycles");
+       event_leader_ebb_init(&event);
+
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open(&event));
+
+       setup_ebb_handler(ebb_callee);
+
+       FAIL_IF(ebb_event_enable(&event));
+
+       sample_period = 5;
+
+       ebb_freeze_pmcs();
+       mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+       ebb_global_enable();
+       ebb_unfreeze_pmcs();
+
+       while (ebb_state.stats.ebb_count < NUMBER_OF_EBBS)
+               FAIL_IF(core_busy_loop());
+
+       ebb_global_disable();
+       ebb_freeze_pmcs();
+
+       count_pmc(1, sample_period);
+
+       dump_ebb_state();
+
+       event_close(&event);
+
+       FAIL_IF(ebb_state.stats.ebb_count != NUMBER_OF_EBBS);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(back_to_back_ebbs, "back_to_back_ebbs");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/close_clears_pmcc_test.c b/tools/testing/selftests/powerpc/pmu/ebb/close_clears_pmcc_test.c
new file mode 100644 (file)
index 0000000..0f0423d
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <signal.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test that closing the EBB event clears MMCR0_PMCC, preventing further access
+ * by userspace to the PMU hardware.
+ */
+
+int close_clears_pmcc(void)
+{
+       struct event event;
+
+       event_init_named(&event, 0x1001e, "cycles");
+       event_leader_ebb_init(&event);
+
+       FAIL_IF(event_open(&event));
+
+       ebb_enable_pmc_counting(1);
+       setup_ebb_handler(standard_ebb_callee);
+       ebb_global_enable();
+       FAIL_IF(ebb_event_enable(&event));
+
+       mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+       while (ebb_state.stats.ebb_count < 1)
+               FAIL_IF(core_busy_loop());
+
+       ebb_global_disable();
+       event_close(&event);
+
+       FAIL_IF(ebb_state.stats.ebb_count == 0);
+
+       /* The real test is here, do we take a SIGILL when writing PMU regs now
+        * that we have closed the event. We expect that we will. */
+
+       FAIL_IF(catch_sigill(write_pmc1));
+
+       /* We should still be able to read EBB regs though */
+       mfspr(SPRN_EBBHR);
+       mfspr(SPRN_EBBRR);
+       mfspr(SPRN_BESCR);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(close_clears_pmcc, "close_clears_pmcc");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c
new file mode 100644 (file)
index 0000000..d3ed64d
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "ebb.h"
+
+
+/*
+ * Tests a pinned cpu event vs an EBB - in that order. The pinned cpu event
+ * should remain and the EBB event should fail to enable.
+ */
+
+static int setup_cpu_event(struct event *event, int cpu)
+{
+       event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
+
+       event->attr.pinned = 1;
+
+       event->attr.exclude_kernel = 1;
+       event->attr.exclude_hv = 1;
+       event->attr.exclude_idle = 1;
+
+       SKIP_IF(require_paranoia_below(1));
+       FAIL_IF(event_open_with_cpu(event, cpu));
+       FAIL_IF(event_enable(event));
+
+       return 0;
+}
+
+int cpu_event_pinned_vs_ebb(void)
+{
+       union pipe read_pipe, write_pipe;
+       struct event event;
+       int cpu, rc;
+       pid_t pid;
+
+       cpu = pick_online_cpu();
+       FAIL_IF(cpu < 0);
+       FAIL_IF(bind_to_cpu(cpu));
+
+       FAIL_IF(pipe(read_pipe.fds) == -1);
+       FAIL_IF(pipe(write_pipe.fds) == -1);
+
+       pid = fork();
+       if (pid == 0) {
+               /* NB order of pipes looks reversed */
+               exit(ebb_child(write_pipe, read_pipe));
+       }
+
+       /* We setup the cpu event first */
+       rc = setup_cpu_event(&event, cpu);
+       if (rc) {
+               kill_child_and_wait(pid);
+               return rc;
+       }
+
+       /* Signal the child to install its EBB event and wait */
+       if (sync_with_child(read_pipe, write_pipe))
+               /* If it fails, wait for it to exit */
+               goto wait;
+
+       /* Signal the child to run */
+       FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+wait:
+       /* We expect it to fail to read the event */
+       FAIL_IF(wait_for_child(pid) != 2);
+
+       FAIL_IF(event_disable(&event));
+       FAIL_IF(event_read(&event));
+
+       event_report(&event);
+
+       /* The cpu event should have run */
+       FAIL_IF(event.result.value == 0);
+       FAIL_IF(event.result.enabled != event.result.running);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(cpu_event_pinned_vs_ebb, "cpu_event_pinned_vs_ebb");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c
new file mode 100644 (file)
index 0000000..8b972c2
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "ebb.h"
+
+
+/*
+ * Tests a cpu event vs an EBB - in that order. The EBB should force the cpu
+ * event off the PMU.
+ */
+
+static int setup_cpu_event(struct event *event, int cpu)
+{
+       event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
+
+       event->attr.exclude_kernel = 1;
+       event->attr.exclude_hv = 1;
+       event->attr.exclude_idle = 1;
+
+       SKIP_IF(require_paranoia_below(1));
+       FAIL_IF(event_open_with_cpu(event, cpu));
+       FAIL_IF(event_enable(event));
+
+       return 0;
+}
+
+int cpu_event_vs_ebb(void)
+{
+       union pipe read_pipe, write_pipe;
+       struct event event;
+       int cpu, rc;
+       pid_t pid;
+
+       cpu = pick_online_cpu();
+       FAIL_IF(cpu < 0);
+       FAIL_IF(bind_to_cpu(cpu));
+
+       FAIL_IF(pipe(read_pipe.fds) == -1);
+       FAIL_IF(pipe(write_pipe.fds) == -1);
+
+       pid = fork();
+       if (pid == 0) {
+               /* NB order of pipes looks reversed */
+               exit(ebb_child(write_pipe, read_pipe));
+       }
+
+       /* We setup the cpu event first */
+       rc = setup_cpu_event(&event, cpu);
+       if (rc) {
+               kill_child_and_wait(pid);
+               return rc;
+       }
+
+       /* Signal the child to install its EBB event and wait */
+       if (sync_with_child(read_pipe, write_pipe))
+               /* If it fails, wait for it to exit */
+               goto wait;
+
+       /* Signal the child to run */
+       FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+wait:
+       /* We expect the child to succeed */
+       FAIL_IF(wait_for_child(pid));
+
+       FAIL_IF(event_disable(&event));
+       FAIL_IF(event_read(&event));
+
+       event_report(&event);
+
+       /* The cpu event may have run */
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(cpu_event_vs_ebb, "cpu_event_vs_ebb");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c
new file mode 100644 (file)
index 0000000..8590fc1
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ebb.h"
+
+
+/*
+ * Basic test that counts user cycles and takes EBBs.
+ */
+int cycles(void)
+{
+       struct event event;
+
+       event_init_named(&event, 0x1001e, "cycles");
+       event_leader_ebb_init(&event);
+
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open(&event));
+
+       ebb_enable_pmc_counting(1);
+       setup_ebb_handler(standard_ebb_callee);
+       ebb_global_enable();
+       FAIL_IF(ebb_event_enable(&event));
+
+       mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+       while (ebb_state.stats.ebb_count < 10) {
+               FAIL_IF(core_busy_loop());
+               FAIL_IF(ebb_check_mmcr0());
+       }
+
+       ebb_global_disable();
+       ebb_freeze_pmcs();
+
+       count_pmc(1, sample_period);
+
+       dump_ebb_state();
+
+       event_close(&event);
+
+       FAIL_IF(ebb_state.stats.ebb_count == 0);
+       FAIL_IF(!ebb_check_count(1, sample_period, 100));
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(cycles, "cycles");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c
new file mode 100644 (file)
index 0000000..754b3f2
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test of counting cycles while using MMCR0_FC (freeze counters) to only count
+ * parts of the code. This is complicated by the fact that FC is set by the
+ * hardware when the event overflows. We may take the EBB after we have set FC,
+ * so we have to be careful about whether we clear FC at the end of the EBB
+ * handler or not.
+ */
+
+static bool counters_frozen = false;
+static int ebbs_while_frozen = 0;
+
+static void ebb_callee(void)
+{
+       uint64_t mask, val;
+
+       mask = MMCR0_PMAO | MMCR0_FC;
+
+       val = mfspr(SPRN_BESCR);
+       if (!(val & BESCR_PMEO)) {
+               ebb_state.stats.spurious++;
+               goto out;
+       }
+
+       ebb_state.stats.ebb_count++;
+       trace_log_counter(ebb_state.trace, ebb_state.stats.ebb_count);
+
+       val = mfspr(SPRN_MMCR0);
+       trace_log_reg(ebb_state.trace, SPRN_MMCR0, val);
+
+       if (counters_frozen) {
+               trace_log_string(ebb_state.trace, "frozen");
+               ebbs_while_frozen++;
+               mask &= ~MMCR0_FC;
+       }
+
+       count_pmc(1, sample_period);
+out:
+       reset_ebb_with_clear_mask(mask);
+}
+
+int cycles_with_freeze(void)
+{
+       struct event event;
+       uint64_t val;
+       bool fc_cleared;
+
+       event_init_named(&event, 0x1001e, "cycles");
+       event_leader_ebb_init(&event);
+
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open(&event));
+
+       setup_ebb_handler(ebb_callee);
+       ebb_global_enable();
+       FAIL_IF(ebb_event_enable(&event));
+
+       mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+       fc_cleared = false;
+
+       /* Make sure we loop until we take at least one EBB */
+       while ((ebb_state.stats.ebb_count < 20 && !fc_cleared) ||
+               ebb_state.stats.ebb_count < 1)
+       {
+               counters_frozen = false;
+               mb();
+               mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
+
+               FAIL_IF(core_busy_loop());
+
+               counters_frozen = true;
+               mb();
+               mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) |  MMCR0_FC);
+
+               val = mfspr(SPRN_MMCR0);
+               if (! (val & MMCR0_FC)) {
+                       printf("Outside of loop, FC NOT set MMCR0 0x%lx\n", val);
+                       fc_cleared = true;
+               }
+       }
+
+       ebb_global_disable();
+       ebb_freeze_pmcs();
+
+       count_pmc(1, sample_period);
+
+       dump_ebb_state();
+
+       printf("EBBs while frozen %d\n", ebbs_while_frozen);
+
+       event_close(&event);
+
+       FAIL_IF(ebb_state.stats.ebb_count == 0);
+       FAIL_IF(fc_cleared);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(cycles_with_freeze, "cycles_with_freeze");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb.c
new file mode 100644 (file)
index 0000000..1b46be9
--- /dev/null
@@ -0,0 +1,727 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#define _GNU_SOURCE    /* For CPU_ZERO etc. */
+
+#include <sched.h>
+#include <sys/wait.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include "trace.h"
+#include "reg.h"
+#include "ebb.h"
+
+
+void (*ebb_user_func)(void);
+
+void ebb_hook(void)
+{
+       if (ebb_user_func)
+               ebb_user_func();
+}
+
+struct ebb_state ebb_state;
+
+u64 sample_period = 0x40000000ull;
+
+void reset_ebb_with_clear_mask(unsigned long mmcr0_clear_mask)
+{
+       u64 val;
+
+       /* 2) clear MMCR0[PMAO] - docs say BESCR[PMEO] should do this */
+       /* 3) set MMCR0[PMAE]   - docs say BESCR[PME] should do this */
+       val = mfspr(SPRN_MMCR0);
+       mtspr(SPRN_MMCR0, (val & ~mmcr0_clear_mask) | MMCR0_PMAE);
+
+       /* 4) clear BESCR[PMEO] */
+       mtspr(SPRN_BESCRR, BESCR_PMEO);
+
+       /* 5) set BESCR[PME] */
+       mtspr(SPRN_BESCRS, BESCR_PME);
+
+       /* 6) rfebb 1 - done in our caller */
+}
+
+void reset_ebb(void)
+{
+       reset_ebb_with_clear_mask(MMCR0_PMAO | MMCR0_FC);
+}
+
+/* Called outside of the EBB handler to check MMCR0 is sane */
+int ebb_check_mmcr0(void)
+{
+       u64 val;
+
+       val = mfspr(SPRN_MMCR0);
+       if ((val & (MMCR0_FC | MMCR0_PMAO)) == MMCR0_FC) {
+               /* It's OK if we see FC & PMAO, but not FC by itself */
+               printf("Outside of loop, only FC set 0x%llx\n", val);
+               return 1;
+       }
+
+       return 0;
+}
+
+bool ebb_check_count(int pmc, u64 sample_period, int fudge)
+{
+       u64 count, upper, lower;
+
+       count = ebb_state.stats.pmc_count[PMC_INDEX(pmc)];
+
+       lower = ebb_state.stats.ebb_count * (sample_period - fudge);
+
+       if (count < lower) {
+               printf("PMC%d count (0x%llx) below lower limit 0x%llx (-0x%llx)\n",
+                       pmc, count, lower, lower - count);
+               return false;
+       }
+
+       upper = ebb_state.stats.ebb_count * (sample_period + fudge);
+
+       if (count > upper) {
+               printf("PMC%d count (0x%llx) above upper limit 0x%llx (+0x%llx)\n",
+                       pmc, count, upper, count - upper);
+               return false;
+       }
+
+       printf("PMC%d count (0x%llx) is between 0x%llx and 0x%llx delta +0x%llx/-0x%llx\n",
+               pmc, count, lower, upper, count - lower, upper - count);
+
+       return true;
+}
+
+void standard_ebb_callee(void)
+{
+       int found, i;
+       u64 val;
+
+       val = mfspr(SPRN_BESCR);
+       if (!(val & BESCR_PMEO)) {
+               ebb_state.stats.spurious++;
+               goto out;
+       }
+
+       ebb_state.stats.ebb_count++;
+       trace_log_counter(ebb_state.trace, ebb_state.stats.ebb_count);
+
+       val = mfspr(SPRN_MMCR0);
+       trace_log_reg(ebb_state.trace, SPRN_MMCR0, val);
+
+       found = 0;
+       for (i = 1; i <= 6; i++) {
+               if (ebb_state.pmc_enable[PMC_INDEX(i)])
+                       found += count_pmc(i, sample_period);
+       }
+
+       if (!found)
+               ebb_state.stats.no_overflow++;
+
+out:
+       reset_ebb();
+}
+
+extern void ebb_handler(void);
+
+void setup_ebb_handler(void (*callee)(void))
+{
+       u64 entry;
+
+#if defined(_CALL_ELF) && _CALL_ELF == 2
+       entry = (u64)ebb_handler;
+#else
+       struct opd
+       {
+           u64 entry;
+           u64 toc;
+       } *opd;
+
+       opd = (struct opd *)ebb_handler;
+       entry = opd->entry;
+#endif
+       printf("EBB Handler is at %#llx\n", entry);
+
+       ebb_user_func = callee;
+
+       /* Ensure ebb_user_func is set before we set the handler */
+       mb();
+       mtspr(SPRN_EBBHR, entry);
+
+       /* Make sure the handler is set before we return */
+       mb();
+}
+
+void clear_ebb_stats(void)
+{
+       memset(&ebb_state.stats, 0, sizeof(ebb_state.stats));
+}
+
+void dump_summary_ebb_state(void)
+{
+       printf("ebb_state:\n"                   \
+              "  ebb_count    = %d\n"          \
+              "  spurious     = %d\n"          \
+              "  negative     = %d\n"          \
+              "  no_overflow  = %d\n"          \
+              "  pmc[1] count = 0x%llx\n"      \
+              "  pmc[2] count = 0x%llx\n"      \
+              "  pmc[3] count = 0x%llx\n"      \
+              "  pmc[4] count = 0x%llx\n"      \
+              "  pmc[5] count = 0x%llx\n"      \
+              "  pmc[6] count = 0x%llx\n",
+               ebb_state.stats.ebb_count, ebb_state.stats.spurious,
+               ebb_state.stats.negative, ebb_state.stats.no_overflow,
+               ebb_state.stats.pmc_count[0], ebb_state.stats.pmc_count[1],
+               ebb_state.stats.pmc_count[2], ebb_state.stats.pmc_count[3],
+               ebb_state.stats.pmc_count[4], ebb_state.stats.pmc_count[5]);
+}
+
+static char *decode_mmcr0(u32 value)
+{
+       static char buf[16];
+
+       buf[0] = '\0';
+
+       if (value & (1 << 31))
+               strcat(buf, "FC ");
+       if (value & (1 << 26))
+               strcat(buf, "PMAE ");
+       if (value & (1 << 7))
+               strcat(buf, "PMAO ");
+
+       return buf;
+}
+
+static char *decode_bescr(u64 value)
+{
+       static char buf[16];
+
+       buf[0] = '\0';
+
+       if (value & (1ull << 63))
+               strcat(buf, "GE ");
+       if (value & (1ull << 32))
+               strcat(buf, "PMAE ");
+       if (value & 1)
+               strcat(buf, "PMAO ");
+
+       return buf;
+}
+
+void dump_ebb_hw_state(void)
+{
+       u64 bescr;
+       u32 mmcr0;
+
+       mmcr0 = mfspr(SPRN_MMCR0);
+       bescr = mfspr(SPRN_BESCR);
+
+       printf("HW state:\n"            \
+              "MMCR0 0x%016x %s\n"     \
+              "EBBHR 0x%016lx\n"       \
+              "BESCR 0x%016llx %s\n"   \
+              "PMC1  0x%016lx\n"       \
+              "PMC2  0x%016lx\n"       \
+              "PMC3  0x%016lx\n"       \
+              "PMC4  0x%016lx\n"       \
+              "PMC5  0x%016lx\n"       \
+              "PMC6  0x%016lx\n"       \
+              "SIAR  0x%016lx\n",
+              mmcr0, decode_mmcr0(mmcr0), mfspr(SPRN_EBBHR), bescr,
+              decode_bescr(bescr), mfspr(SPRN_PMC1), mfspr(SPRN_PMC2),
+              mfspr(SPRN_PMC3), mfspr(SPRN_PMC4), mfspr(SPRN_PMC5),
+              mfspr(SPRN_PMC6), mfspr(SPRN_SIAR));
+}
+
+void dump_ebb_state(void)
+{
+       dump_summary_ebb_state();
+
+       dump_ebb_hw_state();
+
+       trace_buffer_print(ebb_state.trace);
+}
+
+int count_pmc(int pmc, uint32_t sample_period)
+{
+       uint32_t start_value;
+       u64 val;
+
+       /* 0) Read PMC */
+       start_value = pmc_sample_period(sample_period);
+
+       val = read_pmc(pmc);
+       if (val < start_value)
+               ebb_state.stats.negative++;
+       else
+               ebb_state.stats.pmc_count[PMC_INDEX(pmc)] += val - start_value;
+
+       trace_log_reg(ebb_state.trace, SPRN_PMC1 + pmc - 1, val);
+
+       /* 1) Reset PMC */
+       write_pmc(pmc, start_value);
+
+       /* Report if we overflowed */
+       return val >= COUNTER_OVERFLOW;
+}
+
+int ebb_event_enable(struct event *e)
+{
+       int rc;
+
+       /* Ensure any SPR writes are ordered vs us */
+       mb();
+
+       rc = ioctl(e->fd, PERF_EVENT_IOC_ENABLE);
+       if (rc)
+               return rc;
+
+       rc = event_read(e);
+
+       /* Ditto */
+       mb();
+
+       return rc;
+}
+
+void ebb_freeze_pmcs(void)
+{
+       mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) | MMCR0_FC);
+       mb();
+}
+
+void ebb_unfreeze_pmcs(void)
+{
+       /* Unfreeze counters */
+       mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
+       mb();
+}
+
+void ebb_global_enable(void)
+{
+       /* Enable EBBs globally and PMU EBBs */
+       mtspr(SPRN_BESCR, 0x8000000100000000ull);
+       mb();
+}
+
+void ebb_global_disable(void)
+{
+       /* Disable EBBs & freeze counters, events are still scheduled */
+       mtspr(SPRN_BESCRR, BESCR_PME);
+       mb();
+}
+
+void event_ebb_init(struct event *e)
+{
+       e->attr.config |= (1ull << 63);
+}
+
+void event_bhrb_init(struct event *e, unsigned ifm)
+{
+       e->attr.config |= (1ull << 62) | ((u64)ifm << 60);
+}
+
+void event_leader_ebb_init(struct event *e)
+{
+       event_ebb_init(e);
+
+       e->attr.exclusive = 1;
+       e->attr.pinned = 1;
+}
+
+int core_busy_loop(void)
+{
+       int rc;
+
+       asm volatile (
+               "li  3,  0x3030\n"
+               "std 3,  -96(1)\n"
+               "li  4,  0x4040\n"
+               "std 4,  -104(1)\n"
+               "li  5,  0x5050\n"
+               "std 5,  -112(1)\n"
+               "li  6,  0x6060\n"
+               "std 6,  -120(1)\n"
+               "li  7,  0x7070\n"
+               "std 7,  -128(1)\n"
+               "li  8,  0x0808\n"
+               "std 8,  -136(1)\n"
+               "li  9,  0x0909\n"
+               "std 9,  -144(1)\n"
+               "li  10, 0x1010\n"
+               "std 10, -152(1)\n"
+               "li  11, 0x1111\n"
+               "std 11, -160(1)\n"
+               "li  14, 0x1414\n"
+               "std 14, -168(1)\n"
+               "li  15, 0x1515\n"
+               "std 15, -176(1)\n"
+               "li  16, 0x1616\n"
+               "std 16, -184(1)\n"
+               "li  17, 0x1717\n"
+               "std 17, -192(1)\n"
+               "li  18, 0x1818\n"
+               "std 18, -200(1)\n"
+               "li  19, 0x1919\n"
+               "std 19, -208(1)\n"
+               "li  20, 0x2020\n"
+               "std 20, -216(1)\n"
+               "li  21, 0x2121\n"
+               "std 21, -224(1)\n"
+               "li  22, 0x2222\n"
+               "std 22, -232(1)\n"
+               "li  23, 0x2323\n"
+               "std 23, -240(1)\n"
+               "li  24, 0x2424\n"
+               "std 24, -248(1)\n"
+               "li  25, 0x2525\n"
+               "std 25, -256(1)\n"
+               "li  26, 0x2626\n"
+               "std 26, -264(1)\n"
+               "li  27, 0x2727\n"
+               "std 27, -272(1)\n"
+               "li  28, 0x2828\n"
+               "std 28, -280(1)\n"
+               "li  29, 0x2929\n"
+               "std 29, -288(1)\n"
+               "li  30, 0x3030\n"
+               "li  31, 0x3131\n"
+
+               "li    3,  0\n"
+               "0: "
+               "addi  3, 3, 1\n"
+               "cmpwi 3, 100\n"
+               "blt   0b\n"
+
+               /* Return 1 (fail) unless we get through all the checks */
+               "li     0, 1\n"
+
+               /* Check none of our registers have been corrupted */
+               "cmpwi  4,  0x4040\n"
+               "bne    1f\n"
+               "cmpwi  5,  0x5050\n"
+               "bne    1f\n"
+               "cmpwi  6,  0x6060\n"
+               "bne    1f\n"
+               "cmpwi  7,  0x7070\n"
+               "bne    1f\n"
+               "cmpwi  8,  0x0808\n"
+               "bne    1f\n"
+               "cmpwi  9,  0x0909\n"
+               "bne    1f\n"
+               "cmpwi  10, 0x1010\n"
+               "bne    1f\n"
+               "cmpwi  11, 0x1111\n"
+               "bne    1f\n"
+               "cmpwi  14, 0x1414\n"
+               "bne    1f\n"
+               "cmpwi  15, 0x1515\n"
+               "bne    1f\n"
+               "cmpwi  16, 0x1616\n"
+               "bne    1f\n"
+               "cmpwi  17, 0x1717\n"
+               "bne    1f\n"
+               "cmpwi  18, 0x1818\n"
+               "bne    1f\n"
+               "cmpwi  19, 0x1919\n"
+               "bne    1f\n"
+               "cmpwi  20, 0x2020\n"
+               "bne    1f\n"
+               "cmpwi  21, 0x2121\n"
+               "bne    1f\n"
+               "cmpwi  22, 0x2222\n"
+               "bne    1f\n"
+               "cmpwi  23, 0x2323\n"
+               "bne    1f\n"
+               "cmpwi  24, 0x2424\n"
+               "bne    1f\n"
+               "cmpwi  25, 0x2525\n"
+               "bne    1f\n"
+               "cmpwi  26, 0x2626\n"
+               "bne    1f\n"
+               "cmpwi  27, 0x2727\n"
+               "bne    1f\n"
+               "cmpwi  28, 0x2828\n"
+               "bne    1f\n"
+               "cmpwi  29, 0x2929\n"
+               "bne    1f\n"
+               "cmpwi  30, 0x3030\n"
+               "bne    1f\n"
+               "cmpwi  31, 0x3131\n"
+               "bne    1f\n"
+
+               /* Load junk into all our registers before we reload them from the stack. */
+               "li  3,  0xde\n"
+               "li  4,  0xad\n"
+               "li  5,  0xbe\n"
+               "li  6,  0xef\n"
+               "li  7,  0xde\n"
+               "li  8,  0xad\n"
+               "li  9,  0xbe\n"
+               "li  10, 0xef\n"
+               "li  11, 0xde\n"
+               "li  14, 0xad\n"
+               "li  15, 0xbe\n"
+               "li  16, 0xef\n"
+               "li  17, 0xde\n"
+               "li  18, 0xad\n"
+               "li  19, 0xbe\n"
+               "li  20, 0xef\n"
+               "li  21, 0xde\n"
+               "li  22, 0xad\n"
+               "li  23, 0xbe\n"
+               "li  24, 0xef\n"
+               "li  25, 0xde\n"
+               "li  26, 0xad\n"
+               "li  27, 0xbe\n"
+               "li  28, 0xef\n"
+               "li  29, 0xdd\n"
+
+               "ld     3,  -96(1)\n"
+               "cmpwi  3,  0x3030\n"
+               "bne    1f\n"
+               "ld     4,  -104(1)\n"
+               "cmpwi  4,  0x4040\n"
+               "bne    1f\n"
+               "ld     5,  -112(1)\n"
+               "cmpwi  5,  0x5050\n"
+               "bne    1f\n"
+               "ld     6,  -120(1)\n"
+               "cmpwi  6,  0x6060\n"
+               "bne    1f\n"
+               "ld     7,  -128(1)\n"
+               "cmpwi  7,  0x7070\n"
+               "bne    1f\n"
+               "ld     8,  -136(1)\n"
+               "cmpwi  8,  0x0808\n"
+               "bne    1f\n"
+               "ld     9,  -144(1)\n"
+               "cmpwi  9,  0x0909\n"
+               "bne    1f\n"
+               "ld     10, -152(1)\n"
+               "cmpwi  10, 0x1010\n"
+               "bne    1f\n"
+               "ld     11, -160(1)\n"
+               "cmpwi  11, 0x1111\n"
+               "bne    1f\n"
+               "ld     14, -168(1)\n"
+               "cmpwi  14, 0x1414\n"
+               "bne    1f\n"
+               "ld     15, -176(1)\n"
+               "cmpwi  15, 0x1515\n"
+               "bne    1f\n"
+               "ld     16, -184(1)\n"
+               "cmpwi  16, 0x1616\n"
+               "bne    1f\n"
+               "ld     17, -192(1)\n"
+               "cmpwi  17, 0x1717\n"
+               "bne    1f\n"
+               "ld     18, -200(1)\n"
+               "cmpwi  18, 0x1818\n"
+               "bne    1f\n"
+               "ld     19, -208(1)\n"
+               "cmpwi  19, 0x1919\n"
+               "bne    1f\n"
+               "ld     20, -216(1)\n"
+               "cmpwi  20, 0x2020\n"
+               "bne    1f\n"
+               "ld     21, -224(1)\n"
+               "cmpwi  21, 0x2121\n"
+               "bne    1f\n"
+               "ld     22, -232(1)\n"
+               "cmpwi  22, 0x2222\n"
+               "bne    1f\n"
+               "ld     23, -240(1)\n"
+               "cmpwi  23, 0x2323\n"
+               "bne    1f\n"
+               "ld     24, -248(1)\n"
+               "cmpwi  24, 0x2424\n"
+               "bne    1f\n"
+               "ld     25, -256(1)\n"
+               "cmpwi  25, 0x2525\n"
+               "bne    1f\n"
+               "ld     26, -264(1)\n"
+               "cmpwi  26, 0x2626\n"
+               "bne    1f\n"
+               "ld     27, -272(1)\n"
+               "cmpwi  27, 0x2727\n"
+               "bne    1f\n"
+               "ld     28, -280(1)\n"
+               "cmpwi  28, 0x2828\n"
+               "bne    1f\n"
+               "ld     29, -288(1)\n"
+               "cmpwi  29, 0x2929\n"
+               "bne    1f\n"
+
+               /* Load 0 (success) to return */
+               "li     0, 0\n"
+
+               "1:     mr %0, 0\n"
+
+               : "=r" (rc)
+               : /* no inputs */
+               : "3", "4", "5", "6", "7", "8", "9", "10", "11", "14",
+                 "15", "16", "17", "18", "19", "20", "21", "22", "23",
+                  "24", "25", "26", "27", "28", "29", "30", "31",
+                  "memory"
+       );
+
+       return rc;
+}
+
+int core_busy_loop_with_freeze(void)
+{
+       int rc;
+
+       mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
+       rc = core_busy_loop();
+       mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) |  MMCR0_FC);
+
+       return rc;
+}
+
+int ebb_child(union pipe read_pipe, union pipe write_pipe)
+{
+       struct event event;
+       uint64_t val;
+
+       FAIL_IF(wait_for_parent(read_pipe));
+
+       event_init_named(&event, 0x1001e, "cycles");
+       event_leader_ebb_init(&event);
+
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open(&event));
+
+       ebb_enable_pmc_counting(1);
+       setup_ebb_handler(standard_ebb_callee);
+       ebb_global_enable();
+
+       FAIL_IF(event_enable(&event));
+
+       if (event_read(&event)) {
+               /*
+                * Some tests expect to fail here, so don't report an error on
+                * this line, and return a distinguisable error code. Tell the
+                * parent an error happened.
+                */
+               notify_parent_of_error(write_pipe);
+               return 2;
+       }
+
+       mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+       FAIL_IF(notify_parent(write_pipe));
+       FAIL_IF(wait_for_parent(read_pipe));
+       FAIL_IF(notify_parent(write_pipe));
+
+       while (ebb_state.stats.ebb_count < 20) {
+               FAIL_IF(core_busy_loop());
+
+               /* To try and hit SIGILL case */
+               val  = mfspr(SPRN_MMCRA);
+               val |= mfspr(SPRN_MMCR2);
+               val |= mfspr(SPRN_MMCR0);
+       }
+
+       ebb_global_disable();
+       ebb_freeze_pmcs();
+
+       count_pmc(1, sample_period);
+
+       dump_ebb_state();
+
+       event_close(&event);
+
+       FAIL_IF(ebb_state.stats.ebb_count == 0);
+
+       return 0;
+}
+
+static jmp_buf setjmp_env;
+
+static void sigill_handler(int signal)
+{
+       printf("Took sigill\n");
+       longjmp(setjmp_env, 1);
+}
+
+static struct sigaction sigill_action = {
+       .sa_handler = sigill_handler,
+};
+
+int catch_sigill(void (*func)(void))
+{
+       if (sigaction(SIGILL, &sigill_action, NULL)) {
+               perror("sigaction");
+               return 1;
+       }
+
+       if (setjmp(setjmp_env) == 0) {
+               func();
+               return 1;
+       }
+
+       return 0;
+}
+
+void write_pmc1(void)
+{
+       mtspr(SPRN_PMC1, 0);
+}
+
+void write_pmc(int pmc, u64 value)
+{
+       switch (pmc) {
+               case 1: mtspr(SPRN_PMC1, value); break;
+               case 2: mtspr(SPRN_PMC2, value); break;
+               case 3: mtspr(SPRN_PMC3, value); break;
+               case 4: mtspr(SPRN_PMC4, value); break;
+               case 5: mtspr(SPRN_PMC5, value); break;
+               case 6: mtspr(SPRN_PMC6, value); break;
+       }
+}
+
+u64 read_pmc(int pmc)
+{
+       switch (pmc) {
+               case 1: return mfspr(SPRN_PMC1);
+               case 2: return mfspr(SPRN_PMC2);
+               case 3: return mfspr(SPRN_PMC3);
+               case 4: return mfspr(SPRN_PMC4);
+               case 5: return mfspr(SPRN_PMC5);
+               case 6: return mfspr(SPRN_PMC6);
+       }
+
+       return 0;
+}
+
+static void term_handler(int signal)
+{
+       dump_summary_ebb_state();
+       dump_ebb_hw_state();
+       abort();
+}
+
+struct sigaction term_action = {
+       .sa_handler = term_handler,
+};
+
+static void __attribute__((constructor)) ebb_init(void)
+{
+       clear_ebb_stats();
+
+       if (sigaction(SIGTERM, &term_action, NULL))
+               perror("sigaction");
+
+       ebb_state.trace = trace_buffer_allocate(1 * 1024 * 1024);
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb.h b/tools/testing/selftests/powerpc/pmu/ebb/ebb.h
new file mode 100644 (file)
index 0000000..e62bde0
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#ifndef _SELFTESTS_POWERPC_PMU_EBB_EBB_H
+#define _SELFTESTS_POWERPC_PMU_EBB_EBB_H
+
+#include "../event.h"
+#include "../lib.h"
+#include "trace.h"
+#include "reg.h"
+
+#define PMC_INDEX(pmc) ((pmc)-1)
+
+#define NUM_PMC_VALUES 128
+
+struct ebb_state
+{
+       struct {
+               u64 pmc_count[6];
+               volatile int ebb_count;
+               int spurious;
+               int negative;
+               int no_overflow;
+       } stats;
+
+       bool pmc_enable[6];
+       struct trace_buffer *trace;
+};
+
+extern struct ebb_state ebb_state;
+
+#define COUNTER_OVERFLOW 0x80000000ull
+
+static inline uint32_t pmc_sample_period(uint32_t value)
+{
+       return COUNTER_OVERFLOW - value;
+}
+
+static inline void ebb_enable_pmc_counting(int pmc)
+{
+       ebb_state.pmc_enable[PMC_INDEX(pmc)] = true;
+}
+
+bool ebb_check_count(int pmc, u64 sample_period, int fudge);
+void event_leader_ebb_init(struct event *e);
+void event_ebb_init(struct event *e);
+void event_bhrb_init(struct event *e, unsigned ifm);
+void setup_ebb_handler(void (*callee)(void));
+void standard_ebb_callee(void);
+int ebb_event_enable(struct event *e);
+void ebb_global_enable(void);
+void ebb_global_disable(void);
+void ebb_freeze_pmcs(void);
+void ebb_unfreeze_pmcs(void);
+void event_ebb_init(struct event *e);
+void event_leader_ebb_init(struct event *e);
+int count_pmc(int pmc, uint32_t sample_period);
+void dump_ebb_state(void);
+void dump_summary_ebb_state(void);
+void dump_ebb_hw_state(void);
+void clear_ebb_stats(void);
+void write_pmc(int pmc, u64 value);
+u64 read_pmc(int pmc);
+void reset_ebb_with_clear_mask(unsigned long mmcr0_clear_mask);
+void reset_ebb(void);
+int ebb_check_mmcr0(void);
+
+extern u64 sample_period;
+
+int core_busy_loop(void);
+int core_busy_loop_with_freeze(void);
+int ebb_child(union pipe read_pipe, union pipe write_pipe);
+int catch_sigill(void (*func)(void));
+void write_pmc1(void);
+
+#endif /* _SELFTESTS_POWERPC_PMU_EBB_EBB_H */
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_handler.S b/tools/testing/selftests/powerpc/pmu/ebb/ebb_handler.S
new file mode 100644 (file)
index 0000000..14274ea
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <ppc-asm.h>
+#include "reg.h"
+
+
+/* ppc-asm.h defines most of the reg aliases, but not r1/r2. */
+#define r1 1
+#define r2 2
+
+#define RFEBB   .long 0x4c000924
+
+/* Stack layout:
+ *
+ *                   ^
+ *  User stack       |
+ *  Back chain ------+ <- r1           <-------+
+ *  ...                                                |
+ *  Red zone / ABI Gap                         |
+ *  ...                                                |
+ *  vr63       <+                              |
+ *  vr0                 |                              |
+ *  VSCR        |                              |
+ *  FSCR        |                              |
+ *  r31                 | Save area                    |
+ *  r0          |                              |
+ *  XER                 |                              |
+ *  CTR                 |                              |
+ *  LR          |                              |
+ *  CCR                <+                              |
+ *  ...                <+                              |
+ *  LR          | Caller frame                 |
+ *  CCR                 |                              |
+ *  Back chain <+      <- updated r1   --------+
+ *
+ */
+
+#if defined(_CALL_ELF) && _CALL_ELF == 2
+#define ABIGAP         512
+#else
+#define ABIGAP         288
+#endif
+
+#define NR_GPR         32
+#define NR_SPR         6
+#define NR_VSR         64
+
+#define SAVE_AREA      ((NR_GPR + NR_SPR) * 8 + (NR_VSR * 16))
+#define CALLER_FRAME   112
+
+#define STACK_FRAME    (ABIGAP + SAVE_AREA + CALLER_FRAME)
+
+#define CCR_SAVE       (CALLER_FRAME)
+#define LR_SAVE                (CCR_SAVE + 8)
+#define CTR_SAVE       (LR_SAVE  + 8)
+#define XER_SAVE       (CTR_SAVE + 8)
+#define GPR_SAVE(n)    (XER_SAVE + 8 + (8 * n))
+#define FSCR_SAVE      (GPR_SAVE(31) + 8)
+#define VSCR_SAVE      (FSCR_SAVE + 8)
+#define VSR_SAVE(n)    (VSCR_SAVE + 8 + (16 * n))
+
+#define SAVE_GPR(n)    std n,GPR_SAVE(n)(r1)
+#define REST_GPR(n)    ld  n,GPR_SAVE(n)(r1)
+#define TRASH_GPR(n)   lis n,0xaaaa
+
+#define SAVE_VSR(n, b) li b, VSR_SAVE(n); stxvd2x n,b,r1
+#define LOAD_VSR(n, b) li b, VSR_SAVE(n); lxvd2x  n,b,r1
+
+#define LOAD_REG_IMMEDIATE(reg,expr)   \
+       lis     reg,(expr)@highest;     \
+       ori     reg,reg,(expr)@higher;  \
+       rldicr  reg,reg,32,31;          \
+       oris    reg,reg,(expr)@h;       \
+       ori     reg,reg,(expr)@l;
+
+
+#if defined(_CALL_ELF) && _CALL_ELF == 2
+#define ENTRY_POINT(name) \
+       .type FUNC_NAME(name),@function; \
+       .globl FUNC_NAME(name); \
+       FUNC_NAME(name):
+
+#define RESTORE_TOC(name)      \
+       /* Restore our TOC pointer using our entry point */     \
+       LOAD_REG_IMMEDIATE(r12, name)                           \
+0:     addis   r2,r12,(.TOC.-0b)@ha;                           \
+       addi    r2,r2,(.TOC.-0b)@l;
+
+#else
+#define ENTRY_POINT(name) FUNC_START(name)
+#define RESTORE_TOC(name)      \
+       /* Restore our TOC pointer via our opd entry */ \
+       LOAD_REG_IMMEDIATE(r2, name)                    \
+       ld      r2,8(r2);
+#endif
+
+    .text
+
+ENTRY_POINT(ebb_handler)
+    stdu    r1,-STACK_FRAME(r1)
+    SAVE_GPR(0)
+    mflr    r0
+    std     r0,LR_SAVE(r1)
+    mfcr    r0
+    std     r0,CCR_SAVE(r1)
+    mfctr   r0
+    std     r0,CTR_SAVE(r1)
+    mfxer   r0
+    std     r0,XER_SAVE(r1)
+    SAVE_GPR(2)
+    SAVE_GPR(3)
+    SAVE_GPR(4)
+    SAVE_GPR(5)
+    SAVE_GPR(6)
+    SAVE_GPR(7)
+    SAVE_GPR(8)
+    SAVE_GPR(9)
+    SAVE_GPR(10)
+    SAVE_GPR(11)
+    SAVE_GPR(12)
+    SAVE_GPR(13)
+    SAVE_GPR(14)
+    SAVE_GPR(15)
+    SAVE_GPR(16)
+    SAVE_GPR(17)
+    SAVE_GPR(18)
+    SAVE_GPR(19)
+    SAVE_GPR(20)
+    SAVE_GPR(21)
+    SAVE_GPR(22)
+    SAVE_GPR(23)
+    SAVE_GPR(24)
+    SAVE_GPR(25)
+    SAVE_GPR(26)
+    SAVE_GPR(27)
+    SAVE_GPR(28)
+    SAVE_GPR(29)
+    SAVE_GPR(30)
+    SAVE_GPR(31)
+    SAVE_VSR(0, r3)
+    mffs     f0
+    stfd     f0, FSCR_SAVE(r1)
+    mfvscr   f0
+    stfd     f0, VSCR_SAVE(r1)
+    SAVE_VSR(1,  r3)
+    SAVE_VSR(2,  r3)
+    SAVE_VSR(3,  r3)
+    SAVE_VSR(4,  r3)
+    SAVE_VSR(5,  r3)
+    SAVE_VSR(6,  r3)
+    SAVE_VSR(7,  r3)
+    SAVE_VSR(8,  r3)
+    SAVE_VSR(9,  r3)
+    SAVE_VSR(10, r3)
+    SAVE_VSR(11, r3)
+    SAVE_VSR(12, r3)
+    SAVE_VSR(13, r3)
+    SAVE_VSR(14, r3)
+    SAVE_VSR(15, r3)
+    SAVE_VSR(16, r3)
+    SAVE_VSR(17, r3)
+    SAVE_VSR(18, r3)
+    SAVE_VSR(19, r3)
+    SAVE_VSR(20, r3)
+    SAVE_VSR(21, r3)
+    SAVE_VSR(22, r3)
+    SAVE_VSR(23, r3)
+    SAVE_VSR(24, r3)
+    SAVE_VSR(25, r3)
+    SAVE_VSR(26, r3)
+    SAVE_VSR(27, r3)
+    SAVE_VSR(28, r3)
+    SAVE_VSR(29, r3)
+    SAVE_VSR(30, r3)
+    SAVE_VSR(31, r3)
+    SAVE_VSR(32, r3)
+    SAVE_VSR(33, r3)
+    SAVE_VSR(34, r3)
+    SAVE_VSR(35, r3)
+    SAVE_VSR(36, r3)
+    SAVE_VSR(37, r3)
+    SAVE_VSR(38, r3)
+    SAVE_VSR(39, r3)
+    SAVE_VSR(40, r3)
+    SAVE_VSR(41, r3)
+    SAVE_VSR(42, r3)
+    SAVE_VSR(43, r3)
+    SAVE_VSR(44, r3)
+    SAVE_VSR(45, r3)
+    SAVE_VSR(46, r3)
+    SAVE_VSR(47, r3)
+    SAVE_VSR(48, r3)
+    SAVE_VSR(49, r3)
+    SAVE_VSR(50, r3)
+    SAVE_VSR(51, r3)
+    SAVE_VSR(52, r3)
+    SAVE_VSR(53, r3)
+    SAVE_VSR(54, r3)
+    SAVE_VSR(55, r3)
+    SAVE_VSR(56, r3)
+    SAVE_VSR(57, r3)
+    SAVE_VSR(58, r3)
+    SAVE_VSR(59, r3)
+    SAVE_VSR(60, r3)
+    SAVE_VSR(61, r3)
+    SAVE_VSR(62, r3)
+    SAVE_VSR(63, r3)
+
+    TRASH_GPR(2)
+    TRASH_GPR(3)
+    TRASH_GPR(4)
+    TRASH_GPR(5)
+    TRASH_GPR(6)
+    TRASH_GPR(7)
+    TRASH_GPR(8)
+    TRASH_GPR(9)
+    TRASH_GPR(10)
+    TRASH_GPR(11)
+    TRASH_GPR(12)
+    TRASH_GPR(14)
+    TRASH_GPR(15)
+    TRASH_GPR(16)
+    TRASH_GPR(17)
+    TRASH_GPR(18)
+    TRASH_GPR(19)
+    TRASH_GPR(20)
+    TRASH_GPR(21)
+    TRASH_GPR(22)
+    TRASH_GPR(23)
+    TRASH_GPR(24)
+    TRASH_GPR(25)
+    TRASH_GPR(26)
+    TRASH_GPR(27)
+    TRASH_GPR(28)
+    TRASH_GPR(29)
+    TRASH_GPR(30)
+    TRASH_GPR(31)
+
+    RESTORE_TOC(ebb_handler)
+
+    /*
+     * r13 is our TLS pointer. We leave whatever value was in there when the
+     * EBB fired. That seems to be OK because once set the TLS pointer is not
+     * changed - but presumably that could change in future.
+     */
+
+    bl      ebb_hook
+    nop
+
+    /* r2 may be changed here but we don't care */
+
+    lfd      f0, FSCR_SAVE(r1)
+    mtfsf    0xff,f0
+    lfd      f0, VSCR_SAVE(r1)
+    mtvscr   f0
+    LOAD_VSR(0, r3)
+    LOAD_VSR(1,  r3)
+    LOAD_VSR(2,  r3)
+    LOAD_VSR(3,  r3)
+    LOAD_VSR(4,  r3)
+    LOAD_VSR(5,  r3)
+    LOAD_VSR(6,  r3)
+    LOAD_VSR(7,  r3)
+    LOAD_VSR(8,  r3)
+    LOAD_VSR(9,  r3)
+    LOAD_VSR(10, r3)
+    LOAD_VSR(11, r3)
+    LOAD_VSR(12, r3)
+    LOAD_VSR(13, r3)
+    LOAD_VSR(14, r3)
+    LOAD_VSR(15, r3)
+    LOAD_VSR(16, r3)
+    LOAD_VSR(17, r3)
+    LOAD_VSR(18, r3)
+    LOAD_VSR(19, r3)
+    LOAD_VSR(20, r3)
+    LOAD_VSR(21, r3)
+    LOAD_VSR(22, r3)
+    LOAD_VSR(23, r3)
+    LOAD_VSR(24, r3)
+    LOAD_VSR(25, r3)
+    LOAD_VSR(26, r3)
+    LOAD_VSR(27, r3)
+    LOAD_VSR(28, r3)
+    LOAD_VSR(29, r3)
+    LOAD_VSR(30, r3)
+    LOAD_VSR(31, r3)
+    LOAD_VSR(32, r3)
+    LOAD_VSR(33, r3)
+    LOAD_VSR(34, r3)
+    LOAD_VSR(35, r3)
+    LOAD_VSR(36, r3)
+    LOAD_VSR(37, r3)
+    LOAD_VSR(38, r3)
+    LOAD_VSR(39, r3)
+    LOAD_VSR(40, r3)
+    LOAD_VSR(41, r3)
+    LOAD_VSR(42, r3)
+    LOAD_VSR(43, r3)
+    LOAD_VSR(44, r3)
+    LOAD_VSR(45, r3)
+    LOAD_VSR(46, r3)
+    LOAD_VSR(47, r3)
+    LOAD_VSR(48, r3)
+    LOAD_VSR(49, r3)
+    LOAD_VSR(50, r3)
+    LOAD_VSR(51, r3)
+    LOAD_VSR(52, r3)
+    LOAD_VSR(53, r3)
+    LOAD_VSR(54, r3)
+    LOAD_VSR(55, r3)
+    LOAD_VSR(56, r3)
+    LOAD_VSR(57, r3)
+    LOAD_VSR(58, r3)
+    LOAD_VSR(59, r3)
+    LOAD_VSR(60, r3)
+    LOAD_VSR(61, r3)
+    LOAD_VSR(62, r3)
+    LOAD_VSR(63, r3)
+
+    ld      r0,XER_SAVE(r1)
+    mtxer   r0
+    ld      r0,CTR_SAVE(r1)
+    mtctr   r0
+    ld      r0,LR_SAVE(r1)
+    mtlr    r0
+    ld      r0,CCR_SAVE(r1)
+    mtcr    r0
+    REST_GPR(0)
+    REST_GPR(2)
+    REST_GPR(3)
+    REST_GPR(4)
+    REST_GPR(5)
+    REST_GPR(6)
+    REST_GPR(7)
+    REST_GPR(8)
+    REST_GPR(9)
+    REST_GPR(10)
+    REST_GPR(11)
+    REST_GPR(12)
+    REST_GPR(13)
+    REST_GPR(14)
+    REST_GPR(15)
+    REST_GPR(16)
+    REST_GPR(17)
+    REST_GPR(18)
+    REST_GPR(19)
+    REST_GPR(20)
+    REST_GPR(21)
+    REST_GPR(22)
+    REST_GPR(23)
+    REST_GPR(24)
+    REST_GPR(25)
+    REST_GPR(26)
+    REST_GPR(27)
+    REST_GPR(28)
+    REST_GPR(29)
+    REST_GPR(30)
+    REST_GPR(31)
+    addi    r1,r1,STACK_FRAME
+    RFEBB
+FUNC_END(ebb_handler)
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_child_test.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_child_test.c
new file mode 100644 (file)
index 0000000..c45f948
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "ebb.h"
+
+
+/*
+ * Tests we can setup an EBB on our child. Nothing interesting happens, because
+ * even though the event is enabled and running the child hasn't enabled the
+ * actual delivery of the EBBs.
+ */
+
+static int victim_child(union pipe read_pipe, union pipe write_pipe)
+{
+       int i;
+
+       FAIL_IF(wait_for_parent(read_pipe));
+       FAIL_IF(notify_parent(write_pipe));
+
+       /* Parent creates EBB event */
+
+       FAIL_IF(wait_for_parent(read_pipe));
+       FAIL_IF(notify_parent(write_pipe));
+
+       /* Check the EBB is enabled by writing PMC1 */
+       write_pmc1();
+
+       /* EBB event is enabled here */
+       for (i = 0; i < 1000000; i++) ;
+
+       return 0;
+}
+
+int ebb_on_child(void)
+{
+       union pipe read_pipe, write_pipe;
+       struct event event;
+       pid_t pid;
+
+       FAIL_IF(pipe(read_pipe.fds) == -1);
+       FAIL_IF(pipe(write_pipe.fds) == -1);
+
+       pid = fork();
+       if (pid == 0) {
+               /* NB order of pipes looks reversed */
+               exit(victim_child(write_pipe, read_pipe));
+       }
+
+       FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+       /* Child is running now */
+
+       event_init_named(&event, 0x1001e, "cycles");
+       event_leader_ebb_init(&event);
+
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open_with_pid(&event, pid));
+       FAIL_IF(ebb_event_enable(&event));
+
+       FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+       /* Child should just exit happily */
+       FAIL_IF(wait_for_child(pid));
+
+       event_close(&event);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(ebb_on_child, "ebb_on_child");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_willing_child_test.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb_on_willing_child_test.c
new file mode 100644 (file)
index 0000000..11acf1d
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "ebb.h"
+
+
+/*
+ * Tests we can setup an EBB on our child. The child expects this and enables
+ * EBBs, which are then delivered to the child, even though the event is
+ * created by the parent.
+ */
+
+static int victim_child(union pipe read_pipe, union pipe write_pipe)
+{
+       FAIL_IF(wait_for_parent(read_pipe));
+
+       /* Setup our EBB handler, before the EBB event is created */
+       ebb_enable_pmc_counting(1);
+       setup_ebb_handler(standard_ebb_callee);
+       ebb_global_enable();
+
+       FAIL_IF(notify_parent(write_pipe));
+
+       while (ebb_state.stats.ebb_count < 20) {
+               FAIL_IF(core_busy_loop());
+       }
+
+       ebb_global_disable();
+       ebb_freeze_pmcs();
+
+       count_pmc(1, sample_period);
+
+       dump_ebb_state();
+
+       FAIL_IF(ebb_state.stats.ebb_count == 0);
+
+       return 0;
+}
+
+/* Tests we can setup an EBB on our child - if it's expecting it */
+int ebb_on_willing_child(void)
+{
+       union pipe read_pipe, write_pipe;
+       struct event event;
+       pid_t pid;
+
+       FAIL_IF(pipe(read_pipe.fds) == -1);
+       FAIL_IF(pipe(write_pipe.fds) == -1);
+
+       pid = fork();
+       if (pid == 0) {
+               /* NB order of pipes looks reversed */
+               exit(victim_child(write_pipe, read_pipe));
+       }
+
+       /* Signal the child to setup its EBB handler */
+       FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+       /* Child is running now */
+
+       event_init_named(&event, 0x1001e, "cycles");
+       event_leader_ebb_init(&event);
+
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open_with_pid(&event, pid));
+       FAIL_IF(ebb_event_enable(&event));
+
+       /* Child show now take EBBs and then exit */
+       FAIL_IF(wait_for_child(pid));
+
+       event_close(&event);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(ebb_on_willing_child, "ebb_on_willing_child");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_vs_cpu_event_test.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb_vs_cpu_event_test.c
new file mode 100644 (file)
index 0000000..be4dd5a
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "ebb.h"
+
+
+/*
+ * Tests an EBB vs a cpu event - in that order. The EBB should force the cpu
+ * event off the PMU.
+ */
+
+static int setup_cpu_event(struct event *event, int cpu)
+{
+       event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
+
+       event->attr.exclude_kernel = 1;
+       event->attr.exclude_hv = 1;
+       event->attr.exclude_idle = 1;
+
+       SKIP_IF(require_paranoia_below(1));
+       FAIL_IF(event_open_with_cpu(event, cpu));
+       FAIL_IF(event_enable(event));
+
+       return 0;
+}
+
+int ebb_vs_cpu_event(void)
+{
+       union pipe read_pipe, write_pipe;
+       struct event event;
+       int cpu, rc;
+       pid_t pid;
+
+       cpu = pick_online_cpu();
+       FAIL_IF(cpu < 0);
+       FAIL_IF(bind_to_cpu(cpu));
+
+       FAIL_IF(pipe(read_pipe.fds) == -1);
+       FAIL_IF(pipe(write_pipe.fds) == -1);
+
+       pid = fork();
+       if (pid == 0) {
+               /* NB order of pipes looks reversed */
+               exit(ebb_child(write_pipe, read_pipe));
+       }
+
+       /* Signal the child to install its EBB event and wait */
+       FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+       /* Now try to install our CPU event */
+       rc = setup_cpu_event(&event, cpu);
+       if (rc) {
+               kill_child_and_wait(pid);
+               return rc;
+       }
+
+       /* Signal the child to run */
+       FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+       /* .. and wait for it to complete */
+       FAIL_IF(wait_for_child(pid));
+       FAIL_IF(event_disable(&event));
+       FAIL_IF(event_read(&event));
+
+       event_report(&event);
+
+       /* The cpu event may have run, but we don't expect 100% */
+       FAIL_IF(event.result.enabled >= event.result.running);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(ebb_vs_cpu_event, "ebb_vs_cpu_event");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/event_attributes_test.c b/tools/testing/selftests/powerpc/pmu/ebb/event_attributes_test.c
new file mode 100644 (file)
index 0000000..7e78153
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test various attributes of the EBB event are enforced.
+ */
+int event_attributes(void)
+{
+       struct event event, leader;
+
+       event_init(&event, 0x1001e);
+       event_leader_ebb_init(&event);
+       /* Expected to succeed */
+       FAIL_IF(event_open(&event));
+       event_close(&event);
+
+
+       event_init(&event, 0x001e); /* CYCLES - no PMC specified */
+       event_leader_ebb_init(&event);
+       /* Expected to fail, no PMC specified */
+       FAIL_IF(event_open(&event) == 0);
+
+
+       event_init(&event, 0x2001e);
+       event_leader_ebb_init(&event);
+       event.attr.exclusive = 0;
+       /* Expected to fail, not exclusive */
+       FAIL_IF(event_open(&event) == 0);
+
+
+       event_init(&event, 0x3001e);
+       event_leader_ebb_init(&event);
+       event.attr.freq = 1;
+       /* Expected to fail, sets freq */
+       FAIL_IF(event_open(&event) == 0);
+
+
+       event_init(&event, 0x4001e);
+       event_leader_ebb_init(&event);
+       event.attr.sample_period = 1;
+       /* Expected to fail, sets sample_period */
+       FAIL_IF(event_open(&event) == 0);
+
+
+       event_init(&event, 0x1001e);
+       event_leader_ebb_init(&event);
+       event.attr.enable_on_exec = 1;
+       /* Expected to fail, sets enable_on_exec */
+       FAIL_IF(event_open(&event) == 0);
+
+
+       event_init(&event, 0x1001e);
+       event_leader_ebb_init(&event);
+       event.attr.inherit = 1;
+       /* Expected to fail, sets inherit */
+       FAIL_IF(event_open(&event) == 0);
+
+
+       event_init(&leader, 0x1001e);
+       event_leader_ebb_init(&leader);
+       FAIL_IF(event_open(&leader));
+
+       event_init(&event, 0x20002);
+       event_ebb_init(&event);
+
+       /* Expected to succeed */
+       FAIL_IF(event_open_with_group(&event, leader.fd));
+       event_close(&leader);
+       event_close(&event);
+
+
+       event_init(&leader, 0x1001e);
+       event_leader_ebb_init(&leader);
+       FAIL_IF(event_open(&leader));
+
+       event_init(&event, 0x20002);
+
+       /* Expected to fail, event doesn't request EBB, leader does */
+       FAIL_IF(event_open_with_group(&event, leader.fd) == 0);
+       event_close(&leader);
+
+
+       event_init(&leader, 0x1001e);
+       event_leader_ebb_init(&leader);
+       /* Clear the EBB flag */
+       leader.attr.config &= ~(1ull << 63);
+
+       FAIL_IF(event_open(&leader));
+
+       event_init(&event, 0x20002);
+       event_ebb_init(&event);
+
+       /* Expected to fail, leader doesn't request EBB */
+       FAIL_IF(event_open_with_group(&event, leader.fd) == 0);
+       event_close(&leader);
+
+
+       event_init(&leader, 0x1001e);
+       event_leader_ebb_init(&leader);
+       leader.attr.exclusive = 0;
+       /* Expected to fail, leader isn't exclusive */
+       FAIL_IF(event_open(&leader) == 0);
+
+
+       event_init(&leader, 0x1001e);
+       event_leader_ebb_init(&leader);
+       leader.attr.pinned = 0;
+       /* Expected to fail, leader isn't pinned */
+       FAIL_IF(event_open(&leader) == 0);
+
+       event_init(&event, 0x1001e);
+       event_leader_ebb_init(&event);
+       /* Expected to fail, not a task event */
+       SKIP_IF(require_paranoia_below(1));
+       FAIL_IF(event_open_with_cpu(&event, 0) == 0);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(event_attributes, "event_attributes");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/fixed_instruction_loop.S b/tools/testing/selftests/powerpc/pmu/ebb/fixed_instruction_loop.S
new file mode 100644 (file)
index 0000000..b866a05
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <ppc-asm.h>
+
+       .text
+
+FUNC_START(thirty_two_instruction_loop)
+       cmpwi   r3,0
+       beqlr
+       addi    r4,r3,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1 # 28 addi's
+       subi    r3,r3,1
+       b       FUNC_NAME(thirty_two_instruction_loop)
+FUNC_END(thirty_two_instruction_loop)
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/fork_cleanup_test.c b/tools/testing/selftests/powerpc/pmu/ebb/fork_cleanup_test.c
new file mode 100644 (file)
index 0000000..9e7af6e
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <setjmp.h>
+#include <signal.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test that a fork clears the PMU state of the child. eg. BESCR/EBBHR/EBBRR
+ * are cleared, and MMCR0_PMCC is reset, preventing the child from accessing
+ * the PMU.
+ */
+
+static struct event event;
+
+static int child(void)
+{
+       /* Even though we have EBE=0 we can still see the EBB regs */
+       FAIL_IF(mfspr(SPRN_BESCR) != 0);
+       FAIL_IF(mfspr(SPRN_EBBHR) != 0);
+       FAIL_IF(mfspr(SPRN_EBBRR) != 0);
+
+       FAIL_IF(catch_sigill(write_pmc1));
+
+       /* We can still read from the event, though it is on our parent */
+       FAIL_IF(event_read(&event));
+
+       return 0;
+}
+
+/* Tests that fork clears EBB state */
+int fork_cleanup(void)
+{
+       pid_t pid;
+
+       event_init_named(&event, 0x1001e, "cycles");
+       event_leader_ebb_init(&event);
+
+       FAIL_IF(event_open(&event));
+
+       ebb_enable_pmc_counting(1);
+       setup_ebb_handler(standard_ebb_callee);
+       ebb_global_enable();
+
+       FAIL_IF(ebb_event_enable(&event));
+
+       mtspr(SPRN_MMCR0, MMCR0_FC);
+       mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+       /* Don't need to actually take any EBBs */
+
+       pid = fork();
+       if (pid == 0)
+               exit(child());
+
+       /* Child does the actual testing */
+       FAIL_IF(wait_for_child(pid));
+
+       /* After fork */
+       event_close(&event);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(fork_cleanup, "fork_cleanup");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c b/tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c
new file mode 100644 (file)
index 0000000..f8190fa
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/prctl.h>
+
+#include "ebb.h"
+
+
+/*
+ * Run a calibrated instruction loop and count instructions executed using
+ * EBBs. Make sure the counts look right.
+ */
+
+extern void thirty_two_instruction_loop(uint64_t loops);
+
+static bool counters_frozen = true;
+
+static int do_count_loop(struct event *event, uint64_t instructions,
+                        uint64_t overhead, bool report)
+{
+       int64_t difference, expected;
+       double percentage;
+
+       clear_ebb_stats();
+
+       counters_frozen = false;
+       mb();
+       mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
+
+       thirty_two_instruction_loop(instructions >> 5);
+
+       counters_frozen = true;
+       mb();
+       mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) | MMCR0_FC);
+
+       count_pmc(4, sample_period);
+
+       event->result.value = ebb_state.stats.pmc_count[4-1];
+       expected = instructions + overhead;
+       difference = event->result.value - expected;
+       percentage = (double)difference / event->result.value * 100;
+
+       if (report) {
+               printf("Looped for %lu instructions, overhead %lu\n", instructions, overhead);
+               printf("Expected %lu\n", expected);
+               printf("Actual   %llu\n", event->result.value);
+               printf("Error    %ld, %f%%\n", difference, percentage);
+               printf("Took %d EBBs\n", ebb_state.stats.ebb_count);
+       }
+
+       if (difference < 0)
+               difference = -difference;
+
+       /* Tolerate a difference of up to 0.0001 % */
+       difference *= 10000 * 100;
+       if (difference / event->result.value)
+               return -1;
+
+       return 0;
+}
+
+/* Count how many instructions it takes to do a null loop */
+static uint64_t determine_overhead(struct event *event)
+{
+       uint64_t current, overhead;
+       int i;
+
+       do_count_loop(event, 0, 0, false);
+       overhead = event->result.value;
+
+       for (i = 0; i < 100; i++) {
+               do_count_loop(event, 0, 0, false);
+               current = event->result.value;
+               if (current < overhead) {
+                       printf("Replacing overhead %lu with %lu\n", overhead, current);
+                       overhead = current;
+               }
+       }
+
+       return overhead;
+}
+
+static void pmc4_ebb_callee(void)
+{
+       uint64_t val;
+
+       val = mfspr(SPRN_BESCR);
+       if (!(val & BESCR_PMEO)) {
+               ebb_state.stats.spurious++;
+               goto out;
+       }
+
+       ebb_state.stats.ebb_count++;
+       count_pmc(4, sample_period);
+out:
+       if (counters_frozen)
+               reset_ebb_with_clear_mask(MMCR0_PMAO);
+       else
+               reset_ebb();
+}
+
+int instruction_count(void)
+{
+       struct event event;
+       uint64_t overhead;
+
+       event_init_named(&event, 0x400FA, "PM_RUN_INST_CMPL");
+       event_leader_ebb_init(&event);
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open(&event));
+       FAIL_IF(ebb_event_enable(&event));
+
+       sample_period = COUNTER_OVERFLOW;
+
+       setup_ebb_handler(pmc4_ebb_callee);
+       mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC);
+       ebb_global_enable();
+
+       overhead = determine_overhead(&event);
+       printf("Overhead of null loop: %lu instructions\n", overhead);
+
+       /* Run for 1M instructions */
+       FAIL_IF(do_count_loop(&event, 0x100000, overhead, true));
+
+       /* Run for 10M instructions */
+       FAIL_IF(do_count_loop(&event, 0xa00000, overhead, true));
+
+       /* Run for 100M instructions */
+       FAIL_IF(do_count_loop(&event, 0x6400000, overhead, true));
+
+       /* Run for 1G instructions */
+       FAIL_IF(do_count_loop(&event, 0x40000000, overhead, true));
+
+       /* Run for 16G instructions */
+       FAIL_IF(do_count_loop(&event, 0x400000000, overhead, true));
+
+       /* Run for 64G instructions */
+       FAIL_IF(do_count_loop(&event, 0x1000000000, overhead, true));
+
+       /* Run for 128G instructions */
+       FAIL_IF(do_count_loop(&event, 0x2000000000, overhead, true));
+
+       ebb_global_disable();
+       event_close(&event);
+
+       printf("Finished OK\n");
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(instruction_count, "instruction_count");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c b/tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c
new file mode 100644 (file)
index 0000000..0c9dd9b
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <sched.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test that tries to trigger CPU_FTR_PMAO_BUG. Which is a hardware defect
+ * where an exception triggers but we context switch before it is delivered and
+ * lose the exception.
+ */
+
+static int test_body(void)
+{
+       int i, orig_period, max_period;
+       struct event event;
+
+       /* We use PMC4 to make sure the kernel switches all counters correctly */
+       event_init_named(&event, 0x40002, "instructions");
+       event_leader_ebb_init(&event);
+
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open(&event));
+
+       ebb_enable_pmc_counting(4);
+       setup_ebb_handler(standard_ebb_callee);
+       ebb_global_enable();
+       FAIL_IF(ebb_event_enable(&event));
+
+       /*
+        * We want a low sample period, but we also want to get out of the EBB
+        * handler without tripping up again.
+        *
+        * This value picked after much experimentation.
+        */
+       orig_period = max_period = sample_period = 400;
+
+       mtspr(SPRN_PMC4, pmc_sample_period(sample_period));
+
+       while (ebb_state.stats.ebb_count < 1000000) {
+               /*
+                * We are trying to get the EBB exception to race exactly with
+                * us entering the kernel to do the syscall. We then need the
+                * kernel to decide our timeslice is up and context switch to
+                * the other thread. When we come back our EBB will have been
+                * lost and we'll spin in this while loop forever.
+                */
+
+               for (i = 0; i < 100000; i++)
+                       sched_yield();
+
+               /* Change the sample period slightly to try and hit the race */
+               if (sample_period >= (orig_period + 200))
+                       sample_period = orig_period;
+               else
+                       sample_period++;
+
+               if (sample_period > max_period)
+                       max_period = sample_period;
+       }
+
+       ebb_freeze_pmcs();
+       ebb_global_disable();
+
+       count_pmc(4, sample_period);
+       mtspr(SPRN_PMC4, 0xdead);
+
+       dump_summary_ebb_state();
+       dump_ebb_hw_state();
+
+       event_close(&event);
+
+       FAIL_IF(ebb_state.stats.ebb_count == 0);
+
+       /* We vary our sample period so we need extra fudge here */
+       FAIL_IF(!ebb_check_count(4, orig_period, 2 * (max_period - orig_period)));
+
+       return 0;
+}
+
+static int lost_exception(void)
+{
+       return eat_cpu(test_body);
+}
+
+int main(void)
+{
+       return test_harness(lost_exception, "lost_exception");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c b/tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c
new file mode 100644 (file)
index 0000000..67d78af
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test counting multiple events using EBBs.
+ */
+int multi_counter(void)
+{
+       struct event events[6];
+       int i, group_fd;
+
+       event_init_named(&events[0], 0x1001C, "PM_CMPLU_STALL_THRD");
+       event_init_named(&events[1], 0x2D016, "PM_CMPLU_STALL_FXU");
+       event_init_named(&events[2], 0x30006, "PM_CMPLU_STALL_OTHER_CMPL");
+       event_init_named(&events[3], 0x4000A, "PM_CMPLU_STALL");
+       event_init_named(&events[4], 0x600f4, "PM_RUN_CYC");
+       event_init_named(&events[5], 0x500fa, "PM_RUN_INST_CMPL");
+
+       event_leader_ebb_init(&events[0]);
+       for (i = 1; i < 6; i++)
+               event_ebb_init(&events[i]);
+
+       group_fd = -1;
+       for (i = 0; i < 6; i++) {
+               events[i].attr.exclude_kernel = 1;
+               events[i].attr.exclude_hv = 1;
+               events[i].attr.exclude_idle = 1;
+
+               FAIL_IF(event_open_with_group(&events[i], group_fd));
+               if (group_fd == -1)
+                       group_fd = events[0].fd;
+       }
+
+       ebb_enable_pmc_counting(1);
+       ebb_enable_pmc_counting(2);
+       ebb_enable_pmc_counting(3);
+       ebb_enable_pmc_counting(4);
+       ebb_enable_pmc_counting(5);
+       ebb_enable_pmc_counting(6);
+       setup_ebb_handler(standard_ebb_callee);
+
+       FAIL_IF(ioctl(events[0].fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP));
+       FAIL_IF(event_read(&events[0]));
+
+       ebb_global_enable();
+
+       mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+       mtspr(SPRN_PMC2, pmc_sample_period(sample_period));
+       mtspr(SPRN_PMC3, pmc_sample_period(sample_period));
+       mtspr(SPRN_PMC4, pmc_sample_period(sample_period));
+       mtspr(SPRN_PMC5, pmc_sample_period(sample_period));
+       mtspr(SPRN_PMC6, pmc_sample_period(sample_period));
+
+       while (ebb_state.stats.ebb_count < 50) {
+               FAIL_IF(core_busy_loop());
+               FAIL_IF(ebb_check_mmcr0());
+       }
+
+       ebb_global_disable();
+       ebb_freeze_pmcs();
+
+       count_pmc(1, sample_period);
+       count_pmc(2, sample_period);
+       count_pmc(3, sample_period);
+       count_pmc(4, sample_period);
+       count_pmc(5, sample_period);
+       count_pmc(6, sample_period);
+
+       dump_ebb_state();
+
+       for (i = 0; i < 6; i++)
+               event_close(&events[i]);
+
+       FAIL_IF(ebb_state.stats.ebb_count == 0);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(multi_counter, "multi_counter");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c b/tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c
new file mode 100644 (file)
index 0000000..b8dc371
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test running multiple EBB using processes at once on a single CPU. They
+ * should all run happily without interfering with each other.
+ */
+
+static bool child_should_exit;
+
+static void sigint_handler(int signal)
+{
+       child_should_exit = true;
+}
+
+struct sigaction sigint_action = {
+       .sa_handler = sigint_handler,
+};
+
+static int cycles_child(void)
+{
+       struct event event;
+
+       if (sigaction(SIGINT, &sigint_action, NULL)) {
+               perror("sigaction");
+               return 1;
+       }
+
+       event_init_named(&event, 0x1001e, "cycles");
+       event_leader_ebb_init(&event);
+
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open(&event));
+
+       ebb_enable_pmc_counting(1);
+       setup_ebb_handler(standard_ebb_callee);
+       ebb_global_enable();
+
+       FAIL_IF(ebb_event_enable(&event));
+
+       mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+       while (!child_should_exit) {
+               FAIL_IF(core_busy_loop());
+               FAIL_IF(ebb_check_mmcr0());
+       }
+
+       ebb_global_disable();
+       ebb_freeze_pmcs();
+
+       count_pmc(1, sample_period);
+
+       dump_summary_ebb_state();
+
+       event_close(&event);
+
+       FAIL_IF(ebb_state.stats.ebb_count == 0);
+
+       return 0;
+}
+
+#define NR_CHILDREN    4
+
+int multi_ebb_procs(void)
+{
+       pid_t pids[NR_CHILDREN];
+       int cpu, rc, i;
+
+       cpu = pick_online_cpu();
+       FAIL_IF(cpu < 0);
+       FAIL_IF(bind_to_cpu(cpu));
+
+       for (i = 0; i < NR_CHILDREN; i++) {
+               pids[i] = fork();
+               if (pids[i] == 0)
+                       exit(cycles_child());
+       }
+
+       /* Have them all run for "a while" */
+       sleep(10);
+
+       rc = 0;
+       for (i = 0; i < NR_CHILDREN; i++) {
+               /* Tell them to stop */
+               kill(pids[i], SIGINT);
+               /* And wait */
+               rc |= wait_for_child(pids[i]);
+       }
+
+       return rc;
+}
+
+int main(void)
+{
+       return test_harness(multi_ebb_procs, "multi_ebb_procs");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/no_handler_test.c b/tools/testing/selftests/powerpc/pmu/ebb/no_handler_test.c
new file mode 100644 (file)
index 0000000..2f9bf8e
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <signal.h>
+
+#include "ebb.h"
+
+
+/* Test that things work sanely if we have no handler */
+
+static int no_handler_test(void)
+{
+       struct event event;
+       u64 val;
+       int i;
+
+       event_init_named(&event, 0x1001e, "cycles");
+       event_leader_ebb_init(&event);
+
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open(&event));
+       FAIL_IF(ebb_event_enable(&event));
+
+       val = mfspr(SPRN_EBBHR);
+       FAIL_IF(val != 0);
+
+       /* Make sure it overflows quickly */
+       sample_period = 1000;
+       mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+       /* Spin to make sure the event has time to overflow */
+       for (i = 0; i < 1000; i++)
+               mb();
+
+       dump_ebb_state();
+
+       /* We expect to see the PMU frozen & PMAO set */
+       val = mfspr(SPRN_MMCR0);
+       FAIL_IF(val != 0x0000000080000080);
+
+       event_close(&event);
+
+       dump_ebb_state();
+
+       /* The real test is that we never took an EBB at 0x0 */
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(no_handler_test,"no_handler_test");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/pmae_handling_test.c b/tools/testing/selftests/powerpc/pmu/ebb/pmae_handling_test.c
new file mode 100644 (file)
index 0000000..986500f
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <sched.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test that the kernel properly handles PMAE across context switches.
+ *
+ * We test this by calling into the kernel inside our EBB handler, where PMAE
+ * is clear. A cpu eater companion thread is running on the same CPU as us to
+ * encourage the scheduler to switch us.
+ *
+ * The kernel must make sure that when it context switches us back in, it
+ * honours the fact that we had PMAE clear.
+ *
+ * Observed to hit the failing case on the first EBB with a broken kernel.
+ */
+
+static bool mmcr0_mismatch;
+static uint64_t before, after;
+
+static void syscall_ebb_callee(void)
+{
+       uint64_t val;
+
+       val = mfspr(SPRN_BESCR);
+       if (!(val & BESCR_PMEO)) {
+               ebb_state.stats.spurious++;
+               goto out;
+       }
+
+       ebb_state.stats.ebb_count++;
+       count_pmc(1, sample_period);
+
+       before = mfspr(SPRN_MMCR0);
+
+       /* Try and get ourselves scheduled, to force a PMU context switch */
+       sched_yield();
+
+       after = mfspr(SPRN_MMCR0);
+       if (before != after)
+               mmcr0_mismatch = true;
+
+out:
+       reset_ebb();
+}
+
+static int test_body(void)
+{
+       struct event event;
+
+       event_init_named(&event, 0x1001e, "cycles");
+       event_leader_ebb_init(&event);
+
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open(&event));
+
+       setup_ebb_handler(syscall_ebb_callee);
+       ebb_global_enable();
+
+       FAIL_IF(ebb_event_enable(&event));
+
+       mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+
+       while (ebb_state.stats.ebb_count < 20 && !mmcr0_mismatch)
+               FAIL_IF(core_busy_loop());
+
+       ebb_global_disable();
+       ebb_freeze_pmcs();
+
+       count_pmc(1, sample_period);
+
+       dump_ebb_state();
+
+       if (mmcr0_mismatch)
+               printf("Saw MMCR0 before 0x%lx after 0x%lx\n", before, after);
+
+       event_close(&event);
+
+       FAIL_IF(ebb_state.stats.ebb_count == 0);
+       FAIL_IF(mmcr0_mismatch);
+
+       return 0;
+}
+
+int pmae_handling(void)
+{
+       return eat_cpu(test_body);
+}
+
+int main(void)
+{
+       return test_harness(pmae_handling, "pmae_handling");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c b/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c
new file mode 100644 (file)
index 0000000..a503fa7
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ebb.h"
+
+
+/*
+ * Test that PMC5 & 6 are frozen (ie. don't overflow) when they are not being
+ * used. Tests the MMCR0_FC56 logic in the kernel.
+ */
+
+static int pmc56_overflowed;
+
+static void ebb_callee(void)
+{
+       uint64_t val;
+
+       val = mfspr(SPRN_BESCR);
+       if (!(val & BESCR_PMEO)) {
+               ebb_state.stats.spurious++;
+               goto out;
+       }
+
+       ebb_state.stats.ebb_count++;
+       count_pmc(2, sample_period);
+
+       val = mfspr(SPRN_PMC5);
+       if (val >= COUNTER_OVERFLOW)
+               pmc56_overflowed++;
+
+       count_pmc(5, COUNTER_OVERFLOW);
+
+       val = mfspr(SPRN_PMC6);
+       if (val >= COUNTER_OVERFLOW)
+               pmc56_overflowed++;
+
+       count_pmc(6, COUNTER_OVERFLOW);
+
+out:
+       reset_ebb();
+}
+
+int pmc56_overflow(void)
+{
+       struct event event;
+
+       /* Use PMC2 so we set PMCjCE, which enables PMC5/6 */
+       event_init(&event, 0x2001e);
+       event_leader_ebb_init(&event);
+
+       event.attr.exclude_kernel = 1;
+       event.attr.exclude_hv = 1;
+       event.attr.exclude_idle = 1;
+
+       FAIL_IF(event_open(&event));
+
+       setup_ebb_handler(ebb_callee);
+       ebb_global_enable();
+
+       FAIL_IF(ebb_event_enable(&event));
+
+       mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+       mtspr(SPRN_PMC5, 0);
+       mtspr(SPRN_PMC6, 0);
+
+       while (ebb_state.stats.ebb_count < 10)
+               FAIL_IF(core_busy_loop());
+
+       ebb_global_disable();
+       ebb_freeze_pmcs();
+
+       count_pmc(2, sample_period);
+
+       dump_ebb_state();
+
+       printf("PMC5/6 overflow %d\n", pmc56_overflowed);
+
+       event_close(&event);
+
+       FAIL_IF(ebb_state.stats.ebb_count == 0 || pmc56_overflowed != 0);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(pmc56_overflow, "pmc56_overflow");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/reg.h b/tools/testing/selftests/powerpc/pmu/ebb/reg.h
new file mode 100644 (file)
index 0000000..5921b0d
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#ifndef _SELFTESTS_POWERPC_REG_H
+#define _SELFTESTS_POWERPC_REG_H
+
+#define __stringify_1(x)        #x
+#define __stringify(x)          __stringify_1(x)
+
+#define mfspr(rn)       ({unsigned long rval; \
+                         asm volatile("mfspr %0," __stringify(rn) \
+                                 : "=r" (rval)); rval; })
+#define mtspr(rn, v)    asm volatile("mtspr " __stringify(rn) ",%0" : \
+                                    : "r" ((unsigned long)(v)) \
+                                    : "memory")
+
+#define mb()           asm volatile("sync" : : : "memory");
+
+#define SPRN_MMCR2     769
+#define SPRN_MMCRA     770
+#define SPRN_MMCR0     779
+#define   MMCR0_PMAO   0x00000080
+#define   MMCR0_PMAE   0x04000000
+#define   MMCR0_FC     0x80000000
+#define SPRN_EBBHR     804
+#define SPRN_EBBRR     805
+#define SPRN_BESCR     806     /* Branch event status & control register */
+#define SPRN_BESCRS    800     /* Branch event status & control set (1 bits set to 1) */
+#define SPRN_BESCRSU   801     /* Branch event status & control set upper */
+#define SPRN_BESCRR    802     /* Branch event status & control REset (1 bits set to 0) */
+#define SPRN_BESCRRU   803     /* Branch event status & control REset upper */
+
+#define BESCR_PMEO     0x1     /* PMU Event-based exception Occurred */
+#define BESCR_PME      (0x1ul << 32) /* PMU Event-based exception Enable */
+
+#define SPRN_PMC1      771
+#define SPRN_PMC2      772
+#define SPRN_PMC3      773
+#define SPRN_PMC4      774
+#define SPRN_PMC5      775
+#define SPRN_PMC6      776
+
+#define SPRN_SIAR      780
+#define SPRN_SDAR      781
+#define SPRN_SIER      768
+
+#endif /* _SELFTESTS_POWERPC_REG_H */
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/reg_access_test.c b/tools/testing/selftests/powerpc/pmu/ebb/reg_access_test.c
new file mode 100644 (file)
index 0000000..0cae66f
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ebb.h"
+#include "reg.h"
+
+
+/*
+ * Test basic access to the EBB regs, they should be user accessible with no
+ * kernel interaction required.
+ */
+int reg_access(void)
+{
+       uint64_t val, expected;
+
+       expected = 0x8000000100000000ull;
+       mtspr(SPRN_BESCR, expected);
+       val = mfspr(SPRN_BESCR);
+
+       FAIL_IF(val != expected);
+
+       expected = 0x0000000001000000ull;
+       mtspr(SPRN_EBBHR, expected);
+       val = mfspr(SPRN_EBBHR);
+
+       FAIL_IF(val != expected);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(reg_access, "reg_access");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/task_event_pinned_vs_ebb_test.c b/tools/testing/selftests/powerpc/pmu/ebb/task_event_pinned_vs_ebb_test.c
new file mode 100644 (file)
index 0000000..d56607e
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "ebb.h"
+
+
+/*
+ * Tests a pinned per-task event vs an EBB - in that order. The pinned per-task
+ * event should prevent the EBB event from being enabled.
+ */
+
+static int setup_child_event(struct event *event, pid_t child_pid)
+{
+       event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
+
+       event->attr.pinned = 1;
+
+       event->attr.exclude_kernel = 1;
+       event->attr.exclude_hv = 1;
+       event->attr.exclude_idle = 1;
+
+       FAIL_IF(event_open_with_pid(event, child_pid));
+       FAIL_IF(event_enable(event));
+
+       return 0;
+}
+
+int task_event_pinned_vs_ebb(void)
+{
+       union pipe read_pipe, write_pipe;
+       struct event event;
+       pid_t pid;
+       int rc;
+
+       FAIL_IF(pipe(read_pipe.fds) == -1);
+       FAIL_IF(pipe(write_pipe.fds) == -1);
+
+       pid = fork();
+       if (pid == 0) {
+               /* NB order of pipes looks reversed */
+               exit(ebb_child(write_pipe, read_pipe));
+       }
+
+       /* We setup the task event first */
+       rc = setup_child_event(&event, pid);
+       if (rc) {
+               kill_child_and_wait(pid);
+               return rc;
+       }
+
+       /* Signal the child to install its EBB event and wait */
+       if (sync_with_child(read_pipe, write_pipe))
+               /* If it fails, wait for it to exit */
+               goto wait;
+
+       /* Signal the child to run */
+       FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+wait:
+       /* We expect it to fail to read the event */
+       FAIL_IF(wait_for_child(pid) != 2);
+       FAIL_IF(event_disable(&event));
+       FAIL_IF(event_read(&event));
+
+       event_report(&event);
+
+       FAIL_IF(event.result.value == 0);
+       /*
+        * For reasons I don't understand enabled is usually just slightly
+        * lower than running. Would be good to confirm why.
+        */
+       FAIL_IF(event.result.enabled == 0);
+       FAIL_IF(event.result.running == 0);
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(task_event_pinned_vs_ebb, "task_event_pinned_vs_ebb");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/task_event_vs_ebb_test.c b/tools/testing/selftests/powerpc/pmu/ebb/task_event_vs_ebb_test.c
new file mode 100644 (file)
index 0000000..eba3219
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "ebb.h"
+
+
+/*
+ * Tests a per-task event vs an EBB - in that order. The EBB should push the
+ * per-task event off the PMU.
+ */
+
+static int setup_child_event(struct event *event, pid_t child_pid)
+{
+       event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL");
+
+       event->attr.exclude_kernel = 1;
+       event->attr.exclude_hv = 1;
+       event->attr.exclude_idle = 1;
+
+       FAIL_IF(event_open_with_pid(event, child_pid));
+       FAIL_IF(event_enable(event));
+
+       return 0;
+}
+
+int task_event_vs_ebb(void)
+{
+       union pipe read_pipe, write_pipe;
+       struct event event;
+       pid_t pid;
+       int rc;
+
+       FAIL_IF(pipe(read_pipe.fds) == -1);
+       FAIL_IF(pipe(write_pipe.fds) == -1);
+
+       pid = fork();
+       if (pid == 0) {
+               /* NB order of pipes looks reversed */
+               exit(ebb_child(write_pipe, read_pipe));
+       }
+
+       /* We setup the task event first */
+       rc = setup_child_event(&event, pid);
+       if (rc) {
+               kill_child_and_wait(pid);
+               return rc;
+       }
+
+       /* Signal the child to install its EBB event and wait */
+       if (sync_with_child(read_pipe, write_pipe))
+               /* If it fails, wait for it to exit */
+               goto wait;
+
+       /* Signal the child to run */
+       FAIL_IF(sync_with_child(read_pipe, write_pipe));
+
+wait:
+       /* The EBB event should push the task event off so the child should succeed */
+       FAIL_IF(wait_for_child(pid));
+       FAIL_IF(event_disable(&event));
+       FAIL_IF(event_read(&event));
+
+       event_report(&event);
+
+       /* The task event may have run, or not so we can't assert anything about it */
+
+       return 0;
+}
+
+int main(void)
+{
+       return test_harness(task_event_vs_ebb, "task_event_vs_ebb");
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/trace.c b/tools/testing/selftests/powerpc/pmu/ebb/trace.c
new file mode 100644 (file)
index 0000000..251e66a
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include "trace.h"
+
+
+struct trace_buffer *trace_buffer_allocate(u64 size)
+{
+       struct trace_buffer *tb;
+
+       if (size < sizeof(*tb)) {
+               fprintf(stderr, "Error: trace buffer too small\n");
+               return NULL;
+       }
+
+       tb = mmap(NULL, size, PROT_READ | PROT_WRITE,
+                 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+       if (tb == MAP_FAILED) {
+               perror("mmap");
+               return NULL;
+       }
+
+       tb->size = size;
+       tb->tail = tb->data;
+       tb->overflow = false;
+
+       return tb;
+}
+
+static bool trace_check_bounds(struct trace_buffer *tb, void *p)
+{
+       return p < ((void *)tb + tb->size);
+}
+
+static bool trace_check_alloc(struct trace_buffer *tb, void *p)
+{
+       /*
+        * If we ever overflowed don't allow any more input. This prevents us
+        * from dropping a large item and then later logging a small one. The
+        * buffer should just stop when overflow happened, not be patchy. If
+        * you're overflowing, make your buffer bigger.
+        */
+       if (tb->overflow)
+               return false;
+
+       if (!trace_check_bounds(tb, p)) {
+               tb->overflow = true;
+               return false;
+       }
+
+       return true;
+}
+
+static void *trace_alloc(struct trace_buffer *tb, int bytes)
+{
+       void *p, *newtail;
+
+       p = tb->tail;
+       newtail = tb->tail + bytes;
+       if (!trace_check_alloc(tb, newtail))
+               return NULL;
+
+       tb->tail = newtail;
+
+       return p;
+}
+
+static struct trace_entry *trace_alloc_entry(struct trace_buffer *tb, int payload_size)
+{
+       struct trace_entry *e;
+
+       e = trace_alloc(tb, sizeof(*e) + payload_size);
+       if (e)
+               e->length = payload_size;
+
+       return e;
+}
+
+int trace_log_reg(struct trace_buffer *tb, u64 reg, u64 value)
+{
+       struct trace_entry *e;
+       u64 *p;
+
+       e = trace_alloc_entry(tb, sizeof(reg) + sizeof(value));
+       if (!e)
+               return -ENOSPC;
+
+       e->type = TRACE_TYPE_REG;
+       p = (u64 *)e->data;
+       *p++ = reg;
+       *p++ = value;
+
+       return 0;
+}
+
+int trace_log_counter(struct trace_buffer *tb, u64 value)
+{
+       struct trace_entry *e;
+       u64 *p;
+
+       e = trace_alloc_entry(tb, sizeof(value));
+       if (!e)
+               return -ENOSPC;
+
+       e->type = TRACE_TYPE_COUNTER;
+       p = (u64 *)e->data;
+       *p++ = value;
+
+       return 0;
+}
+
+int trace_log_string(struct trace_buffer *tb, char *str)
+{
+       struct trace_entry *e;
+       char *p;
+       int len;
+
+       len = strlen(str);
+
+       /* We NULL terminate to make printing easier */
+       e = trace_alloc_entry(tb, len + 1);
+       if (!e)
+               return -ENOSPC;
+
+       e->type = TRACE_TYPE_STRING;
+       p = (char *)e->data;
+       memcpy(p, str, len);
+       p += len;
+       *p = '\0';
+
+       return 0;
+}
+
+int trace_log_indent(struct trace_buffer *tb)
+{
+       struct trace_entry *e;
+
+       e = trace_alloc_entry(tb, 0);
+       if (!e)
+               return -ENOSPC;
+
+       e->type = TRACE_TYPE_INDENT;
+
+       return 0;
+}
+
+int trace_log_outdent(struct trace_buffer *tb)
+{
+       struct trace_entry *e;
+
+       e = trace_alloc_entry(tb, 0);
+       if (!e)
+               return -ENOSPC;
+
+       e->type = TRACE_TYPE_OUTDENT;
+
+       return 0;
+}
+
+static void trace_print_header(int seq, int prefix)
+{
+       printf("%*s[%d]: ", prefix, "", seq);
+}
+
+static char *trace_decode_reg(int reg)
+{
+       switch (reg) {
+               case 769: return "SPRN_MMCR2"; break;
+               case 770: return "SPRN_MMCRA"; break;
+               case 779: return "SPRN_MMCR0"; break;
+               case 804: return "SPRN_EBBHR"; break;
+               case 805: return "SPRN_EBBRR"; break;
+               case 806: return "SPRN_BESCR"; break;
+               case 800: return "SPRN_BESCRS"; break;
+               case 801: return "SPRN_BESCRSU"; break;
+               case 802: return "SPRN_BESCRR"; break;
+               case 803: return "SPRN_BESCRRU"; break;
+               case 771: return "SPRN_PMC1"; break;
+               case 772: return "SPRN_PMC2"; break;
+               case 773: return "SPRN_PMC3"; break;
+               case 774: return "SPRN_PMC4"; break;
+               case 775: return "SPRN_PMC5"; break;
+               case 776: return "SPRN_PMC6"; break;
+               case 780: return "SPRN_SIAR"; break;
+               case 781: return "SPRN_SDAR"; break;
+               case 768: return "SPRN_SIER"; break;
+       }
+
+       return NULL;
+}
+
+static void trace_print_reg(struct trace_entry *e)
+{
+       u64 *p, *reg, *value;
+       char *name;
+
+       p = (u64 *)e->data;
+       reg = p++;
+       value = p;
+
+       name = trace_decode_reg(*reg);
+       if (name)
+               printf("register %-10s = 0x%016llx\n", name, *value);
+       else
+               printf("register %lld = 0x%016llx\n", *reg, *value);
+}
+
+static void trace_print_counter(struct trace_entry *e)
+{
+       u64 *value;
+
+       value = (u64 *)e->data;
+       printf("counter = %lld\n", *value);
+}
+
+static void trace_print_string(struct trace_entry *e)
+{
+       char *str;
+
+       str = (char *)e->data;
+       puts(str);
+}
+
+#define BASE_PREFIX    2
+#define PREFIX_DELTA   8
+
+static void trace_print_entry(struct trace_entry *e, int seq, int *prefix)
+{
+       switch (e->type) {
+       case TRACE_TYPE_REG:
+               trace_print_header(seq, *prefix);
+               trace_print_reg(e);
+               break;
+       case TRACE_TYPE_COUNTER:
+               trace_print_header(seq, *prefix);
+               trace_print_counter(e);
+               break;
+       case TRACE_TYPE_STRING:
+               trace_print_header(seq, *prefix);
+               trace_print_string(e);
+               break;
+       case TRACE_TYPE_INDENT:
+               trace_print_header(seq, *prefix);
+               puts("{");
+               *prefix += PREFIX_DELTA;
+               break;
+       case TRACE_TYPE_OUTDENT:
+               *prefix -= PREFIX_DELTA;
+               if (*prefix < BASE_PREFIX)
+                       *prefix = BASE_PREFIX;
+               trace_print_header(seq, *prefix);
+               puts("}");
+               break;
+       default:
+               trace_print_header(seq, *prefix);
+               printf("entry @ %p type %d\n", e, e->type);
+               break;
+       }
+}
+
+void trace_buffer_print(struct trace_buffer *tb)
+{
+       struct trace_entry *e;
+       int i, prefix;
+       void *p;
+
+       printf("Trace buffer dump:\n");
+       printf("  address  %p \n", tb);
+       printf("  tail     %p\n", tb->tail);
+       printf("  size     %llu\n", tb->size);
+       printf("  overflow %s\n", tb->overflow ? "TRUE" : "false");
+       printf("  Content:\n");
+
+       p = tb->data;
+
+       i = 0;
+       prefix = BASE_PREFIX;
+
+       while (trace_check_bounds(tb, p) && p < tb->tail) {
+               e = p;
+
+               trace_print_entry(e, i, &prefix);
+
+               i++;
+               p = (void *)e + sizeof(*e) + e->length;
+       }
+}
+
+void trace_print_location(struct trace_buffer *tb)
+{
+       printf("Trace buffer 0x%llx bytes @ %p\n", tb->size, tb);
+}
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/trace.h b/tools/testing/selftests/powerpc/pmu/ebb/trace.h
new file mode 100644 (file)
index 0000000..926458e
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#ifndef _SELFTESTS_POWERPC_PMU_EBB_TRACE_H
+#define _SELFTESTS_POWERPC_PMU_EBB_TRACE_H
+
+#include "utils.h"
+
+#define TRACE_TYPE_REG         1
+#define TRACE_TYPE_COUNTER     2
+#define TRACE_TYPE_STRING      3
+#define TRACE_TYPE_INDENT      4
+#define TRACE_TYPE_OUTDENT     5
+
+struct trace_entry
+{
+       u8 type;
+       u8 length;
+       u8 data[0];
+};
+
+struct trace_buffer
+{
+       u64  size;
+       bool overflow;
+       void *tail;
+       u8   data[0];
+};
+
+struct trace_buffer *trace_buffer_allocate(u64 size);
+int trace_log_reg(struct trace_buffer *tb, u64 reg, u64 value);
+int trace_log_counter(struct trace_buffer *tb, u64 value);
+int trace_log_string(struct trace_buffer *tb, char *str);
+int trace_log_indent(struct trace_buffer *tb);
+int trace_log_outdent(struct trace_buffer *tb);
+void trace_buffer_print(struct trace_buffer *tb);
+void trace_print_location(struct trace_buffer *tb);
+
+#endif /* _SELFTESTS_POWERPC_PMU_EBB_TRACE_H */
index 2b2d11df24508cfb61ea8e0920f28c2f3ba0ac37..184b36807d48c197d4f6471c58bd74f62c9fd4e2 100644 (file)
@@ -39,7 +39,13 @@ void event_init_named(struct event *e, u64 config, char *name)
        event_init_opts(e, config, PERF_TYPE_RAW, name);
 }
 
+void event_init(struct event *e, u64 config)
+{
+       event_init_opts(e, config, PERF_TYPE_RAW, "event");
+}
+
 #define PERF_CURRENT_PID       0
+#define PERF_NO_PID            -1
 #define PERF_NO_CPU            -1
 #define PERF_NO_GROUP          -1
 
@@ -59,6 +65,16 @@ int event_open_with_group(struct event *e, int group_fd)
        return event_open_with_options(e, PERF_CURRENT_PID, PERF_NO_CPU, group_fd);
 }
 
+int event_open_with_pid(struct event *e, pid_t pid)
+{
+       return event_open_with_options(e, pid, PERF_NO_CPU, PERF_NO_GROUP);
+}
+
+int event_open_with_cpu(struct event *e, int cpu)
+{
+       return event_open_with_options(e, PERF_NO_PID, cpu, PERF_NO_GROUP);
+}
+
 int event_open(struct event *e)
 {
        return event_open_with_options(e, PERF_CURRENT_PID, PERF_NO_CPU, PERF_NO_GROUP);
@@ -69,6 +85,16 @@ void event_close(struct event *e)
        close(e->fd);
 }
 
+int event_enable(struct event *e)
+{
+       return ioctl(e->fd, PERF_EVENT_IOC_ENABLE);
+}
+
+int event_disable(struct event *e)
+{
+       return ioctl(e->fd, PERF_EVENT_IOC_DISABLE);
+}
+
 int event_reset(struct event *e)
 {
        return ioctl(e->fd, PERF_EVENT_IOC_RESET);
index e6993192ff34b4726b76ec84440fa18d5a91f31e..a0ea6b1eef73464fa0af44c091068c7ea231a66d 100644 (file)
@@ -29,8 +29,12 @@ void event_init_named(struct event *e, u64 config, char *name);
 void event_init_opts(struct event *e, u64 config, int type, char *name);
 int event_open_with_options(struct event *e, pid_t pid, int cpu, int group_fd);
 int event_open_with_group(struct event *e, int group_fd);
+int event_open_with_pid(struct event *e, pid_t pid);
+int event_open_with_cpu(struct event *e, int cpu);
 int event_open(struct event *e);
 void event_close(struct event *e);
+int event_enable(struct event *e);
+int event_disable(struct event *e);
 int event_reset(struct event *e);
 int event_read(struct event *e);
 void event_report_justified(struct event *e, int name_width, int result_width);
diff --git a/tools/testing/selftests/powerpc/pmu/lib.c b/tools/testing/selftests/powerpc/pmu/lib.c
new file mode 100644 (file)
index 0000000..0f6a473
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#define _GNU_SOURCE    /* For CPU_ZERO etc. */
+
+#include <errno.h>
+#include <sched.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+
+#include "utils.h"
+#include "lib.h"
+
+
+int pick_online_cpu(void)
+{
+       cpu_set_t mask;
+       int cpu;
+
+       CPU_ZERO(&mask);
+
+       if (sched_getaffinity(0, sizeof(mask), &mask)) {
+               perror("sched_getaffinity");
+               return -1;
+       }
+
+       /* We prefer a primary thread, but skip 0 */
+       for (cpu = 8; cpu < CPU_SETSIZE; cpu += 8)
+               if (CPU_ISSET(cpu, &mask))
+                       return cpu;
+
+       /* Search for anything, but in reverse */
+       for (cpu = CPU_SETSIZE - 1; cpu >= 0; cpu--)
+               if (CPU_ISSET(cpu, &mask))
+                       return cpu;
+
+       printf("No cpus in affinity mask?!\n");
+       return -1;
+}
+
+int bind_to_cpu(int cpu)
+{
+       cpu_set_t mask;
+
+       printf("Binding to cpu %d\n", cpu);
+
+       CPU_ZERO(&mask);
+       CPU_SET(cpu, &mask);
+
+       return sched_setaffinity(0, sizeof(mask), &mask);
+}
+
+#define PARENT_TOKEN   0xAA
+#define CHILD_TOKEN    0x55
+
+int sync_with_child(union pipe read_pipe, union pipe write_pipe)
+{
+       char c = PARENT_TOKEN;
+
+       FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
+       FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1);
+       if (c != CHILD_TOKEN) /* sometimes expected */
+               return 1;
+
+       return 0;
+}
+
+int wait_for_parent(union pipe read_pipe)
+{
+       char c;
+
+       FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1);
+       FAIL_IF(c != PARENT_TOKEN);
+
+       return 0;
+}
+
+int notify_parent(union pipe write_pipe)
+{
+       char c = CHILD_TOKEN;
+
+       FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
+
+       return 0;
+}
+
+int notify_parent_of_error(union pipe write_pipe)
+{
+       char c = ~CHILD_TOKEN;
+
+       FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
+
+       return 0;
+}
+
+int wait_for_child(pid_t child_pid)
+{
+       int rc;
+
+       if (waitpid(child_pid, &rc, 0) == -1) {
+               perror("waitpid");
+               return 1;
+       }
+
+       if (WIFEXITED(rc))
+               rc = WEXITSTATUS(rc);
+       else
+               rc = 1; /* Signal or other */
+
+       return rc;
+}
+
+int kill_child_and_wait(pid_t child_pid)
+{
+       kill(child_pid, SIGTERM);
+
+       return wait_for_child(child_pid);
+}
+
+static int eat_cpu_child(union pipe read_pipe, union pipe write_pipe)
+{
+       volatile int i = 0;
+
+       /*
+        * We are just here to eat cpu and die. So make sure we can be killed,
+        * and also don't do any custom SIGTERM handling.
+        */
+       signal(SIGTERM, SIG_DFL);
+
+       notify_parent(write_pipe);
+       wait_for_parent(read_pipe);
+
+       /* Soak up cpu forever */
+       while (1) i++;
+
+       return 0;
+}
+
+pid_t eat_cpu(int (test_function)(void))
+{
+       union pipe read_pipe, write_pipe;
+       int cpu, rc;
+       pid_t pid;
+
+       cpu = pick_online_cpu();
+       FAIL_IF(cpu < 0);
+       FAIL_IF(bind_to_cpu(cpu));
+
+       if (pipe(read_pipe.fds) == -1)
+               return -1;
+
+       if (pipe(write_pipe.fds) == -1)
+               return -1;
+
+       pid = fork();
+       if (pid == 0)
+               exit(eat_cpu_child(write_pipe, read_pipe));
+
+       if (sync_with_child(read_pipe, write_pipe)) {
+               rc = -1;
+               goto out;
+       }
+
+       printf("main test running as pid %d\n", getpid());
+
+       rc = test_function();
+out:
+       kill(pid, SIGKILL);
+
+       return rc;
+}
+
+struct addr_range libc, vdso;
+
+int parse_proc_maps(void)
+{
+       char execute, name[128];
+       uint64_t start, end;
+       FILE *f;
+       int rc;
+
+       f = fopen("/proc/self/maps", "r");
+       if (!f) {
+               perror("fopen");
+               return -1;
+       }
+
+       do {
+               /* This skips line with no executable which is what we want */
+               rc = fscanf(f, "%lx-%lx %*c%*c%c%*c %*x %*d:%*d %*d %127s\n",
+                           &start, &end, &execute, name);
+               if (rc <= 0)
+                       break;
+
+               if (execute != 'x')
+                       continue;
+
+               if (strstr(name, "libc")) {
+                       libc.first = start;
+                       libc.last = end - 1;
+               } else if (strstr(name, "[vdso]")) {
+                       vdso.first = start;
+                       vdso.last = end - 1;
+               }
+       } while(1);
+
+       fclose(f);
+
+       return 0;
+}
+
+#define PARANOID_PATH  "/proc/sys/kernel/perf_event_paranoid"
+
+bool require_paranoia_below(int level)
+{
+       unsigned long current;
+       char *end, buf[16];
+       FILE *f;
+       int rc;
+
+       rc = -1;
+
+       f = fopen(PARANOID_PATH, "r");
+       if (!f) {
+               perror("fopen");
+               goto out;
+       }
+
+       if (!fgets(buf, sizeof(buf), f)) {
+               printf("Couldn't read " PARANOID_PATH "?\n");
+               goto out_close;
+       }
+
+       current = strtoul(buf, &end, 10);
+
+       if (end == buf) {
+               printf("Couldn't parse " PARANOID_PATH "?\n");
+               goto out_close;
+       }
+
+       if (current >= level)
+               goto out;
+
+       rc = 0;
+out_close:
+       fclose(f);
+out:
+       return rc;
+}
diff --git a/tools/testing/selftests/powerpc/pmu/lib.h b/tools/testing/selftests/powerpc/pmu/lib.h
new file mode 100644 (file)
index 0000000..ca5d72a
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014, Michael Ellerman, IBM Corp.
+ * Licensed under GPLv2.
+ */
+
+#ifndef __SELFTESTS_POWERPC_PMU_LIB_H
+#define __SELFTESTS_POWERPC_PMU_LIB_H
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+union pipe {
+       struct {
+               int read_fd;
+               int write_fd;
+       };
+       int fds[2];
+};
+
+extern int pick_online_cpu(void);
+extern int bind_to_cpu(int cpu);
+extern int kill_child_and_wait(pid_t child_pid);
+extern int wait_for_child(pid_t child_pid);
+extern int sync_with_child(union pipe read_pipe, union pipe write_pipe);
+extern int wait_for_parent(union pipe read_pipe);
+extern int notify_parent(union pipe write_pipe);
+extern int notify_parent_of_error(union pipe write_pipe);
+extern pid_t eat_cpu(int (test_function)(void));
+extern bool require_paranoia_below(int level);
+
+struct addr_range {
+       uint64_t first, last;
+};
+
+extern struct addr_range libc, vdso;
+
+int parse_proc_maps(void);
+
+#endif /* __SELFTESTS_POWERPC_PMU_LIB_H */
index 8820e3df144460e3dc44d60af83c25443755bd4b..20c1f0876c4772492f34b6f4215257586f73aff9 100644 (file)
@@ -3,44 +3,41 @@
  * Licensed under GPLv2.
  */
 
+#include <ppc-asm.h>
+
        .text
 
-       .global thirty_two_instruction_loop
-       .type .thirty_two_instruction_loop,@function
-       .section ".opd","aw",@progbits
-thirty_two_instruction_loop:
-       .quad .thirty_two_instruction_loop, .TOC.@tocbase, 0
-       .previous
-.thirty_two_instruction_loop:
-       cmpwi   %r3,0
+FUNC_START(thirty_two_instruction_loop)
+       cmpdi   r3,0
        beqlr
-       addi    %r4,%r3,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1
-       addi    %r4,%r4,1       # 28 addi's
-       subi    %r3,%r3,1
-       b       .thirty_two_instruction_loop
+       addi    r4,r3,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1
+       addi    r4,r4,1 # 28 addi's
+       subi    r3,r3,1
+       b       FUNC_NAME(thirty_two_instruction_loop)
+FUNC_END(thirty_two_instruction_loop)
index 98a22920792d812b931fe7c5e7b86af58ba4ccec..9c6c4e901ab617671c669315bcc62280362b9104 100644 (file)
@@ -26,6 +26,11 @@ static inline void test_error(char *name)
        printf("error: %s\n", name);
 }
 
+static inline void test_skip(char *name)
+{
+       printf("skip: %s\n", name);
+}
+
 static inline void test_success(char *name)
 {
        printf("success: %s\n", name);
diff --git a/tools/testing/selftests/powerpc/tm/Makefile b/tools/testing/selftests/powerpc/tm/Makefile
new file mode 100644 (file)
index 0000000..51267f4
--- /dev/null
@@ -0,0 +1,15 @@
+PROGS := tm-resched-dscr
+
+all: $(PROGS)
+
+$(PROGS):
+
+run_tests: all
+       @-for PROG in $(PROGS); do \
+               ./$$PROG; \
+       done;
+
+clean:
+       rm -f $(PROGS) *.o
+
+.PHONY: all run_tests clean
diff --git a/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c b/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c
new file mode 100644 (file)
index 0000000..ee98e38
--- /dev/null
@@ -0,0 +1,90 @@
+/* Test context switching to see if the DSCR SPR is correctly preserved
+ * when within a transaction.
+ *
+ * Note: We assume that the DSCR has been left at the default value (0)
+ * for all CPUs.
+ *
+ * Method:
+ *
+ * Set a value into the DSCR.
+ *
+ * Start a transaction, and suspend it (*).
+ *
+ * Hard loop checking to see if the transaction has become doomed.
+ *
+ * Now that we *may* have been preempted, record the DSCR and TEXASR SPRS.
+ *
+ * If the abort was because of a context switch, check the DSCR value.
+ * Otherwise, try again.
+ *
+ * (*) If the transaction is not suspended we can't see the problem because
+ * the transaction abort handler will restore the DSCR to it's checkpointed
+ * value before we regain control.
+ */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <asm/tm.h>
+
+#define TBEGIN          ".long 0x7C00051D ;"
+#define TEND            ".long 0x7C00055D ;"
+#define TCHECK          ".long 0x7C00059C ;"
+#define TSUSPEND        ".long 0x7C0005DD ;"
+#define TRESUME         ".long 0x7C2005DD ;"
+#define SPRN_TEXASR     0x82
+#define SPRN_DSCR       0x03
+
+int main(void) {
+       uint64_t rv, dscr1 = 1, dscr2, texasr;
+
+       printf("Check DSCR TM context switch: ");
+       fflush(stdout);
+       for (;;) {
+               rv = 1;
+               asm __volatile__ (
+                       /* set a known value into the DSCR */
+                       "ld      3, %[dscr1];"
+                       "mtspr   %[sprn_dscr], 3;"
+
+                       /* start and suspend a transaction */
+                       TBEGIN
+                       "beq     1f;"
+                       TSUSPEND
+
+                       /* hard loop until the transaction becomes doomed */
+                       "2: ;"
+                       TCHECK
+                       "bc      4, 0, 2b;"
+
+                       /* record DSCR and TEXASR */
+                       "mfspr   3, %[sprn_dscr];"
+                       "std     3, %[dscr2];"
+                       "mfspr   3, %[sprn_texasr];"
+                       "std     3, %[texasr];"
+
+                       TRESUME
+                       TEND
+                       "li      %[rv], 0;"
+                       "1: ;"
+                       : [rv]"=r"(rv), [dscr2]"=m"(dscr2), [texasr]"=m"(texasr)
+                       : [dscr1]"m"(dscr1)
+                       , [sprn_dscr]"i"(SPRN_DSCR), [sprn_texasr]"i"(SPRN_TEXASR)
+                       : "memory", "r3"
+               );
+               assert(rv); /* make sure the transaction aborted */
+               if ((texasr >> 56) != TM_CAUSE_RESCHED) {
+                       putchar('.');
+                       fflush(stdout);
+                       continue;
+               }
+               if (dscr2 != dscr1) {
+                       printf(" FAIL\n");
+                       exit(EXIT_FAILURE);
+               } else {
+                       printf(" OK\n");
+                       exit(EXIT_SUCCESS);
+               }
+       }
+}
index 0de064406dabf5d7589f9cc094f133e12b1798a1..a93777ae06846a4df47e5fbb5241af92cb6d6928 100644 (file)
@@ -31,6 +31,18 @@ do {                                                         \
        }                                                       \
 } while (0)
 
+/* The test harness uses this, yes it's gross */
+#define MAGIC_SKIP_RETURN_VALUE        99
+
+#define SKIP_IF(x)                                             \
+do {                                                           \
+       if ((x)) {                                              \
+               fprintf(stderr,                                 \
+               "[SKIP] Test skipped on line %d\n", __LINE__);  \
+               return MAGIC_SKIP_RETURN_VALUE;                 \
+       }                                                       \
+} while (0)
+
 #define _str(s) #s
 #define str(s) _str(s)
 
index c86be0f983db706c81cfe60aedfb77f4a315e20f..4b6c01b477f9cf86df4a69504de6940c5f6bebbf 100644 (file)
@@ -1714,11 +1714,11 @@ void kvm_vcpu_kick(struct kvm_vcpu *vcpu)
 EXPORT_SYMBOL_GPL(kvm_vcpu_kick);
 #endif /* !CONFIG_S390 */
 
-bool kvm_vcpu_yield_to(struct kvm_vcpu *target)
+int kvm_vcpu_yield_to(struct kvm_vcpu *target)
 {
        struct pid *pid;
        struct task_struct *task = NULL;
-       bool ret = false;
+       int ret = 0;
 
        rcu_read_lock();
        pid = rcu_dereference(target->pid);