Merge branch 'for-davem' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
authorDavid S. Miller <davem@davemloft.net>
Mon, 13 Apr 2015 22:18:05 +0000 (18:18 -0400)
committerDavid S. Miller <davem@davemloft.net>
Mon, 13 Apr 2015 22:18:05 +0000 (18:18 -0400)
Al Viro says:

====================
netdev-related stuff in vfs.git

There are several commits sitting in vfs.git that probably ought to go in
via net-next.git.  First of all, there's merge with vfs.git#iocb - that's
Christoph's aio rework, which has triggered conflicts with the ->sendmsg()
and ->recvmsg() patches a while ago.  It's not so much Christoph's stuff
that ought to be in net-next, as (pretty simple) conflict resolution on merge.
The next chunk is switch to {compat_,}import_iovec/import_single_range - new
safer primitives for initializing iov_iter.  The primitives themselves come
from vfs/git#iov_iter (and they are used quite a lot in vfs part of queue),
conversion of net/socket.c syscalls belongs in net-next, IMO.  Next there's
afs and rxrpc stuff from dhowells.  And then there's sanitizing kernel_sendmsg
et.al.  + missing inlined helper for "how much data is left in msg->msg_iter" -
this stuff is used in e.g.  cifs stuff, but it belongs in net-next.

That pile is pullable from
git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git for-davem

I'll post the individual patches in there in followups; could you take a look
and tell if everything in there is OK with you?
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
302 files changed:
Documentation/devicetree/bindings/net/nfc/nxp-nci.txt [new file with mode: 0644]
Documentation/devicetree/bindings/net/stmmac.txt
Documentation/networking/igb.txt
Documentation/networking/ixgb.txt
Documentation/networking/ixgbe.txt
MAINTAINERS
drivers/bluetooth/Kconfig
drivers/bluetooth/Makefile
drivers/bluetooth/btbcm.c [new file with mode: 0644]
drivers/bluetooth/btbcm.h [new file with mode: 0644]
drivers/bluetooth/btintel.c [new file with mode: 0644]
drivers/bluetooth/btintel.h [new file with mode: 0644]
drivers/bluetooth/btusb.c
drivers/bluetooth/hci_ath.c
drivers/bluetooth/hci_bcm.c [new file with mode: 0644]
drivers/bluetooth/hci_bcsp.c
drivers/bluetooth/hci_h4.c
drivers/bluetooth/hci_h5.c
drivers/bluetooth/hci_intel.c [new file with mode: 0644]
drivers/bluetooth/hci_ldisc.c
drivers/bluetooth/hci_ll.c
drivers/bluetooth/hci_uart.h
drivers/net/bonding/bond_3ad.c
drivers/net/ethernet/amd/xgbe/xgbe-dev.c
drivers/net/ethernet/amd/xgbe/xgbe-drv.c
drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
drivers/net/ethernet/amd/xgbe/xgbe-main.c
drivers/net/ethernet/amd/xgbe/xgbe.h
drivers/net/ethernet/cadence/macb.c
drivers/net/ethernet/chelsio/cxgb3/sge.c
drivers/net/ethernet/chelsio/cxgb4/Makefile
drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c [new file with mode: 0644]
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
drivers/net/ethernet/chelsio/cxgb4/sge.c
drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
drivers/net/ethernet/chelsio/cxgb4vf/sge.c
drivers/net/ethernet/intel/e100.c
drivers/net/ethernet/intel/e1000/e1000_main.c
drivers/net/ethernet/intel/e1000e/e1000.h
drivers/net/ethernet/intel/e1000e/netdev.c
drivers/net/ethernet/intel/i40e/i40e_txrx.c
drivers/net/ethernet/intel/i40evf/i40e_txrx.c
drivers/net/ethernet/intel/ixgbe/ixgbe.h
drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c
drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.h
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h
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_x550.c
drivers/net/ethernet/intel/ixgbevf/ethtool.c
drivers/net/ethernet/intel/ixgbevf/ixgbevf.h
drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
drivers/net/ethernet/intel/ixgbevf/mbx.h
drivers/net/ethernet/intel/ixgbevf/vf.c
drivers/net/ethernet/intel/ixgbevf/vf.h
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/mlx5/core/eq.c
drivers/net/ethernet/neterion/s2io.c
drivers/net/ethernet/stmicro/stmmac/stmmac.h
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ethernet/sun/sungem.c
drivers/net/ethernet/sun/sunhme.c
drivers/net/ethernet/sun/sunvnet.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/vxlan.c
drivers/net/wan/cosa.c
drivers/net/wan/lmc/lmc_main.c
drivers/net/wireless/ath/ath9k/gpio.c
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/reg.h
drivers/net/wireless/ath/ath9k/wmi.c
drivers/net/wireless/ath/ath9k/wmi.h
drivers/net/wireless/b43/dma.c
drivers/net/wireless/b43legacy/dma.c
drivers/net/wireless/b43legacy/rfkill.c
drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c
drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c
drivers/net/wireless/iwlwifi/iwl-8000.c
drivers/net/wireless/iwlwifi/iwl-config.h
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/iwlwifi/iwl-drv.h
drivers/net/wireless/iwlwifi/iwl-eeprom-read.c
drivers/net/wireless/iwlwifi/iwl-fh.h
drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
drivers/net/wireless/iwlwifi/iwl-fw-file.h
drivers/net/wireless/iwlwifi/iwl-io.c
drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
drivers/net/wireless/iwlwifi/iwl-nvm-parse.h
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/d3.c
drivers/net/wireless/iwlwifi/mvm/debugfs.c
drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h
drivers/net/wireless/iwlwifi/mvm/fw-api-scan.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/rs.c
drivers/net/wireless/iwlwifi/mvm/rx.c
drivers/net/wireless/iwlwifi/mvm/scan.c
drivers/net/wireless/iwlwifi/mvm/sta.c
drivers/net/wireless/iwlwifi/mvm/sta.h
drivers/net/wireless/iwlwifi/mvm/time-event.c
drivers/net/wireless/iwlwifi/mvm/time-event.h
drivers/net/wireless/iwlwifi/mvm/tx.c
drivers/net/wireless/iwlwifi/mvm/utils.c
drivers/net/wireless/iwlwifi/pcie/rx.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/rtlwifi/base.c
drivers/net/wireless/rtlwifi/base.h
drivers/net/wireless/rtlwifi/pci.c
drivers/net/wireless/rtlwifi/rc.c
drivers/net/wireless/ti/wl1251/main.c
drivers/nfc/Kconfig
drivers/nfc/Makefile
drivers/nfc/microread/i2c.c
drivers/nfc/nfcmrvl/main.c
drivers/nfc/nfcmrvl/usb.c
drivers/nfc/nxp-nci/Kconfig [new file with mode: 0644]
drivers/nfc/nxp-nci/Makefile [new file with mode: 0644]
drivers/nfc/nxp-nci/core.c [new file with mode: 0644]
drivers/nfc/nxp-nci/firmware.c [new file with mode: 0644]
drivers/nfc/nxp-nci/i2c.c [new file with mode: 0644]
drivers/nfc/nxp-nci/nxp-nci.h [new file with mode: 0644]
drivers/nfc/pn533.c
drivers/nfc/pn544/i2c.c
drivers/nfc/port100.c
drivers/nfc/st21nfca/st21nfca.c
drivers/nfc/st21nfca/st21nfca_se.c
drivers/nfc/st21nfcb/i2c.c
drivers/nfc/st21nfcb/ndlc.c
drivers/nfc/st21nfcb/st21nfcb_se.c
drivers/ssb/driver_pcicore.c
include/linux/if_link.h
include/linux/netdevice.h
include/linux/netfilter.h
include/linux/netfilter_bridge.h
include/linux/netlink.h
include/linux/platform_data/nxp-nci.h [new file with mode: 0644]
include/linux/rtnetlink.h
include/linux/skbuff.h
include/linux/tcp.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/cfg80211.h
include/net/dn_neigh.h
include/net/inet_timewait_sock.h
include/net/ip.h
include/net/ip6_route.h
include/net/ip6_tunnel.h
include/net/ipv6.h
include/net/mac80211.h
include/net/mac802154.h
include/net/netfilter/nf_tables.h
include/net/netfilter/nf_tables_core.h
include/net/nfc/hci.h
include/net/nfc/nci_core.h
include/net/nfc/nfc.h
include/net/rtnetlink.h
include/net/tcp.h
include/net/udp_tunnel.h
include/net/vxlan.h
include/net/xfrm.h
include/uapi/linux/fou.h
include/uapi/linux/if_link.h
include/uapi/linux/netfilter/nf_tables.h
include/uapi/linux/rtnetlink.h
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/mgmt.c
net/bridge/br_forward.c
net/bridge/br_input.c
net/bridge/br_multicast.c
net/bridge/br_netfilter.c
net/bridge/br_private.h
net/bridge/br_stp_bpdu.c
net/bridge/netfilter/nft_reject_bridge.c
net/core/dev.c
net/core/net_namespace.c
net/core/rtnetlink.c
net/core/skbuff.c
net/core/sock.c
net/dccp/minisocks.c
net/decnet/dn_neigh.c
net/decnet/dn_nsp_in.c
net/decnet/dn_route.c
net/ipv4/arp.c
net/ipv4/fou.c
net/ipv4/geneve.c
net/ipv4/inet_diag.c
net/ipv4/inet_hashtables.c
net/ipv4/inet_timewait_sock.c
net/ipv4/ip_forward.c
net/ipv4/ip_input.c
net/ipv4/ip_output.c
net/ipv4/ip_tunnel.c
net/ipv4/ip_tunnel_core.c
net/ipv4/ipmr.c
net/ipv4/netfilter/nf_reject_ipv4.c
net/ipv4/netfilter/nft_reject_ipv4.c
net/ipv4/proc.c
net/ipv4/raw.c
net/ipv4/tcp_fastopen.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_metrics.c
net/ipv4/tcp_minisocks.c
net/ipv4/tcp_output.c
net/ipv4/tcp_timer.c
net/ipv4/udp.c
net/ipv4/udp_tunnel.c
net/ipv4/xfrm4_input.c
net/ipv4/xfrm4_output.c
net/ipv6/inet6_hashtables.c
net/ipv6/ip6_gre.c
net/ipv6/ip6_input.c
net/ipv6/ip6_output.c
net/ipv6/ip6_tunnel.c
net/ipv6/ip6_udp_tunnel.c
net/ipv6/ip6mr.c
net/ipv6/mcast.c
net/ipv6/ndisc.c
net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
net/ipv6/netfilter/nf_reject_ipv6.c
net/ipv6/netfilter/nft_reject_ipv6.c
net/ipv6/output_core.c
net/ipv6/raw.c
net/ipv6/sit.c
net/ipv6/tcp_ipv6.c
net/ipv6/udp.c
net/ipv6/xfrm6_input.c
net/ipv6/xfrm6_output.c
net/mac80211/agg-tx.c
net/mac80211/driver-ops.h
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/main.c
net/mac80211/mlme.c
net/mac80211/rc80211_minstrel.c
net/mac80211/rc80211_minstrel.h
net/mac80211/rc80211_minstrel_debugfs.c
net/mac80211/rc80211_minstrel_ht.c
net/mac80211/rc80211_minstrel_ht.h
net/mac80211/rc80211_minstrel_ht_debugfs.c
net/mac80211/rx.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/status.c
net/mac80211/trace.h
net/mac80211/tx.c
net/mac80211/util.c
net/netfilter/Makefile
net/netfilter/ipset/ip_set_hash_netiface.c
net/netfilter/ipvs/ip_vs_xmit.c
net/netfilter/nf_log_common.c
net/netfilter/nf_queue.c
net/netfilter/nf_tables_api.c
net/netfilter/nf_tables_core.c
net/netfilter/nfnetlink_log.c
net/netfilter/nfnetlink_queue_core.c
net/netfilter/nft_compat.c
net/netfilter/nft_ct.c
net/netfilter/nft_dynset.c [new file with mode: 0644]
net/netfilter/nft_hash.c
net/netfilter/nft_lookup.c
net/netfilter/nft_meta.c
net/netfilter/xt_TPROXY.c
net/netfilter/xt_cgroup.c
net/netfilter/xt_physdev.c
net/netfilter/xt_socket.c
net/nfc/nci/core.c
net/nfc/netlink.c
net/openvswitch/vport-vxlan.c
net/sched/sch_ingress.c
net/tipc/udp_media.c
net/wireless/Kconfig
net/wireless/nl80211.c
net/wireless/reg.c
net/wireless/reg.h
net/wireless/sme.c
net/wireless/util.c
net/xfrm/xfrm_output.c
net/xfrm/xfrm_user.c
security/selinux/nlmsgtab.c

diff --git a/Documentation/devicetree/bindings/net/nfc/nxp-nci.txt b/Documentation/devicetree/bindings/net/nfc/nxp-nci.txt
new file mode 100644 (file)
index 0000000..5b6cd9b
--- /dev/null
@@ -0,0 +1,35 @@
+* NXP Semiconductors NXP NCI NFC Controllers
+
+Required properties:
+- compatible: Should be "nxp,nxp-nci-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 chip
+- 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 NPC100 NFC controller on I2C2):
+
+&i2c2 {
+
+       status = "okay";
+
+       npc100: npc100@29 {
+
+               compatible = "nxp,nxp-nci-i2c";
+
+               reg = <0x29>;
+               clock-frequency = <100000>;
+
+               interrupt-parent = <&gpio1>;
+               interrupts = <29 GPIO_ACTIVE_HIGH>;
+
+               enable-gpios = <&gpio0 30 GPIO_ACTIVE_HIGH>;
+               firmware-gpios = <&gpio0 31 GPIO_ACTIVE_HIGH>;
+       };
+};
index 8ca65cec52ae8cc25e8231891a159c9525297391..29aca8591b163070211e29f02ea7608c19ca66a8 100644 (file)
@@ -35,10 +35,11 @@ Optional properties:
 - reset-names: Should contain the reset signal name "stmmaceth", if a
        reset phandle is given
 - max-frame-size: See ethernet.txt file in the same directory
-- clocks: If present, the first clock should be the GMAC main clock,
-  further clocks may be specified in derived bindings.
+- clocks: If present, the first clock should be the GMAC main clock and
+  the second clock should be peripheral's register interface clock. Further
+  clocks may be specified in derived bindings.
 - clock-names: One name for each entry in the clocks property, the
-  first one should be "stmmaceth".
+  first one should be "stmmaceth" and the second one should be "pclk".
 - clk_ptp_ref: this is the PTP reference clock; in case of the PTP is
   available this clock is used for programming the Timestamp Addend Register.
   If not passed then the system clock will be used and this is fine on some
index 43d3549366a09d6cfbb7e4b284bb01d04fd314a0..15534fdd09a88f97a8d9c20b72666d6eff93dd0d 100644 (file)
@@ -42,10 +42,10 @@ Additional Configurations
   Jumbo Frames
   ------------
   Jumbo Frames support is enabled by changing the MTU to a value larger than
-  the default of 1500.  Use the ifconfig command to increase the MTU size.
+  the default of 1500.  Use the ip command to increase the MTU size.
   For example:
 
-       ifconfig eth<x> mtu 9000 up
+       ip link set dev eth<x> mtu 9000
 
   This setting is not saved across reboots.
 
index 1e0c045e89f7dc12f5c2ccf016020e6e2fdb2857..9b4a10a1cf50b4513eecaa6e638051a356c34efa 100644 (file)
@@ -39,7 +39,7 @@ Channel Bonding documentation can be found in the Linux kernel source:
 
 The driver information previously displayed in the /proc filesystem is not
 supported in this release.  Alternatively, you can use ethtool (version 1.6
-or later), lspci, and ifconfig to obtain the same information.
+or later), lspci, and iproute2 to obtain the same information.
 
 Instructions on updating ethtool can be found in the section "Additional
 Configurations" later in this document.
@@ -90,7 +90,7 @@ select m for "Intel(R) PRO/10GbE support" located at:
 3. Assign an IP address to the interface by entering the following, where
    x is the interface number:
 
-     ifconfig ethx <IP_address>
+     ip addr add ethx <IP_address>
 
 4. Verify that the interface works. Enter the following, where <IP_address>
    is the IP address for another machine on the same subnet as the interface
@@ -177,7 +177,7 @@ NOTE: These changes are only suggestions, and serve as a starting point for
       tuning your network performance.
 
 The changes are made in three major ways, listed in order of greatest effect:
-- Use ifconfig to modify the mtu (maximum transmission unit) and the txqueuelen
+- Use ip link to modify the mtu (maximum transmission unit) and the txqueuelen
   parameter.
 - Use sysctl to modify /proc parameters (essentially kernel tuning)
 - Use setpci to modify the MMRBC field in PCI-X configuration space to increase
@@ -202,7 +202,7 @@ setpci -d 8086:1a48 e6.b=2e
 # to change as well.
 # set the txqueuelen
 # your ixgb adapter should be loaded as eth1 for this to work, change if needed
-ifconfig eth1 mtu 9000 txqueuelen 1000 up
+ip li set dev eth1 mtu 9000 txqueuelen 1000 up
 # call the sysctl utility to modify /proc/sys entries
 sysctl -p ./sysctl_ixgb.conf
 - END ixgb_perf.sh
@@ -297,10 +297,10 @@ Additional Configurations
   ------------
   The driver supports Jumbo Frames for all adapters. Jumbo Frames support is
   enabled by changing the MTU to a value larger than the default of 1500.
-  The maximum value for the MTU is 16114.  Use the ifconfig command to
+  The maximum value for the MTU is 16114.  Use the ip command to
   increase the MTU size.  For example:
 
-        ifconfig ethx mtu 9000 up
+        ip li set dev ethx mtu 9000
 
   The maximum MTU setting for Jumbo Frames is 16114.  This value coincides
   with the maximum Jumbo Frames size of 16128.
index 0ace6e776ac86d93c9f96f4ca1814eee71454624..6f0cb57b59c6b3713b3428c0dfadbbec46972ab5 100644 (file)
@@ -70,10 +70,10 @@ Avago      1000BASE-T SFP                                    ABCU-5710RZ
 82599-based adapters support all passive and active limiting direct attach
 cables that comply with SFF-8431 v4.1 and SFF-8472 v10.4 specifications.
 
-Laser turns off for SFP+ when ifconfig down
+Laser turns off for SFP+ when device is down
 -------------------------------------------
-"ifconfig down" turns off the laser for 82599-based SFP+ fiber adapters.
-"ifconfig up" turns on the laser.
+"ip link set down" turns off the laser for 82599-based SFP+ fiber adapters.
+"ip link set up" turns on the laser.
 
 
 82598-BASED ADAPTERS
@@ -213,13 +213,13 @@ Additional Configurations
   ------------
   The driver supports Jumbo Frames for all adapters. Jumbo Frames support is
   enabled by changing the MTU to a value larger than the default of 1500.
-  The maximum value for the MTU is 16110.  Use the ifconfig command to
+  The maximum value for the MTU is 16110.  Use the ip command to
   increase the MTU size.  For example:
 
-        ifconfig ethx mtu 9000 up
+        ip link set dev ethx mtu 9000
 
-  The maximum MTU setting for Jumbo Frames is 16110.  This value coincides
-  with the maximum Jumbo Frames size of 16128.
+  The maximum MTU setting for Jumbo Frames is 9710.  This value coincides
+  with the maximum Jumbo Frames size of 9728.
 
   Generic Receive Offload, aka GRO
   --------------------------------
index 9091b4ad1cc3c74c380da5e1eff26c14f8e165d1..dcaa542524790f94e02c06f3faeb0f1c5a8be7f8 100644 (file)
@@ -6944,6 +6944,13 @@ S:       Supported
 F:     drivers/block/nvme*
 F:     include/linux/nvme.h
 
+NXP-NCI NFC DRIVER
+M:     Clément Perrochaud <clement.perrochaud@effinnov.com>
+R:     Charles Gorand <charles.gorand@effinnov.com>
+L:     linux-nfc@lists.01.org (moderated for non-subscribers)
+S:     Supported
+F:     drivers/nfc/nxp-nci
+
 NXP TDA998X DRM DRIVER
 M:     Russell King <rmk+kernel@arm.linux.org.uk>
 S:     Supported
index 364f080768d02b2a13ea621da81f880685c39579..ed5c2738bea20efcf0c6ed0a920ad9faf5b1fd67 100644 (file)
@@ -2,9 +2,17 @@
 menu "Bluetooth device drivers"
        depends on BT
 
+config BT_INTEL
+       tristate
+
+config BT_BCM
+       tristate
+       select FW_LOADER
+
 config BT_HCIBTUSB
        tristate "HCI USB driver"
        depends on USB
+       select BT_INTEL
        help
          Bluetooth HCI USB driver.
          This driver is required if you want to use Bluetooth devices with
@@ -13,6 +21,17 @@ config BT_HCIBTUSB
          Say Y here to compile support for Bluetooth USB devices into the
          kernel or say M to compile it as module (btusb).
 
+config BT_HCIBTUSB_BCM
+       bool "Broadcom protocol support"
+       depends on BT_HCIBTUSB
+       select BT_BCM
+       default y
+       help
+         The Broadcom protocol support enables firmware and patchram
+         download support for Broadcom Bluetooth controllers.
+
+         Say Y here to compile support for Broadcom protocol.
+
 config BT_HCIBTSDIO
        tristate "HCI SDIO driver"
        depends on MMC
@@ -62,6 +81,7 @@ config BT_HCIUART_BCSP
 config BT_HCIUART_ATH3K
        bool "Atheros AR300x serial support"
        depends on BT_HCIUART
+       select BT_HCIUART_H4
        help
          HCIATH3K (HCI Atheros AR300x) is a serial protocol for
          communication between host and Atheros AR300x Bluetooth devices.
@@ -94,6 +114,27 @@ config BT_HCIUART_3WIRE
 
          Say Y here to compile support for Three-wire UART protocol.
 
+config BT_HCIUART_INTEL
+       bool "Intel protocol support"
+       depends on BT_HCIUART
+       select BT_INTEL
+       help
+         The Intel protocol support enables Bluetooth HCI over serial
+         port interface for Intel Bluetooth controllers.
+
+         Say Y here to compile support for Intel protocol.
+
+config BT_HCIUART_BCM
+       bool "Broadcom protocol support"
+       depends on BT_HCIUART
+       select BT_HCIUART_H4
+       select BT_BCM
+       help
+         The Broadcom protocol support enables Bluetooth HCI over serial
+         port interface for Broadcom Bluetooth controllers.
+
+         Say Y here to compile support for Broadcom protocol.
+
 config BT_HCIBCM203X
        tristate "HCI BCM203x USB driver"
        depends on USB
index 9fe8a875a8277b51c069ce682d6ece31c922d426..dd0d9c40b99914817f76728d600fb359dbcac007 100644 (file)
@@ -15,10 +15,12 @@ obj-$(CONFIG_BT_HCIBTUART)  += btuart_cs.o
 obj-$(CONFIG_BT_HCIBTUSB)      += btusb.o
 obj-$(CONFIG_BT_HCIBTSDIO)     += btsdio.o
 
+obj-$(CONFIG_BT_INTEL)         += btintel.o
 obj-$(CONFIG_BT_ATH3K)         += ath3k.o
 obj-$(CONFIG_BT_MRVL)          += btmrvl.o
 obj-$(CONFIG_BT_MRVL_SDIO)     += btmrvl_sdio.o
 obj-$(CONFIG_BT_WILINK)                += btwilink.o
+obj-$(CONFIG_BT_BCM)           += btbcm.o
 
 btmrvl-y                       := btmrvl_main.o
 btmrvl-$(CONFIG_DEBUG_FS)      += btmrvl_debugfs.o
@@ -29,6 +31,8 @@ hci_uart-$(CONFIG_BT_HCIUART_BCSP)    += hci_bcsp.o
 hci_uart-$(CONFIG_BT_HCIUART_LL)       += hci_ll.o
 hci_uart-$(CONFIG_BT_HCIUART_ATH3K)    += hci_ath.o
 hci_uart-$(CONFIG_BT_HCIUART_3WIRE)    += hci_h5.o
+hci_uart-$(CONFIG_BT_HCIUART_INTEL)    += hci_intel.o
+hci_uart-$(CONFIG_BT_HCIUART_BCM)      += hci_bcm.o
 hci_uart-objs                          := $(hci_uart-y)
 
 ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c
new file mode 100644 (file)
index 0000000..d0741f3
--- /dev/null
@@ -0,0 +1,387 @@
+/*
+ *
+ *  Bluetooth support for Broadcom devices
+ *
+ *  Copyright (C) 2015  Intel 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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <asm/unaligned.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btbcm.h"
+
+#define VERSION "0.1"
+
+#define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}})
+
+int btbcm_check_bdaddr(struct hci_dev *hdev)
+{
+       struct hci_rp_read_bd_addr *bda;
+       struct sk_buff *skb;
+
+       skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
+                            HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               int err = PTR_ERR(skb);
+               BT_ERR("%s: BCM: Reading device address failed (%d)",
+                      hdev->name, err);
+               return err;
+       }
+
+       if (skb->len != sizeof(*bda)) {
+               BT_ERR("%s: BCM: Device address length mismatch", hdev->name);
+               kfree_skb(skb);
+               return -EIO;
+       }
+
+       bda = (struct hci_rp_read_bd_addr *)skb->data;
+       if (bda->status) {
+               BT_ERR("%s: BCM: Device address result failed (%02x)",
+                      hdev->name, bda->status);
+               kfree_skb(skb);
+               return -bt_to_errno(bda->status);
+       }
+
+       /* The address 00:20:70:02:A0:00 indicates a BCM20702A0 controller
+        * with no configured address.
+        */
+       if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0)) {
+               BT_INFO("%s: BCM: Using default device address (%pMR)",
+                       hdev->name, &bda->bdaddr);
+               set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
+       }
+
+       kfree_skb(skb);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(btbcm_check_bdaddr);
+
+int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+       struct sk_buff *skb;
+       int err;
+
+       skb = __hci_cmd_sync(hdev, 0xfc01, 6, bdaddr, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               err = PTR_ERR(skb);
+               BT_ERR("%s: BCM: Change address command failed (%d)",
+                      hdev->name, err);
+               return err;
+       }
+       kfree_skb(skb);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(btbcm_set_bdaddr);
+
+static int btbcm_reset(struct hci_dev *hdev)
+{
+       struct sk_buff *skb;
+
+       skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               int err = PTR_ERR(skb);
+               BT_ERR("%s: BCM: Reset failed (%d)", hdev->name, err);
+               return err;
+       }
+       kfree_skb(skb);
+
+       return 0;
+}
+
+static struct sk_buff *btbcm_read_local_version(struct hci_dev *hdev)
+{
+       struct sk_buff *skb;
+
+       skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
+                            HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               BT_ERR("%s: BCM: Reading local version info failed (%ld)",
+                      hdev->name, PTR_ERR(skb));
+               return skb;
+       }
+
+       if (skb->len != sizeof(struct hci_rp_read_local_version)) {
+               BT_ERR("%s: BCM: Local version length mismatch", hdev->name);
+               kfree_skb(skb);
+               return ERR_PTR(-EIO);
+       }
+
+       return skb;
+}
+
+static struct sk_buff *btbcm_read_verbose_config(struct hci_dev *hdev)
+{
+       struct sk_buff *skb;
+
+       skb = __hci_cmd_sync(hdev, 0xfc79, 0, NULL, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               BT_ERR("%s: BCM: Read verbose config info failed (%ld)",
+                      hdev->name, PTR_ERR(skb));
+               return skb;
+       }
+
+       if (skb->len != 7) {
+               BT_ERR("%s: BCM: Verbose config length mismatch", hdev->name);
+               kfree_skb(skb);
+               return ERR_PTR(-EIO);
+       }
+
+       return skb;
+}
+
+static struct sk_buff *btbcm_read_usb_product(struct hci_dev *hdev)
+{
+       struct sk_buff *skb;
+
+       skb = __hci_cmd_sync(hdev, 0xfc5a, 0, NULL, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               BT_ERR("%s: BCM: Read USB product info failed (%ld)",
+                      hdev->name, PTR_ERR(skb));
+               return skb;
+       }
+
+       if (skb->len != 5) {
+               BT_ERR("%s: BCM: USB product length mismatch", hdev->name);
+               kfree_skb(skb);
+               return ERR_PTR(-EIO);
+       }
+
+       return skb;
+}
+
+static const struct {
+       u16 subver;
+       const char *name;
+} bcm_uart_subver_table[] = {
+       { 0x410e, "BCM43341B0"  },      /* 002.001.014 */
+       { }
+};
+
+static const struct {
+       u16 subver;
+       const char *name;
+} bcm_usb_subver_table[] = {
+       { 0x210b, "BCM43142A0"  },      /* 001.001.011 */
+       { 0x2112, "BCM4314A0"   },      /* 001.001.018 */
+       { 0x2118, "BCM20702A0"  },      /* 001.001.024 */
+       { 0x2126, "BCM4335A0"   },      /* 001.001.038 */
+       { 0x220e, "BCM20702A1"  },      /* 001.002.014 */
+       { 0x230f, "BCM4354A2"   },      /* 001.003.015 */
+       { 0x4106, "BCM4335B0"   },      /* 002.001.006 */
+       { 0x410e, "BCM20702B0"  },      /* 002.001.014 */
+       { 0x6109, "BCM4335C0"   },      /* 003.001.009 */
+       { 0x610c, "BCM4354"     },      /* 003.001.012 */
+       { }
+};
+
+int btbcm_setup_patchram(struct hci_dev *hdev)
+{
+       const struct hci_command_hdr *cmd;
+       const struct firmware *fw;
+       const u8 *fw_ptr;
+       size_t fw_size;
+       char fw_name[64];
+       u16 opcode, subver, rev, pid, vid;
+       const char *hw_name = NULL;
+       struct sk_buff *skb;
+       struct hci_rp_read_local_version *ver;
+       int i, err;
+
+       /* Reset */
+       err = btbcm_reset(hdev);
+       if (err)
+               return err;
+
+       /* Read Local Version Info */
+       skb = btbcm_read_local_version(hdev);
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       ver = (struct hci_rp_read_local_version *)skb->data;
+       rev = le16_to_cpu(ver->hci_rev);
+       subver = le16_to_cpu(ver->lmp_subver);
+       kfree_skb(skb);
+
+       /* Read Verbose Config Version Info */
+       skb = btbcm_read_verbose_config(hdev);
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]);
+       kfree_skb(skb);
+
+       switch ((rev & 0xf000) >> 12) {
+       case 0:
+               for (i = 0; bcm_uart_subver_table[i].name; i++) {
+                       if (subver == bcm_uart_subver_table[i].subver) {
+                               hw_name = bcm_uart_subver_table[i].name;
+                               break;
+                       }
+               }
+
+               snprintf(fw_name, sizeof(fw_name), "brcm/%s.hcd",
+                        hw_name ? : "BCM");
+               break;
+       case 1:
+       case 2:
+               /* Read USB Product Info */
+               skb = btbcm_read_usb_product(hdev);
+               if (IS_ERR(skb))
+                       return PTR_ERR(skb);
+
+               vid = get_unaligned_le16(skb->data + 1);
+               pid = get_unaligned_le16(skb->data + 3);
+               kfree_skb(skb);
+
+               for (i = 0; bcm_usb_subver_table[i].name; i++) {
+                       if (subver == bcm_usb_subver_table[i].subver) {
+                               hw_name = bcm_usb_subver_table[i].name;
+                               break;
+                       }
+               }
+
+               snprintf(fw_name, sizeof(fw_name), "brcm/%s-%4.4x-%4.4x.hcd",
+                        hw_name ? : "BCM", vid, pid);
+               break;
+       default:
+               return 0;
+       }
+
+       BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
+               hw_name ? : "BCM", (subver & 0x7000) >> 13,
+               (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
+
+       err = request_firmware(&fw, fw_name, &hdev->dev);
+       if (err < 0) {
+               BT_INFO("%s: BCM: patch %s not found", hdev->name, fw_name);
+               return 0;
+       }
+
+       /* Start Download */
+       skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               err = PTR_ERR(skb);
+               BT_ERR("%s: BCM: Download Minidrv command failed (%d)",
+                      hdev->name, err);
+               goto reset;
+       }
+       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)) {
+               const u8 *cmd_param;
+
+               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);
+                       err = -EINVAL;
+                       goto reset;
+               }
+
+               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)) {
+                       err = PTR_ERR(skb);
+                       BT_ERR("%s: BCM: patch command %04x failed (%d)",
+                              hdev->name, opcode, err);
+                       goto reset;
+               }
+               kfree_skb(skb);
+       }
+
+       /* 250 msec delay after Launch Ram completes */
+       msleep(250);
+
+reset:
+       /* Reset */
+       err = btbcm_reset(hdev);
+       if (err)
+               goto done;
+
+       /* Read Local Version Info */
+       skb = btbcm_read_local_version(hdev);
+       if (IS_ERR(skb)) {
+               err = PTR_ERR(skb);
+               goto done;
+       }
+
+       ver = (struct hci_rp_read_local_version *)skb->data;
+       rev = le16_to_cpu(ver->hci_rev);
+       subver = le16_to_cpu(ver->lmp_subver);
+       kfree_skb(skb);
+
+       BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
+               hw_name ? : "BCM", (subver & 0x7000) >> 13,
+               (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
+
+       btbcm_check_bdaddr(hdev);
+
+       set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
+
+done:
+       release_firmware(fw);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(btbcm_setup_patchram);
+
+int btbcm_setup_apple(struct hci_dev *hdev)
+{
+       struct sk_buff *skb;
+
+       /* Read Verbose Config Version Info */
+       skb = btbcm_read_verbose_config(hdev);
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       BT_INFO("%s: BCM: chip id %u build %4.4u", hdev->name, skb->data[1],
+               get_unaligned_le16(skb->data + 5));
+       kfree_skb(skb);
+
+       set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(btbcm_setup_apple);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth support for Broadcom devices ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/btbcm.h b/drivers/bluetooth/btbcm.h
new file mode 100644 (file)
index 0000000..34268ae
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ *
+ *  Bluetooth support for Broadcom devices
+ *
+ *  Copyright (C) 2015  Intel 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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#if IS_ENABLED(CONFIG_BT_BCM)
+
+int btbcm_check_bdaddr(struct hci_dev *hdev);
+int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
+
+int btbcm_setup_patchram(struct hci_dev *hdev);
+int btbcm_setup_apple(struct hci_dev *hdev);
+
+#else
+
+static inline int btbcm_check_bdaddr(struct hci_dev *hdev)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline int btbcm_setup_patchram(struct hci_dev *hdev)
+{
+       return 0;
+}
+
+static inline int btbcm_setup_apple(struct hci_dev *hdev)
+{
+       return 0;
+}
+
+#endif
diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
new file mode 100644 (file)
index 0000000..2d43d42
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ *
+ *  Bluetooth support for Intel devices
+ *
+ *  Copyright (C) 2015  Intel 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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/module.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btintel.h"
+
+#define VERSION "0.1"
+
+#define BDADDR_INTEL (&(bdaddr_t) {{0x00, 0x8b, 0x9e, 0x19, 0x03, 0x00}})
+
+int btintel_check_bdaddr(struct hci_dev *hdev)
+{
+       struct hci_rp_read_bd_addr *bda;
+       struct sk_buff *skb;
+
+       skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
+                            HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               int err = PTR_ERR(skb);
+               BT_ERR("%s: Reading Intel device address failed (%d)",
+                      hdev->name, err);
+               return err;
+       }
+
+       if (skb->len != sizeof(*bda)) {
+               BT_ERR("%s: Intel device address length mismatch", hdev->name);
+               kfree_skb(skb);
+               return -EIO;
+       }
+
+       bda = (struct hci_rp_read_bd_addr *)skb->data;
+       if (bda->status) {
+               BT_ERR("%s: Intel device address result failed (%02x)",
+                      hdev->name, bda->status);
+               kfree_skb(skb);
+               return -bt_to_errno(bda->status);
+       }
+
+       /* For some Intel based controllers, the default Bluetooth device
+        * address 00:03:19:9E:8B:00 can be found. These controllers are
+        * fully operational, but have the danger of duplicate addresses
+        * and that in turn can cause problems with Bluetooth operation.
+        */
+       if (!bacmp(&bda->bdaddr, BDADDR_INTEL)) {
+               BT_ERR("%s: Found Intel default device address (%pMR)",
+                      hdev->name, &bda->bdaddr);
+               set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
+       }
+
+       kfree_skb(skb);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(btintel_check_bdaddr);
+
+int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+       struct sk_buff *skb;
+       int err;
+
+       skb = __hci_cmd_sync(hdev, 0xfc31, 6, bdaddr, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               err = PTR_ERR(skb);
+               BT_ERR("%s: Changing Intel device address failed (%d)",
+                      hdev->name, err);
+               return err;
+       }
+       kfree_skb(skb);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(btintel_set_bdaddr);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth support for Intel devices ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h
new file mode 100644 (file)
index 0000000..4bda6ab
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ *
+ *  Bluetooth support for Intel devices
+ *
+ *  Copyright (C) 2015  Intel 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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+struct intel_version {
+       u8 status;
+       u8 hw_platform;
+       u8 hw_variant;
+       u8 hw_revision;
+       u8 fw_variant;
+       u8 fw_revision;
+       u8 fw_build_num;
+       u8 fw_build_ww;
+       u8 fw_build_yy;
+       u8 fw_patch_num;
+} __packed;
+
+struct intel_boot_params {
+       __u8     status;
+       __u8     otp_format;
+       __u8     otp_content;
+       __u8     otp_patch;
+       __le16   dev_revid;
+       __u8     secure_boot;
+       __u8     key_from_hdr;
+       __u8     key_type;
+       __u8     otp_lock;
+       __u8     api_lock;
+       __u8     debug_lock;
+       bdaddr_t otp_bdaddr;
+       __u8     min_fw_build_nn;
+       __u8     min_fw_build_cw;
+       __u8     min_fw_build_yy;
+       __u8     limited_cce;
+       __u8     unlocked_state;
+} __packed;
+
+struct intel_bootup {
+       __u8     zero;
+       __u8     num_cmds;
+       __u8     source;
+       __u8     reset_type;
+       __u8     reset_reason;
+       __u8     ddc_status;
+} __packed;
+
+struct intel_secure_send_result {
+       __u8     result;
+       __le16   opcode;
+       __u8     status;
+} __packed;
+
+#if IS_ENABLED(CONFIG_BT_INTEL)
+
+int btintel_check_bdaddr(struct hci_dev *hdev);
+int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
+
+#else
+
+static inline int btintel_check_bdaddr(struct hci_dev *hdev)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+       return -EOPNOTSUPP;
+}
+
+#endif
index 6e4ff16e487b06229346a9284e867962f70f0d5c..de7b236eeae7777f71389ec42c233525d309aef2 100644 (file)
 #include <linux/module.h>
 #include <linux/usb.h>
 #include <linux/firmware.h>
-#include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
-#define VERSION "0.7"
+#include "btintel.h"
+#include "btbcm.h"
+
+#define VERSION "0.8"
 
 static bool disable_scofix;
 static bool force_scofix;
@@ -1343,39 +1345,6 @@ static int btusb_setup_csr(struct hci_dev *hdev)
        return ret;
 }
 
-struct intel_version {
-       u8 status;
-       u8 hw_platform;
-       u8 hw_variant;
-       u8 hw_revision;
-       u8 fw_variant;
-       u8 fw_revision;
-       u8 fw_build_num;
-       u8 fw_build_ww;
-       u8 fw_build_yy;
-       u8 fw_patch_num;
-} __packed;
-
-struct intel_boot_params {
-       __u8     status;
-       __u8     otp_format;
-       __u8     otp_content;
-       __u8     otp_patch;
-       __le16   dev_revid;
-       __u8     secure_boot;
-       __u8     key_from_hdr;
-       __u8     key_type;
-       __u8     otp_lock;
-       __u8     api_lock;
-       __u8     debug_lock;
-       bdaddr_t otp_bdaddr;
-       __u8     min_fw_build_nn;
-       __u8     min_fw_build_cw;
-       __u8     min_fw_build_yy;
-       __u8     limited_cce;
-       __u8     unlocked_state;
-} __packed;
-
 static const struct firmware *btusb_setup_intel_get_fw(struct hci_dev *hdev,
                                                       struct intel_version *ver)
 {
@@ -1532,51 +1501,6 @@ static int btusb_setup_intel_patching(struct hci_dev *hdev,
        return 0;
 }
 
-#define BDADDR_INTEL (&(bdaddr_t) {{0x00, 0x8b, 0x9e, 0x19, 0x03, 0x00}})
-
-static int btusb_check_bdaddr_intel(struct hci_dev *hdev)
-{
-       struct sk_buff *skb;
-       struct hci_rp_read_bd_addr *rp;
-
-       skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
-                            HCI_INIT_TIMEOUT);
-       if (IS_ERR(skb)) {
-               BT_ERR("%s reading Intel device address failed (%ld)",
-                      hdev->name, PTR_ERR(skb));
-               return PTR_ERR(skb);
-       }
-
-       if (skb->len != sizeof(*rp)) {
-               BT_ERR("%s Intel device address length mismatch", hdev->name);
-               kfree_skb(skb);
-               return -EIO;
-       }
-
-       rp = (struct hci_rp_read_bd_addr *)skb->data;
-       if (rp->status) {
-               BT_ERR("%s Intel device address result failed (%02x)",
-                      hdev->name, rp->status);
-               kfree_skb(skb);
-               return -bt_to_errno(rp->status);
-       }
-
-       /* For some Intel based controllers, the default Bluetooth device
-        * address 00:03:19:9E:8B:00 can be found. These controllers are
-        * fully operational, but have the danger of duplicate addresses
-        * and that in turn can cause problems with Bluetooth operation.
-        */
-       if (!bacmp(&rp->bdaddr, BDADDR_INTEL)) {
-               BT_ERR("%s found Intel default device address (%pMR)",
-                      hdev->name, &rp->bdaddr);
-               set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
-       }
-
-       kfree_skb(skb);
-
-       return 0;
-}
-
 static int btusb_setup_intel(struct hci_dev *hdev)
 {
        struct sk_buff *skb;
@@ -1649,7 +1573,7 @@ static int btusb_setup_intel(struct hci_dev *hdev)
                BT_INFO("%s: Intel device is already patched. patch num: %02x",
                        hdev->name, ver->fw_patch_num);
                kfree_skb(skb);
-               btusb_check_bdaddr_intel(hdev);
+               btintel_check_bdaddr(hdev);
                return 0;
        }
 
@@ -1662,7 +1586,7 @@ static int btusb_setup_intel(struct hci_dev *hdev)
        fw = btusb_setup_intel_get_fw(hdev, ver);
        if (!fw) {
                kfree_skb(skb);
-               btusb_check_bdaddr_intel(hdev);
+               btintel_check_bdaddr(hdev);
                return 0;
        }
        fw_ptr = fw->data;
@@ -1743,7 +1667,7 @@ static int btusb_setup_intel(struct hci_dev *hdev)
        BT_INFO("%s: Intel Bluetooth firmware patch completed and activated",
                hdev->name);
 
-       btusb_check_bdaddr_intel(hdev);
+       btintel_check_bdaddr(hdev);
        return 0;
 
 exit_mfg_disable:
@@ -1759,7 +1683,7 @@ exit_mfg_disable:
 
        BT_INFO("%s: Intel Bluetooth firmware patch completed", hdev->name);
 
-       btusb_check_bdaddr_intel(hdev);
+       btintel_check_bdaddr(hdev);
        return 0;
 
 exit_mfg_deactivate:
@@ -1780,7 +1704,7 @@ exit_mfg_deactivate:
        BT_INFO("%s: Intel Bluetooth firmware patch completed and deactivated",
                hdev->name);
 
-       btusb_check_bdaddr_intel(hdev);
+       btintel_check_bdaddr(hdev);
        return 0;
 }
 
@@ -1822,6 +1746,38 @@ static int btusb_recv_bulk_intel(struct btusb_data *data, void *buffer,
        return btusb_recv_bulk(data, buffer, count);
 }
 
+static void btusb_intel_bootup(struct btusb_data *data, const void *ptr,
+                              unsigned int len)
+{
+       const struct intel_bootup *evt = ptr;
+
+       if (len != sizeof(*evt))
+               return;
+
+       if (test_and_clear_bit(BTUSB_BOOTING, &data->flags)) {
+               smp_mb__after_atomic();
+               wake_up_bit(&data->flags, BTUSB_BOOTING);
+       }
+}
+
+static void btusb_intel_secure_send_result(struct btusb_data *data,
+                                          const void *ptr, unsigned int len)
+{
+       const struct intel_secure_send_result *evt = ptr;
+
+       if (len != sizeof(*evt))
+               return;
+
+       if (evt->result)
+               set_bit(BTUSB_FIRMWARE_FAILED, &data->flags);
+
+       if (test_and_clear_bit(BTUSB_DOWNLOADING, &data->flags) &&
+           test_bit(BTUSB_FIRMWARE_LOADED, &data->flags)) {
+               smp_mb__after_atomic();
+               wake_up_bit(&data->flags, BTUSB_DOWNLOADING);
+       }
+}
+
 static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct btusb_data *data = hci_get_drvdata(hdev);
@@ -1829,32 +1785,27 @@ static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb)
        if (test_bit(BTUSB_BOOTLOADER, &data->flags)) {
                struct hci_event_hdr *hdr = (void *)skb->data;
 
-               /* When the firmware loading completes the device sends
-                * out a vendor specific event indicating the result of
-                * the firmware loading.
-                */
-               if (skb->len == 7 && hdr->evt == 0xff && hdr->plen == 0x05 &&
-                   skb->data[2] == 0x06) {
-                       if (skb->data[3] != 0x00)
-                               test_bit(BTUSB_FIRMWARE_FAILED, &data->flags);
-
-                       if (test_and_clear_bit(BTUSB_DOWNLOADING,
-                                              &data->flags) &&
-                           test_bit(BTUSB_FIRMWARE_LOADED, &data->flags)) {
-                               smp_mb__after_atomic();
-                               wake_up_bit(&data->flags, BTUSB_DOWNLOADING);
-                       }
-               }
-
-               /* When switching to the operational firmware the device
-                * sends a vendor specific event indicating that the bootup
-                * completed.
-                */
-               if (skb->len == 9 && hdr->evt == 0xff && hdr->plen == 0x07 &&
-                   skb->data[2] == 0x02) {
-                       if (test_and_clear_bit(BTUSB_BOOTING, &data->flags)) {
-                               smp_mb__after_atomic();
-                               wake_up_bit(&data->flags, BTUSB_BOOTING);
+               if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff &&
+                   hdr->plen > 0) {
+                       const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1;
+                       unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1;
+
+                       switch (skb->data[2]) {
+                       case 0x02:
+                               /* When switching to the operational firmware
+                                * the device sends a vendor specific event
+                                * indicating that the bootup completed.
+                                */
+                               btusb_intel_bootup(data, ptr, len);
+                               break;
+                       case 0x06:
+                               /* When the firmware loading completes the
+                                * device sends out a vendor specific event
+                                * indicating the result of the firmware
+                                * loading.
+                                */
+                               btusb_intel_secure_send_result(data, ptr, len);
+                               break;
                        }
                }
        }
@@ -2056,7 +2007,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
        if (ver->fw_variant == 0x23) {
                kfree_skb(skb);
                clear_bit(BTUSB_BOOTLOADER, &data->flags);
-               btusb_check_bdaddr_intel(hdev);
+               btintel_check_bdaddr(hdev);
                return 0;
        }
 
@@ -2340,23 +2291,6 @@ static void btusb_hw_error_intel(struct hci_dev *hdev, u8 code)
        kfree_skb(skb);
 }
 
-static int btusb_set_bdaddr_intel(struct hci_dev *hdev, const bdaddr_t *bdaddr)
-{
-       struct sk_buff *skb;
-       long ret;
-
-       skb = __hci_cmd_sync(hdev, 0xfc31, 6, bdaddr, HCI_INIT_TIMEOUT);
-       if (IS_ERR(skb)) {
-               ret = PTR_ERR(skb);
-               BT_ERR("%s: changing Intel device address failed (%ld)",
-                      hdev->name, ret);
-               return ret;
-       }
-       kfree_skb(skb);
-
-       return 0;
-}
-
 static int btusb_shutdown_intel(struct hci_dev *hdev)
 {
        struct sk_buff *skb;
@@ -2401,266 +2335,6 @@ static int btusb_set_bdaddr_marvell(struct hci_dev *hdev,
        return 0;
 }
 
-static const struct {
-       u16 subver;
-       const char *name;
-} bcm_subver_table[] = {
-       { 0x210b, "BCM43142A0"  },      /* 001.001.011 */
-       { 0x2112, "BCM4314A0"   },      /* 001.001.018 */
-       { 0x2118, "BCM20702A0"  },      /* 001.001.024 */
-       { 0x2126, "BCM4335A0"   },      /* 001.001.038 */
-       { 0x220e, "BCM20702A1"  },      /* 001.002.014 */
-       { 0x230f, "BCM4354A2"   },      /* 001.003.015 */
-       { 0x4106, "BCM4335B0"   },      /* 002.001.006 */
-       { 0x410e, "BCM20702B0"  },      /* 002.001.014 */
-       { 0x6109, "BCM4335C0"   },      /* 003.001.009 */
-       { 0x610c, "BCM4354"     },      /* 003.001.012 */
-       { }
-};
-
-#define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}})
-
-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, subver, rev;
-       const char *hw_name = NULL;
-       struct sk_buff *skb;
-       struct hci_rp_read_local_version *ver;
-       struct hci_rp_read_bd_addr *bda;
-       long ret;
-       int i;
-
-       /* 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);
-               return ret;
-       }
-       kfree_skb(skb);
-
-       /* Read Local Version Info */
-       skb = btusb_read_local_version(hdev);
-       if (IS_ERR(skb))
-               return PTR_ERR(skb);
-
-       ver = (struct hci_rp_read_local_version *)skb->data;
-       rev = le16_to_cpu(ver->hci_rev);
-       subver = le16_to_cpu(ver->lmp_subver);
-       kfree_skb(skb);
-
-       /* Read Verbose Config Version Info */
-       skb = __hci_cmd_sync(hdev, 0xfc79, 0, NULL, HCI_INIT_TIMEOUT);
-       if (IS_ERR(skb)) {
-               ret = PTR_ERR(skb);
-               BT_ERR("%s: BCM: Read Verbose Version failed (%ld)",
-                      hdev->name, ret);
-               return ret;
-       }
-
-       if (skb->len != 7) {
-               BT_ERR("%s: BCM: Read Verbose Version event length mismatch",
-                      hdev->name);
-               kfree_skb(skb);
-               return -EIO;
-       }
-
-       BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]);
-       kfree_skb(skb);
-
-       for (i = 0; bcm_subver_table[i].name; i++) {
-               if (subver == bcm_subver_table[i].subver) {
-                       hw_name = bcm_subver_table[i].name;
-                       break;
-               }
-       }
-
-       BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
-               hw_name ? : "BCM", (subver & 0x7000) >> 13,
-               (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
-
-       snprintf(fw_name, sizeof(fw_name), "brcm/%s-%4.4x-%4.4x.hcd",
-                hw_name ? : "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;
-       }
-
-       /* 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 = btusb_read_local_version(hdev);
-       if (IS_ERR(skb)) {
-               ret = PTR_ERR(skb);
-               goto done;
-       }
-
-       ver = (struct hci_rp_read_local_version *)skb->data;
-       rev = le16_to_cpu(ver->hci_rev);
-       subver = le16_to_cpu(ver->lmp_subver);
-       kfree_skb(skb);
-
-       BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
-               hw_name ? : "BCM", (subver & 0x7000) >> 13,
-               (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
-
-       /* Read BD Address */
-       skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
-                            HCI_INIT_TIMEOUT);
-       if (IS_ERR(skb)) {
-               ret = PTR_ERR(skb);
-               BT_ERR("%s: HCI_OP_READ_BD_ADDR failed (%ld)",
-                      hdev->name, ret);
-               goto done;
-       }
-
-       if (skb->len != sizeof(*bda)) {
-               BT_ERR("%s: HCI_OP_READ_BD_ADDR event length mismatch",
-                      hdev->name);
-               kfree_skb(skb);
-               ret = -EIO;
-               goto done;
-       }
-
-       bda = (struct hci_rp_read_bd_addr *)skb->data;
-       if (bda->status) {
-               BT_ERR("%s: HCI_OP_READ_BD_ADDR error status (%02x)",
-                      hdev->name, bda->status);
-               kfree_skb(skb);
-               ret = -bt_to_errno(bda->status);
-               goto done;
-       }
-
-       /* The address 00:20:70:02:A0:00 indicates a BCM20702A0 controller
-        * with no configured address.
-        */
-       if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0)) {
-               BT_INFO("%s: BCM: using default device address (%pMR)",
-                       hdev->name, &bda->bdaddr);
-               set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
-       }
-
-       kfree_skb(skb);
-
-done:
-       release_firmware(fw);
-
-       return ret;
-}
-
-static int btusb_set_bdaddr_bcm(struct hci_dev *hdev, const bdaddr_t *bdaddr)
-{
-       struct sk_buff *skb;
-       long ret;
-
-       skb = __hci_cmd_sync(hdev, 0xfc01, 6, bdaddr, HCI_INIT_TIMEOUT);
-       if (IS_ERR(skb)) {
-               ret = PTR_ERR(skb);
-               BT_ERR("%s: BCM: Change address command failed (%ld)",
-                      hdev->name, ret);
-               return ret;
-       }
-       kfree_skb(skb);
-
-       return 0;
-}
-
-static int btusb_setup_bcm_apple(struct hci_dev *hdev)
-{
-       struct sk_buff *skb;
-       int err;
-
-       /* Read Verbose Config Version Info */
-       skb = __hci_cmd_sync(hdev, 0xfc79, 0, NULL, HCI_INIT_TIMEOUT);
-       if (IS_ERR(skb)) {
-               err = PTR_ERR(skb);
-               BT_ERR("%s: BCM: Read Verbose Version failed (%d)",
-                      hdev->name, err);
-               return err;
-       }
-
-       if (skb->len != 7) {
-               BT_ERR("%s: BCM: Read Verbose Version event length mismatch",
-                      hdev->name);
-               kfree_skb(skb);
-               return -EIO;
-       }
-
-       BT_INFO("%s: BCM: chip id %u build %4.4u", hdev->name, skb->data[1],
-               get_unaligned_le16(skb->data + 5));
-       kfree_skb(skb);
-
-       return 0;
-}
-
 static int btusb_set_bdaddr_ath3012(struct hci_dev *hdev,
                                    const bdaddr_t *bdaddr)
 {
@@ -3054,21 +2728,20 @@ static int btusb_probe(struct usb_interface *intf,
        if (id->driver_info & BTUSB_BCM92035)
                hdev->setup = btusb_setup_bcm92035;
 
+#ifdef CONFIG_BT_HCIBTUSB_BCM
        if (id->driver_info & BTUSB_BCM_PATCHRAM) {
-               hdev->setup = btusb_setup_bcm_patchram;
-               hdev->set_bdaddr = btusb_set_bdaddr_bcm;
-               set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
+               hdev->setup = btbcm_setup_patchram;
+               hdev->set_bdaddr = btbcm_set_bdaddr;
        }
 
-       if (id->driver_info & BTUSB_BCM_APPLE) {
-               hdev->setup = btusb_setup_bcm_apple;
-               set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
-       }
+       if (id->driver_info & BTUSB_BCM_APPLE)
+               hdev->setup = btbcm_setup_apple;
+#endif
 
        if (id->driver_info & BTUSB_INTEL) {
                hdev->setup = btusb_setup_intel;
                hdev->shutdown = btusb_shutdown_intel;
-               hdev->set_bdaddr = btusb_set_bdaddr_intel;
+               hdev->set_bdaddr = btintel_set_bdaddr;
                set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
                set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
        }
@@ -3077,7 +2750,7 @@ static int btusb_probe(struct usb_interface *intf,
                hdev->send = btusb_send_frame_intel;
                hdev->setup = btusb_setup_intel_new;
                hdev->hw_error = btusb_hw_error_intel;
-               hdev->set_bdaddr = btusb_set_bdaddr_intel;
+               hdev->set_bdaddr = btintel_set_bdaddr;
                set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
        }
 
index 9c4dcf4c62ea763e318ecc41f7ef5db03a9fbe67..1b3f8647ea2fd446e1a6f14979a27e399fb0dbf5 100644 (file)
@@ -45,6 +45,7 @@ struct ath_struct {
        struct hci_uart *hu;
        unsigned int cur_sleep;
 
+       struct sk_buff *rx_skb;
        struct sk_buff_head txq;
        struct work_struct ctxtsw;
 };
@@ -136,6 +137,8 @@ static int ath_close(struct hci_uart *hu)
 
        skb_queue_purge(&ath->txq);
 
+       kfree_skb(ath->rx_skb);
+
        cancel_work_sync(&ath->ctxtsw);
 
        hu->priv = NULL;
@@ -187,40 +190,42 @@ static struct sk_buff *ath_dequeue(struct hci_uart *hu)
        return skb_dequeue(&ath->txq);
 }
 
+static const struct h4_recv_pkt ath_recv_pkts[] = {
+       { H4_RECV_ACL,   .recv = hci_recv_frame },
+       { H4_RECV_SCO,   .recv = hci_recv_frame },
+       { H4_RECV_EVENT, .recv = hci_recv_frame },
+};
+
 /* Recv data */
-static int ath_recv(struct hci_uart *hu, void *data, int count)
+static int ath_recv(struct hci_uart *hu, const void *data, int count)
 {
-       int ret;
+       struct ath_struct *ath = hu->priv;
 
-       ret = hci_recv_stream_fragment(hu->hdev, data, count);
-       if (ret < 0) {
-               BT_ERR("Frame Reassembly Failed");
-               return ret;
+       ath->rx_skb = h4_recv_buf(hu->hdev, ath->rx_skb, data, count,
+                                 ath_recv_pkts, ARRAY_SIZE(ath_recv_pkts));
+       if (IS_ERR(ath->rx_skb)) {
+               int err = PTR_ERR(ath->rx_skb);
+               BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
+               return err;
        }
 
        return count;
 }
 
-static struct hci_uart_proto athp = {
-       .id = HCI_UART_ATH3K,
-       .open = ath_open,
-       .close = ath_close,
-       .recv = ath_recv,
-       .enqueue = ath_enqueue,
-       .dequeue = ath_dequeue,
-       .flush = ath_flush,
+static const struct hci_uart_proto athp = {
+       .id             = HCI_UART_ATH3K,
+       .name           = "ATH3K",
+       .open           = ath_open,
+       .close          = ath_close,
+       .recv           = ath_recv,
+       .enqueue        = ath_enqueue,
+       .dequeue        = ath_dequeue,
+       .flush          = ath_flush,
 };
 
 int __init ath_init(void)
 {
-       int err = hci_uart_register_proto(&athp);
-
-       if (!err)
-               BT_INFO("HCIATH3K protocol initialized");
-       else
-               BT_ERR("HCIATH3K protocol registration failed");
-
-       return err;
+       return hci_uart_register_proto(&athp);
 }
 
 int __exit ath_deinit(void)
diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
new file mode 100644 (file)
index 0000000..1ec0b4a
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ *
+ *  Bluetooth HCI UART driver for Broadcom devices
+ *
+ *  Copyright (C) 2015  Intel 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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btbcm.h"
+#include "hci_uart.h"
+
+struct bcm_data {
+       struct sk_buff *rx_skb;
+       struct sk_buff_head txq;
+};
+
+static int bcm_open(struct hci_uart *hu)
+{
+       struct bcm_data *bcm;
+
+       BT_DBG("hu %p", hu);
+
+       bcm = kzalloc(sizeof(*bcm), GFP_KERNEL);
+       if (!bcm)
+               return -ENOMEM;
+
+       skb_queue_head_init(&bcm->txq);
+
+       hu->priv = bcm;
+       return 0;
+}
+
+static int bcm_close(struct hci_uart *hu)
+{
+       struct bcm_data *bcm = hu->priv;
+
+       BT_DBG("hu %p", hu);
+
+       skb_queue_purge(&bcm->txq);
+       kfree_skb(bcm->rx_skb);
+       kfree(bcm);
+
+       hu->priv = NULL;
+       return 0;
+}
+
+static int bcm_flush(struct hci_uart *hu)
+{
+       struct bcm_data *bcm = hu->priv;
+
+       BT_DBG("hu %p", hu);
+
+       skb_queue_purge(&bcm->txq);
+
+       return 0;
+}
+
+static int bcm_setup(struct hci_uart *hu)
+{
+       BT_DBG("hu %p", hu);
+
+       hu->hdev->set_bdaddr = btbcm_set_bdaddr;
+
+       return btbcm_setup_patchram(hu->hdev);
+}
+
+static const struct h4_recv_pkt bcm_recv_pkts[] = {
+       { H4_RECV_ACL,   .recv = hci_recv_frame },
+       { H4_RECV_SCO,   .recv = hci_recv_frame },
+       { H4_RECV_EVENT, .recv = hci_recv_frame },
+};
+
+static int bcm_recv(struct hci_uart *hu, const void *data, int count)
+{
+       struct bcm_data *bcm = hu->priv;
+
+       if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
+               return -EUNATCH;
+
+       bcm->rx_skb = h4_recv_buf(hu->hdev, bcm->rx_skb, data, count,
+                                 bcm_recv_pkts, ARRAY_SIZE(bcm_recv_pkts));
+       if (IS_ERR(bcm->rx_skb)) {
+               int err = PTR_ERR(bcm->rx_skb);
+               BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
+               return err;
+       }
+
+       return count;
+}
+
+static int bcm_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+       struct bcm_data *bcm = hu->priv;
+
+       BT_DBG("hu %p skb %p", hu, skb);
+
+       /* Prepend skb with frame type */
+       memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+       skb_queue_tail(&bcm->txq, skb);
+
+       return 0;
+}
+
+static struct sk_buff *bcm_dequeue(struct hci_uart *hu)
+{
+       struct bcm_data *bcm = hu->priv;
+
+       return skb_dequeue(&bcm->txq);
+}
+
+static const struct hci_uart_proto bcm_proto = {
+       .id             = HCI_UART_BCM,
+       .name           = "BCM",
+       .open           = bcm_open,
+       .close          = bcm_close,
+       .flush          = bcm_flush,
+       .setup          = bcm_setup,
+       .recv           = bcm_recv,
+       .enqueue        = bcm_enqueue,
+       .dequeue        = bcm_dequeue,
+};
+
+int __init bcm_init(void)
+{
+       return hci_uart_register_proto(&bcm_proto);
+}
+
+int __exit bcm_deinit(void)
+{
+       return hci_uart_unregister_proto(&bcm_proto);
+}
index 21cc45b34f134fe8028606e2595d381a125e0926..dc8e3d4356a0ff40f1b27ddc63de3e7eefef7a87 100644 (file)
@@ -47,8 +47,6 @@
 
 #include "hci_uart.h"
 
-#define VERSION "0.3"
-
 static bool txcrc = 1;
 static bool hciextn = 1;
 
@@ -554,10 +552,10 @@ static u16 bscp_get_crc(struct bcsp_struct *bcsp)
 }
 
 /* Recv data */
-static int bcsp_recv(struct hci_uart *hu, void *data, int count)
+static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
 {
        struct bcsp_struct *bcsp = hu->priv;
-       unsigned char *ptr;
+       const unsigned char *ptr;
 
        BT_DBG("hu %p count %d rx_state %d rx_count %ld", 
                hu, count, bcsp->rx_state, bcsp->rx_count);
@@ -735,8 +733,9 @@ static int bcsp_close(struct hci_uart *hu)
        return 0;
 }
 
-static struct hci_uart_proto bcsp = {
+static const struct hci_uart_proto bcsp = {
        .id             = HCI_UART_BCSP,
+       .name           = "BCSP",
        .open           = bcsp_open,
        .close          = bcsp_close,
        .enqueue        = bcsp_enqueue,
@@ -747,14 +746,7 @@ static struct hci_uart_proto bcsp = {
 
 int __init bcsp_init(void)
 {
-       int err = hci_uart_register_proto(&bcsp);
-
-       if (!err)
-               BT_INFO("HCI BCSP protocol initialized");
-       else
-               BT_ERR("HCI BCSP protocol registration failed");
-
-       return err;
+       return hci_uart_register_proto(&bcsp);
 }
 
 int __exit bcsp_deinit(void)
index 66db9a803373efb92c8966c9f69fdb6b8ec7aa59..f7190f01e1357b764504b0084128804ca87d6a6e 100644 (file)
 #include <linux/signal.h>
 #include <linux/ioctl.h>
 #include <linux/skbuff.h>
+#include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
 #include "hci_uart.h"
 
-#define VERSION "1.2"
-
 struct h4_struct {
-       unsigned long rx_state;
-       unsigned long rx_count;
        struct sk_buff *rx_skb;
        struct sk_buff_head txq;
 };
@@ -117,18 +114,26 @@ static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb)
        return 0;
 }
 
+static const struct h4_recv_pkt h4_recv_pkts[] = {
+       { H4_RECV_ACL,   .recv = hci_recv_frame },
+       { H4_RECV_SCO,   .recv = hci_recv_frame },
+       { H4_RECV_EVENT, .recv = hci_recv_frame },
+};
+
 /* Recv data */
-static int h4_recv(struct hci_uart *hu, void *data, int count)
+static int h4_recv(struct hci_uart *hu, const void *data, int count)
 {
-       int ret;
+       struct h4_struct *h4 = hu->priv;
 
        if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
                return -EUNATCH;
 
-       ret = hci_recv_stream_fragment(hu->hdev, data, count);
-       if (ret < 0) {
-               BT_ERR("Frame Reassembly Failed");
-               return ret;
+       h4->rx_skb = h4_recv_buf(hu->hdev, h4->rx_skb, data, count,
+                                h4_recv_pkts, ARRAY_SIZE(h4_recv_pkts));
+       if (IS_ERR(h4->rx_skb)) {
+               int err = PTR_ERR(h4->rx_skb);
+               BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
+               return err;
        }
 
        return count;
@@ -140,8 +145,9 @@ static struct sk_buff *h4_dequeue(struct hci_uart *hu)
        return skb_dequeue(&h4->txq);
 }
 
-static struct hci_uart_proto h4p = {
+static const struct hci_uart_proto h4p = {
        .id             = HCI_UART_H4,
+       .name           = "H4",
        .open           = h4_open,
        .close          = h4_close,
        .recv           = h4_recv,
@@ -152,17 +158,105 @@ static struct hci_uart_proto h4p = {
 
 int __init h4_init(void)
 {
-       int err = hci_uart_register_proto(&h4p);
-
-       if (!err)
-               BT_INFO("HCI H4 protocol initialized");
-       else
-               BT_ERR("HCI H4 protocol registration failed");
-
-       return err;
+       return hci_uart_register_proto(&h4p);
 }
 
 int __exit h4_deinit(void)
 {
        return hci_uart_unregister_proto(&h4p);
 }
+
+struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
+                           const unsigned char *buffer, int count,
+                           const struct h4_recv_pkt *pkts, int pkts_count)
+{
+       while (count) {
+               int i, len;
+
+               if (!skb) {
+                       for (i = 0; i < pkts_count; i++) {
+                               if (buffer[0] != (&pkts[i])->type)
+                                       continue;
+
+                               skb = bt_skb_alloc((&pkts[i])->maxlen,
+                                                  GFP_ATOMIC);
+                               if (!skb)
+                                       return ERR_PTR(-ENOMEM);
+
+                               bt_cb(skb)->pkt_type = (&pkts[i])->type;
+                               bt_cb(skb)->expect = (&pkts[i])->hlen;
+                               break;
+                       }
+
+                       /* Check for invalid packet type */
+                       if (!skb)
+                               return ERR_PTR(-EILSEQ);
+
+                       count -= 1;
+                       buffer += 1;
+               }
+
+               len = min_t(uint, bt_cb(skb)->expect - skb->len, count);
+               memcpy(skb_put(skb, len), buffer, len);
+
+               count -= len;
+               buffer += len;
+
+               /* Check for partial packet */
+               if (skb->len < bt_cb(skb)->expect)
+                       continue;
+
+               for (i = 0; i < pkts_count; i++) {
+                       if (bt_cb(skb)->pkt_type == (&pkts[i])->type)
+                               break;
+               }
+
+               if (i >= pkts_count) {
+                       kfree_skb(skb);
+                       return ERR_PTR(-EILSEQ);
+               }
+
+               if (skb->len == (&pkts[i])->hlen) {
+                       u16 dlen;
+
+                       switch ((&pkts[i])->lsize) {
+                       case 0:
+                               /* No variable data length */
+                               (&pkts[i])->recv(hdev, skb);
+                               skb = NULL;
+                               break;
+                       case 1:
+                               /* Single octet variable length */
+                               dlen = skb->data[(&pkts[i])->loff];
+                               bt_cb(skb)->expect += dlen;
+
+                               if (skb_tailroom(skb) < dlen) {
+                                       kfree_skb(skb);
+                                       return ERR_PTR(-EMSGSIZE);
+                               }
+                               break;
+                       case 2:
+                               /* Double octet variable length */
+                               dlen = get_unaligned_le16(skb->data +
+                                                         (&pkts[i])->loff);
+                               bt_cb(skb)->expect += dlen;
+
+                               if (skb_tailroom(skb) < dlen) {
+                                       kfree_skb(skb);
+                                       return ERR_PTR(-EMSGSIZE);
+                               }
+                               break;
+                       default:
+                               /* Unsupported variable length */
+                               kfree_skb(skb);
+                               return ERR_PTR(-EILSEQ);
+                       }
+               } else {
+                       /* Complete frame */
+                       (&pkts[i])->recv(hdev, skb);
+                       skb = NULL;
+               }
+       }
+
+       return skb;
+}
index ec0fa7732c0d953c359c86f16a5f307a00f3d930..3455cecc9ecfe630c3331d67d22715ddb60cfcf8 100644 (file)
@@ -511,10 +511,10 @@ static void h5_reset_rx(struct h5 *h5)
        clear_bit(H5_RX_ESC, &h5->flags);
 }
 
-static int h5_recv(struct hci_uart *hu, void *data, int count)
+static int h5_recv(struct hci_uart *hu, const void *data, int count)
 {
        struct h5 *h5 = hu->priv;
-       unsigned char *ptr = data;
+       const unsigned char *ptr = data;
 
        BT_DBG("%s pending %zu count %d", hu->hdev->name, h5->rx_pending,
               count);
@@ -743,8 +743,9 @@ static int h5_flush(struct hci_uart *hu)
        return 0;
 }
 
-static struct hci_uart_proto h5p = {
+static const struct hci_uart_proto h5p = {
        .id             = HCI_UART_3WIRE,
+       .name           = "Three-wire (H5)",
        .open           = h5_open,
        .close          = h5_close,
        .recv           = h5_recv,
@@ -755,14 +756,7 @@ static struct hci_uart_proto h5p = {
 
 int __init h5_init(void)
 {
-       int err = hci_uart_register_proto(&h5p);
-
-       if (!err)
-               BT_INFO("HCI Three-wire UART (H5) protocol initialized");
-       else
-               BT_ERR("HCI Three-wire UART (H5) protocol init failed");
-
-       return err;
+       return hci_uart_register_proto(&h5p);
 }
 
 int __exit h5_deinit(void)
diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c
new file mode 100644 (file)
index 0000000..5dd07bf
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ *
+ *  Bluetooth HCI UART driver for Intel devices
+ *
+ *  Copyright (C) 2015  Intel 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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_uart.h"
index 1363dc616ace2f751443d6f70adeea976f5a461d..5c9a73f026649c99a1b56d2f91d5981e29b905c9 100644 (file)
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
+#include "btintel.h"
+#include "btbcm.h"
 #include "hci_uart.h"
 
-#define VERSION "2.2"
+#define VERSION "2.3"
 
-static struct hci_uart_proto *hup[HCI_UART_MAX_PROTO];
+static const struct hci_uart_proto *hup[HCI_UART_MAX_PROTO];
 
-int hci_uart_register_proto(struct hci_uart_proto *p)
+int hci_uart_register_proto(const struct hci_uart_proto *p)
 {
        if (p->id >= HCI_UART_MAX_PROTO)
                return -EINVAL;
@@ -60,10 +62,12 @@ int hci_uart_register_proto(struct hci_uart_proto *p)
 
        hup[p->id] = p;
 
+       BT_INFO("HCI UART protocol %s registered", p->name);
+
        return 0;
 }
 
-int hci_uart_unregister_proto(struct hci_uart_proto *p)
+int hci_uart_unregister_proto(const struct hci_uart_proto *p)
 {
        if (p->id >= HCI_UART_MAX_PROTO)
                return -EINVAL;
@@ -76,7 +80,7 @@ int hci_uart_unregister_proto(struct hci_uart_proto *p)
        return 0;
 }
 
-static struct hci_uart_proto *hci_uart_get_proto(unsigned int id)
+static const struct hci_uart_proto *hci_uart_get_proto(unsigned int id)
 {
        if (id >= HCI_UART_MAX_PROTO)
                return NULL;
@@ -264,10 +268,48 @@ static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 static int hci_uart_setup(struct hci_dev *hdev)
 {
        struct hci_uart *hu = hci_get_drvdata(hdev);
+       struct hci_rp_read_local_version *ver;
+       struct sk_buff *skb;
 
        if (hu->proto->setup)
                return hu->proto->setup(hu);
 
+       if (!test_bit(HCI_UART_VND_DETECT, &hu->hdev_flags))
+               return 0;
+
+       skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
+                            HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               BT_ERR("%s: Reading local version information failed (%ld)",
+                      hdev->name, PTR_ERR(skb));
+               return 0;
+       }
+
+       if (skb->len != sizeof(*ver)) {
+               BT_ERR("%s: Event length mismatch for version information",
+                      hdev->name);
+               goto done;
+       }
+
+       ver = (struct hci_rp_read_local_version *)skb->data;
+
+       switch (le16_to_cpu(ver->manufacturer)) {
+#ifdef CONFIG_BT_HCIUART_INTEL
+       case 2:
+               hdev->set_bdaddr = btintel_set_bdaddr;
+               btintel_check_bdaddr(hdev);
+               break;
+#endif
+#ifdef CONFIG_BT_HCIUART_BCM
+       case 15:
+               hdev->set_bdaddr = btbcm_set_bdaddr;
+               btbcm_check_bdaddr(hdev);
+               break;
+#endif
+       }
+
+done:
+       kfree_skb(skb);
        return 0;
 }
 
@@ -326,7 +368,7 @@ static int hci_uart_tty_open(struct tty_struct *tty)
  */
 static void hci_uart_tty_close(struct tty_struct *tty)
 {
-       struct hci_uart *hu = (void *)tty->disc_data;
+       struct hci_uart *hu = tty->disc_data;
        struct hci_dev *hdev;
 
        BT_DBG("tty %p", tty);
@@ -365,7 +407,7 @@ static void hci_uart_tty_close(struct tty_struct *tty)
  */
 static void hci_uart_tty_wakeup(struct tty_struct *tty)
 {
-       struct hci_uart *hu = (void *)tty->disc_data;
+       struct hci_uart *hu = tty->disc_data;
 
        BT_DBG("");
 
@@ -393,9 +435,10 @@ static void hci_uart_tty_wakeup(struct tty_struct *tty)
  *
  * Return Value:    None
  */
-static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *flags, int count)
+static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
+                                char *flags, int count)
 {
-       struct hci_uart *hu = (void *)tty->disc_data;
+       struct hci_uart *hu = tty->disc_data;
 
        if (!hu || tty != hu->tty)
                return;
@@ -404,7 +447,7 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *f
                return;
 
        spin_lock(&hu->rx_lock);
-       hu->proto->recv(hu, (void *) data, count);
+       hu->proto->recv(hu, data, count);
 
        if (hu->hdev)
                hu->hdev->stat.byte_rx += count;
@@ -469,7 +512,7 @@ static int hci_uart_register_dev(struct hci_uart *hu)
 
 static int hci_uart_set_proto(struct hci_uart *hu, int id)
 {
-       struct hci_uart_proto *p;
+       const struct hci_uart_proto *p;
        int err;
 
        p = hci_uart_get_proto(id);
@@ -497,7 +540,8 @@ static int hci_uart_set_flags(struct hci_uart *hu, unsigned long flags)
                                    BIT(HCI_UART_RESET_ON_INIT) |
                                    BIT(HCI_UART_CREATE_AMP) |
                                    BIT(HCI_UART_INIT_PENDING) |
-                                   BIT(HCI_UART_EXT_CONFIG);
+                                   BIT(HCI_UART_EXT_CONFIG) |
+                                   BIT(HCI_UART_VND_DETECT);
 
        if (flags & ~valid_flags)
                return -EINVAL;
@@ -520,10 +564,10 @@ static int hci_uart_set_flags(struct hci_uart *hu, unsigned long flags)
  *
  * Return Value:    Command dependent
  */
-static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file,
-                                       unsigned int cmd, unsigned long arg)
+static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file *file,
+                             unsigned int cmd, unsigned long arg)
 {
-       struct hci_uart *hu = (void *)tty->disc_data;
+       struct hci_uart *hu = tty->disc_data;
        int err = 0;
 
        BT_DBG("");
@@ -577,19 +621,19 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file,
  * We don't provide read/write/poll interface for user space.
  */
 static ssize_t hci_uart_tty_read(struct tty_struct *tty, struct file *file,
-                                       unsigned char __user *buf, size_t nr)
+                                unsigned char __user *buf, size_t nr)
 {
        return 0;
 }
 
 static ssize_t hci_uart_tty_write(struct tty_struct *tty, struct file *file,
-                                       const unsigned char *data, size_t count)
+                                 const unsigned char *data, size_t count)
 {
        return 0;
 }
 
 static unsigned int hci_uart_tty_poll(struct tty_struct *tty,
-                                       struct file *filp, poll_table *wait)
+                                     struct file *filp, poll_table *wait)
 {
        return 0;
 }
@@ -637,6 +681,9 @@ static int __init hci_uart_init(void)
 #ifdef CONFIG_BT_HCIUART_3WIRE
        h5_init();
 #endif
+#ifdef CONFIG_BT_HCIUART_BCM
+       bcm_init();
+#endif
 
        return 0;
 }
@@ -660,6 +707,9 @@ static void __exit hci_uart_exit(void)
 #ifdef CONFIG_BT_HCIUART_3WIRE
        h5_deinit();
 #endif
+#ifdef CONFIG_BT_HCIUART_BCM
+       bcm_deinit();
+#endif
 
        /* Release tty registration of line discipline */
        err = tty_unregister_ldisc(N_HCI);
index 69a90b1b5ff56131aa7a2e51b7d18aabba28b560..9ee24b075f7973e7578043c74e7421ddd535b7b5 100644 (file)
@@ -370,10 +370,10 @@ static inline int ll_check_data_len(struct hci_dev *hdev, struct ll_struct *ll,
 }
 
 /* Recv data */
-static int ll_recv(struct hci_uart *hu, void *data, int count)
+static int ll_recv(struct hci_uart *hu, const void *data, int count)
 {
        struct ll_struct *ll = hu->priv;
-       char *ptr;
+       const char *ptr;
        struct hci_event_hdr *eh;
        struct hci_acl_hdr   *ah;
        struct hci_sco_hdr   *sh;
@@ -505,8 +505,9 @@ static struct sk_buff *ll_dequeue(struct hci_uart *hu)
        return skb_dequeue(&ll->txq);
 }
 
-static struct hci_uart_proto llp = {
+static const struct hci_uart_proto llp = {
        .id             = HCI_UART_LL,
+       .name           = "LL",
        .open           = ll_open,
        .close          = ll_close,
        .recv           = ll_recv,
@@ -517,14 +518,7 @@ static struct hci_uart_proto llp = {
 
 int __init ll_init(void)
 {
-       int err = hci_uart_register_proto(&llp);
-
-       if (!err)
-               BT_INFO("HCILL protocol initialized");
-       else
-               BT_ERR("HCILL protocol registration failed");
-
-       return err;
+       return hci_uart_register_proto(&llp);
 }
 
 int __exit ll_deinit(void)
index 074ed29092b487b700838b34f1504e8379335dd9..72120a5ba13c8a6056649a7ec51a617eac8cf536 100644 (file)
@@ -35,7 +35,7 @@
 #define HCIUARTGETFLAGS                _IOR('U', 204, int)
 
 /* UART protocols */
-#define HCI_UART_MAX_PROTO     6
+#define HCI_UART_MAX_PROTO     8
 
 #define HCI_UART_H4    0
 #define HCI_UART_BCSP  1
 #define HCI_UART_H4DS  3
 #define HCI_UART_LL    4
 #define HCI_UART_ATH3K 5
+#define HCI_UART_INTEL 6
+#define HCI_UART_BCM   7
 
 #define HCI_UART_RAW_DEVICE    0
 #define HCI_UART_RESET_ON_INIT 1
 #define HCI_UART_CREATE_AMP    2
 #define HCI_UART_INIT_PENDING  3
 #define HCI_UART_EXT_CONFIG    4
+#define HCI_UART_VND_DETECT    5
 
 struct hci_uart;
 
 struct hci_uart_proto {
        unsigned int id;
+       const char *name;
        int (*open)(struct hci_uart *hu);
        int (*close)(struct hci_uart *hu);
        int (*flush)(struct hci_uart *hu);
-       int (*recv)(struct hci_uart *hu, void *data, int len);
-       int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb);
        int (*setup)(struct hci_uart *hu);
+       int (*recv)(struct hci_uart *hu, const void *data, int len);
+       int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb);
        struct sk_buff *(*dequeue)(struct hci_uart *hu);
 };
 
@@ -72,7 +76,7 @@ struct hci_uart {
        struct work_struct      init_ready;
        struct work_struct      write_work;
 
-       struct hci_uart_proto   *proto;
+       const struct hci_uart_proto *proto;
        void                    *priv;
 
        struct sk_buff          *tx_skb;
@@ -88,14 +92,48 @@ struct hci_uart {
 #define HCI_UART_SENDING       1
 #define HCI_UART_TX_WAKEUP     2
 
-int hci_uart_register_proto(struct hci_uart_proto *p);
-int hci_uart_unregister_proto(struct hci_uart_proto *p);
+int hci_uart_register_proto(const struct hci_uart_proto *p);
+int hci_uart_unregister_proto(const struct hci_uart_proto *p);
 int hci_uart_tx_wakeup(struct hci_uart *hu);
 int hci_uart_init_ready(struct hci_uart *hu);
 
 #ifdef CONFIG_BT_HCIUART_H4
 int h4_init(void);
 int h4_deinit(void);
+
+struct h4_recv_pkt {
+       u8  type;       /* Packet type */
+       u8  hlen;       /* Header length */
+       u8  loff;       /* Data length offset in header */
+       u8  lsize;      /* Data length field size */
+       u16 maxlen;     /* Max overall packet length */
+       int (*recv)(struct hci_dev *hdev, struct sk_buff *skb);
+};
+
+#define H4_RECV_ACL \
+       .type = HCI_ACLDATA_PKT, \
+       .hlen = HCI_ACL_HDR_SIZE, \
+       .loff = 2, \
+       .lsize = 2, \
+       .maxlen = HCI_MAX_FRAME_SIZE \
+
+#define H4_RECV_SCO \
+       .type = HCI_SCODATA_PKT, \
+       .hlen = HCI_SCO_HDR_SIZE, \
+       .loff = 2, \
+       .lsize = 1, \
+       .maxlen = HCI_MAX_SCO_SIZE
+
+#define H4_RECV_EVENT \
+       .type = HCI_EVENT_PKT, \
+       .hlen = HCI_EVENT_HDR_SIZE, \
+       .loff = 1, \
+       .lsize = 1, \
+       .maxlen = HCI_MAX_EVENT_SIZE
+
+struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
+                           const unsigned char *buffer, int count,
+                           const struct h4_recv_pkt *pkts, int pkts_count);
 #endif
 
 #ifdef CONFIG_BT_HCIUART_BCSP
@@ -117,3 +155,8 @@ int ath_deinit(void);
 int h5_init(void);
 int h5_deinit(void);
 #endif
+
+#ifdef CONFIG_BT_HCIUART_BCM
+int bcm_init(void);
+int bcm_deinit(void);
+#endif
index 374696de796cd707c99cf150ec12349335d5f958..fbd54f0e32e8d7baf7e1f2de3c5155201aeccfea 100644 (file)
@@ -1428,8 +1428,10 @@ static void ad_port_selection_logic(struct port *port, bool *update_slave_arr)
                        else
                                port->aggregator->is_individual = true;
 
-                       port->aggregator->actor_admin_aggregator_key = port->actor_admin_port_key;
-                       port->aggregator->actor_oper_aggregator_key = port->actor_oper_port_key;
+                       port->aggregator->actor_admin_aggregator_key =
+                               port->actor_admin_port_key;
+                       port->aggregator->actor_oper_aggregator_key =
+                               port->actor_oper_port_key;
                        port->aggregator->partner_system =
                                port->partner_oper.system;
                        port->aggregator->partner_system_priority =
@@ -1755,14 +1757,9 @@ static void ad_initialize_port(struct port *port, int lacp_fast)
        };
 
        if (port) {
-               port->actor_port_number = 1;
                port->actor_port_priority = 0xff;
-               port->actor_system = null_mac_addr;
-               port->actor_system_priority = 0xffff;
                port->actor_port_aggregator_identifier = 0;
                port->ntt = false;
-               port->actor_admin_port_key = 1;
-               port->actor_oper_port_key  = 1;
                port->actor_admin_port_state = AD_STATE_AGGREGATION |
                                               AD_STATE_LACP_ACTIVITY;
                port->actor_oper_port_state  = AD_STATE_AGGREGATION |
@@ -1784,8 +1781,6 @@ static void ad_initialize_port(struct port *port, int lacp_fast)
                port->sm_mux_state = 0;
                port->sm_mux_timer_counter = 0;
                port->sm_tx_state = 0;
-               port->sm_tx_timer_counter = 0;
-               port->slave = NULL;
                port->aggregator = NULL;
                port->next_port_in_aggregator = NULL;
                port->transaction_id = 0;
@@ -1968,8 +1963,6 @@ void bond_3ad_bind_slave(struct slave *slave)
                 * lacpdu's are sent in one second)
                 */
                port->sm_tx_timer_counter = ad_ticks_per_sec/AD_MAX_TX_IN_SECOND;
-               port->aggregator = NULL;
-               port->next_port_in_aggregator = NULL;
 
                __disable_port(port);
 
@@ -2332,8 +2325,8 @@ void bond_3ad_adapter_speed_changed(struct slave *slave)
        spin_lock_bh(&slave->bond->mode_lock);
 
        port->actor_admin_port_key &= ~AD_SPEED_KEY_MASKS;
-       port->actor_oper_port_key = port->actor_admin_port_key |=
-               (__get_link_speed(port) << 1);
+       port->actor_admin_port_key |= __get_link_speed(port) << 1;
+       port->actor_oper_port_key = port->actor_admin_port_key;
        netdev_dbg(slave->bond->dev, "Port %d changed speed\n", port->actor_port_number);
        /* there is no need to reselect a new aggregator, just signal the
         * state machines to reinitialize
@@ -2365,8 +2358,8 @@ void bond_3ad_adapter_duplex_changed(struct slave *slave)
        spin_lock_bh(&slave->bond->mode_lock);
 
        port->actor_admin_port_key &= ~AD_DUPLEX_KEY_MASKS;
-       port->actor_oper_port_key = port->actor_admin_port_key |=
-               __get_duplex(port);
+       port->actor_admin_port_key |= __get_duplex(port);
+       port->actor_oper_port_key = port->actor_admin_port_key;
        netdev_dbg(slave->bond->dev, "Port %d slave %s changed duplex\n",
                   port->actor_port_number, slave->dev->name);
        if (port->actor_oper_port_key & AD_DUPLEX_KEY_MASKS)
@@ -2407,24 +2400,19 @@ void bond_3ad_handle_link_change(struct slave *slave, char link)
         * on link up we are forcing recheck on the duplex and speed since
         * some of he adaptors(ce1000.lan) report.
         */
+       port->actor_admin_port_key &= ~(AD_DUPLEX_KEY_MASKS|AD_SPEED_KEY_MASKS);
        if (link == BOND_LINK_UP) {
                port->is_enabled = true;
-               port->actor_admin_port_key &= ~AD_DUPLEX_KEY_MASKS;
-               port->actor_oper_port_key = port->actor_admin_port_key |=
-                       __get_duplex(port);
-               port->actor_admin_port_key &= ~AD_SPEED_KEY_MASKS;
-               port->actor_oper_port_key = port->actor_admin_port_key |=
-                       (__get_link_speed(port) << 1);
-               if (port->actor_oper_port_key & AD_DUPLEX_KEY_MASKS)
+               port->actor_admin_port_key |=
+                       (__get_link_speed(port) << 1) | __get_duplex(port);
+               if (port->actor_admin_port_key & AD_DUPLEX_KEY_MASKS)
                        port->sm_vars |= AD_PORT_LACP_ENABLED;
        } else {
                /* link has failed */
                port->is_enabled = false;
-               port->actor_admin_port_key &= ~AD_DUPLEX_KEY_MASKS;
-               port->actor_oper_port_key = (port->actor_admin_port_key &=
-                                            ~AD_SPEED_KEY_MASKS);
                port->sm_vars &= ~AD_PORT_LACP_ENABLED;
        }
+       port->actor_oper_port_key = port->actor_admin_port_key;
        netdev_dbg(slave->bond->dev, "Port %d changed link status to %s\n",
                   port->actor_port_number,
                   link == BOND_LINK_UP ? "UP" : "DOWN");
index 80dd7a92f3574b5d09f04d077def087016c136e0..21d9497518fde356f45e6e0eb0632368d680116b 100644 (file)
@@ -853,6 +853,22 @@ static int xgbe_set_mac_address(struct xgbe_prv_data *pdata, u8 *addr)
        return 0;
 }
 
+static int xgbe_config_rx_mode(struct xgbe_prv_data *pdata)
+{
+       struct net_device *netdev = pdata->netdev;
+       unsigned int pr_mode, am_mode;
+
+       pr_mode = ((netdev->flags & IFF_PROMISC) != 0);
+       am_mode = ((netdev->flags & IFF_ALLMULTI) != 0);
+
+       xgbe_set_promiscuous_mode(pdata, pr_mode);
+       xgbe_set_all_multicast_mode(pdata, am_mode);
+
+       xgbe_add_mac_addresses(pdata);
+
+       return 0;
+}
+
 static int xgbe_read_mmd_regs(struct xgbe_prv_data *pdata, int prtad,
                              int mmd_reg)
 {
@@ -1101,9 +1117,24 @@ static void xgbe_tx_desc_init(struct xgbe_channel *channel)
        DBGPR("<--tx_desc_init\n");
 }
 
-static void xgbe_rx_desc_reset(struct xgbe_ring_data *rdata)
+static void xgbe_rx_desc_reset(struct xgbe_prv_data *pdata,
+                              struct xgbe_ring_data *rdata, unsigned int index)
 {
        struct xgbe_ring_desc *rdesc = rdata->rdesc;
+       unsigned int rx_usecs = pdata->rx_usecs;
+       unsigned int rx_frames = pdata->rx_frames;
+       unsigned int inte;
+
+       if (!rx_usecs && !rx_frames) {
+               /* No coalescing, interrupt for every descriptor */
+               inte = 1;
+       } else {
+               /* Set interrupt based on Rx frame coalescing setting */
+               if (rx_frames && !((index + 1) % rx_frames))
+                       inte = 1;
+               else
+                       inte = 0;
+       }
 
        /* Reset the Rx descriptor
         *   Set buffer 1 (lo) address to header dma address (lo)
@@ -1117,8 +1148,7 @@ static void xgbe_rx_desc_reset(struct xgbe_ring_data *rdata)
        rdesc->desc2 = cpu_to_le32(lower_32_bits(rdata->rx.buf.dma));
        rdesc->desc3 = cpu_to_le32(upper_32_bits(rdata->rx.buf.dma));
 
-       XGMAC_SET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, INTE,
-                         rdata->interrupt ? 1 : 0);
+       XGMAC_SET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, INTE, inte);
 
        /* Since the Rx DMA engine is likely running, make sure everything
         * is written to the descriptor(s) before setting the OWN bit
@@ -1138,26 +1168,16 @@ static void xgbe_rx_desc_init(struct xgbe_channel *channel)
        struct xgbe_ring *ring = channel->rx_ring;
        struct xgbe_ring_data *rdata;
        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 = XGBE_GET_DESC_DATA(ring, i);
 
-               /* Set interrupt on completion bit as appropriate */
-               if (rx_coalesce && (!rx_frames || ((i + 1) % rx_frames)))
-                       rdata->interrupt = 0;
-               else
-                       rdata->interrupt = 1;
-
                /* Initialize Rx descriptor */
-               xgbe_rx_desc_reset(rdata);
+               xgbe_rx_desc_reset(pdata, rdata, i);
        }
 
        /* Update the total number of Rx descriptors */
@@ -2804,6 +2824,7 @@ static int xgbe_init(struct xgbe_prv_data *pdata)
         * Initialize MAC related features
         */
        xgbe_config_mac_address(pdata);
+       xgbe_config_rx_mode(pdata);
        xgbe_config_jumbo_enable(pdata);
        xgbe_config_flow_control(pdata);
        xgbe_config_mac_speed(pdata);
@@ -2823,10 +2844,8 @@ void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *hw_if)
 
        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->add_mac_addresses = xgbe_add_mac_addresses;
        hw_if->set_mac_address = xgbe_set_mac_address;
+       hw_if->config_rx_mode = xgbe_config_rx_mode;
 
        hw_if->enable_rx_csum = xgbe_enable_rx_csum;
        hw_if->disable_rx_csum = xgbe_disable_rx_csum;
index 347fe2419a18a0514b7ea3f29350a0c1e33deae4..db84ddcfec8464191a3edcccfd87c869ac1c5a7c 100644 (file)
 
 static int xgbe_one_poll(struct napi_struct *, int);
 static int xgbe_all_poll(struct napi_struct *, int);
-static void xgbe_set_rx_mode(struct net_device *);
 
 static int xgbe_alloc_channels(struct xgbe_prv_data *pdata)
 {
@@ -952,8 +951,6 @@ static int xgbe_start(struct xgbe_prv_data *pdata)
 
        DBGPR("-->xgbe_start\n");
 
-       xgbe_set_rx_mode(netdev);
-
        hw_if->init(pdata);
 
        phy_start(pdata->phydev);
@@ -1533,17 +1530,10 @@ 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);
-
-       hw_if->set_promiscuous_mode(pdata, pr_mode);
-       hw_if->set_all_multicast_mode(pdata, am_mode);
-
-       hw_if->add_mac_addresses(pdata);
+       hw_if->config_rx_mode(pdata);
 
        DBGPR("<--xgbe_set_rx_mode\n");
 }
@@ -1610,6 +1600,14 @@ static int xgbe_change_mtu(struct net_device *netdev, int mtu)
        return 0;
 }
 
+static void xgbe_tx_timeout(struct net_device *netdev)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+
+       netdev_warn(netdev, "tx timeout, device restarting\n");
+       schedule_work(&pdata->restart_work);
+}
+
 static struct rtnl_link_stats64 *xgbe_get_stats64(struct net_device *netdev,
                                                  struct rtnl_link_stats64 *s)
 {
@@ -1774,6 +1772,7 @@ static const struct net_device_ops xgbe_netdev_ops = {
        .ndo_validate_addr      = eth_validate_addr,
        .ndo_do_ioctl           = xgbe_ioctl,
        .ndo_change_mtu         = xgbe_change_mtu,
+       .ndo_tx_timeout         = xgbe_tx_timeout,
        .ndo_get_stats64        = xgbe_get_stats64,
        .ndo_vlan_rx_add_vid    = xgbe_vlan_rx_add_vid,
        .ndo_vlan_rx_kill_vid   = xgbe_vlan_rx_kill_vid,
@@ -1806,7 +1805,7 @@ static void xgbe_rx_refresh(struct xgbe_channel *channel)
                if (desc_if->map_rx_buffer(pdata, ring, rdata))
                        break;
 
-               hw_if->rx_desc_reset(rdata);
+               hw_if->rx_desc_reset(pdata, rdata, ring->dirty);
 
                ring->dirty++;
        }
index b4f6eaaa08f0732211435c011e87e518c63cfbad..5f149e8ee20f0fa1a0878c37f2d44427fbe75f7f 100644 (file)
@@ -424,16 +424,6 @@ static int xgbe_set_coalesce(struct net_device *netdev,
            (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_usecs = ec->rx_coalesce_usecs;
        rx_frames = ec->rx_max_coalesced_frames;
index 2e4c22d94a6bef50e0606b9fce861c29f88c24d4..7149053849008de10da3be7eb054884b4a808f8c 100644 (file)
@@ -491,6 +491,9 @@ static int xgbe_probe(struct platform_device *pdev)
 
        netdev->priv_flags |= IFF_UNICAST_FLT;
 
+       /* Use default watchdog timeout */
+       netdev->watchdog_timeo = 0;
+
        xgbe_init_rx_coalesce(pdata);
        xgbe_init_tx_coalesce(pdata);
 
index dd742426eb0425e9c4a7b041ee4cf3764694515e..e62dfa2deab67565cbb6c62116f4f8b6b722384f 100644 (file)
@@ -325,8 +325,6 @@ struct xgbe_ring_data {
        struct xgbe_tx_ring_data tx;    /* Tx-related data */
        struct xgbe_rx_ring_data rx;    /* Rx-related data */
 
-       unsigned int interrupt;         /* Interrupt indicator */
-
        unsigned int mapped_as_page;
 
        /* Incomplete receive save location.  If the budget is exhausted
@@ -497,10 +495,8 @@ struct xgbe_mmc_stats {
 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 (*add_mac_addresses)(struct xgbe_prv_data *);
        int (*set_mac_address)(struct xgbe_prv_data *, u8 *addr);
+       int (*config_rx_mode)(struct xgbe_prv_data *);
 
        int (*enable_rx_csum)(struct xgbe_prv_data *);
        int (*disable_rx_csum)(struct xgbe_prv_data *);
@@ -536,8 +532,9 @@ struct xgbe_hw_if {
        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 *);
+       void (*rx_desc_reset)(struct xgbe_prv_data *, struct xgbe_ring_data *,
+                             unsigned int);
        int (*is_last_desc)(struct xgbe_ring_desc *);
        int (*is_context_desc)(struct xgbe_ring_desc *);
        void (*tx_start_xmit)(struct xgbe_channel *, struct xgbe_ring *);
index 448a32309dd08c79c99bca7692fea10d429c1b41..9f5387249f242374437581e6c2df7c037917f83a 100644 (file)
@@ -1956,12 +1956,12 @@ static struct net_device_stats *macb_get_stats(struct net_device *dev)
                            hwstat->rx_oversize_pkts +
                            hwstat->rx_jabbers +
                            hwstat->rx_undersize_pkts +
-                           hwstat->sqe_test_errors +
                            hwstat->rx_length_mismatch);
        nstat->tx_errors = (hwstat->tx_late_cols +
                            hwstat->tx_excessive_cols +
                            hwstat->tx_underruns +
-                           hwstat->tx_carrier_errors);
+                           hwstat->tx_carrier_errors +
+                           hwstat->sqe_test_errors);
        nstat->collisions = (hwstat->tx_single_cols +
                             hwstat->tx_multiple_cols +
                             hwstat->tx_excessive_cols);
index d6aa602f168d776cd1450fb10c8fdeb6c0118ae3..e4b5b057f41786733ea9883de572702aeec78e68 100644 (file)
@@ -422,7 +422,7 @@ static inline int add_one_rx_buf(void *va, unsigned int len,
 
        d->addr_lo = cpu_to_be32(mapping);
        d->addr_hi = cpu_to_be32((u64) mapping >> 32);
-       wmb();
+       dma_wmb();
        d->len_gen = cpu_to_be32(V_FLD_GEN1(gen));
        d->gen2 = cpu_to_be32(V_FLD_GEN2(gen));
        return 0;
@@ -433,7 +433,7 @@ static inline int add_one_rx_chunk(dma_addr_t mapping, struct rx_desc *d,
 {
        d->addr_lo = cpu_to_be32(mapping);
        d->addr_hi = cpu_to_be32((u64) mapping >> 32);
-       wmb();
+       dma_wmb();
        d->len_gen = cpu_to_be32(V_FLD_GEN1(gen));
        d->gen2 = cpu_to_be32(V_FLD_GEN2(gen));
        return 0;
@@ -579,7 +579,7 @@ static void recycle_rx_buf(struct adapter *adap, struct sge_fl *q,
        q->sdesc[q->pidx] = q->sdesc[idx];
        to->addr_lo = from->addr_lo;    /* already big endian */
        to->addr_hi = from->addr_hi;    /* likewise */
-       wmb();
+       dma_wmb();
        to->len_gen = cpu_to_be32(V_FLD_GEN1(q->gen));
        to->gen2 = cpu_to_be32(V_FLD_GEN2(q->gen));
 
@@ -1068,7 +1068,7 @@ static void write_wr_hdr_sgl(unsigned int ndesc, struct sk_buff *skb,
                sd->eop = 1;
                wrp->wr_hi = htonl(F_WR_SOP | F_WR_EOP | V_WR_DATATYPE(1) |
                                   V_WR_SGLSFLT(flits)) | wr_hi;
-               wmb();
+               dma_wmb();
                wrp->wr_lo = htonl(V_WR_LEN(flits + sgl_flits) |
                                   V_WR_GEN(gen)) | wr_lo;
                wr_gen2(d, gen);
@@ -1114,7 +1114,7 @@ static void write_wr_hdr_sgl(unsigned int ndesc, struct sk_buff *skb,
                }
                sd->eop = 1;
                wrp->wr_hi |= htonl(F_WR_EOP);
-               wmb();
+               dma_wmb();
                wp->wr_lo = htonl(V_WR_LEN(WR_FLITS) | V_WR_GEN(ogen)) | wr_lo;
                wr_gen2((struct tx_desc *)wp, ogen);
                WARN_ON(ndesc != 0);
@@ -1184,7 +1184,7 @@ static void write_tx_pkt_wr(struct adapter *adap, struct sk_buff *skb,
                        cpl->wr.wr_hi = htonl(V_WR_BCNTLFLT(skb->len & 7) |
                                              V_WR_OP(FW_WROPCODE_TUNNEL_TX_PKT)
                                              | F_WR_SOP | F_WR_EOP | compl);
-                       wmb();
+                       dma_wmb();
                        cpl->wr.wr_lo = htonl(V_WR_LEN(flits) | V_WR_GEN(gen) |
                                              V_WR_TID(q->token));
                        wr_gen2(d, gen);
@@ -1342,7 +1342,7 @@ static inline void write_imm(struct tx_desc *d, struct sk_buff *skb,
 
        to->wr_hi = from->wr_hi | htonl(F_WR_SOP | F_WR_EOP |
                                        V_WR_BCNTLFLT(len & 7));
-       wmb();
+       dma_wmb();
        to->wr_lo = from->wr_lo | htonl(V_WR_GEN(gen) |
                                        V_WR_LEN((len + 7) / 8));
        wr_gen2(d, gen);
@@ -2271,7 +2271,7 @@ static int process_responses(struct adapter *adap, struct sge_qset *qs,
                u32 len, flags;
                __be32 rss_hi, rss_lo;
 
-               rmb();
+               dma_rmb();
                eth = r->rss_hdr.opcode == CPL_RX_PKT;
                rss_hi = *(const __be32 *)r;
                rss_lo = r->rss_hdr.rss_hash_val;
@@ -2488,7 +2488,7 @@ static int process_pure_responses(struct adapter *adap, struct sge_qset *qs,
                }
                if (!is_new_response(r, q))
                        break;
-               rmb();
+               dma_rmb();
        } while (is_pure_response(r));
 
        if (sleeping)
@@ -2523,7 +2523,7 @@ static inline int handle_responses(struct adapter *adap, struct sge_rspq *q)
 
        if (!is_new_response(r, q))
                return -1;
-       rmb();
+       dma_rmb();
        if (is_pure_response(r) && process_pure_responses(adap, qs, r) == 0) {
                t3_write_reg(adap, A_SG_GTS, V_RSPQ(q->cntxt_id) |
                             V_NEWTIMER(q->holdoff_tmr) | V_NEWINDEX(q->cidx));
index 07d9b68a4da20146f14cbc1aa418fed1dcbdabb7..ace0ab98d0f1dbc9a7534242bc0c05d860df50ef 100644 (file)
@@ -4,7 +4,7 @@
 
 obj-$(CONFIG_CHELSIO_T4) += cxgb4.o
 
-cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o clip_tbl.o
+cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o clip_tbl.o cxgb4_ethtool.o
 cxgb4-$(CONFIG_CHELSIO_T4_DCB) +=  cxgb4_dcb.o
 cxgb4-$(CONFIG_CHELSIO_T4_FCOE) +=  cxgb4_fcoe.o
 cxgb4-$(CONFIG_DEBUG_FS) += cxgb4_debugfs.o
index 6c80eb2e61f4b5e271f968787e5a2958c0e20e53..524d11098c566d178d132fe0b3a05be557036440 100644 (file)
@@ -59,6 +59,11 @@ enum {
        PN_LEN     = 16,    /* Part Number length */
 };
 
+enum {
+       T4_REGMAP_SIZE = (160 * 1024),
+       T5_REGMAP_SIZE = (332 * 1024),
+};
+
 enum {
        MEM_EDC0,
        MEM_EDC1,
@@ -373,6 +378,17 @@ enum {
        MAX_ISCSI_QUEUES = NCHAN,     /* # of streaming iSCSI Rx queues */
 };
 
+enum {
+       MAX_TXQ_ENTRIES      = 16384,
+       MAX_CTRL_TXQ_ENTRIES = 1024,
+       MAX_RSPQ_ENTRIES     = 16384,
+       MAX_RX_BUFFERS       = 16384,
+       MIN_TXQ_ENTRIES      = 32,
+       MIN_CTRL_TXQ_ENTRIES = 32,
+       MIN_RSPQ_ENTRIES     = 128,
+       MIN_FL_ENTRIES       = 16
+};
+
 enum {
        INGQ_EXTRAS = 2,        /* firmware event queue and */
                                /*   forwarded interrupts */
@@ -1000,6 +1016,30 @@ static inline bool cxgb_poll_busy_polling(struct sge_rspq *q)
 }
 #endif /* CONFIG_NET_RX_BUSY_POLL */
 
+/* Return a version number to identify the type of adapter.  The scheme is:
+ * - bits 0..9: chip version
+ * - bits 10..15: chip revision
+ * - bits 16..23: register dump version
+ */
+static inline unsigned int mk_adap_vers(struct adapter *ap)
+{
+       return CHELSIO_CHIP_VERSION(ap->params.chip) |
+               (CHELSIO_CHIP_RELEASE(ap->params.chip) << 10) | (1 << 16);
+}
+
+/* Return a queue's interrupt hold-off time in us.  0 means no timer. */
+static inline unsigned int qtimer_val(const struct adapter *adap,
+                                     const struct sge_rspq *q)
+{
+       unsigned int idx = q->intr_params >> 1;
+
+       return idx < SGE_NTIMERS ? adap->sge.timer_val[idx] : 0;
+}
+
+/* driver version & name used for ethtool_drvinfo */
+extern char cxgb4_driver_name[];
+extern const char cxgb4_driver_version[];
+
 void t4_os_portmod_changed(const struct adapter *adap, int port_id);
 void t4_os_link_changed(struct adapter *adap, int port_id, int link_stat);
 
@@ -1029,6 +1069,10 @@ int t4_sge_init(struct adapter *adap);
 void t4_sge_start(struct adapter *adap);
 void t4_sge_stop(struct adapter *adap);
 int cxgb_busy_poll(struct napi_struct *napi);
+int cxgb4_set_rspq_intr_params(struct sge_rspq *q, unsigned int us,
+                              unsigned int cnt);
+void cxgb4_set_ethtool_ops(struct net_device *netdev);
+int cxgb4_write_rss(const struct port_info *pi, const u16 *queues);
 extern int dbfifo_int_thresh;
 
 #define for_each_port(adapter, iter) \
@@ -1117,6 +1161,9 @@ static inline int t4_memory_write(struct adapter *adap, int mtype, u32 addr,
        return t4_memory_rw(adap, 0, mtype, addr, len, buf, 0);
 }
 
+unsigned int t4_get_regs_len(struct adapter *adapter);
+void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size);
+
 int t4_seeprom_wp(struct adapter *adapter, bool enable);
 int get_vpd_params(struct adapter *adapter, struct vpd_params *p);
 int t4_read_flash(struct adapter *adapter, unsigned int addr,
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c
new file mode 100644 (file)
index 0000000..10d82b5
--- /dev/null
@@ -0,0 +1,915 @@
+/*
+ *  Copyright (C) 2013-2015 Chelsio Communications.  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 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.
+ *
+ *  The full GNU General Public License is included in this distribution in
+ *  the file called "COPYING".
+ *
+ */
+
+#include <linux/firmware.h>
+#include <linux/mdio.h>
+
+#include "cxgb4.h"
+#include "t4_regs.h"
+#include "t4fw_api.h"
+
+#define EEPROM_MAGIC 0x38E2F10C
+
+static u32 get_msglevel(struct net_device *dev)
+{
+       return netdev2adap(dev)->msg_enable;
+}
+
+static void set_msglevel(struct net_device *dev, u32 val)
+{
+       netdev2adap(dev)->msg_enable = val;
+}
+
+static const char stats_strings[][ETH_GSTRING_LEN] = {
+       "TxOctetsOK         ",
+       "TxFramesOK         ",
+       "TxBroadcastFrames  ",
+       "TxMulticastFrames  ",
+       "TxUnicastFrames    ",
+       "TxErrorFrames      ",
+
+       "TxFrames64         ",
+       "TxFrames65To127    ",
+       "TxFrames128To255   ",
+       "TxFrames256To511   ",
+       "TxFrames512To1023  ",
+       "TxFrames1024To1518 ",
+       "TxFrames1519ToMax  ",
+
+       "TxFramesDropped    ",
+       "TxPauseFrames      ",
+       "TxPPP0Frames       ",
+       "TxPPP1Frames       ",
+       "TxPPP2Frames       ",
+       "TxPPP3Frames       ",
+       "TxPPP4Frames       ",
+       "TxPPP5Frames       ",
+       "TxPPP6Frames       ",
+       "TxPPP7Frames       ",
+
+       "RxOctetsOK         ",
+       "RxFramesOK         ",
+       "RxBroadcastFrames  ",
+       "RxMulticastFrames  ",
+       "RxUnicastFrames    ",
+
+       "RxFramesTooLong    ",
+       "RxJabberErrors     ",
+       "RxFCSErrors        ",
+       "RxLengthErrors     ",
+       "RxSymbolErrors     ",
+       "RxRuntFrames       ",
+
+       "RxFrames64         ",
+       "RxFrames65To127    ",
+       "RxFrames128To255   ",
+       "RxFrames256To511   ",
+       "RxFrames512To1023  ",
+       "RxFrames1024To1518 ",
+       "RxFrames1519ToMax  ",
+
+       "RxPauseFrames      ",
+       "RxPPP0Frames       ",
+       "RxPPP1Frames       ",
+       "RxPPP2Frames       ",
+       "RxPPP3Frames       ",
+       "RxPPP4Frames       ",
+       "RxPPP5Frames       ",
+       "RxPPP6Frames       ",
+       "RxPPP7Frames       ",
+
+       "RxBG0FramesDropped ",
+       "RxBG1FramesDropped ",
+       "RxBG2FramesDropped ",
+       "RxBG3FramesDropped ",
+       "RxBG0FramesTrunc   ",
+       "RxBG1FramesTrunc   ",
+       "RxBG2FramesTrunc   ",
+       "RxBG3FramesTrunc   ",
+
+       "TSO                ",
+       "TxCsumOffload      ",
+       "RxCsumGood         ",
+       "VLANextractions    ",
+       "VLANinsertions     ",
+       "GROpackets         ",
+       "GROmerged          ",
+       "WriteCoalSuccess   ",
+       "WriteCoalFail      ",
+};
+
+static int get_sset_count(struct net_device *dev, int sset)
+{
+       switch (sset) {
+       case ETH_SS_STATS:
+               return ARRAY_SIZE(stats_strings);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int get_regs_len(struct net_device *dev)
+{
+       struct adapter *adap = netdev2adap(dev);
+
+       return t4_get_regs_len(adap);
+}
+
+static int get_eeprom_len(struct net_device *dev)
+{
+       return EEPROMSIZE;
+}
+
+static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+       struct adapter *adapter = netdev2adap(dev);
+       u32 exprom_vers;
+
+       strlcpy(info->driver, cxgb4_driver_name, sizeof(info->driver));
+       strlcpy(info->version, cxgb4_driver_version,
+               sizeof(info->version));
+       strlcpy(info->bus_info, pci_name(adapter->pdev),
+               sizeof(info->bus_info));
+
+       if (adapter->params.fw_vers)
+               snprintf(info->fw_version, sizeof(info->fw_version),
+                        "%u.%u.%u.%u, TP %u.%u.%u.%u",
+                        FW_HDR_FW_VER_MAJOR_G(adapter->params.fw_vers),
+                        FW_HDR_FW_VER_MINOR_G(adapter->params.fw_vers),
+                        FW_HDR_FW_VER_MICRO_G(adapter->params.fw_vers),
+                        FW_HDR_FW_VER_BUILD_G(adapter->params.fw_vers),
+                        FW_HDR_FW_VER_MAJOR_G(adapter->params.tp_vers),
+                        FW_HDR_FW_VER_MINOR_G(adapter->params.tp_vers),
+                        FW_HDR_FW_VER_MICRO_G(adapter->params.tp_vers),
+                        FW_HDR_FW_VER_BUILD_G(adapter->params.tp_vers));
+
+       if (!t4_get_exprom_version(adapter, &exprom_vers))
+               snprintf(info->erom_version, sizeof(info->erom_version),
+                        "%u.%u.%u.%u",
+                        FW_HDR_FW_VER_MAJOR_G(exprom_vers),
+                        FW_HDR_FW_VER_MINOR_G(exprom_vers),
+                        FW_HDR_FW_VER_MICRO_G(exprom_vers),
+                        FW_HDR_FW_VER_BUILD_G(exprom_vers));
+}
+
+static void get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+       if (stringset == ETH_SS_STATS)
+               memcpy(data, stats_strings, sizeof(stats_strings));
+}
+
+/* port stats maintained per queue of the port. They should be in the same
+ * order as in stats_strings above.
+ */
+struct queue_port_stats {
+       u64 tso;
+       u64 tx_csum;
+       u64 rx_csum;
+       u64 vlan_ex;
+       u64 vlan_ins;
+       u64 gro_pkts;
+       u64 gro_merged;
+};
+
+static void collect_sge_port_stats(const struct adapter *adap,
+                                  const struct port_info *p,
+                                  struct queue_port_stats *s)
+{
+       int i;
+       const struct sge_eth_txq *tx = &adap->sge.ethtxq[p->first_qset];
+       const struct sge_eth_rxq *rx = &adap->sge.ethrxq[p->first_qset];
+
+       memset(s, 0, sizeof(*s));
+       for (i = 0; i < p->nqsets; i++, rx++, tx++) {
+               s->tso += tx->tso;
+               s->tx_csum += tx->tx_cso;
+               s->rx_csum += rx->stats.rx_cso;
+               s->vlan_ex += rx->stats.vlan_ex;
+               s->vlan_ins += tx->vlan_ins;
+               s->gro_pkts += rx->stats.lro_pkts;
+               s->gro_merged += rx->stats.lro_merged;
+       }
+}
+
+static void get_stats(struct net_device *dev, struct ethtool_stats *stats,
+                     u64 *data)
+{
+       struct port_info *pi = netdev_priv(dev);
+       struct adapter *adapter = pi->adapter;
+       u32 val1, val2;
+
+       t4_get_port_stats(adapter, pi->tx_chan, (struct port_stats *)data);
+
+       data += sizeof(struct port_stats) / sizeof(u64);
+       collect_sge_port_stats(adapter, pi, (struct queue_port_stats *)data);
+       data += sizeof(struct queue_port_stats) / sizeof(u64);
+       if (!is_t4(adapter->params.chip)) {
+               t4_write_reg(adapter, SGE_STAT_CFG_A, STATSOURCE_T5_V(7));
+               val1 = t4_read_reg(adapter, SGE_STAT_TOTAL_A);
+               val2 = t4_read_reg(adapter, SGE_STAT_MATCH_A);
+               *data = val1 - val2;
+               data++;
+               *data = val2;
+               data++;
+       } else {
+               memset(data, 0, 2 * sizeof(u64));
+               *data += 2;
+       }
+}
+
+static void get_regs(struct net_device *dev, struct ethtool_regs *regs,
+                    void *buf)
+{
+       struct adapter *adap = netdev2adap(dev);
+       size_t buf_size;
+
+       buf_size = t4_get_regs_len(adap);
+       regs->version = mk_adap_vers(adap);
+       t4_get_regs(adap, buf, buf_size);
+}
+
+static int restart_autoneg(struct net_device *dev)
+{
+       struct port_info *p = netdev_priv(dev);
+
+       if (!netif_running(dev))
+               return -EAGAIN;
+       if (p->link_cfg.autoneg != AUTONEG_ENABLE)
+               return -EINVAL;
+       t4_restart_aneg(p->adapter, p->adapter->fn, p->tx_chan);
+       return 0;
+}
+
+static int identify_port(struct net_device *dev,
+                        enum ethtool_phys_id_state state)
+{
+       unsigned int val;
+       struct adapter *adap = netdev2adap(dev);
+
+       if (state == ETHTOOL_ID_ACTIVE)
+               val = 0xffff;
+       else if (state == ETHTOOL_ID_INACTIVE)
+               val = 0;
+       else
+               return -EINVAL;
+
+       return t4_identify_port(adap, adap->fn, netdev2pinfo(dev)->viid, val);
+}
+
+static unsigned int from_fw_linkcaps(enum fw_port_type type, unsigned int caps)
+{
+       unsigned int v = 0;
+
+       if (type == FW_PORT_TYPE_BT_SGMII || type == FW_PORT_TYPE_BT_XFI ||
+           type == FW_PORT_TYPE_BT_XAUI) {
+               v |= SUPPORTED_TP;
+               if (caps & FW_PORT_CAP_SPEED_100M)
+                       v |= SUPPORTED_100baseT_Full;
+               if (caps & FW_PORT_CAP_SPEED_1G)
+                       v |= SUPPORTED_1000baseT_Full;
+               if (caps & FW_PORT_CAP_SPEED_10G)
+                       v |= SUPPORTED_10000baseT_Full;
+       } else if (type == FW_PORT_TYPE_KX4 || type == FW_PORT_TYPE_KX) {
+               v |= SUPPORTED_Backplane;
+               if (caps & FW_PORT_CAP_SPEED_1G)
+                       v |= SUPPORTED_1000baseKX_Full;
+               if (caps & FW_PORT_CAP_SPEED_10G)
+                       v |= SUPPORTED_10000baseKX4_Full;
+       } else if (type == FW_PORT_TYPE_KR) {
+               v |= SUPPORTED_Backplane | SUPPORTED_10000baseKR_Full;
+       } else if (type == FW_PORT_TYPE_BP_AP) {
+               v |= SUPPORTED_Backplane | SUPPORTED_10000baseR_FEC |
+                    SUPPORTED_10000baseKR_Full | SUPPORTED_1000baseKX_Full;
+       } else if (type == FW_PORT_TYPE_BP4_AP) {
+               v |= SUPPORTED_Backplane | SUPPORTED_10000baseR_FEC |
+                    SUPPORTED_10000baseKR_Full | SUPPORTED_1000baseKX_Full |
+                    SUPPORTED_10000baseKX4_Full;
+       } else if (type == FW_PORT_TYPE_FIBER_XFI ||
+                  type == FW_PORT_TYPE_FIBER_XAUI ||
+                  type == FW_PORT_TYPE_SFP ||
+                  type == FW_PORT_TYPE_QSFP_10G ||
+                  type == FW_PORT_TYPE_QSA) {
+               v |= SUPPORTED_FIBRE;
+               if (caps & FW_PORT_CAP_SPEED_1G)
+                       v |= SUPPORTED_1000baseT_Full;
+               if (caps & FW_PORT_CAP_SPEED_10G)
+                       v |= SUPPORTED_10000baseT_Full;
+       } else if (type == FW_PORT_TYPE_BP40_BA ||
+                  type == FW_PORT_TYPE_QSFP) {
+               v |= SUPPORTED_40000baseSR4_Full;
+               v |= SUPPORTED_FIBRE;
+       }
+
+       if (caps & FW_PORT_CAP_ANEG)
+               v |= SUPPORTED_Autoneg;
+       return v;
+}
+
+static unsigned int to_fw_linkcaps(unsigned int caps)
+{
+       unsigned int v = 0;
+
+       if (caps & ADVERTISED_100baseT_Full)
+               v |= FW_PORT_CAP_SPEED_100M;
+       if (caps & ADVERTISED_1000baseT_Full)
+               v |= FW_PORT_CAP_SPEED_1G;
+       if (caps & ADVERTISED_10000baseT_Full)
+               v |= FW_PORT_CAP_SPEED_10G;
+       if (caps & ADVERTISED_40000baseSR4_Full)
+               v |= FW_PORT_CAP_SPEED_40G;
+       return v;
+}
+
+static int get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+       const struct port_info *p = netdev_priv(dev);
+
+       if (p->port_type == FW_PORT_TYPE_BT_SGMII ||
+           p->port_type == FW_PORT_TYPE_BT_XFI ||
+           p->port_type == FW_PORT_TYPE_BT_XAUI) {
+               cmd->port = PORT_TP;
+       } 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 ||
+                  p->port_type == FW_PORT_TYPE_QSFP_10G ||
+                  p->port_type == FW_PORT_TYPE_QSA ||
+                  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_OTHER;
+       } else {
+               cmd->port = PORT_OTHER;
+       }
+
+       if (p->mdio_addr >= 0) {
+               cmd->phy_address = p->mdio_addr;
+               cmd->transceiver = XCVR_EXTERNAL;
+               cmd->mdio_support = p->port_type == FW_PORT_TYPE_BT_SGMII ?
+                       MDIO_SUPPORTS_C22 : MDIO_SUPPORTS_C45;
+       } else {
+               cmd->phy_address = 0;  /* not really, but no better option */
+               cmd->transceiver = XCVR_INTERNAL;
+               cmd->mdio_support = 0;
+       }
+
+       cmd->supported = from_fw_linkcaps(p->port_type, p->link_cfg.supported);
+       cmd->advertising = from_fw_linkcaps(p->port_type,
+                                           p->link_cfg.advertising);
+       ethtool_cmd_speed_set(cmd,
+                             netif_carrier_ok(dev) ? p->link_cfg.speed : 0);
+       cmd->duplex = DUPLEX_FULL;
+       cmd->autoneg = p->link_cfg.autoneg;
+       cmd->maxtxpkt = 0;
+       cmd->maxrxpkt = 0;
+       return 0;
+}
+
+static unsigned int speed_to_caps(int speed)
+{
+       if (speed == 100)
+               return FW_PORT_CAP_SPEED_100M;
+       if (speed == 1000)
+               return FW_PORT_CAP_SPEED_1G;
+       if (speed == 10000)
+               return FW_PORT_CAP_SPEED_10G;
+       if (speed == 40000)
+               return FW_PORT_CAP_SPEED_40G;
+       return 0;
+}
+
+static int set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+       unsigned int cap;
+       struct port_info *p = netdev_priv(dev);
+       struct link_config *lc = &p->link_cfg;
+       u32 speed = ethtool_cmd_speed(cmd);
+
+       if (cmd->duplex != DUPLEX_FULL)     /* only full-duplex supported */
+               return -EINVAL;
+
+       if (!(lc->supported & FW_PORT_CAP_ANEG)) {
+               /* PHY offers a single speed.  See if that's what's
+                * being requested.
+                */
+               if (cmd->autoneg == AUTONEG_DISABLE &&
+                   (lc->supported & speed_to_caps(speed)))
+                       return 0;
+               return -EINVAL;
+       }
+
+       if (cmd->autoneg == AUTONEG_DISABLE) {
+               cap = speed_to_caps(speed);
+
+               if (!(lc->supported & cap) ||
+                   (speed == 1000) ||
+                   (speed == 10000) ||
+                   (speed == 40000))
+                       return -EINVAL;
+               lc->requested_speed = cap;
+               lc->advertising = 0;
+       } else {
+               cap = to_fw_linkcaps(cmd->advertising);
+               if (!(lc->supported & cap))
+                       return -EINVAL;
+               lc->requested_speed = 0;
+               lc->advertising = cap | FW_PORT_CAP_ANEG;
+       }
+       lc->autoneg = cmd->autoneg;
+
+       if (netif_running(dev))
+               return t4_link_start(p->adapter, p->adapter->fn, p->tx_chan,
+                                    lc);
+       return 0;
+}
+
+static void get_pauseparam(struct net_device *dev,
+                          struct ethtool_pauseparam *epause)
+{
+       struct port_info *p = netdev_priv(dev);
+
+       epause->autoneg = (p->link_cfg.requested_fc & PAUSE_AUTONEG) != 0;
+       epause->rx_pause = (p->link_cfg.fc & PAUSE_RX) != 0;
+       epause->tx_pause = (p->link_cfg.fc & PAUSE_TX) != 0;
+}
+
+static int set_pauseparam(struct net_device *dev,
+                         struct ethtool_pauseparam *epause)
+{
+       struct port_info *p = netdev_priv(dev);
+       struct link_config *lc = &p->link_cfg;
+
+       if (epause->autoneg == AUTONEG_DISABLE)
+               lc->requested_fc = 0;
+       else if (lc->supported & FW_PORT_CAP_ANEG)
+               lc->requested_fc = PAUSE_AUTONEG;
+       else
+               return -EINVAL;
+
+       if (epause->rx_pause)
+               lc->requested_fc |= PAUSE_RX;
+       if (epause->tx_pause)
+               lc->requested_fc |= PAUSE_TX;
+       if (netif_running(dev))
+               return t4_link_start(p->adapter, p->adapter->fn, p->tx_chan,
+                                    lc);
+       return 0;
+}
+
+static void get_sge_param(struct net_device *dev, struct ethtool_ringparam *e)
+{
+       const struct port_info *pi = netdev_priv(dev);
+       const struct sge *s = &pi->adapter->sge;
+
+       e->rx_max_pending = MAX_RX_BUFFERS;
+       e->rx_mini_max_pending = MAX_RSPQ_ENTRIES;
+       e->rx_jumbo_max_pending = 0;
+       e->tx_max_pending = MAX_TXQ_ENTRIES;
+
+       e->rx_pending = s->ethrxq[pi->first_qset].fl.size - 8;
+       e->rx_mini_pending = s->ethrxq[pi->first_qset].rspq.size;
+       e->rx_jumbo_pending = 0;
+       e->tx_pending = s->ethtxq[pi->first_qset].q.size;
+}
+
+static int set_sge_param(struct net_device *dev, struct ethtool_ringparam *e)
+{
+       int i;
+       const struct port_info *pi = netdev_priv(dev);
+       struct adapter *adapter = pi->adapter;
+       struct sge *s = &adapter->sge;
+
+       if (e->rx_pending > MAX_RX_BUFFERS || e->rx_jumbo_pending ||
+           e->tx_pending > MAX_TXQ_ENTRIES ||
+           e->rx_mini_pending > MAX_RSPQ_ENTRIES ||
+           e->rx_mini_pending < MIN_RSPQ_ENTRIES ||
+           e->rx_pending < MIN_FL_ENTRIES || e->tx_pending < MIN_TXQ_ENTRIES)
+               return -EINVAL;
+
+       if (adapter->flags & FULL_INIT_DONE)
+               return -EBUSY;
+
+       for (i = 0; i < pi->nqsets; ++i) {
+               s->ethtxq[pi->first_qset + i].q.size = e->tx_pending;
+               s->ethrxq[pi->first_qset + i].fl.size = e->rx_pending + 8;
+               s->ethrxq[pi->first_qset + i].rspq.size = e->rx_mini_pending;
+       }
+       return 0;
+}
+
+/**
+ * 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)
+{
+       int i, err;
+       struct port_info *pi = netdev_priv(dev);
+       struct adapter *adap = pi->adapter;
+       struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset];
+
+       for (i = 0; i < pi->nqsets; i++, q++) {
+               err = cxgb4_set_rspq_intr_params(&q->rspq, us, cnt);
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+static int set_adaptive_rx_setting(struct net_device *dev, int adaptive_rx)
+{
+       int i;
+       struct port_info *pi = netdev_priv(dev);
+       struct adapter *adap = pi->adapter;
+       struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset];
+
+       for (i = 0; i < pi->nqsets; i++, q++)
+               q->rspq.adaptive_rx = adaptive_rx;
+
+       return 0;
+}
+
+static int get_adaptive_rx_setting(struct net_device *dev)
+{
+       struct port_info *pi = netdev_priv(dev);
+       struct adapter *adap = pi->adapter;
+       struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset];
+
+       return q->rspq.adaptive_rx;
+}
+
+static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
+{
+       set_adaptive_rx_setting(dev, c->use_adaptive_rx_coalesce);
+       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)
+{
+       const struct port_info *pi = netdev_priv(dev);
+       const struct adapter *adap = pi->adapter;
+       const struct sge_rspq *rq = &adap->sge.ethrxq[pi->first_qset].rspq;
+
+       c->rx_coalesce_usecs = qtimer_val(adap, rq);
+       c->rx_max_coalesced_frames = (rq->intr_params & QINTR_CNT_EN) ?
+               adap->sge.counter_val[rq->pktcnt_idx] : 0;
+       c->use_adaptive_rx_coalesce = get_adaptive_rx_setting(dev);
+       return 0;
+}
+
+/**
+ *     eeprom_ptov - translate a physical EEPROM address to virtual
+ *     @phys_addr: the physical EEPROM address
+ *     @fn: the PCI function number
+ *     @sz: size of function-specific area
+ *
+ *     Translate a physical EEPROM address to virtual.  The first 1K is
+ *     accessed through virtual addresses starting at 31K, the rest is
+ *     accessed through virtual addresses starting at 0.
+ *
+ *     The mapping is as follows:
+ *     [0..1K) -> [31K..32K)
+ *     [1K..1K+A) -> [31K-A..31K)
+ *     [1K+A..ES) -> [0..ES-A-1K)
+ *
+ *     where A = @fn * @sz, and ES = EEPROM size.
+ */
+static int eeprom_ptov(unsigned int phys_addr, unsigned int fn, unsigned int sz)
+{
+       fn *= sz;
+       if (phys_addr < 1024)
+               return phys_addr + (31 << 10);
+       if (phys_addr < 1024 + fn)
+               return 31744 - fn + phys_addr - 1024;
+       if (phys_addr < EEPROMSIZE)
+               return phys_addr - 1024 - fn;
+       return -EINVAL;
+}
+
+/* The next two routines implement eeprom read/write from physical addresses.
+ */
+static int eeprom_rd_phys(struct adapter *adap, unsigned int phys_addr, u32 *v)
+{
+       int vaddr = eeprom_ptov(phys_addr, adap->fn, EEPROMPFSIZE);
+
+       if (vaddr >= 0)
+               vaddr = pci_read_vpd(adap->pdev, vaddr, sizeof(u32), v);
+       return vaddr < 0 ? vaddr : 0;
+}
+
+static int eeprom_wr_phys(struct adapter *adap, unsigned int phys_addr, u32 v)
+{
+       int vaddr = eeprom_ptov(phys_addr, adap->fn, EEPROMPFSIZE);
+
+       if (vaddr >= 0)
+               vaddr = pci_write_vpd(adap->pdev, vaddr, sizeof(u32), &v);
+       return vaddr < 0 ? vaddr : 0;
+}
+
+#define EEPROM_MAGIC 0x38E2F10C
+
+static int get_eeprom(struct net_device *dev, struct ethtool_eeprom *e,
+                     u8 *data)
+{
+       int i, err = 0;
+       struct adapter *adapter = netdev2adap(dev);
+       u8 *buf = kmalloc(EEPROMSIZE, GFP_KERNEL);
+
+       if (!buf)
+               return -ENOMEM;
+
+       e->magic = EEPROM_MAGIC;
+       for (i = e->offset & ~3; !err && i < e->offset + e->len; i += 4)
+               err = eeprom_rd_phys(adapter, i, (u32 *)&buf[i]);
+
+       if (!err)
+               memcpy(data, buf + e->offset, e->len);
+       kfree(buf);
+       return err;
+}
+
+static int set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
+                     u8 *data)
+{
+       u8 *buf;
+       int err = 0;
+       u32 aligned_offset, aligned_len, *p;
+       struct adapter *adapter = netdev2adap(dev);
+
+       if (eeprom->magic != EEPROM_MAGIC)
+               return -EINVAL;
+
+       aligned_offset = eeprom->offset & ~3;
+       aligned_len = (eeprom->len + (eeprom->offset & 3) + 3) & ~3;
+
+       if (adapter->fn > 0) {
+               u32 start = 1024 + adapter->fn * EEPROMPFSIZE;
+
+               if (aligned_offset < start ||
+                   aligned_offset + aligned_len > start + EEPROMPFSIZE)
+                       return -EPERM;
+       }
+
+       if (aligned_offset != eeprom->offset || aligned_len != eeprom->len) {
+               /* RMW possibly needed for first or last words.
+                */
+               buf = kmalloc(aligned_len, GFP_KERNEL);
+               if (!buf)
+                       return -ENOMEM;
+               err = eeprom_rd_phys(adapter, aligned_offset, (u32 *)buf);
+               if (!err && aligned_len > 4)
+                       err = eeprom_rd_phys(adapter,
+                                            aligned_offset + aligned_len - 4,
+                                            (u32 *)&buf[aligned_len - 4]);
+               if (err)
+                       goto out;
+               memcpy(buf + (eeprom->offset & 3), data, eeprom->len);
+       } else {
+               buf = data;
+       }
+
+       err = t4_seeprom_wp(adapter, false);
+       if (err)
+               goto out;
+
+       for (p = (u32 *)buf; !err && aligned_len; aligned_len -= 4, p++) {
+               err = eeprom_wr_phys(adapter, aligned_offset, *p);
+               aligned_offset += 4;
+       }
+
+       if (!err)
+               err = t4_seeprom_wp(adapter, true);
+out:
+       if (buf != data)
+               kfree(buf);
+       return err;
+}
+
+static int set_flash(struct net_device *netdev, struct ethtool_flash *ef)
+{
+       int ret;
+       const struct firmware *fw;
+       struct adapter *adap = netdev2adap(netdev);
+       unsigned int mbox = PCIE_FW_MASTER_M + 1;
+
+       ef->data[sizeof(ef->data) - 1] = '\0';
+       ret = request_firmware(&fw, ef->data, adap->pdev_dev);
+       if (ret < 0)
+               return ret;
+
+       /* If the adapter has been fully initialized then we'll go ahead and
+        * try to get the firmware's cooperation in upgrading to the new
+        * firmware image otherwise we'll try to do the entire job from the
+        * host ... and we always "force" the operation in this path.
+        */
+       if (adap->flags & FULL_INIT_DONE)
+               mbox = adap->mbox;
+
+       ret = t4_fw_upgrade(adap, mbox, fw->data, fw->size, 1);
+       release_firmware(fw);
+       if (!ret)
+               dev_info(adap->pdev_dev,
+                        "loaded firmware %s, reload cxgb4 driver\n", ef->data);
+       return ret;
+}
+
+#define WOL_SUPPORTED (WAKE_BCAST | WAKE_MAGIC)
+#define BCAST_CRC 0xa0ccc1a6
+
+static void get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+       wol->supported = WAKE_BCAST | WAKE_MAGIC;
+       wol->wolopts = netdev2adap(dev)->wol;
+       memset(&wol->sopass, 0, sizeof(wol->sopass));
+}
+
+static int set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+       int err = 0;
+       struct port_info *pi = netdev_priv(dev);
+
+       if (wol->wolopts & ~WOL_SUPPORTED)
+               return -EINVAL;
+       t4_wol_magic_enable(pi->adapter, pi->tx_chan,
+                           (wol->wolopts & WAKE_MAGIC) ? dev->dev_addr : NULL);
+       if (wol->wolopts & WAKE_BCAST) {
+               err = t4_wol_pat_enable(pi->adapter, pi->tx_chan, 0xfe, ~0ULL,
+                                       ~0ULL, 0, false);
+               if (!err)
+                       err = t4_wol_pat_enable(pi->adapter, pi->tx_chan, 1,
+                                               ~6ULL, ~0ULL, BCAST_CRC, true);
+       } else {
+               t4_wol_pat_enable(pi->adapter, pi->tx_chan, 0, 0, 0, 0, false);
+       }
+       return err;
+}
+
+static u32 get_rss_table_size(struct net_device *dev)
+{
+       const struct port_info *pi = netdev_priv(dev);
+
+       return pi->rss_size;
+}
+
+static int get_rss_table(struct net_device *dev, u32 *p, u8 *key, u8 *hfunc)
+{
+       const struct port_info *pi = netdev_priv(dev);
+       unsigned int n = pi->rss_size;
+
+       if (hfunc)
+               *hfunc = ETH_RSS_HASH_TOP;
+       if (!p)
+               return 0;
+       while (n--)
+               p[n] = pi->rss[n];
+       return 0;
+}
+
+static int set_rss_table(struct net_device *dev, const u32 *p, const u8 *key,
+                        const u8 hfunc)
+{
+       unsigned int i;
+       struct port_info *pi = netdev_priv(dev);
+
+       /* We require at least one supported parameter to be changed and no
+        * change in any of the unsupported parameters
+        */
+       if (key ||
+           (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP))
+               return -EOPNOTSUPP;
+       if (!p)
+               return 0;
+
+       for (i = 0; i < pi->rss_size; i++)
+               pi->rss[i] = p[i];
+       if (pi->adapter->flags & FULL_INIT_DONE)
+               return cxgb4_write_rss(pi, pi->rss);
+       return 0;
+}
+
+static int get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info,
+                    u32 *rules)
+{
+       const struct port_info *pi = netdev_priv(dev);
+
+       switch (info->cmd) {
+       case ETHTOOL_GRXFH: {
+               unsigned int v = pi->rss_mode;
+
+               info->data = 0;
+               switch (info->flow_type) {
+               case TCP_V4_FLOW:
+                       if (v & FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN_F)
+                               info->data = RXH_IP_SRC | RXH_IP_DST |
+                                            RXH_L4_B_0_1 | RXH_L4_B_2_3;
+                       else if (v & FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F)
+                               info->data = RXH_IP_SRC | RXH_IP_DST;
+                       break;
+               case UDP_V4_FLOW:
+                       if ((v & FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN_F) &&
+                           (v & FW_RSS_VI_CONFIG_CMD_UDPEN_F))
+                               info->data = RXH_IP_SRC | RXH_IP_DST |
+                                            RXH_L4_B_0_1 | RXH_L4_B_2_3;
+                       else if (v & FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F)
+                               info->data = RXH_IP_SRC | RXH_IP_DST;
+                       break;
+               case SCTP_V4_FLOW:
+               case AH_ESP_V4_FLOW:
+               case IPV4_FLOW:
+                       if (v & FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F)
+                               info->data = RXH_IP_SRC | RXH_IP_DST;
+                       break;
+               case TCP_V6_FLOW:
+                       if (v & FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN_F)
+                               info->data = RXH_IP_SRC | RXH_IP_DST |
+                                            RXH_L4_B_0_1 | RXH_L4_B_2_3;
+                       else if (v & FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F)
+                               info->data = RXH_IP_SRC | RXH_IP_DST;
+                       break;
+               case UDP_V6_FLOW:
+                       if ((v & FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN_F) &&
+                           (v & FW_RSS_VI_CONFIG_CMD_UDPEN_F))
+                               info->data = RXH_IP_SRC | RXH_IP_DST |
+                                            RXH_L4_B_0_1 | RXH_L4_B_2_3;
+                       else if (v & FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F)
+                               info->data = RXH_IP_SRC | RXH_IP_DST;
+                       break;
+               case SCTP_V6_FLOW:
+               case AH_ESP_V6_FLOW:
+               case IPV6_FLOW:
+                       if (v & FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F)
+                               info->data = RXH_IP_SRC | RXH_IP_DST;
+                       break;
+               }
+               return 0;
+       }
+       case ETHTOOL_GRXRINGS:
+               info->data = pi->nqsets;
+               return 0;
+       }
+       return -EOPNOTSUPP;
+}
+
+static const struct ethtool_ops cxgb_ethtool_ops = {
+       .get_settings      = get_settings,
+       .set_settings      = set_settings,
+       .get_drvinfo       = get_drvinfo,
+       .get_msglevel      = get_msglevel,
+       .set_msglevel      = set_msglevel,
+       .get_ringparam     = get_sge_param,
+       .set_ringparam     = set_sge_param,
+       .get_coalesce      = get_coalesce,
+       .set_coalesce      = set_coalesce,
+       .get_eeprom_len    = get_eeprom_len,
+       .get_eeprom        = get_eeprom,
+       .set_eeprom        = set_eeprom,
+       .get_pauseparam    = get_pauseparam,
+       .set_pauseparam    = set_pauseparam,
+       .get_link          = ethtool_op_get_link,
+       .get_strings       = get_strings,
+       .set_phys_id       = identify_port,
+       .nway_reset        = restart_autoneg,
+       .get_sset_count    = get_sset_count,
+       .get_ethtool_stats = get_stats,
+       .get_regs_len      = get_regs_len,
+       .get_regs          = get_regs,
+       .get_wol           = get_wol,
+       .set_wol           = set_wol,
+       .get_rxnfc         = get_rxnfc,
+       .get_rxfh_indir_size = get_rss_table_size,
+       .get_rxfh          = get_rss_table,
+       .set_rxfh          = set_rss_table,
+       .flash_device      = set_flash,
+};
+
+void cxgb4_set_ethtool_ops(struct net_device *netdev)
+{
+       netdev->ethtool_ops = &cxgb_ethtool_ops;
+}
index 58c537f16763e6c9fc7ea3faf8d5c3fa0f3af35a..24e10ea3d5efa64c5280348acd2eefa0f04205cc 100644 (file)
 #include "clip_tbl.h"
 #include "l2t.h"
 
+char cxgb4_driver_name[] = KBUILD_MODNAME;
+
 #ifdef DRV_VERSION
 #undef DRV_VERSION
 #endif
 #define DRV_VERSION "2.0.0-ko"
+const char cxgb4_driver_version[] = DRV_VERSION;
 #define DRV_DESC "Chelsio T4/T5 Network Driver"
 
-enum {
-       MAX_TXQ_ENTRIES      = 16384,
-       MAX_CTRL_TXQ_ENTRIES = 1024,
-       MAX_RSPQ_ENTRIES     = 16384,
-       MAX_RX_BUFFERS       = 16384,
-       MIN_TXQ_ENTRIES      = 32,
-       MIN_CTRL_TXQ_ENTRIES = 32,
-       MIN_RSPQ_ENTRIES     = 128,
-       MIN_FL_ENTRIES       = 16
-};
-
 /* Host shadow copy of ingress filter entry.  This is in host native format
  * and doesn't match the ordering or bit order, etc. of the hardware of the
  * firmware command.  The use of bit-field structure elements is purely to
@@ -857,14 +849,14 @@ static void free_msix_queue_irqs(struct adapter *adap)
 }
 
 /**
- *     write_rss - write the RSS table for a given port
+ *     cxgb4_write_rss - write the RSS table for a given port
  *     @pi: the port
  *     @queues: array of queue indices for RSS
  *
  *     Sets up the portion of the HW RSS table for the port's VI to distribute
  *     packets to the Rx queues in @queues.
  */
-static int write_rss(const struct port_info *pi, const u16 *queues)
+int cxgb4_write_rss(const struct port_info *pi, const u16 *queues)
 {
        u16 *rss;
        int i, err;
@@ -897,7 +889,7 @@ static int setup_rss(struct adapter *adap)
        for_each_port(adap, i) {
                const struct port_info *pi = adap2pinfo(adap, i);
 
-               err = write_rss(pi, pi->rss);
+               err = cxgb4_write_rss(pi, pi->rss);
                if (err)
                        return err;
        }
@@ -1327,1192 +1319,6 @@ static inline int is_offload(const struct adapter *adap)
        return adap->params.offload;
 }
 
-/*
- * Implementation of ethtool operations.
- */
-
-static u32 get_msglevel(struct net_device *dev)
-{
-       return netdev2adap(dev)->msg_enable;
-}
-
-static void set_msglevel(struct net_device *dev, u32 val)
-{
-       netdev2adap(dev)->msg_enable = val;
-}
-
-static char stats_strings[][ETH_GSTRING_LEN] = {
-       "TxOctetsOK         ",
-       "TxFramesOK         ",
-       "TxBroadcastFrames  ",
-       "TxMulticastFrames  ",
-       "TxUnicastFrames    ",
-       "TxErrorFrames      ",
-
-       "TxFrames64         ",
-       "TxFrames65To127    ",
-       "TxFrames128To255   ",
-       "TxFrames256To511   ",
-       "TxFrames512To1023  ",
-       "TxFrames1024To1518 ",
-       "TxFrames1519ToMax  ",
-
-       "TxFramesDropped    ",
-       "TxPauseFrames      ",
-       "TxPPP0Frames       ",
-       "TxPPP1Frames       ",
-       "TxPPP2Frames       ",
-       "TxPPP3Frames       ",
-       "TxPPP4Frames       ",
-       "TxPPP5Frames       ",
-       "TxPPP6Frames       ",
-       "TxPPP7Frames       ",
-
-       "RxOctetsOK         ",
-       "RxFramesOK         ",
-       "RxBroadcastFrames  ",
-       "RxMulticastFrames  ",
-       "RxUnicastFrames    ",
-
-       "RxFramesTooLong    ",
-       "RxJabberErrors     ",
-       "RxFCSErrors        ",
-       "RxLengthErrors     ",
-       "RxSymbolErrors     ",
-       "RxRuntFrames       ",
-
-       "RxFrames64         ",
-       "RxFrames65To127    ",
-       "RxFrames128To255   ",
-       "RxFrames256To511   ",
-       "RxFrames512To1023  ",
-       "RxFrames1024To1518 ",
-       "RxFrames1519ToMax  ",
-
-       "RxPauseFrames      ",
-       "RxPPP0Frames       ",
-       "RxPPP1Frames       ",
-       "RxPPP2Frames       ",
-       "RxPPP3Frames       ",
-       "RxPPP4Frames       ",
-       "RxPPP5Frames       ",
-       "RxPPP6Frames       ",
-       "RxPPP7Frames       ",
-
-       "RxBG0FramesDropped ",
-       "RxBG1FramesDropped ",
-       "RxBG2FramesDropped ",
-       "RxBG3FramesDropped ",
-       "RxBG0FramesTrunc   ",
-       "RxBG1FramesTrunc   ",
-       "RxBG2FramesTrunc   ",
-       "RxBG3FramesTrunc   ",
-
-       "TSO                ",
-       "TxCsumOffload      ",
-       "RxCsumGood         ",
-       "VLANextractions    ",
-       "VLANinsertions     ",
-       "GROpackets         ",
-       "GROmerged          ",
-       "WriteCoalSuccess   ",
-       "WriteCoalFail      ",
-};
-
-static int get_sset_count(struct net_device *dev, int sset)
-{
-       switch (sset) {
-       case ETH_SS_STATS:
-               return ARRAY_SIZE(stats_strings);
-       default:
-               return -EOPNOTSUPP;
-       }
-}
-
-#define T4_REGMAP_SIZE (160 * 1024)
-#define T5_REGMAP_SIZE (332 * 1024)
-
-static int get_regs_len(struct net_device *dev)
-{
-       struct adapter *adap = netdev2adap(dev);
-       if (is_t4(adap->params.chip))
-               return T4_REGMAP_SIZE;
-       else
-               return T5_REGMAP_SIZE;
-}
-
-static int get_eeprom_len(struct net_device *dev)
-{
-       return EEPROMSIZE;
-}
-
-static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
-{
-       struct adapter *adapter = netdev2adap(dev);
-       u32 exprom_vers;
-
-       strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
-       strlcpy(info->version, DRV_VERSION, sizeof(info->version));
-       strlcpy(info->bus_info, pci_name(adapter->pdev),
-               sizeof(info->bus_info));
-
-       if (adapter->params.fw_vers)
-               snprintf(info->fw_version, sizeof(info->fw_version),
-                       "%u.%u.%u.%u, TP %u.%u.%u.%u",
-                       FW_HDR_FW_VER_MAJOR_G(adapter->params.fw_vers),
-                       FW_HDR_FW_VER_MINOR_G(adapter->params.fw_vers),
-                       FW_HDR_FW_VER_MICRO_G(adapter->params.fw_vers),
-                       FW_HDR_FW_VER_BUILD_G(adapter->params.fw_vers),
-                       FW_HDR_FW_VER_MAJOR_G(adapter->params.tp_vers),
-                       FW_HDR_FW_VER_MINOR_G(adapter->params.tp_vers),
-                       FW_HDR_FW_VER_MICRO_G(adapter->params.tp_vers),
-                       FW_HDR_FW_VER_BUILD_G(adapter->params.tp_vers));
-
-       if (!t4_get_exprom_version(adapter, &exprom_vers))
-               snprintf(info->erom_version, sizeof(info->erom_version),
-                        "%u.%u.%u.%u",
-                        FW_HDR_FW_VER_MAJOR_G(exprom_vers),
-                        FW_HDR_FW_VER_MINOR_G(exprom_vers),
-                        FW_HDR_FW_VER_MICRO_G(exprom_vers),
-                        FW_HDR_FW_VER_BUILD_G(exprom_vers));
-}
-
-static void get_strings(struct net_device *dev, u32 stringset, u8 *data)
-{
-       if (stringset == ETH_SS_STATS)
-               memcpy(data, stats_strings, sizeof(stats_strings));
-}
-
-/*
- * port stats maintained per queue of the port.  They should be in the same
- * order as in stats_strings above.
- */
-struct queue_port_stats {
-       u64 tso;
-       u64 tx_csum;
-       u64 rx_csum;
-       u64 vlan_ex;
-       u64 vlan_ins;
-       u64 gro_pkts;
-       u64 gro_merged;
-};
-
-static void collect_sge_port_stats(const struct adapter *adap,
-               const struct port_info *p, struct queue_port_stats *s)
-{
-       int i;
-       const struct sge_eth_txq *tx = &adap->sge.ethtxq[p->first_qset];
-       const struct sge_eth_rxq *rx = &adap->sge.ethrxq[p->first_qset];
-
-       memset(s, 0, sizeof(*s));
-       for (i = 0; i < p->nqsets; i++, rx++, tx++) {
-               s->tso += tx->tso;
-               s->tx_csum += tx->tx_cso;
-               s->rx_csum += rx->stats.rx_cso;
-               s->vlan_ex += rx->stats.vlan_ex;
-               s->vlan_ins += tx->vlan_ins;
-               s->gro_pkts += rx->stats.lro_pkts;
-               s->gro_merged += rx->stats.lro_merged;
-       }
-}
-
-static void get_stats(struct net_device *dev, struct ethtool_stats *stats,
-                     u64 *data)
-{
-       struct port_info *pi = netdev_priv(dev);
-       struct adapter *adapter = pi->adapter;
-       u32 val1, val2;
-
-       t4_get_port_stats(adapter, pi->tx_chan, (struct port_stats *)data);
-
-       data += sizeof(struct port_stats) / sizeof(u64);
-       collect_sge_port_stats(adapter, pi, (struct queue_port_stats *)data);
-       data += sizeof(struct queue_port_stats) / sizeof(u64);
-       if (!is_t4(adapter->params.chip)) {
-               t4_write_reg(adapter, SGE_STAT_CFG_A, STATSOURCE_T5_V(7));
-               val1 = t4_read_reg(adapter, SGE_STAT_TOTAL_A);
-               val2 = t4_read_reg(adapter, SGE_STAT_MATCH_A);
-               *data = val1 - val2;
-               data++;
-               *data = val2;
-               data++;
-       } else {
-               memset(data, 0, 2 * sizeof(u64));
-               *data += 2;
-       }
-}
-
-/*
- * Return a version number to identify the type of adapter.  The scheme is:
- * - bits 0..9: chip version
- * - bits 10..15: chip revision
- * - bits 16..23: register dump version
- */
-static inline unsigned int mk_adap_vers(const struct adapter *ap)
-{
-       return CHELSIO_CHIP_VERSION(ap->params.chip) |
-               (CHELSIO_CHIP_RELEASE(ap->params.chip) << 10) | (1 << 16);
-}
-
-static void reg_block_dump(struct adapter *ap, void *buf, unsigned int start,
-                          unsigned int end)
-{
-       u32 *p = buf + start;
-
-       for ( ; start <= end; start += sizeof(u32))
-               *p++ = t4_read_reg(ap, start);
-}
-
-static void get_regs(struct net_device *dev, struct ethtool_regs *regs,
-                    void *buf)
-{
-       static const unsigned int t4_reg_ranges[] = {
-               0x1008, 0x1108,
-               0x1180, 0x11b4,
-               0x11fc, 0x123c,
-               0x1300, 0x173c,
-               0x1800, 0x18fc,
-               0x3000, 0x30d8,
-               0x30e0, 0x5924,
-               0x5960, 0x59d4,
-               0x5a00, 0x5af8,
-               0x6000, 0x6098,
-               0x6100, 0x6150,
-               0x6200, 0x6208,
-               0x6240, 0x6248,
-               0x6280, 0x6338,
-               0x6370, 0x638c,
-               0x6400, 0x643c,
-               0x6500, 0x6524,
-               0x6a00, 0x6a38,
-               0x6a60, 0x6a78,
-               0x6b00, 0x6b84,
-               0x6bf0, 0x6c84,
-               0x6cf0, 0x6d84,
-               0x6df0, 0x6e84,
-               0x6ef0, 0x6f84,
-               0x6ff0, 0x7084,
-               0x70f0, 0x7184,
-               0x71f0, 0x7284,
-               0x72f0, 0x7384,
-               0x73f0, 0x7450,
-               0x7500, 0x7530,
-               0x7600, 0x761c,
-               0x7680, 0x76cc,
-               0x7700, 0x7798,
-               0x77c0, 0x77fc,
-               0x7900, 0x79fc,
-               0x7b00, 0x7c38,
-               0x7d00, 0x7efc,
-               0x8dc0, 0x8e1c,
-               0x8e30, 0x8e78,
-               0x8ea0, 0x8f6c,
-               0x8fc0, 0x9074,
-               0x90fc, 0x90fc,
-               0x9400, 0x9458,
-               0x9600, 0x96bc,
-               0x9800, 0x9808,
-               0x9820, 0x983c,
-               0x9850, 0x9864,
-               0x9c00, 0x9c6c,
-               0x9c80, 0x9cec,
-               0x9d00, 0x9d6c,
-               0x9d80, 0x9dec,
-               0x9e00, 0x9e6c,
-               0x9e80, 0x9eec,
-               0x9f00, 0x9f6c,
-               0x9f80, 0x9fec,
-               0xd004, 0xd03c,
-               0xdfc0, 0xdfe0,
-               0xe000, 0xea7c,
-               0xf000, 0x11110,
-               0x11118, 0x11190,
-               0x19040, 0x1906c,
-               0x19078, 0x19080,
-               0x1908c, 0x19124,
-               0x19150, 0x191b0,
-               0x191d0, 0x191e8,
-               0x19238, 0x1924c,
-               0x193f8, 0x19474,
-               0x19490, 0x194f8,
-               0x19800, 0x19f30,
-               0x1a000, 0x1a06c,
-               0x1a0b0, 0x1a120,
-               0x1a128, 0x1a138,
-               0x1a190, 0x1a1c4,
-               0x1a1fc, 0x1a1fc,
-               0x1e040, 0x1e04c,
-               0x1e284, 0x1e28c,
-               0x1e2c0, 0x1e2c0,
-               0x1e2e0, 0x1e2e0,
-               0x1e300, 0x1e384,
-               0x1e3c0, 0x1e3c8,
-               0x1e440, 0x1e44c,
-               0x1e684, 0x1e68c,
-               0x1e6c0, 0x1e6c0,
-               0x1e6e0, 0x1e6e0,
-               0x1e700, 0x1e784,
-               0x1e7c0, 0x1e7c8,
-               0x1e840, 0x1e84c,
-               0x1ea84, 0x1ea8c,
-               0x1eac0, 0x1eac0,
-               0x1eae0, 0x1eae0,
-               0x1eb00, 0x1eb84,
-               0x1ebc0, 0x1ebc8,
-               0x1ec40, 0x1ec4c,
-               0x1ee84, 0x1ee8c,
-               0x1eec0, 0x1eec0,
-               0x1eee0, 0x1eee0,
-               0x1ef00, 0x1ef84,
-               0x1efc0, 0x1efc8,
-               0x1f040, 0x1f04c,
-               0x1f284, 0x1f28c,
-               0x1f2c0, 0x1f2c0,
-               0x1f2e0, 0x1f2e0,
-               0x1f300, 0x1f384,
-               0x1f3c0, 0x1f3c8,
-               0x1f440, 0x1f44c,
-               0x1f684, 0x1f68c,
-               0x1f6c0, 0x1f6c0,
-               0x1f6e0, 0x1f6e0,
-               0x1f700, 0x1f784,
-               0x1f7c0, 0x1f7c8,
-               0x1f840, 0x1f84c,
-               0x1fa84, 0x1fa8c,
-               0x1fac0, 0x1fac0,
-               0x1fae0, 0x1fae0,
-               0x1fb00, 0x1fb84,
-               0x1fbc0, 0x1fbc8,
-               0x1fc40, 0x1fc4c,
-               0x1fe84, 0x1fe8c,
-               0x1fec0, 0x1fec0,
-               0x1fee0, 0x1fee0,
-               0x1ff00, 0x1ff84,
-               0x1ffc0, 0x1ffc8,
-               0x20000, 0x2002c,
-               0x20100, 0x2013c,
-               0x20190, 0x201c8,
-               0x20200, 0x20318,
-               0x20400, 0x20528,
-               0x20540, 0x20614,
-               0x21000, 0x21040,
-               0x2104c, 0x21060,
-               0x210c0, 0x210ec,
-               0x21200, 0x21268,
-               0x21270, 0x21284,
-               0x212fc, 0x21388,
-               0x21400, 0x21404,
-               0x21500, 0x21518,
-               0x2152c, 0x2153c,
-               0x21550, 0x21554,
-               0x21600, 0x21600,
-               0x21608, 0x21628,
-               0x21630, 0x2163c,
-               0x21700, 0x2171c,
-               0x21780, 0x2178c,
-               0x21800, 0x21c38,
-               0x21c80, 0x21d7c,
-               0x21e00, 0x21e04,
-               0x22000, 0x2202c,
-               0x22100, 0x2213c,
-               0x22190, 0x221c8,
-               0x22200, 0x22318,
-               0x22400, 0x22528,
-               0x22540, 0x22614,
-               0x23000, 0x23040,
-               0x2304c, 0x23060,
-               0x230c0, 0x230ec,
-               0x23200, 0x23268,
-               0x23270, 0x23284,
-               0x232fc, 0x23388,
-               0x23400, 0x23404,
-               0x23500, 0x23518,
-               0x2352c, 0x2353c,
-               0x23550, 0x23554,
-               0x23600, 0x23600,
-               0x23608, 0x23628,
-               0x23630, 0x2363c,
-               0x23700, 0x2371c,
-               0x23780, 0x2378c,
-               0x23800, 0x23c38,
-               0x23c80, 0x23d7c,
-               0x23e00, 0x23e04,
-               0x24000, 0x2402c,
-               0x24100, 0x2413c,
-               0x24190, 0x241c8,
-               0x24200, 0x24318,
-               0x24400, 0x24528,
-               0x24540, 0x24614,
-               0x25000, 0x25040,
-               0x2504c, 0x25060,
-               0x250c0, 0x250ec,
-               0x25200, 0x25268,
-               0x25270, 0x25284,
-               0x252fc, 0x25388,
-               0x25400, 0x25404,
-               0x25500, 0x25518,
-               0x2552c, 0x2553c,
-               0x25550, 0x25554,
-               0x25600, 0x25600,
-               0x25608, 0x25628,
-               0x25630, 0x2563c,
-               0x25700, 0x2571c,
-               0x25780, 0x2578c,
-               0x25800, 0x25c38,
-               0x25c80, 0x25d7c,
-               0x25e00, 0x25e04,
-               0x26000, 0x2602c,
-               0x26100, 0x2613c,
-               0x26190, 0x261c8,
-               0x26200, 0x26318,
-               0x26400, 0x26528,
-               0x26540, 0x26614,
-               0x27000, 0x27040,
-               0x2704c, 0x27060,
-               0x270c0, 0x270ec,
-               0x27200, 0x27268,
-               0x27270, 0x27284,
-               0x272fc, 0x27388,
-               0x27400, 0x27404,
-               0x27500, 0x27518,
-               0x2752c, 0x2753c,
-               0x27550, 0x27554,
-               0x27600, 0x27600,
-               0x27608, 0x27628,
-               0x27630, 0x2763c,
-               0x27700, 0x2771c,
-               0x27780, 0x2778c,
-               0x27800, 0x27c38,
-               0x27c80, 0x27d7c,
-               0x27e00, 0x27e04
-       };
-
-       static const unsigned int t5_reg_ranges[] = {
-               0x1008, 0x1148,
-               0x1180, 0x11b4,
-               0x11fc, 0x123c,
-               0x1280, 0x173c,
-               0x1800, 0x18fc,
-               0x3000, 0x3028,
-               0x3060, 0x30d8,
-               0x30e0, 0x30fc,
-               0x3140, 0x357c,
-               0x35a8, 0x35cc,
-               0x35ec, 0x35ec,
-               0x3600, 0x5624,
-               0x56cc, 0x575c,
-               0x580c, 0x5814,
-               0x5890, 0x58bc,
-               0x5940, 0x59dc,
-               0x59fc, 0x5a18,
-               0x5a60, 0x5a9c,
-               0x5b9c, 0x5bfc,
-               0x6000, 0x6040,
-               0x6058, 0x614c,
-               0x7700, 0x7798,
-               0x77c0, 0x78fc,
-               0x7b00, 0x7c54,
-               0x7d00, 0x7efc,
-               0x8dc0, 0x8de0,
-               0x8df8, 0x8e84,
-               0x8ea0, 0x8f84,
-               0x8fc0, 0x90f8,
-               0x9400, 0x9470,
-               0x9600, 0x96f4,
-               0x9800, 0x9808,
-               0x9820, 0x983c,
-               0x9850, 0x9864,
-               0x9c00, 0x9c6c,
-               0x9c80, 0x9cec,
-               0x9d00, 0x9d6c,
-               0x9d80, 0x9dec,
-               0x9e00, 0x9e6c,
-               0x9e80, 0x9eec,
-               0x9f00, 0x9f6c,
-               0x9f80, 0xa020,
-               0xd004, 0xd03c,
-               0xdfc0, 0xdfe0,
-               0xe000, 0x11088,
-               0x1109c, 0x11110,
-               0x11118, 0x1117c,
-               0x11190, 0x11204,
-               0x19040, 0x1906c,
-               0x19078, 0x19080,
-               0x1908c, 0x19124,
-               0x19150, 0x191b0,
-               0x191d0, 0x191e8,
-               0x19238, 0x19290,
-               0x193f8, 0x19474,
-               0x19490, 0x194cc,
-               0x194f0, 0x194f8,
-               0x19c00, 0x19c60,
-               0x19c94, 0x19e10,
-               0x19e50, 0x19f34,
-               0x19f40, 0x19f50,
-               0x19f90, 0x19fe4,
-               0x1a000, 0x1a06c,
-               0x1a0b0, 0x1a120,
-               0x1a128, 0x1a138,
-               0x1a190, 0x1a1c4,
-               0x1a1fc, 0x1a1fc,
-               0x1e008, 0x1e00c,
-               0x1e040, 0x1e04c,
-               0x1e284, 0x1e290,
-               0x1e2c0, 0x1e2c0,
-               0x1e2e0, 0x1e2e0,
-               0x1e300, 0x1e384,
-               0x1e3c0, 0x1e3c8,
-               0x1e408, 0x1e40c,
-               0x1e440, 0x1e44c,
-               0x1e684, 0x1e690,
-               0x1e6c0, 0x1e6c0,
-               0x1e6e0, 0x1e6e0,
-               0x1e700, 0x1e784,
-               0x1e7c0, 0x1e7c8,
-               0x1e808, 0x1e80c,
-               0x1e840, 0x1e84c,
-               0x1ea84, 0x1ea90,
-               0x1eac0, 0x1eac0,
-               0x1eae0, 0x1eae0,
-               0x1eb00, 0x1eb84,
-               0x1ebc0, 0x1ebc8,
-               0x1ec08, 0x1ec0c,
-               0x1ec40, 0x1ec4c,
-               0x1ee84, 0x1ee90,
-               0x1eec0, 0x1eec0,
-               0x1eee0, 0x1eee0,
-               0x1ef00, 0x1ef84,
-               0x1efc0, 0x1efc8,
-               0x1f008, 0x1f00c,
-               0x1f040, 0x1f04c,
-               0x1f284, 0x1f290,
-               0x1f2c0, 0x1f2c0,
-               0x1f2e0, 0x1f2e0,
-               0x1f300, 0x1f384,
-               0x1f3c0, 0x1f3c8,
-               0x1f408, 0x1f40c,
-               0x1f440, 0x1f44c,
-               0x1f684, 0x1f690,
-               0x1f6c0, 0x1f6c0,
-               0x1f6e0, 0x1f6e0,
-               0x1f700, 0x1f784,
-               0x1f7c0, 0x1f7c8,
-               0x1f808, 0x1f80c,
-               0x1f840, 0x1f84c,
-               0x1fa84, 0x1fa90,
-               0x1fac0, 0x1fac0,
-               0x1fae0, 0x1fae0,
-               0x1fb00, 0x1fb84,
-               0x1fbc0, 0x1fbc8,
-               0x1fc08, 0x1fc0c,
-               0x1fc40, 0x1fc4c,
-               0x1fe84, 0x1fe90,
-               0x1fec0, 0x1fec0,
-               0x1fee0, 0x1fee0,
-               0x1ff00, 0x1ff84,
-               0x1ffc0, 0x1ffc8,
-               0x30000, 0x30030,
-               0x30100, 0x30144,
-               0x30190, 0x301d0,
-               0x30200, 0x30318,
-               0x30400, 0x3052c,
-               0x30540, 0x3061c,
-               0x30800, 0x30834,
-               0x308c0, 0x30908,
-               0x30910, 0x309ac,
-               0x30a00, 0x30a04,
-               0x30a0c, 0x30a2c,
-               0x30a44, 0x30a50,
-               0x30a74, 0x30c24,
-               0x30d08, 0x30d14,
-               0x30d1c, 0x30d20,
-               0x30d3c, 0x30d50,
-               0x31200, 0x3120c,
-               0x31220, 0x31220,
-               0x31240, 0x31240,
-               0x31600, 0x31600,
-               0x31608, 0x3160c,
-               0x31a00, 0x31a1c,
-               0x31e04, 0x31e20,
-               0x31e38, 0x31e3c,
-               0x31e80, 0x31e80,
-               0x31e88, 0x31ea8,
-               0x31eb0, 0x31eb4,
-               0x31ec8, 0x31ed4,
-               0x31fb8, 0x32004,
-               0x32208, 0x3223c,
-               0x32600, 0x32630,
-               0x32a00, 0x32abc,
-               0x32b00, 0x32b70,
-               0x33000, 0x33048,
-               0x33060, 0x3309c,
-               0x330f0, 0x33148,
-               0x33160, 0x3319c,
-               0x331f0, 0x332e4,
-               0x332f8, 0x333e4,
-               0x333f8, 0x33448,
-               0x33460, 0x3349c,
-               0x334f0, 0x33548,
-               0x33560, 0x3359c,
-               0x335f0, 0x336e4,
-               0x336f8, 0x337e4,
-               0x337f8, 0x337fc,
-               0x33814, 0x33814,
-               0x3382c, 0x3382c,
-               0x33880, 0x3388c,
-               0x338e8, 0x338ec,
-               0x33900, 0x33948,
-               0x33960, 0x3399c,
-               0x339f0, 0x33ae4,
-               0x33af8, 0x33b10,
-               0x33b28, 0x33b28,
-               0x33b3c, 0x33b50,
-               0x33bf0, 0x33c10,
-               0x33c28, 0x33c28,
-               0x33c3c, 0x33c50,
-               0x33cf0, 0x33cfc,
-               0x34000, 0x34030,
-               0x34100, 0x34144,
-               0x34190, 0x341d0,
-               0x34200, 0x34318,
-               0x34400, 0x3452c,
-               0x34540, 0x3461c,
-               0x34800, 0x34834,
-               0x348c0, 0x34908,
-               0x34910, 0x349ac,
-               0x34a00, 0x34a04,
-               0x34a0c, 0x34a2c,
-               0x34a44, 0x34a50,
-               0x34a74, 0x34c24,
-               0x34d08, 0x34d14,
-               0x34d1c, 0x34d20,
-               0x34d3c, 0x34d50,
-               0x35200, 0x3520c,
-               0x35220, 0x35220,
-               0x35240, 0x35240,
-               0x35600, 0x35600,
-               0x35608, 0x3560c,
-               0x35a00, 0x35a1c,
-               0x35e04, 0x35e20,
-               0x35e38, 0x35e3c,
-               0x35e80, 0x35e80,
-               0x35e88, 0x35ea8,
-               0x35eb0, 0x35eb4,
-               0x35ec8, 0x35ed4,
-               0x35fb8, 0x36004,
-               0x36208, 0x3623c,
-               0x36600, 0x36630,
-               0x36a00, 0x36abc,
-               0x36b00, 0x36b70,
-               0x37000, 0x37048,
-               0x37060, 0x3709c,
-               0x370f0, 0x37148,
-               0x37160, 0x3719c,
-               0x371f0, 0x372e4,
-               0x372f8, 0x373e4,
-               0x373f8, 0x37448,
-               0x37460, 0x3749c,
-               0x374f0, 0x37548,
-               0x37560, 0x3759c,
-               0x375f0, 0x376e4,
-               0x376f8, 0x377e4,
-               0x377f8, 0x377fc,
-               0x37814, 0x37814,
-               0x3782c, 0x3782c,
-               0x37880, 0x3788c,
-               0x378e8, 0x378ec,
-               0x37900, 0x37948,
-               0x37960, 0x3799c,
-               0x379f0, 0x37ae4,
-               0x37af8, 0x37b10,
-               0x37b28, 0x37b28,
-               0x37b3c, 0x37b50,
-               0x37bf0, 0x37c10,
-               0x37c28, 0x37c28,
-               0x37c3c, 0x37c50,
-               0x37cf0, 0x37cfc,
-               0x38000, 0x38030,
-               0x38100, 0x38144,
-               0x38190, 0x381d0,
-               0x38200, 0x38318,
-               0x38400, 0x3852c,
-               0x38540, 0x3861c,
-               0x38800, 0x38834,
-               0x388c0, 0x38908,
-               0x38910, 0x389ac,
-               0x38a00, 0x38a04,
-               0x38a0c, 0x38a2c,
-               0x38a44, 0x38a50,
-               0x38a74, 0x38c24,
-               0x38d08, 0x38d14,
-               0x38d1c, 0x38d20,
-               0x38d3c, 0x38d50,
-               0x39200, 0x3920c,
-               0x39220, 0x39220,
-               0x39240, 0x39240,
-               0x39600, 0x39600,
-               0x39608, 0x3960c,
-               0x39a00, 0x39a1c,
-               0x39e04, 0x39e20,
-               0x39e38, 0x39e3c,
-               0x39e80, 0x39e80,
-               0x39e88, 0x39ea8,
-               0x39eb0, 0x39eb4,
-               0x39ec8, 0x39ed4,
-               0x39fb8, 0x3a004,
-               0x3a208, 0x3a23c,
-               0x3a600, 0x3a630,
-               0x3aa00, 0x3aabc,
-               0x3ab00, 0x3ab70,
-               0x3b000, 0x3b048,
-               0x3b060, 0x3b09c,
-               0x3b0f0, 0x3b148,
-               0x3b160, 0x3b19c,
-               0x3b1f0, 0x3b2e4,
-               0x3b2f8, 0x3b3e4,
-               0x3b3f8, 0x3b448,
-               0x3b460, 0x3b49c,
-               0x3b4f0, 0x3b548,
-               0x3b560, 0x3b59c,
-               0x3b5f0, 0x3b6e4,
-               0x3b6f8, 0x3b7e4,
-               0x3b7f8, 0x3b7fc,
-               0x3b814, 0x3b814,
-               0x3b82c, 0x3b82c,
-               0x3b880, 0x3b88c,
-               0x3b8e8, 0x3b8ec,
-               0x3b900, 0x3b948,
-               0x3b960, 0x3b99c,
-               0x3b9f0, 0x3bae4,
-               0x3baf8, 0x3bb10,
-               0x3bb28, 0x3bb28,
-               0x3bb3c, 0x3bb50,
-               0x3bbf0, 0x3bc10,
-               0x3bc28, 0x3bc28,
-               0x3bc3c, 0x3bc50,
-               0x3bcf0, 0x3bcfc,
-               0x3c000, 0x3c030,
-               0x3c100, 0x3c144,
-               0x3c190, 0x3c1d0,
-               0x3c200, 0x3c318,
-               0x3c400, 0x3c52c,
-               0x3c540, 0x3c61c,
-               0x3c800, 0x3c834,
-               0x3c8c0, 0x3c908,
-               0x3c910, 0x3c9ac,
-               0x3ca00, 0x3ca04,
-               0x3ca0c, 0x3ca2c,
-               0x3ca44, 0x3ca50,
-               0x3ca74, 0x3cc24,
-               0x3cd08, 0x3cd14,
-               0x3cd1c, 0x3cd20,
-               0x3cd3c, 0x3cd50,
-               0x3d200, 0x3d20c,
-               0x3d220, 0x3d220,
-               0x3d240, 0x3d240,
-               0x3d600, 0x3d600,
-               0x3d608, 0x3d60c,
-               0x3da00, 0x3da1c,
-               0x3de04, 0x3de20,
-               0x3de38, 0x3de3c,
-               0x3de80, 0x3de80,
-               0x3de88, 0x3dea8,
-               0x3deb0, 0x3deb4,
-               0x3dec8, 0x3ded4,
-               0x3dfb8, 0x3e004,
-               0x3e208, 0x3e23c,
-               0x3e600, 0x3e630,
-               0x3ea00, 0x3eabc,
-               0x3eb00, 0x3eb70,
-               0x3f000, 0x3f048,
-               0x3f060, 0x3f09c,
-               0x3f0f0, 0x3f148,
-               0x3f160, 0x3f19c,
-               0x3f1f0, 0x3f2e4,
-               0x3f2f8, 0x3f3e4,
-               0x3f3f8, 0x3f448,
-               0x3f460, 0x3f49c,
-               0x3f4f0, 0x3f548,
-               0x3f560, 0x3f59c,
-               0x3f5f0, 0x3f6e4,
-               0x3f6f8, 0x3f7e4,
-               0x3f7f8, 0x3f7fc,
-               0x3f814, 0x3f814,
-               0x3f82c, 0x3f82c,
-               0x3f880, 0x3f88c,
-               0x3f8e8, 0x3f8ec,
-               0x3f900, 0x3f948,
-               0x3f960, 0x3f99c,
-               0x3f9f0, 0x3fae4,
-               0x3faf8, 0x3fb10,
-               0x3fb28, 0x3fb28,
-               0x3fb3c, 0x3fb50,
-               0x3fbf0, 0x3fc10,
-               0x3fc28, 0x3fc28,
-               0x3fc3c, 0x3fc50,
-               0x3fcf0, 0x3fcfc,
-               0x40000, 0x4000c,
-               0x40040, 0x40068,
-               0x40080, 0x40144,
-               0x40180, 0x4018c,
-               0x40200, 0x40298,
-               0x402ac, 0x4033c,
-               0x403f8, 0x403fc,
-               0x41304, 0x413c4,
-               0x41400, 0x4141c,
-               0x41480, 0x414d0,
-               0x44000, 0x44078,
-               0x440c0, 0x44278,
-               0x442c0, 0x44478,
-               0x444c0, 0x44678,
-               0x446c0, 0x44878,
-               0x448c0, 0x449fc,
-               0x45000, 0x45068,
-               0x45080, 0x45084,
-               0x450a0, 0x450b0,
-               0x45200, 0x45268,
-               0x45280, 0x45284,
-               0x452a0, 0x452b0,
-               0x460c0, 0x460e4,
-               0x47000, 0x4708c,
-               0x47200, 0x47250,
-               0x47400, 0x47420,
-               0x47600, 0x47618,
-               0x47800, 0x47814,
-               0x48000, 0x4800c,
-               0x48040, 0x48068,
-               0x48080, 0x48144,
-               0x48180, 0x4818c,
-               0x48200, 0x48298,
-               0x482ac, 0x4833c,
-               0x483f8, 0x483fc,
-               0x49304, 0x493c4,
-               0x49400, 0x4941c,
-               0x49480, 0x494d0,
-               0x4c000, 0x4c078,
-               0x4c0c0, 0x4c278,
-               0x4c2c0, 0x4c478,
-               0x4c4c0, 0x4c678,
-               0x4c6c0, 0x4c878,
-               0x4c8c0, 0x4c9fc,
-               0x4d000, 0x4d068,
-               0x4d080, 0x4d084,
-               0x4d0a0, 0x4d0b0,
-               0x4d200, 0x4d268,
-               0x4d280, 0x4d284,
-               0x4d2a0, 0x4d2b0,
-               0x4e0c0, 0x4e0e4,
-               0x4f000, 0x4f08c,
-               0x4f200, 0x4f250,
-               0x4f400, 0x4f420,
-               0x4f600, 0x4f618,
-               0x4f800, 0x4f814,
-               0x50000, 0x500cc,
-               0x50400, 0x50400,
-               0x50800, 0x508cc,
-               0x50c00, 0x50c00,
-               0x51000, 0x5101c,
-               0x51300, 0x51308,
-       };
-
-       int i;
-       struct adapter *ap = netdev2adap(dev);
-       static const unsigned int *reg_ranges;
-       int arr_size = 0, buf_size = 0;
-
-       if (is_t4(ap->params.chip)) {
-               reg_ranges = &t4_reg_ranges[0];
-               arr_size = ARRAY_SIZE(t4_reg_ranges);
-               buf_size = T4_REGMAP_SIZE;
-       } else {
-               reg_ranges = &t5_reg_ranges[0];
-               arr_size = ARRAY_SIZE(t5_reg_ranges);
-               buf_size = T5_REGMAP_SIZE;
-       }
-
-       regs->version = mk_adap_vers(ap);
-
-       memset(buf, 0, buf_size);
-       for (i = 0; i < arr_size; i += 2)
-               reg_block_dump(ap, buf, reg_ranges[i], reg_ranges[i + 1]);
-}
-
-static int restart_autoneg(struct net_device *dev)
-{
-       struct port_info *p = netdev_priv(dev);
-
-       if (!netif_running(dev))
-               return -EAGAIN;
-       if (p->link_cfg.autoneg != AUTONEG_ENABLE)
-               return -EINVAL;
-       t4_restart_aneg(p->adapter, p->adapter->fn, p->tx_chan);
-       return 0;
-}
-
-static int identify_port(struct net_device *dev,
-                        enum ethtool_phys_id_state state)
-{
-       unsigned int val;
-       struct adapter *adap = netdev2adap(dev);
-
-       if (state == ETHTOOL_ID_ACTIVE)
-               val = 0xffff;
-       else if (state == ETHTOOL_ID_INACTIVE)
-               val = 0;
-       else
-               return -EINVAL;
-
-       return t4_identify_port(adap, adap->fn, netdev2pinfo(dev)->viid, val);
-}
-
-static unsigned int from_fw_linkcaps(enum fw_port_type type, unsigned int caps)
-{
-       unsigned int v = 0;
-
-       if (type == FW_PORT_TYPE_BT_SGMII || type == FW_PORT_TYPE_BT_XFI ||
-           type == FW_PORT_TYPE_BT_XAUI) {
-               v |= SUPPORTED_TP;
-               if (caps & FW_PORT_CAP_SPEED_100M)
-                       v |= SUPPORTED_100baseT_Full;
-               if (caps & FW_PORT_CAP_SPEED_1G)
-                       v |= SUPPORTED_1000baseT_Full;
-               if (caps & FW_PORT_CAP_SPEED_10G)
-                       v |= SUPPORTED_10000baseT_Full;
-       } else if (type == FW_PORT_TYPE_KX4 || type == FW_PORT_TYPE_KX) {
-               v |= SUPPORTED_Backplane;
-               if (caps & FW_PORT_CAP_SPEED_1G)
-                       v |= SUPPORTED_1000baseKX_Full;
-               if (caps & FW_PORT_CAP_SPEED_10G)
-                       v |= SUPPORTED_10000baseKX4_Full;
-       } else if (type == FW_PORT_TYPE_KR)
-               v |= SUPPORTED_Backplane | SUPPORTED_10000baseKR_Full;
-       else if (type == FW_PORT_TYPE_BP_AP)
-               v |= SUPPORTED_Backplane | SUPPORTED_10000baseR_FEC |
-                    SUPPORTED_10000baseKR_Full | SUPPORTED_1000baseKX_Full;
-       else if (type == FW_PORT_TYPE_BP4_AP)
-               v |= SUPPORTED_Backplane | SUPPORTED_10000baseR_FEC |
-                    SUPPORTED_10000baseKR_Full | SUPPORTED_1000baseKX_Full |
-                    SUPPORTED_10000baseKX4_Full;
-       else if (type == FW_PORT_TYPE_FIBER_XFI ||
-                type == FW_PORT_TYPE_FIBER_XAUI ||
-                type == FW_PORT_TYPE_SFP ||
-                type == FW_PORT_TYPE_QSFP_10G ||
-                type == FW_PORT_TYPE_QSA) {
-               v |= SUPPORTED_FIBRE;
-               if (caps & FW_PORT_CAP_SPEED_1G)
-                       v |= SUPPORTED_1000baseT_Full;
-               if (caps & FW_PORT_CAP_SPEED_10G)
-                       v |= SUPPORTED_10000baseT_Full;
-       } else if (type == FW_PORT_TYPE_BP40_BA ||
-                  type == FW_PORT_TYPE_QSFP) {
-               v |= SUPPORTED_40000baseSR4_Full;
-               v |= SUPPORTED_FIBRE;
-       }
-
-       if (caps & FW_PORT_CAP_ANEG)
-               v |= SUPPORTED_Autoneg;
-       return v;
-}
-
-static unsigned int to_fw_linkcaps(unsigned int caps)
-{
-       unsigned int v = 0;
-
-       if (caps & ADVERTISED_100baseT_Full)
-               v |= FW_PORT_CAP_SPEED_100M;
-       if (caps & ADVERTISED_1000baseT_Full)
-               v |= FW_PORT_CAP_SPEED_1G;
-       if (caps & ADVERTISED_10000baseT_Full)
-               v |= FW_PORT_CAP_SPEED_10G;
-       if (caps & ADVERTISED_40000baseSR4_Full)
-               v |= FW_PORT_CAP_SPEED_40G;
-       return v;
-}
-
-static int get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
-{
-       const struct port_info *p = netdev_priv(dev);
-
-       if (p->port_type == FW_PORT_TYPE_BT_SGMII ||
-           p->port_type == FW_PORT_TYPE_BT_XFI ||
-           p->port_type == FW_PORT_TYPE_BT_XAUI)
-               cmd->port = PORT_TP;
-       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 ||
-                p->port_type == FW_PORT_TYPE_QSFP_10G ||
-                p->port_type == FW_PORT_TYPE_QSA ||
-                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_OTHER;
-       } else
-               cmd->port = PORT_OTHER;
-
-       if (p->mdio_addr >= 0) {
-               cmd->phy_address = p->mdio_addr;
-               cmd->transceiver = XCVR_EXTERNAL;
-               cmd->mdio_support = p->port_type == FW_PORT_TYPE_BT_SGMII ?
-                       MDIO_SUPPORTS_C22 : MDIO_SUPPORTS_C45;
-       } else {
-               cmd->phy_address = 0;  /* not really, but no better option */
-               cmd->transceiver = XCVR_INTERNAL;
-               cmd->mdio_support = 0;
-       }
-
-       cmd->supported = from_fw_linkcaps(p->port_type, p->link_cfg.supported);
-       cmd->advertising = from_fw_linkcaps(p->port_type,
-                                           p->link_cfg.advertising);
-       ethtool_cmd_speed_set(cmd,
-                             netif_carrier_ok(dev) ? p->link_cfg.speed : 0);
-       cmd->duplex = DUPLEX_FULL;
-       cmd->autoneg = p->link_cfg.autoneg;
-       cmd->maxtxpkt = 0;
-       cmd->maxrxpkt = 0;
-       return 0;
-}
-
-static unsigned int speed_to_caps(int speed)
-{
-       if (speed == 100)
-               return FW_PORT_CAP_SPEED_100M;
-       if (speed == 1000)
-               return FW_PORT_CAP_SPEED_1G;
-       if (speed == 10000)
-               return FW_PORT_CAP_SPEED_10G;
-       if (speed == 40000)
-               return FW_PORT_CAP_SPEED_40G;
-       return 0;
-}
-
-static int set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
-{
-       unsigned int cap;
-       struct port_info *p = netdev_priv(dev);
-       struct link_config *lc = &p->link_cfg;
-       u32 speed = ethtool_cmd_speed(cmd);
-
-       if (cmd->duplex != DUPLEX_FULL)     /* only full-duplex supported */
-               return -EINVAL;
-
-       if (!(lc->supported & FW_PORT_CAP_ANEG)) {
-               /*
-                * PHY offers a single speed.  See if that's what's
-                * being requested.
-                */
-               if (cmd->autoneg == AUTONEG_DISABLE &&
-                   (lc->supported & speed_to_caps(speed)))
-                       return 0;
-               return -EINVAL;
-       }
-
-       if (cmd->autoneg == AUTONEG_DISABLE) {
-               cap = speed_to_caps(speed);
-
-               if (!(lc->supported & cap) ||
-                   (speed == 1000) ||
-                   (speed == 10000) ||
-                   (speed == 40000))
-                       return -EINVAL;
-               lc->requested_speed = cap;
-               lc->advertising = 0;
-       } else {
-               cap = to_fw_linkcaps(cmd->advertising);
-               if (!(lc->supported & cap))
-                       return -EINVAL;
-               lc->requested_speed = 0;
-               lc->advertising = cap | FW_PORT_CAP_ANEG;
-       }
-       lc->autoneg = cmd->autoneg;
-
-       if (netif_running(dev))
-               return t4_link_start(p->adapter, p->adapter->fn, p->tx_chan,
-                                    lc);
-       return 0;
-}
-
-static void get_pauseparam(struct net_device *dev,
-                          struct ethtool_pauseparam *epause)
-{
-       struct port_info *p = netdev_priv(dev);
-
-       epause->autoneg = (p->link_cfg.requested_fc & PAUSE_AUTONEG) != 0;
-       epause->rx_pause = (p->link_cfg.fc & PAUSE_RX) != 0;
-       epause->tx_pause = (p->link_cfg.fc & PAUSE_TX) != 0;
-}
-
-static int set_pauseparam(struct net_device *dev,
-                         struct ethtool_pauseparam *epause)
-{
-       struct port_info *p = netdev_priv(dev);
-       struct link_config *lc = &p->link_cfg;
-
-       if (epause->autoneg == AUTONEG_DISABLE)
-               lc->requested_fc = 0;
-       else if (lc->supported & FW_PORT_CAP_ANEG)
-               lc->requested_fc = PAUSE_AUTONEG;
-       else
-               return -EINVAL;
-
-       if (epause->rx_pause)
-               lc->requested_fc |= PAUSE_RX;
-       if (epause->tx_pause)
-               lc->requested_fc |= PAUSE_TX;
-       if (netif_running(dev))
-               return t4_link_start(p->adapter, p->adapter->fn, p->tx_chan,
-                                    lc);
-       return 0;
-}
-
-static void get_sge_param(struct net_device *dev, struct ethtool_ringparam *e)
-{
-       const struct port_info *pi = netdev_priv(dev);
-       const struct sge *s = &pi->adapter->sge;
-
-       e->rx_max_pending = MAX_RX_BUFFERS;
-       e->rx_mini_max_pending = MAX_RSPQ_ENTRIES;
-       e->rx_jumbo_max_pending = 0;
-       e->tx_max_pending = MAX_TXQ_ENTRIES;
-
-       e->rx_pending = s->ethrxq[pi->first_qset].fl.size - 8;
-       e->rx_mini_pending = s->ethrxq[pi->first_qset].rspq.size;
-       e->rx_jumbo_pending = 0;
-       e->tx_pending = s->ethtxq[pi->first_qset].q.size;
-}
-
-static int set_sge_param(struct net_device *dev, struct ethtool_ringparam *e)
-{
-       int i;
-       const struct port_info *pi = netdev_priv(dev);
-       struct adapter *adapter = pi->adapter;
-       struct sge *s = &adapter->sge;
-
-       if (e->rx_pending > MAX_RX_BUFFERS || e->rx_jumbo_pending ||
-           e->tx_pending > MAX_TXQ_ENTRIES ||
-           e->rx_mini_pending > MAX_RSPQ_ENTRIES ||
-           e->rx_mini_pending < MIN_RSPQ_ENTRIES ||
-           e->rx_pending < MIN_FL_ENTRIES || e->tx_pending < MIN_TXQ_ENTRIES)
-               return -EINVAL;
-
-       if (adapter->flags & FULL_INIT_DONE)
-               return -EBUSY;
-
-       for (i = 0; i < pi->nqsets; ++i) {
-               s->ethtxq[pi->first_qset + i].q.size = e->tx_pending;
-               s->ethrxq[pi->first_qset + i].fl.size = e->rx_pending + 8;
-               s->ethrxq[pi->first_qset + i].rspq.size = e->rx_mini_pending;
-       }
-       return 0;
-}
-
 static int closest_timer(const struct sge *s, int time)
 {
        int i, delta, match = 0, min_delta = INT_MAX;
@@ -2545,19 +1351,8 @@ static int closest_thres(const struct sge *s, int thres)
        return match;
 }
 
-/*
- * Return a queue's interrupt hold-off time in us.  0 means no timer.
- */
-unsigned int qtimer_val(const struct adapter *adap,
-                       const struct sge_rspq *q)
-{
-       unsigned int idx = q->intr_params >> 1;
-
-       return idx < SGE_NTIMERS ? adap->sge.timer_val[idx] : 0;
-}
-
 /**
- *     set_rspq_intr_params - set a queue's interrupt holdoff parameters
+ *     cxgb4_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
@@ -2565,8 +1360,8 @@ 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_rspq_intr_params(struct sge_rspq *q,
-                               unsigned int us, unsigned int cnt)
+int cxgb4_set_rspq_intr_params(struct sge_rspq *q,
+                              unsigned int us, unsigned int cnt)
 {
        struct adapter *adap = q->adap;
 
@@ -2597,259 +1392,6 @@ static int set_rspq_intr_params(struct sge_rspq *q,
        return 0;
 }
 
-/**
- * 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)
-{
-       int i, err;
-       struct port_info *pi = netdev_priv(dev);
-       struct adapter *adap = pi->adapter;
-       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 0;
-}
-
-static int set_adaptive_rx_setting(struct net_device *dev, int adaptive_rx)
-{
-       int i;
-       struct port_info *pi = netdev_priv(dev);
-       struct adapter *adap = pi->adapter;
-       struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset];
-
-       for (i = 0; i < pi->nqsets; i++, q++)
-               q->rspq.adaptive_rx = adaptive_rx;
-
-       return 0;
-}
-
-static int get_adaptive_rx_setting(struct net_device *dev)
-{
-       struct port_info *pi = netdev_priv(dev);
-       struct adapter *adap = pi->adapter;
-       struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset];
-
-       return q->rspq.adaptive_rx;
-}
-
-static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
-{
-       set_adaptive_rx_setting(dev, c->use_adaptive_rx_coalesce);
-       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)
-{
-       const struct port_info *pi = netdev_priv(dev);
-       const struct adapter *adap = pi->adapter;
-       const struct sge_rspq *rq = &adap->sge.ethrxq[pi->first_qset].rspq;
-
-       c->rx_coalesce_usecs = qtimer_val(adap, rq);
-       c->rx_max_coalesced_frames = (rq->intr_params & QINTR_CNT_EN) ?
-               adap->sge.counter_val[rq->pktcnt_idx] : 0;
-       c->use_adaptive_rx_coalesce = get_adaptive_rx_setting(dev);
-       return 0;
-}
-
-/**
- *     eeprom_ptov - translate a physical EEPROM address to virtual
- *     @phys_addr: the physical EEPROM address
- *     @fn: the PCI function number
- *     @sz: size of function-specific area
- *
- *     Translate a physical EEPROM address to virtual.  The first 1K is
- *     accessed through virtual addresses starting at 31K, the rest is
- *     accessed through virtual addresses starting at 0.
- *
- *     The mapping is as follows:
- *     [0..1K) -> [31K..32K)
- *     [1K..1K+A) -> [31K-A..31K)
- *     [1K+A..ES) -> [0..ES-A-1K)
- *
- *     where A = @fn * @sz, and ES = EEPROM size.
- */
-static int eeprom_ptov(unsigned int phys_addr, unsigned int fn, unsigned int sz)
-{
-       fn *= sz;
-       if (phys_addr < 1024)
-               return phys_addr + (31 << 10);
-       if (phys_addr < 1024 + fn)
-               return 31744 - fn + phys_addr - 1024;
-       if (phys_addr < EEPROMSIZE)
-               return phys_addr - 1024 - fn;
-       return -EINVAL;
-}
-
-/*
- * The next two routines implement eeprom read/write from physical addresses.
- */
-static int eeprom_rd_phys(struct adapter *adap, unsigned int phys_addr, u32 *v)
-{
-       int vaddr = eeprom_ptov(phys_addr, adap->fn, EEPROMPFSIZE);
-
-       if (vaddr >= 0)
-               vaddr = pci_read_vpd(adap->pdev, vaddr, sizeof(u32), v);
-       return vaddr < 0 ? vaddr : 0;
-}
-
-static int eeprom_wr_phys(struct adapter *adap, unsigned int phys_addr, u32 v)
-{
-       int vaddr = eeprom_ptov(phys_addr, adap->fn, EEPROMPFSIZE);
-
-       if (vaddr >= 0)
-               vaddr = pci_write_vpd(adap->pdev, vaddr, sizeof(u32), &v);
-       return vaddr < 0 ? vaddr : 0;
-}
-
-#define EEPROM_MAGIC 0x38E2F10C
-
-static int get_eeprom(struct net_device *dev, struct ethtool_eeprom *e,
-                     u8 *data)
-{
-       int i, err = 0;
-       struct adapter *adapter = netdev2adap(dev);
-
-       u8 *buf = kmalloc(EEPROMSIZE, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-
-       e->magic = EEPROM_MAGIC;
-       for (i = e->offset & ~3; !err && i < e->offset + e->len; i += 4)
-               err = eeprom_rd_phys(adapter, i, (u32 *)&buf[i]);
-
-       if (!err)
-               memcpy(data, buf + e->offset, e->len);
-       kfree(buf);
-       return err;
-}
-
-static int set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
-                     u8 *data)
-{
-       u8 *buf;
-       int err = 0;
-       u32 aligned_offset, aligned_len, *p;
-       struct adapter *adapter = netdev2adap(dev);
-
-       if (eeprom->magic != EEPROM_MAGIC)
-               return -EINVAL;
-
-       aligned_offset = eeprom->offset & ~3;
-       aligned_len = (eeprom->len + (eeprom->offset & 3) + 3) & ~3;
-
-       if (adapter->fn > 0) {
-               u32 start = 1024 + adapter->fn * EEPROMPFSIZE;
-
-               if (aligned_offset < start ||
-                   aligned_offset + aligned_len > start + EEPROMPFSIZE)
-                       return -EPERM;
-       }
-
-       if (aligned_offset != eeprom->offset || aligned_len != eeprom->len) {
-               /*
-                * RMW possibly needed for first or last words.
-                */
-               buf = kmalloc(aligned_len, GFP_KERNEL);
-               if (!buf)
-                       return -ENOMEM;
-               err = eeprom_rd_phys(adapter, aligned_offset, (u32 *)buf);
-               if (!err && aligned_len > 4)
-                       err = eeprom_rd_phys(adapter,
-                                            aligned_offset + aligned_len - 4,
-                                            (u32 *)&buf[aligned_len - 4]);
-               if (err)
-                       goto out;
-               memcpy(buf + (eeprom->offset & 3), data, eeprom->len);
-       } else
-               buf = data;
-
-       err = t4_seeprom_wp(adapter, false);
-       if (err)
-               goto out;
-
-       for (p = (u32 *)buf; !err && aligned_len; aligned_len -= 4, p++) {
-               err = eeprom_wr_phys(adapter, aligned_offset, *p);
-               aligned_offset += 4;
-       }
-
-       if (!err)
-               err = t4_seeprom_wp(adapter, true);
-out:
-       if (buf != data)
-               kfree(buf);
-       return err;
-}
-
-static int set_flash(struct net_device *netdev, struct ethtool_flash *ef)
-{
-       int ret;
-       const struct firmware *fw;
-       struct adapter *adap = netdev2adap(netdev);
-       unsigned int mbox = PCIE_FW_MASTER_M + 1;
-
-       ef->data[sizeof(ef->data) - 1] = '\0';
-       ret = request_firmware(&fw, ef->data, adap->pdev_dev);
-       if (ret < 0)
-               return ret;
-
-       /* If the adapter has been fully initialized then we'll go ahead and
-        * try to get the firmware's cooperation in upgrading to the new
-        * firmware image otherwise we'll try to do the entire job from the
-        * host ... and we always "force" the operation in this path.
-        */
-       if (adap->flags & FULL_INIT_DONE)
-               mbox = adap->mbox;
-
-       ret = t4_fw_upgrade(adap, mbox, fw->data, fw->size, 1);
-       release_firmware(fw);
-       if (!ret)
-               dev_info(adap->pdev_dev, "loaded firmware %s,"
-                        " reload cxgb4 driver\n", ef->data);
-       return ret;
-}
-
-#define WOL_SUPPORTED (WAKE_BCAST | WAKE_MAGIC)
-#define BCAST_CRC 0xa0ccc1a6
-
-static void get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
-{
-       wol->supported = WAKE_BCAST | WAKE_MAGIC;
-       wol->wolopts = netdev2adap(dev)->wol;
-       memset(&wol->sopass, 0, sizeof(wol->sopass));
-}
-
-static int set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
-{
-       int err = 0;
-       struct port_info *pi = netdev_priv(dev);
-
-       if (wol->wolopts & ~WOL_SUPPORTED)
-               return -EINVAL;
-       t4_wol_magic_enable(pi->adapter, pi->tx_chan,
-                           (wol->wolopts & WAKE_MAGIC) ? dev->dev_addr : NULL);
-       if (wol->wolopts & WAKE_BCAST) {
-               err = t4_wol_pat_enable(pi->adapter, pi->tx_chan, 0xfe, ~0ULL,
-                                       ~0ULL, 0, false);
-               if (!err)
-                       err = t4_wol_pat_enable(pi->adapter, pi->tx_chan, 1,
-                                               ~6ULL, ~0ULL, BCAST_CRC, true);
-       } else
-               t4_wol_pat_enable(pi->adapter, pi->tx_chan, 0, 0, 0, 0, false);
-       return err;
-}
-
 static int cxgb_set_features(struct net_device *dev, netdev_features_t features)
 {
        const struct port_info *pi = netdev_priv(dev);
@@ -2867,144 +1409,6 @@ static int cxgb_set_features(struct net_device *dev, netdev_features_t features)
        return err;
 }
 
-static u32 get_rss_table_size(struct net_device *dev)
-{
-       const struct port_info *pi = netdev_priv(dev);
-
-       return pi->rss_size;
-}
-
-static int get_rss_table(struct net_device *dev, u32 *p, u8 *key, u8 *hfunc)
-{
-       const struct port_info *pi = netdev_priv(dev);
-       unsigned int n = pi->rss_size;
-
-       if (hfunc)
-               *hfunc = ETH_RSS_HASH_TOP;
-       if (!p)
-               return 0;
-       while (n--)
-               p[n] = pi->rss[n];
-       return 0;
-}
-
-static int set_rss_table(struct net_device *dev, const u32 *p, const u8 *key,
-                        const u8 hfunc)
-{
-       unsigned int i;
-       struct port_info *pi = netdev_priv(dev);
-
-       /* We require at least one supported parameter to be changed and no
-        * change in any of the unsupported parameters
-        */
-       if (key ||
-           (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP))
-               return -EOPNOTSUPP;
-       if (!p)
-               return 0;
-
-       for (i = 0; i < pi->rss_size; i++)
-               pi->rss[i] = p[i];
-       if (pi->adapter->flags & FULL_INIT_DONE)
-               return write_rss(pi, pi->rss);
-       return 0;
-}
-
-static int get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info,
-                    u32 *rules)
-{
-       const struct port_info *pi = netdev_priv(dev);
-
-       switch (info->cmd) {
-       case ETHTOOL_GRXFH: {
-               unsigned int v = pi->rss_mode;
-
-               info->data = 0;
-               switch (info->flow_type) {
-               case TCP_V4_FLOW:
-                       if (v & FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN_F)
-                               info->data = RXH_IP_SRC | RXH_IP_DST |
-                                            RXH_L4_B_0_1 | RXH_L4_B_2_3;
-                       else if (v & FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F)
-                               info->data = RXH_IP_SRC | RXH_IP_DST;
-                       break;
-               case UDP_V4_FLOW:
-                       if ((v & FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN_F) &&
-                           (v & FW_RSS_VI_CONFIG_CMD_UDPEN_F))
-                               info->data = RXH_IP_SRC | RXH_IP_DST |
-                                            RXH_L4_B_0_1 | RXH_L4_B_2_3;
-                       else if (v & FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F)
-                               info->data = RXH_IP_SRC | RXH_IP_DST;
-                       break;
-               case SCTP_V4_FLOW:
-               case AH_ESP_V4_FLOW:
-               case IPV4_FLOW:
-                       if (v & FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F)
-                               info->data = RXH_IP_SRC | RXH_IP_DST;
-                       break;
-               case TCP_V6_FLOW:
-                       if (v & FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN_F)
-                               info->data = RXH_IP_SRC | RXH_IP_DST |
-                                            RXH_L4_B_0_1 | RXH_L4_B_2_3;
-                       else if (v & FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F)
-                               info->data = RXH_IP_SRC | RXH_IP_DST;
-                       break;
-               case UDP_V6_FLOW:
-                       if ((v & FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN_F) &&
-                           (v & FW_RSS_VI_CONFIG_CMD_UDPEN_F))
-                               info->data = RXH_IP_SRC | RXH_IP_DST |
-                                            RXH_L4_B_0_1 | RXH_L4_B_2_3;
-                       else if (v & FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F)
-                               info->data = RXH_IP_SRC | RXH_IP_DST;
-                       break;
-               case SCTP_V6_FLOW:
-               case AH_ESP_V6_FLOW:
-               case IPV6_FLOW:
-                       if (v & FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F)
-                               info->data = RXH_IP_SRC | RXH_IP_DST;
-                       break;
-               }
-               return 0;
-       }
-       case ETHTOOL_GRXRINGS:
-               info->data = pi->nqsets;
-               return 0;
-       }
-       return -EOPNOTSUPP;
-}
-
-static const struct ethtool_ops cxgb_ethtool_ops = {
-       .get_settings      = get_settings,
-       .set_settings      = set_settings,
-       .get_drvinfo       = get_drvinfo,
-       .get_msglevel      = get_msglevel,
-       .set_msglevel      = set_msglevel,
-       .get_ringparam     = get_sge_param,
-       .set_ringparam     = set_sge_param,
-       .get_coalesce      = get_coalesce,
-       .set_coalesce      = set_coalesce,
-       .get_eeprom_len    = get_eeprom_len,
-       .get_eeprom        = get_eeprom,
-       .set_eeprom        = set_eeprom,
-       .get_pauseparam    = get_pauseparam,
-       .set_pauseparam    = set_pauseparam,
-       .get_link          = ethtool_op_get_link,
-       .get_strings       = get_strings,
-       .set_phys_id       = identify_port,
-       .nway_reset        = restart_autoneg,
-       .get_sset_count    = get_sset_count,
-       .get_ethtool_stats = get_stats,
-       .get_regs_len      = get_regs_len,
-       .get_regs          = get_regs,
-       .get_wol           = get_wol,
-       .set_wol           = set_wol,
-       .get_rxnfc         = get_rxnfc,
-       .get_rxfh_indir_size = get_rss_table_size,
-       .get_rxfh          = get_rss_table,
-       .set_rxfh          = set_rss_table,
-       .flash_device      = set_flash,
-};
-
 static int setup_debugfs(struct adapter *adap)
 {
        if (IS_ERR_OR_NULL(adap->debugfs_root))
@@ -5689,7 +4093,7 @@ static inline void init_rspq(struct adapter *adap, struct sge_rspq *q,
                             unsigned int size, unsigned int iqe_size)
 {
        q->adap = adap;
-       set_rspq_intr_params(q, us, cnt);
+       cxgb4_set_rspq_intr_params(q, us, cnt);
        q->iqe_len = iqe_size;
        q->size = size;
 }
@@ -6184,7 +4588,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                netdev->dcbnl_ops = &cxgb4_dcb_ops;
                cxgb4_dcb_state_init(netdev);
 #endif
-               netdev->ethtool_ops = &cxgb_ethtool_ops;
+               cxgb4_set_ethtool_ops(netdev);
        }
 
        pci_set_drvdata(pdev, adapter);
index c438f3895c40b2b9c96f5152de4bd9fcc950a76f..e622214e2eca03266235de22adbd5412b0b7f4d3 100644 (file)
@@ -1968,7 +1968,7 @@ static int process_responses(struct sge_rspq *q, int budget)
                if (!is_new_response(rc, q))
                        break;
 
-               rmb();
+               dma_rmb();
                rsp_type = RSPD_TYPE(rc->type_gen);
                if (likely(rsp_type == RSP_TYPE_FLBUF)) {
                        struct page_frag *fp;
@@ -2160,7 +2160,7 @@ static unsigned int process_intrq(struct adapter *adap)
                if (!is_new_response(rc, q))
                        break;
 
-               rmb();
+               dma_rmb();
                if (RSPD_TYPE(rc->type_gen) == RSP_TYPE_INTR) {
                        unsigned int qid = ntohl(rc->pldbuflen_qid);
 
index 5ed8db977432710198c2bc3c01d94d23ff868095..5959e3ae72da213e11587e8cd27a2bc9759755d0 100644 (file)
@@ -625,6 +625,734 @@ int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr,
        return 0;
 }
 
+/**
+ *     t4_get_regs_len - return the size of the chips register set
+ *     @adapter: the adapter
+ *
+ *     Returns the size of the chip's BAR0 register space.
+ */
+unsigned int t4_get_regs_len(struct adapter *adapter)
+{
+       unsigned int chip_version = CHELSIO_CHIP_VERSION(adapter->params.chip);
+
+       switch (chip_version) {
+       case CHELSIO_T4:
+               return T4_REGMAP_SIZE;
+
+       case CHELSIO_T5:
+               return T5_REGMAP_SIZE;
+       }
+
+       dev_err(adapter->pdev_dev,
+               "Unsupported chip version %d\n", chip_version);
+       return 0;
+}
+
+/**
+ *     t4_get_regs - read chip registers into provided buffer
+ *     @adap: the adapter
+ *     @buf: register buffer
+ *     @buf_size: size (in bytes) of register buffer
+ *
+ *     If the provided register buffer isn't large enough for the chip's
+ *     full register range, the register dump will be truncated to the
+ *     register buffer's size.
+ */
+void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
+{
+       static const unsigned int t4_reg_ranges[] = {
+               0x1008, 0x1108,
+               0x1180, 0x11b4,
+               0x11fc, 0x123c,
+               0x1300, 0x173c,
+               0x1800, 0x18fc,
+               0x3000, 0x30d8,
+               0x30e0, 0x5924,
+               0x5960, 0x59d4,
+               0x5a00, 0x5af8,
+               0x6000, 0x6098,
+               0x6100, 0x6150,
+               0x6200, 0x6208,
+               0x6240, 0x6248,
+               0x6280, 0x6338,
+               0x6370, 0x638c,
+               0x6400, 0x643c,
+               0x6500, 0x6524,
+               0x6a00, 0x6a38,
+               0x6a60, 0x6a78,
+               0x6b00, 0x6b84,
+               0x6bf0, 0x6c84,
+               0x6cf0, 0x6d84,
+               0x6df0, 0x6e84,
+               0x6ef0, 0x6f84,
+               0x6ff0, 0x7084,
+               0x70f0, 0x7184,
+               0x71f0, 0x7284,
+               0x72f0, 0x7384,
+               0x73f0, 0x7450,
+               0x7500, 0x7530,
+               0x7600, 0x761c,
+               0x7680, 0x76cc,
+               0x7700, 0x7798,
+               0x77c0, 0x77fc,
+               0x7900, 0x79fc,
+               0x7b00, 0x7c38,
+               0x7d00, 0x7efc,
+               0x8dc0, 0x8e1c,
+               0x8e30, 0x8e78,
+               0x8ea0, 0x8f6c,
+               0x8fc0, 0x9074,
+               0x90fc, 0x90fc,
+               0x9400, 0x9458,
+               0x9600, 0x96bc,
+               0x9800, 0x9808,
+               0x9820, 0x983c,
+               0x9850, 0x9864,
+               0x9c00, 0x9c6c,
+               0x9c80, 0x9cec,
+               0x9d00, 0x9d6c,
+               0x9d80, 0x9dec,
+               0x9e00, 0x9e6c,
+               0x9e80, 0x9eec,
+               0x9f00, 0x9f6c,
+               0x9f80, 0x9fec,
+               0xd004, 0xd03c,
+               0xdfc0, 0xdfe0,
+               0xe000, 0xea7c,
+               0xf000, 0x11110,
+               0x11118, 0x11190,
+               0x19040, 0x1906c,
+               0x19078, 0x19080,
+               0x1908c, 0x19124,
+               0x19150, 0x191b0,
+               0x191d0, 0x191e8,
+               0x19238, 0x1924c,
+               0x193f8, 0x19474,
+               0x19490, 0x194f8,
+               0x19800, 0x19f30,
+               0x1a000, 0x1a06c,
+               0x1a0b0, 0x1a120,
+               0x1a128, 0x1a138,
+               0x1a190, 0x1a1c4,
+               0x1a1fc, 0x1a1fc,
+               0x1e040, 0x1e04c,
+               0x1e284, 0x1e28c,
+               0x1e2c0, 0x1e2c0,
+               0x1e2e0, 0x1e2e0,
+               0x1e300, 0x1e384,
+               0x1e3c0, 0x1e3c8,
+               0x1e440, 0x1e44c,
+               0x1e684, 0x1e68c,
+               0x1e6c0, 0x1e6c0,
+               0x1e6e0, 0x1e6e0,
+               0x1e700, 0x1e784,
+               0x1e7c0, 0x1e7c8,
+               0x1e840, 0x1e84c,
+               0x1ea84, 0x1ea8c,
+               0x1eac0, 0x1eac0,
+               0x1eae0, 0x1eae0,
+               0x1eb00, 0x1eb84,
+               0x1ebc0, 0x1ebc8,
+               0x1ec40, 0x1ec4c,
+               0x1ee84, 0x1ee8c,
+               0x1eec0, 0x1eec0,
+               0x1eee0, 0x1eee0,
+               0x1ef00, 0x1ef84,
+               0x1efc0, 0x1efc8,
+               0x1f040, 0x1f04c,
+               0x1f284, 0x1f28c,
+               0x1f2c0, 0x1f2c0,
+               0x1f2e0, 0x1f2e0,
+               0x1f300, 0x1f384,
+               0x1f3c0, 0x1f3c8,
+               0x1f440, 0x1f44c,
+               0x1f684, 0x1f68c,
+               0x1f6c0, 0x1f6c0,
+               0x1f6e0, 0x1f6e0,
+               0x1f700, 0x1f784,
+               0x1f7c0, 0x1f7c8,
+               0x1f840, 0x1f84c,
+               0x1fa84, 0x1fa8c,
+               0x1fac0, 0x1fac0,
+               0x1fae0, 0x1fae0,
+               0x1fb00, 0x1fb84,
+               0x1fbc0, 0x1fbc8,
+               0x1fc40, 0x1fc4c,
+               0x1fe84, 0x1fe8c,
+               0x1fec0, 0x1fec0,
+               0x1fee0, 0x1fee0,
+               0x1ff00, 0x1ff84,
+               0x1ffc0, 0x1ffc8,
+               0x20000, 0x2002c,
+               0x20100, 0x2013c,
+               0x20190, 0x201c8,
+               0x20200, 0x20318,
+               0x20400, 0x20528,
+               0x20540, 0x20614,
+               0x21000, 0x21040,
+               0x2104c, 0x21060,
+               0x210c0, 0x210ec,
+               0x21200, 0x21268,
+               0x21270, 0x21284,
+               0x212fc, 0x21388,
+               0x21400, 0x21404,
+               0x21500, 0x21518,
+               0x2152c, 0x2153c,
+               0x21550, 0x21554,
+               0x21600, 0x21600,
+               0x21608, 0x21628,
+               0x21630, 0x2163c,
+               0x21700, 0x2171c,
+               0x21780, 0x2178c,
+               0x21800, 0x21c38,
+               0x21c80, 0x21d7c,
+               0x21e00, 0x21e04,
+               0x22000, 0x2202c,
+               0x22100, 0x2213c,
+               0x22190, 0x221c8,
+               0x22200, 0x22318,
+               0x22400, 0x22528,
+               0x22540, 0x22614,
+               0x23000, 0x23040,
+               0x2304c, 0x23060,
+               0x230c0, 0x230ec,
+               0x23200, 0x23268,
+               0x23270, 0x23284,
+               0x232fc, 0x23388,
+               0x23400, 0x23404,
+               0x23500, 0x23518,
+               0x2352c, 0x2353c,
+               0x23550, 0x23554,
+               0x23600, 0x23600,
+               0x23608, 0x23628,
+               0x23630, 0x2363c,
+               0x23700, 0x2371c,
+               0x23780, 0x2378c,
+               0x23800, 0x23c38,
+               0x23c80, 0x23d7c,
+               0x23e00, 0x23e04,
+               0x24000, 0x2402c,
+               0x24100, 0x2413c,
+               0x24190, 0x241c8,
+               0x24200, 0x24318,
+               0x24400, 0x24528,
+               0x24540, 0x24614,
+               0x25000, 0x25040,
+               0x2504c, 0x25060,
+               0x250c0, 0x250ec,
+               0x25200, 0x25268,
+               0x25270, 0x25284,
+               0x252fc, 0x25388,
+               0x25400, 0x25404,
+               0x25500, 0x25518,
+               0x2552c, 0x2553c,
+               0x25550, 0x25554,
+               0x25600, 0x25600,
+               0x25608, 0x25628,
+               0x25630, 0x2563c,
+               0x25700, 0x2571c,
+               0x25780, 0x2578c,
+               0x25800, 0x25c38,
+               0x25c80, 0x25d7c,
+               0x25e00, 0x25e04,
+               0x26000, 0x2602c,
+               0x26100, 0x2613c,
+               0x26190, 0x261c8,
+               0x26200, 0x26318,
+               0x26400, 0x26528,
+               0x26540, 0x26614,
+               0x27000, 0x27040,
+               0x2704c, 0x27060,
+               0x270c0, 0x270ec,
+               0x27200, 0x27268,
+               0x27270, 0x27284,
+               0x272fc, 0x27388,
+               0x27400, 0x27404,
+               0x27500, 0x27518,
+               0x2752c, 0x2753c,
+               0x27550, 0x27554,
+               0x27600, 0x27600,
+               0x27608, 0x27628,
+               0x27630, 0x2763c,
+               0x27700, 0x2771c,
+               0x27780, 0x2778c,
+               0x27800, 0x27c38,
+               0x27c80, 0x27d7c,
+               0x27e00, 0x27e04
+       };
+
+       static const unsigned int t5_reg_ranges[] = {
+               0x1008, 0x1148,
+               0x1180, 0x11b4,
+               0x11fc, 0x123c,
+               0x1280, 0x173c,
+               0x1800, 0x18fc,
+               0x3000, 0x3028,
+               0x3060, 0x30d8,
+               0x30e0, 0x30fc,
+               0x3140, 0x357c,
+               0x35a8, 0x35cc,
+               0x35ec, 0x35ec,
+               0x3600, 0x5624,
+               0x56cc, 0x575c,
+               0x580c, 0x5814,
+               0x5890, 0x58bc,
+               0x5940, 0x59dc,
+               0x59fc, 0x5a18,
+               0x5a60, 0x5a9c,
+               0x5b9c, 0x5bfc,
+               0x6000, 0x6040,
+               0x6058, 0x614c,
+               0x7700, 0x7798,
+               0x77c0, 0x78fc,
+               0x7b00, 0x7c54,
+               0x7d00, 0x7efc,
+               0x8dc0, 0x8de0,
+               0x8df8, 0x8e84,
+               0x8ea0, 0x8f84,
+               0x8fc0, 0x90f8,
+               0x9400, 0x9470,
+               0x9600, 0x96f4,
+               0x9800, 0x9808,
+               0x9820, 0x983c,
+               0x9850, 0x9864,
+               0x9c00, 0x9c6c,
+               0x9c80, 0x9cec,
+               0x9d00, 0x9d6c,
+               0x9d80, 0x9dec,
+               0x9e00, 0x9e6c,
+               0x9e80, 0x9eec,
+               0x9f00, 0x9f6c,
+               0x9f80, 0xa020,
+               0xd004, 0xd03c,
+               0xdfc0, 0xdfe0,
+               0xe000, 0x11088,
+               0x1109c, 0x11110,
+               0x11118, 0x1117c,
+               0x11190, 0x11204,
+               0x19040, 0x1906c,
+               0x19078, 0x19080,
+               0x1908c, 0x19124,
+               0x19150, 0x191b0,
+               0x191d0, 0x191e8,
+               0x19238, 0x19290,
+               0x193f8, 0x19474,
+               0x19490, 0x194cc,
+               0x194f0, 0x194f8,
+               0x19c00, 0x19c60,
+               0x19c94, 0x19e10,
+               0x19e50, 0x19f34,
+               0x19f40, 0x19f50,
+               0x19f90, 0x19fe4,
+               0x1a000, 0x1a06c,
+               0x1a0b0, 0x1a120,
+               0x1a128, 0x1a138,
+               0x1a190, 0x1a1c4,
+               0x1a1fc, 0x1a1fc,
+               0x1e008, 0x1e00c,
+               0x1e040, 0x1e04c,
+               0x1e284, 0x1e290,
+               0x1e2c0, 0x1e2c0,
+               0x1e2e0, 0x1e2e0,
+               0x1e300, 0x1e384,
+               0x1e3c0, 0x1e3c8,
+               0x1e408, 0x1e40c,
+               0x1e440, 0x1e44c,
+               0x1e684, 0x1e690,
+               0x1e6c0, 0x1e6c0,
+               0x1e6e0, 0x1e6e0,
+               0x1e700, 0x1e784,
+               0x1e7c0, 0x1e7c8,
+               0x1e808, 0x1e80c,
+               0x1e840, 0x1e84c,
+               0x1ea84, 0x1ea90,
+               0x1eac0, 0x1eac0,
+               0x1eae0, 0x1eae0,
+               0x1eb00, 0x1eb84,
+               0x1ebc0, 0x1ebc8,
+               0x1ec08, 0x1ec0c,
+               0x1ec40, 0x1ec4c,
+               0x1ee84, 0x1ee90,
+               0x1eec0, 0x1eec0,
+               0x1eee0, 0x1eee0,
+               0x1ef00, 0x1ef84,
+               0x1efc0, 0x1efc8,
+               0x1f008, 0x1f00c,
+               0x1f040, 0x1f04c,
+               0x1f284, 0x1f290,
+               0x1f2c0, 0x1f2c0,
+               0x1f2e0, 0x1f2e0,
+               0x1f300, 0x1f384,
+               0x1f3c0, 0x1f3c8,
+               0x1f408, 0x1f40c,
+               0x1f440, 0x1f44c,
+               0x1f684, 0x1f690,
+               0x1f6c0, 0x1f6c0,
+               0x1f6e0, 0x1f6e0,
+               0x1f700, 0x1f784,
+               0x1f7c0, 0x1f7c8,
+               0x1f808, 0x1f80c,
+               0x1f840, 0x1f84c,
+               0x1fa84, 0x1fa90,
+               0x1fac0, 0x1fac0,
+               0x1fae0, 0x1fae0,
+               0x1fb00, 0x1fb84,
+               0x1fbc0, 0x1fbc8,
+               0x1fc08, 0x1fc0c,
+               0x1fc40, 0x1fc4c,
+               0x1fe84, 0x1fe90,
+               0x1fec0, 0x1fec0,
+               0x1fee0, 0x1fee0,
+               0x1ff00, 0x1ff84,
+               0x1ffc0, 0x1ffc8,
+               0x30000, 0x30030,
+               0x30100, 0x30144,
+               0x30190, 0x301d0,
+               0x30200, 0x30318,
+               0x30400, 0x3052c,
+               0x30540, 0x3061c,
+               0x30800, 0x30834,
+               0x308c0, 0x30908,
+               0x30910, 0x309ac,
+               0x30a00, 0x30a04,
+               0x30a0c, 0x30a2c,
+               0x30a44, 0x30a50,
+               0x30a74, 0x30c24,
+               0x30d08, 0x30d14,
+               0x30d1c, 0x30d20,
+               0x30d3c, 0x30d50,
+               0x31200, 0x3120c,
+               0x31220, 0x31220,
+               0x31240, 0x31240,
+               0x31600, 0x31600,
+               0x31608, 0x3160c,
+               0x31a00, 0x31a1c,
+               0x31e04, 0x31e20,
+               0x31e38, 0x31e3c,
+               0x31e80, 0x31e80,
+               0x31e88, 0x31ea8,
+               0x31eb0, 0x31eb4,
+               0x31ec8, 0x31ed4,
+               0x31fb8, 0x32004,
+               0x32208, 0x3223c,
+               0x32600, 0x32630,
+               0x32a00, 0x32abc,
+               0x32b00, 0x32b70,
+               0x33000, 0x33048,
+               0x33060, 0x3309c,
+               0x330f0, 0x33148,
+               0x33160, 0x3319c,
+               0x331f0, 0x332e4,
+               0x332f8, 0x333e4,
+               0x333f8, 0x33448,
+               0x33460, 0x3349c,
+               0x334f0, 0x33548,
+               0x33560, 0x3359c,
+               0x335f0, 0x336e4,
+               0x336f8, 0x337e4,
+               0x337f8, 0x337fc,
+               0x33814, 0x33814,
+               0x3382c, 0x3382c,
+               0x33880, 0x3388c,
+               0x338e8, 0x338ec,
+               0x33900, 0x33948,
+               0x33960, 0x3399c,
+               0x339f0, 0x33ae4,
+               0x33af8, 0x33b10,
+               0x33b28, 0x33b28,
+               0x33b3c, 0x33b50,
+               0x33bf0, 0x33c10,
+               0x33c28, 0x33c28,
+               0x33c3c, 0x33c50,
+               0x33cf0, 0x33cfc,
+               0x34000, 0x34030,
+               0x34100, 0x34144,
+               0x34190, 0x341d0,
+               0x34200, 0x34318,
+               0x34400, 0x3452c,
+               0x34540, 0x3461c,
+               0x34800, 0x34834,
+               0x348c0, 0x34908,
+               0x34910, 0x349ac,
+               0x34a00, 0x34a04,
+               0x34a0c, 0x34a2c,
+               0x34a44, 0x34a50,
+               0x34a74, 0x34c24,
+               0x34d08, 0x34d14,
+               0x34d1c, 0x34d20,
+               0x34d3c, 0x34d50,
+               0x35200, 0x3520c,
+               0x35220, 0x35220,
+               0x35240, 0x35240,
+               0x35600, 0x35600,
+               0x35608, 0x3560c,
+               0x35a00, 0x35a1c,
+               0x35e04, 0x35e20,
+               0x35e38, 0x35e3c,
+               0x35e80, 0x35e80,
+               0x35e88, 0x35ea8,
+               0x35eb0, 0x35eb4,
+               0x35ec8, 0x35ed4,
+               0x35fb8, 0x36004,
+               0x36208, 0x3623c,
+               0x36600, 0x36630,
+               0x36a00, 0x36abc,
+               0x36b00, 0x36b70,
+               0x37000, 0x37048,
+               0x37060, 0x3709c,
+               0x370f0, 0x37148,
+               0x37160, 0x3719c,
+               0x371f0, 0x372e4,
+               0x372f8, 0x373e4,
+               0x373f8, 0x37448,
+               0x37460, 0x3749c,
+               0x374f0, 0x37548,
+               0x37560, 0x3759c,
+               0x375f0, 0x376e4,
+               0x376f8, 0x377e4,
+               0x377f8, 0x377fc,
+               0x37814, 0x37814,
+               0x3782c, 0x3782c,
+               0x37880, 0x3788c,
+               0x378e8, 0x378ec,
+               0x37900, 0x37948,
+               0x37960, 0x3799c,
+               0x379f0, 0x37ae4,
+               0x37af8, 0x37b10,
+               0x37b28, 0x37b28,
+               0x37b3c, 0x37b50,
+               0x37bf0, 0x37c10,
+               0x37c28, 0x37c28,
+               0x37c3c, 0x37c50,
+               0x37cf0, 0x37cfc,
+               0x38000, 0x38030,
+               0x38100, 0x38144,
+               0x38190, 0x381d0,
+               0x38200, 0x38318,
+               0x38400, 0x3852c,
+               0x38540, 0x3861c,
+               0x38800, 0x38834,
+               0x388c0, 0x38908,
+               0x38910, 0x389ac,
+               0x38a00, 0x38a04,
+               0x38a0c, 0x38a2c,
+               0x38a44, 0x38a50,
+               0x38a74, 0x38c24,
+               0x38d08, 0x38d14,
+               0x38d1c, 0x38d20,
+               0x38d3c, 0x38d50,
+               0x39200, 0x3920c,
+               0x39220, 0x39220,
+               0x39240, 0x39240,
+               0x39600, 0x39600,
+               0x39608, 0x3960c,
+               0x39a00, 0x39a1c,
+               0x39e04, 0x39e20,
+               0x39e38, 0x39e3c,
+               0x39e80, 0x39e80,
+               0x39e88, 0x39ea8,
+               0x39eb0, 0x39eb4,
+               0x39ec8, 0x39ed4,
+               0x39fb8, 0x3a004,
+               0x3a208, 0x3a23c,
+               0x3a600, 0x3a630,
+               0x3aa00, 0x3aabc,
+               0x3ab00, 0x3ab70,
+               0x3b000, 0x3b048,
+               0x3b060, 0x3b09c,
+               0x3b0f0, 0x3b148,
+               0x3b160, 0x3b19c,
+               0x3b1f0, 0x3b2e4,
+               0x3b2f8, 0x3b3e4,
+               0x3b3f8, 0x3b448,
+               0x3b460, 0x3b49c,
+               0x3b4f0, 0x3b548,
+               0x3b560, 0x3b59c,
+               0x3b5f0, 0x3b6e4,
+               0x3b6f8, 0x3b7e4,
+               0x3b7f8, 0x3b7fc,
+               0x3b814, 0x3b814,
+               0x3b82c, 0x3b82c,
+               0x3b880, 0x3b88c,
+               0x3b8e8, 0x3b8ec,
+               0x3b900, 0x3b948,
+               0x3b960, 0x3b99c,
+               0x3b9f0, 0x3bae4,
+               0x3baf8, 0x3bb10,
+               0x3bb28, 0x3bb28,
+               0x3bb3c, 0x3bb50,
+               0x3bbf0, 0x3bc10,
+               0x3bc28, 0x3bc28,
+               0x3bc3c, 0x3bc50,
+               0x3bcf0, 0x3bcfc,
+               0x3c000, 0x3c030,
+               0x3c100, 0x3c144,
+               0x3c190, 0x3c1d0,
+               0x3c200, 0x3c318,
+               0x3c400, 0x3c52c,
+               0x3c540, 0x3c61c,
+               0x3c800, 0x3c834,
+               0x3c8c0, 0x3c908,
+               0x3c910, 0x3c9ac,
+               0x3ca00, 0x3ca04,
+               0x3ca0c, 0x3ca2c,
+               0x3ca44, 0x3ca50,
+               0x3ca74, 0x3cc24,
+               0x3cd08, 0x3cd14,
+               0x3cd1c, 0x3cd20,
+               0x3cd3c, 0x3cd50,
+               0x3d200, 0x3d20c,
+               0x3d220, 0x3d220,
+               0x3d240, 0x3d240,
+               0x3d600, 0x3d600,
+               0x3d608, 0x3d60c,
+               0x3da00, 0x3da1c,
+               0x3de04, 0x3de20,
+               0x3de38, 0x3de3c,
+               0x3de80, 0x3de80,
+               0x3de88, 0x3dea8,
+               0x3deb0, 0x3deb4,
+               0x3dec8, 0x3ded4,
+               0x3dfb8, 0x3e004,
+               0x3e208, 0x3e23c,
+               0x3e600, 0x3e630,
+               0x3ea00, 0x3eabc,
+               0x3eb00, 0x3eb70,
+               0x3f000, 0x3f048,
+               0x3f060, 0x3f09c,
+               0x3f0f0, 0x3f148,
+               0x3f160, 0x3f19c,
+               0x3f1f0, 0x3f2e4,
+               0x3f2f8, 0x3f3e4,
+               0x3f3f8, 0x3f448,
+               0x3f460, 0x3f49c,
+               0x3f4f0, 0x3f548,
+               0x3f560, 0x3f59c,
+               0x3f5f0, 0x3f6e4,
+               0x3f6f8, 0x3f7e4,
+               0x3f7f8, 0x3f7fc,
+               0x3f814, 0x3f814,
+               0x3f82c, 0x3f82c,
+               0x3f880, 0x3f88c,
+               0x3f8e8, 0x3f8ec,
+               0x3f900, 0x3f948,
+               0x3f960, 0x3f99c,
+               0x3f9f0, 0x3fae4,
+               0x3faf8, 0x3fb10,
+               0x3fb28, 0x3fb28,
+               0x3fb3c, 0x3fb50,
+               0x3fbf0, 0x3fc10,
+               0x3fc28, 0x3fc28,
+               0x3fc3c, 0x3fc50,
+               0x3fcf0, 0x3fcfc,
+               0x40000, 0x4000c,
+               0x40040, 0x40068,
+               0x40080, 0x40144,
+               0x40180, 0x4018c,
+               0x40200, 0x40298,
+               0x402ac, 0x4033c,
+               0x403f8, 0x403fc,
+               0x41304, 0x413c4,
+               0x41400, 0x4141c,
+               0x41480, 0x414d0,
+               0x44000, 0x44078,
+               0x440c0, 0x44278,
+               0x442c0, 0x44478,
+               0x444c0, 0x44678,
+               0x446c0, 0x44878,
+               0x448c0, 0x449fc,
+               0x45000, 0x45068,
+               0x45080, 0x45084,
+               0x450a0, 0x450b0,
+               0x45200, 0x45268,
+               0x45280, 0x45284,
+               0x452a0, 0x452b0,
+               0x460c0, 0x460e4,
+               0x47000, 0x4708c,
+               0x47200, 0x47250,
+               0x47400, 0x47420,
+               0x47600, 0x47618,
+               0x47800, 0x47814,
+               0x48000, 0x4800c,
+               0x48040, 0x48068,
+               0x48080, 0x48144,
+               0x48180, 0x4818c,
+               0x48200, 0x48298,
+               0x482ac, 0x4833c,
+               0x483f8, 0x483fc,
+               0x49304, 0x493c4,
+               0x49400, 0x4941c,
+               0x49480, 0x494d0,
+               0x4c000, 0x4c078,
+               0x4c0c0, 0x4c278,
+               0x4c2c0, 0x4c478,
+               0x4c4c0, 0x4c678,
+               0x4c6c0, 0x4c878,
+               0x4c8c0, 0x4c9fc,
+               0x4d000, 0x4d068,
+               0x4d080, 0x4d084,
+               0x4d0a0, 0x4d0b0,
+               0x4d200, 0x4d268,
+               0x4d280, 0x4d284,
+               0x4d2a0, 0x4d2b0,
+               0x4e0c0, 0x4e0e4,
+               0x4f000, 0x4f08c,
+               0x4f200, 0x4f250,
+               0x4f400, 0x4f420,
+               0x4f600, 0x4f618,
+               0x4f800, 0x4f814,
+               0x50000, 0x500cc,
+               0x50400, 0x50400,
+               0x50800, 0x508cc,
+               0x50c00, 0x50c00,
+               0x51000, 0x5101c,
+               0x51300, 0x51308,
+       };
+
+       u32 *buf_end = (u32 *)((char *)buf + buf_size);
+       const unsigned int *reg_ranges;
+       int reg_ranges_size, range;
+       unsigned int chip_version = CHELSIO_CHIP_VERSION(adap->params.chip);
+
+       /* Select the right set of register ranges to dump depending on the
+        * adapter chip type.
+        */
+       switch (chip_version) {
+       case CHELSIO_T4:
+               reg_ranges = t4_reg_ranges;
+               reg_ranges_size = ARRAY_SIZE(t4_reg_ranges);
+               break;
+
+       case CHELSIO_T5:
+               reg_ranges = t5_reg_ranges;
+               reg_ranges_size = ARRAY_SIZE(t5_reg_ranges);
+               break;
+
+       default:
+               dev_err(adap->pdev_dev,
+                       "Unsupported chip version %d\n", chip_version);
+               return;
+       }
+
+       /* Clear the register buffer and insert the appropriate register
+        * values selected by the above register ranges.
+        */
+       memset(buf, 0, buf_size);
+       for (range = 0; range < reg_ranges_size; range += 2) {
+               unsigned int reg = reg_ranges[range];
+               unsigned int last_reg = reg_ranges[range + 1];
+               u32 *bufp = (u32 *)((char *)buf + reg);
+
+               /* Iterate across the register range filling in the register
+                * buffer but don't write past the end of the register buffer.
+                */
+               while (reg <= last_reg && bufp < buf_end) {
+                       *bufp++ = t4_read_reg(adap, reg);
+                       reg += sizeof(u32);
+               }
+       }
+}
+
 #define EEPROM_STAT_ADDR   0x7bfc
 #define VPD_BASE           0x400
 #define VPD_BASE_OLD       0
index 7715982230e511285eea81432086ef31330c9b81..482f6de6817d47e77e0b3ef648d0239554ef9561 100644 (file)
@@ -1751,7 +1751,7 @@ static int process_responses(struct sge_rspq *rspq, int budget)
                 * Figure out what kind of response we've received from the
                 * SGE.
                 */
-               rmb();
+               dma_rmb();
                rsp_type = RSPD_TYPE(rc->type_gen);
                if (likely(rsp_type == RSP_TYPE_FLBUF)) {
                        struct page_frag *fp;
@@ -1935,7 +1935,7 @@ static unsigned int process_intrq(struct adapter *adapter)
                 * error and go on to the next response message.  This should
                 * never happen ...
                 */
-               rmb();
+               dma_rmb();
                if (unlikely(RSPD_TYPE(rc->type_gen) != RSP_TYPE_INTR)) {
                        dev_err(adapter->pdev_dev,
                                "Unexpected INTRQ response type %d\n",
index 05f88394f9a5599dcc1a4076a05694e596e6fdbe..1a450f4b6b125d2234ea159022666b5ca381d328 100644 (file)
@@ -899,7 +899,7 @@ static int e100_exec_cb(struct nic *nic, struct sk_buff *skb,
        /* Order is important otherwise we'll be in a race with h/w:
         * set S-bit in current first, then clear S-bit in previous. */
        cb->command |= cpu_to_le16(cb_s);
-       wmb();
+       dma_wmb();
        cb->prev->command &= cpu_to_le16(~cb_s);
 
        while (nic->cb_to_send != nic->cb_to_use) {
@@ -1843,7 +1843,7 @@ static int e100_tx_clean(struct nic *nic)
        for (cb = nic->cb_to_clean;
            cb->status & cpu_to_le16(cb_complete);
            cb = nic->cb_to_clean = cb->next) {
-               rmb(); /* read skb after status */
+               dma_rmb(); /* read skb after status */
                netif_printk(nic, tx_done, KERN_DEBUG, nic->netdev,
                             "cb[%d]->status = 0x%04X\n",
                             (int)(((void*)cb - (void*)nic->cbs)/sizeof(struct cb)),
@@ -1993,7 +1993,7 @@ static int e100_rx_indicate(struct nic *nic, struct rx *rx,
 
        netif_printk(nic, rx_status, KERN_DEBUG, nic->netdev,
                     "status=0x%04X\n", rfd_status);
-       rmb(); /* read size after status bit */
+       dma_rmb(); /* read size after status bit */
 
        /* If data isn't ready, nothing to indicate */
        if (unlikely(!(rfd_status & cb_complete))) {
index b548ef0cf56be1278daa2d92edcde4fed51f9541..983eb4e6f7aa184deb48f526316c3254622c6756 100644 (file)
@@ -3856,7 +3856,7 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter,
        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 */
+               dma_rmb();      /* read buffer_info after eop_desc */
                for ( ; !cleaned; count++) {
                        tx_desc = E1000_TX_DESC(*tx_ring, i);
                        buffer_info = &tx_ring->buffer_info[i];
@@ -4154,7 +4154,7 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_adapter *adapter,
                if (*work_done >= work_to_do)
                        break;
                (*work_done)++;
-               rmb(); /* read descriptor and rx_buffer_info after status DD */
+               dma_rmb(); /* read descriptor and rx_buffer_info after status DD */
 
                status = rx_desc->status;
 
@@ -4375,7 +4375,7 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter,
                if (*work_done >= work_to_do)
                        break;
                (*work_done)++;
-               rmb(); /* read descriptor and rx_buffer_info after status DD */
+               dma_rmb(); /* read descriptor and rx_buffer_info after status DD */
 
                status = rx_desc->status;
                length = le16_to_cpu(rx_desc->length);
index a69f09e37b5893a02b2e0d521806769ea6f951c5..5d9ceb17b4cbad4f7e89cf0bb050e915f5b8d285 100644 (file)
@@ -343,6 +343,7 @@ struct e1000_adapter {
        struct timecounter tc;
        struct ptp_clock *ptp_clock;
        struct ptp_clock_info ptp_clock_info;
+       struct pm_qos_request pm_qos_req;
 
        u16 eee_advert;
 };
index 4e56c31959897402bf581131af693be5375311e1..c509a5c900f5253973b24c9f966cb95f2fe1a2bc 100644 (file)
@@ -947,7 +947,7 @@ static bool e1000_clean_rx_irq(struct e1000_ring *rx_ring, int *work_done,
                if (*work_done >= work_to_do)
                        break;
                (*work_done)++;
-               rmb();  /* read descriptor and rx_buffer_info after status DD */
+               dma_rmb();      /* read descriptor and rx_buffer_info after status DD */
 
                skb = buffer_info->skb;
                buffer_info->skb = NULL;
@@ -1232,7 +1232,7 @@ static bool e1000_clean_tx_irq(struct e1000_ring *tx_ring)
               (count < tx_ring->count)) {
                bool cleaned = false;
 
-               rmb();          /* read buffer_info after eop_desc */
+               dma_rmb();              /* read buffer_info after eop_desc */
                for (; !cleaned; count++) {
                        tx_desc = E1000_TX_DESC(*tx_ring, i);
                        buffer_info = &tx_ring->buffer_info[i];
@@ -1332,7 +1332,7 @@ static bool e1000_clean_rx_irq_ps(struct e1000_ring *rx_ring, int *work_done,
                        break;
                (*work_done)++;
                skb = buffer_info->skb;
-               rmb();  /* read descriptor and rx_buffer_info after status DD */
+               dma_rmb();      /* read descriptor and rx_buffer_info after status DD */
 
                /* in the packet split case this is header only */
                prefetch(skb->data - NET_IP_ALIGN);
@@ -1536,7 +1536,7 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_ring *rx_ring, int *work_done,
                if (*work_done >= work_to_do)
                        break;
                (*work_done)++;
-               rmb();  /* read descriptor and rx_buffer_info after status DD */
+               dma_rmb();      /* read descriptor and rx_buffer_info after status DD */
 
                skb = buffer_info->skb;
                buffer_info->skb = NULL;
@@ -3297,9 +3297,9 @@ static void e1000_configure_rx(struct e1000_adapter *adapter)
                        ew32(RXDCTL(0), rxdctl | 0x3);
                }
 
-               pm_qos_update_request(&adapter->netdev->pm_qos_req, lat);
+               pm_qos_update_request(&adapter->pm_qos_req, lat);
        } else {
-               pm_qos_update_request(&adapter->netdev->pm_qos_req,
+               pm_qos_update_request(&adapter->pm_qos_req,
                                      PM_QOS_DEFAULT_VALUE);
        }
 
@@ -4403,7 +4403,7 @@ static int e1000_open(struct net_device *netdev)
                e1000_update_mng_vlan(adapter);
 
        /* DMA latency requirement to workaround jumbo issue */
-       pm_qos_add_request(&adapter->netdev->pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
+       pm_qos_add_request(&adapter->pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
                           PM_QOS_DEFAULT_VALUE);
 
        /* before we allocate an interrupt, we must be ready to handle it.
@@ -4514,7 +4514,7 @@ static int e1000_close(struct net_device *netdev)
            !test_bit(__E1000_TESTING, &adapter->state))
                e1000e_release_hw_control(adapter);
 
-       pm_qos_remove_request(&adapter->netdev->pm_qos_req);
+       pm_qos_remove_request(&adapter->pm_qos_req);
 
        pm_runtime_put_sync(&pdev->dev);
 
index d8989f9d1798cfa7279c3216a08a4cdbfafd03d9..4bd3a80aba82998bba343a1870b2d21f59bca4e0 100644 (file)
@@ -1554,7 +1554,7 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget)
                 * any other fields out of the rx_desc until we know the
                 * DD bit is set.
                 */
-               rmb();
+               dma_rmb();
                if (i40e_rx_is_programming_status(qword)) {
                        i40e_clean_programming_status(rx_ring, rx_desc);
                        I40E_RX_INCREMENT(rx_ring, i);
@@ -1745,7 +1745,7 @@ static int i40e_clean_rx_irq_1buf(struct i40e_ring *rx_ring, int budget)
                 * any other fields out of the rx_desc until we know the
                 * DD bit is set.
                 */
-               rmb();
+               dma_rmb();
 
                if (i40e_rx_is_programming_status(qword)) {
                        i40e_clean_programming_status(rx_ring, rx_desc);
index e2ddb30e96f594a210470b56621cf2ac3f19a0d0..b077e02a0cc7ac8f67ad90560cf990f8f7a66277 100644 (file)
@@ -1034,7 +1034,7 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget)
                 * any other fields out of the rx_desc until we know the
                 * DD bit is set.
                 */
-               rmb();
+               dma_rmb();
                rx_bi = &rx_ring->rx_bi[i];
                skb = rx_bi->skb;
                if (likely(!skb)) {
@@ -1213,7 +1213,7 @@ static int i40e_clean_rx_irq_1buf(struct i40e_ring *rx_ring, int budget)
                 * any other fields out of the rx_desc until we know the
                 * DD bit is set.
                 */
-               rmb();
+               dma_rmb();
 
                rx_bi = &rx_ring->rx_bi[i];
                skb = rx_bi->skb;
index 7068e9c3691dd84af5faf6712eafae23b81b901b..636f9e350162bc58a3ea9fb54c0509934fad8985 100644 (file)
@@ -151,6 +151,7 @@ struct vf_data_storage {
        u16 tx_rate;
        u16 vlan_count;
        u8 spoofchk_enabled;
+       bool rss_query_enabled;
        unsigned int vf_api;
 };
 
@@ -642,7 +643,6 @@ struct ixgbe_adapter {
 #define IXGBE_FLAG2_RSS_FIELD_IPV4_UDP         (u32)(1 << 8)
 #define IXGBE_FLAG2_RSS_FIELD_IPV6_UDP         (u32)(1 << 9)
 #define IXGBE_FLAG2_PTP_PPS_ENABLED            (u32)(1 << 10)
-#define IXGBE_FLAG2_BRIDGE_MODE_VEB            (u32)(1 << 11)
 
        /* Tx fast path data */
        int num_tx_queues;
@@ -722,6 +722,8 @@ struct ixgbe_adapter {
        u8 __iomem *io_addr; /* Mainly for iounmap use */
        u32 wol;
 
+       u16 bridge_mode;
+
        u16 eeprom_verh;
        u16 eeprom_verl;
        u16 eeprom_cap;
@@ -765,6 +767,15 @@ struct ixgbe_adapter {
 
        u8 default_up;
        unsigned long fwd_bitmask; /* Bitmask indicating in use pools */
+
+/* maximum number of RETA entries among all devices supported by ixgbe
+ * driver: currently it's x550 device in non-SRIOV mode
+ */
+#define IXGBE_MAX_RETA_ENTRIES 512
+       u8 rss_indir_tbl[IXGBE_MAX_RETA_ENTRIES];
+
+#define IXGBE_RSS_KEY_SIZE     40  /* size of RSS Hash Key in bytes */
+       u32 rss_key[IXGBE_RSS_KEY_SIZE / sizeof(u32)];
 };
 
 static inline u8 ixgbe_max_rss_indices(struct ixgbe_adapter *adapter)
@@ -954,4 +965,5 @@ void ixgbe_sriov_reinit(struct ixgbe_adapter *adapter);
 netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb,
                                  struct ixgbe_adapter *adapter,
                                  struct ixgbe_ring *tx_ring);
+u32 ixgbe_rss_indir_tbl_entries(struct ixgbe_adapter *adapter);
 #endif /* _IXGBE_H_ */
index ccaecb1b86194823ae7f0b62212295adc7ce7cdb..eafa9ec802bab921fee1fc28b927d02e7d48bcae 100644 (file)
@@ -2853,6 +2853,45 @@ static int ixgbe_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
        return ret;
 }
 
+static u32 ixgbe_get_rxfh_key_size(struct net_device *netdev)
+{
+       struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+       return sizeof(adapter->rss_key);
+}
+
+static u32 ixgbe_rss_indir_size(struct net_device *netdev)
+{
+       struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+       return ixgbe_rss_indir_tbl_entries(adapter);
+}
+
+static void ixgbe_get_reta(struct ixgbe_adapter *adapter, u32 *indir)
+{
+       int i, reta_size = ixgbe_rss_indir_tbl_entries(adapter);
+
+       for (i = 0; i < reta_size; i++)
+               indir[i] = adapter->rss_indir_tbl[i];
+}
+
+static int ixgbe_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
+                         u8 *hfunc)
+{
+       struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+       if (hfunc)
+               *hfunc = ETH_RSS_HASH_TOP;
+
+       if (indir)
+               ixgbe_get_reta(adapter, indir);
+
+       if (key)
+               memcpy(key, adapter->rss_key, ixgbe_get_rxfh_key_size(netdev));
+
+       return 0;
+}
+
 static int ixgbe_get_ts_info(struct net_device *dev,
                             struct ethtool_ts_info *info)
 {
@@ -3110,6 +3149,9 @@ static const struct ethtool_ops ixgbe_ethtool_ops = {
        .set_coalesce           = ixgbe_set_coalesce,
        .get_rxnfc              = ixgbe_get_rxnfc,
        .set_rxnfc              = ixgbe_set_rxnfc,
+       .get_rxfh_indir_size    = ixgbe_rss_indir_size,
+       .get_rxfh_key_size      = ixgbe_get_rxfh_key_size,
+       .get_rxfh               = ixgbe_get_rxfh,
        .get_channels           = ixgbe_get_channels,
        .set_channels           = ixgbe_set_channels,
        .get_ts_info            = ixgbe_get_ts_info,
index 2ad91cb04dab9fc7f0cfbd9c328e4b269d6ac0f4..631c603fc96649c95995b286577ca7b489d3f58d 100644 (file)
@@ -71,6 +71,7 @@ int ixgbe_fcoe_ddp_put(struct net_device *netdev, u16 xid)
        struct ixgbe_fcoe *fcoe;
        struct ixgbe_adapter *adapter;
        struct ixgbe_fcoe_ddp *ddp;
+       struct ixgbe_hw *hw;
        u32 fcbuff;
 
        if (!netdev)
@@ -85,25 +86,51 @@ int ixgbe_fcoe_ddp_put(struct net_device *netdev, u16 xid)
        if (!ddp->udl)
                return 0;
 
+       hw = &adapter->hw;
        len = ddp->len;
-       /* if there an error, force to invalidate ddp context */
-       if (ddp->err) {
+       /* if no error then skip ddp context invalidation */
+       if (!ddp->err)
+               goto skip_ddpinv;
+
+       if (hw->mac.type == ixgbe_mac_X550) {
+               /* X550 does not require DDP FCoE lock */
+
+               IXGBE_WRITE_REG(hw, IXGBE_FCDFC(0, xid), 0);
+               IXGBE_WRITE_REG(hw, IXGBE_FCDFC(3, xid),
+                               (xid | IXGBE_FCFLTRW_WE));
+
+               /* program FCBUFF */
+               IXGBE_WRITE_REG(hw, IXGBE_FCDDC(2, xid), 0);
+
+               /* program FCDMARW */
+               IXGBE_WRITE_REG(hw, IXGBE_FCDDC(3, xid),
+                               (xid | IXGBE_FCDMARW_WE));
+
+               /* read FCBUFF to check context invalidated */
+               IXGBE_WRITE_REG(hw, IXGBE_FCDDC(3, xid),
+                               (xid | IXGBE_FCDMARW_RE));
+               fcbuff = IXGBE_READ_REG(hw, IXGBE_FCDDC(2, xid));
+       } else {
+               /* other hardware requires DDP FCoE lock */
                spin_lock_bh(&fcoe->lock);
-               IXGBE_WRITE_REG(&adapter->hw, IXGBE_FCFLT, 0);
-               IXGBE_WRITE_REG(&adapter->hw, IXGBE_FCFLTRW,
+               IXGBE_WRITE_REG(hw, IXGBE_FCFLT, 0);
+               IXGBE_WRITE_REG(hw, IXGBE_FCFLTRW,
                                (xid | IXGBE_FCFLTRW_WE));
-               IXGBE_WRITE_REG(&adapter->hw, IXGBE_FCBUFF, 0);
-               IXGBE_WRITE_REG(&adapter->hw, IXGBE_FCDMARW,
+               IXGBE_WRITE_REG(hw, IXGBE_FCBUFF, 0);
+               IXGBE_WRITE_REG(hw, IXGBE_FCDMARW,
                                (xid | IXGBE_FCDMARW_WE));
 
                /* guaranteed to be invalidated after 100us */
-               IXGBE_WRITE_REG(&adapter->hw, IXGBE_FCDMARW,
+               IXGBE_WRITE_REG(hw, IXGBE_FCDMARW,
                                (xid | IXGBE_FCDMARW_RE));
-               fcbuff = IXGBE_READ_REG(&adapter->hw, IXGBE_FCBUFF);
+               fcbuff = IXGBE_READ_REG(hw, IXGBE_FCBUFF);
                spin_unlock_bh(&fcoe->lock);
-               if (fcbuff & IXGBE_FCBUFF_VALID)
-                       udelay(100);
-       }
+               }
+
+       if (fcbuff & IXGBE_FCBUFF_VALID)
+               usleep_range(100, 150);
+
+skip_ddpinv:
        if (ddp->sgl)
                dma_unmap_sg(&adapter->pdev->dev, ddp->sgl, ddp->sgc,
                             DMA_FROM_DEVICE);
@@ -272,7 +299,6 @@ static int ixgbe_fcoe_ddp_setup(struct net_device *netdev, u16 xid,
 
        /* program DMA context */
        hw = &adapter->hw;
-       spin_lock_bh(&fcoe->lock);
 
        /* turn on last frame indication for target mode as FCP_RSPtarget is
         * supposed to send FCP_RSP when it is done. */
@@ -283,16 +309,33 @@ static int ixgbe_fcoe_ddp_setup(struct net_device *netdev, u16 xid,
                IXGBE_WRITE_REG(hw, IXGBE_FCRXCTRL, fcrxctl);
        }
 
-       IXGBE_WRITE_REG(hw, IXGBE_FCPTRL, ddp->udp & DMA_BIT_MASK(32));
-       IXGBE_WRITE_REG(hw, IXGBE_FCPTRH, (u64)ddp->udp >> 32);
-       IXGBE_WRITE_REG(hw, IXGBE_FCBUFF, fcbuff);
-       IXGBE_WRITE_REG(hw, IXGBE_FCDMARW, fcdmarw);
-       /* program filter context */
-       IXGBE_WRITE_REG(hw, IXGBE_FCPARAM, 0);
-       IXGBE_WRITE_REG(hw, IXGBE_FCFLT, IXGBE_FCFLT_VALID);
-       IXGBE_WRITE_REG(hw, IXGBE_FCFLTRW, fcfltrw);
+       if (hw->mac.type == ixgbe_mac_X550) {
+               /* X550 does not require DDP lock */
+
+               IXGBE_WRITE_REG(hw, IXGBE_FCDDC(0, xid),
+                               ddp->udp & DMA_BIT_MASK(32));
+               IXGBE_WRITE_REG(hw, IXGBE_FCDDC(1, xid), (u64)ddp->udp >> 32);
+               IXGBE_WRITE_REG(hw, IXGBE_FCDDC(2, xid), fcbuff);
+               IXGBE_WRITE_REG(hw, IXGBE_FCDDC(3, xid), fcdmarw);
+               /* program filter context */
+               IXGBE_WRITE_REG(hw, IXGBE_FCDFC(0, xid), IXGBE_FCFLT_VALID);
+               IXGBE_WRITE_REG(hw, IXGBE_FCDFC(1, xid), 0);
+               IXGBE_WRITE_REG(hw, IXGBE_FCDFC(3, xid), fcfltrw);
+       } else {
+               /* DDP lock for indirect DDP context access */
+               spin_lock_bh(&fcoe->lock);
+
+               IXGBE_WRITE_REG(hw, IXGBE_FCPTRL, ddp->udp & DMA_BIT_MASK(32));
+               IXGBE_WRITE_REG(hw, IXGBE_FCPTRH, (u64)ddp->udp >> 32);
+               IXGBE_WRITE_REG(hw, IXGBE_FCBUFF, fcbuff);
+               IXGBE_WRITE_REG(hw, IXGBE_FCDMARW, fcdmarw);
+               /* program filter context */
+               IXGBE_WRITE_REG(hw, IXGBE_FCPARAM, 0);
+               IXGBE_WRITE_REG(hw, IXGBE_FCFLT, IXGBE_FCFLT_VALID);
+               IXGBE_WRITE_REG(hw, IXGBE_FCFLTRW, fcfltrw);
 
-       spin_unlock_bh(&fcoe->lock);
+               spin_unlock_bh(&fcoe->lock);
+       }
 
        return 1;
 
@@ -371,6 +414,7 @@ int ixgbe_fcoe_ddp(struct ixgbe_adapter *adapter,
        struct fcoe_crc_eof *crc;
        __le32 fcerr = ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_ERR_FCERR);
        __le32 ddp_err;
+       int ddp_max;
        u32 fctl;
        u16 xid;
 
@@ -392,7 +436,11 @@ int ixgbe_fcoe_ddp(struct ixgbe_adapter *adapter,
        else
                xid =  be16_to_cpu(fh->fh_rx_id);
 
-       if (xid >= IXGBE_FCOE_DDP_MAX)
+       ddp_max = IXGBE_FCOE_DDP_MAX;
+       /* X550 has different DDP Max limit */
+       if (adapter->hw.mac.type == ixgbe_mac_X550)
+               ddp_max = IXGBE_FCOE_DDP_MAX_X550;
+       if (xid >= ddp_max)
                return -EINVAL;
 
        fcoe = &adapter->fcoe;
@@ -612,7 +660,8 @@ void ixgbe_configure_fcoe(struct ixgbe_adapter *adapter)
 {
        struct ixgbe_ring_feature *fcoe = &adapter->ring_feature[RING_F_FCOE];
        struct ixgbe_hw *hw = &adapter->hw;
-       int i, fcoe_q, fcoe_i;
+       int i, fcoe_q, fcoe_i, fcoe_q_h = 0;
+       int fcreta_size;
        u32 etqf;
 
        /* Minimal functionality for FCoE requires at least CRC offloads */
@@ -633,10 +682,23 @@ void ixgbe_configure_fcoe(struct ixgbe_adapter *adapter)
                return;
 
        /* Use one or more Rx queues for FCoE by redirection table */
-       for (i = 0; i < IXGBE_FCRETA_SIZE; i++) {
+       fcreta_size = IXGBE_FCRETA_SIZE;
+       if (adapter->hw.mac.type == ixgbe_mac_X550)
+               fcreta_size = IXGBE_FCRETA_SIZE_X550;
+
+       for (i = 0; i < fcreta_size; i++) {
+               if (adapter->hw.mac.type == ixgbe_mac_X550) {
+                       int fcoe_i_h = fcoe->offset + ((i + fcreta_size) %
+                                                       fcoe->indices);
+                       fcoe_q_h = adapter->rx_ring[fcoe_i_h]->reg_idx;
+                       fcoe_q_h = (fcoe_q_h << IXGBE_FCRETA_ENTRY_HIGH_SHIFT) &
+                                  IXGBE_FCRETA_ENTRY_HIGH_MASK;
+               }
+
                fcoe_i = fcoe->offset + (i % fcoe->indices);
                fcoe_i &= IXGBE_FCRETA_ENTRY_MASK;
                fcoe_q = adapter->rx_ring[fcoe_i]->reg_idx;
+               fcoe_q |= fcoe_q_h;
                IXGBE_WRITE_REG(hw, IXGBE_FCRETA(i), fcoe_q);
        }
        IXGBE_WRITE_REG(hw, IXGBE_FCRECTL, IXGBE_FCRECTL_ENA);
@@ -672,13 +734,18 @@ void ixgbe_configure_fcoe(struct ixgbe_adapter *adapter)
 void ixgbe_free_fcoe_ddp_resources(struct ixgbe_adapter *adapter)
 {
        struct ixgbe_fcoe *fcoe = &adapter->fcoe;
-       int cpu, i;
+       int cpu, i, ddp_max;
 
        /* do nothing if no DDP pools were allocated */
        if (!fcoe->ddp_pool)
                return;
 
-       for (i = 0; i < IXGBE_FCOE_DDP_MAX; i++)
+       ddp_max = IXGBE_FCOE_DDP_MAX;
+       /* X550 has different DDP Max limit */
+       if (adapter->hw.mac.type == ixgbe_mac_X550)
+               ddp_max = IXGBE_FCOE_DDP_MAX_X550;
+
+       for (i = 0; i < ddp_max; i++)
                ixgbe_fcoe_ddp_put(adapter->netdev, i);
 
        for_each_possible_cpu(cpu)
@@ -758,6 +825,9 @@ static int ixgbe_fcoe_ddp_enable(struct ixgbe_adapter *adapter)
        }
 
        adapter->netdev->fcoe_ddp_xid = IXGBE_FCOE_DDP_MAX - 1;
+       /* X550 has different DDP Max limit */
+       if (adapter->hw.mac.type == ixgbe_mac_X550)
+               adapter->netdev->fcoe_ddp_xid = IXGBE_FCOE_DDP_MAX_X550 - 1;
 
        return 0;
 }
index 0772b7730fce92de4e2ff54d44f6528397c2b3a9..38385876effb0c9a3d8659afebc5f469575f55f4 100644 (file)
@@ -46,6 +46,7 @@
 #define IXGBE_FCBUFF_MAX       65536   /* 64KB max */
 #define IXGBE_FCBUFF_MIN       4096    /* 4KB min */
 #define IXGBE_FCOE_DDP_MAX     512     /* 9 bits xid */
+#define IXGBE_FCOE_DDP_MAX_X550        2048    /* 11 bits xid */
 
 /* Default traffic class to use for FCoE */
 #define IXGBE_FCOE_DEFTC       3
@@ -77,7 +78,7 @@ struct ixgbe_fcoe {
        struct ixgbe_fcoe_ddp_pool __percpu *ddp_pool;
        atomic_t refcnt;
        spinlock_t lock;
-       struct ixgbe_fcoe_ddp ddp[IXGBE_FCOE_DDP_MAX];
+       struct ixgbe_fcoe_ddp ddp[IXGBE_FCOE_DDP_MAX_X550];
        void *extra_ddp_buffer;
        dma_addr_t extra_ddp_buffer_dma;
        unsigned long mode;
index 395dc6bb5d82139b6e4a502517f171256d06eac9..d3f4b0ceb3f781216599408248b351cd4854bc92 100644 (file)
@@ -3228,89 +3228,148 @@ static void ixgbe_configure_srrctl(struct ixgbe_adapter *adapter,
        IXGBE_WRITE_REG(hw, IXGBE_SRRCTL(reg_idx), srrctl);
 }
 
-static void ixgbe_setup_reta(struct ixgbe_adapter *adapter, const u32 *seed)
+/**
+ * Return a number of entries in the RSS indirection table
+ *
+ * @adapter: device handle
+ *
+ *  - 82598/82599/X540:     128
+ *  - X550(non-SRIOV mode): 512
+ *  - X550(SRIOV mode):     64
+ */
+u32 ixgbe_rss_indir_tbl_entries(struct ixgbe_adapter *adapter)
+{
+       if (adapter->hw.mac.type < ixgbe_mac_X550)
+               return 128;
+       else if (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED)
+               return 64;
+       else
+               return 512;
+}
+
+/**
+ * Write the RETA table to HW
+ *
+ * @adapter: device handle
+ *
+ * Write the RSS redirection table stored in adapter.rss_indir_tbl[] to HW.
+ */
+static void ixgbe_store_reta(struct ixgbe_adapter *adapter)
 {
+       u32 i, reta_entries = ixgbe_rss_indir_tbl_entries(adapter);
        struct ixgbe_hw *hw = &adapter->hw;
        u32 reta = 0;
-       int i, j;
-       int reta_entries = 128;
-       u16 rss_i = adapter->ring_feature[RING_F_RSS].indices;
-       int indices_multi;
-
-       /*
-        * Program table for at least 2 queues w/ SR-IOV so that VFs can
-        * make full use of any rings they may have.  We will use the
-        * PSRTYPE register to control how many rings we use within the PF.
-        */
-       if ((adapter->flags & IXGBE_FLAG_SRIOV_ENABLED) && (rss_i < 2))
-               rss_i = 2;
-
-       /* Fill out hash function seeds */
-       for (i = 0; i < 10; i++)
-               IXGBE_WRITE_REG(hw, IXGBE_RSSRK(i), seed[i]);
+       u32 indices_multi;
+       u8 *indir_tbl = adapter->rss_indir_tbl;
 
        /* Fill out the redirection table as follows:
-        * 82598: 128 (8 bit wide) entries containing pair of 4 bit RSS indices
-        * 82599/X540: 128 (8 bit wide) entries containing 4 bit RSS index
-        * X550: 512 (8 bit wide) entries containing 6 bit RSS index
+        *  - 82598:      8 bit wide entries containing pair of 4 bit RSS
+        *    indices.
+        *  - 82599/X540: 8 bit wide entries containing 4 bit RSS index
+        *  - X550:       8 bit wide entries containing 6 bit RSS index
         */
        if (adapter->hw.mac.type == ixgbe_mac_82598EB)
                indices_multi = 0x11;
        else
                indices_multi = 0x1;
 
-       switch (adapter->hw.mac.type) {
-       case ixgbe_mac_X550:
-       case ixgbe_mac_X550EM_x:
-               if (!(adapter->flags & IXGBE_FLAG_SRIOV_ENABLED))
-                       reta_entries = 512;
-       default:
-               break;
-       }
-
-       /* Fill out redirection table */
-       for (i = 0, j = 0; i < reta_entries; i++, j++) {
-               if (j == rss_i)
-                       j = 0;
-               reta = (reta << 8) | (j * indices_multi);
+       /* Write redirection table to HW */
+       for (i = 0; i < reta_entries; i++) {
+               reta |= indices_multi * indir_tbl[i] << (i & 0x3) * 8;
                if ((i & 3) == 3) {
                        if (i < 128)
                                IXGBE_WRITE_REG(hw, IXGBE_RETA(i >> 2), reta);
                        else
                                IXGBE_WRITE_REG(hw, IXGBE_ERETA((i >> 2) - 32),
                                                reta);
+                       reta = 0;
                }
        }
 }
 
-static void ixgbe_setup_vfreta(struct ixgbe_adapter *adapter, const u32 *seed)
+/**
+ * Write the RETA table to HW (for x550 devices in SRIOV mode)
+ *
+ * @adapter: device handle
+ *
+ * Write the RSS redirection table stored in adapter.rss_indir_tbl[] to HW.
+ */
+static void ixgbe_store_vfreta(struct ixgbe_adapter *adapter)
 {
+       u32 i, reta_entries = ixgbe_rss_indir_tbl_entries(adapter);
        struct ixgbe_hw *hw = &adapter->hw;
        u32 vfreta = 0;
+       unsigned int pf_pool = adapter->num_vfs;
+
+       /* Write redirection table to HW */
+       for (i = 0; i < reta_entries; i++) {
+               vfreta |= (u32)adapter->rss_indir_tbl[i] << (i & 0x3) * 8;
+               if ((i & 3) == 3) {
+                       IXGBE_WRITE_REG(hw, IXGBE_PFVFRETA(i >> 2, pf_pool),
+                                       vfreta);
+                       vfreta = 0;
+               }
+       }
+}
+
+static void ixgbe_setup_reta(struct ixgbe_adapter *adapter)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+       u32 i, j;
+       u32 reta_entries = ixgbe_rss_indir_tbl_entries(adapter);
+       u16 rss_i = adapter->ring_feature[RING_F_RSS].indices;
+
+       /* Program table for at least 2 queues w/ SR-IOV so that VFs can
+        * make full use of any rings they may have.  We will use the
+        * PSRTYPE register to control how many rings we use within the PF.
+        */
+       if ((adapter->flags & IXGBE_FLAG_SRIOV_ENABLED) && (rss_i < 2))
+               rss_i = 2;
+
+       /* Fill out hash function seeds */
+       for (i = 0; i < 10; i++)
+               IXGBE_WRITE_REG(hw, IXGBE_RSSRK(i), adapter->rss_key[i]);
+
+       /* Fill out redirection table */
+       memset(adapter->rss_indir_tbl, 0, sizeof(adapter->rss_indir_tbl));
+
+       for (i = 0, j = 0; i < reta_entries; i++, j++) {
+               if (j == rss_i)
+                       j = 0;
+
+               adapter->rss_indir_tbl[i] = j;
+       }
+
+       ixgbe_store_reta(adapter);
+}
+
+static void ixgbe_setup_vfreta(struct ixgbe_adapter *adapter)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
        u16 rss_i = adapter->ring_feature[RING_F_RSS].indices;
        unsigned int pf_pool = adapter->num_vfs;
        int i, j;
 
        /* Fill out hash function seeds */
        for (i = 0; i < 10; i++)
-               IXGBE_WRITE_REG(hw, IXGBE_PFVFRSSRK(i, pf_pool), seed[i]);
+               IXGBE_WRITE_REG(hw, IXGBE_PFVFRSSRK(i, pf_pool),
+                               adapter->rss_key[i]);
 
        /* Fill out the redirection table */
        for (i = 0, j = 0; i < 64; i++, j++) {
                if (j == rss_i)
                        j = 0;
-               vfreta = (vfreta << 8) | j;
-               if ((i & 3) == 3)
-                       IXGBE_WRITE_REG(hw, IXGBE_PFVFRETA(i >> 2, pf_pool),
-                                       vfreta);
+
+               adapter->rss_indir_tbl[i] = j;
        }
+
+       ixgbe_store_vfreta(adapter);
 }
 
 static void ixgbe_setup_mrqc(struct ixgbe_adapter *adapter)
 {
        struct ixgbe_hw *hw = &adapter->hw;
        u32 mrqc = 0, rss_field = 0, vfmrqc = 0;
-       u32 rss_key[10];
        u32 rxcsum;
 
        /* Disable indicating checksum in descriptor, enables RSS hash */
@@ -3354,7 +3413,7 @@ static void ixgbe_setup_mrqc(struct ixgbe_adapter *adapter)
        if (adapter->flags2 & IXGBE_FLAG2_RSS_FIELD_IPV6_UDP)
                rss_field |= IXGBE_MRQC_RSS_FIELD_IPV6_UDP;
 
-       netdev_rss_key_fill(rss_key, sizeof(rss_key));
+       netdev_rss_key_fill(adapter->rss_key, sizeof(adapter->rss_key));
        if ((hw->mac.type >= ixgbe_mac_X550) &&
            (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED)) {
                unsigned int pf_pool = adapter->num_vfs;
@@ -3364,12 +3423,12 @@ static void ixgbe_setup_mrqc(struct ixgbe_adapter *adapter)
                IXGBE_WRITE_REG(hw, IXGBE_MRQC, mrqc);
 
                /* Setup RSS through the VF registers */
-               ixgbe_setup_vfreta(adapter, rss_key);
+               ixgbe_setup_vfreta(adapter);
                vfmrqc = IXGBE_MRQC_RSSEN;
                vfmrqc |= rss_field;
                IXGBE_WRITE_REG(hw, IXGBE_PFVFMRQC(pf_pool), vfmrqc);
        } else {
-               ixgbe_setup_reta(adapter, rss_key);
+               ixgbe_setup_reta(adapter);
                mrqc |= rss_field;
                IXGBE_WRITE_REG(hw, IXGBE_MRQC, mrqc);
        }
@@ -3553,7 +3612,7 @@ static void ixgbe_configure_virtualization(struct ixgbe_adapter *adapter)
        IXGBE_WRITE_REG(hw, IXGBE_VFRE(reg_offset ^ 1), reg_offset - 1);
        IXGBE_WRITE_REG(hw, IXGBE_VFTE(reg_offset), (~0) << vf_shift);
        IXGBE_WRITE_REG(hw, IXGBE_VFTE(reg_offset ^ 1), reg_offset - 1);
-       if (adapter->flags2 & IXGBE_FLAG2_BRIDGE_MODE_VEB)
+       if (adapter->bridge_mode == BRIDGE_MODE_VEB)
                IXGBE_WRITE_REG(hw, IXGBE_PFDTXGSWC, IXGBE_PFDTXGSWC_VT_LBEN);
 
        /* Map PF MAC address in RAR Entry 0 to first pool following VFs */
@@ -3599,6 +3658,10 @@ static void ixgbe_configure_virtualization(struct ixgbe_adapter *adapter)
                /* enable ethertype anti spoofing if hw supports it */
                if (hw->mac.ops.set_ethertype_anti_spoofing)
                        hw->mac.ops.set_ethertype_anti_spoofing(hw, true, i);
+
+               /* Enable/Disable RSS query feature  */
+               ixgbe_ndo_set_vf_rss_query_en(adapter->netdev, i,
+                                         adapter->vfinfo[i].rss_query_enabled);
        }
 }
 
@@ -7870,6 +7933,80 @@ static int ixgbe_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
        return ndo_dflt_fdb_add(ndm, tb, dev, addr, vid, flags);
 }
 
+/**
+ * ixgbe_configure_bridge_mode - set various bridge modes
+ * @adapter - the private structure
+ * @mode - requested bridge mode
+ *
+ * Configure some settings require for various bridge modes.
+ **/
+static int ixgbe_configure_bridge_mode(struct ixgbe_adapter *adapter,
+                                      __u16 mode)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+       unsigned int p, num_pools;
+       u32 vmdctl;
+
+       switch (mode) {
+       case BRIDGE_MODE_VEPA:
+               /* disable Tx loopback, rely on switch hairpin mode */
+               IXGBE_WRITE_REG(&adapter->hw, IXGBE_PFDTXGSWC, 0);
+
+               /* must enable Rx switching replication to allow multicast
+                * packet reception on all VFs, and to enable source address
+                * pruning.
+                */
+               vmdctl = IXGBE_READ_REG(hw, IXGBE_VMD_CTL);
+               vmdctl |= IXGBE_VT_CTL_REPLEN;
+               IXGBE_WRITE_REG(hw, IXGBE_VMD_CTL, vmdctl);
+
+               /* enable Rx source address pruning. Note, this requires
+                * replication to be enabled or else it does nothing.
+                */
+               num_pools = adapter->num_vfs + adapter->num_rx_pools;
+               for (p = 0; p < num_pools; p++) {
+                       if (hw->mac.ops.set_source_address_pruning)
+                               hw->mac.ops.set_source_address_pruning(hw,
+                                                                      true,
+                                                                      p);
+               }
+               break;
+       case BRIDGE_MODE_VEB:
+               /* enable Tx loopback for internal VF/PF communication */
+               IXGBE_WRITE_REG(&adapter->hw, IXGBE_PFDTXGSWC,
+                               IXGBE_PFDTXGSWC_VT_LBEN);
+
+               /* disable Rx switching replication unless we have SR-IOV
+                * virtual functions
+                */
+               vmdctl = IXGBE_READ_REG(hw, IXGBE_VMD_CTL);
+               if (!adapter->num_vfs)
+                       vmdctl &= ~IXGBE_VT_CTL_REPLEN;
+               IXGBE_WRITE_REG(hw, IXGBE_VMD_CTL, vmdctl);
+
+               /* disable Rx source address pruning, since we don't expect to
+                * be receiving external loopback of our transmitted frames.
+                */
+               num_pools = adapter->num_vfs + adapter->num_rx_pools;
+               for (p = 0; p < num_pools; p++) {
+                       if (hw->mac.ops.set_source_address_pruning)
+                               hw->mac.ops.set_source_address_pruning(hw,
+                                                                      false,
+                                                                      p);
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       adapter->bridge_mode = mode;
+
+       e_info(drv, "enabling bridge mode: %s\n",
+              mode == BRIDGE_MODE_VEPA ? "VEPA" : "VEB");
+
+       return 0;
+}
+
 static int ixgbe_ndo_bridge_setlink(struct net_device *dev,
                                    struct nlmsghdr *nlh, u16 flags)
 {
@@ -7885,8 +8022,8 @@ static int ixgbe_ndo_bridge_setlink(struct net_device *dev,
                return -EINVAL;
 
        nla_for_each_nested(attr, br_spec, rem) {
+               u32 status;
                __u16 mode;
-               u32 reg = 0;
 
                if (nla_type(attr) != IFLA_BRIDGE_MODE)
                        continue;
@@ -7895,19 +8032,11 @@ static int ixgbe_ndo_bridge_setlink(struct net_device *dev,
                        return -EINVAL;
 
                mode = nla_get_u16(attr);
-               if (mode == BRIDGE_MODE_VEPA) {
-                       reg = 0;
-                       adapter->flags2 &= ~IXGBE_FLAG2_BRIDGE_MODE_VEB;
-               } else if (mode == BRIDGE_MODE_VEB) {
-                       reg = IXGBE_PFDTXGSWC_VT_LBEN;
-                       adapter->flags2 |= IXGBE_FLAG2_BRIDGE_MODE_VEB;
-               } else
-                       return -EINVAL;
-
-               IXGBE_WRITE_REG(&adapter->hw, IXGBE_PFDTXGSWC, reg);
+               status = ixgbe_configure_bridge_mode(adapter, mode);
+               if (status)
+                       return status;
 
-               e_info(drv, "enabling bridge mode: %s\n",
-                       mode == BRIDGE_MODE_VEPA ? "VEPA" : "VEB");
+               break;
        }
 
        return 0;
@@ -7918,17 +8047,12 @@ static int ixgbe_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
                                    u32 filter_mask)
 {
        struct ixgbe_adapter *adapter = netdev_priv(dev);
-       u16 mode;
 
        if (!(adapter->flags & IXGBE_FLAG_SRIOV_ENABLED))
                return 0;
 
-       if (adapter->flags2 & IXGBE_FLAG2_BRIDGE_MODE_VEB)
-               mode = BRIDGE_MODE_VEB;
-       else
-               mode = BRIDGE_MODE_VEPA;
-
-       return ndo_dflt_bridge_getlink(skb, pid, seq, dev, mode, 0, 0);
+       return ndo_dflt_bridge_getlink(skb, pid, seq, dev,
+                                      adapter->bridge_mode, 0, 0);
 }
 
 static void *ixgbe_fwd_add(struct net_device *pdev, struct net_device *vdev)
@@ -8040,6 +8164,7 @@ static const struct net_device_ops ixgbe_netdev_ops = {
        .ndo_set_vf_vlan        = ixgbe_ndo_set_vf_vlan,
        .ndo_set_vf_rate        = ixgbe_ndo_set_vf_bw,
        .ndo_set_vf_spoofchk    = ixgbe_ndo_set_vf_spoofchk,
+       .ndo_set_vf_rss_query_en = ixgbe_ndo_set_vf_rss_query_en,
        .ndo_get_vf_config      = ixgbe_ndo_get_vf_config,
        .ndo_get_stats64        = ixgbe_get_stats64,
 #ifdef CONFIG_IXGBE_DCB
@@ -8394,7 +8519,6 @@ skip_sriov:
                           NETIF_F_IPV6_CSUM |
                           NETIF_F_HW_VLAN_CTAG_TX |
                           NETIF_F_HW_VLAN_CTAG_RX |
-                          NETIF_F_HW_VLAN_CTAG_FILTER |
                           NETIF_F_TSO |
                           NETIF_F_TSO6 |
                           NETIF_F_RXHASH |
@@ -8416,6 +8540,7 @@ skip_sriov:
        }
 
        netdev->hw_features |= NETIF_F_RXALL;
+       netdev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
 
        netdev->vlan_features |= NETIF_F_TSO;
        netdev->vlan_features |= NETIF_F_TSO6;
@@ -8977,8 +9102,6 @@ static void __exit ixgbe_exit_module(void)
        pci_unregister_driver(&ixgbe_driver);
 
        ixgbe_dbg_exit();
-
-       rcu_barrier(); /* Wait for completion of call_rcu()'s */
 }
 
 #ifdef CONFIG_IXGBE_DCA
index a5cb755de3a994a34f68e0c9142414a8da52cc68..b1e4703ff2a5949fc4c35db6c0c1cd1dceea6675 100644 (file)
@@ -73,6 +73,7 @@ enum ixgbe_pfvf_api_rev {
        ixgbe_mbox_api_10,      /* API version 1.0, linux/freebsd VF driver */
        ixgbe_mbox_api_20,      /* API version 2.0, solaris Phase1 VF driver */
        ixgbe_mbox_api_11,      /* API version 1.1, linux/freebsd VF driver */
+       ixgbe_mbox_api_12,      /* API version 1.2, linux/freebsd VF driver */
        /* This value should always be last */
        ixgbe_mbox_api_unknown, /* indicates that API version is not known */
 };
@@ -97,6 +98,10 @@ enum ixgbe_pfvf_api_rev {
 #define IXGBE_VF_TRANS_VLAN    3       /* Indication of port vlan */
 #define IXGBE_VF_DEF_QUEUE     4       /* Default queue offset */
 
+/* mailbox API, version 1.2 VF requests */
+#define IXGBE_VF_GET_RETA      0x0a    /* VF request for RETA */
+#define IXGBE_VF_GET_RSS_KEY   0x0b    /* get RSS key */
+
 /* length of permanent address message returned from PF */
 #define IXGBE_VF_PERMADDR_MSG_LEN 4
 /* word in permanent address message with the current multicast type */
index 09a291bb7c343c6b202e8bff6f7594329ec2a564..1d17b5872dd1f266625891b600d673c98a8cfa0f 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/ip.h>
 #include <linux/tcp.h>
 #include <linux/ipv6.h>
+#include <linux/if_bridge.h>
 #ifdef NETIF_F_HW_VLAN_CTAG_TX
 #include <linux/if_vlan.h>
 #endif
@@ -79,7 +80,7 @@ static int __ixgbe_enable_sriov(struct ixgbe_adapter *adapter)
 
        /* Initialize default switching mode VEB */
        IXGBE_WRITE_REG(hw, IXGBE_PFDTXGSWC, IXGBE_PFDTXGSWC_VT_LBEN);
-       adapter->flags2 |= IXGBE_FLAG2_BRIDGE_MODE_VEB;
+       adapter->bridge_mode = BRIDGE_MODE_VEB;
 
        /* If call to enable VFs succeeded then allocate memory
         * for per VF control structures.
@@ -105,9 +106,18 @@ static int __ixgbe_enable_sriov(struct ixgbe_adapter *adapter)
                adapter->flags2 &= ~(IXGBE_FLAG2_RSC_CAPABLE |
                                     IXGBE_FLAG2_RSC_ENABLED);
 
-               /* enable spoof checking for all VFs */
-               for (i = 0; i < adapter->num_vfs; i++)
+               for (i = 0; i < adapter->num_vfs; i++) {
+                       /* enable spoof checking for all VFs */
                        adapter->vfinfo[i].spoofchk_enabled = true;
+
+                       /* We support VF RSS querying only for 82599 and x540
+                        * devices at the moment. These devices share RSS
+                        * indirection table and RSS hash key with PF therefore
+                        * we want to disable the querying by default.
+                        */
+                       adapter->vfinfo[i].rss_query_enabled = 0;
+               }
+
                return 0;
        }
 
@@ -424,6 +434,7 @@ static s32 ixgbe_set_vf_lpe(struct ixgbe_adapter *adapter, u32 *msgbuf, u32 vf)
 #endif /* CONFIG_FCOE */
                switch (adapter->vfinfo[vf].vf_api) {
                case ixgbe_mbox_api_11:
+               case ixgbe_mbox_api_12:
                        /*
                         * Version 1.1 supports jumbo frames on VFs if PF has
                         * jumbo frames enabled which means legacy VFs are
@@ -891,6 +902,7 @@ static int ixgbe_negotiate_vf_api(struct ixgbe_adapter *adapter,
        switch (api) {
        case ixgbe_mbox_api_10:
        case ixgbe_mbox_api_11:
+       case ixgbe_mbox_api_12:
                adapter->vfinfo[vf].vf_api = api;
                return 0;
        default:
@@ -914,6 +926,7 @@ static int ixgbe_get_vf_queues(struct ixgbe_adapter *adapter,
        switch (adapter->vfinfo[vf].vf_api) {
        case ixgbe_mbox_api_20:
        case ixgbe_mbox_api_11:
+       case ixgbe_mbox_api_12:
                break;
        default:
                return -1;
@@ -941,6 +954,53 @@ static int ixgbe_get_vf_queues(struct ixgbe_adapter *adapter,
        return 0;
 }
 
+static int ixgbe_get_vf_reta(struct ixgbe_adapter *adapter, u32 *msgbuf, u32 vf)
+{
+       u32 i, j;
+       u32 *out_buf = &msgbuf[1];
+       const u8 *reta = adapter->rss_indir_tbl;
+       u32 reta_size = ixgbe_rss_indir_tbl_entries(adapter);
+
+       /* Check if operation is permitted */
+       if (!adapter->vfinfo[vf].rss_query_enabled)
+               return -EPERM;
+
+       /* verify the PF is supporting the correct API */
+       if (adapter->vfinfo[vf].vf_api != ixgbe_mbox_api_12)
+               return -EOPNOTSUPP;
+
+       /* This mailbox command is supported (required) only for 82599 and x540
+        * VFs which support up to 4 RSS queues. Therefore we will compress the
+        * RETA by saving only 2 bits from each entry. This way we will be able
+        * to transfer the whole RETA in a single mailbox operation.
+        */
+       for (i = 0; i < reta_size / 16; i++) {
+               out_buf[i] = 0;
+               for (j = 0; j < 16; j++)
+                       out_buf[i] |= (u32)(reta[16 * i + j] & 0x3) << (2 * j);
+       }
+
+       return 0;
+}
+
+static int ixgbe_get_vf_rss_key(struct ixgbe_adapter *adapter,
+                               u32 *msgbuf, u32 vf)
+{
+       u32 *rss_key = &msgbuf[1];
+
+       /* Check if the operation is permitted */
+       if (!adapter->vfinfo[vf].rss_query_enabled)
+               return -EPERM;
+
+       /* verify the PF is supporting the correct API */
+       if (adapter->vfinfo[vf].vf_api != ixgbe_mbox_api_12)
+               return -EOPNOTSUPP;
+
+       memcpy(rss_key, adapter->rss_key, sizeof(adapter->rss_key));
+
+       return 0;
+}
+
 static int ixgbe_rcv_msg_from_vf(struct ixgbe_adapter *adapter, u32 vf)
 {
        u32 mbx_size = IXGBE_VFMAILBOX_SIZE;
@@ -997,6 +1057,12 @@ static int ixgbe_rcv_msg_from_vf(struct ixgbe_adapter *adapter, u32 vf)
        case IXGBE_VF_GET_QUEUES:
                retval = ixgbe_get_vf_queues(adapter, msgbuf, vf);
                break;
+       case IXGBE_VF_GET_RETA:
+               retval = ixgbe_get_vf_reta(adapter, msgbuf, vf);
+               break;
+       case IXGBE_VF_GET_RSS_KEY:
+               retval = ixgbe_get_vf_rss_key(adapter, msgbuf, vf);
+               break;
        default:
                e_err(drv, "Unhandled Msg %8.8x\n", msgbuf[0]);
                retval = IXGBE_ERR_MBX;
@@ -1330,6 +1396,26 @@ int ixgbe_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting)
        return 0;
 }
 
+int ixgbe_ndo_set_vf_rss_query_en(struct net_device *netdev, int vf,
+                                 bool setting)
+{
+       struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+       /* This operation is currently supported only for 82599 and x540
+        * devices.
+        */
+       if (adapter->hw.mac.type < ixgbe_mac_82599EB ||
+           adapter->hw.mac.type >= ixgbe_mac_X550)
+               return -EOPNOTSUPP;
+
+       if (vf >= adapter->num_vfs)
+               return -EINVAL;
+
+       adapter->vfinfo[vf].rss_query_enabled = setting;
+
+       return 0;
+}
+
 int ixgbe_ndo_get_vf_config(struct net_device *netdev,
                            int vf, struct ifla_vf_info *ivi)
 {
@@ -1343,5 +1429,6 @@ int ixgbe_ndo_get_vf_config(struct net_device *netdev,
        ivi->vlan = adapter->vfinfo[vf].pf_vlan;
        ivi->qos = adapter->vfinfo[vf].pf_qos;
        ivi->spoofchk = adapter->vfinfo[vf].spoofchk_enabled;
+       ivi->rss_query_en = adapter->vfinfo[vf].rss_query_enabled;
        return 0;
 }
index 32c26d586c01e1c5d561922774df4b250d3fdd73..2c197e6d1fe7c3cb66c1be785bba2a8084e806fa 100644 (file)
@@ -47,6 +47,8 @@ int ixgbe_ndo_set_vf_vlan(struct net_device *netdev, int queue, u16 vlan,
 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_set_vf_rss_query_en(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);
 void ixgbe_check_vf_rate_limit(struct ixgbe_adapter *adapter);
index c3ddc944f1e95722b9db06a14d0dcb74df704484..dd6ba5916dfe002b528684db30eb23c847829c2c 100644 (file)
@@ -285,6 +285,8 @@ struct ixgbe_thermal_sensor_data {
 #define IXGBE_VLVF(_i)  (0x0F100 + ((_i) * 4))  /* 64 of these (0-63) */
 #define IXGBE_VLVFB(_i) (0x0F200 + ((_i) * 4))  /* 128 of these (0-127) */
 #define IXGBE_VMVIR(_i) (0x08000 + ((_i) * 4))  /* 64 of these (0-63) */
+#define IXGBE_PFFLPL   0x050B0
+#define IXGBE_PFFLPH   0x050B4
 #define IXGBE_VT_CTL         0x051B0
 #define IXGBE_PFMAILBOX(_i)  (0x04B00 + (4 * (_i))) /* 64 total */
 #define IXGBE_PFMBMEM(_i)    (0x13000 + (64 * (_i))) /* 64 Mailboxes, 16 DW each */
@@ -608,6 +610,8 @@ struct ixgbe_thermal_sensor_data {
 #define IXGBE_RTTBCNRM    0x04980
 #define IXGBE_RTTQCNRM    0x04980
 
+/* FCoE Direct DMA Context */
+#define IXGBE_FCDDC(_i, _j)    (0x20000 + ((_i) * 0x4) + ((_j) * 0x10))
 /* FCoE DMA Context Registers */
 #define IXGBE_FCPTRL    0x02410 /* FC User Desc. PTR Low */
 #define IXGBE_FCPTRH    0x02414 /* FC USer Desc. PTR High */
@@ -634,6 +638,9 @@ struct ixgbe_thermal_sensor_data {
 #define IXGBE_TSOFF     0x04A98 /* Tx FC SOF */
 #define IXGBE_REOFF     0x05158 /* Rx FC EOF */
 #define IXGBE_RSOFF     0x051F8 /* Rx FC SOF */
+/* FCoE Direct Filter Context */
+#define IXGBE_FCDFC(_i, _j)    (0x28000 + ((_i) * 0x4) + ((_j) * 0x10))
+#define IXGBE_FCDFCD(_i)       (0x30000 + ((_i) * 0x4))
 /* FCoE Filter Context Registers */
 #define IXGBE_FCFLT     0x05108 /* FC FLT Context */
 #define IXGBE_FCFLTRW   0x05110 /* FC Filter RW Control */
@@ -664,6 +671,10 @@ struct ixgbe_thermal_sensor_data {
 #define IXGBE_FCRECTL_ENA       0x1        /* FCoE Redir Table Enable */
 #define IXGBE_FCRETA_SIZE       8          /* Max entries in FCRETA */
 #define IXGBE_FCRETA_ENTRY_MASK 0x0000007f /* 7 bits for the queue index */
+#define IXGBE_FCRETA_SIZE_X550 32 /* Max entries in FCRETA */
+/* Higher 7 bits for the queue index */
+#define IXGBE_FCRETA_ENTRY_HIGH_MASK   0x007F0000
+#define IXGBE_FCRETA_ENTRY_HIGH_SHIFT  16
 
 /* Stats registers */
 #define IXGBE_CRCERRS   0x04000
@@ -3069,6 +3080,8 @@ struct ixgbe_mac_operations {
        s32 (*init_thermal_sensor_thresh)(struct ixgbe_hw *hw);
        void (*disable_rx)(struct ixgbe_hw *hw);
        void (*enable_rx)(struct ixgbe_hw *hw);
+       void (*set_source_address_pruning)(struct ixgbe_hw *, bool,
+                                          unsigned int);
        void (*set_ethertype_anti_spoofing)(struct ixgbe_hw *, bool, int);
 
        /* DMA Coalescing */
index 58a3155af7cd0215a8bf552973218859b59f1ed8..cf5cf819a6b890bdce1da9d8eecc4acbe0d479bb 100644 (file)
@@ -1363,6 +1363,33 @@ static void ixgbe_set_ethertype_anti_spoofing_X550(struct ixgbe_hw *hw,
        IXGBE_WRITE_REG(hw, IXGBE_PFVFSPOOF(vf_target_reg), pfvfspoof);
 }
 
+/** ixgbe_set_source_address_pruning_X550 - Enable/Disbale src address pruning
+ *  @hw: pointer to hardware structure
+ *  @enable: enable or disable source address pruning
+ *  @pool: Rx pool to set source address pruning for
+ **/
+static void ixgbe_set_source_address_pruning_X550(struct ixgbe_hw *hw,
+                                                 bool enable,
+                                                 unsigned int pool)
+{
+       u64 pfflp;
+
+       /* max rx pool is 63 */
+       if (pool > 63)
+               return;
+
+       pfflp = (u64)IXGBE_READ_REG(hw, IXGBE_PFFLPL);
+       pfflp |= (u64)IXGBE_READ_REG(hw, IXGBE_PFFLPH) << 32;
+
+       if (enable)
+               pfflp |= (1ULL << pool);
+       else
+               pfflp &= ~(1ULL << pool);
+
+       IXGBE_WRITE_REG(hw, IXGBE_PFFLPL, (u32)pfflp);
+       IXGBE_WRITE_REG(hw, IXGBE_PFFLPH, (u32)(pfflp >> 32));
+}
+
 #define X550_COMMON_MAC \
        .init_hw                        = &ixgbe_init_hw_generic, \
        .start_hw                       = &ixgbe_start_hw_X540, \
@@ -1397,6 +1424,8 @@ static void ixgbe_set_ethertype_anti_spoofing_X550(struct ixgbe_hw *hw,
        .init_uta_tables                = &ixgbe_init_uta_tables_generic, \
        .set_mac_anti_spoofing          = &ixgbe_set_mac_anti_spoofing, \
        .set_vlan_anti_spoofing         = &ixgbe_set_vlan_anti_spoofing, \
+       .set_source_address_pruning     = \
+                               &ixgbe_set_source_address_pruning_X550, \
        .set_ethertype_anti_spoofing    = \
                                &ixgbe_set_ethertype_anti_spoofing_X550, \
        .acquire_swfw_sync              = &ixgbe_acquire_swfw_sync_X540, \
index e83c85bf2602094271c8446bb1a644410a3c0c94..b2f5b161d792a769bdb780154bfe5895f56ba218 100644 (file)
@@ -794,6 +794,71 @@ static int ixgbevf_set_coalesce(struct net_device *netdev,
        return 0;
 }
 
+static int ixgbevf_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info,
+                            u32 *rules __always_unused)
+{
+       struct ixgbevf_adapter *adapter = netdev_priv(dev);
+
+       switch (info->cmd) {
+       case ETHTOOL_GRXRINGS:
+               info->data = adapter->num_rx_queues;
+               return 0;
+       default:
+               hw_dbg(&adapter->hw, "Command parameters not supported\n");
+               return -EOPNOTSUPP;
+       }
+}
+
+static u32 ixgbevf_get_rxfh_indir_size(struct net_device *netdev)
+{
+       struct ixgbevf_adapter *adapter = netdev_priv(netdev);
+
+       /* We support this operation only for 82599 and x540 at the moment */
+       if (adapter->hw.mac.type < ixgbe_mac_X550_vf)
+               return IXGBEVF_82599_RETA_SIZE;
+
+       return 0;
+}
+
+static u32 ixgbevf_get_rxfh_key_size(struct net_device *netdev)
+{
+       struct ixgbevf_adapter *adapter = netdev_priv(netdev);
+
+       /* We support this operation only for 82599 and x540 at the moment */
+       if (adapter->hw.mac.type < ixgbe_mac_X550_vf)
+               return IXGBEVF_RSS_HASH_KEY_SIZE;
+
+       return 0;
+}
+
+static int ixgbevf_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
+                           u8 *hfunc)
+{
+       struct ixgbevf_adapter *adapter = netdev_priv(netdev);
+       int err = 0;
+
+       if (hfunc)
+               *hfunc = ETH_RSS_HASH_TOP;
+
+       /* If neither indirection table nor hash key was requested - just
+        * return a success avoiding taking any locks.
+        */
+       if (!indir && !key)
+               return 0;
+
+       spin_lock_bh(&adapter->mbx_lock);
+       if (indir)
+               err = ixgbevf_get_reta_locked(&adapter->hw, indir,
+                                             adapter->num_rx_queues);
+
+       if (!err && key)
+               err = ixgbevf_get_rss_key_locked(&adapter->hw, key);
+
+       spin_unlock_bh(&adapter->mbx_lock);
+
+       return err;
+}
+
 static const struct ethtool_ops ixgbevf_ethtool_ops = {
        .get_settings           = ixgbevf_get_settings,
        .get_drvinfo            = ixgbevf_get_drvinfo,
@@ -811,6 +876,10 @@ static const struct ethtool_ops ixgbevf_ethtool_ops = {
        .get_ethtool_stats      = ixgbevf_get_ethtool_stats,
        .get_coalesce           = ixgbevf_get_coalesce,
        .set_coalesce           = ixgbevf_set_coalesce,
+       .get_rxnfc              = ixgbevf_get_rxnfc,
+       .get_rxfh_indir_size    = ixgbevf_get_rxfh_indir_size,
+       .get_rxfh_key_size      = ixgbevf_get_rxfh_key_size,
+       .get_rxfh               = ixgbevf_get_rxfh,
 };
 
 void ixgbevf_set_ethtool_ops(struct net_device *netdev)
index bc939a1fcb3cf0b073ebfb1a5873196e2eed3a1f..775d089009499ddfcee4d97c3a55af73528138c5 100644 (file)
@@ -145,6 +145,8 @@ struct ixgbevf_ring {
 #define MAX_RX_QUEUES IXGBE_VF_MAX_RX_QUEUES
 #define MAX_TX_QUEUES IXGBE_VF_MAX_TX_QUEUES
 #define IXGBEVF_MAX_RSS_QUEUES 2
+#define IXGBEVF_82599_RETA_SIZE        128
+#define IXGBEVF_RSS_HASH_KEY_SIZE      40
 
 #define IXGBEVF_DEFAULT_TXD    1024
 #define IXGBEVF_DEFAULT_RXD    512
index 4ee15adb3bd9a6f7b7ecfa76b3acc7f2a5b0885c..a16d267fbce4b0f6883c8cf5d43db1ad3bf7e458 100644 (file)
@@ -2030,7 +2030,8 @@ static void ixgbevf_init_last_counter_stats(struct ixgbevf_adapter *adapter)
 static void ixgbevf_negotiate_api(struct ixgbevf_adapter *adapter)
 {
        struct ixgbe_hw *hw = &adapter->hw;
-       int api[] = { ixgbe_mbox_api_11,
+       int api[] = { ixgbe_mbox_api_12,
+                     ixgbe_mbox_api_11,
                      ixgbe_mbox_api_10,
                      ixgbe_mbox_api_unknown };
        int err = 0, idx = 0;
@@ -2332,6 +2333,7 @@ static void ixgbevf_set_num_queues(struct ixgbevf_adapter *adapter)
 
                switch (hw->api_version) {
                case ixgbe_mbox_api_11:
+               case ixgbe_mbox_api_12:
                        adapter->num_rx_queues = rss;
                        adapter->num_tx_queues = rss;
                default:
@@ -3712,6 +3714,7 @@ static int ixgbevf_change_mtu(struct net_device *netdev, int new_mtu)
 
        switch (adapter->hw.api_version) {
        case ixgbe_mbox_api_11:
+       case ixgbe_mbox_api_12:
                max_possible_frame = IXGBE_MAX_JUMBO_FRAME_SIZE;
                break;
        default:
index 6253e9335cae47bd18ab330f966820708c010d37..82f44e06e5fca11ad86ec92930428fbbc130c93d 100644 (file)
@@ -83,6 +83,7 @@ enum ixgbe_pfvf_api_rev {
        ixgbe_mbox_api_10,      /* API version 1.0, linux/freebsd VF driver */
        ixgbe_mbox_api_20,      /* API version 2.0, solaris Phase1 VF driver */
        ixgbe_mbox_api_11,      /* API version 1.1, linux/freebsd VF driver */
+       ixgbe_mbox_api_12,      /* API version 1.2, linux/freebsd VF driver */
        /* This value should always be last */
        ixgbe_mbox_api_unknown, /* indicates that API version is not known */
 };
@@ -107,6 +108,10 @@ enum ixgbe_pfvf_api_rev {
 #define IXGBE_VF_TRANS_VLAN    3 /* Indication of port VLAN */
 #define IXGBE_VF_DEF_QUEUE     4 /* Default queue offset */
 
+/* mailbox API, version 1.2 VF requests */
+#define IXGBE_VF_GET_RETA      0x0a    /* VF request for RETA */
+#define IXGBE_VF_GET_RSS_KEY   0x0b    /* get RSS hash key */
+
 /* length of permanent address message returned from PF */
 #define IXGBE_VF_PERMADDR_MSG_LEN      4
 /* word in permanent address message with the current multicast type */
index 2614fd328e47d8a3e8380429d010a3a452b13375..d1339b0506274ce0acbfc1e642b1b7fa57c332e8 100644 (file)
@@ -256,6 +256,129 @@ static s32 ixgbevf_set_uc_addr_vf(struct ixgbe_hw *hw, u32 index, u8 *addr)
        return ret_val;
 }
 
+/**
+ * ixgbevf_get_reta_locked - get the RSS redirection table (RETA) contents.
+ * @adapter: pointer to the port handle
+ * @reta: buffer to fill with RETA contents.
+ * @num_rx_queues: Number of Rx queues configured for this port
+ *
+ * The "reta" buffer should be big enough to contain 32 registers.
+ *
+ * Returns: 0 on success.
+ *          if API doesn't support this operation - (-EOPNOTSUPP).
+ */
+int ixgbevf_get_reta_locked(struct ixgbe_hw *hw, u32 *reta, int num_rx_queues)
+{
+       int err, i, j;
+       u32 msgbuf[IXGBE_VFMAILBOX_SIZE];
+       u32 *hw_reta = &msgbuf[1];
+       u32 mask = 0;
+
+       /* We have to use a mailbox for 82599 and x540 devices only.
+        * For these devices RETA has 128 entries.
+        * Also these VFs support up to 4 RSS queues. Therefore PF will compress
+        * 16 RETA entries in each DWORD giving 2 bits to each entry.
+        */
+       int dwords = IXGBEVF_82599_RETA_SIZE / 16;
+
+       /* We support the RSS querying for 82599 and x540 devices only.
+        * Thus return an error if API doesn't support RETA querying or querying
+        * is not supported for this device type.
+        */
+       if (hw->api_version != ixgbe_mbox_api_12 ||
+           hw->mac.type >= ixgbe_mac_X550_vf)
+               return -EOPNOTSUPP;
+
+       msgbuf[0] = IXGBE_VF_GET_RETA;
+
+       err = hw->mbx.ops.write_posted(hw, msgbuf, 1);
+
+       if (err)
+               return err;
+
+       err = hw->mbx.ops.read_posted(hw, msgbuf, dwords + 1);
+
+       if (err)
+               return err;
+
+       msgbuf[0] &= ~IXGBE_VT_MSGTYPE_CTS;
+
+       /* If the operation has been refused by a PF return -EPERM */
+       if (msgbuf[0] == (IXGBE_VF_GET_RETA | IXGBE_VT_MSGTYPE_NACK))
+               return -EPERM;
+
+       /* If we didn't get an ACK there must have been
+        * some sort of mailbox error so we should treat it
+        * as such.
+        */
+       if (msgbuf[0] != (IXGBE_VF_GET_RETA | IXGBE_VT_MSGTYPE_ACK))
+               return IXGBE_ERR_MBX;
+
+       /* ixgbevf doesn't support more than 2 queues at the moment */
+       if (num_rx_queues > 1)
+               mask = 0x1;
+
+       for (i = 0; i < dwords; i++)
+               for (j = 0; j < 16; j++)
+                       reta[i * 16 + j] = (hw_reta[i] >> (2 * j)) & mask;
+
+       return 0;
+}
+
+/**
+ * ixgbevf_get_rss_key_locked - get the RSS Random Key
+ * @hw: pointer to the HW structure
+ * @rss_key: buffer to fill with RSS Hash Key contents.
+ *
+ * The "rss_key" buffer should be big enough to contain 10 registers.
+ *
+ * Returns: 0 on success.
+ *          if API doesn't support this operation - (-EOPNOTSUPP).
+ */
+int ixgbevf_get_rss_key_locked(struct ixgbe_hw *hw, u8 *rss_key)
+{
+       int err;
+       u32 msgbuf[IXGBE_VFMAILBOX_SIZE];
+
+       /* We currently support the RSS Random Key retrieval for 82599 and x540
+        * devices only.
+        *
+        * Thus return an error if API doesn't support RSS Random Key retrieval
+        * or if the operation is not supported for this device type.
+        */
+       if (hw->api_version != ixgbe_mbox_api_12 ||
+           hw->mac.type >= ixgbe_mac_X550_vf)
+               return -EOPNOTSUPP;
+
+       msgbuf[0] = IXGBE_VF_GET_RSS_KEY;
+       err = hw->mbx.ops.write_posted(hw, msgbuf, 1);
+
+       if (err)
+               return err;
+
+       err = hw->mbx.ops.read_posted(hw, msgbuf, 11);
+
+       if (err)
+               return err;
+
+       msgbuf[0] &= ~IXGBE_VT_MSGTYPE_CTS;
+
+       /* If the operation has been refused by a PF return -EPERM */
+       if (msgbuf[0] == (IXGBE_VF_GET_RETA | IXGBE_VT_MSGTYPE_NACK))
+               return -EPERM;
+
+       /* If we didn't get an ACK there must have been
+        * some sort of mailbox error so we should treat it
+        * as such.
+        */
+       if (msgbuf[0] != (IXGBE_VF_GET_RSS_KEY | IXGBE_VT_MSGTYPE_ACK))
+               return IXGBE_ERR_MBX;
+
+       memcpy(rss_key, msgbuf + 1, IXGBEVF_RSS_HASH_KEY_SIZE);
+
+       return 0;
+}
+
 /**
  *  ixgbevf_set_rar_vf - set device MAC address
  *  @hw: pointer to hardware structure
@@ -545,6 +668,7 @@ int ixgbevf_get_queues(struct ixgbe_hw *hw, unsigned int *num_tcs,
        /* do nothing if API doesn't support ixgbevf_get_queues */
        switch (hw->api_version) {
        case ixgbe_mbox_api_11:
+       case ixgbe_mbox_api_12:
                break;
        default:
                return 0;
index 6688250da7a12afc41a7570e0121e477fce2d621..d40f036b6df0c828b5a3efe10ee90ba780b36a52 100644 (file)
@@ -210,4 +210,6 @@ void ixgbevf_rlpml_set_vf(struct ixgbe_hw *hw, u16 max_size);
 int ixgbevf_negotiate_api_version(struct ixgbe_hw *hw, int api);
 int ixgbevf_get_queues(struct ixgbe_hw *hw, unsigned int *num_tcs,
                       unsigned int *default_tc);
+int ixgbevf_get_reta_locked(struct ixgbe_hw *hw, u32 *reta, int num_rx_queues);
+int ixgbevf_get_rss_key_locked(struct ixgbe_hw *hw, u8 *rss_key);
 #endif /* __IXGBE_VF_H__ */
index 79b1501e7951fc18a97caf3c3f127a50c6c3e256..4fdd3c37e47bf7c7862b9edf569be6f7f38e8dae 100644 (file)
@@ -771,7 +771,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
                /*
                 * make sure we read the CQE after we read the ownership bit
                 */
-               rmb();
+               dma_rmb();
 
                /* Drop packet on bad receive or bad checksum */
                if (unlikely((cqe->owner_sr_opcode & MLX4_CQE_OPCODE_MASK) ==
index 55f9f5c5344e19a3d8d76b083e0fd73d44b9870b..1783705273d89773c0a462cb28684f2969e55ac4 100644 (file)
@@ -416,7 +416,7 @@ static bool mlx4_en_process_tx_cq(struct net_device *dev,
                 * make sure we read the CQE after we read the
                 * ownership bit
                 */
-               rmb();
+               dma_rmb();
 
                if (unlikely((cqe->owner_sr_opcode & MLX4_CQE_OPCODE_MASK) ==
                             MLX4_CQE_OPCODE_ERROR)) {
@@ -667,7 +667,7 @@ static void build_inline_wqe(struct mlx4_en_tx_desc *tx_desc,
                                       skb_frag_size(&shinfo->frags[0]));
                }
 
-               wmb();
+               dma_wmb();
                inl->byte_count = cpu_to_be32(1 << 31 | (skb->len - spc));
        }
 }
@@ -804,7 +804,7 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev)
 
                        data->addr = cpu_to_be64(dma);
                        data->lkey = ring->mr_key;
-                       wmb();
+                       dma_wmb();
                        data->byte_count = cpu_to_be32(byte_count);
                        --data;
                }
@@ -821,7 +821,7 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev)
 
                        data->addr = cpu_to_be64(dma);
                        data->lkey = ring->mr_key;
-                       wmb();
+                       dma_wmb();
                        data->byte_count = cpu_to_be32(byte_count);
                }
                /* tx completion can avoid cache line miss for common cases */
@@ -938,7 +938,7 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev)
                /* Ensure new descriptor hits memory
                 * before setting ownership of this descriptor to HW
                 */
-               wmb();
+               dma_wmb();
                tx_desc->ctrl.owner_opcode = op_own;
 
                wmb();
@@ -958,7 +958,7 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev)
                /* Ensure new descriptor hits memory
                 * before setting ownership of this descriptor to HW
                 */
-               wmb();
+               dma_wmb();
                tx_desc->ctrl.owner_opcode = op_own;
                if (send_doorbell) {
                        wmb();
index 6e70ffee8e87ee2fa9957adeb5994ca346bf155b..190fd624bdfebd6e7b5b9e83f8470b840f8d09cb 100644 (file)
@@ -188,7 +188,7 @@ static void slave_event(struct mlx4_dev *dev, u8 slave, struct mlx4_eqe *eqe)
        memcpy(s_eqe, eqe, dev->caps.eqe_size - 1);
        s_eqe->slave_id = slave;
        /* ensure all information is written before setting the ownersip bit */
-       wmb();
+       dma_wmb();
        s_eqe->owner = !!(slave_eq->prod & SLAVE_EVENT_EQ_SIZE) ? 0x0 : 0x80;
        ++slave_eq->prod;
 
@@ -473,7 +473,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
                 * Make sure we read EQ entry contents after we've
                 * checked the ownership bit.
                 */
-               rmb();
+               dma_rmb();
 
                switch (eqe->type) {
                case MLX4_EVENT_TYPE_COMP:
index dbf190d9b9adb57c9108a5dcbea8dd80b6e25d5e..58800e4f39585c2fd30d76e8de4a21bfb6f8bf66 100644 (file)
@@ -208,7 +208,7 @@ static int mlx5_eq_int(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
                 * Make sure we read EQ entry contents after we've
                 * checked the ownership bit.
                 */
-               rmb();
+               dma_rmb();
 
                mlx5_core_dbg(eq->dev, "eqn %d, eqe type %s\n",
                              eq->eqn, eqe_type_str(eqe->type));
index 092dcae0d4a969523e7cd99dfd3380398c3e7c8c..1e0f72b65459027059cd85a3cece83ae6c24a1be 100644 (file)
@@ -2520,7 +2520,7 @@ static int fill_rx_buffers(struct s2io_nic *nic, struct ring_info *ring,
                        DBG_PRINT(INFO_DBG, "%s: Could not allocate skb\n",
                                  ring->dev->name);
                        if (first_rxdp) {
-                               wmb();
+                               dma_wmb();
                                first_rxdp->Control_1 |= RXD_OWN_XENA;
                        }
                        swstats->mem_alloc_fail_cnt++;
@@ -2634,7 +2634,7 @@ static int fill_rx_buffers(struct s2io_nic *nic, struct ring_info *ring,
                rxdp->Control_2 |= SET_RXD_MARKER;
                if (!(alloc_tab & ((1 << rxsync_frequency) - 1))) {
                        if (first_rxdp) {
-                               wmb();
+                               dma_wmb();
                                first_rxdp->Control_1 |= RXD_OWN_XENA;
                        }
                        first_rxdp = rxdp;
@@ -2649,7 +2649,7 @@ end:
         * and other fields are seen by adapter correctly.
         */
        if (first_rxdp) {
-               wmb();
+               dma_wmb();
                first_rxdp->Control_1 |= RXD_OWN_XENA;
        }
 
@@ -6950,7 +6950,7 @@ static  int rxd_owner_bit_reset(struct s2io_nic *sp)
                                }
 
                                set_rxd_buffer_size(sp, rxdp, size);
-                               wmb();
+                               dma_wmb();
                                /* flip the Ownership bit to Hardware */
                                rxdp->Control_1 |= RXD_OWN_XENA;
                        }
index c0a39198337268d88b8996f9607794c8a3f4f14b..2ac9552d1fa385953e261ff3797c74b8d5ad4add 100644 (file)
@@ -97,6 +97,7 @@ struct stmmac_priv {
        int wolopts;
        int wol_irq;
        struct clk *stmmac_clk;
+       struct clk *pclk;
        struct reset_control *stmmac_rst;
        int clk_csr;
        struct timer_list eee_ctrl_timer;
index 5336594abed1c373259b67f1a6cb8ef1866fc92d..06103cad7c77cb4e6c530752b70b808b5e993984 100644 (file)
@@ -2849,6 +2849,16 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device,
        }
        clk_prepare_enable(priv->stmmac_clk);
 
+       priv->pclk = devm_clk_get(priv->device, "pclk");
+       if (IS_ERR(priv->pclk)) {
+               if (PTR_ERR(priv->pclk) == -EPROBE_DEFER) {
+                       ret = -EPROBE_DEFER;
+                       goto error_pclk_get;
+               }
+               priv->pclk = NULL;
+       }
+       clk_prepare_enable(priv->pclk);
+
        priv->stmmac_rst = devm_reset_control_get(priv->device,
                                                  STMMAC_RESOURCE_NAME);
        if (IS_ERR(priv->stmmac_rst)) {
@@ -2934,6 +2944,8 @@ error_mdio_register:
 error_netdev_register:
        netif_napi_del(&priv->napi);
 error_hw_init:
+       clk_disable_unprepare(priv->pclk);
+error_pclk_get:
        clk_disable_unprepare(priv->stmmac_clk);
 error_clk_get:
        free_netdev(ndev);
@@ -2965,6 +2977,7 @@ int stmmac_dvr_remove(struct net_device *ndev)
        unregister_netdev(ndev);
        if (priv->stmmac_rst)
                reset_control_assert(priv->stmmac_rst);
+       clk_disable_unprepare(priv->pclk);
        clk_disable_unprepare(priv->stmmac_clk);
        free_netdev(ndev);
 
@@ -3011,6 +3024,7 @@ int stmmac_suspend(struct net_device *ndev)
                stmmac_set_mac(priv->ioaddr, false);
                pinctrl_pm_select_sleep_state(priv->device);
                /* Disable clock in case of PWM is off */
+               clk_disable(priv->pclk);
                clk_disable(priv->stmmac_clk);
        }
        spin_unlock_irqrestore(&priv->lock, flags);
@@ -3051,6 +3065,7 @@ int stmmac_resume(struct net_device *ndev)
                pinctrl_pm_select_default_state(priv->device);
                /* enable the clk prevously disabled */
                clk_enable(priv->stmmac_clk);
+               clk_enable(priv->pclk);
                /* reset the phy so that it's ready */
                if (priv->mii)
                        stmmac_mdio_reset(priv->mii);
index 74e9b148378c1b1e1551976f1bfe4aa7c7920abe..e23a642357e7c01d7010ae070b47632b3a926ba9 100644 (file)
@@ -718,7 +718,7 @@ static __inline__ void gem_post_rxds(struct gem *gp, int limit)
        cluster_start = curr = (gp->rx_new & ~(4 - 1));
        count = 0;
        kick = -1;
-       wmb();
+       dma_wmb();
        while (curr != limit) {
                curr = NEXT_RX(curr);
                if (++count == 4) {
@@ -1038,7 +1038,7 @@ static netdev_tx_t gem_start_xmit(struct sk_buff *skb,
                if (gem_intme(entry))
                        ctrl |= TXDCTRL_INTME;
                txd->buffer = cpu_to_le64(mapping);
-               wmb();
+               dma_wmb();
                txd->control_word = cpu_to_le64(ctrl);
                entry = NEXT_TX(entry);
        } else {
@@ -1076,7 +1076,7 @@ static netdev_tx_t gem_start_xmit(struct sk_buff *skb,
 
                        txd = &gp->init_block->txd[entry];
                        txd->buffer = cpu_to_le64(mapping);
-                       wmb();
+                       dma_wmb();
                        txd->control_word = cpu_to_le64(this_ctrl | len);
 
                        if (gem_intme(entry))
@@ -1086,7 +1086,7 @@ static netdev_tx_t gem_start_xmit(struct sk_buff *skb,
                }
                txd = &gp->init_block->txd[first_entry];
                txd->buffer = cpu_to_le64(first_mapping);
-               wmb();
+               dma_wmb();
                txd->control_word =
                        cpu_to_le64(ctrl | TXDCTRL_SOF | intme | first_len);
        }
@@ -1585,7 +1585,7 @@ static void gem_clean_rings(struct gem *gp)
                        gp->rx_skbs[i] = NULL;
                }
                rxd->status_word = 0;
-               wmb();
+               dma_wmb();
                rxd->buffer = 0;
        }
 
@@ -1647,7 +1647,7 @@ static void gem_init_rings(struct gem *gp)
                                        RX_BUF_ALLOC_SIZE(gp),
                                        PCI_DMA_FROMDEVICE);
                rxd->buffer = cpu_to_le64(dma_addr);
-               wmb();
+               dma_wmb();
                rxd->status_word = cpu_to_le64(RXDCTRL_FRESH(gp));
                skb_reserve(skb, RX_OFFSET);
        }
@@ -1656,7 +1656,7 @@ static void gem_init_rings(struct gem *gp)
                struct gem_txd *txd = &gb->txd[i];
 
                txd->control_word = 0;
-               wmb();
+               dma_wmb();
                txd->buffer = 0;
        }
        wmb();
index 7a8ca2c7b7df3b3d4c82828d5c820f7ed2a6f996..cf4dcff051d5b9d3fb3edf20552501cc44c72a5b 100644 (file)
@@ -196,14 +196,14 @@ static u32 sbus_hme_read32(void __iomem *reg)
 static void sbus_hme_write_rxd(struct happy_meal_rxd *rxd, u32 flags, u32 addr)
 {
        rxd->rx_addr = (__force hme32)addr;
-       wmb();
+       dma_wmb();
        rxd->rx_flags = (__force hme32)flags;
 }
 
 static void sbus_hme_write_txd(struct happy_meal_txd *txd, u32 flags, u32 addr)
 {
        txd->tx_addr = (__force hme32)addr;
-       wmb();
+       dma_wmb();
        txd->tx_flags = (__force hme32)flags;
 }
 
@@ -225,14 +225,14 @@ static u32 pci_hme_read32(void __iomem *reg)
 static void pci_hme_write_rxd(struct happy_meal_rxd *rxd, u32 flags, u32 addr)
 {
        rxd->rx_addr = (__force hme32)cpu_to_le32(addr);
-       wmb();
+       dma_wmb();
        rxd->rx_flags = (__force hme32)cpu_to_le32(flags);
 }
 
 static void pci_hme_write_txd(struct happy_meal_txd *txd, u32 flags, u32 addr)
 {
        txd->tx_addr = (__force hme32)cpu_to_le32(addr);
-       wmb();
+       dma_wmb();
        txd->tx_flags = (__force hme32)cpu_to_le32(flags);
 }
 
@@ -268,12 +268,12 @@ static u32 pci_hme_read_desc32(hme32 *p)
        sbus_readl(__reg)
 #define hme_write_rxd(__hp, __rxd, __flags, __addr) \
 do {   (__rxd)->rx_addr = (__force hme32)(u32)(__addr); \
-       wmb(); \
+       dma_wmb(); \
        (__rxd)->rx_flags = (__force hme32)(u32)(__flags); \
 } while(0)
 #define hme_write_txd(__hp, __txd, __flags, __addr) \
 do {   (__txd)->tx_addr = (__force hme32)(u32)(__addr); \
-       wmb(); \
+       dma_wmb(); \
        (__txd)->tx_flags = (__force hme32)(u32)(__flags); \
 } while(0)
 #define hme_read_desc32(__hp, __p)     ((__force u32)(hme32)*(__p))
@@ -293,12 +293,12 @@ do {      (__txd)->tx_addr = (__force hme32)(u32)(__addr); \
        readl(__reg)
 #define hme_write_rxd(__hp, __rxd, __flags, __addr) \
 do {   (__rxd)->rx_addr = (__force hme32)cpu_to_le32(__addr); \
-       wmb(); \
+       dma_wmb(); \
        (__rxd)->rx_flags = (__force hme32)cpu_to_le32(__flags); \
 } while(0)
 #define hme_write_txd(__hp, __txd, __flags, __addr) \
 do {   (__txd)->tx_addr = (__force hme32)cpu_to_le32(__addr); \
-       wmb(); \
+       dma_wmb(); \
        (__txd)->tx_flags = (__force hme32)cpu_to_le32(__flags); \
 } while(0)
 static inline u32 hme_read_desc32(struct happy_meal *hp, hme32 *p)
index 401abf7254d33bfb0e81e12dc1eeaf4abbf32b51..53fe200e0b7949b810071c23a08af49167ccdf63 100644 (file)
@@ -519,7 +519,7 @@ static int vnet_walk_rx_one(struct vnet_port *port,
        if (desc->hdr.state != VIO_DESC_READY)
                return 1;
 
-       rmb();
+       dma_rmb();
 
        viodbg(DATA, "vio_walk_rx_one desc[%02x:%02x:%08x:%08x:%llx:%llx]\n",
               desc->hdr.state, desc->hdr.ack,
@@ -1380,7 +1380,7 @@ static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev)
        /* This has to be a non-SMP write barrier because we are writing
         * to memory which is shared with the peer LDOM.
         */
-       wmb();
+       dma_wmb();
 
        d->hdr.state = VIO_DESC_READY;
 
@@ -1395,7 +1395,7 @@ static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev)
         * is marked READY, but start_cons was false.
         * If so, vnet_ack() should send out the missed "start" trigger.
         *
-        * Note that the wmb() above makes sure the cookies et al. are
+        * Note that the dma_wmb() above makes sure the cookies et al. are
         * not globally visible before the VIO_DESC_READY, and that the
         * stores are ordered correctly by the compiler. The consumer will
         * not proceed until the VIO_DESC_READY is visible assuring that
index 309adee6e7910db99cfe2cd08d332c24033c1d15..f0b8b3e0ed7cdf8387d010b4967ca9b172e2c011 100644 (file)
@@ -130,7 +130,6 @@ struct hv_netvsc_packet {
        u32 status;
        bool part_of_skb;
 
-       struct hv_device *device;
        bool is_data_pkt;
        bool xmit_more; /* from skb */
        u16 vlan_tci;
@@ -189,6 +188,7 @@ int netvsc_send(struct hv_device *device,
                struct hv_netvsc_packet *packet);
 void netvsc_linkstatus_callback(struct hv_device *device_obj,
                                struct rndis_message *resp);
+void netvsc_xmit_completion(void *context);
 int netvsc_recv_callback(struct hv_device *device_obj,
                        struct hv_netvsc_packet *packet,
                        struct ndis_tcp_ip_checksum_info *csum_info);
@@ -959,6 +959,10 @@ struct ndis_tcp_lso_info {
 #define NDIS_HASH_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \
                sizeof(u32))
 
+/* Total size of all PPI data */
+#define NDIS_ALL_PPI_SIZE (NDIS_VLAN_PPI_SIZE + NDIS_CSUM_PPI_SIZE + \
+                          NDIS_LSO_PPI_SIZE + NDIS_HASH_PPI_SIZE)
+
 /* Format of Information buffer passed in a SetRequest for the OID */
 /* OID_GEN_RNDIS_CONFIG_PARAMETER. */
 struct rndis_config_parameter_info {
@@ -1171,6 +1175,8 @@ struct rndis_message {
 #define RNDIS_HEADER_SIZE      (sizeof(struct rndis_message) - \
                                 sizeof(union rndis_message_container))
 
+#define RNDIS_AND_PPI_SIZE (sizeof(struct rndis_message) + NDIS_ALL_PPI_SIZE)
+
 #define NDIS_PACKET_TYPE_DIRECTED      0x00000001
 #define NDIS_PACKET_TYPE_MULTICAST     0x00000002
 #define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004
index f69923695b5b1b6fbb886f523203ba86656ac5f6..4d4d497d5762896d037f7c8e447451f24dac824e 100644 (file)
@@ -878,7 +878,9 @@ int netvsc_send(struct hv_device *device,
                packet->send_buf_index = section_index;
                packet->total_data_buflen += msd_len;
 
-               kfree(msdp->pkt);
+               if (msdp->pkt)
+                       netvsc_xmit_completion(msdp->pkt);
+
                if (packet->xmit_more) {
                        msdp->pkt = packet;
                        msdp->count++;
@@ -902,7 +904,7 @@ int netvsc_send(struct hv_device *device,
                if (m_ret != 0) {
                        netvsc_free_send_slot(net_device,
                                              msd_send->send_buf_index);
-                       kfree(msd_send);
+                       netvsc_xmit_completion(msd_send);
                }
        }
 
@@ -1011,7 +1013,6 @@ static void netvsc_receive(struct netvsc_device *net_device,
        }
 
        count = vmxferpage_packet->range_cnt;
-       netvsc_packet->device = device;
        netvsc_packet->channel = channel;
 
        /* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */
index f9db6bc513e954b0831d881baae5048ad7cc9f87..448716787e73c237f5de26f072ce03a28ab4986e 100644 (file)
@@ -229,7 +229,7 @@ static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb,
        return q_idx;
 }
 
-static void netvsc_xmit_completion(void *context)
+void netvsc_xmit_completion(void *context)
 {
        struct hv_netvsc_packet *packet = (struct hv_netvsc_packet *)context;
        struct sk_buff *skb = (struct sk_buff *)
@@ -370,50 +370,60 @@ not_ip:
 static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
 {
        struct net_device_context *net_device_ctx = netdev_priv(net);
-       struct hv_netvsc_packet *packet;
+       struct hv_netvsc_packet *packet = NULL;
        int ret;
        unsigned int num_data_pgs;
        struct rndis_message *rndis_msg;
        struct rndis_packet *rndis_pkt;
        u32 rndis_msg_size;
        bool isvlan;
+       bool linear = false;
        struct rndis_per_packet_info *ppi;
        struct ndis_tcp_ip_checksum_info *csum_info;
        struct ndis_tcp_lso_info *lso_info;
        int  hdr_offset;
        u32 net_trans_info;
        u32 hash;
-       u32 skb_length = skb->len;
-       u32 head_room = skb_headroom(skb);
+       u32 skb_length;
+       u32 head_room;
        u32 pkt_sz;
        struct hv_page_buffer page_buf[MAX_PAGE_BUFFER_COUNT];
 
 
        /* We will atmost need two pages to describe the rndis
         * header. We can only transmit MAX_PAGE_BUFFER_COUNT number
-        * of pages in a single packet.
+        * of pages in a single packet. If skb is scattered around
+        * more pages we try linearizing it.
         */
+
+check_size:
+       skb_length = skb->len;
+       head_room = skb_headroom(skb);
        num_data_pgs = netvsc_get_slots(skb) + 2;
-       if (num_data_pgs > MAX_PAGE_BUFFER_COUNT) {
-               netdev_err(net, "Packet too big: %u\n", skb->len);
-               dev_kfree_skb(skb);
-               net->stats.tx_dropped++;
-               return NETDEV_TX_OK;
+       if (num_data_pgs > MAX_PAGE_BUFFER_COUNT && linear) {
+               net_alert_ratelimited("packet too big: %u pages (%u bytes)\n",
+                                     num_data_pgs, skb->len);
+               ret = -EFAULT;
+               goto drop;
+       } else if (num_data_pgs > MAX_PAGE_BUFFER_COUNT) {
+               if (skb_linearize(skb)) {
+                       net_alert_ratelimited("failed to linearize skb\n");
+                       ret = -ENOMEM;
+                       goto drop;
+               }
+               linear = true;
+               goto check_size;
        }
 
-       pkt_sz = sizeof(struct hv_netvsc_packet) +
-                       sizeof(struct rndis_message) +
-                       NDIS_VLAN_PPI_SIZE + NDIS_CSUM_PPI_SIZE +
-                       NDIS_LSO_PPI_SIZE + NDIS_HASH_PPI_SIZE;
+       pkt_sz = sizeof(struct hv_netvsc_packet) + RNDIS_AND_PPI_SIZE;
 
        if (head_room < pkt_sz) {
                packet = kmalloc(pkt_sz, GFP_ATOMIC);
                if (!packet) {
                        /* out of memory, drop packet */
                        netdev_err(net, "unable to alloc hv_netvsc_packet\n");
-                       dev_kfree_skb(skb);
-                       net->stats.tx_dropped++;
-                       return NETDEV_TX_OK;
+                       ret = -ENOMEM;
+                       goto drop;
                }
                packet->part_of_skb = false;
        } else {
@@ -436,11 +446,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
        packet->rndis_msg = (struct rndis_message *)((unsigned long)packet +
                                sizeof(struct hv_netvsc_packet));
 
-       memset(packet->rndis_msg, 0, sizeof(struct rndis_message) +
-                                       NDIS_VLAN_PPI_SIZE +
-                                       NDIS_CSUM_PPI_SIZE +
-                                       NDIS_LSO_PPI_SIZE +
-                                       NDIS_HASH_PPI_SIZE);
+       memset(packet->rndis_msg, 0, RNDIS_AND_PPI_SIZE);
 
        /* Set the completion routine */
        packet->send_completion = netvsc_xmit_completion;
@@ -581,7 +587,7 @@ drop:
                net->stats.tx_bytes += skb_length;
                net->stats.tx_packets++;
        } else {
-               if (!packet->part_of_skb)
+               if (packet && !packet->part_of_skb)
                        kfree(packet);
                if (ret != -EAGAIN) {
                        dev_kfree_skb_any(skb);
@@ -872,9 +878,7 @@ static int netvsc_probe(struct hv_device *dev,
                return -ENOMEM;
 
        max_needed_headroom = sizeof(struct hv_netvsc_packet) +
-                               sizeof(struct rndis_message) +
-                               NDIS_VLAN_PPI_SIZE + NDIS_CSUM_PPI_SIZE +
-                               NDIS_LSO_PPI_SIZE + NDIS_HASH_PPI_SIZE;
+                             RNDIS_AND_PPI_SIZE;
 
        netif_carrier_off(net);
 
index a1604376aee1a1d9fb420e77e4d6791fc4719fa1..0d92efefd796c9b631beeca1b35ad9979cb36d2f 100644 (file)
@@ -47,8 +47,6 @@ struct rndis_request {
 
        /* Simplify allocation by having a netvsc packet inline */
        struct hv_netvsc_packet pkt;
-       /* Set 2 pages for rndis requests crossing page boundary */
-       struct hv_page_buffer buf[2];
 
        struct rndis_message request_msg;
        /*
index 5ad46f7f514f1332df5f42f6f7546aeef313da30..38026650c0387ecb101d085e24273bf24ded2783 100644 (file)
@@ -1173,7 +1173,7 @@ at86rf230_set_hw_addr_filt(struct ieee802154_hw *hw,
 }
 
 static int
-at86rf230_set_txpower(struct ieee802154_hw *hw, int db)
+at86rf230_set_txpower(struct ieee802154_hw *hw, s8 db)
 {
        struct at86rf230_local *lp = hw->priv;
 
index b5fecb49a0c6aef28e4cf886abae837ca5e52b40..577c9b071ad9e8568d955a39ce00eb185e52e186 100644 (file)
@@ -1672,7 +1672,8 @@ static void vxlan_build_gbp_hdr(struct vxlanhdr *vxh, u32 vxflags,
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
-static int vxlan6_xmit_skb(struct dst_entry *dst, struct sk_buff *skb,
+static int vxlan6_xmit_skb(struct dst_entry *dst, struct sock *sk,
+                          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,
@@ -1748,7 +1749,7 @@ static int vxlan6_xmit_skb(struct dst_entry *dst, struct sk_buff *skb,
 
        skb_set_inner_protocol(skb, htons(ETH_P_TEB));
 
-       udp_tunnel6_xmit_skb(dst, skb, dev, saddr, daddr, prio,
+       udp_tunnel6_xmit_skb(dst, sk, skb, dev, saddr, daddr, prio,
                             ttl, src_port, dst_port,
                             !!(vxflags & VXLAN_F_UDP_ZERO_CSUM6_TX));
        return 0;
@@ -1758,7 +1759,7 @@ err:
 }
 #endif
 
-int vxlan_xmit_skb(struct rtable *rt, struct sk_buff *skb,
+int vxlan_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
                   __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df,
                   __be16 src_port, __be16 dst_port,
                   struct vxlan_metadata *md, bool xnet, u32 vxflags)
@@ -1827,7 +1828,7 @@ int vxlan_xmit_skb(struct rtable *rt, struct sk_buff *skb,
 
        skb_set_inner_protocol(skb, htons(ETH_P_TEB));
 
-       return udp_tunnel_xmit_skb(rt, skb, src, dst, tos,
+       return udp_tunnel_xmit_skb(rt, sk, skb, src, dst, tos,
                                   ttl, df, src_port, dst_port, xnet,
                                   !(vxflags & VXLAN_F_UDP_CSUM));
 }
@@ -1882,6 +1883,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                           struct vxlan_rdst *rdst, bool did_rsc)
 {
        struct vxlan_dev *vxlan = netdev_priv(dev);
+       struct sock *sk = vxlan->vn_sock->sock->sk;
        struct rtable *rt = NULL;
        const struct iphdr *old_iph;
        struct flowi4 fl4;
@@ -1961,7 +1963,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                md.vni = htonl(vni << 8);
                md.gbp = skb->mark;
 
-               err = vxlan_xmit_skb(rt, skb, fl4.saddr,
+               err = vxlan_xmit_skb(rt, sk, skb, fl4.saddr,
                                     dst->sin.sin_addr.s_addr, tos, ttl, df,
                                     src_port, dst_port, &md,
                                     !net_eq(vxlan->net, dev_net(vxlan->dev)),
@@ -1975,7 +1977,6 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
 #if IS_ENABLED(CONFIG_IPV6)
        } else {
-               struct sock *sk = vxlan->vn_sock->sock->sk;
                struct dst_entry *ndst;
                struct flowi6 fl6;
                u32 flags;
@@ -2021,7 +2022,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                md.vni = htonl(vni << 8);
                md.gbp = skb->mark;
 
-               err = vxlan6_xmit_skb(ndst, skb, dev, &fl6.saddr, &fl6.daddr,
+               err = vxlan6_xmit_skb(ndst, sk, skb, dev, &fl6.saddr, &fl6.daddr,
                                      0, ttl, src_port, dst_port, &md,
                                      !net_eq(vxlan->net, dev_net(vxlan->dev)),
                                      vxlan->flags);
@@ -2255,11 +2256,8 @@ static int vxlan_stop(struct net_device *dev)
        int ret = 0;
 
        if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip) &&
-           !vxlan_group_used(vn, vxlan)) {
+           !vxlan_group_used(vn, vxlan))
                ret = vxlan_igmp_leave(vxlan);
-               if (ret)
-                       return ret;
-       }
 
        del_timer_sync(&vxlan->age_timer);
 
index 88d121d43c08bedf2efc3265964188cf2b7f94a7..bcfa01add7cc479ca25d5f2f198f9623fc966415 100644 (file)
@@ -579,6 +579,7 @@ static int cosa_probe(int base, int irq, int dma)
                /* Register the network interface */
                if (!(chan->netdev = alloc_hdlcdev(chan))) {
                        pr_warn("%s: alloc_hdlcdev failed\n", chan->name);
+                       err = -ENOMEM;
                        goto err_hdlcdev;
                }
                dev_to_hdlc(chan->netdev)->attach = cosa_net_attach;
index bea0f313a7a80cbd7528795ff04e67162f77f347..317bc79cc8b9b22eace33116de7504c216b60a39 100644 (file)
@@ -850,6 +850,7 @@ static int lmc_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        dev = alloc_hdlcdev(sc);
        if (!dev) {
                printk(KERN_ERR "lmc:alloc_netdev for device failed\n");
+               err = -ENOMEM;
                goto err_hdlcdev;
        }
 
index 86d46c196966f0e65b3c963011b124318f592f8b..284706798c71deda9ecc8a400bcff6e26b8999c5 100644 (file)
@@ -69,9 +69,15 @@ void ath_fill_led_pin(struct ath_softc *sc)
 {
        struct ath_hw *ah = sc->sc_ah;
 
-       if (AR_SREV_9100(ah) || (ah->led_pin >= 0))
+       if (AR_SREV_9100(ah))
                return;
 
+       if (ah->led_pin >= 0) {
+               if (!((1 << ah->led_pin) & AR_GPIO_OE_OUT_MASK))
+                       ath9k_hw_request_gpio(ah, ah->led_pin, "ath9k-led");
+               return;
+       }
+
        if (AR_SREV_9287(ah))
                ah->led_pin = ATH_LED_PIN_9287;
        else if (AR_SREV_9485(sc->sc_ah))
index 5cdbdb0383710d024397af69abe35738c05820ea..5e15e8e10ed39f0b605783176fe260faa387d083 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/time.h>
 #include <linux/bitops.h>
 #include <linux/etherdevice.h>
+#include <linux/gpio.h>
 #include <asm/unaligned.h>
 
 #include "hw.h"
@@ -2711,11 +2712,23 @@ void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val)
        if (AR_SREV_9271(ah))
                val = ~val;
 
-       REG_RMW(ah, AR_GPIO_IN_OUT, ((val & 1) << gpio),
-               AR_GPIO_BIT(gpio));
+       if ((1 << gpio) & AR_GPIO_OE_OUT_MASK)
+               REG_RMW(ah, AR_GPIO_IN_OUT, ((val & 1) << gpio),
+                       AR_GPIO_BIT(gpio));
+       else
+               gpio_set_value(gpio, val & 1);
 }
 EXPORT_SYMBOL(ath9k_hw_set_gpio);
 
+void ath9k_hw_request_gpio(struct ath_hw *ah, u32 gpio, const char *label)
+{
+       if (gpio >= ah->caps.num_gpio_pins)
+               return;
+
+       gpio_request_one(gpio, GPIOF_DIR_OUT | GPIOF_INIT_LOW, label);
+}
+EXPORT_SYMBOL(ath9k_hw_request_gpio);
+
 void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna)
 {
        REG_WRITE(ah, AR_DEF_ANTENNA, (antenna & 0x7));
index 92fab1a54697e6ed77cea915c205aeae293363b6..c1d2d0340febadb445bed891754f0565025f8f09 100644 (file)
@@ -1024,6 +1024,7 @@ u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio);
 void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio,
                         u32 ah_signal_type);
 void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val);
+void ath9k_hw_request_gpio(struct ath_hw *ah, u32 gpio, const char *label);
 void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna);
 
 /* General Operation */
index 1234399a43dd78692a52507a78185729a219a329..caba54ddad25c3dc4d7abd932e48602c4c885b94 100644 (file)
 
 #define AR_SREV_9550(_ah) \
        (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9550))
+#define AR_SREV_9550_OR_LATER(_ah) \
+       (((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9550))
 
 #define AR_SREV_9580(_ah) \
        (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9580) && \
@@ -1128,6 +1130,8 @@ enum {
 
 #define AR_GPIO_OE_OUT                           (AR_SREV_9340(ah) ? 0x4030 : \
                                                  (AR_SREV_9300_20_OR_LATER(ah) ? 0x4050 : 0x404c))
+#define AR_GPIO_OE_OUT_MASK                     (AR_SREV_9550_OR_LATER(ah) ? \
+                                                 0x0000000F : 0xFFFFFFFF)
 #define AR_GPIO_OE_OUT_DRV                       0x3
 #define AR_GPIO_OE_OUT_DRV_NO                    0x0
 #define AR_GPIO_OE_OUT_DRV_LOW                   0x1
index 67a2f8c888292162ad8329dfb5c5131e9f347d69..ca533b4321bddc9ee7621ca4e3dbbe5f03377236 100644 (file)
@@ -227,7 +227,7 @@ static void ath9k_wmi_ctrl_rx(void *priv, struct sk_buff *skb,
 
        /* Check if there has been a timeout. */
        spin_lock(&wmi->wmi_lock);
-       if (cmd_id != wmi->last_cmd_id) {
+       if (be16_to_cpu(hdr->seq_no) != wmi->last_seq_id) {
                spin_unlock(&wmi->wmi_lock);
                goto free_skb;
        }
@@ -275,11 +275,16 @@ static int ath9k_wmi_cmd_issue(struct wmi *wmi,
                               enum wmi_cmd_id cmd, u16 len)
 {
        struct wmi_cmd_hdr *hdr;
+       unsigned long flags;
 
        hdr = (struct wmi_cmd_hdr *) skb_push(skb, sizeof(struct wmi_cmd_hdr));
        hdr->command_id = cpu_to_be16(cmd);
        hdr->seq_no = cpu_to_be16(++wmi->tx_seq_id);
 
+       spin_lock_irqsave(&wmi->wmi_lock, flags);
+       wmi->last_seq_id = wmi->tx_seq_id;
+       spin_unlock_irqrestore(&wmi->wmi_lock, flags);
+
        return htc_send_epid(wmi->htc, skb, wmi->ctrl_epid);
 }
 
@@ -295,7 +300,6 @@ int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id,
        struct sk_buff *skb;
        u8 *data;
        int time_left, ret = 0;
-       unsigned long flags;
 
        if (ah->ah_flags & AH_UNPLUGGED)
                return 0;
@@ -323,10 +327,6 @@ int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id,
        wmi->cmd_rsp_buf = rsp_buf;
        wmi->cmd_rsp_len = rsp_len;
 
-       spin_lock_irqsave(&wmi->wmi_lock, flags);
-       wmi->last_cmd_id = cmd_id;
-       spin_unlock_irqrestore(&wmi->wmi_lock, flags);
-
        ret = ath9k_wmi_cmd_issue(wmi, skb, cmd_id, cmd_len);
        if (ret)
                goto out;
index aa84a335289a59841f3623e9203fac52aa3d13cb..380175d5ecd7a7d54e22d27fb434efa65dfcdd10 100644 (file)
@@ -151,7 +151,7 @@ struct wmi {
        enum htc_endpoint_id ctrl_epid;
        struct mutex op_mutex;
        struct completion cmd_wait;
-       enum wmi_cmd_id last_cmd_id;
+       u16 last_seq_id;
        struct sk_buff_head wmi_event_queue;
        struct tasklet_struct wmi_event_tasklet;
        u16 tx_seq_id;
index 1d7982afc0ad6af6b1b32f92ae07fb9ef694f2e1..6837064908bed38f05c4d98642c50c82d9cf984d 100644 (file)
@@ -553,7 +553,7 @@ static bool b43_dma_mapping_error(struct b43_dmaring *ring,
                                  size_t buffersize, bool dma_to_device)
 {
        if (unlikely(dma_mapping_error(ring->dev->dev->dma_dev, addr)))
-               return 1;
+               return true;
 
        switch (ring->type) {
        case B43_DMA_30BIT:
@@ -571,13 +571,13 @@ static bool b43_dma_mapping_error(struct b43_dmaring *ring,
        }
 
        /* The address is OK. */
-       return 0;
+       return false;
 
 address_error:
        /* We can't support this address. Unmap it again. */
        unmap_descbuffer(ring, addr, buffersize, dma_to_device);
 
-       return 1;
+       return true;
 }
 
 static bool b43_rx_buffer_is_poisoned(struct b43_dmaring *ring, struct sk_buff *skb)
@@ -1099,16 +1099,16 @@ static bool b43_dma_translation_in_low_word(struct b43_wldev *dev,
                                            enum b43_dmatype type)
 {
        if (type != B43_DMA_64BIT)
-               return 1;
+               return true;
 
 #ifdef CONFIG_B43_SSB
        if (dev->dev->bus_type == B43_BUS_SSB &&
            dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI &&
            !(pci_is_pcie(dev->dev->sdev->bus->host_pci) &&
              ssb_read32(dev->dev->sdev, SSB_TMSHIGH) & SSB_TMSHIGH_DMA64))
-                       return 1;
+                       return true;
 #endif
-       return 0;
+       return false;
 }
 
 int b43_dma_init(struct b43_wldev *dev)
index b2ed1795130bb0d7e1f10f95ab9775743b5f276b..f9dd892b9f27e9978ea35e8514f457d037b2d9b1 100644 (file)
@@ -427,7 +427,7 @@ static bool b43legacy_dma_mapping_error(struct b43legacy_dmaring *ring,
                                         bool dma_to_device)
 {
        if (unlikely(dma_mapping_error(ring->dev->dev->dma_dev, addr)))
-               return 1;
+               return true;
 
        switch (ring->type) {
        case B43legacy_DMA_30BIT:
@@ -441,13 +441,13 @@ static bool b43legacy_dma_mapping_error(struct b43legacy_dmaring *ring,
        }
 
        /* The address is OK. */
-       return 0;
+       return false;
 
 address_error:
        /* We can't support this address. Unmap it again. */
        unmap_descbuffer(ring, addr, buffersize, dma_to_device);
 
-       return 1;
+       return true;
 }
 
 static int setup_rx_descbuffer(struct b43legacy_dmaring *ring,
index c4559bcbc707aa00ff4f047876683451c8382619..7c1bdbc0256912568afd455cec0e429006ab8318 100644 (file)
@@ -32,7 +32,7 @@ bool b43legacy_is_hw_radio_enabled(struct b43legacy_wldev *dev)
        if (dev->dev->id.revision >= 3) {
                if (!(b43legacy_read32(dev, B43legacy_MMIO_RADIO_HWENABLED_HI)
                      & B43legacy_MMIO_RADIO_HWENABLED_HI_MASK))
-                       return 1;
+                       return true;
        } else {
                /* To prevent CPU fault on PPC, do not read a register
                 * unless the interface is started; however, on resume
@@ -40,12 +40,12 @@ bool b43legacy_is_hw_radio_enabled(struct b43legacy_wldev *dev)
                 * that happens, unconditionally return TRUE.
                 */
                if (b43legacy_status(dev) < B43legacy_STAT_STARTED)
-                       return 1;
+                       return true;
                if (b43legacy_read16(dev, B43legacy_MMIO_RADIO_HWENABLED_LO)
                    & B43legacy_MMIO_RADIO_HWENABLED_LO_MASK)
-                       return 1;
+                       return true;
        }
-       return 0;
+       return false;
 }
 
 /* The poll callback for the hardware button. */
index 941b1e41f3664cc0ea7f5708a337e9edceb7271b..1c4e9dd57960f133ba20661eef5c47ea24336fad 100644 (file)
@@ -2949,5 +2949,5 @@ bool wlc_phy_txpower_ipa_ison(struct brcms_phy_pub *ppi)
        if (ISNPHY(pi))
                return wlc_phy_n_txpower_ipa_ison(pi);
        else
-               return 0;
+               return false;
 }
index 5f1366234a0dc3e906d085b927b2360b60f05a66..93d4cde0eb3135bbb4334a5ba482437744c09592 100644 (file)
@@ -4999,7 +4999,7 @@ void wlc_2064_vco_cal(struct brcms_phy *pi)
 bool wlc_phy_tpc_isenabled_lcnphy(struct brcms_phy *pi)
 {
        if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi))
-               return 0;
+               return false;
        else
                return (LCNPHY_TX_PWR_CTRL_HW ==
                        wlc_lcnphy_get_tx_pwr_ctrl((pi)));
index 9c396a42aec8ba708fcab1e81b69567c23f055ba..ce6321b7d24156269fd553f4409078b1c3fb24df 100644 (file)
@@ -94,8 +94,8 @@
        IWL8000_FW_PRE "-" __stringify(api) ".ucode"
 
 #define NVM_HW_SECTION_NUM_FAMILY_8000         10
-#define DEFAULT_NVM_FILE_FAMILY_8000A          "iwl_nvm_8000.bin"
-#define DEFAULT_NVM_FILE_FAMILY_8000           "iwl_nvm_8000B.bin"
+#define DEFAULT_NVM_FILE_FAMILY_8000B          "nvmData-8000B"
+#define DEFAULT_NVM_FILE_FAMILY_8000C          "nvmData-8000C"
 
 /* Max SDIO RX aggregation size of the ADDBA request/response */
 #define MAX_RX_AGG_SIZE_8260_SDIO      28
@@ -177,8 +177,8 @@ const struct iwl_cfg iwl8260_2ac_sdio_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,
-       .default_nvm_file_8000A = DEFAULT_NVM_FILE_FAMILY_8000A,
+       .default_nvm_file_B_step = DEFAULT_NVM_FILE_FAMILY_8000B,
+       .default_nvm_file_C_step = DEFAULT_NVM_FILE_FAMILY_8000C,
        .max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO,
        .disable_dummy_notification = true,
        .max_ht_ampdu_exponent  = MAX_HT_AMPDU_EXPONENT_8260_SDIO,
@@ -192,8 +192,8 @@ const struct iwl_cfg iwl4165_2ac_sdio_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,
-       .default_nvm_file_8000A = DEFAULT_NVM_FILE_FAMILY_8000A,
+       .default_nvm_file_B_step = DEFAULT_NVM_FILE_FAMILY_8000B,
+       .default_nvm_file_C_step = DEFAULT_NVM_FILE_FAMILY_8000C,
        .max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO,
        .bt_shared_single_ant = true,
        .disable_dummy_notification = true,
index 4b190d98a1ec0c3f98427c3efaab9e0162eb80f3..3f33f753ce2f9ce4a81ef1247d05fb517aeec0ec 100644 (file)
@@ -92,9 +92,9 @@ static inline bool iwl_has_secure_boot(u32 hw_rev,
 {
        /* return 1 only for family 8000 B0 */
        if ((family == IWL_DEVICE_FAMILY_8000) && (hw_rev & 0xC))
-               return 1;
+               return true;
 
-       return 0;
+       return false;
 }
 
 /*
@@ -228,7 +228,7 @@ struct iwl_pwr_tx_backoff {
 
 /**
  * struct iwl_cfg
- * @name: Offical name of the device
+ * @name: Official name of the device
  * @fw_name_pre: Firmware filename prefix. The api version and extension
  *     (.ucode) will be added to filename before loading from disk. The
  *     filename is constructed as fw_name_pre<api>.ucode.
@@ -303,8 +303,8 @@ struct iwl_cfg {
        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;
-       const char *default_nvm_file_8000A;
+       const char *default_nvm_file_B_step;
+       const char *default_nvm_file_C_step;
        unsigned int max_rx_agg_size;
        bool disable_dummy_notification;
        unsigned int max_tx_agg_size;
index aefdd9b7c1051f70abcf85597cf6966606f99b7f..7267152e7dc7705aaa514237c17ed908b9e4919d 100644 (file)
@@ -145,7 +145,7 @@ static struct iwlwifi_opmode_table {
 #define IWL_DEFAULT_SCAN_CHANNELS 40
 
 /*
- * struct fw_sec: Just for the image parsing proccess.
+ * struct fw_sec: Just for the image parsing process.
  * For the fw storage we are using struct fw_desc.
  */
 struct fw_sec {
@@ -241,16 +241,10 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first)
         * previous name and uses the new format.
         */
        if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
-               char rev_step[2] = {
-                       'A' + CSR_HW_REV_STEP(drv->trans->hw_rev), 0
-               };
-
-               /* A-step doesn't have an indication */
-               if (CSR_HW_REV_STEP(drv->trans->hw_rev) == SILICON_A_STEP)
-                       rev_step[0] = 0;
+               char rev_step = 'A' + CSR_HW_REV_STEP(drv->trans->hw_rev);
 
                snprintf(drv->firmware_name, sizeof(drv->firmware_name),
-                        "%s%s-%s.ucode", name_pre, rev_step, tag);
+                        "%s%c-%s.ucode", name_pre, rev_step, tag);
        }
 
        IWL_DEBUG_INFO(drv, "attempting to load firmware %s'%s'\n",
@@ -1108,6 +1102,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
        const unsigned int api_max = drv->cfg->ucode_api_max;
        unsigned int api_ok = drv->cfg->ucode_api_ok;
        const unsigned int api_min = drv->cfg->ucode_api_min;
+       size_t trigger_tlv_sz[FW_DBG_TRIGGER_MAX];
        u32 api_ver;
        int i;
        bool load_module = false;
@@ -1227,8 +1222,37 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
                }
        }
 
+       memset(&trigger_tlv_sz, 0xff, sizeof(trigger_tlv_sz));
+
+       trigger_tlv_sz[FW_DBG_TRIGGER_MISSED_BEACONS] =
+               sizeof(struct iwl_fw_dbg_trigger_missed_bcon);
+       trigger_tlv_sz[FW_DBG_TRIGGER_CHANNEL_SWITCH] = 0;
+       trigger_tlv_sz[FW_DBG_TRIGGER_FW_NOTIF] =
+               sizeof(struct iwl_fw_dbg_trigger_cmd);
+       trigger_tlv_sz[FW_DBG_TRIGGER_MLME] =
+               sizeof(struct iwl_fw_dbg_trigger_mlme);
+       trigger_tlv_sz[FW_DBG_TRIGGER_STATS] =
+               sizeof(struct iwl_fw_dbg_trigger_stats);
+       trigger_tlv_sz[FW_DBG_TRIGGER_RSSI] =
+               sizeof(struct iwl_fw_dbg_trigger_low_rssi);
+       trigger_tlv_sz[FW_DBG_TRIGGER_TXQ_TIMERS] =
+               sizeof(struct iwl_fw_dbg_trigger_txq_timer);
+       trigger_tlv_sz[FW_DBG_TRIGGER_TIME_EVENT] =
+               sizeof(struct iwl_fw_dbg_trigger_time_event);
+
        for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_trigger_tlv); i++) {
                if (pieces->dbg_trigger_tlv[i]) {
+                       /*
+                        * If the trigger isn't long enough, WARN and exit.
+                        * Someone is trying to debug something and he won't
+                        * be able to catch the bug he is trying to chase.
+                        * We'd better be noisy to be sure he knows what's
+                        * going on.
+                        */
+                       if (WARN_ON(pieces->dbg_trigger_tlv_len[i] <
+                                   (trigger_tlv_sz[i] +
+                                    sizeof(struct iwl_fw_dbg_trigger_tlv))))
+                               goto out_free_fw;
                        drv->fw.dbg_trigger_tlv_len[i] =
                                pieces->dbg_trigger_tlv_len[i];
                        drv->fw.dbg_trigger_tlv[i] =
index 67a3a241b331cd08180bbfe888d7707ad7dc149b..cda746b33db1f60e228cde9c90f5e040847cc0ae 100644 (file)
@@ -123,7 +123,7 @@ struct iwl_cfg;
  * starts the driver: fetches the firmware. This should be called by bus
  * specific system flows implementations. For example, the bus specific probe
  * function should do bus related operations only, and then call to this
- * function. It returns the driver object or %NULL if an error occured.
+ * function. It returns the driver object or %NULL if an error occurred.
  */
 struct iwl_drv *iwl_drv_start(struct iwl_trans *trans,
                              const struct iwl_cfg *cfg);
index 25d0105741dbee2b1a292d10c1b6cc56ce3f41d5..219ca8acca6281f02abb2c44abc066e730360848 100644 (file)
@@ -248,7 +248,7 @@ static int iwl_read_otp_word(struct iwl_trans *trans, u16 addr,
        otpgp = iwl_read32(trans, CSR_OTP_GP_REG);
        if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) {
                /* stop in this case */
-               /* set the uncorrectable OTP ECC bit for acknowledgement */
+               /* set the uncorrectable OTP ECC bit for acknowledgment */
                iwl_set_bit(trans, CSR_OTP_GP_REG,
                            CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK);
                IWL_ERR(trans, "Uncorrectable OTP ECC error, abort OTP read\n");
@@ -256,7 +256,7 @@ static int iwl_read_otp_word(struct iwl_trans *trans, u16 addr,
        }
        if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) {
                /* continue in this case */
-               /* set the correctable OTP ECC bit for acknowledgement */
+               /* set the correctable OTP ECC bit for acknowledgment */
                iwl_set_bit(trans, CSR_OTP_GP_REG,
                            CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK);
                IWL_ERR(trans, "Correctable OTP ECC error, continue read\n");
index 1f7f15eb86da208df591ae8e7ca27e52c5921acf..d45dc021cda2c0715b8d7e740ff90b46589ae141 100644 (file)
@@ -445,7 +445,7 @@ static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl)
 #define RX_LOW_WATERMARK 8
 
 /**
- * struct iwl_rb_status - reseve buffer status
+ * struct iwl_rb_status - reserve buffer status
  *     host memory mapped FH registers
  * @closed_rb_num [0:11] - Indicates the index of the RB which was closed
  * @closed_fr_num [0:11] - Indicates the index of the RX Frame which was closed
index 37b38a585dd182f8fa1c44256f4a16ecc008d3df..251bf8dc4a12133b4ae77757170436c67219afc9 100644 (file)
@@ -183,7 +183,7 @@ struct iwl_fw_error_dump_info {
  * struct iwl_fw_error_dump_fw_mon - FW monitor data
  * @fw_mon_wr_ptr: the position of the write pointer in the cyclic buffer
  * @fw_mon_base_ptr: base pointer of the data
- * @fw_mon_cycle_cnt: number of wrap arounds
+ * @fw_mon_cycle_cnt: number of wraparounds
  * @reserved: for future use
  * @data: captured data
  */
@@ -246,10 +246,14 @@ iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data)
  * @FW_DBG_TRIGGER_CHANNEL_SWITCH: trigger log collection upon channel switch.
  * @FW_DBG_TRIGGER_FW_NOTIF: trigger log collection when the firmware sends a
  *     command response or a notification.
- * @FW_DB_TRIGGER_RESERVED: reserved
+ * @FW_DBG_TRIGGER_MLME: trigger log collection upon MLME event.
  * @FW_DBG_TRIGGER_STATS: trigger log collection upon statistics threshold.
  * @FW_DBG_TRIGGER_RSSI: trigger log collection when the rssi of the beacon
  *     goes below a threshold.
+ * @FW_DBG_TRIGGER_TXQ_TIMERS: configures the timers for the Tx queue hang
+ *     detection.
+ * @FW_DBG_TRIGGER_TIME_EVENT: trigger log collection upon time events related
+ *     events.
  */
 enum iwl_fw_dbg_trigger {
        FW_DBG_TRIGGER_INVALID = 0,
@@ -258,9 +262,11 @@ enum iwl_fw_dbg_trigger {
        FW_DBG_TRIGGER_MISSED_BEACONS,
        FW_DBG_TRIGGER_CHANNEL_SWITCH,
        FW_DBG_TRIGGER_FW_NOTIF,
-       FW_DB_TRIGGER_RESERVED,
+       FW_DBG_TRIGGER_MLME,
        FW_DBG_TRIGGER_STATS,
        FW_DBG_TRIGGER_RSSI,
+       FW_DBG_TRIGGER_TXQ_TIMERS,
+       FW_DBG_TRIGGER_TIME_EVENT,
 
        /* must be last */
        FW_DBG_TRIGGER_MAX,
index 291a3382aa3fc5596e243ff3a4e93d7cd44557ba..bfdf3faa6c470dafbd9a66672b2f55e38b0872bc 100644 (file)
@@ -191,7 +191,7 @@ struct iwl_ucode_capa {
  * enum iwl_ucode_tlv_flag - ucode API flags
  * @IWL_UCODE_TLV_FLAGS_PAN: This is PAN capable microcode; this previously
  *     was a separate TLV but moved here to save space.
- * @IWL_UCODE_TLV_FLAGS_NEWSCAN: new uCode scan behaviour on hidden SSID,
+ * @IWL_UCODE_TLV_FLAGS_NEWSCAN: new uCode scan behavior on hidden SSID,
  *     treats good CRC threshold as a boolean
  * @IWL_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w).
  * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P.
@@ -290,6 +290,10 @@ enum iwl_ucode_tlv_api {
  * @IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command
  * @IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS: support radio and beacon statistics
  * @IWL_UCODE_TLV_CAPA_BT_COEX_PLCR: enabled BT Coex packet level co-running
+ * @IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC: ucode supports LAR updates with different
+ *     sources for the MCC. This TLV bit is a future replacement to
+ *     IWL_UCODE_TLV_API_WIFI_MCC_UPDATE. When either is set, multi-source LAR
+ *     is supported.
  * @IWL_UCODE_TLV_CAPA_BT_COEX_RRC: supports BT Coex RRC
  */
 enum iwl_ucode_tlv_capa {
@@ -307,6 +311,7 @@ enum iwl_ucode_tlv_capa {
        IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT              = BIT(18),
        IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS           = BIT(22),
        IWL_UCODE_TLV_CAPA_BT_COEX_PLCR                 = BIT(28),
+       IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC                = BIT(29),
        IWL_UCODE_TLV_CAPA_BT_COEX_RRC                  = BIT(30),
 };
 
@@ -573,6 +578,84 @@ struct iwl_fw_dbg_trigger_low_rssi {
        __le32 rssi;
 } __packed;
 
+/**
+ * struct iwl_fw_dbg_trigger_mlme - configures trigger for mlme events
+ * @stop_auth_denied: number of denied authentication to collect
+ * @stop_auth_timeout: number of authentication timeout to collect
+ * @stop_rx_deauth: number of Rx deauth before to collect
+ * @stop_tx_deauth: number of Tx deauth before to collect
+ * @stop_assoc_denied: number of denied association to collect
+ * @stop_assoc_timeout: number of association timeout to collect
+ * @stop_connection_loss: number of connection loss to collect
+ * @start_auth_denied: number of denied authentication to start recording
+ * @start_auth_timeout: number of authentication timeout to start recording
+ * @start_rx_deauth: number of Rx deauth to start recording
+ * @start_tx_deauth: number of Tx deauth to start recording
+ * @start_assoc_denied: number of denied association to start recording
+ * @start_assoc_timeout: number of association timeout to start recording
+ * @start_connection_loss: number of connection loss to start recording
+ */
+struct iwl_fw_dbg_trigger_mlme {
+       u8 stop_auth_denied;
+       u8 stop_auth_timeout;
+       u8 stop_rx_deauth;
+       u8 stop_tx_deauth;
+
+       u8 stop_assoc_denied;
+       u8 stop_assoc_timeout;
+       u8 stop_connection_loss;
+       u8 reserved;
+
+       u8 start_auth_denied;
+       u8 start_auth_timeout;
+       u8 start_rx_deauth;
+       u8 start_tx_deauth;
+
+       u8 start_assoc_denied;
+       u8 start_assoc_timeout;
+       u8 start_connection_loss;
+       u8 reserved2;
+} __packed;
+
+/**
+ * struct iwl_fw_dbg_trigger_txq_timer - configures the Tx queue's timer
+ * @command_queue: timeout for the command queue in ms
+ * @bss: timeout for the queues of a BSS (except for TDLS queues) in ms
+ * @softap: timeout for the queues of a softAP in ms
+ * @p2p_go: timeout for the queues of a P2P GO in ms
+ * @p2p_client: timeout for the queues of a P2P client in ms
+ * @p2p_device: timeout for the queues of a P2P device in ms
+ * @ibss: timeout for the queues of an IBSS in ms
+ * @tdls: timeout for the queues of a TDLS station in ms
+ */
+struct iwl_fw_dbg_trigger_txq_timer {
+       __le32 command_queue;
+       __le32 bss;
+       __le32 softap;
+       __le32 p2p_go;
+       __le32 p2p_client;
+       __le32 p2p_device;
+       __le32 ibss;
+       __le32 tdls;
+       __le32 reserved[4];
+} __packed;
+
+/**
+ * struct iwl_fw_dbg_trigger_time_event - configures a time event trigger
+ * time_Events: a list of tuples <id, action_bitmap>. The driver will issue a
+ *     trigger each time a time event notification that relates to time event
+ *     id with one of the actions in the bitmap is received and
+ *     BIT(notif->status) is set in status_bitmap.
+ *
+ */
+struct iwl_fw_dbg_trigger_time_event {
+       struct {
+               __le32 id;
+               __le32 action_bitmap;
+               __le32 status_bitmap;
+       } __packed time_events[16];
+} __packed;
+
 /**
  * struct iwl_fw_dbg_conf_tlv - a TLV that describes a debug configuration.
  * @id: conf id
index 78cac43e2bcd163997006065d112b1a61a6f6643..27c66e477833f7747d621fabe489b5d687a3120d 100644 (file)
@@ -186,21 +186,14 @@ 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) ||
-           (CSR_HW_REV_STEP(trans->hw_rev) == SILICON_A_STEP)) {
+       if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
                iwl_write_prph(trans, DEVICE_SET_NMI_REG,
                               DEVICE_SET_NMI_VAL_DRV);
                iwl_write_prph(trans, DEVICE_SET_NMI_REG,
                               DEVICE_SET_NMI_VAL_HW);
        } else {
-               iwl_write_prph(trans, DEVICE_SET_NMI_8000B_REG,
-                              DEVICE_SET_NMI_8000B_VAL);
+               iwl_write_prph(trans, DEVICE_SET_NMI_8000_REG,
+                              DEVICE_SET_NMI_8000_VAL);
                iwl_write_prph(trans, DEVICE_SET_NMI_REG,
                               DEVICE_SET_NMI_VAL_DRV);
        }
index 774637746427ccce154a236b186fa7c79ffdc514..83903a5025c2e69779554e7bcf980aff48b3d080 100644 (file)
@@ -99,14 +99,9 @@ enum family_8000_nvm_offsets {
        /* NVM SW-Section offset (in words) definitions */
        NVM_SW_SECTION_FAMILY_8000 = 0x1C0,
        NVM_VERSION_FAMILY_8000 = 0,
-       RADIO_CFG_FAMILY_8000 = 2,
-       SKU_FAMILY_8000 = 4,
-       N_HW_ADDRS_FAMILY_8000 = 5,
-
-       /* NVM PHY-SKU-Section offset (in words) for B0 */
-       RADIO_CFG_FAMILY_8000_B0 = 0,
-       SKU_FAMILY_8000_B0 = 2,
-       N_HW_ADDRS_FAMILY_8000_B0 = 3,
+       RADIO_CFG_FAMILY_8000 = 0,
+       SKU_FAMILY_8000 = 2,
+       N_HW_ADDRS_FAMILY_8000 = 3,
 
        /* NVM REGULATORY -Section offset (in words) definitions */
        NVM_CHANNELS_FAMILY_8000 = 0,
@@ -446,22 +441,16 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
                            n_used, n_channels);
 }
 
-static int iwl_get_sku(const struct iwl_cfg *cfg,
-                      const __le16 *nvm_sw, const __le16 *phy_sku,
-                      bool is_family_8000_a_step)
+static int iwl_get_sku(const struct iwl_cfg *cfg, const __le16 *nvm_sw,
+                      const __le16 *phy_sku)
 {
        if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
                return le16_to_cpup(nvm_sw + SKU);
 
-       if (!is_family_8000_a_step)
-               return le32_to_cpup((__le32 *)(phy_sku +
-                                              SKU_FAMILY_8000_B0));
-       else
-               return le32_to_cpup((__le32 *)(nvm_sw + SKU_FAMILY_8000));
+       return le32_to_cpup((__le32 *)(phy_sku + SKU_FAMILY_8000));
 }
 
-static int iwl_get_nvm_version(const struct iwl_cfg *cfg,
-                              const __le16 *nvm_sw)
+static int iwl_get_nvm_version(const struct iwl_cfg *cfg, const __le16 *nvm_sw)
 {
        if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
                return le16_to_cpup(nvm_sw + NVM_VERSION);
@@ -470,35 +459,24 @@ static int iwl_get_nvm_version(const struct iwl_cfg *cfg,
                                               NVM_VERSION_FAMILY_8000));
 }
 
-static int iwl_get_radio_cfg(const struct iwl_cfg *cfg,
-                            const __le16 *nvm_sw, const __le16 *phy_sku,
-                            bool is_family_8000_a_step)
+static int iwl_get_radio_cfg(const struct iwl_cfg *cfg, const __le16 *nvm_sw,
+                            const __le16 *phy_sku)
 {
        if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
                return le16_to_cpup(nvm_sw + RADIO_CFG);
 
-       if (!is_family_8000_a_step)
-               return le32_to_cpup((__le32 *)(phy_sku +
-                                              RADIO_CFG_FAMILY_8000_B0));
-       else
-               return le32_to_cpup((__le32 *)(nvm_sw + RADIO_CFG_FAMILY_8000));
+       return le32_to_cpup((__le32 *)(nvm_sw + RADIO_CFG_FAMILY_8000));
 
 }
 
-static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg,
-                             const __le16 *nvm_sw, bool is_family_8000_a_step)
+static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg, const __le16 *nvm_sw)
 {
        int n_hw_addr;
 
        if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
                return le16_to_cpup(nvm_sw + N_HW_ADDRS);
 
-       if (!is_family_8000_a_step)
-               n_hw_addr = le32_to_cpup((__le32 *)(nvm_sw +
-                                                   N_HW_ADDRS_FAMILY_8000_B0));
-       else
-               n_hw_addr = le32_to_cpup((__le32 *)(nvm_sw +
-                                                   N_HW_ADDRS_FAMILY_8000));
+       n_hw_addr = le32_to_cpup((__le32 *)(nvm_sw + N_HW_ADDRS_FAMILY_8000));
 
        return n_hw_addr & N_HW_ADDR_MASK;
 }
@@ -594,8 +572,7 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
                   const __le16 *nvm_hw, const __le16 *nvm_sw,
                   const __le16 *nvm_calib, const __le16 *regulatory,
                   const __le16 *mac_override, const __le16 *phy_sku,
-                  u8 tx_chains, u8 rx_chains,
-                  bool lar_fw_supported, bool is_family_8000_a_step,
+                  u8 tx_chains, u8 rx_chains, bool lar_fw_supported,
                   u32 mac_addr0, u32 mac_addr1)
 {
        struct iwl_nvm_data *data;
@@ -618,15 +595,14 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
 
        data->nvm_version = iwl_get_nvm_version(cfg, nvm_sw);
 
-       radio_cfg =
-               iwl_get_radio_cfg(cfg, nvm_sw, phy_sku, is_family_8000_a_step);
+       radio_cfg = iwl_get_radio_cfg(cfg, nvm_sw, phy_sku);
        iwl_set_radio_cfg(cfg, data, radio_cfg);
        if (data->valid_tx_ant)
                tx_chains &= data->valid_tx_ant;
        if (data->valid_rx_ant)
                rx_chains &= data->valid_rx_ant;
 
-       sku = iwl_get_sku(cfg, nvm_sw, phy_sku, is_family_8000_a_step);
+       sku = iwl_get_sku(cfg, nvm_sw, phy_sku);
        data->sku_cap_band_24GHz_enable = sku & NVM_SKU_CAP_BAND_24GHZ;
        data->sku_cap_band_52GHz_enable = sku & NVM_SKU_CAP_BAND_52GHZ;
        data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE;
@@ -635,8 +611,7 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
        data->sku_cap_11ac_enable = data->sku_cap_11n_enable &&
                                    (sku & NVM_SKU_CAP_11AC_ENABLE);
 
-       data->n_hw_addrs =
-               iwl_get_n_hw_addrs(cfg, nvm_sw, is_family_8000_a_step);
+       data->n_hw_addrs = iwl_get_n_hw_addrs(cfg, nvm_sw);
 
        if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
                /* Checking for required sections */
index c995d2cee3f691c6dbe2c3592856d155d4e98f90..822ba52e0e5af537e42262ee4106af53d0b37f04 100644 (file)
@@ -78,8 +78,7 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
                   const __le16 *nvm_hw, const __le16 *nvm_sw,
                   const __le16 *nvm_calib, const __le16 *regulatory,
                   const __le16 *mac_override, const __le16 *phy_sku,
-                  u8 tx_chains, u8 rx_chains,
-                  bool lar_fw_supported, bool is_family_8000_a_step,
+                  u8 tx_chains, u8 rx_chains, bool lar_fw_supported,
                   u32 mac_addr0, u32 mac_addr1);
 
 /**
index 17de6d46222ae7c803605017c066453d73076f01..ce1cdd7604e8a156cf7356466b9232bdc176d9db 100644 (file)
@@ -94,7 +94,7 @@ struct iwl_cfg;
  * The operational mode has a very simple life cycle.
  *
  *     1) The driver layer (iwl-drv.c) chooses the op_mode based on the
- *        capabilities advertized by the fw file (in TLV format).
+ *        capabilities advertised by the fw file (in TLV format).
  *     2) The driver layer starts the op_mode (ops->start)
  *     3) The op_mode registers mac80211
  *     4) The op_mode is governed by mac80211
@@ -116,7 +116,7 @@ struct iwl_cfg;
  *     May sleep
  * @rx: Rx notification to the op_mode. rxb is the Rx buffer itself. Cmd is the
  *     HCMD this Rx responds to. Can't sleep.
- * @napi_add: NAPI initialisation. The transport is fully responsible for NAPI,
+ * @napi_add: NAPI initialization. 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.
index e893c6eb260cd4866b7c7d03c1437b9d479ad9de..a105455b6a2469c2afb07e1b04e4949d8e1184f9 100644 (file)
@@ -125,7 +125,7 @@ struct iwl_phy_db_chg_txp {
 } __packed;
 
 /*
- * phy db - Receieve phy db chunk after calibrations
+ * phy db - Receive phy db chunk after calibrations
  */
 struct iwl_calib_res_notif_phy_db {
        __le16 type;
index bc962888c5836435450882a0d535c8d9534d14e7..88a57e6e232ff318dea917d0856da781662bc5eb 100644 (file)
 #define DEVICE_SET_NMI_REG 0x00a01c30
 #define DEVICE_SET_NMI_VAL_HW BIT(0)
 #define DEVICE_SET_NMI_VAL_DRV BIT(7)
-#define DEVICE_SET_NMI_8000B_REG 0x00a01c24
-#define DEVICE_SET_NMI_8000B_VAL 0x1000000
+#define DEVICE_SET_NMI_8000_REG 0x00a01c24
+#define DEVICE_SET_NMI_8000_VAL 0x1000000
 
 /* Shared registers (0x0..0x3ff, via target indirect or periphery */
 #define SHR_BASE       0x00a10000
 #define OSC_CLK                                (0xa04068)
 #define OSC_CLK_FORCE_CONTROL          (0x8)
 
-/* SECURE boot registers */
-#define LMPM_SECURE_BOOT_CONFIG_ADDR   (0x100)
-enum secure_boot_config_reg {
-       LMPM_SECURE_BOOT_CONFIG_INSPECTOR_BURNED_IN_OTP = 0x00000001,
-       LMPM_SECURE_BOOT_CONFIG_INSPECTOR_NOT_REQ       = 0x00000002,
-};
-
-#define LMPM_SECURE_BOOT_CPU1_STATUS_ADDR_B0   (0xA01E30)
-#define LMPM_SECURE_BOOT_CPU1_STATUS_ADDR      (0x1E30)
-#define LMPM_SECURE_BOOT_CPU2_STATUS_ADDR      (0x1E34)
-enum secure_boot_status_reg {
-       LMPM_SECURE_BOOT_CPU_STATUS_VERF_STATUS         = 0x00000001,
-       LMPM_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED      = 0x00000002,
-       LMPM_SECURE_BOOT_CPU_STATUS_VERF_SUCCESS        = 0x00000004,
-       LMPM_SECURE_BOOT_CPU_STATUS_VERF_FAIL           = 0x00000008,
-       LMPM_SECURE_BOOT_CPU_STATUS_SIGN_VERF_FAIL      = 0x00000010,
-       LMPM_SECURE_BOOT_STATUS_SUCCESS                 = 0x00000003,
-};
-
 #define FH_UCODE_LOAD_STATUS           (0x1AF0)
 #define CSR_UCODE_LOAD_STATUS_ADDR     (0x1E70)
 enum secure_load_status_reg {
@@ -334,8 +315,6 @@ enum secure_load_status_reg {
 #define LMPM_SECURE_CPU1_HDR_MEM_SPACE         (0x420000)
 #define LMPM_SECURE_CPU2_HDR_MEM_SPACE         (0x420400)
 
-#define LMPM_SECURE_TIME_OUT   (100) /* 10 micro */
-
 /* Rx FIFO */
 #define RXF_SIZE_ADDR                  (0xa00c88)
 #define RXF_RD_D_SPACE                 (0xa00c40)
index 11ac5c58527f6f92b5980b15f43defa9ad5822c9..6dfed1259260f06d23feb544d78ce6484c01fb36 100644 (file)
 /**
  * DOC: Transport layer - what is it ?
  *
- * The tranport layer is the layer that deals with the HW directly. It provides
+ * The transport layer is the layer that deals with the HW directly. It provides
  * an abstraction of the underlying HW to the upper layer. The transport layer
  * doesn't provide any policy, algorithm or anything of this kind, but only
- * mechanisms to make the HW do something.It is not completely stateless but
+ * mechanisms to make the HW do something. It is not completely stateless but
  * close to it.
  * We will have an implementation for each different supported bus.
  */
 /**
  * DOC: Host command section
  *
- * A host command is a commaned issued by the upper layer to the fw. There are
+ * A host command is a command issued by the upper layer to the fw. There are
  * several versions of fw that have several APIs. The transport layer is
  * completely agnostic to these differences.
- * The transport does provide helper functionnality (i.e. SYNC / ASYNC mode),
+ * The transport does provide helper functionality (i.e. SYNC / ASYNC mode),
  */
 #define SEQ_TO_QUEUE(s)        (((s) >> 8) & 0x1f)
 #define QUEUE_TO_SEQ(q)        (((q) & 0x1f) << 8)
@@ -195,7 +195,7 @@ static inline u32 iwl_rx_packet_payload_len(const struct iwl_rx_packet *pkt)
  * @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
+ *     command queue, but after other high priority commands. Valid only
  *     with CMD_ASYNC.
  * @CMD_SEND_IN_IDLE: The command should be sent even when the trans is idle.
  * @CMD_MAKE_TRANS_IDLE: The command response should mark the trans as idle.
@@ -582,7 +582,7 @@ enum iwl_d0i3_mode {
  * @cfg - pointer to the configuration
  * @status: a bit-mask of transport status flags
  * @dev - pointer to struct device * that represents the device
- * @hw_id: a u32 with the ID of the device / subdevice.
+ * @hw_id: a u32 with the ID of the device / sub-device.
  *     Set during transport allocation.
  * @hw_id_str: a string with info about HW ID. Set during transport allocation.
  * @pm_support: set to true in start_hw if link pm is supported
index 5f8afa5f11a35f8da4761fb638d76f34f4f0a9eb..a6c48c7b1e1683fdbdcb99fd0e4f971cf6ad66d2 100644 (file)
@@ -1131,6 +1131,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
        iwl_trans_d3_suspend(mvm->trans, test);
  out:
        if (ret < 0) {
+               iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
                ieee80211_restart_hw(mvm->hw);
                iwl_mvm_free_nd(mvm);
        }
@@ -1725,6 +1726,10 @@ iwl_mvm_netdetect_query_results(struct iwl_mvm *mvm,
        results->matched_profiles = le32_to_cpu(query->matched_profiles);
        memcpy(results->matches, query->matches, sizeof(results->matches));
 
+#ifdef CPTCFG_IWLWIFI_DEBUGFS
+       mvm->last_netdetect_scans = le32_to_cpu(query->n_scans_done);
+#endif
+
 out_free_resp:
        iwl_free_resp(&cmd);
        return ret;
@@ -2016,6 +2021,7 @@ static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file)
        __iwl_mvm_resume(mvm, true);
        rtnl_unlock();
        iwl_abort_notification_waits(&mvm->notif_wait);
+       iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
        ieee80211_restart_hw(mvm->hw);
 
        /* wait for restart and disconnect all interfaces */
index 8c5229892e573aa58234e4e64893ddee0c89253e..9ac04c1ea7063d985980a5237fe705c1bba7186c 100644 (file)
@@ -1473,26 +1473,6 @@ out:
        return count;
 }
 
-static ssize_t iwl_dbgfs_enable_scan_iteration_notif_write(struct iwl_mvm *mvm,
-                                                          char *buf,
-                                                          size_t count,
-                                                          loff_t *ppos)
-{
-       int val;
-
-       mutex_lock(&mvm->mutex);
-
-       if (kstrtoint(buf, 10, &val)) {
-               mutex_unlock(&mvm->mutex);
-               return -EINVAL;
-       }
-
-       mvm->scan_iter_notif_enabled = val;
-       mutex_unlock(&mvm->mutex);
-
-       return count;
-}
-
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64);
 
 /* Device wide debugfs entries */
@@ -1515,7 +1495,6 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg_conf, 8);
 MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 8);
-MVM_DEBUGFS_WRITE_FILE_OPS(enable_scan_iteration_notif, 8);
 
 #ifdef CONFIG_IWLWIFI_BCAST_FILTERING
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256);
@@ -1559,8 +1538,11 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
        MVM_DEBUGFS_ADD_FILE(d0i3_refs, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
        MVM_DEBUGFS_ADD_FILE(fw_dbg_conf, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
        MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, S_IWUSR);
-       MVM_DEBUGFS_ADD_FILE(enable_scan_iteration_notif, mvm->debugfs_dir,
-                            S_IWUSR);
+       if (!debugfs_create_bool("enable_scan_iteration_notif",
+                                S_IRUSR | S_IWUSR,
+                                mvm->debugfs_dir,
+                                &mvm->scan_iter_notif_enabled))
+               goto err;
 
 #ifdef CONFIG_IWLWIFI_BCAST_FILTERING
        if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING) {
@@ -1587,6 +1569,9 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
        if (!debugfs_create_bool("d3_wake_sysassert", S_IRUSR | S_IWUSR,
                                 mvm->debugfs_dir, &mvm->d3_wake_sysassert))
                goto err;
+       if (!debugfs_create_u32("last_netdetect_scans", S_IRUSR,
+                               mvm->debugfs_dir, &mvm->last_netdetect_scans))
+               goto err;
        MVM_DEBUGFS_ADD_FILE(netdetect, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
 #endif
 
index 6d3bea5c59d1eb8ab42bca2f0c1a4523a23b8291..d7658d16e965c835144d8804d4e7185d605e8bfd 100644 (file)
@@ -132,7 +132,7 @@ struct iwl_proto_offload_cmd_common {
  * @solicited_node_ipv6_addr: broken -- solicited node address exists
  *     for each target address
  * @target_ipv6_addr: our target addresses
- * @ndp_mac_addr: neighbor soliciation response MAC address
+ * @ndp_mac_addr: neighbor solicitation response MAC address
  */
 struct iwl_proto_offload_cmd_v1 {
        struct iwl_proto_offload_cmd_common common;
@@ -150,7 +150,7 @@ struct iwl_proto_offload_cmd_v1 {
  * @solicited_node_ipv6_addr: broken -- solicited node address exists
  *     for each target address
  * @target_ipv6_addr: our target addresses
- * @ndp_mac_addr: neighbor soliciation response MAC address
+ * @ndp_mac_addr: neighbor solicitation response MAC address
  */
 struct iwl_proto_offload_cmd_v2 {
        struct iwl_proto_offload_cmd_common common;
index aabaedd3b3ee1c6e3deeadc1ec7a9a9fe6059b83..f3f3ee0a766bd21645e26fe4c671caa8e15e71c0 100644 (file)
@@ -255,7 +255,7 @@ struct iwl_mac_data_p2p_dev {
 /**
  * enum iwl_mac_filter_flags - MAC context filter flags
  * @MAC_FILTER_IN_PROMISC: accept all data frames
- * @MAC_FILTER_IN_CONTROL_AND_MGMT: pass all mangement and
+ * @MAC_FILTER_IN_CONTROL_AND_MGMT: pass all management and
  *     control frames to the host
  * @MAC_FILTER_ACCEPT_GRP: accept multicast frames
  * @MAC_FILTER_DIS_DECRYPT: don't decrypt unicast frames
index a5fbbd637070795b97fafde922aabb50d7bcaf4c..4f81dcf57a736e7409087f4b3193809c3570db6f 100644 (file)
@@ -103,7 +103,7 @@ struct iwl_ssid_ie {
  * @SCAN_COMP_STATUS_ERR_COEX: medium was lost ot WiMax
  * @SCAN_COMP_STATUS_P2P_ACTION_OK: P2P public action frame TX was successful
  *     (not an error!)
- * @SCAN_COMP_STATUS_ITERATION_END: indicates end of one repeatition the driver
+ * @SCAN_COMP_STATUS_ITERATION_END: indicates end of one repetition the driver
  *     asked for
  * @SCAN_COMP_STATUS_ERR_ALLOC_TE: scan could not allocate time events
 */
@@ -187,11 +187,11 @@ enum scan_framework_client {
  * struct iwl_scan_offload_cmd - SCAN_REQUEST_FIXED_PART_API_S_VER_6
  * @scan_flags:                see enum iwl_scan_flags
  * @channel_count:     channels in channel list
- * @quiet_time:                dwell time, in milisiconds, on quiet channel
+ * @quiet_time:                dwell time, in milliseconds, on quiet channel
  * @quiet_plcp_th:     quiet channel num of packets threshold
  * @good_CRC_th:       passive to active promotion threshold
  * @rx_chain:          RXON rx chain.
- * @max_out_time:      max TUs to be out of assoceated channel
+ * @max_out_time:      max TUs to be out of associated channel
  * @suspend_time:      pause scan this TUs when returning to service channel
  * @flags:             RXON flags
  * @filter_flags:      RXONfilter
@@ -232,7 +232,7 @@ enum iwl_scan_offload_channel_flags {
  *     see enum iwl_scan_offload_channel_flags.
  * __le16 channel_number: channel number 1-13 etc.
  * __le16 iter_count: repetition count for the channel.
- * __le32 iter_interval: interval between two innteration on one channel.
+ * __le32 iter_interval: interval between two iterations on one channel.
  * u8 active_dwell.
  * u8 passive_dwell.
  */
@@ -275,8 +275,8 @@ enum iwl_scan_offload_band_selection {
 /**
  * iwl_scan_offload_profile - SCAN_OFFLOAD_PROFILE_S
  * @ssid_index:                index to ssid list in fixed part
- * @unicast_cipher:    encryption olgorithm to match - bitmap
- * @aut_alg:           authentication olgorithm to match - bitmap
+ * @unicast_cipher:    encryption algorithm to match - bitmap
+ * @aut_alg:           authentication algorithm to match - bitmap
  * @network_type:      enum iwl_scan_offload_network_type
  * @band_selection:    enum iwl_scan_offload_band_selection
  * @client_bitmap:     clients waiting for match - enum scan_framework_client
@@ -748,7 +748,7 @@ enum iwl_umac_scan_general_flags {
  * @flags:             bitmap - 0-19:  directed scan to i'th ssid.
  * @channel_num:       channel number 1-13 etc.
  * @iter_count:                repetition count for the channel.
- * @iter_interval:     interval between two scan interations on one channel.
+ * @iter_interval:     interval between two scan iterations on one channel.
  */
 struct iwl_scan_channel_cfg_umac {
        __le32 flags;
index 6cf7d9837ca54e938500302cd013a8a4a396be5d..bc5eac4960e18a79a211da2a2bf6492b1a39e570 100644 (file)
@@ -526,16 +526,33 @@ int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig,
 
 int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
                                struct iwl_fw_dbg_trigger_tlv *trigger,
-                               const char *str, size_t len)
+                               const char *fmt, ...)
 {
        unsigned int delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay));
        u16 occurrences = le16_to_cpu(trigger->occurrences);
-       int ret;
+       int ret, len = 0;
+       char buf[64];
 
        if (!occurrences)
                return 0;
 
-       ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), str,
+       if (fmt) {
+               va_list ap;
+
+               buf[sizeof(buf) - 1] = '\0';
+
+               va_start(ap, fmt);
+               vsnprintf(buf, sizeof(buf), fmt, ap);
+               va_end(ap);
+
+               /* check for truncation */
+               if (WARN_ON_ONCE(buf[sizeof(buf) - 1]))
+                       buf[sizeof(buf) - 1] = '\0';
+
+               len = strlen(buf) + 1;
+       }
+
+       ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), buf,
                                     len, delay);
        if (ret)
                return ret;
index 581b3b8f29f9b6d7460b98eeb9ee54e3b9612c35..8088c7137f7c9462417ffdfc07a30dcc37be6ec2 100644 (file)
@@ -470,9 +470,8 @@ exit_fail:
 
 int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 {
-       unsigned int wdg_timeout = iwlmvm_mod_params.tfd_q_hang_detect ?
-                                       mvm->cfg->base_params->wd_timeout :
-                                       IWL_WATCHDOG_DISABLED;
+       unsigned int wdg_timeout =
+               iwl_mvm_get_wd_timeout(mvm, vif, false, false);
        u32 ac;
        int ret;
 
@@ -1413,7 +1412,7 @@ static void iwl_mvm_beacon_loss_iterator(void *_data, u8 *mac,
 
        if (rx_missed_bcon_since_rx >= stop_trig_missed_bcon_since_rx ||
            rx_missed_bcon >= stop_trig_missed_bcon)
-               iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL, 0);
+               iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL);
 }
 
 int iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm,
index 302c8cc50f25a695c40ce59555f8a05bf58cae2d..84555170b6f751bb4f0925bf5c85319de76293b6 100644 (file)
@@ -379,11 +379,13 @@ int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm)
 {
        enum iwl_mcc_source used_src;
        struct ieee80211_regdomain *regd;
+       int ret;
+       bool changed;
        const struct ieee80211_regdomain *r =
                        rtnl_dereference(mvm->hw->wiphy->regd);
 
        if (!r)
-               return 0;
+               return -ENOENT;
 
        /* save the last source in case we overwrite it below */
        used_src = mvm->mcc_src;
@@ -395,14 +397,19 @@ int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm)
        }
 
        /* Now set our last stored MCC and source */
-       regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, r->alpha2, used_src, NULL);
+       regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, r->alpha2, used_src,
+                                    &changed);
        if (IS_ERR_OR_NULL(regd))
                return -EIO;
 
-       regulatory_set_wiphy_regd(mvm->hw->wiphy, regd);
-       kfree(regd);
+       /* update cfg80211 if the regdomain was changed */
+       if (changed)
+               ret = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd);
+       else
+               ret = 0;
 
-       return 0;
+       kfree(regd);
+       return ret;
 }
 
 int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
@@ -1007,6 +1014,9 @@ void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm)
        mvm->fw_dump_desc = NULL;
 }
 
+#define IWL8260_ICCM_OFFSET            0x44000 /* Only for B-step */
+#define IWL8260_ICCM_LEN               0xC000 /* Only for B-step */
+
 void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
 {
        struct iwl_fw_error_dump_file *dump_file;
@@ -1022,16 +1032,6 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
 
        lockdep_assert_held(&mvm->mutex);
 
-       /* W/A for 8000 HW family A-step */
-       if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
-           CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_A_STEP) {
-               if (smem_len)
-                       smem_len = 0x38000;
-
-               if (sram2_len)
-                       sram2_len = 0x10000;
-       }
-
        fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL);
        if (!fw_error_dump)
                return;
@@ -1083,6 +1083,14 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
                   fifo_data_len +
                   sizeof(*dump_info);
 
+       /*
+        * In 8000 HW family B-step include the ICCM (which resides separately)
+        */
+       if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
+           CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP)
+               file_len += sizeof(*dump_data) + sizeof(*dump_mem) +
+                           IWL8260_ICCM_LEN;
+
        if (mvm->fw_dump_desc)
                file_len += sizeof(*dump_data) + sizeof(*dump_trig) +
                            mvm->fw_dump_desc->len;
@@ -1170,6 +1178,19 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
                                         dump_mem->data, sram2_len);
        }
 
+       if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
+           CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) {
+               dump_data = iwl_fw_error_next_data(dump_data);
+               dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
+               dump_data->len = cpu_to_le32(IWL8260_ICCM_LEN +
+                                            sizeof(*dump_mem));
+               dump_mem = (void *)dump_data->data;
+               dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
+               dump_mem->offset = cpu_to_le32(IWL8260_ICCM_OFFSET);
+               iwl_trans_read_mem_bytes(mvm->trans, IWL8260_ICCM_OFFSET,
+                                        dump_mem->data, IWL8260_ICCM_LEN);
+       }
+
        fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans);
        fw_error_dump->op_mode_len = file_len;
        if (fw_error_dump->trans_ptr)
@@ -1399,6 +1420,20 @@ void __iwl_mvm_mac_stop(struct iwl_mvm *mvm)
         */
        clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
 
+       /* We shouldn't have any UIDs still set.  Loop over all the UIDs to
+        * make sure there's nothing left there and warn if any is found.
+        */
+       if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
+               int i;
+
+               for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++) {
+                       if (WARN_ONCE(mvm->scan_uid[i],
+                                     "UMAC scan UID %d was not cleaned\n",
+                                     mvm->scan_uid[i]))
+                               mvm->scan_uid[i] = 0;
+               }
+       }
+
        mvm->ucode_loaded = false;
 }
 
@@ -1595,9 +1630,33 @@ static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm,
        u32 tfd_msk = iwl_mvm_mac_get_queues_mask(vif);
 
        if (tfd_msk) {
+               /*
+                * mac80211 first removes all the stations of the vif and
+                * then removes the vif. When it removes a station it also
+                * flushes the AMPDU session. So by now, all the AMPDU sessions
+                * of all the stations of this vif are closed, and the queues
+                * of these AMPDU sessions are properly closed.
+                * We still need to take care of the shared queues of the vif.
+                * Flush them here.
+                */
                mutex_lock(&mvm->mutex);
                iwl_mvm_flush_tx_path(mvm, tfd_msk, true);
                mutex_unlock(&mvm->mutex);
+
+               /*
+                * There are transports that buffer a few frames in the host.
+                * For these, the flush above isn't enough since while we were
+                * flushing, the transport might have sent more frames to the
+                * device. To solve this, wait here until the transport is
+                * empty. Technically, this could have replaced the flush
+                * above, but flush is much faster than draining. So flush
+                * first, and drain to make sure we have no frames in the
+                * transport anymore.
+                * If a station still had frames on the shared queues, it is
+                * already marked as draining, so to complete the draining, we
+                * just need to wait until the transport is empty.
+                */
+               iwl_trans_wait_tx_queue_empty(mvm->trans, tfd_msk);
        }
 
        if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
@@ -2167,8 +2226,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
        if (iwl_mvm_phy_ctx_count(mvm) > 1)
                iwl_mvm_teardown_tdls_peers(mvm);
 
-       mutex_unlock(&mvm->mutex);
-       return 0;
+       goto out_unlock;
 
 out_quota_failed:
        iwl_mvm_power_update_mac(mvm);
@@ -3022,6 +3080,8 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw,
        IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,
                           duration, type);
 
+       flush_work(&mvm->roc_done_wk);
+
        mutex_lock(&mvm->mutex);
 
        switch (vif->type) {
@@ -3646,11 +3706,12 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
 
        mutex_lock(&mvm->mutex);
 
+       mvmvif->csa_failed = false;
+
        IWL_DEBUG_MAC80211(mvm, "pre CSA to freq %d\n",
                           chsw->chandef.center_freq1);
 
-       iwl_fw_dbg_trigger_simple_stop(mvm, vif, FW_DBG_TRIGGER_CHANNEL_SWITCH,
-                                      NULL, 0);
+       iwl_fw_dbg_trigger_simple_stop(mvm, vif, FW_DBG_TRIGGER_CHANNEL_SWITCH);
 
        switch (vif->type) {
        case NL80211_IFTYPE_AP:
@@ -3721,6 +3782,12 @@ static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw,
 
        mutex_lock(&mvm->mutex);
 
+       if (mvmvif->csa_failed) {
+               mvmvif->csa_failed = false;
+               ret = -EIO;
+               goto out_unlock;
+       }
+
        if (vif->type == NL80211_IFTYPE_STATION) {
                struct iwl_mvm_sta *mvmsta;
 
@@ -3844,6 +3911,7 @@ static int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx,
                            mvm->radio_stats.on_time_scan;
        do_div(survey->time_scan, USEC_PER_MSEC);
 
+       ret = 0;
  out:
        mutex_unlock(&mvm->mutex);
        return ret;
@@ -3889,6 +3957,64 @@ static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw,
        mutex_unlock(&mvm->mutex);
 }
 
+static void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw,
+                                      struct ieee80211_vif *vif,
+                                      const struct ieee80211_event *event)
+{
+#define CHECK_MLME_TRIGGER(_mvm, _trig, _buf, _cnt, _fmt...)   \
+       do {                                                    \
+               if ((_cnt) && --(_cnt))                         \
+                       break;                                  \
+               iwl_mvm_fw_dbg_collect_trig(_mvm, _trig, _fmt);\
+       } while (0)
+
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_fw_dbg_trigger_tlv *trig;
+       struct iwl_fw_dbg_trigger_mlme *trig_mlme;
+
+       if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_MLME))
+               return;
+
+       if (event->u.mlme.status == MLME_SUCCESS)
+               return;
+
+       trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_MLME);
+       trig_mlme = (void *)trig->data;
+       if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
+               return;
+
+       if (event->u.mlme.data == ASSOC_EVENT) {
+               if (event->u.mlme.status == MLME_DENIED)
+                       CHECK_MLME_TRIGGER(mvm, trig, buf,
+                                          trig_mlme->stop_assoc_denied,
+                                          "DENIED ASSOC: reason %d",
+                                           event->u.mlme.reason);
+               else if (event->u.mlme.status == MLME_TIMEOUT)
+                       CHECK_MLME_TRIGGER(mvm, trig, buf,
+                                          trig_mlme->stop_assoc_timeout,
+                                          "ASSOC TIMEOUT");
+       } else if (event->u.mlme.data == AUTH_EVENT) {
+               if (event->u.mlme.status == MLME_DENIED)
+                       CHECK_MLME_TRIGGER(mvm, trig, buf,
+                                          trig_mlme->stop_auth_denied,
+                                          "DENIED AUTH: reason %d",
+                                          event->u.mlme.reason);
+               else if (event->u.mlme.status == MLME_TIMEOUT)
+                       CHECK_MLME_TRIGGER(mvm, trig, buf,
+                                          trig_mlme->stop_auth_timeout,
+                                          "AUTH TIMEOUT");
+       } else if (event->u.mlme.data == DEAUTH_RX_EVENT) {
+               CHECK_MLME_TRIGGER(mvm, trig, buf,
+                                  trig_mlme->stop_rx_deauth,
+                                  "DEAUTH RX %d", event->u.mlme.reason);
+       } else if (event->u.mlme.data == DEAUTH_TX_EVENT) {
+               CHECK_MLME_TRIGGER(mvm, trig, buf,
+                                  trig_mlme->stop_tx_deauth,
+                                  "DEAUTH TX %d", event->u.mlme.reason);
+       }
+#undef CHECK_MLME_TRIGGER
+}
+
 const struct ieee80211_ops iwl_mvm_hw_ops = {
        .tx = iwl_mvm_mac_tx,
        .ampdu_action = iwl_mvm_mac_ampdu_action,
@@ -3942,6 +4068,8 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
        .tdls_cancel_channel_switch = iwl_mvm_tdls_cancel_channel_switch,
        .tdls_recv_channel_switch = iwl_mvm_tdls_recv_channel_switch,
 
+       .event_callback = iwl_mvm_mac_event_callback,
+
        CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)
 
 #ifdef CONFIG_PM_SLEEP
index 4b5c8f66df8baa865c7f6d0f8f3f885c9193ceef..d5522a16124292cd6cab36159851a8446b5e2d95 100644 (file)
@@ -349,11 +349,12 @@ struct iwl_mvm_vif_bf_data {
  * @bcast_sta: station used for broadcast packets. Used by the following
  *  vifs: P2P_DEVICE, GO and AP.
  * @beacon_skb: the skb used to hold the AP/GO beacon template
- * @smps_requests: the SMPS requests of differents parts of the driver,
+ * @smps_requests: the SMPS requests of different parts of the driver,
  *     combined on update to yield the overall request to mac80211.
  * @beacon_stats: beacon statistics, containing the # of received beacons,
  *     # of received beacons accumulated over FW restart, and the current
  *     average signal of beacons retrieved from the firmware
+ * @csa_failed: CSA failed to schedule time event, report an error later
  */
 struct iwl_mvm_vif {
        struct iwl_mvm *mvm;
@@ -433,6 +434,7 @@ struct iwl_mvm_vif {
 
        /* Indicates that CSA countdown may be started */
        bool csa_countdown;
+       bool csa_failed;
 };
 
 static inline struct iwl_mvm_vif *
@@ -686,7 +688,7 @@ struct iwl_mvm {
        bool disable_power_off;
        bool disable_power_off_d3;
 
-       bool scan_iter_notif_enabled;
+       u32 scan_iter_notif_enabled; /* must be u32 for debugfs_create_bool */
 
        struct debugfs_blob_wrapper nvm_hw_blob;
        struct debugfs_blob_wrapper nvm_sw_blob;
@@ -746,6 +748,7 @@ struct iwl_mvm {
        void *d3_resume_sram;
        u32 d3_test_pme_ptr;
        struct ieee80211_vif *keep_vif;
+       u32 last_netdetect_scans; /* no. of scans in the last net-detect wake */
 #endif
 #endif
 
@@ -934,7 +937,8 @@ static inline bool iwl_mvm_is_lar_supported(struct iwl_mvm *mvm)
 
 static inline bool iwl_mvm_is_wifi_mcc_supported(struct iwl_mvm *mvm)
 {
-       return mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_WIFI_MCC_UPDATE;
+       return mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_WIFI_MCC_UPDATE ||
+              mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC;
 }
 
 static inline bool iwl_mvm_is_scd_cfg_supported(struct iwl_mvm *mvm)
@@ -1146,6 +1150,7 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, bool force_upload,
 int iwl_mvm_scan_size(struct iwl_mvm *mvm);
 int iwl_mvm_cancel_scan(struct iwl_mvm *mvm);
 int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm, bool is_sched_scan);
+void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm);
 
 /* Scheduled scan */
 int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
@@ -1475,8 +1480,12 @@ int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm,
 void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm);
 int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
                                struct iwl_fw_dbg_trigger_tlv *trigger,
-                               const char *str, size_t len);
-
+                               const char *fmt, ...) __printf(3, 4);
+unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm,
+                                   struct ieee80211_vif *vif,
+                                   bool tdls, bool cmd_q);
+void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                            const char *errmsg);
 static inline bool
 iwl_fw_dbg_trigger_vif_match(struct iwl_fw_dbg_trigger_tlv *trig,
                             struct ieee80211_vif *vif)
@@ -1509,8 +1518,7 @@ iwl_fw_dbg_trigger_check_stop(struct iwl_mvm *mvm,
 static inline void
 iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm,
                               struct ieee80211_vif *vif,
-                              enum iwl_fw_dbg_trigger trig,
-                              const char *str, size_t len)
+                              enum iwl_fw_dbg_trigger trig)
 {
        struct iwl_fw_dbg_trigger_tlv *trigger;
 
@@ -1521,7 +1529,7 @@ iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm,
        if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger))
                return;
 
-       iwl_mvm_fw_dbg_collect_trig(mvm, trigger, str, len);
+       iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL);
 }
 
 #endif /* __IWL_MVM_H__ */
index 123e0a16aea88d185656b160c241763fd8b91d1b..87b2a30a2308439c4e7a3f3bd80e419f9583af5a 100644 (file)
@@ -77,8 +77,7 @@
 /* Default NVM size to read */
 #define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024)
 #define IWL_MAX_NVM_SECTION_SIZE       0x1b58
-#define IWL_MAX_NVM_8000A_SECTION_SIZE 0xffc
-#define IWL_MAX_NVM_8000B_SECTION_SIZE 0x1ffc
+#define IWL_MAX_NVM_8000_SECTION_SIZE  0x1ffc
 
 #define NVM_WRITE_OPCODE 1
 #define NVM_READ_OPCODE 0
@@ -267,7 +266,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
 {
        struct iwl_nvm_section *sections = mvm->nvm_sections;
        const __le16 *hw, *sw, *calib, *regulatory, *mac_override, *phy_sku;
-       bool is_family_8000_a_step = false, lar_enabled;
+       bool lar_enabled;
        u32 mac_addr0, mac_addr1;
 
        /* Checking for required sections */
@@ -293,12 +292,8 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
                        return NULL;
                }
 
-               if (CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_A_STEP)
-                       is_family_8000_a_step = true;
-
                /* PHY_SKU section is mandatory in B0 */
-               if (!is_family_8000_a_step &&
-                   !mvm->nvm_sections[NVM_SECTION_TYPE_PHY_SKU].data) {
+               if (!mvm->nvm_sections[NVM_SECTION_TYPE_PHY_SKU].data) {
                        IWL_ERR(mvm,
                                "Can't parse phy_sku in B0, empty sections\n");
                        return NULL;
@@ -327,8 +322,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
        return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib,
                                  regulatory, mac_override, phy_sku,
                                  mvm->fw->valid_tx_ant, mvm->fw->valid_rx_ant,
-                                 lar_enabled, is_family_8000_a_step,
-                                 mac_addr0, mac_addr1);
+                                 lar_enabled, mac_addr0, mac_addr1);
 }
 
 #define MAX_NVM_FILE_LEN       16384
@@ -381,10 +375,8 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
        /* Maximal size depends on HW family and step */
        if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
                max_section_size = IWL_MAX_NVM_SECTION_SIZE;
-       else if (CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_A_STEP)
-               max_section_size = IWL_MAX_NVM_8000A_SECTION_SIZE;
-       else /* Family 8000 B-step or C-step */
-               max_section_size = IWL_MAX_NVM_8000B_SECTION_SIZE;
+       else
+               max_section_size = IWL_MAX_NVM_8000_SECTION_SIZE;
 
        /*
         * Obtain NVM image via request_firmware. Since we already used
@@ -426,6 +418,15 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
                IWL_INFO(mvm, "NVM Version %08X\n", le32_to_cpu(dword_buff[2]));
                IWL_INFO(mvm, "NVM Manufacturing date %08X\n",
                         le32_to_cpu(dword_buff[3]));
+
+               /* nvm file validation, dword_buff[2] holds the file version */
+               if ((CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_C_STEP &&
+                    le32_to_cpu(dword_buff[2]) < 0xE4A) ||
+                   (CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP &&
+                    le32_to_cpu(dword_buff[2]) >= 0xE4A)) {
+                       ret = -EFAULT;
+                       goto out;
+               }
        } else {
                file_sec = (void *)fw_entry->data;
        }
@@ -524,6 +525,8 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic)
        int ret, section;
        u32 size_read = 0;
        u8 *nvm_buffer, *temp;
+       const char *nvm_file_B = mvm->cfg->default_nvm_file_B_step;
+       const char *nvm_file_C = mvm->cfg->default_nvm_file_C_step;
 
        if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS))
                return -EINVAL;
@@ -582,10 +585,27 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic)
 
        /* load external NVM if configured */
        if (mvm->nvm_file_name) {
-               /* move to External NVM flow */
+               /* read External NVM file - take the default */
                ret = iwl_mvm_read_external_nvm(mvm);
-               if (ret)
-                       return ret;
+               if (ret) {
+                       /* choose the nvm_file name according to the
+                        * HW step
+                        */
+                       if (CSR_HW_REV_STEP(mvm->trans->hw_rev) ==
+                           SILICON_B_STEP)
+                               mvm->nvm_file_name = nvm_file_B;
+                       else
+                               mvm->nvm_file_name = nvm_file_C;
+
+                       if (ret == -EFAULT && mvm->nvm_file_name) {
+                               /* in case nvm file was failed try again */
+                               ret = iwl_mvm_read_external_nvm(mvm);
+                               if (ret)
+                                       return ret;
+                       } else {
+                               return ret;
+                       }
+               }
        }
 
        /* parse the relevant nvm sections */
@@ -786,13 +806,12 @@ int iwl_mvm_init_mcc(struct iwl_mvm *mvm)
                return 0;
 
        /*
-        * During HW restart, only replay the last set MCC to FW. Otherwise,
+        * try to replay the last set MCC to FW. If it doesn't exist,
         * queue an update to cfg80211 to retrieve the default alpha2 from FW.
         */
-       if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
-               /* This should only be called during vif up and hold RTNL */
-               return iwl_mvm_init_fw_regd(mvm);
-       }
+       retval = iwl_mvm_init_fw_regd(mvm);
+       if (retval != -ENOENT)
+               return retval;
 
        /*
         * Driver regulatory hint for initial update, this also informs the
index 80121e41ca22f3aca2a09c59ba30016e9872caae..a08b03d58d4bf0f3ebd4773a7fdb6e07cc8406b8 100644 (file)
@@ -488,8 +488,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
 
        /* Set a short watchdog for the command queue */
        trans_cfg.cmd_q_wdg_timeout =
-               iwlmvm_mod_params.tfd_q_hang_detect ? IWL_DEF_WD_TIMEOUT :
-                                                     IWL_WATCHDOG_DISABLED;
+               iwl_mvm_get_wd_timeout(mvm, NULL, false, true);
 
        snprintf(mvm->hw->wiphy->fw_version,
                 sizeof(mvm->hw->wiphy->fw_version),
@@ -524,12 +523,11 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        /* set the nvm_file_name according to priority */
        if (iwlwifi_mod_params.nvm_file) {
                mvm->nvm_file_name = iwlwifi_mod_params.nvm_file;
-       } else {
-               if ((trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) &&
-                   (CSR_HW_REV_STEP(trans->hw_rev) == SILICON_A_STEP))
-                       mvm->nvm_file_name = mvm->cfg->default_nvm_file_8000A;
+       } else if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
+               if (CSR_HW_REV_STEP(trans->hw_rev) == SILICON_B_STEP)
+                       mvm->nvm_file_name = mvm->cfg->default_nvm_file_B_step;
                else
-                       mvm->nvm_file_name = mvm->cfg->default_nvm_file;
+                       mvm->nvm_file_name = mvm->cfg->default_nvm_file_C_step;
        }
 
        if (WARN(cfg->no_power_up_nic_in_init && !mvm->nvm_file_name,
@@ -691,7 +689,6 @@ static inline void iwl_mvm_rx_check_trigger(struct iwl_mvm *mvm,
 {
        struct iwl_fw_dbg_trigger_tlv *trig;
        struct iwl_fw_dbg_trigger_cmd *cmds_trig;
-       char buf[32];
        int i;
 
        if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_FW_NOTIF))
@@ -711,9 +708,9 @@ static inline void iwl_mvm_rx_check_trigger(struct iwl_mvm *mvm,
                if (cmds_trig->cmds[i].cmd_id != pkt->hdr.cmd)
                        continue;
 
-               memset(buf, 0, sizeof(buf));
-               snprintf(buf, sizeof(buf), "CMD 0x%02x received", pkt->hdr.cmd);
-               iwl_mvm_fw_dbg_collect_trig(mvm, trig, buf, sizeof(buf));
+               iwl_mvm_fw_dbg_collect_trig(mvm, trig,
+                                           "CMD 0x%02x received",
+                                           pkt->hdr.cmd);
                break;
        }
 }
@@ -894,18 +891,7 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error)
         * the next start() call from mac80211. If restart isn't called
         * (no fw restart) scan status will stay busy.
         */
-       switch (mvm->scan_status) {
-       case IWL_MVM_SCAN_NONE:
-               break;
-       case IWL_MVM_SCAN_OS:
-               ieee80211_scan_completed(mvm->hw, true);
-               break;
-       case IWL_MVM_SCAN_SCHED:
-               /* Sched scan will be restarted by mac80211 in restart_hw. */
-               if (!mvm->restart_fw)
-                       ieee80211_sched_scan_stopped(mvm->hw);
-               break;
-       }
+       iwl_mvm_report_scan_aborted(mvm);
 
        /*
         * If we're restarting already, don't cycle restarts.
@@ -1175,7 +1161,7 @@ static void iwl_mvm_d0i3_disconnect_iter(void *data, u8 *mac,
 
        if (vif->type == NL80211_IFTYPE_STATION && vif->bss_conf.assoc &&
            mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id)
-               ieee80211_connection_loss(vif);
+               iwl_mvm_connection_loss(mvm, vif, "D0i3");
 }
 
 void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq)
index 192b74bc8cf67270a7db805f846f16abf7a23081..e68a475e307194cd140a2708fc664e8a8f7f6f11 100644 (file)
@@ -67,7 +67,7 @@
 #include "fw-api.h"
 #include "mvm.h"
 
-/* Maps the driver specific channel width definition to the the fw values */
+/* Maps the driver specific channel width definition to the fw values */
 u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef)
 {
        switch (chandef->width) {
index 9140b0b701c75cd316c8d1063fdc1e4286339f7c..f9928f2c125f726bbf89474096bd47990bfb86eb 100644 (file)
@@ -1277,9 +1277,7 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                                        info->status.ampdu_ack_len);
                }
        } else {
-       /*
-        * For legacy, update frame history with for each Tx retry.
-        */
+               /* For legacy, update frame history with for each Tx retry. */
                retries = info->status.rates[0].count - 1;
                /* HW doesn't send more than 15 retries */
                retries = min(retries, 15);
@@ -1615,9 +1613,9 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search)
 static void rs_update_rate_tbl(struct iwl_mvm *mvm,
                               struct ieee80211_sta *sta,
                               struct iwl_lq_sta *lq_sta,
-                              struct rs_rate *rate)
+                              struct iwl_scale_tbl_info *tbl)
 {
-       rs_fill_lq_cmd(mvm, sta, lq_sta, rate);
+       rs_fill_lq_cmd(mvm, sta, lq_sta, &tbl->rate);
        iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false);
 }
 
@@ -2147,7 +2145,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
                        rate->type = LQ_NONE;
                        lq_sta->search_better_tbl = 0;
                        tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
-                       rs_update_rate_tbl(mvm, sta, lq_sta, &tbl->rate);
+                       rs_update_rate_tbl(mvm, sta, lq_sta, tbl);
                }
                return;
        }
@@ -2310,7 +2308,7 @@ lq_update:
        /* Replace uCode's rate table for the destination station. */
        if (update_lq) {
                tbl->rate.index = index;
-               rs_update_rate_tbl(mvm, sta, lq_sta, &tbl->rate);
+               rs_update_rate_tbl(mvm, sta, lq_sta, tbl);
        }
 
        rs_stay_in_table(lq_sta, false);
@@ -2357,8 +2355,7 @@ lq_update:
 
                        rs_dump_rate(mvm, &tbl->rate,
                                     "Switch to SEARCH TABLE:");
-                       rs_fill_lq_cmd(mvm, sta, lq_sta, &tbl->rate);
-                       iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false);
+                       rs_update_rate_tbl(mvm, sta, lq_sta, tbl);
                } else {
                        done_search = 1;
                }
@@ -3238,7 +3235,7 @@ static void rs_fill_lq_cmd(struct iwl_mvm *mvm,
        lq_cmd->agg_frame_cnt_limit = mvmsta->max_agg_bufsize;
 
        /*
-        * In case of low latency, tell the firwmare to leave a frame in the
+        * In case of low latency, tell the firmware to leave a frame in the
         * Tx Fifo so that it can start a transaction in the same TxOP. This
         * basically allows the firmware to send bursts.
         */
@@ -3745,7 +3742,7 @@ void iwl_mvm_rate_control_unregister(void)
 
 /**
  * iwl_mvm_tx_protection - Gets LQ command, change it to enable/disable
- * Tx protection, according to this rquest and previous requests,
+ * Tx protection, according to this request and previous requests,
  * and send the LQ command.
  * @mvmsta: The station
  * @enable: Enable Tx protection?
index 6177e24f4c016d09c8496186d65c394d05bb5eb3..78ec7db64ba59e886e2a7b18a3df64f70a4ea29c 100644 (file)
@@ -362,7 +362,7 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
                                iwl_fw_dbg_trigger_check_stop(mvm, mvmsta->vif,
                                                              trig);
                        if (trig_check && rx_status->signal < rssi)
-                               iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL, 0);
+                               iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL);
                }
        }
 
@@ -552,7 +552,7 @@ iwl_mvm_rx_stats_check_trigger(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt)
        if (le32_to_cpup((__le32 *) (pkt->data + trig_offset)) < trig_thold)
                return;
 
-       iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL, 0);
+       iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL);
 }
 
 void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
index a75bb150ea275ef18fd6449e8c1d5543fc466bf6..74e1c86289dcbcedc1f5c7b963e468095de7cf25 100644 (file)
@@ -935,6 +935,8 @@ int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm,
 
        cmd->n_channels = (u8)req->n_channels;
 
+       cmd->delay = cpu_to_le32(req->delay);
+
        if (iwl_mvm_scan_pass_all(mvm, req))
                flags |= IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL;
        else
@@ -1177,6 +1179,18 @@ static bool iwl_mvm_find_scan_type(struct iwl_mvm *mvm,
        return false;
 }
 
+static int iwl_mvm_find_first_scan(struct iwl_mvm *mvm,
+                                  enum iwl_umac_scan_uid_type type)
+{
+       int i;
+
+       for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++)
+               if (mvm->scan_uid[i] & type)
+                       return i;
+
+       return i;
+}
+
 static u32 iwl_generate_scan_uid(struct iwl_mvm *mvm,
                                 enum iwl_umac_scan_uid_type type)
 {
@@ -1436,7 +1450,13 @@ int iwl_mvm_sched_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                                cpu_to_le16(req->interval / MSEC_PER_SEC);
        sec_part->schedule[0].iter_count = 0xff;
 
-       sec_part->delay = 0;
+       if (req->delay > U16_MAX) {
+               IWL_DEBUG_SCAN(mvm,
+                              "delay value is > 16-bits, set to max possible\n");
+               sec_part->delay = cpu_to_le16(U16_MAX);
+       } else {
+               sec_part->delay = cpu_to_le16(req->delay);
+       }
 
        iwl_mvm_build_unified_scan_probe(mvm, vif, ies, &sec_part->preq,
                req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR ?
@@ -1613,3 +1633,54 @@ int iwl_mvm_scan_size(struct iwl_mvm *mvm)
                mvm->fw->ucode_capa.n_scan_channels +
                sizeof(struct iwl_scan_probe_req);
 }
+
+/*
+ * This function is used in nic restart flow, to inform mac80211 about scans
+ * that was aborted by restart flow or by an assert.
+ */
+void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm)
+{
+       if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
+               u32 uid, i;
+
+               uid = iwl_mvm_find_first_scan(mvm, IWL_UMAC_SCAN_UID_REG_SCAN);
+               if (uid < IWL_MVM_MAX_SIMULTANEOUS_SCANS) {
+                       ieee80211_scan_completed(mvm->hw, true);
+                       mvm->scan_uid[uid] = 0;
+               }
+               uid = iwl_mvm_find_first_scan(mvm,
+                                             IWL_UMAC_SCAN_UID_SCHED_SCAN);
+               if (uid < IWL_MVM_MAX_SIMULTANEOUS_SCANS && !mvm->restart_fw) {
+                       ieee80211_sched_scan_stopped(mvm->hw);
+                       mvm->scan_uid[uid] = 0;
+               }
+
+               /* We shouldn't have any UIDs still set.  Loop over all the
+                * UIDs to make sure there's nothing left there and warn if
+                * any is found.
+                */
+               for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++) {
+                       if (WARN_ONCE(mvm->scan_uid[i],
+                                     "UMAC scan UID %d was not cleaned\n",
+                                     mvm->scan_uid[i]))
+                               mvm->scan_uid[i] = 0;
+               }
+       } else {
+               switch (mvm->scan_status) {
+               case IWL_MVM_SCAN_NONE:
+                       break;
+               case IWL_MVM_SCAN_OS:
+                       ieee80211_scan_completed(mvm->hw, true);
+                       break;
+               case IWL_MVM_SCAN_SCHED:
+                       /*
+                        * Sched scan will be restarted by mac80211 in
+                        * restart_hw, so do not report if FW is about to be
+                        * restarted.
+                        */
+                       if (!mvm->restart_fw)
+                               ieee80211_sched_scan_stopped(mvm->hw);
+                       break;
+               }
+       }
+}
index 50f9288368af169fe4eb5c31b8316f46dfd35c97..1845b79487c81b446e0432bc385e9fc12884ff68 100644 (file)
@@ -209,9 +209,8 @@ static int iwl_mvm_tdls_sta_init(struct iwl_mvm *mvm,
 {
        unsigned long used_hw_queues;
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
-       unsigned int wdg_timeout = iwlmvm_mod_params.tfd_q_hang_detect ?
-                                       mvm->cfg->base_params->wd_timeout :
-                                       IWL_WATCHDOG_DISABLED;
+       unsigned int wdg_timeout =
+               iwl_mvm_get_wd_timeout(mvm, NULL, true, false);
        u32 ac;
 
        lockdep_assert_held(&mvm->mutex);
@@ -491,8 +490,18 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
 
        if (vif->type == NL80211_IFTYPE_STATION &&
            mvmvif->ap_sta_id == mvm_sta->sta_id) {
+               ret = iwl_mvm_drain_sta(mvm, mvm_sta, true);
+               if (ret)
+                       return ret;
                /* flush its queues here since we are freeing mvm_sta */
                ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, true);
+               if (ret)
+                       return ret;
+               ret = iwl_trans_wait_tx_queue_empty(mvm->trans,
+                                                   mvm_sta->tfd_queue_msk);
+               if (ret)
+                       return ret;
+               ret = iwl_mvm_drain_sta(mvm, mvm_sta, false);
 
                /* if we are associated - we can't remove the AP STA now */
                if (vif->bss_conf.assoc)
@@ -971,9 +980,8 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 {
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
        struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
-       unsigned int wdg_timeout = iwlmvm_mod_params.tfd_q_hang_detect ?
-                                       mvm->cfg->base_params->wd_timeout :
-                                       IWL_WATCHDOG_DISABLED;
+       unsigned int wdg_timeout =
+               iwl_mvm_get_wd_timeout(mvm, vif, sta->tdls, false);
        int queue, fifo, ret;
        u16 ssn;
 
@@ -1120,8 +1128,12 @@ int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        spin_unlock_bh(&mvmsta->lock);
 
        if (old_state >= IWL_AGG_ON) {
+               iwl_mvm_drain_sta(mvm, mvmsta, true);
                if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), true))
                        IWL_ERR(mvm, "Couldn't flush the AGG queue\n");
+               iwl_trans_wait_tx_queue_empty(mvm->trans,
+                                             mvmsta->tfd_queue_msk);
+               iwl_mvm_drain_sta(mvm, mvmsta, false);
 
                iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false);
 
@@ -1702,8 +1714,8 @@ void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm,
        mvm_sta->disable_tx = disable;
 
        /*
-        * Tell mac80211 to start/stop queueing tx for this station,
-        * but don't stop queueing if there are still pending frames
+        * Tell mac80211 to start/stop queuing tx for this station,
+        * but don't stop queuing if there are still pending frames
         * for this station.
         */
        if (disable || !atomic_read(&mvm->pending_frames[mvm_sta->sta_id]))
index d8f48975ad087db1fad57b9f764fcb137416e2d0..748f5dc3f9f4337952efc84fe93e5bfb89f97c81 100644 (file)
@@ -150,7 +150,7 @@ struct iwl_mvm_vif;
  * DOC: station table - AP Station in STA mode
  *
  * %iwl_mvm_vif includes the index of the AP station in the fw's STA table:
- * %ap_sta_id. To get the point to the coresponsding %ieee80211_sta,
+ * %ap_sta_id. To get the point to the corresponding %ieee80211_sta,
  * &fw_id_to_mac_id can be used. Due to the way the fw works, we must not remove
  * the AP station from the fw before setting the MAC context as unassociated.
  * Hence, %fw_id_to_mac_id[%ap_sta_id] will be NULLed when the AP station is
@@ -209,14 +209,14 @@ struct iwl_mvm_vif;
  * When a trigger frame is received, mac80211 tells the driver to send frames
  * from the AMPDU queues or sends frames to non-aggregation queues itself,
  * depending on which ACs are delivery-enabled and what TID has frames to
- * transmit. Note that mac80211 has all the knowledege since all the non-agg
+ * transmit. Note that mac80211 has all the knowledge since all the non-agg
  * frames are buffered / filtered, and the driver tells mac80211 about agg
  * frames). The driver needs to tell the fw to let frames out even if the
  * station is asleep. This is done by %iwl_mvm_sta_modify_sleep_tx_count.
  *
  * When we receive a frame from that station with PM bit unset, the driver
  * needs to let the fw know that this station isn't asleep any more. This is
- * done by %iwl_mvm_sta_modify_ps_wake in response to mac80211 signalling the
+ * done by %iwl_mvm_sta_modify_ps_wake in response to mac80211 signaling the
  * station's wakeup.
  *
  * For a GO, the Service Period might be cut short due to an absence period
index 8d179ab67cc237026e8653d18e791d826d9ee0e4..fd7b0d36f9a620b8e99dcc7b643806b76f09af4d 100644 (file)
@@ -119,7 +119,7 @@ void iwl_mvm_roc_done_wk(struct work_struct *wk)
 
        /*
         * Flush the offchannel queue -- this is called when the time
-        * event finishes or is cancelled, so that frames queued for it
+        * event finishes or is canceled, so that frames queued for it
         * won't get stuck on the queue and be transmitted in the next
         * time event.
         * We have to send the command asynchronously since this cannot
@@ -187,7 +187,8 @@ static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm,
                return false;
        if (errmsg)
                IWL_ERR(mvm, "%s\n", errmsg);
-       ieee80211_connection_loss(vif);
+
+       iwl_mvm_connection_loss(mvm, vif, errmsg);
        return true;
 }
 
@@ -196,19 +197,24 @@ iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm,
                             struct iwl_mvm_time_event_data *te_data,
                             struct iwl_time_event_notif *notif)
 {
-       if (!le32_to_cpu(notif->status)) {
-               if (te_data->vif->type == NL80211_IFTYPE_STATION)
-                       ieee80211_connection_loss(te_data->vif);
+       struct ieee80211_vif *vif = te_data->vif;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (!notif->status)
                IWL_DEBUG_TE(mvm, "CSA time event failed to start\n");
-               iwl_mvm_te_clear_data(mvm, te_data);
-               return;
-       }
 
        switch (te_data->vif->type) {
        case NL80211_IFTYPE_AP:
+               if (!notif->status)
+                       mvmvif->csa_failed = true;
                iwl_mvm_csa_noa_start(mvm);
                break;
        case NL80211_IFTYPE_STATION:
+               if (!notif->status) {
+                       iwl_mvm_connection_loss(mvm, vif,
+                                               "CSA TE failed to start");
+                       break;
+               }
                iwl_mvm_csa_client_absent(mvm, te_data->vif);
                ieee80211_chswitch_done(te_data->vif, true);
                break;
@@ -222,6 +228,44 @@ iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm,
        iwl_mvm_te_clear_data(mvm, te_data);
 }
 
+static void iwl_mvm_te_check_trigger(struct iwl_mvm *mvm,
+                                    struct iwl_time_event_notif *notif,
+                                    struct iwl_mvm_time_event_data *te_data)
+{
+       struct iwl_fw_dbg_trigger_tlv *trig;
+       struct iwl_fw_dbg_trigger_time_event *te_trig;
+       int i;
+
+       if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TIME_EVENT))
+               return;
+
+       trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TIME_EVENT);
+       te_trig = (void *)trig->data;
+
+       if (!iwl_fw_dbg_trigger_check_stop(mvm, te_data->vif, trig))
+               return;
+
+       for (i = 0; i < ARRAY_SIZE(te_trig->time_events); i++) {
+               u32 trig_te_id = le32_to_cpu(te_trig->time_events[i].id);
+               u32 trig_action_bitmap =
+                       le32_to_cpu(te_trig->time_events[i].action_bitmap);
+               u32 trig_status_bitmap =
+                       le32_to_cpu(te_trig->time_events[i].status_bitmap);
+
+               if (trig_te_id != te_data->id ||
+                   !(trig_action_bitmap & le32_to_cpu(notif->action)) ||
+                   !(trig_status_bitmap & BIT(le32_to_cpu(notif->status))))
+                       continue;
+
+               iwl_mvm_fw_dbg_collect_trig(mvm, trig,
+                                           "Time event %d Action 0x%x received status: %d",
+                                           te_data->id,
+                                           le32_to_cpu(notif->action),
+                                           le32_to_cpu(notif->status));
+               break;
+       }
+}
+
 /*
  * Handles a FW notification for an event that is known to the driver.
  *
@@ -239,6 +283,8 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
                     le32_to_cpu(notif->unique_id),
                     le32_to_cpu(notif->action));
 
+       iwl_mvm_te_check_trigger(mvm, notif, te_data);
+
        /*
         * The FW sends the start/end time event notifications even for events
         * that it fails to schedule. This is indicated in the status field of
@@ -248,11 +294,16 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
         * events in the system).
         */
        if (!le32_to_cpu(notif->status)) {
-               bool start = le32_to_cpu(notif->action) &
-                               TE_V2_NOTIF_HOST_EVENT_START;
-               IWL_WARN(mvm, "Time Event %s notification failure\n",
-                        start ? "start" : "end");
-               if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, NULL)) {
+               const char *msg;
+
+               if (notif->action & cpu_to_le32(TE_V2_NOTIF_HOST_EVENT_START))
+                       msg = "Time Event start notification failure";
+               else
+                       msg = "Time Event end notification failure";
+
+               IWL_DEBUG_TE(mvm, "%s\n", msg);
+
+               if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, msg)) {
                        iwl_mvm_te_clear_data(mvm, te_data);
                        return;
                }
@@ -315,6 +366,8 @@ static int iwl_mvm_aux_roc_te_handle_notif(struct iwl_mvm *mvm,
        if (!aux_roc_te) /* Not a Aux ROC time event */
                return -EINVAL;
 
+       iwl_mvm_te_check_trigger(mvm, notif, te_data);
+
        if (!le32_to_cpu(notif->status)) {
                IWL_DEBUG_TE(mvm,
                             "ERROR: Aux ROC Time Event %s notification failure\n",
@@ -769,7 +822,7 @@ void iwl_mvm_stop_roc(struct iwl_mvm *mvm)
         * Iterate over the list of aux roc time events and find the time
         * event that is associated with a BSS interface.
         * This assumes that a BSS interface can have only a single time
-        * event at any given time and this time event coresponds to a ROC
+        * event at any given time and this time event corresponds to a ROC
         * request
         */
        list_for_each_entry(te_data, &mvm->aux_roc_te_list, list) {
index 6f6b35db3ab8eb9b71c8bf178d7227254340a41a..de4fbc6d57f150130e095fbec5471e9149d951bf 100644 (file)
@@ -147,7 +147,7 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm,
  * @vif: the virtual interface for which the session is issued
  *
  * This functions cancels the session protection which is an act of good
- * citizenship. If it is not needed any more it should be cancelled because
+ * citizenship. If it is not needed any more it should be canceled because
  * the other bindings wait for the medium during that time.
  * This funtions doesn't sleep.
  */
@@ -162,7 +162,7 @@ int iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm,
                                struct iwl_device_cmd *cmd);
 
 /**
- * iwl_mvm_start_p2p_roc - start remain on channel for p2p device functionlity
+ * iwl_mvm_start_p2p_roc - start remain on channel for p2p device functionality
  * @mvm: the mvm component
  * @vif: the virtual interface for which the roc is requested. It is assumed
  * that the vif type is NL80211_IFTYPE_P2P_DEVICE
index ba34dda1ae36bc8ca562f2cbae7a443bf3bf1868..ef32e177f662b3ba03772e02b1c9b9e512bcd64f 100644 (file)
@@ -1049,6 +1049,14 @@ out:
        return 0;
 }
 
+/*
+ * Note that there are transports that buffer frames before they reach
+ * the firmware. This means that after flush_tx_path is called, the
+ * queue might not be empty. The race-free way to handle this is to:
+ * 1) set the station as draining
+ * 2) flush the Tx path
+ * 3) wait for the transport queues to be empty
+ */
 int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync)
 {
        int ret;
index 435faee0a28eee1d674d95f07fc934baef2e13b0..bc55a8b82db6d88ad42ef1151e0c5c53c2cbf64c 100644 (file)
@@ -122,7 +122,7 @@ int iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u8 id,
 }
 
 /*
- * We assume that the caller set the status to the sucess value
+ * We assume that the caller set the status to the success value
  */
 int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd,
                            u32 *status)
@@ -737,7 +737,7 @@ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init)
 }
 
 /**
- * iwl_mvm_update_smps - Get a requst to change the SMPS mode
+ * iwl_mvm_update_smps - Get a request to change the SMPS mode
  * @req_type: The part of the driver who call for a change.
  * @smps_requests: The request to change the SMPS mode.
  *
@@ -921,3 +921,71 @@ struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm)
 
        return bss_iter_data.vif;
 }
+
+unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm,
+                                   struct ieee80211_vif *vif,
+                                   bool tdls, bool cmd_q)
+{
+       struct iwl_fw_dbg_trigger_tlv *trigger;
+       struct iwl_fw_dbg_trigger_txq_timer *txq_timer;
+       unsigned int default_timeout =
+               cmd_q ? IWL_DEF_WD_TIMEOUT : mvm->cfg->base_params->wd_timeout;
+
+       if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TXQ_TIMERS))
+               return iwlmvm_mod_params.tfd_q_hang_detect ?
+                       default_timeout : IWL_WATCHDOG_DISABLED;
+
+       trigger = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TXQ_TIMERS);
+       txq_timer = (void *)trigger->data;
+
+       if (tdls)
+               return le32_to_cpu(txq_timer->tdls);
+
+       if (cmd_q)
+               return le32_to_cpu(txq_timer->command_queue);
+
+       if (WARN_ON(!vif))
+               return default_timeout;
+
+       switch (ieee80211_vif_type_p2p(vif)) {
+       case NL80211_IFTYPE_ADHOC:
+               return le32_to_cpu(txq_timer->ibss);
+       case NL80211_IFTYPE_STATION:
+               return le32_to_cpu(txq_timer->bss);
+       case NL80211_IFTYPE_AP:
+               return le32_to_cpu(txq_timer->softap);
+       case NL80211_IFTYPE_P2P_CLIENT:
+               return le32_to_cpu(txq_timer->p2p_client);
+       case NL80211_IFTYPE_P2P_GO:
+               return le32_to_cpu(txq_timer->p2p_go);
+       case NL80211_IFTYPE_P2P_DEVICE:
+               return le32_to_cpu(txq_timer->p2p_device);
+       default:
+               WARN_ON(1);
+               return mvm->cfg->base_params->wd_timeout;
+       }
+}
+
+void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                            const char *errmsg)
+{
+       struct iwl_fw_dbg_trigger_tlv *trig;
+       struct iwl_fw_dbg_trigger_mlme *trig_mlme;
+
+       if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_MLME))
+               goto out;
+
+       trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_MLME);
+       trig_mlme = (void *)trig->data;
+       if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
+               goto out;
+
+       if (trig_mlme->stop_connection_loss &&
+           --trig_mlme->stop_connection_loss)
+               goto out;
+
+       iwl_mvm_fw_dbg_collect_trig(mvm, trig, "%s", errmsg);
+
+out:
+       ieee80211_connection_loss(vif);
+}
index 7b7e2f223fb230fad922e7fcaec08ec8ae7eb5ac..7ff69c642103f1febeea67d9c082a035039b4121 100644 (file)
@@ -600,9 +600,11 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans,
                if (pkt->len_n_flags == cpu_to_le32(FH_RSCSR_FRAME_INVALID))
                        break;
 
-               IWL_DEBUG_RX(trans, "cmd at offset %d: %s (0x%.2x)\n",
-                       rxcb._offset, get_cmd_string(trans_pcie, pkt->hdr.cmd),
-                       pkt->hdr.cmd);
+               IWL_DEBUG_RX(trans,
+                            "cmd at offset %d: %s (0x%.2x, seq 0x%x)\n",
+                            rxcb._offset,
+                            get_cmd_string(trans_pcie, pkt->hdr.cmd),
+                            pkt->hdr.cmd, le16_to_cpu(pkt->hdr.sequence));
 
                len = iwl_rx_packet_len(pkt);
                len += sizeof(u32); /* account for status word */
index dc247325d8d7f9ebd11472bcf6c64e9a48d7bb14..2de8fbfe4edf4d6c6997307fb91052177fd7e6e4 100644 (file)
@@ -691,11 +691,15 @@ static int iwl_pcie_rsa_race_bug_wa(struct iwl_trans *trans)
 {
        u32 val, loop = 1000;
 
-       /* Check the RSA semaphore is accessible - if not, we are in trouble */
+       /*
+        * Check the RSA semaphore is accessible.
+        * If the HW isn't locked and the rsa semaphore isn't accessible,
+        * we are in trouble.
+        */
        val = iwl_read_prph(trans, PREG_AUX_BUS_WPROT_0);
        if (val & (BIT(1) | BIT(17))) {
-               IWL_ERR(trans,
-                       "can't access the RSA semaphore it is write protected\n");
+               IWL_INFO(trans,
+                        "can't access the RSA semaphore it is write protected\n");
                return 0;
        }
 
@@ -719,10 +723,10 @@ static int iwl_pcie_rsa_race_bug_wa(struct iwl_trans *trans)
        return -EIO;
 }
 
-static int iwl_pcie_load_cpu_sections_8000b(struct iwl_trans *trans,
-                                           const struct fw_img *image,
-                                           int cpu,
-                                           int *first_ucode_section)
+static int iwl_pcie_load_cpu_sections_8000(struct iwl_trans *trans,
+                                          const struct fw_img *image,
+                                          int cpu,
+                                          int *first_ucode_section)
 {
        int shift_param;
        int i, ret = 0, sec_num = 0x1;
@@ -917,20 +921,16 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
        }
 
        /* release CPU reset */
-       if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
-               iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT);
-       else
-               iwl_write32(trans, CSR_RESET, 0);
+       iwl_write32(trans, CSR_RESET, 0);
 
        return 0;
 }
 
-static int iwl_pcie_load_given_ucode_8000b(struct iwl_trans *trans,
-                                          const struct fw_img *image)
+static int iwl_pcie_load_given_ucode_8000(struct iwl_trans *trans,
+                                         const struct fw_img *image)
 {
        int ret = 0;
        int first_ucode_section;
-       u32 reg;
 
        IWL_DEBUG_FW(trans, "working with %s CPU\n",
                     image->is_dual_cpus ? "Dual" : "Single");
@@ -948,38 +948,23 @@ static int iwl_pcie_load_given_ucode_8000b(struct iwl_trans *trans,
        iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT);
 
        /* load to FW the binary Secured sections of CPU1 */
-       ret = iwl_pcie_load_cpu_sections_8000b(trans, image, 1,
-                                              &first_ucode_section);
+       ret = iwl_pcie_load_cpu_sections_8000(trans, image, 1,
+                                             &first_ucode_section);
        if (ret)
                return ret;
 
        /* load to FW the binary sections of CPU2 */
-       ret = iwl_pcie_load_cpu_sections_8000b(trans, image, 2,
-                                              &first_ucode_section);
+       ret = iwl_pcie_load_cpu_sections_8000(trans, image, 2,
+                                             &first_ucode_section);
        if (ret)
                return ret;
 
-       /* wait for image verification to complete  */
-       ret = iwl_poll_prph_bit(trans, LMPM_SECURE_BOOT_CPU1_STATUS_ADDR_B0,
-                               LMPM_SECURE_BOOT_STATUS_SUCCESS,
-                               LMPM_SECURE_BOOT_STATUS_SUCCESS,
-                               LMPM_SECURE_TIME_OUT);
-       if (ret < 0) {
-               reg = iwl_read_prph(trans,
-                                   LMPM_SECURE_BOOT_CPU1_STATUS_ADDR_B0);
-
-               IWL_ERR(trans, "Timeout on secure boot process, reg = %x\n",
-                       reg);
-               return ret;
-       }
-
        return 0;
 }
 
 static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
                                   const struct fw_img *fw, bool run_in_rfkill)
 {
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        int ret;
        bool hw_rfkill;
 
@@ -1009,9 +994,6 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
                return ret;
        }
 
-       /* init ref_count to 1 (should be cleared when ucode is loaded) */
-       trans_pcie->ref_count = 1;
-
        /* make sure rfkill handshake bits are cleared */
        iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
        iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR,
@@ -1026,9 +1008,8 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
        iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
 
        /* Load the given image to the HW */
-       if ((trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) &&
-           (CSR_HW_REV_STEP(trans->hw_rev) != SILICON_A_STEP))
-               return iwl_pcie_load_given_ucode_8000b(trans, fw);
+       if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+               return iwl_pcie_load_given_ucode_8000(trans, fw);
        else
                return iwl_pcie_load_given_ucode(trans, fw);
 }
@@ -1330,6 +1311,9 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans,
        trans_pcie->bc_table_dword = trans_cfg->bc_table_dword;
        trans_pcie->scd_set_active = trans_cfg->scd_set_active;
 
+       /* init ref_count to 1 (should be cleared when ucode is loaded) */
+       trans_pcie->ref_count = 1;
+
        /* 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
index 074f716020aae4e28d3e8340da846514065a94db..01f56c7df8b501f7b6390a1f2a12bd1a70168a26 100644 (file)
@@ -1315,7 +1315,8 @@ static void setup_arp_tx(struct rtl_priv *rtlpriv, struct rtl_ps_ctl *ppsc)
 }
 
 /*should call before software enc*/
-u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx)
+u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx,
+                      bool is_enc)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
@@ -1344,7 +1345,9 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx)
                break;
        }
 
-       offset = mac_hdr_len + SNAP_SIZE + encrypt_header_len;
+       offset = mac_hdr_len + SNAP_SIZE;
+       if (is_enc)
+               offset += encrypt_header_len;
        ether_type = be16_to_cpup((__be16 *)(skb->data + offset));
 
        if (ETH_P_IP == ether_type) {
index ff9a4bfd45154aaaf37055d82eba1d32b0276a83..74233d601a909b12b6eb33dcd1793db28efca3da 100644 (file)
@@ -120,7 +120,8 @@ bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx);
 int rtlwifi_rate_mapping(struct ieee80211_hw *hw, bool isht,
                         bool isvht, u8 desc_rate);
 bool rtl_tx_mgmt_proc(struct ieee80211_hw *hw, struct sk_buff *skb);
-u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx);
+u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx,
+                      bool is_enc);
 
 void rtl_beacon_statistic(struct ieee80211_hw *hw, struct sk_buff *skb);
 int rtl_tx_agg_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
index 8c45cf44ce24bac6393902ae2daf708b0ce11e6a..f46c9d7f652813a46fe6d8bbe24a48ab814afd6f 100644 (file)
@@ -887,7 +887,7 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
                                unicast = true;
                                rtlpriv->stats.rxbytesunicast += skb->len;
                        }
-                       rtl_is_special_data(hw, skb, false);
+                       rtl_is_special_data(hw, skb, false, true);
 
                        if (ieee80211_is_data(fc)) {
                                rtlpriv->cfg->ops->led_control(hw, LED_CTL_RX);
index 7863bd278b227f6c7dd5057405d64a042f2fe69a..74c14ce28238eed70f8ad798f0724642147411d3 100644 (file)
@@ -56,7 +56,8 @@ static u8 _rtl_rc_get_highest_rix(struct rtl_priv *rtlpriv,
                wireless_mode = sta_entry->wireless_mode;
        }
 
-       if (rtl_is_special_data(rtlpriv->mac80211.hw, skb, true) || not_data) {
+       if (rtl_is_special_data(rtlpriv->mac80211.hw, skb, true, false) ||
+           not_data) {
                return 0;
        } else {
                if (rtlhal->current_bandtype == BAND_ON_2_4G) {
@@ -201,7 +202,7 @@ static void rtl_tx_status(void *ppriv,
        if (!priv_sta || !ieee80211_is_data(fc))
                return;
 
-       if (rtl_is_special_data(mac->hw, skb, true))
+       if (rtl_is_special_data(mac->hw, skb, true, true))
                return;
 
        if (is_multicast_ether_addr(ieee80211_get_DA(hdr)) ||
index d1e9a13be910b584d5e17394822ba3c1ff2a9ba4..5d54d16a59e7151942246ff1d737af28c332fadc 100644 (file)
@@ -1608,7 +1608,7 @@ int wl1251_free_hw(struct wl1251 *wl)
 }
 EXPORT_SYMBOL_GPL(wl1251_free_hw);
 
-MODULE_DESCRIPTION("TI wl1251 Wireles LAN Driver Core");
+MODULE_DESCRIPTION("TI wl1251 Wireless LAN Driver Core");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Kalle Valo <kvalo@adurom.com>");
 MODULE_FIRMWARE(WL1251_FW_NAME);
index 7929fac13e1c55ac072d46f6c2d3a6ea10528fa7..107714e4405fd348331c6f2ecdbfc18c43709d51 100644 (file)
@@ -73,4 +73,5 @@ source "drivers/nfc/microread/Kconfig"
 source "drivers/nfc/nfcmrvl/Kconfig"
 source "drivers/nfc/st21nfca/Kconfig"
 source "drivers/nfc/st21nfcb/Kconfig"
+source "drivers/nfc/nxp-nci/Kconfig"
 endmenu
index 6b23a2c6e34adf7c1ed35e0aa7e40aa2e9a7cc66..a4292d790f9b900130462d7f4bf7b3f64e3dec7b 100644 (file)
@@ -13,5 +13,6 @@ obj-$(CONFIG_NFC_MRVL)                += nfcmrvl/
 obj-$(CONFIG_NFC_TRF7970A)     += trf7970a.o
 obj-$(CONFIG_NFC_ST21NFCA)     += st21nfca/
 obj-$(CONFIG_NFC_ST21NFCB)     += st21nfcb/
+obj-$(CONFIG_NFC_NXP_NCI)      += nxp-nci/
 
 ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
index df85cd3d9db0c76e1d80ebbf3bcc204f3e3f8a0c..661e2c8143c40a8e4ab6e98b76a007eafa9a6fd4 100644 (file)
@@ -286,7 +286,7 @@ static int microread_i2c_probe(struct i2c_client *client,
        if (r < 0)
                goto err_irq;
 
-       nfc_info(&client->dev, "Probed");
+       nfc_info(&client->dev, "Probed\n");
 
        return 0;
 
index 85e8bcf986936123bdab959e5b01fb8738d80e79..ad4933cefbd1b2c60ae5ef23aeece275d2fcf0fd 100644 (file)
@@ -111,7 +111,7 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data,
 
        priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols, 0, 0);
        if (!priv->ndev) {
-               nfc_err(dev, "nci_allocate_device failed");
+               nfc_err(dev, "nci_allocate_device failed\n");
                rc = -ENOMEM;
                goto error;
        }
@@ -120,7 +120,7 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data,
 
        rc = nci_register_device(priv->ndev);
        if (rc) {
-               nfc_err(dev, "nci_register_device failed %d", rc);
+               nfc_err(dev, "nci_register_device failed %d\n", rc);
                nci_free_device(priv->ndev);
                goto error;
        }
index 3221ca37d6c9463700af6ed4f5a25fe7d4825379..6cf15c1a261831dadc5194bdc3615c7362c6ce0d 100644 (file)
@@ -80,7 +80,7 @@ static void nfcmrvl_bulk_complete(struct urb *urb)
        if (!urb->status) {
                if (nfcmrvl_nci_recv_frame(drv_data->priv, urb->transfer_buffer,
                                           urb->actual_length) < 0)
-                       nfc_err(&drv_data->udev->dev, "corrupted Rx packet");
+                       nfc_err(&drv_data->udev->dev, "corrupted Rx packet\n");
        }
 
        if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
@@ -96,7 +96,7 @@ static void nfcmrvl_bulk_complete(struct urb *urb)
                 */
                if (err != -EPERM && err != -ENODEV)
                        nfc_err(&drv_data->udev->dev,
-                               "urb %p failed to resubmit (%d)", urb, -err);
+                               "urb %p failed to resubmit (%d)\n", urb, -err);
                usb_unanchor_urb(urb);
        }
 }
@@ -137,7 +137,7 @@ nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags)
        if (err) {
                if (err != -EPERM && err != -ENODEV)
                        nfc_err(&drv_data->udev->dev,
-                               "urb %p submission failed (%d)", urb, -err);
+                               "urb %p submission failed (%d)\n", urb, -err);
                usb_unanchor_urb(urb);
        }
 
@@ -153,7 +153,7 @@ static void nfcmrvl_tx_complete(struct urb *urb)
        struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
        struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
 
-       nfc_info(priv->dev, "urb %p status %d count %d",
+       nfc_info(priv->dev, "urb %p status %d count %d\n",
                 urb, urb->status, urb->actual_length);
 
        spin_lock(&drv_data->txlock);
@@ -253,7 +253,7 @@ static int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv,
        if (err) {
                if (err != -EPERM && err != -ENODEV)
                        nfc_err(&drv_data->udev->dev,
-                               "urb %p submission failed (%d)", urb, -err);
+                               "urb %p submission failed (%d)\n", urb, -err);
                kfree(urb->setup_packet);
                usb_unanchor_urb(urb);
        } else {
@@ -293,7 +293,7 @@ static int nfcmrvl_probe(struct usb_interface *intf,
        int i;
        struct usb_device *udev = interface_to_usbdev(intf);
 
-       nfc_info(&udev->dev, "intf %p id %p", intf, id);
+       nfc_info(&udev->dev, "intf %p id %p\n", intf, id);
 
        drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL);
        if (!drv_data)
@@ -348,7 +348,7 @@ static void nfcmrvl_disconnect(struct usb_interface *intf)
        if (!drv_data)
                return;
 
-       nfc_info(&drv_data->udev->dev, "intf %p", intf);
+       nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
 
        nfcmrvl_nci_unregister_dev(drv_data->priv);
 
@@ -360,7 +360,7 @@ static int nfcmrvl_suspend(struct usb_interface *intf, pm_message_t message)
 {
        struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
 
-       nfc_info(&drv_data->udev->dev, "intf %p", intf);
+       nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
 
        if (drv_data->suspend_count++)
                return 0;
@@ -401,7 +401,7 @@ static int nfcmrvl_resume(struct usb_interface *intf)
        struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
        int err = 0;
 
-       nfc_info(&drv_data->udev->dev, "intf %p", intf);
+       nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
 
        if (--drv_data->suspend_count)
                return 0;
diff --git a/drivers/nfc/nxp-nci/Kconfig b/drivers/nfc/nxp-nci/Kconfig
new file mode 100644 (file)
index 0000000..37b4061
--- /dev/null
@@ -0,0 +1,25 @@
+config NFC_NXP_NCI
+       tristate "NXP-NCI NFC driver"
+       depends on NFC_NCI
+       default n
+       ---help---
+         Generic core driver for NXP NCI chips such as the NPC100
+         or PN7150 families.
+         This is a driver based on the NCI NFC kernel layers and
+         will thus not work with NXP libnfc library.
+
+         To compile this driver as a module, choose m here. The module will
+         be called nxp_nci.
+         Say N if unsure.
+
+config NFC_NXP_NCI_I2C
+       tristate "NXP-NCI I2C support"
+       depends on NFC_NXP_NCI && I2C
+       ---help---
+         This module adds support for an I2C interface to the NXP NCI
+         chips.
+         Select this if your platform is using the I2C bus.
+
+         To compile this driver as a module, choose m here. The module will
+         be called nxp_nci_i2c.
+         Say Y if unsure.
diff --git a/drivers/nfc/nxp-nci/Makefile b/drivers/nfc/nxp-nci/Makefile
new file mode 100644 (file)
index 0000000..c008be3
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# Makefile for NXP-NCI NFC driver
+#
+
+nxp-nci-objs = core.o firmware.o
+nxp-nci_i2c-objs = i2c.o
+
+obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci.o
+obj-$(CONFIG_NFC_NXP_NCI_I2C) += nxp-nci_i2c.o
+
+ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
diff --git a/drivers/nfc/nxp-nci/core.c b/drivers/nfc/nxp-nci/core.c
new file mode 100644 (file)
index 0000000..8979636
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Generic driver for NXP NCI NFC chips
+ *
+ * Copyright (C) 2014  NXP Semiconductors  All rights reserved.
+ *
+ * Authors: Clément Perrochaud <clement.perrochaud@nxp.com>
+ *
+ * Derived from PN544 device driver:
+ * Copyright (C) 2012  Intel Corporation. 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/delay.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <linux/platform_data/nxp-nci.h>
+
+#include <net/nfc/nci_core.h>
+
+#include "nxp-nci.h"
+
+#define NXP_NCI_HDR_LEN        4
+
+#define NXP_NCI_NFC_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \
+                              NFC_PROTO_MIFARE_MASK | \
+                              NFC_PROTO_FELICA_MASK | \
+                              NFC_PROTO_ISO14443_MASK | \
+                              NFC_PROTO_ISO14443_B_MASK | \
+                              NFC_PROTO_NFC_DEP_MASK)
+
+static int nxp_nci_open(struct nci_dev *ndev)
+{
+       struct nxp_nci_info *info = nci_get_drvdata(ndev);
+       int r = 0;
+
+       mutex_lock(&info->info_lock);
+
+       if (info->mode != NXP_NCI_MODE_COLD) {
+               r = -EBUSY;
+               goto open_exit;
+       }
+
+       if (info->phy_ops->set_mode)
+               r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_NCI);
+
+       info->mode = NXP_NCI_MODE_NCI;
+
+open_exit:
+       mutex_unlock(&info->info_lock);
+       return r;
+}
+
+static int nxp_nci_close(struct nci_dev *ndev)
+{
+       struct nxp_nci_info *info = nci_get_drvdata(ndev);
+       int r = 0;
+
+       mutex_lock(&info->info_lock);
+
+       if (info->phy_ops->set_mode)
+               r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
+
+       info->mode = NXP_NCI_MODE_COLD;
+
+       mutex_unlock(&info->info_lock);
+       return r;
+}
+
+static int nxp_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
+{
+       struct nxp_nci_info *info = nci_get_drvdata(ndev);
+       int r;
+
+       if (!info->phy_ops->write) {
+               r = -ENOTSUPP;
+               goto send_exit;
+       }
+
+       if (info->mode != NXP_NCI_MODE_NCI) {
+               r = -EINVAL;
+               goto send_exit;
+       }
+
+       r = info->phy_ops->write(info->phy_id, skb);
+       if (r < 0)
+               kfree_skb(skb);
+
+send_exit:
+       return r;
+}
+
+static struct nci_ops nxp_nci_ops = {
+       .open = nxp_nci_open,
+       .close = nxp_nci_close,
+       .send = nxp_nci_send,
+       .fw_download = nxp_nci_fw_download,
+};
+
+int nxp_nci_probe(void *phy_id, struct device *pdev,
+                 struct nxp_nci_phy_ops *phy_ops, unsigned int max_payload,
+                 struct nci_dev **ndev)
+{
+       struct nxp_nci_info *info;
+       int r;
+
+       info = devm_kzalloc(pdev, sizeof(struct nxp_nci_info), GFP_KERNEL);
+       if (!info) {
+               r = -ENOMEM;
+               goto probe_exit;
+       }
+
+       info->phy_id = phy_id;
+       info->pdev = pdev;
+       info->phy_ops = phy_ops;
+       info->max_payload = max_payload;
+       INIT_WORK(&info->fw_info.work, nxp_nci_fw_work);
+       init_completion(&info->fw_info.cmd_completion);
+       mutex_init(&info->info_lock);
+
+       if (info->phy_ops->set_mode) {
+               r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
+               if (r < 0)
+                       goto probe_exit;
+       }
+
+       info->mode = NXP_NCI_MODE_COLD;
+
+       info->ndev = nci_allocate_device(&nxp_nci_ops, NXP_NCI_NFC_PROTOCOLS,
+                                        NXP_NCI_HDR_LEN, 0);
+       if (!info->ndev) {
+               r = -ENOMEM;
+               goto probe_exit;
+       }
+
+       nci_set_parent_dev(info->ndev, pdev);
+       nci_set_drvdata(info->ndev, info);
+       r = nci_register_device(info->ndev);
+       if (r < 0)
+               goto probe_exit_free_nci;
+
+       *ndev = info->ndev;
+
+       goto probe_exit;
+
+probe_exit_free_nci:
+       nci_free_device(info->ndev);
+probe_exit:
+       return r;
+}
+EXPORT_SYMBOL(nxp_nci_probe);
+
+void nxp_nci_remove(struct nci_dev *ndev)
+{
+       struct nxp_nci_info *info = nci_get_drvdata(ndev);
+
+       if (info->mode == NXP_NCI_MODE_FW)
+               nxp_nci_fw_work_complete(info, -ESHUTDOWN);
+       cancel_work_sync(&info->fw_info.work);
+
+       mutex_lock(&info->info_lock);
+
+       if (info->phy_ops->set_mode)
+               info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
+
+       nci_unregister_device(ndev);
+       nci_free_device(ndev);
+
+       mutex_unlock(&info->info_lock);
+}
+EXPORT_SYMBOL(nxp_nci_remove);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("NXP NCI NFC driver");
+MODULE_AUTHOR("Clément Perrochaud <clement.perrochaud@nxp.com>");
diff --git a/drivers/nfc/nxp-nci/firmware.c b/drivers/nfc/nxp-nci/firmware.c
new file mode 100644 (file)
index 0000000..5291797
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * Generic driver for NXP NCI NFC chips
+ *
+ * Copyright (C) 2014  NXP Semiconductors  All rights reserved.
+ *
+ * Author: Clément Perrochaud <clement.perrochaud@nxp.com>
+ *
+ * Derived from PN544 device driver:
+ * Copyright (C) 2012  Intel Corporation. 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/completion.h>
+#include <linux/firmware.h>
+#include <linux/nfc.h>
+#include <linux/unaligned/access_ok.h>
+
+#include "nxp-nci.h"
+
+/* Crypto operations can take up to 30 seconds */
+#define NXP_NCI_FW_ANSWER_TIMEOUT      msecs_to_jiffies(30000)
+
+#define NXP_NCI_FW_CMD_RESET           0xF0
+#define NXP_NCI_FW_CMD_GETVERSION      0xF1
+#define NXP_NCI_FW_CMD_CHECKINTEGRITY  0xE0
+#define NXP_NCI_FW_CMD_WRITE           0xC0
+#define NXP_NCI_FW_CMD_READ            0xA2
+#define NXP_NCI_FW_CMD_GETSESSIONSTATE 0xF2
+#define NXP_NCI_FW_CMD_LOG             0xA7
+#define NXP_NCI_FW_CMD_FORCE           0xD0
+#define NXP_NCI_FW_CMD_GET_DIE_ID      0xF4
+
+#define NXP_NCI_FW_CHUNK_FLAG  0x0400
+
+#define NXP_NCI_FW_RESULT_OK                           0x00
+#define NXP_NCI_FW_RESULT_INVALID_ADDR                 0x01
+#define NXP_NCI_FW_RESULT_GENERIC_ERROR                        0x02
+#define NXP_NCI_FW_RESULT_UNKNOWN_CMD                  0x0B
+#define NXP_NCI_FW_RESULT_ABORTED_CMD                  0x0C
+#define NXP_NCI_FW_RESULT_PLL_ERROR                    0x0D
+#define NXP_NCI_FW_RESULT_ADDR_RANGE_OFL_ERROR         0x1E
+#define NXP_NCI_FW_RESULT_BUFFER_OFL_ERROR             0x1F
+#define NXP_NCI_FW_RESULT_MEM_BSY                      0x20
+#define NXP_NCI_FW_RESULT_SIGNATURE_ERROR              0x21
+#define NXP_NCI_FW_RESULT_FIRMWARE_VERSION_ERROR       0x24
+#define NXP_NCI_FW_RESULT_PROTOCOL_ERROR               0x28
+#define NXP_NCI_FW_RESULT_SFWU_DEGRADED                        0x2A
+#define NXP_NCI_FW_RESULT_PH_STATUS_FIRST_CHUNK                0x2D
+#define NXP_NCI_FW_RESULT_PH_STATUS_NEXT_CHUNK         0x2E
+#define NXP_NCI_FW_RESULT_PH_STATUS_INTERNAL_ERROR_5   0xC5
+
+void nxp_nci_fw_work_complete(struct nxp_nci_info *info, int result)
+{
+       struct nxp_nci_fw_info *fw_info = &info->fw_info;
+       int r;
+
+       if (info->phy_ops->set_mode) {
+               r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
+               if (r < 0 && result == 0)
+                       result = -r;
+       }
+
+       info->mode = NXP_NCI_MODE_COLD;
+
+       if (fw_info->fw) {
+               release_firmware(fw_info->fw);
+               fw_info->fw = NULL;
+       }
+
+       nfc_fw_download_done(info->ndev->nfc_dev, fw_info->name, (u32) -result);
+}
+
+/* crc_ccitt cannot be used since it is computed MSB first and not LSB first */
+static u16 nxp_nci_fw_crc(u8 const *buffer, size_t len)
+{
+       u16 crc = 0xffff;
+
+       while (len--) {
+               crc = ((crc >> 8) | (crc << 8)) ^ *buffer++;
+               crc ^= (crc & 0xff) >> 4;
+               crc ^= (crc & 0xff) << 12;
+               crc ^= (crc & 0xff) << 5;
+       }
+
+       return crc;
+}
+
+static int nxp_nci_fw_send_chunk(struct nxp_nci_info *info)
+{
+       struct nxp_nci_fw_info *fw_info = &info->fw_info;
+       u16 header, crc;
+       struct sk_buff *skb;
+       size_t chunk_len;
+       size_t remaining_len;
+       int r;
+
+       skb = nci_skb_alloc(info->ndev, info->max_payload, GFP_KERNEL);
+       if (!skb) {
+               r = -ENOMEM;
+               goto chunk_exit;
+       }
+
+       chunk_len = info->max_payload - NXP_NCI_FW_HDR_LEN - NXP_NCI_FW_CRC_LEN;
+       remaining_len = fw_info->frame_size - fw_info->written;
+
+       if (remaining_len > chunk_len) {
+               header = NXP_NCI_FW_CHUNK_FLAG;
+       } else {
+               chunk_len = remaining_len;
+               header = 0x0000;
+       }
+
+       header |= chunk_len & NXP_NCI_FW_FRAME_LEN_MASK;
+       put_unaligned_be16(header, skb_put(skb, NXP_NCI_FW_HDR_LEN));
+
+       memcpy(skb_put(skb, chunk_len), fw_info->data + fw_info->written,
+              chunk_len);
+
+       crc = nxp_nci_fw_crc(skb->data, chunk_len + NXP_NCI_FW_HDR_LEN);
+       put_unaligned_be16(crc, skb_put(skb, NXP_NCI_FW_CRC_LEN));
+
+       r = info->phy_ops->write(info->phy_id, skb);
+       if (r >= 0)
+               r = chunk_len;
+
+       kfree_skb(skb);
+
+chunk_exit:
+       return r;
+}
+
+static int nxp_nci_fw_send(struct nxp_nci_info *info)
+{
+       struct nxp_nci_fw_info *fw_info = &info->fw_info;
+       long completion_rc;
+       int r;
+
+       reinit_completion(&fw_info->cmd_completion);
+
+       if (fw_info->written == 0) {
+               fw_info->frame_size = get_unaligned_be16(fw_info->data) &
+                                     NXP_NCI_FW_FRAME_LEN_MASK;
+               fw_info->data += NXP_NCI_FW_HDR_LEN;
+               fw_info->size -= NXP_NCI_FW_HDR_LEN;
+       }
+
+       if (fw_info->frame_size > fw_info->size)
+               return -EMSGSIZE;
+
+       r = nxp_nci_fw_send_chunk(info);
+       if (r < 0)
+               return r;
+
+       fw_info->written += r;
+
+       if (*fw_info->data == NXP_NCI_FW_CMD_RESET) {
+               fw_info->cmd_result = 0;
+               if (fw_info->fw)
+                       schedule_work(&fw_info->work);
+       } else {
+               completion_rc = wait_for_completion_interruptible_timeout(
+                       &fw_info->cmd_completion, NXP_NCI_FW_ANSWER_TIMEOUT);
+               if (completion_rc == 0)
+                       return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+void nxp_nci_fw_work(struct work_struct *work)
+{
+       struct nxp_nci_info *info;
+       struct nxp_nci_fw_info *fw_info;
+       int r;
+
+       fw_info = container_of(work, struct nxp_nci_fw_info, work);
+       info = container_of(fw_info, struct nxp_nci_info, fw_info);
+
+       mutex_lock(&info->info_lock);
+
+       r = fw_info->cmd_result;
+       if (r < 0)
+               goto exit_work;
+
+       if (fw_info->written == fw_info->frame_size) {
+               fw_info->data += fw_info->frame_size;
+               fw_info->size -= fw_info->frame_size;
+               fw_info->written = 0;
+       }
+
+       if (fw_info->size > 0)
+               r = nxp_nci_fw_send(info);
+
+exit_work:
+       if (r < 0 || fw_info->size == 0)
+               nxp_nci_fw_work_complete(info, r);
+       mutex_unlock(&info->info_lock);
+}
+
+int nxp_nci_fw_download(struct nci_dev *ndev, const char *firmware_name)
+{
+       struct nxp_nci_info *info = nci_get_drvdata(ndev);
+       struct nxp_nci_fw_info *fw_info = &info->fw_info;
+       int r;
+
+       mutex_lock(&info->info_lock);
+
+       if (!info->phy_ops->set_mode || !info->phy_ops->write) {
+               r = -ENOTSUPP;
+               goto fw_download_exit;
+       }
+
+       if (!firmware_name || firmware_name[0] == '\0') {
+               r = -EINVAL;
+               goto fw_download_exit;
+       }
+
+       strcpy(fw_info->name, firmware_name);
+
+       r = request_firmware(&fw_info->fw, firmware_name,
+                            ndev->nfc_dev->dev.parent);
+       if (r < 0)
+               goto fw_download_exit;
+
+       r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_FW);
+       if (r < 0) {
+               release_firmware(fw_info->fw);
+               goto fw_download_exit;
+       }
+
+       info->mode = NXP_NCI_MODE_FW;
+
+       fw_info->data = fw_info->fw->data;
+       fw_info->size = fw_info->fw->size;
+       fw_info->written = 0;
+       fw_info->frame_size = 0;
+       fw_info->cmd_result = 0;
+
+       schedule_work(&fw_info->work);
+
+fw_download_exit:
+       mutex_unlock(&info->info_lock);
+       return r;
+}
+
+static int nxp_nci_fw_read_status(u8 stat)
+{
+       switch (stat) {
+       case NXP_NCI_FW_RESULT_OK:
+               return 0;
+       case NXP_NCI_FW_RESULT_INVALID_ADDR:
+               return -EINVAL;
+       case NXP_NCI_FW_RESULT_UNKNOWN_CMD:
+               return -EINVAL;
+       case NXP_NCI_FW_RESULT_ABORTED_CMD:
+               return -EMSGSIZE;
+       case NXP_NCI_FW_RESULT_ADDR_RANGE_OFL_ERROR:
+               return -EADDRNOTAVAIL;
+       case NXP_NCI_FW_RESULT_BUFFER_OFL_ERROR:
+               return -ENOBUFS;
+       case NXP_NCI_FW_RESULT_MEM_BSY:
+               return -ENOKEY;
+       case NXP_NCI_FW_RESULT_SIGNATURE_ERROR:
+               return -EKEYREJECTED;
+       case NXP_NCI_FW_RESULT_FIRMWARE_VERSION_ERROR:
+               return -EALREADY;
+       case NXP_NCI_FW_RESULT_PROTOCOL_ERROR:
+               return -EPROTO;
+       case NXP_NCI_FW_RESULT_SFWU_DEGRADED:
+               return -EHWPOISON;
+       case NXP_NCI_FW_RESULT_PH_STATUS_FIRST_CHUNK:
+               return 0;
+       case NXP_NCI_FW_RESULT_PH_STATUS_NEXT_CHUNK:
+               return 0;
+       case NXP_NCI_FW_RESULT_PH_STATUS_INTERNAL_ERROR_5:
+               return -EINVAL;
+       default:
+               return -EIO;
+       }
+}
+
+static u16 nxp_nci_fw_check_crc(struct sk_buff *skb)
+{
+       u16 crc, frame_crc;
+       size_t len = skb->len - NXP_NCI_FW_CRC_LEN;
+
+       crc = nxp_nci_fw_crc(skb->data, len);
+       frame_crc = get_unaligned_be16(skb->data + len);
+
+       return (crc ^ frame_crc);
+}
+
+void nxp_nci_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb)
+{
+       struct nxp_nci_info *info = nci_get_drvdata(ndev);
+       struct nxp_nci_fw_info *fw_info = &info->fw_info;
+
+       complete(&fw_info->cmd_completion);
+
+       if (skb) {
+               if (nxp_nci_fw_check_crc(skb) != 0x00)
+                       fw_info->cmd_result = -EBADMSG;
+               else
+                       fw_info->cmd_result = nxp_nci_fw_read_status(
+                                       *skb_pull(skb, NXP_NCI_FW_HDR_LEN));
+               kfree_skb(skb);
+       } else {
+               fw_info->cmd_result = -EIO;
+       }
+
+       if (fw_info->fw)
+               schedule_work(&fw_info->work);
+}
+EXPORT_SYMBOL(nxp_nci_fw_recv_frame);
diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c
new file mode 100644 (file)
index 0000000..17bd67d
--- /dev/null
@@ -0,0 +1,415 @@
+/*
+ * I2C link layer for the NXP NCI driver
+ *
+ * Copyright (C) 2014  NXP Semiconductors  All rights reserved.
+ *
+ * Authors: Clément Perrochaud <clement.perrochaud@nxp.com>
+ *
+ * Derived from PN544 device driver:
+ * Copyright (C) 2012  Intel Corporation. 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/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/platform_data/nxp-nci.h>
+#include <linux/unaligned/access_ok.h>
+
+#include <net/nfc/nfc.h>
+
+#include "nxp-nci.h"
+
+#define NXP_NCI_I2C_DRIVER_NAME        "nxp-nci_i2c"
+
+#define NXP_NCI_I2C_MAX_PAYLOAD        32
+
+struct nxp_nci_i2c_phy {
+       struct i2c_client *i2c_dev;
+       struct nci_dev *ndev;
+
+       unsigned int gpio_en;
+       unsigned int gpio_fw;
+
+       int hard_fault; /*
+                        * < 0 if hardware error occurred (e.g. i2c err)
+                        * and prevents normal operation.
+                        */
+};
+
+static int nxp_nci_i2c_set_mode(void *phy_id,
+                                   enum nxp_nci_mode mode)
+{
+       struct nxp_nci_i2c_phy *phy = (struct nxp_nci_i2c_phy *) phy_id;
+
+       gpio_set_value(phy->gpio_fw, (mode == NXP_NCI_MODE_FW) ? 1 : 0);
+       gpio_set_value(phy->gpio_en, (mode != NXP_NCI_MODE_COLD) ? 1 : 0);
+       usleep_range(10000, 15000);
+
+       if (mode == NXP_NCI_MODE_COLD)
+               phy->hard_fault = 0;
+
+       return 0;
+}
+
+static int nxp_nci_i2c_write(void *phy_id, struct sk_buff *skb)
+{
+       int r;
+       struct nxp_nci_i2c_phy *phy = phy_id;
+       struct i2c_client *client = phy->i2c_dev;
+
+       if (phy->hard_fault != 0)
+               return phy->hard_fault;
+
+       r = i2c_master_send(client, skb->data, skb->len);
+       if (r == -EREMOTEIO) {
+               /* Retry, chip was in standby */
+               usleep_range(110000, 120000);
+               r = i2c_master_send(client, skb->data, skb->len);
+       }
+
+       if (r < 0) {
+               nfc_err(&client->dev, "Error %d on I2C send\n", r);
+       } else if (r != skb->len) {
+               nfc_err(&client->dev,
+                       "Invalid length sent: %u (expected %u)\n",
+                       r, skb->len);
+               r = -EREMOTEIO;
+       } else {
+               /* Success but return 0 and not number of bytes */
+               r = 0;
+       }
+
+       return r;
+}
+
+static struct nxp_nci_phy_ops i2c_phy_ops = {
+       .set_mode = nxp_nci_i2c_set_mode,
+       .write = nxp_nci_i2c_write,
+};
+
+static int nxp_nci_i2c_fw_read(struct nxp_nci_i2c_phy *phy,
+                              struct sk_buff **skb)
+{
+       struct i2c_client *client = phy->i2c_dev;
+       u16 header;
+       size_t frame_len;
+       int r;
+
+       r = i2c_master_recv(client, (u8 *) &header, NXP_NCI_FW_HDR_LEN);
+       if (r < 0) {
+               goto fw_read_exit;
+       } else if (r != NXP_NCI_FW_HDR_LEN) {
+               nfc_err(&client->dev, "Incorrect header length: %u\n", r);
+               r = -EBADMSG;
+               goto fw_read_exit;
+       }
+
+       frame_len = (get_unaligned_be16(&header) & NXP_NCI_FW_FRAME_LEN_MASK) +
+                   NXP_NCI_FW_CRC_LEN;
+
+       *skb = alloc_skb(NXP_NCI_FW_HDR_LEN + frame_len, GFP_KERNEL);
+       if (*skb == NULL) {
+               r = -ENOMEM;
+               goto fw_read_exit;
+       }
+
+       memcpy(skb_put(*skb, NXP_NCI_FW_HDR_LEN), &header, NXP_NCI_FW_HDR_LEN);
+
+       r = i2c_master_recv(client, skb_put(*skb, frame_len), frame_len);
+       if (r != frame_len) {
+               nfc_err(&client->dev,
+                       "Invalid frame length: %u (expected %zu)\n",
+                       r, frame_len);
+               r = -EBADMSG;
+               goto fw_read_exit_free_skb;
+       }
+
+       return 0;
+
+fw_read_exit_free_skb:
+       kfree_skb(*skb);
+fw_read_exit:
+       return r;
+}
+
+static int nxp_nci_i2c_nci_read(struct nxp_nci_i2c_phy *phy,
+                               struct sk_buff **skb)
+{
+       struct nci_ctrl_hdr header; /* May actually be a data header */
+       struct i2c_client *client = phy->i2c_dev;
+       int r;
+
+       r = i2c_master_recv(client, (u8 *) &header, NCI_CTRL_HDR_SIZE);
+       if (r < 0) {
+               goto nci_read_exit;
+       } else if (r != NCI_CTRL_HDR_SIZE) {
+               nfc_err(&client->dev, "Incorrect header length: %u\n", r);
+               r = -EBADMSG;
+               goto nci_read_exit;
+       }
+
+       *skb = alloc_skb(NCI_CTRL_HDR_SIZE + header.plen, GFP_KERNEL);
+       if (*skb == NULL) {
+               r = -ENOMEM;
+               goto nci_read_exit;
+       }
+
+       memcpy(skb_put(*skb, NCI_CTRL_HDR_SIZE), (void *) &header,
+              NCI_CTRL_HDR_SIZE);
+
+       r = i2c_master_recv(client, skb_put(*skb, header.plen), header.plen);
+       if (r != header.plen) {
+               nfc_err(&client->dev,
+                       "Invalid frame payload length: %u (expected %u)\n",
+                       r, header.plen);
+               r = -EBADMSG;
+               goto nci_read_exit_free_skb;
+       }
+
+       return 0;
+
+nci_read_exit_free_skb:
+       kfree_skb(*skb);
+nci_read_exit:
+       return r;
+}
+
+static irqreturn_t nxp_nci_i2c_irq_thread_fn(int irq, void *phy_id)
+{
+       struct nxp_nci_i2c_phy *phy = phy_id;
+       struct i2c_client *client;
+       struct nxp_nci_info *info;
+
+       struct sk_buff *skb = NULL;
+       int r = 0;
+
+       if (!phy || !phy->ndev)
+               goto exit_irq_none;
+
+       client = phy->i2c_dev;
+
+       if (!client || irq != client->irq)
+               goto exit_irq_none;
+
+       info = nci_get_drvdata(phy->ndev);
+
+       if (!info)
+               goto exit_irq_none;
+
+       mutex_lock(&info->info_lock);
+
+       if (phy->hard_fault != 0)
+               goto exit_irq_handled;
+
+       switch (info->mode) {
+       case NXP_NCI_MODE_NCI:
+               r = nxp_nci_i2c_nci_read(phy, &skb);
+               break;
+       case NXP_NCI_MODE_FW:
+               r = nxp_nci_i2c_fw_read(phy, &skb);
+               break;
+       case NXP_NCI_MODE_COLD:
+               r = -EREMOTEIO;
+               break;
+       }
+
+       if (r == -EREMOTEIO) {
+               phy->hard_fault = r;
+               skb = NULL;
+       } else if (r < 0) {
+               nfc_err(&client->dev, "Read failed with error %d\n", r);
+               goto exit_irq_handled;
+       }
+
+       switch (info->mode) {
+       case NXP_NCI_MODE_NCI:
+               nci_recv_frame(phy->ndev, skb);
+               break;
+       case NXP_NCI_MODE_FW:
+               nxp_nci_fw_recv_frame(phy->ndev, skb);
+               break;
+       case NXP_NCI_MODE_COLD:
+               break;
+       }
+
+exit_irq_handled:
+       mutex_unlock(&info->info_lock);
+       return IRQ_HANDLED;
+exit_irq_none:
+       WARN_ON_ONCE(1);
+       return IRQ_NONE;
+}
+
+#ifdef CONFIG_OF
+
+static int nxp_nci_i2c_parse_devtree(struct i2c_client *client)
+{
+       struct nxp_nci_i2c_phy *phy = i2c_get_clientdata(client);
+       struct device_node *pp;
+       int r;
+
+       pp = client->dev.of_node;
+       if (!pp)
+               return -ENODEV;
+
+       r = of_get_named_gpio(pp, "enable-gpios", 0);
+       if (r == -EPROBE_DEFER)
+               r = of_get_named_gpio(pp, "enable-gpios", 0);
+       if (r < 0) {
+               nfc_err(&client->dev, "Failed to get EN gpio, error: %d\n", r);
+               return r;
+       }
+       phy->gpio_en = r;
+
+       r = of_get_named_gpio(pp, "firmware-gpios", 0);
+       if (r == -EPROBE_DEFER)
+               r = of_get_named_gpio(pp, "firmware-gpios", 0);
+       if (r < 0) {
+               nfc_err(&client->dev, "Failed to get FW gpio, error: %d\n", r);
+               return r;
+       }
+       phy->gpio_fw = r;
+
+       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;
+       }
+       client->irq = r;
+
+       return 0;
+}
+
+#else
+
+static int nxp_nci_i2c_parse_devtree(struct i2c_client *client)
+{
+       return -ENODEV;
+}
+
+#endif
+
+static int nxp_nci_i2c_probe(struct i2c_client *client,
+                           const struct i2c_device_id *id)
+{
+       struct nxp_nci_i2c_phy *phy;
+       struct nxp_nci_nfc_platform_data *pdata;
+       int r;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               nfc_err(&client->dev, "Need I2C_FUNC_I2C\n");
+               r = -ENODEV;
+               goto probe_exit;
+       }
+
+       phy = devm_kzalloc(&client->dev, sizeof(struct nxp_nci_i2c_phy),
+                          GFP_KERNEL);
+       if (!phy) {
+               r = -ENOMEM;
+               goto probe_exit;
+       }
+
+       phy->i2c_dev = client;
+       i2c_set_clientdata(client, phy);
+
+       pdata = client->dev.platform_data;
+
+       if (!pdata && client->dev.of_node) {
+               r = nxp_nci_i2c_parse_devtree(client);
+               if (r < 0) {
+                       nfc_err(&client->dev, "Failed to get DT data\n");
+                       goto probe_exit;
+               }
+       } else if (pdata) {
+               phy->gpio_en = pdata->gpio_en;
+               phy->gpio_fw = pdata->gpio_fw;
+               client->irq = pdata->irq;
+       } else {
+               nfc_err(&client->dev, "No platform data\n");
+               r = -EINVAL;
+               goto probe_exit;
+       }
+
+       r = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_en,
+                                 GPIOF_OUT_INIT_LOW, "nxp_nci_en");
+       if (r < 0)
+               goto probe_exit;
+
+       r = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_fw,
+                                 GPIOF_OUT_INIT_LOW, "nxp_nci_fw");
+       if (r < 0)
+               goto probe_exit;
+
+       r = nxp_nci_probe(phy, &client->dev, &i2c_phy_ops,
+                         NXP_NCI_I2C_MAX_PAYLOAD, &phy->ndev);
+       if (r < 0)
+               goto probe_exit;
+
+       r = request_threaded_irq(client->irq, NULL,
+                                nxp_nci_i2c_irq_thread_fn,
+                                IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                NXP_NCI_I2C_DRIVER_NAME, phy);
+       if (r < 0)
+               nfc_err(&client->dev, "Unable to register IRQ handler\n");
+
+probe_exit:
+       return r;
+}
+
+static int nxp_nci_i2c_remove(struct i2c_client *client)
+{
+       struct nxp_nci_i2c_phy *phy = i2c_get_clientdata(client);
+
+       nxp_nci_remove(phy->ndev);
+       free_irq(client->irq, phy);
+
+       return 0;
+}
+
+static struct i2c_device_id nxp_nci_i2c_id_table[] = {
+       {"nxp-nci_i2c", 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, nxp_nci_i2c_id_table);
+
+static const struct of_device_id of_nxp_nci_i2c_match[] = {
+       { .compatible = "nxp,nxp-nci-i2c", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, of_nxp_nci_i2c_match);
+
+static struct i2c_driver nxp_nci_i2c_driver = {
+       .driver = {
+                  .name = NXP_NCI_I2C_DRIVER_NAME,
+                  .owner  = THIS_MODULE,
+                  .of_match_table = of_match_ptr(of_nxp_nci_i2c_match),
+                 },
+       .probe = nxp_nci_i2c_probe,
+       .id_table = nxp_nci_i2c_id_table,
+       .remove = nxp_nci_i2c_remove,
+};
+
+module_i2c_driver(nxp_nci_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("I2C driver for NXP NCI NFC controllers");
+MODULE_AUTHOR("Clément Perrochaud <clement.perrochaud@nxp.com>");
diff --git a/drivers/nfc/nxp-nci/nxp-nci.h b/drivers/nfc/nxp-nci/nxp-nci.h
new file mode 100644 (file)
index 0000000..f1fecc4
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014  NXP Semiconductors  All rights reserved.
+ *
+ * Authors: Clément Perrochaud <clement.perrochaud@nxp.com>
+ *
+ * Derived from PN544 device driver:
+ * Copyright (C) 2012  Intel Corporation. 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_NXP_NCI_H_
+#define __LOCAL_NXP_NCI_H_
+
+#include <linux/completion.h>
+#include <linux/firmware.h>
+#include <linux/nfc.h>
+#include <linux/platform_data/nxp-nci.h>
+
+#include <net/nfc/nci_core.h>
+
+#define NXP_NCI_FW_HDR_LEN     2
+#define NXP_NCI_FW_CRC_LEN     2
+
+#define NXP_NCI_FW_FRAME_LEN_MASK      0x03FF
+
+enum nxp_nci_mode {
+       NXP_NCI_MODE_COLD,
+       NXP_NCI_MODE_NCI,
+       NXP_NCI_MODE_FW
+};
+
+struct nxp_nci_phy_ops {
+       int (*set_mode)(void *id, enum nxp_nci_mode mode);
+       int (*write)(void *id, struct sk_buff *skb);
+};
+
+struct nxp_nci_fw_info {
+       char name[NFC_FIRMWARE_NAME_MAXSIZE + 1];
+       const struct firmware *fw;
+
+       size_t size;
+       size_t written;
+
+       const u8 *data;
+       size_t frame_size;
+
+       struct work_struct work;
+       struct completion cmd_completion;
+
+       int cmd_result;
+};
+
+struct nxp_nci_info {
+       struct nci_dev *ndev;
+       void *phy_id;
+       struct device *pdev;
+
+       enum nxp_nci_mode mode;
+
+       struct nxp_nci_phy_ops *phy_ops;
+       unsigned int max_payload;
+
+       struct mutex info_lock;
+
+       struct nxp_nci_fw_info fw_info;
+};
+
+int nxp_nci_fw_download(struct nci_dev *ndev, const char *firmware_name);
+void nxp_nci_fw_work(struct work_struct *work);
+void nxp_nci_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb);
+void nxp_nci_fw_work_complete(struct nxp_nci_info *info, int result);
+
+int nxp_nci_probe(void *phy_id, struct device *pdev,
+                 struct nxp_nci_phy_ops *phy_ops, unsigned int max_payload,
+                 struct nci_dev **ndev);
+void nxp_nci_remove(struct nci_dev *ndev);
+
+#endif /* __LOCAL_NXP_NCI_H_ */
index d46a700a9637a9152aee5bc5c41b58eacfbe8ec5..a03e4eb5fe2978c24d10899d249ddc2373968db6 100644 (file)
@@ -1820,7 +1820,7 @@ static int pn533_rf_complete(struct pn533 *dev, void *arg,
        if (IS_ERR(resp)) {
                rc = PTR_ERR(resp);
 
-               nfc_err(&dev->interface->dev, "RF setting error %d", rc);
+               nfc_err(&dev->interface->dev, "RF setting error %d\n", rc);
 
                return rc;
        }
@@ -2554,8 +2554,10 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg,
        }
 
        skb = pn533_build_response(dev);
-       if (!skb)
+       if (!skb) {
+               rc = -ENOMEM;
                goto error;
+       }
 
        arg->cb(arg->cb_context, skb, 0);
        kfree(arg);
index cdde745b96bd142e07a90b441447ed57bd94b087..6fd986f5ac3ed522e46a1c226ba418f4576b1711 100644 (file)
@@ -953,7 +953,7 @@ static int pn544_hci_i2c_acpi_request_resources(struct i2c_client *client)
        }
 
        nfc_info(dev, "GPIO resource, no:%d irq:%d\n",
-                       desc_to_gpio(gpiod_irq), ret);
+                desc_to_gpio(gpiod_irq), ret);
        client->irq = ret;
 
        return 0;
@@ -1062,11 +1062,8 @@ static int pn544_hci_i2c_probe(struct i2c_client *client,
 
        phy = devm_kzalloc(&client->dev, sizeof(struct pn544_i2c_phy),
                           GFP_KERNEL);
-       if (!phy) {
-               nfc_err(&client->dev,
-                       "Cannot allocate memory for pn544 i2c phy.\n");
+       if (!phy)
                return -ENOMEM;
-       }
 
        INIT_WORK(&phy->fw_work, pn544_hci_i2c_fw_work);
        phy->fw_work_state = FW_WORK_STATE_IDLE;
index 4ac4d31f6c598309a9c9e35f871cd0316419ba5f..87d509996704039c3ac224dcb95bb4b096570542 100644 (file)
@@ -604,11 +604,11 @@ static void port100_recv_response(struct urb *urb)
        case -ECONNRESET:
        case -ENOENT:
                nfc_err(&dev->interface->dev,
-                       "The urb has been canceled (status %d)", urb->status);
+                       "The urb has been canceled (status %d)\n", urb->status);
                goto sched_wq;
        case -ESHUTDOWN:
        default:
-               nfc_err(&dev->interface->dev, "Urb failure (status %d)",
+               nfc_err(&dev->interface->dev, "Urb failure (status %d)\n",
                        urb->status);
                goto sched_wq;
        }
@@ -616,7 +616,7 @@ static void port100_recv_response(struct urb *urb)
        in_frame = dev->in_urb->transfer_buffer;
 
        if (!port100_rx_frame_is_valid(in_frame)) {
-               nfc_err(&dev->interface->dev, "Received an invalid frame");
+               nfc_err(&dev->interface->dev, "Received an invalid frame\n");
                cmd->status = -EIO;
                goto sched_wq;
        }
@@ -626,7 +626,7 @@ static void port100_recv_response(struct urb *urb)
 
        if (!port100_rx_frame_is_cmd_response(dev, in_frame)) {
                nfc_err(&dev->interface->dev,
-                       "It's not the response to the last command");
+                       "It's not the response to the last command\n");
                cmd->status = -EIO;
                goto sched_wq;
        }
@@ -657,11 +657,11 @@ static void port100_recv_ack(struct urb *urb)
        case -ECONNRESET:
        case -ENOENT:
                nfc_err(&dev->interface->dev,
-                       "The urb has been stopped (status %d)", urb->status);
+                       "The urb has been stopped (status %d)\n", urb->status);
                goto sched_wq;
        case -ESHUTDOWN:
        default:
-               nfc_err(&dev->interface->dev, "Urb failure (status %d)",
+               nfc_err(&dev->interface->dev, "Urb failure (status %d)\n",
                        urb->status);
                goto sched_wq;
        }
@@ -669,7 +669,7 @@ static void port100_recv_ack(struct urb *urb)
        in_frame = dev->in_urb->transfer_buffer;
 
        if (!port100_rx_frame_is_ack(in_frame)) {
-               nfc_err(&dev->interface->dev, "Received an invalid ack");
+               nfc_err(&dev->interface->dev, "Received an invalid ack\n");
                cmd->status = -EIO;
                goto sched_wq;
        }
@@ -677,7 +677,7 @@ static void port100_recv_ack(struct urb *urb)
        rc = port100_submit_urb_for_response(dev, GFP_ATOMIC);
        if (rc) {
                nfc_err(&dev->interface->dev,
-                       "usb_submit_urb failed with result %d", rc);
+                       "usb_submit_urb failed with result %d\n", rc);
                cmd->status = rc;
                goto sched_wq;
        }
@@ -873,11 +873,11 @@ static void port100_send_complete(struct urb *urb)
        case -ECONNRESET:
        case -ENOENT:
                nfc_err(&dev->interface->dev,
-                       "The urb has been stopped (status %d)", urb->status);
+                       "The urb has been stopped (status %d)\n", urb->status);
                break;
        case -ESHUTDOWN:
        default:
-               nfc_err(&dev->interface->dev, "Urb failure (status %d)",
+               nfc_err(&dev->interface->dev, "Urb failure (status %d)\n",
                        urb->status);
        }
 }
@@ -1094,7 +1094,7 @@ static void port100_in_comm_rf_complete(struct port100 *dev, void *arg,
 
        if (resp->len < 4) {
                nfc_err(&dev->interface->dev,
-                       "Invalid packet length received.\n");
+                       "Invalid packet length received\n");
                rc = -EIO;
                goto error;
        }
@@ -1250,7 +1250,7 @@ static bool port100_tg_target_activated(struct port100 *dev, u8 tgt_activated)
                       PORT100_MDAA_TGT_WAS_ACTIVATED_MASK;
                break;
        default:
-               nfc_err(&dev->interface->dev, "Unknonwn command type.\n");
+               nfc_err(&dev->interface->dev, "Unknown command type\n");
                return false;
        }
 
@@ -1481,7 +1481,7 @@ static int port100_probe(struct usb_interface *interface,
        cmd_type_mask = port100_get_command_type_mask(dev);
        if (!cmd_type_mask) {
                nfc_err(&interface->dev,
-                       "Could not get supported command types.\n");
+                       "Could not get supported command types\n");
                rc = -ENODEV;
                goto error;
        }
@@ -1494,7 +1494,7 @@ static int port100_probe(struct usb_interface *interface,
        rc = port100_set_command_type(dev, dev->cmd_type);
        if (rc) {
                nfc_err(&interface->dev,
-                       "The device does not support command type %u.\n",
+                       "The device does not support command type %u\n",
                        dev->cmd_type);
                goto error;
        }
@@ -1502,7 +1502,7 @@ static int port100_probe(struct usb_interface *interface,
        fw_version = port100_get_firmware_version(dev);
        if (!fw_version)
                nfc_err(&interface->dev,
-                       "Could not get device firmware version.\n");
+                       "Could not get device firmware version\n");
 
        nfc_info(&interface->dev,
                 "Sony NFC Port-100 Series attached (firmware v%x.%02x)\n",
@@ -1515,7 +1515,7 @@ static int port100_probe(struct usb_interface *interface,
                                                           dev->skb_tailroom);
        if (!dev->nfc_digital_dev) {
                nfc_err(&interface->dev,
-                       "Could not allocate nfc_digital_dev.\n");
+                       "Could not allocate nfc_digital_dev\n");
                rc = -ENOMEM;
                goto error;
        }
@@ -1526,7 +1526,7 @@ static int port100_probe(struct usb_interface *interface,
        rc = nfc_digital_register_device(dev->nfc_digital_dev);
        if (rc) {
                nfc_err(&interface->dev,
-                       "Could not register digital device.\n");
+                       "Could not register digital device\n");
                goto free_nfc_dev;
        }
 
@@ -1562,7 +1562,7 @@ static void port100_disconnect(struct usb_interface *interface)
 
        kfree(dev->cmd);
 
-       nfc_info(&interface->dev, "Sony Port-100 NFC device disconnected");
+       nfc_info(&interface->dev, "Sony Port-100 NFC device disconnected\n");
 }
 
 static struct usb_driver port100_driver = {
index 24d3d240d5f42c83484925ed110f3a9dbf74ffcf..d251f7229c4e8fe5aa9da16baf09e75ed0a5b412 100644 (file)
@@ -572,7 +572,7 @@ exit:
        return r;
 }
 
-static int st21nfca_get_iso14443_3_uid(struct nfc_hci_dev *hdev, u8 *gate,
+static int st21nfca_get_iso14443_3_uid(struct nfc_hci_dev *hdev, u8 *uid,
                                       int *len)
 {
        int r;
@@ -588,7 +588,7 @@ static int st21nfca_get_iso14443_3_uid(struct nfc_hci_dev *hdev, u8 *gate,
                goto exit;
        }
 
-       gate = uid_skb->data;
+       memcpy(uid, uid_skb->data, uid_skb->len);
        *len = uid_skb->len;
 exit:
        kfree_skb(uid_skb);
index bd13cac9c66ac711cda7c1c3d0b8e5e3cb0b2ccd..3197e9bb66f73c53d940a3292b2140a2e7e72171 100644 (file)
@@ -310,6 +310,13 @@ int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host,
        case ST21NFCA_EVT_CONNECTIVITY:
                break;
        case ST21NFCA_EVT_TRANSACTION:
+               /*
+                * According to specification etsi 102 622
+                * 11.2.2.4 EVT_TRANSACTION Table 52
+                * Description  Tag     Length
+                * AID          81      5 to 16
+                * PARAMETERS   82      0 to 255
+                */
                if (skb->len < NFC_MIN_AID_LENGTH + 2 &&
                    skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG)
                        return -EPROTO;
@@ -318,8 +325,10 @@ int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host,
                                                   skb->len - 2, GFP_KERNEL);
 
                transaction->aid_len = skb->data[1];
-               memcpy(transaction->aid, &skb->data[2], skb->data[1]);
+               memcpy(transaction->aid, &skb->data[2],
+                      transaction->aid_len);
 
+               /* Check next byte is PARAMETERS tag (82) */
                if (skb->data[transaction->aid_len + 2] !=
                    NFC_EVT_TRANSACTION_PARAMS_TAG)
                        return -EPROTO;
index eb886932d97278cfa044fb66a20dea49ffcaca6b..76a4cad41cec9b3affa4f903683a313d4620b389 100644 (file)
@@ -109,7 +109,7 @@ static int st21nfcb_nci_i2c_write(void *phy_id, struct sk_buff *skb)
                return phy->ndlc->hard_fault;
 
        r = i2c_master_send(client, skb->data, skb->len);
-       if (r == -EREMOTEIO) {  /* Retry, chip was in standby */
+       if (r < 0) {  /* Retry, chip was in standby */
                usleep_range(1000, 4000);
                r = i2c_master_send(client, skb->data, skb->len);
        }
@@ -148,7 +148,7 @@ static int st21nfcb_nci_i2c_read(struct st21nfcb_i2c_phy *phy,
        struct i2c_client *client = phy->i2c_dev;
 
        r = i2c_master_recv(client, buf, ST21NFCB_NCI_I2C_MIN_SIZE);
-       if (r == -EREMOTEIO) {  /* Retry, chip was in standby */
+       if (r < 0) {  /* Retry, chip was in standby */
                usleep_range(1000, 4000);
                r = i2c_master_recv(client, buf, ST21NFCB_NCI_I2C_MIN_SIZE);
        }
@@ -313,11 +313,8 @@ static int st21nfcb_nci_i2c_probe(struct i2c_client *client,
 
        phy = devm_kzalloc(&client->dev, sizeof(struct st21nfcb_i2c_phy),
                           GFP_KERNEL);
-       if (!phy) {
-               nfc_err(&client->dev,
-                       "Cannot allocate memory for st21nfcb i2c phy.\n");
+       if (!phy)
                return -ENOMEM;
-       }
 
        phy->i2c_dev = client;
 
index 5fbf59d2138c1b0a0c723550905d37931a89bcbe..6014b585946595dc9afa1f51b5f24553d2a52e16 100644 (file)
@@ -256,10 +256,9 @@ int ndlc_probe(void *phy_id, struct nfc_phy_ops *phy_ops, struct device *dev,
        struct llt_ndlc *ndlc;
 
        ndlc = devm_kzalloc(dev, sizeof(struct llt_ndlc), GFP_KERNEL);
-       if (!ndlc) {
-               nfc_err(dev, "Cannot allocate memory for ndlc.\n");
+       if (!ndlc)
                return -ENOMEM;
-       }
+
        ndlc->ops = phy_ops;
        ndlc->phy_id = phy_id;
        ndlc->dev = dev;
index 7c82e9d87a65d718a92b7066e6b06c3b233e5c60..24862a525fb5044bbb3d18c8ea593e82b6f2c9bb 100644 (file)
@@ -321,6 +321,12 @@ static int st21nfcb_hci_connectivity_event_received(struct nci_dev *ndev,
 
        break;
        case ST21NFCB_EVT_TRANSACTION:
+               /* According to specification etsi 102 622
+                * 11.2.2.4 EVT_TRANSACTION Table 52
+                * Description  Tag     Length
+                * AID          81      5 to 16
+                * PARAMETERS   82      0 to 255
+                */
                if (skb->len < NFC_MIN_AID_LENGTH + 2 &&
                    skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG)
                        return -EPROTO;
@@ -329,8 +335,9 @@ static int st21nfcb_hci_connectivity_event_received(struct nci_dev *ndev,
                                            skb->len - 2, GFP_KERNEL);
 
                transaction->aid_len = skb->data[1];
-               memcpy(transaction->aid, &skb->data[2], skb->data[1]);
+               memcpy(transaction->aid, &skb->data[2], transaction->aid_len);
 
+               /* Check next byte is PARAMETERS tag (82) */
                if (skb->data[transaction->aid_len + 2] !=
                    NFC_EVT_TRANSACTION_PARAMS_TAG)
                        return -EPROTO;
@@ -340,6 +347,7 @@ static int st21nfcb_hci_connectivity_event_received(struct nci_dev *ndev,
                       transaction->aid_len + 4, transaction->params_len);
 
                r = nfc_se_transaction(ndev->nfc_dev, host, transaction);
+               break;
        default:
                return 1;
        }
@@ -542,14 +550,12 @@ static int st21nfcb_hci_network_init(struct nci_dev *ndev)
 
        r = nci_hci_dev_session_init(ndev);
        if (r != NCI_HCI_ANY_OK)
-               goto exit;
+               goto free_dest_params;
 
        r = nci_nfcee_mode_set(ndev, ndev->hci_dev->conn_info->id,
                               NCI_NFCEE_ENABLE);
        if (r != NCI_STATUS_OK)
-               goto exit;
-
-       return 0;
+               goto free_dest_params;
 
 free_dest_params:
        kfree(dest_params);
index d75b72ba2672b74ccfc50f8f8a5c67dc17db0ea9..15a7ee3859dd7dd74aee31593876b422093eff04 100644 (file)
@@ -357,6 +357,15 @@ static void ssb_pcicore_init_hostmode(struct ssb_pcicore *pc)
        pcicore_write32(pc, SSB_PCICORE_SBTOPCI2,
                        SSB_PCICORE_SBTOPCI_MEM | SSB_PCI_DMA);
 
+       /*
+        * Accessing PCI config without a proper delay after devices reset (not
+        * GPIO reset) was causing reboots on WRT300N v1.0.
+        * Tested delay 850 us lowered reboot chance to 50-80%, 1000 us fixed it
+        * completely. Flushing all writes was also tested but with no luck.
+        */
+       if (pc->dev->bus->chip_id == 0x4704)
+               usleep_range(1000, 2000);
+
        /* Enable PCI bridge BAR0 prefetch and burst */
        val = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
        ssb_extpci_write_config(pc, 0, 0, 0, PCI_COMMAND, &val, 2);
index 119130e9298b21ef0aa2a75e90a7c756318492a7..da4929927f6914262775c3fb02ef31594357dbc3 100644 (file)
@@ -14,5 +14,6 @@ struct ifla_vf_info {
        __u32 linkstate;
        __u32 min_tx_rate;
        __u32 max_tx_rate;
+       __u32 rss_query_en;
 };
 #endif /* _LINUX_IF_LINK_H */
index 41bf58a2b936bed3ec8e0d2fcda0a417662e4fbd..b5679aed660b82896ca003a1aedfbf5a77e446af 100644 (file)
@@ -878,6 +878,11 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,
  * int (*ndo_set_vf_link_state)(struct net_device *dev, int vf, int link_state);
  * int (*ndo_set_vf_port)(struct net_device *dev, int vf,
  *                       struct nlattr *port[]);
+ *
+ *      Enable or disable the VF ability to query its RSS Redirection Table and
+ *      Hash Key. This is needed since on some devices VF share this information
+ *      with PF and querying it may adduce a theoretical security risk.
+ * int (*ndo_set_vf_rss_query_en)(struct net_device *dev, int vf, bool setting);
  * int (*ndo_get_vf_port)(struct net_device *dev, int vf, struct sk_buff *skb);
  * int (*ndo_setup_tc)(struct net_device *dev, u8 tc)
  *     Called to setup 'tc' number of traffic classes in the net device. This
@@ -1099,6 +1104,9 @@ struct net_device_ops {
                                                   struct nlattr *port[]);
        int                     (*ndo_get_vf_port)(struct net_device *dev,
                                                   int vf, struct sk_buff *skb);
+       int                     (*ndo_set_vf_rss_query_en)(
+                                                  struct net_device *dev,
+                                                  int vf, bool setting);
        int                     (*ndo_setup_tc)(struct net_device *dev, u8 tc);
 #if IS_ENABLED(CONFIG_FCOE)
        int                     (*ndo_fcoe_enable)(struct net_device *dev);
@@ -1305,6 +1313,8 @@ enum netdev_priv_flags {
  *     @base_addr:     Device I/O address
  *     @irq:           Device IRQ number
  *
+ *     @carrier_changes:       Stats to monitor carrier on<->off transitions
+ *
  *     @state:         Generic network queuing layer state, see netdev_state_t
  *     @dev_list:      The global list of network devices
  *     @napi_list:     List entry, that is used for polling napi devices
@@ -1328,7 +1338,7 @@ enum netdev_priv_flags {
  *     @mpls_features: Mask of features inheritable by MPLS
  *
  *     @ifindex:       interface index
- *     @iflink:        unique device identifier
+ *     @group:         The group, that the device belongs to
  *
  *     @stats:         Statistics struct, which was left as a legacy, use
  *                     rtnl_link_stats64 instead
@@ -1338,8 +1348,6 @@ enum netdev_priv_flags {
  *     @tx_dropped:    Dropped packets by core network,
  *                     do not use this in drivers
  *
- *     @carrier_changes:       Stats to monitor carrier on<->off transitions
- *
  *     @wireless_handlers:     List of functions to handle Wireless Extensions,
  *                             instead of ioctl,
  *                             see <net/iw_handler.h> for details.
@@ -1382,14 +1390,14 @@ enum netdev_priv_flags {
  *     @dev_port:              Used to differentiate devices that share
  *                             the same function
  *     @addr_list_lock:        XXX: need comments on this one
- *     @uc:                    unicast mac addresses
- *     @mc:                    multicast mac addresses
- *     @dev_addrs:             list of device hw addresses
- *     @queues_kset:           Group of all Kobjects in the Tx and RX queues
  *     @uc_promisc:            Counter, that indicates, that promiscuous mode
  *                             has been enabled due to the need to listen to
  *                             additional unicast addresses in a device that
  *                             does not implement ndo_set_rx_mode()
+ *     @uc:                    unicast mac addresses
+ *     @mc:                    multicast mac addresses
+ *     @dev_addrs:             list of device hw addresses
+ *     @queues_kset:           Group of all Kobjects in the Tx and RX queues
  *     @promiscuity:           Number of times, the NIC is told to work in
  *                             Promiscuous mode, if it becomes 0 the NIC will
  *                             exit from working in Promiscuous mode
@@ -1419,6 +1427,12 @@ enum netdev_priv_flags {
  *     @ingress_queue:         XXX: need comments on this one
  *     @broadcast:             hw bcast address
  *
+ *     @rx_cpu_rmap:   CPU reverse-mapping for RX completion interrupts,
+ *                     indexed by RX queue number. Assigned by driver.
+ *                     This must only be set if the ndo_rx_flow_steer
+ *                     operation is defined
+ *     @index_hlist:           Device index hash chain
+ *
  *     @_tx:                   Array of TX queues
  *     @num_tx_queues:         Number of TX queues allocated at alloc_netdev_mq() time
  *     @real_num_tx_queues:    Number of TX queues currently active in device
@@ -1428,11 +1442,6 @@ enum netdev_priv_flags {
  *
  *     @xps_maps:      XXX: need comments on this one
  *
- *     @rx_cpu_rmap:   CPU reverse-mapping for RX completion interrupts,
- *                     indexed by RX queue number. Assigned by driver.
- *                     This must only be set if the ndo_rx_flow_steer
- *                     operation is defined
- *
  *     @trans_start:           Time (in jiffies) of last Tx
  *     @watchdog_timeo:        Represents the timeout that is used by
  *                             the watchdog ( see dev_watchdog() )
@@ -1440,7 +1449,6 @@ enum netdev_priv_flags {
  *
  *     @pcpu_refcnt:           Number of references to this device
  *     @todo_list:             Delayed register/unregister
- *     @index_hlist:           Device index hash chain
  *     @link_watch_list:       XXX: need comments on this one
  *
  *     @reg_state:             Register/unregister state machine
@@ -1488,7 +1496,6 @@ enum netdev_priv_flags {
  *
  *     @qdisc_tx_busylock:     XXX: need comments on this one
  *
- *     @group:         The group, that the device belongs to
  *     @pm_qos_req:    Power Management QoS object
  *
  *     FIXME: cleanup struct net_device such that network protocol info
@@ -1508,6 +1515,8 @@ struct net_device {
        unsigned long           base_addr;
        int                     irq;
 
+       atomic_t                carrier_changes;
+
        /*
         *      Some hardware also needs these fields (state,dev_list,
         *      napi_list,unreg_list,close_list) but they are not
@@ -1548,8 +1557,6 @@ struct net_device {
        atomic_long_t           rx_dropped;
        atomic_long_t           tx_dropped;
 
-       atomic_t                carrier_changes;
-
 #ifdef CONFIG_WIRELESS_EXT
        const struct iw_handler_def *   wireless_handlers;
        struct iw_public_data * wireless_data;
@@ -1589,6 +1596,8 @@ struct net_device {
        unsigned short          dev_id;
        unsigned short          dev_port;
        spinlock_t              addr_list_lock;
+       unsigned char           name_assign_type;
+       bool                    uc_promisc;
        struct netdev_hw_addr_list      uc;
        struct netdev_hw_addr_list      mc;
        struct netdev_hw_addr_list      dev_addrs;
@@ -1596,10 +1605,6 @@ struct net_device {
 #ifdef CONFIG_SYSFS
        struct kset             *queues_kset;
 #endif
-
-       unsigned char           name_assign_type;
-
-       bool                    uc_promisc;
        unsigned int            promiscuity;
        unsigned int            allmulti;
 
@@ -1646,7 +1651,10 @@ struct net_device {
 
        struct netdev_queue __rcu *ingress_queue;
        unsigned char           broadcast[MAX_ADDR_LEN];
-
+#ifdef CONFIG_RFS_ACCEL
+       struct cpu_rmap         *rx_cpu_rmap;
+#endif
+       struct hlist_node       index_hlist;
 
 /*
  * Cache lines mostly used on transmit path
@@ -1657,13 +1665,11 @@ struct net_device {
        struct Qdisc            *qdisc;
        unsigned long           tx_queue_len;
        spinlock_t              tx_global_lock;
+       int                     watchdog_timeo;
 
 #ifdef CONFIG_XPS
        struct xps_dev_maps __rcu *xps_maps;
 #endif
-#ifdef CONFIG_RFS_ACCEL
-       struct cpu_rmap         *rx_cpu_rmap;
-#endif
 
        /* These may be needed for future network-power-down code. */
 
@@ -1673,13 +1679,11 @@ struct net_device {
         */
        unsigned long           trans_start;
 
-       int                     watchdog_timeo;
        struct timer_list       watchdog_timer;
 
        int __percpu            *pcpu_refcnt;
        struct list_head        todo_list;
 
-       struct hlist_node       index_hlist;
        struct list_head        link_watch_list;
 
        enum { NETREG_UNINITIALIZED=0,
@@ -1744,7 +1748,6 @@ struct net_device {
 #endif
        struct phy_device *phydev;
        struct lock_class_key *qdisc_tx_busylock;
-       struct pm_qos_request   pm_qos_req;
 };
 #define to_net_dev(d) container_of(d, struct net_device, dev)
 
@@ -2165,8 +2168,12 @@ int dev_open(struct net_device *dev);
 int dev_close(struct net_device *dev);
 int dev_close_many(struct list_head *head, bool unlink);
 void dev_disable_lro(struct net_device *dev);
-int dev_loopback_xmit(struct sk_buff *newskb);
-int dev_queue_xmit(struct sk_buff *skb);
+int dev_loopback_xmit(struct sock *sk, struct sk_buff *newskb);
+int dev_queue_xmit_sk(struct sock *sk, struct sk_buff *skb);
+static inline int dev_queue_xmit(struct sk_buff *skb)
+{
+       return dev_queue_xmit_sk(skb->sk, skb);
+}
 int dev_queue_xmit_accel(struct sk_buff *skb, void *accel_priv);
 int register_netdevice(struct net_device *dev);
 void unregister_netdevice_queue(struct net_device *dev, struct list_head *head);
@@ -2927,7 +2934,11 @@ static inline void dev_consume_skb_any(struct sk_buff *skb)
 
 int netif_rx(struct sk_buff *skb);
 int netif_rx_ni(struct sk_buff *skb);
-int netif_receive_skb(struct sk_buff *skb);
+int netif_receive_skb_sk(struct sock *sk, struct sk_buff *skb);
+static inline int netif_receive_skb(struct sk_buff *skb)
+{
+       return netif_receive_skb_sk(skb->sk, skb);
+}
 gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb);
 void napi_gro_flush(struct napi_struct *napi, bool flush_old);
 struct sk_buff *napi_get_frags(struct napi_struct *napi);
index c480c43ad8f7a59538c3bc71fd971744e6f0246c..63560d0a8dfe2802ec826d87921a8848fe56e68b 100644 (file)
@@ -45,15 +45,35 @@ struct sk_buff;
 
 struct nf_hook_ops;
 
+struct sock;
+
 struct nf_hook_state {
        unsigned int hook;
        int thresh;
        u_int8_t pf;
        struct net_device *in;
        struct net_device *out;
-       int (*okfn)(struct sk_buff *);
+       struct sock *sk;
+       int (*okfn)(struct sock *, struct sk_buff *);
 };
 
+static inline void nf_hook_state_init(struct nf_hook_state *p,
+                                     unsigned int hook,
+                                     int thresh, u_int8_t pf,
+                                     struct net_device *indev,
+                                     struct net_device *outdev,
+                                     struct sock *sk,
+                                     int (*okfn)(struct sock *, struct sk_buff *))
+{
+       p->hook = hook;
+       p->thresh = thresh;
+       p->pf = pf;
+       p->in = indev;
+       p->out = outdev;
+       p->sk = sk;
+       p->okfn = okfn;
+}
+
 typedef unsigned int nf_hookfn(const struct nf_hook_ops *ops,
                               struct sk_buff *skb,
                               const struct nf_hook_state *state);
@@ -136,31 +156,29 @@ int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state);
  *     value indicates the packet has been consumed by the hook.
  */
 static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,
+                                struct sock *sk,
                                 struct sk_buff *skb,
                                 struct net_device *indev,
                                 struct net_device *outdev,
-                                int (*okfn)(struct sk_buff *), int thresh)
+                                int (*okfn)(struct sock *, struct sk_buff *),
+                                int thresh)
 {
        if (nf_hooks_active(pf, hook)) {
-               struct nf_hook_state state = {
-                       .hook = hook,
-                       .thresh = thresh,
-                       .pf = pf,
-                       .in = indev,
-                       .out = outdev,
-                       .okfn = okfn
-               };
+               struct nf_hook_state state;
 
+               nf_hook_state_init(&state, hook, thresh, pf,
+                                  indev, outdev, sk, okfn);
                return nf_hook_slow(skb, &state);
        }
        return 1;
 }
 
-static inline int nf_hook(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
-                         struct net_device *indev, struct net_device *outdev,
-                         int (*okfn)(struct sk_buff *))
+static inline int nf_hook(u_int8_t pf, unsigned int hook, struct sock *sk,
+                         struct sk_buff *skb, struct net_device *indev,
+                         struct net_device *outdev,
+                         int (*okfn)(struct sock *, struct sk_buff *))
 {
-       return nf_hook_thresh(pf, hook, skb, indev, outdev, okfn, INT_MIN);
+       return nf_hook_thresh(pf, hook, sk, skb, indev, outdev, okfn, INT_MIN);
 }
                    
 /* Activate hook; either okfn or kfree_skb called, unless a hook
@@ -181,35 +199,36 @@ static inline int nf_hook(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
 */
 
 static inline int
-NF_HOOK_THRESH(uint8_t pf, unsigned int hook, struct sk_buff *skb,
-              struct net_device *in, struct net_device *out,
-              int (*okfn)(struct sk_buff *), int thresh)
+NF_HOOK_THRESH(uint8_t pf, unsigned int hook, struct sock *sk,
+              struct sk_buff *skb, struct net_device *in,
+              struct net_device *out,
+              int (*okfn)(struct sock *, struct sk_buff *), int thresh)
 {
-       int ret = nf_hook_thresh(pf, hook, skb, in, out, okfn, thresh);
+       int ret = nf_hook_thresh(pf, hook, sk, skb, in, out, okfn, thresh);
        if (ret == 1)
-               ret = okfn(skb);
+               ret = okfn(sk, skb);
        return ret;
 }
 
 static inline int
-NF_HOOK_COND(uint8_t pf, unsigned int hook, struct sk_buff *skb,
-            struct net_device *in, struct net_device *out,
-            int (*okfn)(struct sk_buff *), bool cond)
+NF_HOOK_COND(uint8_t pf, unsigned int hook, struct sock *sk,
+            struct sk_buff *skb, struct net_device *in, struct net_device *out,
+            int (*okfn)(struct sock *, struct sk_buff *), bool cond)
 {
        int ret;
 
        if (!cond ||
-           ((ret = nf_hook_thresh(pf, hook, skb, in, out, okfn, INT_MIN)) == 1))
-               ret = okfn(skb);
+           ((ret = nf_hook_thresh(pf, hook, sk, skb, in, out, okfn, INT_MIN)) == 1))
+               ret = okfn(sk, skb);
        return ret;
 }
 
 static inline int
-NF_HOOK(uint8_t pf, unsigned int hook, struct sk_buff *skb,
+NF_HOOK(uint8_t pf, unsigned int hook, struct sock *sk, struct sk_buff *skb,
        struct net_device *in, struct net_device *out,
-       int (*okfn)(struct sk_buff *))
+       int (*okfn)(struct sock *, struct sk_buff *))
 {
-       return NF_HOOK_THRESH(pf, hook, skb, in, out, okfn, INT_MIN);
+       return NF_HOOK_THRESH(pf, hook, sk, skb, in, out, okfn, INT_MIN);
 }
 
 /* Call setsockopt() */
@@ -309,19 +328,21 @@ nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, u_int8_t family)
 }
 
 #else /* !CONFIG_NETFILTER */
-#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)
-#define NF_HOOK_COND(pf, hook, skb, indev, outdev, okfn, cond) (okfn)(skb)
+#define NF_HOOK(pf, hook, sk, skb, indev, outdev, okfn) (okfn)(sk, skb)
+#define NF_HOOK_COND(pf, hook, sk, skb, indev, outdev, okfn, cond) (okfn)(sk, skb)
 static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,
+                                struct sock *sk,
                                 struct sk_buff *skb,
                                 struct net_device *indev,
                                 struct net_device *outdev,
-                                int (*okfn)(struct sk_buff *), int thresh)
+                                int (*okfn)(struct sock *sk, struct sk_buff *), int thresh)
 {
-       return okfn(skb);
+       return okfn(sk, skb);
 }
-static inline int nf_hook(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
-                         struct net_device *indev, struct net_device *outdev,
-                         int (*okfn)(struct sk_buff *))
+static inline int nf_hook(u_int8_t pf, unsigned int hook, struct sock *sk,
+                         struct sk_buff *skb, struct net_device *indev,
+                         struct net_device *outdev,
+                         int (*okfn)(struct sock *, struct sk_buff *))
 {
        return 1;
 }
index 2734977199cac6a587126202dff59827724af277..ab8f76dba6680f485943545407daeb117620b489 100644 (file)
@@ -2,7 +2,7 @@
 #define __LINUX_BRIDGE_NETFILTER_H
 
 #include <uapi/linux/netfilter_bridge.h>
-
+#include <linux/skbuff.h>
 
 enum nf_br_hook_priorities {
        NF_BR_PRI_FIRST = INT_MIN,
@@ -17,20 +17,17 @@ enum nf_br_hook_priorities {
 
 #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
 
-#define BRNF_PKT_TYPE                  0x01
 #define BRNF_BRIDGED_DNAT              0x02
 #define BRNF_NF_BRIDGE_PREROUTING      0x08
-#define BRNF_8021Q                     0x10
-#define BRNF_PPPoE                     0x20
 
 static inline unsigned int nf_bridge_mtu_reduction(const struct sk_buff *skb)
 {
-       if (unlikely(skb->nf_bridge->mask & BRNF_PPPoE))
+       if (skb->nf_bridge->orig_proto == BRNF_PROTO_PPPOE)
                return PPPOE_SES_HLEN;
        return 0;
 }
 
-int br_handle_frame_finish(struct sk_buff *skb);
+int br_handle_frame_finish(struct sock *sk, struct sk_buff *skb);
 
 static inline void br_drop_fake_rtable(struct sk_buff *skb)
 {
@@ -40,6 +37,27 @@ static inline void br_drop_fake_rtable(struct sk_buff *skb)
                skb_dst_drop(skb);
 }
 
+static inline int nf_bridge_get_physinif(const struct sk_buff *skb)
+{
+       return skb->nf_bridge ? skb->nf_bridge->physindev->ifindex : 0;
+}
+
+static inline int nf_bridge_get_physoutif(const struct sk_buff *skb)
+{
+       return skb->nf_bridge ? skb->nf_bridge->physoutdev->ifindex : 0;
+}
+
+static inline struct net_device *
+nf_bridge_get_physindev(const struct sk_buff *skb)
+{
+       return skb->nf_bridge ? skb->nf_bridge->physindev : NULL;
+}
+
+static inline struct net_device *
+nf_bridge_get_physoutdev(const struct sk_buff *skb)
+{
+       return skb->nf_bridge ? skb->nf_bridge->physoutdev : NULL;
+}
 #else
 #define br_drop_fake_rtable(skb)               do { } while (0)
 #endif /* CONFIG_BRIDGE_NETFILTER */
index 02fc86d2348e2157d19cd4c16c4574ef86ac4d07..6835c1279df77328894ef9bc1396d786e7f29dec 100644 (file)
@@ -134,7 +134,7 @@ struct netlink_callback {
 
 struct netlink_notify {
        struct net *net;
-       int portid;
+       u32 portid;
        int protocol;
 };
 
diff --git a/include/linux/platform_data/nxp-nci.h b/include/linux/platform_data/nxp-nci.h
new file mode 100644 (file)
index 0000000..d6ed286
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Generic platform data for the NXP NCI NFC chips.
+ *
+ * Copyright (C) 2014  NXP Semiconductors  All rights reserved.
+ *
+ * Authors: Clément Perrochaud <clement.perrochaud@nxp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _NXP_NCI_H_
+#define _NXP_NCI_H_
+
+struct nxp_nci_nfc_platform_data {
+       unsigned int gpio_en;
+       unsigned int gpio_fw;
+       unsigned int irq;
+};
+
+#endif /* _NXP_NCI_H_ */
index 5db76a32fcaba3d9656fa5de6d077780d1d07e46..2da5d1081ad990b57a07715ea0751ae10a80ac54 100644 (file)
@@ -77,7 +77,20 @@ static inline struct netdev_queue *dev_ingress_queue(struct net_device *dev)
        return rtnl_dereference(dev->ingress_queue);
 }
 
-extern struct netdev_queue *dev_ingress_queue_create(struct net_device *dev);
+struct netdev_queue *dev_ingress_queue_create(struct net_device *dev);
+
+#ifdef CONFIG_NET_CLS_ACT
+void net_inc_ingress_queue(void);
+void net_dec_ingress_queue(void);
+#else
+static inline void net_inc_ingress_queue(void)
+{
+}
+
+static inline void net_dec_ingress_queue(void)
+{
+}
+#endif
 
 extern void rtnetlink_init(void);
 extern void __rtnl_unlock(void);
index 36f3f43c011789efe68c372296cc691cc7a3863e..0991259643d6ef44eac3940728a4c3fd1d51d7bb 100644 (file)
@@ -166,10 +166,16 @@ struct nf_conntrack {
 #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
 struct nf_bridge_info {
        atomic_t                use;
+       enum {
+               BRNF_PROTO_UNCHANGED,
+               BRNF_PROTO_8021Q,
+               BRNF_PROTO_PPPOE
+       } orig_proto;
+       bool                    pkt_otherhost;
        unsigned int            mask;
        struct net_device       *physindev;
        struct net_device       *physoutdev;
-       unsigned long           data[32 / sizeof(unsigned long)];
+       char                    neigh_header[8];
 };
 #endif
 
index f869ae8afbaf9f4092625d7f5c0317ef7538ca1f..0caa3a2d4106eab0137d20ac75518af7964281ba 100644 (file)
@@ -58,6 +58,7 @@ static inline unsigned int tcp_optlen(const struct sk_buff *skb)
 struct tcp_fastopen_cookie {
        s8      len;
        u8      val[TCP_FASTOPEN_COOKIE_MAX];
+       bool    exp;    /* In RFC6994 experimental option format */
 };
 
 /* This defines a selective acknowledgement block. */
@@ -188,6 +189,7 @@ 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_fastopen_exp:1,/* SYN includes Fast Open exp. option */
                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. */
index 3acecf35420b707a40ab18fe0fa3d221025aa07f..d95da83cb1b0f1d63325891a0281e5bc184f7dd4 100644 (file)
@@ -374,6 +374,7 @@ enum {
 /* LE features */
 #define HCI_LE_ENCRYPTION              0x01
 #define HCI_LE_CONN_PARAM_REQ_PROC     0x02
+#define HCI_LE_SLAVE_FEATURES          0x08
 #define HCI_LE_PING                    0x10
 #define HCI_LE_DATA_LEN_EXT            0x20
 #define HCI_LE_EXT_SCAN_POLICY         0x80
@@ -463,12 +464,14 @@ enum {
 #define EIR_NAME_COMPLETE      0x09 /* complete local name */
 #define EIR_TX_POWER           0x0A /* transmit power level */
 #define EIR_CLASS_OF_DEV       0x0D /* Class of Device */
-#define EIR_SSP_HASH_C         0x0E /* Simple Pairing Hash C */
-#define EIR_SSP_RAND_R         0x0F /* Simple Pairing Randomizer R */
+#define EIR_SSP_HASH_C192      0x0E /* Simple Pairing Hash C-192 */
+#define EIR_SSP_RAND_R192      0x0F /* Simple Pairing Randomizer R-192 */
 #define EIR_DEVICE_ID          0x10 /* device ID */
 #define EIR_APPEARANCE         0x19 /* Device appearance */
 #define EIR_LE_BDADDR          0x1B /* LE Bluetooth device address */
 #define EIR_LE_ROLE            0x1C /* LE role */
+#define EIR_SSP_HASH_C256      0x1D /* Simple Pairing Hash C-256 */
+#define EIR_SSP_RAND_R256      0x1E /* Simple Pairing Rand R-256 */
 #define EIR_LE_SC_CONFIRM      0x22 /* LE SC Confirmation Value */
 #define EIR_LE_SC_RANDOM       0x23 /* LE SC Random Value */
 
@@ -1374,6 +1377,11 @@ struct hci_cp_le_conn_update {
        __le16   max_ce_len;
 } __packed;
 
+#define HCI_OP_LE_READ_REMOTE_FEATURES 0x2016
+struct hci_cp_le_read_remote_features {
+       __le16   handle;
+} __packed;
+
 #define HCI_OP_LE_START_ENC            0x2019
 struct hci_cp_le_start_enc {
        __le16  handle;
@@ -1866,6 +1874,13 @@ struct hci_ev_le_conn_update_complete {
        __le16   supervision_timeout;
 } __packed;
 
+#define HCI_EV_LE_REMOTE_FEAT_COMPLETE 0x04
+struct hci_ev_le_remote_feat_complete {
+       __u8     status;
+       __le16   handle;
+       __u8     features[8];
+} __packed;
+
 #define HCI_EV_LE_LTK_REQ              0x05
 struct hci_ev_le_ltk_req {
        __le16  handle;
index 93fd3e756b8afc72d2618c35f4d8202b0abac5ce..a056c2bfeb811465afc285699537741d42fdaaac 100644 (file)
@@ -185,7 +185,6 @@ struct amp_assoc {
 
 #define HCI_MAX_PAGES  3
 
-#define NUM_REASSEMBLY 4
 struct hci_dev {
        struct list_head list;
        struct mutex    lock;
@@ -327,7 +326,6 @@ struct hci_dev {
        struct sk_buff_head     cmd_q;
 
        struct sk_buff          *sent_cmd;
-       struct sk_buff          *reassembly[NUM_REASSEMBLY];
 
        struct mutex            req_lock;
        wait_queue_head_t       req_wait_q;
@@ -1012,7 +1010,6 @@ int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
 void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
 
 int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb);
-int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count);
 
 void hci_init_sysfs(struct hci_dev *hdev);
 void hci_conn_init_sysfs(struct hci_conn *conn);
index 44130643656933783d77e356878651d7d10db4b5..f8d6813cd5b2c05eb5f7bea0595a12ad4c02dca8 100644 (file)
@@ -5000,6 +5000,64 @@ int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len,
                          enum ieee80211_p2p_attr_id attr,
                          u8 *buf, unsigned int bufsize);
 
+/**
+ * ieee80211_ie_split_ric - split an IE buffer according to ordering (with RIC)
+ * @ies: the IE buffer
+ * @ielen: the length of the IE buffer
+ * @ids: an array with element IDs that are allowed before
+ *     the split
+ * @n_ids: the size of the element ID array
+ * @after_ric: array IE types that come after the RIC element
+ * @n_after_ric: size of the @after_ric array
+ * @offset: offset where to start splitting in the buffer
+ *
+ * This function splits an IE buffer by updating the @offset
+ * variable to point to the location where the buffer should be
+ * split.
+ *
+ * It assumes that the given IE buffer is well-formed, this
+ * has to be guaranteed by the caller!
+ *
+ * It also assumes that the IEs in the buffer are ordered
+ * correctly, if not the result of using this function will not
+ * be ordered correctly either, i.e. it does no reordering.
+ *
+ * The function returns the offset where the next part of the
+ * buffer starts, which may be @ielen if the entire (remainder)
+ * of the buffer should be used.
+ */
+size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
+                             const u8 *ids, int n_ids,
+                             const u8 *after_ric, int n_after_ric,
+                             size_t offset);
+
+/**
+ * ieee80211_ie_split - split an IE buffer according to ordering
+ * @ies: the IE buffer
+ * @ielen: the length of the IE buffer
+ * @ids: an array with element IDs that are allowed before
+ *     the split
+ * @n_ids: the size of the element ID array
+ * @offset: offset where to start splitting in the buffer
+ *
+ * This function splits an IE buffer by updating the @offset
+ * variable to point to the location where the buffer should be
+ * split.
+ *
+ * It assumes that the given IE buffer is well-formed, this
+ * has to be guaranteed by the caller!
+ *
+ * It also assumes that the IEs in the buffer are ordered
+ * correctly, if not the result of using this function will not
+ * be ordered correctly either, i.e. it does no reordering.
+ *
+ * The function returns the offset where the next part of the
+ * buffer starts, which may be @ielen if the entire (remainder)
+ * of the buffer should be used.
+ */
+size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
+                         const u8 *ids, int n_ids, size_t offset);
+
 /**
  * cfg80211_report_wowlan_wakeup - report wakeup from WoWLAN
  * @wdev: the wireless device reporting the wakeup
index 0f26aa707e62a13225031797c9c2edadbff05a3a..d0424269313fe2a84df0db71e07271e033756c9a 100644 (file)
@@ -18,11 +18,11 @@ struct dn_neigh {
 
 void dn_neigh_init(void);
 void dn_neigh_cleanup(void);
-int dn_neigh_router_hello(struct sk_buff *skb);
-int dn_neigh_endnode_hello(struct sk_buff *skb);
+int dn_neigh_router_hello(struct sock *sk, struct sk_buff *skb);
+int dn_neigh_endnode_hello(struct sock *sk, struct sk_buff *skb);
 void dn_neigh_pointopoint_hello(struct sk_buff *skb);
 int dn_neigh_elist(struct net_device *dev, unsigned char *ptr, int n);
-int dn_to_neigh_output(struct sk_buff *skb);
+int dn_to_neigh_output(struct sock *sk, struct sk_buff *skb);
 
 extern struct neigh_table dn_neigh_table;
 
index b7ce1003c429d43b0a69e45408eddd5fb35129e4..360c4802288db91a38b435bcf5b5d2eb71a8cd1f 100644 (file)
 
 struct inet_hashinfo;
 
-#define INET_TWDR_RECYCLE_SLOTS_LOG    5
-#define INET_TWDR_RECYCLE_SLOTS                (1 << INET_TWDR_RECYCLE_SLOTS_LOG)
-
-/*
- * If time > 4sec, it is "slow" path, no recycling is required,
- * so that we select tick to get range about 4 seconds.
- */
-#if HZ <= 16 || HZ > 4096
-# error Unsupported: HZ <= 16 or HZ > 4096
-#elif HZ <= 32
-# define INET_TWDR_RECYCLE_TICK (5 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
-#elif HZ <= 64
-# define INET_TWDR_RECYCLE_TICK (6 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
-#elif HZ <= 128
-# define INET_TWDR_RECYCLE_TICK (7 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
-#elif HZ <= 256
-# define INET_TWDR_RECYCLE_TICK (8 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
-#elif HZ <= 512
-# define INET_TWDR_RECYCLE_TICK (9 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
-#elif HZ <= 1024
-# define INET_TWDR_RECYCLE_TICK (10 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
-#elif HZ <= 2048
-# define INET_TWDR_RECYCLE_TICK (11 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
-#else
-# define INET_TWDR_RECYCLE_TICK (12 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
-#endif
-
-static inline u32 inet_tw_time_stamp(void)
-{
-       return jiffies;
-}
-
-/* TIME_WAIT reaping mechanism. */
-#define INET_TWDR_TWKILL_SLOTS 8 /* Please keep this a power of 2. */
-
-#define INET_TWDR_TWKILL_QUOTA 100
-
 struct inet_timewait_death_row {
-       /* Short-time timewait calendar */
-       int                     twcal_hand;
-       unsigned long           twcal_jiffie;
-       struct timer_list       twcal_timer;
-       struct hlist_head       twcal_row[INET_TWDR_RECYCLE_SLOTS];
-
-       spinlock_t              death_lock;
-       int                     tw_count;
-       int                     period;
-       u32                     thread_slots;
-       struct work_struct      twkill_work;
-       struct timer_list       tw_timer;
-       int                     slot;
-       struct hlist_head       cells[INET_TWDR_TWKILL_SLOTS];
-       struct inet_hashinfo    *hashinfo;
+       atomic_t                tw_count;
+
+       struct inet_hashinfo    *hashinfo ____cacheline_aligned_in_smp;
        int                     sysctl_tw_recycle;
        int                     sysctl_max_tw_buckets;
 };
 
-void inet_twdr_hangman(unsigned long data);
-void inet_twdr_twkill_work(struct work_struct *work);
-void inet_twdr_twcal_tick(unsigned long data);
-
 struct inet_bind_bucket;
 
 /*
@@ -133,52 +80,18 @@ struct inet_timewait_sock {
        __be16                  tw_sport;
        kmemcheck_bitfield_begin(flags);
        /* And these are ours. */
-       unsigned int            tw_pad0         : 1,    /* 1 bit hole */
+       unsigned int            tw_kill         : 1,
                                tw_transparent  : 1,
                                tw_flowlabel    : 20,
                                tw_pad          : 2,    /* 2 bits hole */
                                tw_tos          : 8;
        kmemcheck_bitfield_end(flags);
-       u32                     tw_ttd;
+       struct timer_list       tw_timer;
        struct inet_bind_bucket *tw_tb;
-       struct hlist_node       tw_death_node;
+       struct inet_timewait_death_row *tw_dr;
 };
 #define tw_tclass tw_tos
 
-static inline int inet_twsk_dead_hashed(const struct inet_timewait_sock *tw)
-{
-       return !hlist_unhashed(&tw->tw_death_node);
-}
-
-static inline void inet_twsk_dead_node_init(struct inet_timewait_sock *tw)
-{
-       tw->tw_death_node.pprev = NULL;
-}
-
-static inline void __inet_twsk_del_dead_node(struct inet_timewait_sock *tw)
-{
-       __hlist_del(&tw->tw_death_node);
-       inet_twsk_dead_node_init(tw);
-}
-
-static inline int inet_twsk_del_dead_node(struct inet_timewait_sock *tw)
-{
-       if (inet_twsk_dead_hashed(tw)) {
-               __inet_twsk_del_dead_node(tw);
-               return 1;
-       }
-       return 0;
-}
-
-#define inet_twsk_for_each(tw, node, head) \
-       hlist_nulls_for_each_entry(tw, node, head, tw_node)
-
-#define inet_twsk_for_each_inmate(tw, jail) \
-       hlist_for_each_entry(tw, jail, tw_death_node)
-
-#define inet_twsk_for_each_inmate_safe(tw, safe, jail) \
-       hlist_for_each_entry_safe(tw, safe, jail, tw_death_node)
-
 static inline struct inet_timewait_sock *inet_twsk(const struct sock *sk)
 {
        return (struct inet_timewait_sock *)sk;
@@ -193,16 +106,14 @@ int inet_twsk_bind_unhash(struct inet_timewait_sock *tw,
                          struct inet_hashinfo *hashinfo);
 
 struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk,
+                                          struct inet_timewait_death_row *dr,
                                           const int state);
 
 void __inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk,
                           struct inet_hashinfo *hashinfo);
 
-void inet_twsk_schedule(struct inet_timewait_sock *tw,
-                       struct inet_timewait_death_row *twdr,
-                       const int timeo, const int timewait_len);
-void inet_twsk_deschedule(struct inet_timewait_sock *tw,
-                         struct inet_timewait_death_row *twdr);
+void inet_twsk_schedule(struct inet_timewait_sock *tw, const int timeo);
+void inet_twsk_deschedule(struct inet_timewait_sock *tw);
 
 void inet_twsk_purge(struct inet_hashinfo *hashinfo,
                     struct inet_timewait_death_row *twdr, int family);
index 69cd9cb8400c981b4a1aa9530e744e8db1bdd015..d14af7edd197c2c364c1da62addc9781aa79fe84 100644 (file)
@@ -108,7 +108,8 @@ int ip_local_deliver(struct sk_buff *skb);
 int ip_mr_input(struct sk_buff *skb);
 int ip_output(struct sock *sk, struct sk_buff *skb);
 int ip_mc_output(struct sock *sk, struct sk_buff *skb);
-int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *));
+int ip_fragment(struct sock *sk, struct sk_buff *skb,
+               int (*output)(struct sock *, struct sk_buff *));
 int ip_do_nat(struct sk_buff *skb);
 void ip_send_check(struct iphdr *ip);
 int __ip_local_out(struct sk_buff *skb);
index eda131d179d971147f5b7ac254876dbf07946861..5e192068e6cb61a78d9b19b2b58bffd7c68b44bb 100644 (file)
@@ -170,7 +170,8 @@ static inline bool ipv6_anycast_destination(const struct sk_buff *skb)
        return rt->rt6i_flags & RTF_ANYCAST;
 }
 
-int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *));
+int ip6_fragment(struct sock *sk, struct sk_buff *skb,
+                int (*output)(struct sock *, struct sk_buff *));
 
 static inline int ip6_skb_dst_mtu(struct sk_buff *skb)
 {
index 1668be5937e62c9a8f150d75dc0f27f441090222..b8529aa1dae7a0b601008afd18a8218c41ab10ff 100644 (file)
@@ -73,13 +73,14 @@ __u32 ip6_tnl_get_cap(struct ip6_tnl *t, const struct in6_addr *laddr,
 struct net *ip6_tnl_get_link_net(const struct net_device *dev);
 int ip6_tnl_get_iflink(const struct net_device *dev);
 
-static inline void ip6tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
+static inline void ip6tunnel_xmit(struct sock *sk, struct sk_buff *skb,
+                                 struct net_device *dev)
 {
        struct net_device_stats *stats = &dev->stats;
        int pkt_len, err;
 
        pkt_len = skb->len;
-       err = ip6_local_out(skb);
+       err = ip6_local_out_sk(sk, skb);
 
        if (net_xmit_eval(err) == 0) {
                struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);
index 65142e6af44052b2807914c96cf48cf5882072df..eec8ad3c98432af6250a7a0c659d443da90ce45a 100644 (file)
@@ -47,8 +47,6 @@
 
 #define NEXTHDR_MAX            255
 
-
-
 #define IPV6_DEFAULT_HOPLIMIT   64
 #define IPV6_DEFAULT_MCASTHOPS 1
 
@@ -769,7 +767,7 @@ static inline u8 ip6_tclass(__be32 flowinfo)
 int ipv6_rcv(struct sk_buff *skb, struct net_device *dev,
             struct packet_type *pt, struct net_device *orig_dev);
 
-int ip6_rcv_finish(struct sk_buff *skb);
+int ip6_rcv_finish(struct sock *sk, struct sk_buff *skb);
 
 /*
  *     upper-layer output functions
@@ -827,6 +825,7 @@ int ip6_input(struct sk_buff *skb);
 int ip6_mc_input(struct sk_buff *skb);
 
 int __ip6_local_out(struct sk_buff *skb);
+int ip6_local_out_sk(struct sock *sk, struct sk_buff *skb);
 int ip6_local_out(struct sk_buff *skb);
 
 /*
index 201bc68e0cffd19766c31e983c6e652d50c681f9..b4bef1152c05c52b87dc877e9d4e8b09050842f7 100644 (file)
  *
  */
 
+/**
+ * DOC: mac80211 software tx queueing
+ *
+ * mac80211 provides an optional intermediate queueing implementation designed
+ * to allow the driver to keep hardware queues short and provide some fairness
+ * between different stations/interfaces.
+ * In this model, the driver pulls data frames from the mac80211 queue instead
+ * of letting mac80211 push them via drv_tx().
+ * Other frames (e.g. control or management) are still pushed using drv_tx().
+ *
+ * Drivers indicate that they use this model by implementing the .wake_tx_queue
+ * driver operation.
+ *
+ * Intermediate queues (struct ieee80211_txq) are kept per-sta per-tid, with a
+ * single per-vif queue for multicast data frames.
+ *
+ * The driver is expected to initialize its private per-queue data for stations
+ * and interfaces in the .add_interface and .sta_add ops.
+ *
+ * The driver can't access the queue directly. To dequeue a frame, it calls
+ * ieee80211_tx_dequeue(). Whenever mac80211 adds a new frame to a queue, it
+ * calls the .wake_tx_queue driver op.
+ *
+ * For AP powersave TIM handling, the driver only needs to indicate if it has
+ * buffered packets in the driver specific data structures by calling
+ * ieee80211_sta_set_buffered(). For frames buffered in the ieee80211_txq
+ * struct, mac80211 sets the appropriate TIM PVB bits and calls
+ * .release_buffered_frames().
+ * In that callback the driver is therefore expected to release its own
+ * buffered frames and afterwards also frames from the ieee80211_txq (obtained
+ * via the usual ieee80211_tx_dequeue).
+ */
+
 struct device;
 
 /**
@@ -1306,6 +1339,7 @@ enum ieee80211_vif_flags {
  *     monitor interface (if that is requested.)
  * @drv_priv: data area for driver use, will always be aligned to
  *     sizeof(void *).
+ * @txq: the multicast data TX queue (if driver uses the TXQ abstraction)
  */
 struct ieee80211_vif {
        enum nl80211_iftype type;
@@ -1317,6 +1351,8 @@ struct ieee80211_vif {
        u8 cab_queue;
        u8 hw_queue[IEEE80211_NUM_ACS];
 
+       struct ieee80211_txq *txq;
+
        struct ieee80211_chanctx_conf __rcu *chanctx_conf;
 
        u32 driver_flags;
@@ -1575,6 +1611,7 @@ struct ieee80211_sta_rates {
  * @tdls_initiator: indicates the STA is an initiator of the TDLS link. Only
  *     valid if the STA is a TDLS peer in the first place.
  * @mfp: indicates whether the STA uses management frame protection or not.
+ * @txq: per-TID data TX queues (if driver uses the TXQ abstraction)
  */
 struct ieee80211_sta {
        u32 supp_rates[IEEE80211_NUM_BANDS];
@@ -1593,6 +1630,8 @@ struct ieee80211_sta {
        bool tdls_initiator;
        bool mfp;
 
+       struct ieee80211_txq *txq[IEEE80211_NUM_TIDS];
+
        /* must be last */
        u8 drv_priv[0] __aligned(sizeof(void *));
 };
@@ -1620,6 +1659,27 @@ struct ieee80211_tx_control {
        struct ieee80211_sta *sta;
 };
 
+/**
+ * struct ieee80211_txq - Software intermediate tx queue
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @sta: station table entry, %NULL for per-vif queue
+ * @tid: the TID for this queue (unused for per-vif queue)
+ * @ac: the AC for this queue
+ *
+ * The driver can obtain packets from this queue by calling
+ * ieee80211_tx_dequeue().
+ */
+struct ieee80211_txq {
+       struct ieee80211_vif *vif;
+       struct ieee80211_sta *sta;
+       u8 tid;
+       u8 ac;
+
+       /* must be last */
+       u8 drv_priv[0] __aligned(sizeof(void *));
+};
+
 /**
  * enum ieee80211_hw_flags - hardware flags
  *
@@ -1844,6 +1904,8 @@ enum ieee80211_hw_flags {
  *     within &struct ieee80211_sta.
  * @chanctx_data_size: size (in bytes) of the drv_priv data area
  *     within &struct ieee80211_chanctx_conf.
+ * @txq_data_size: size (in bytes) of the drv_priv data area
+ *     within @struct ieee80211_txq.
  *
  * @max_rates: maximum number of alternate rate retry stages the hw
  *     can handle.
@@ -1892,6 +1954,9 @@ enum ieee80211_hw_flags {
  * @n_cipher_schemes: a size of an array of cipher schemes definitions.
  * @cipher_schemes: a pointer to an array of cipher scheme definitions
  *     supported by HW.
+ *
+ * @txq_ac_max_pending: maximum number of frames per AC pending in all txq
+ *     entries for a vif.
  */
 struct ieee80211_hw {
        struct ieee80211_conf conf;
@@ -1904,6 +1969,7 @@ struct ieee80211_hw {
        int vif_data_size;
        int sta_data_size;
        int chanctx_data_size;
+       int txq_data_size;
        u16 queues;
        u16 max_listen_interval;
        s8 max_signal;
@@ -1920,6 +1986,7 @@ struct ieee80211_hw {
        u8 uapsd_max_sp_len;
        u8 n_cipher_schemes;
        const struct ieee80211_cipher_scheme *cipher_schemes;
+       int txq_ac_max_pending;
 };
 
 /**
@@ -3082,6 +3149,8 @@ enum ieee80211_reconfig_type {
  *     response template is provided, together with the location of the
  *     switch-timing IE within the template. The skb can only be used within
  *     the function call.
+ *
+ * @wake_tx_queue: Called when new packets have been added to the queue.
  */
 struct ieee80211_ops {
        void (*tx)(struct ieee80211_hw *hw,
@@ -3313,6 +3382,9 @@ struct ieee80211_ops {
        void (*tdls_recv_channel_switch)(struct ieee80211_hw *hw,
                                         struct ieee80211_vif *vif,
                                         struct ieee80211_tdls_ch_sw_params *params);
+
+       void (*wake_tx_queue)(struct ieee80211_hw *hw,
+                             struct ieee80211_txq *txq);
 };
 
 /**
@@ -5308,30 +5380,13 @@ int ieee80211_reserve_tid(struct ieee80211_sta *sta, u8 tid);
 void ieee80211_unreserve_tid(struct ieee80211_sta *sta, u8 tid);
 
 /**
- * ieee80211_ie_split - split an IE buffer according to ordering
- *
- * @ies: the IE buffer
- * @ielen: the length of the IE buffer
- * @ids: an array with element IDs that are allowed before
- *     the split
- * @n_ids: the size of the element ID array
- * @offset: offset where to start splitting in the buffer
+ * ieee80211_tx_dequeue - dequeue a packet from a software tx queue
  *
- * This function splits an IE buffer by updating the @offset
- * variable to point to the location where the buffer should be
- * split.
- *
- * It assumes that the given IE buffer is well-formed, this
- * has to be guaranteed by the caller!
- *
- * It also assumes that the IEs in the buffer are ordered
- * correctly, if not the result of using this function will not
- * be ordered correctly either, i.e. it does no reordering.
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+ * @txq: pointer obtained from station or virtual interface
  *
- * The function returns the offset where the next part of the
- * buffer starts, which may be @ielen if the entire (remainder)
- * of the buffer should be used.
+ * Returns the skb if successful, %NULL if no frame was available.
  */
-size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
-                         const u8 *ids, int n_ids, size_t offset);
+struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
+                                    struct ieee80211_txq *txq);
 #endif /* MAC80211_H */
index fb4e8a3d6229b1205bd14644900d4640a3ac40fa..e18e7fd43f47d996613b0c1e7dfe6bc0e636c476 100644 (file)
@@ -213,7 +213,7 @@ struct ieee802154_ops {
        int             (*set_hw_addr_filt)(struct ieee802154_hw *hw,
                                            struct ieee802154_hw_addr_filt *filt,
                                            unsigned long changed);
-       int             (*set_txpower)(struct ieee802154_hw *hw, int db);
+       int             (*set_txpower)(struct ieee802154_hw *hw, s8 dbm);
        int             (*set_lbt)(struct ieee802154_hw *hw, bool on);
        int             (*set_cca_mode)(struct ieee802154_hw *hw,
                                        const struct wpan_phy_cca *cca);
@@ -247,7 +247,7 @@ static inline void ieee802154_le64_to_be64(void *be64_dst, const void *le64_src)
        __put_unaligned_memmove64(swab64p(le64_src), be64_dst);
 }
 
-/* Basic interface to register ieee802154 hwice */
+/* Basic interface to register ieee802154 device */
 struct ieee802154_hw *
 ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops);
 void ieee802154_free_hw(struct ieee802154_hw *hw);
index 8049819803939043db30ea182b715341f0cd9587..d6a2f0ed5130582d0489bc372e3b7e033f5267e8 100644 (file)
@@ -195,6 +195,7 @@ struct nft_set_estimate {
 };
 
 struct nft_set_ext;
+struct nft_expr;
 
 /**
  *     struct nft_set_ops - nf_tables set operations
@@ -217,6 +218,15 @@ struct nft_set_ops {
        bool                            (*lookup)(const struct nft_set *set,
                                                  const struct nft_data *key,
                                                  const struct nft_set_ext **ext);
+       bool                            (*update)(struct nft_set *set,
+                                                 const struct nft_data *key,
+                                                 void *(*new)(struct nft_set *,
+                                                              const struct nft_expr *,
+                                                              struct nft_data []),
+                                                 const struct nft_expr *expr,
+                                                 struct nft_data data[],
+                                                 const struct nft_set_ext **ext);
+
        int                             (*insert)(const struct nft_set *set,
                                                  const struct nft_set_elem *elem);
        void                            (*activate)(const struct nft_set *set,
@@ -257,6 +267,9 @@ void nft_unregister_set(struct nft_set_ops *ops);
  *     @dtype: data type (verdict or numeric type defined by userspace)
  *     @size: maximum set size
  *     @nelems: number of elements
+ *     @ndeact: number of deactivated elements queued for removal
+ *     @timeout: default timeout value in msecs
+ *     @gc_int: garbage collection interval in msecs
  *     @policy: set parameterization (see enum nft_set_policies)
  *     @ops: set ops
  *     @pnet: network namespace
@@ -272,7 +285,10 @@ struct nft_set {
        u32                             ktype;
        u32                             dtype;
        u32                             size;
-       u32                             nelems;
+       atomic_t                        nelems;
+       u32                             ndeact;
+       u64                             timeout;
+       u32                             gc_int;
        u16                             policy;
        /* runtime data below here */
        const struct nft_set_ops        *ops ____cacheline_aligned;
@@ -289,16 +305,27 @@ static inline void *nft_set_priv(const struct nft_set *set)
        return (void *)set->data;
 }
 
+static inline struct nft_set *nft_set_container_of(const void *priv)
+{
+       return (void *)priv - offsetof(struct nft_set, data);
+}
+
 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);
 
+static inline unsigned long nft_set_gc_interval(const struct nft_set *set)
+{
+       return set->gc_int ? msecs_to_jiffies(set->gc_int) : HZ;
+}
+
 /**
  *     struct nft_set_binding - nf_tables set binding
  *
  *     @list: set bindings list node
  *     @chain: chain containing the rule bound to the set
+ *     @flags: set action flags
  *
  *     A set binding contains all information necessary for validation
  *     of new elements added to a bound set.
@@ -306,6 +333,7 @@ struct nft_set *nf_tables_set_lookup_byid(const struct net *net,
 struct nft_set_binding {
        struct list_head                list;
        const struct nft_chain          *chain;
+       u32                             flags;
 };
 
 int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
@@ -319,12 +347,18 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
  *     @NFT_SET_EXT_KEY: element key
  *     @NFT_SET_EXT_DATA: mapping data
  *     @NFT_SET_EXT_FLAGS: element flags
+ *     @NFT_SET_EXT_TIMEOUT: element timeout
+ *     @NFT_SET_EXT_EXPIRATION: element expiration time
+ *     @NFT_SET_EXT_USERDATA: user data associated with the element
  *     @NFT_SET_EXT_NUM: number of extension types
  */
 enum nft_set_extensions {
        NFT_SET_EXT_KEY,
        NFT_SET_EXT_DATA,
        NFT_SET_EXT_FLAGS,
+       NFT_SET_EXT_TIMEOUT,
+       NFT_SET_EXT_EXPIRATION,
+       NFT_SET_EXT_USERDATA,
        NFT_SET_EXT_NUM
 };
 
@@ -421,14 +455,96 @@ static inline u8 *nft_set_ext_flags(const struct nft_set_ext *ext)
        return nft_set_ext(ext, NFT_SET_EXT_FLAGS);
 }
 
+static inline u64 *nft_set_ext_timeout(const struct nft_set_ext *ext)
+{
+       return nft_set_ext(ext, NFT_SET_EXT_TIMEOUT);
+}
+
+static inline unsigned long *nft_set_ext_expiration(const struct nft_set_ext *ext)
+{
+       return nft_set_ext(ext, NFT_SET_EXT_EXPIRATION);
+}
+
+static inline struct nft_userdata *nft_set_ext_userdata(const struct nft_set_ext *ext)
+{
+       return nft_set_ext(ext, NFT_SET_EXT_USERDATA);
+}
+
+static inline bool nft_set_elem_expired(const struct nft_set_ext *ext)
+{
+       return nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION) &&
+              time_is_before_eq_jiffies(*nft_set_ext_expiration(ext));
+}
+
 static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set,
                                                   void *elem)
 {
        return elem + set->ops->elemsize;
 }
 
+void *nft_set_elem_init(const struct nft_set *set,
+                       const struct nft_set_ext_tmpl *tmpl,
+                       const struct nft_data *key,
+                       const struct nft_data *data,
+                       u64 timeout, gfp_t gfp);
 void nft_set_elem_destroy(const struct nft_set *set, void *elem);
 
+/**
+ *     struct nft_set_gc_batch_head - nf_tables set garbage collection batch
+ *
+ *     @rcu: rcu head
+ *     @set: set the elements belong to
+ *     @cnt: count of elements
+ */
+struct nft_set_gc_batch_head {
+       struct rcu_head                 rcu;
+       const struct nft_set            *set;
+       unsigned int                    cnt;
+};
+
+#define NFT_SET_GC_BATCH_SIZE  ((PAGE_SIZE -                             \
+                                 sizeof(struct nft_set_gc_batch_head)) / \
+                                sizeof(void *))
+
+/**
+ *     struct nft_set_gc_batch - nf_tables set garbage collection batch
+ *
+ *     @head: GC batch head
+ *     @elems: garbage collection elements
+ */
+struct nft_set_gc_batch {
+       struct nft_set_gc_batch_head    head;
+       void                            *elems[NFT_SET_GC_BATCH_SIZE];
+};
+
+struct nft_set_gc_batch *nft_set_gc_batch_alloc(const struct nft_set *set,
+                                               gfp_t gfp);
+void nft_set_gc_batch_release(struct rcu_head *rcu);
+
+static inline void nft_set_gc_batch_complete(struct nft_set_gc_batch *gcb)
+{
+       if (gcb != NULL)
+               call_rcu(&gcb->head.rcu, nft_set_gc_batch_release);
+}
+
+static inline struct nft_set_gc_batch *
+nft_set_gc_batch_check(const struct nft_set *set, struct nft_set_gc_batch *gcb,
+                      gfp_t gfp)
+{
+       if (gcb != NULL) {
+               if (gcb->head.cnt + 1 < ARRAY_SIZE(gcb->elems))
+                       return gcb;
+               nft_set_gc_batch_complete(gcb);
+       }
+       return nft_set_gc_batch_alloc(set, gfp);
+}
+
+static inline void nft_set_gc_batch_add(struct nft_set_gc_batch *gcb,
+                                       void *elem)
+{
+       gcb->elems[gcb->head.cnt++] = elem;
+}
+
 /**
  *     struct nft_expr_type - nf_tables expression type
  *
@@ -750,6 +866,8 @@ static inline u8 nft_genmask_cur(const struct net *net)
        return 1 << ACCESS_ONCE(net->nft.gencursor);
 }
 
+#define NFT_GENMASK_ANY                ((1 << 0) | (1 << 1))
+
 /*
  * Set element transaction helpers
  */
@@ -766,6 +884,41 @@ static inline void nft_set_elem_change_active(const struct nft_set *set,
        ext->genmask ^= nft_genmask_next(read_pnet(&set->pnet));
 }
 
+/*
+ * We use a free bit in the genmask field to indicate the element
+ * is busy, meaning it is currently being processed either by
+ * the netlink API or GC.
+ *
+ * Even though the genmask is only a single byte wide, this works
+ * because the extension structure if fully constant once initialized,
+ * so there are no non-atomic write accesses unless it is already
+ * marked busy.
+ */
+#define NFT_SET_ELEM_BUSY_MASK (1 << 2)
+
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+#define NFT_SET_ELEM_BUSY_BIT  2
+#elif defined(__BIG_ENDIAN_BITFIELD)
+#define NFT_SET_ELEM_BUSY_BIT  (BITS_PER_LONG - BITS_PER_BYTE + 2)
+#else
+#error
+#endif
+
+static inline int nft_set_elem_mark_busy(struct nft_set_ext *ext)
+{
+       unsigned long *word = (unsigned long *)ext;
+
+       BUILD_BUG_ON(offsetof(struct nft_set_ext, genmask) != 0);
+       return test_and_set_bit(NFT_SET_ELEM_BUSY_BIT, word);
+}
+
+static inline void nft_set_elem_clear_busy(struct nft_set_ext *ext)
+{
+       unsigned long *word = (unsigned long *)ext;
+
+       clear_bit(NFT_SET_ELEM_BUSY_BIT, word);
+}
+
 /**
  *     struct nft_trans - nf_tables object update in transaction
  *
index a75fc8e27cd698483232a3f3a379efc76308a905..c6f400cfaac8d76673d559bf041c5da4b32abf13 100644 (file)
@@ -31,6 +31,9 @@ void nft_cmp_module_exit(void);
 int nft_lookup_module_init(void);
 void nft_lookup_module_exit(void);
 
+int nft_dynset_module_init(void);
+void nft_dynset_module_exit(void);
+
 int nft_bitwise_module_init(void);
 void nft_bitwise_module_exit(void);
 
index ab672b537dd4618dc25c31d1e10f400c96284fd2..020a814bc8ed7145f7b7491c6c21ba07b01e9c36 100644 (file)
@@ -83,6 +83,10 @@ struct nfc_hci_pipe {
 };
 
 #define NFC_HCI_MAX_CUSTOM_GATES       50
+/*
+ * According to specification 102 622 chapter 4.4 Pipes,
+ * the pipe identifier is 7 bits long.
+ */
 #define NFC_HCI_MAX_PIPES              127
 struct nfc_hci_init_data {
        u8 gate_count;
index ff87f8611fa3246d4570c9c780d29f8ee4ff0f6c..d4dcc7199fd766aeddd2cfe51a8411d6a873581f 100644 (file)
@@ -71,6 +71,7 @@ struct nci_ops {
        int   (*close)(struct nci_dev *ndev);
        int   (*send)(struct nci_dev *ndev, struct sk_buff *skb);
        int   (*setup)(struct nci_dev *ndev);
+       int   (*fw_download)(struct nci_dev *ndev, const char *firmware_name);
        __u32 (*get_rfprotocol)(struct nci_dev *ndev, __u8 rf_protocol);
        int   (*discover_se)(struct nci_dev *ndev);
        int   (*disable_se)(struct nci_dev *ndev, u32 se_idx);
@@ -137,6 +138,10 @@ struct nci_conn_info {
 #define NCI_HCI_INVALID_HOST               0x80
 
 #define NCI_HCI_MAX_CUSTOM_GATES   50
+/*
+ * According to specification 102 622 chapter 4.4 Pipes,
+ * the pipe identifier is 7 bits long.
+ */
 #define NCI_HCI_MAX_PIPES          127
 
 struct nci_hci_gate {
index 73190e65d5c13aa7217cce910203f4bd5f63d385..7ac029c0754678d900bb1cb78dc20fee6ba859c1 100644 (file)
@@ -157,7 +157,7 @@ struct nfc_evt_transaction {
        u32 aid_len;
        u8 aid[NFC_MAX_AID_LENGTH];
        u8 params_len;
-       u8 params[NFC_MAX_PARAMS_LENGTH];
+       u8 params[0];
 } __packed;
 
 struct nfc_genl_data {
index 6c6d5393fc349b46c384aa033554e4d12682040d..343d922d15c2ce0ce1a53dd55d7c006a42e15c99 100644 (file)
@@ -137,7 +137,7 @@ void rtnl_af_register(struct rtnl_af_ops *ops);
 void rtnl_af_unregister(struct rtnl_af_ops *ops);
 
 struct net *rtnl_link_get_net(struct net *src_net, struct nlattr *tb[]);
-struct net_device *rtnl_create_link(struct net *net, char *ifname,
+struct net_device *rtnl_create_link(struct net *net, const char *ifname,
                                    unsigned char name_assign_type,
                                    const struct rtnl_link_ops *ops,
                                    struct nlattr *tb[]);
index 963303fb96ae227263e648fb0c8dbafdc9cbc945..9598871485ce3d7d36f96be9643d94e7ca85cfe0 100644 (file)
@@ -179,6 +179,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo);
 #define TCPOPT_SACK             5       /* SACK Block */
 #define TCPOPT_TIMESTAMP       8       /* Better RTT estimations/PAWS */
 #define TCPOPT_MD5SIG          19      /* MD5 Signature (RFC2385) */
+#define TCPOPT_FASTOPEN                34      /* Fast open (RFC7413) */
 #define TCPOPT_EXP             254     /* Experimental */
 /* Magic number to be after the option value for sharing TCP
  * experimental options. See draft-ietf-tcpm-experimental-options-00.txt
@@ -194,6 +195,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo);
 #define TCPOLEN_SACK_PERM      2
 #define TCPOLEN_TIMESTAMP      10
 #define TCPOLEN_MD5SIG         18
+#define TCPOLEN_FASTOPEN_BASE  2
 #define TCPOLEN_EXP_FASTOPEN_BASE  4
 
 /* But this is what stacks really send out. */
@@ -1337,7 +1339,8 @@ void tcp_fastopen_cache_get(struct sock *sk, u16 *mss,
                            struct tcp_fastopen_cookie *cookie, int *syn_loss,
                            unsigned long *last_syn_loss);
 void tcp_fastopen_cache_set(struct sock *sk, u16 mss,
-                           struct tcp_fastopen_cookie *cookie, bool syn_lost);
+                           struct tcp_fastopen_cookie *cookie, bool syn_lost,
+                           u16 try_exp);
 struct tcp_fastopen_request {
        /* Fast Open cookie. Size 0 means a cookie request */
        struct tcp_fastopen_cookie      cookie;
index 1a20d33d56bc1ffb6d91826282ddabfc53d4fb25..c491c1221606e0f2625de8b53cfd8fdcd29282c9 100644 (file)
@@ -77,13 +77,14 @@ void setup_udp_tunnel_sock(struct net *net, struct socket *sock,
                           struct udp_tunnel_sock_cfg *sock_cfg);
 
 /* Transmit the skb using UDP encapsulation. */
-int udp_tunnel_xmit_skb(struct rtable *rt, struct sk_buff *skb,
+int udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
                        __be32 src, __be32 dst, __u8 tos, __u8 ttl,
                        __be16 df, __be16 src_port, __be16 dst_port,
                        bool xnet, bool nocheck);
 
 #if IS_ENABLED(CONFIG_IPV6)
-int udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sk_buff *skb,
+int udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sock *sk,
+                        struct sk_buff *skb,
                         struct net_device *dev, struct in6_addr *saddr,
                         struct in6_addr *daddr,
                         __u8 prio, __u8 ttl, __be16 src_port,
index 756e4636bad8a3a8395013db990cf774aa49aa19..0082b5d33d7d3f2ea66fe94c26b8c3572188affc 100644 (file)
@@ -145,7 +145,7 @@ struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port,
 
 void vxlan_sock_release(struct vxlan_sock *vs);
 
-int vxlan_xmit_skb(struct rtable *rt, struct sk_buff *skb,
+int vxlan_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
                   __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df,
                   __be16 src_port, __be16 dst_port, struct vxlan_metadata *md,
                   bool xnet, u32 vxflags);
index 461f8353949370f7342f83d23513d864ac0adc6f..36ac102c97c72b1b5d62f99e28bf285fbad9f8bb 100644 (file)
@@ -332,7 +332,7 @@ struct xfrm_state_afinfo {
        int                     (*tmpl_sort)(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n);
        int                     (*state_sort)(struct xfrm_state **dst, struct xfrm_state **src, int n);
        int                     (*output)(struct sock *sk, struct sk_buff *skb);
-       int                     (*output_finish)(struct sk_buff *skb);
+       int                     (*output_finish)(struct sock *sk, struct sk_buff *skb);
        int                     (*extract_input)(struct xfrm_state *x,
                                                 struct sk_buff *skb);
        int                     (*extract_output)(struct xfrm_state *x,
@@ -1503,7 +1503,7 @@ int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb);
 int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type);
 int xfrm_input_resume(struct sk_buff *skb, int nexthdr);
 int xfrm_output_resume(struct sk_buff *skb, int err);
-int xfrm_output(struct sk_buff *skb);
+int xfrm_output(struct sock *sk, struct sk_buff *skb);
 int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb);
 void xfrm_local_error(struct sk_buff *skb, int mtu);
 int xfrm4_extract_header(struct sk_buff *skb);
@@ -1524,7 +1524,7 @@ static inline int xfrm4_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi)
 int xfrm4_extract_output(struct xfrm_state *x, struct sk_buff *skb);
 int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb);
 int xfrm4_output(struct sock *sk, struct sk_buff *skb);
-int xfrm4_output_finish(struct sk_buff *skb);
+int xfrm4_output_finish(struct sock *sk, struct sk_buff *skb);
 int xfrm4_rcv_cb(struct sk_buff *skb, u8 protocol, int err);
 int xfrm4_protocol_register(struct xfrm4_protocol *handler, unsigned char protocol);
 int xfrm4_protocol_deregister(struct xfrm4_protocol *handler, unsigned char protocol);
@@ -1549,7 +1549,7 @@ __be32 xfrm6_tunnel_spi_lookup(struct net *net, const xfrm_address_t *saddr);
 int xfrm6_extract_output(struct xfrm_state *x, struct sk_buff *skb);
 int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb);
 int xfrm6_output(struct sock *sk, struct sk_buff *skb);
-int xfrm6_output_finish(struct sk_buff *skb);
+int xfrm6_output_finish(struct sock *sk, struct sk_buff *skb);
 int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb,
                          u8 **prevhdr);
 
index c303588bb7670dcf3f90edf4fa0427f27ff1d1de..d2947c52dc670f817a661ad1c8ad7925c575c61a 100644 (file)
@@ -25,6 +25,7 @@ enum {
        FOU_CMD_UNSPEC,
        FOU_CMD_ADD,
        FOU_CMD_DEL,
+       FOU_CMD_GET,
 
        __FOU_CMD_MAX,
 };
index 7ffb18df01caebb7cbe1c06db756a7a9e284a541..d9cd19214b9816c036db8f8d26de81c3d53dffb7 100644 (file)
@@ -465,6 +465,9 @@ enum {
        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_RSS_QUERY_EN,   /* RSS Redirection Table and Hash Key query
+                                * on/off switch
+                                */
        __IFLA_VF_MAX,
 };
 
@@ -509,6 +512,11 @@ struct ifla_vf_link_state {
        __u32 link_state;
 };
 
+struct ifla_vf_rss_query_en {
+       __u32 vf;
+       __u32 setting;
+};
+
 /* VF ports management section
  *
  *     Nested layout of set/get msg is:
index b9783931503b1f704a2422d85922301929dc2675..05ee1e0804a3f2c8971c1956dc768c416e4cec71 100644 (file)
@@ -208,12 +208,14 @@ enum nft_rule_compat_attributes {
  * @NFT_SET_CONSTANT: set contents may not change while bound
  * @NFT_SET_INTERVAL: set contains intervals
  * @NFT_SET_MAP: set is used as a dictionary
+ * @NFT_SET_TIMEOUT: set uses timeouts
  */
 enum nft_set_flags {
        NFT_SET_ANONYMOUS               = 0x1,
        NFT_SET_CONSTANT                = 0x2,
        NFT_SET_INTERVAL                = 0x4,
        NFT_SET_MAP                     = 0x8,
+       NFT_SET_TIMEOUT                 = 0x10,
 };
 
 /**
@@ -252,6 +254,8 @@ enum nft_set_desc_attributes {
  * @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)
+ * @NFTA_SET_TIMEOUT: default timeout value (NLA_U64)
+ * @NFTA_SET_GC_INTERVAL: garbage collection interval (NLA_U32)
  */
 enum nft_set_attributes {
        NFTA_SET_UNSPEC,
@@ -265,6 +269,8 @@ enum nft_set_attributes {
        NFTA_SET_POLICY,
        NFTA_SET_DESC,
        NFTA_SET_ID,
+       NFTA_SET_TIMEOUT,
+       NFTA_SET_GC_INTERVAL,
        __NFTA_SET_MAX
 };
 #define NFTA_SET_MAX           (__NFTA_SET_MAX - 1)
@@ -284,12 +290,18 @@ enum nft_set_elem_flags {
  * @NFTA_SET_ELEM_KEY: key value (NLA_NESTED: nft_data)
  * @NFTA_SET_ELEM_DATA: data value of mapping (NLA_NESTED: nft_data_attributes)
  * @NFTA_SET_ELEM_FLAGS: bitmask of nft_set_elem_flags (NLA_U32)
+ * @NFTA_SET_ELEM_TIMEOUT: timeout value (NLA_U64)
+ * @NFTA_SET_ELEM_EXPIRATION: expiration time (NLA_U64)
+ * @NFTA_SET_ELEM_USERDATA: user data (NLA_BINARY)
  */
 enum nft_set_elem_attributes {
        NFTA_SET_ELEM_UNSPEC,
        NFTA_SET_ELEM_KEY,
        NFTA_SET_ELEM_DATA,
        NFTA_SET_ELEM_FLAGS,
+       NFTA_SET_ELEM_TIMEOUT,
+       NFTA_SET_ELEM_EXPIRATION,
+       NFTA_SET_ELEM_USERDATA,
        __NFTA_SET_ELEM_MAX
 };
 #define NFTA_SET_ELEM_MAX      (__NFTA_SET_ELEM_MAX - 1)
@@ -505,6 +517,33 @@ enum nft_lookup_attributes {
 };
 #define NFTA_LOOKUP_MAX                (__NFTA_LOOKUP_MAX - 1)
 
+enum nft_dynset_ops {
+       NFT_DYNSET_OP_ADD,
+       NFT_DYNSET_OP_UPDATE,
+};
+
+/**
+ * enum nft_dynset_attributes - dynset expression attributes
+ *
+ * @NFTA_DYNSET_SET_NAME: name of set the to add data to (NLA_STRING)
+ * @NFTA_DYNSET_SET_ID: uniquely identifier of the set in the transaction (NLA_U32)
+ * @NFTA_DYNSET_OP: operation (NLA_U32)
+ * @NFTA_DYNSET_SREG_KEY: source register of the key (NLA_U32)
+ * @NFTA_DYNSET_SREG_DATA: source register of the data (NLA_U32)
+ * @NFTA_DYNSET_TIMEOUT: timeout value for the new element (NLA_U64)
+ */
+enum nft_dynset_attributes {
+       NFTA_DYNSET_UNSPEC,
+       NFTA_DYNSET_SET_NAME,
+       NFTA_DYNSET_SET_ID,
+       NFTA_DYNSET_OP,
+       NFTA_DYNSET_SREG_KEY,
+       NFTA_DYNSET_SREG_DATA,
+       NFTA_DYNSET_TIMEOUT,
+       __NFTA_DYNSET_MAX,
+};
+#define NFTA_DYNSET_MAX                (__NFTA_DYNSET_MAX - 1)
+
 /**
  * enum nft_payload_bases - nf_tables payload expression offset bases
  *
index bea910f924dde6b125568e83e737801e721778c0..974db03f7b1a2d9ddf96d0b34a409f8356e94d1a 100644 (file)
@@ -134,6 +134,8 @@ enum {
 
        RTM_NEWNSID = 88,
 #define RTM_NEWNSID RTM_NEWNSID
+       RTM_DELNSID = 89,
+#define RTM_DELNSID RTM_DELNSID
        RTM_GETNSID = 90,
 #define RTM_GETNSID RTM_GETNSID
 
@@ -635,6 +637,8 @@ enum rtnetlink_groups {
 #define RTNLGRP_MDB            RTNLGRP_MDB
        RTNLGRP_MPLS_ROUTE,
 #define RTNLGRP_MPLS_ROUTE     RTNLGRP_MPLS_ROUTE
+       RTNLGRP_NSID,
+#define RTNLGRP_NSID           RTNLGRP_NSID
        __RTNLGRP_MAX
 };
 #define RTNLGRP_MAX    (__RTNLGRP_MAX - 1)
index 46b114c0140bf6901f94fda107e799a87a10f4db..476709bd068a474f7edcac83a4869849ccfb4b17 100644 (file)
@@ -3200,7 +3200,7 @@ EXPORT_SYMBOL(hci_register_dev);
 /* Unregister HCI device */
 void hci_unregister_dev(struct hci_dev *hdev)
 {
-       int i, id;
+       int id;
 
        BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
 
@@ -3214,9 +3214,6 @@ void hci_unregister_dev(struct hci_dev *hdev)
 
        hci_dev_do_close(hdev);
 
-       for (i = 0; i < NUM_REASSEMBLY; i++)
-               kfree_skb(hdev->reassembly[i]);
-
        cancel_work_sync(&hdev->power_on);
 
        if (!test_bit(HCI_INIT, &hdev->flags) &&
@@ -3320,149 +3317,6 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
 }
 EXPORT_SYMBOL(hci_recv_frame);
 
-static int hci_reassembly(struct hci_dev *hdev, int type, void *data,
-                         int count, __u8 index)
-{
-       int len = 0;
-       int hlen = 0;
-       int remain = count;
-       struct sk_buff *skb;
-       struct bt_skb_cb *scb;
-
-       if ((type < HCI_ACLDATA_PKT || type > HCI_EVENT_PKT) ||
-           index >= NUM_REASSEMBLY)
-               return -EILSEQ;
-
-       skb = hdev->reassembly[index];
-
-       if (!skb) {
-               switch (type) {
-               case HCI_ACLDATA_PKT:
-                       len = HCI_MAX_FRAME_SIZE;
-                       hlen = HCI_ACL_HDR_SIZE;
-                       break;
-               case HCI_EVENT_PKT:
-                       len = HCI_MAX_EVENT_SIZE;
-                       hlen = HCI_EVENT_HDR_SIZE;
-                       break;
-               case HCI_SCODATA_PKT:
-                       len = HCI_MAX_SCO_SIZE;
-                       hlen = HCI_SCO_HDR_SIZE;
-                       break;
-               }
-
-               skb = bt_skb_alloc(len, GFP_ATOMIC);
-               if (!skb)
-                       return -ENOMEM;
-
-               scb = (void *) skb->cb;
-               scb->expect = hlen;
-               scb->pkt_type = type;
-
-               hdev->reassembly[index] = skb;
-       }
-
-       while (count) {
-               scb = (void *) skb->cb;
-               len = min_t(uint, scb->expect, count);
-
-               memcpy(skb_put(skb, len), data, len);
-
-               count -= len;
-               data += len;
-               scb->expect -= len;
-               remain = count;
-
-               switch (type) {
-               case HCI_EVENT_PKT:
-                       if (skb->len == HCI_EVENT_HDR_SIZE) {
-                               struct hci_event_hdr *h = hci_event_hdr(skb);
-                               scb->expect = h->plen;
-
-                               if (skb_tailroom(skb) < scb->expect) {
-                                       kfree_skb(skb);
-                                       hdev->reassembly[index] = NULL;
-                                       return -ENOMEM;
-                               }
-                       }
-                       break;
-
-               case HCI_ACLDATA_PKT:
-                       if (skb->len  == HCI_ACL_HDR_SIZE) {
-                               struct hci_acl_hdr *h = hci_acl_hdr(skb);
-                               scb->expect = __le16_to_cpu(h->dlen);
-
-                               if (skb_tailroom(skb) < scb->expect) {
-                                       kfree_skb(skb);
-                                       hdev->reassembly[index] = NULL;
-                                       return -ENOMEM;
-                               }
-                       }
-                       break;
-
-               case HCI_SCODATA_PKT:
-                       if (skb->len == HCI_SCO_HDR_SIZE) {
-                               struct hci_sco_hdr *h = hci_sco_hdr(skb);
-                               scb->expect = h->dlen;
-
-                               if (skb_tailroom(skb) < scb->expect) {
-                                       kfree_skb(skb);
-                                       hdev->reassembly[index] = NULL;
-                                       return -ENOMEM;
-                               }
-                       }
-                       break;
-               }
-
-               if (scb->expect == 0) {
-                       /* Complete frame */
-
-                       bt_cb(skb)->pkt_type = type;
-                       hci_recv_frame(hdev, skb);
-
-                       hdev->reassembly[index] = NULL;
-                       return remain;
-               }
-       }
-
-       return remain;
-}
-
-#define STREAM_REASSEMBLY 0
-
-int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count)
-{
-       int type;
-       int rem = 0;
-
-       while (count) {
-               struct sk_buff *skb = hdev->reassembly[STREAM_REASSEMBLY];
-
-               if (!skb) {
-                       struct { char type; } *pkt;
-
-                       /* Start of the frame */
-                       pkt = data;
-                       type = pkt->type;
-
-                       data++;
-                       count--;
-               } else
-                       type = bt_cb(skb)->pkt_type;
-
-               rem = hci_reassembly(hdev, type, data, count,
-                                    STREAM_REASSEMBLY);
-               if (rem < 0)
-                       return rem;
-
-               data += (count - rem);
-               count = rem;
-       }
-
-       return rem;
-}
-EXPORT_SYMBOL(hci_recv_stream_fragment);
-
 /* ---- Interface to upper protocols ---- */
 
 int hci_register_cb(struct hci_cb *cb)
index 01031038eb0e489a43c3484b0b79483fe617195b..7b61be73650fe574bd655c19ae79f8b4d8a1b877 100644 (file)
@@ -2036,6 +2036,33 @@ unlock:
        hci_dev_unlock(hdev);
 }
 
+static void hci_cs_le_read_remote_features(struct hci_dev *hdev, u8 status)
+{
+       struct hci_cp_le_read_remote_features *cp;
+       struct hci_conn *conn;
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+       if (!status)
+               return;
+
+       cp = hci_sent_cmd_data(hdev, HCI_OP_LE_READ_REMOTE_FEATURES);
+       if (!cp)
+               return;
+
+       hci_dev_lock(hdev);
+
+       conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
+       if (conn) {
+               if (conn->state == BT_CONFIG) {
+                       hci_connect_cfm(conn, status);
+                       hci_conn_drop(conn);
+               }
+       }
+
+       hci_dev_unlock(hdev);
+}
+
 static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status)
 {
        struct hci_cp_le_start_enc *cp;
@@ -3104,6 +3131,10 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb,
                hci_cs_le_create_conn(hdev, ev->status);
                break;
 
+       case HCI_OP_LE_READ_REMOTE_FEATURES:
+               hci_cs_le_read_remote_features(hdev, ev->status);
+               break;
+
        case HCI_OP_LE_START_ENC:
                hci_cs_le_start_enc(hdev, ev->status);
                break;
@@ -4515,7 +4546,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 
        conn->sec_level = BT_SECURITY_LOW;
        conn->handle = __le16_to_cpu(ev->handle);
-       conn->state = BT_CONNECTED;
+       conn->state = BT_CONFIG;
 
        conn->le_conn_interval = le16_to_cpu(ev->interval);
        conn->le_conn_latency = le16_to_cpu(ev->latency);
@@ -4524,7 +4555,33 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
        hci_debugfs_create_conn(conn);
        hci_conn_add_sysfs(conn);
 
-       hci_connect_cfm(conn, ev->status);
+       if (!ev->status) {
+               /* The remote features procedure is defined for master
+                * role only. So only in case of an initiated connection
+                * request the remote features.
+                *
+                * If the local controller supports slave-initiated features
+                * exchange, then requesting the remote features in slave
+                * role is possible. Otherwise just transition into the
+                * connected state without requesting the remote features.
+                */
+               if (conn->out ||
+                   (hdev->le_features[0] & HCI_LE_SLAVE_FEATURES)) {
+                       struct hci_cp_le_read_remote_features cp;
+
+                       cp.handle = __cpu_to_le16(conn->handle);
+
+                       hci_send_cmd(hdev, HCI_OP_LE_READ_REMOTE_FEATURES,
+                                    sizeof(cp), &cp);
+
+                       hci_conn_hold(conn);
+               } else {
+                       conn->state = BT_CONNECTED;
+                       hci_connect_cfm(conn, ev->status);
+               }
+       } else {
+               hci_connect_cfm(conn, ev->status);
+       }
 
        params = hci_pend_le_action_lookup(&hdev->pend_le_conns, &conn->dst,
                                           conn->dst_type);
@@ -4826,6 +4883,48 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb)
        hci_dev_unlock(hdev);
 }
 
+static void hci_le_remote_feat_complete_evt(struct hci_dev *hdev,
+                                           struct sk_buff *skb)
+{
+       struct hci_ev_le_remote_feat_complete *ev = (void *)skb->data;
+       struct hci_conn *conn;
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
+
+       hci_dev_lock(hdev);
+
+       conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
+       if (conn) {
+               if (!ev->status)
+                       memcpy(conn->features[0], ev->features, 8);
+
+               if (conn->state == BT_CONFIG) {
+                       __u8 status;
+
+                       /* If the local controller supports slave-initiated
+                        * features exchange, but the remote controller does
+                        * not, then it is possible that the error code 0x1a
+                        * for unsupported remote feature gets returned.
+                        *
+                        * In this specific case, allow the connection to
+                        * transition into connected state and mark it as
+                        * successful.
+                        */
+                       if ((hdev->le_features[0] & HCI_LE_SLAVE_FEATURES) &&
+                           !conn->out && ev->status == 0x1a)
+                               status = 0x00;
+                       else
+                               status = ev->status;
+
+                       conn->state = BT_CONNECTED;
+                       hci_connect_cfm(conn, status);
+                       hci_conn_drop(conn);
+               }
+       }
+
+       hci_dev_unlock(hdev);
+}
+
 static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_ev_le_ltk_req *ev = (void *) skb->data;
@@ -4999,6 +5098,10 @@ static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_le_adv_report_evt(hdev, skb);
                break;
 
+       case HCI_EV_LE_REMOTE_FEAT_COMPLETE:
+               hci_le_remote_feat_complete_evt(hdev, skb);
+               break;
+
        case HCI_EV_LE_LTK_REQ:
                hci_le_ltk_request_evt(hdev, skb);
                break;
index 845dfcc43a20e79b12904eae88399d8230b1e564..7fd87e7135b52753c0bcefd58cb4a290c57c77ba 100644 (file)
@@ -6466,6 +6466,145 @@ static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data,
        return eir_len;
 }
 
+static void read_local_oob_ext_data_complete(struct hci_dev *hdev, u8 status,
+                                            u16 opcode, struct sk_buff *skb)
+{
+       const struct mgmt_cp_read_local_oob_ext_data *mgmt_cp;
+       struct mgmt_rp_read_local_oob_ext_data *mgmt_rp;
+       u8 *h192, *r192, *h256, *r256;
+       struct mgmt_pending_cmd *cmd;
+       u16 eir_len;
+       int err;
+
+       BT_DBG("%s status %u", hdev->name, status);
+
+       cmd = pending_find(MGMT_OP_READ_LOCAL_OOB_EXT_DATA, hdev);
+       if (!cmd)
+               return;
+
+       mgmt_cp = cmd->param;
+
+       if (status) {
+               status = mgmt_status(status);
+               eir_len = 0;
+
+               h192 = NULL;
+               r192 = NULL;
+               h256 = NULL;
+               r256 = NULL;
+       } else if (opcode == HCI_OP_READ_LOCAL_OOB_DATA) {
+               struct hci_rp_read_local_oob_data *rp;
+
+               if (skb->len != sizeof(*rp)) {
+                       status = MGMT_STATUS_FAILED;
+                       eir_len = 0;
+               } else {
+                       status = MGMT_STATUS_SUCCESS;
+                       rp = (void *)skb->data;
+
+                       eir_len = 5 + 18 + 18;
+                       h192 = rp->hash;
+                       r192 = rp->rand;
+                       h256 = NULL;
+                       r256 = NULL;
+               }
+       } else {
+               struct hci_rp_read_local_oob_ext_data *rp;
+
+               if (skb->len != sizeof(*rp)) {
+                       status = MGMT_STATUS_FAILED;
+                       eir_len = 0;
+               } else {
+                       status = MGMT_STATUS_SUCCESS;
+                       rp = (void *)skb->data;
+
+                       if (hci_dev_test_flag(hdev, HCI_SC_ONLY)) {
+                               eir_len = 5 + 18 + 18;
+                               h192 = NULL;
+                               r192 = NULL;
+                       } else {
+                               eir_len = 5 + 18 + 18 + 18 + 18;
+                               h192 = rp->hash192;
+                               r192 = rp->rand192;
+                       }
+
+                       h256 = rp->hash256;
+                       r256 = rp->rand256;
+               }
+       }
+
+       mgmt_rp = kmalloc(sizeof(*mgmt_rp) + eir_len, GFP_KERNEL);
+       if (!mgmt_rp)
+               goto done;
+
+       if (status)
+               goto send_rsp;
+
+       eir_len = eir_append_data(mgmt_rp->eir, 0, EIR_CLASS_OF_DEV,
+                                 hdev->dev_class, 3);
+
+       if (h192 && r192) {
+               eir_len = eir_append_data(mgmt_rp->eir, eir_len,
+                                         EIR_SSP_HASH_C192, h192, 16);
+               eir_len = eir_append_data(mgmt_rp->eir, eir_len,
+                                         EIR_SSP_RAND_R192, r192, 16);
+       }
+
+       if (h256 && r256) {
+               eir_len = eir_append_data(mgmt_rp->eir, eir_len,
+                                         EIR_SSP_HASH_C256, h256, 16);
+               eir_len = eir_append_data(mgmt_rp->eir, eir_len,
+                                         EIR_SSP_RAND_R256, r256, 16);
+       }
+
+send_rsp:
+       mgmt_rp->type = mgmt_cp->type;
+       mgmt_rp->eir_len = cpu_to_le16(eir_len);
+
+       err = mgmt_cmd_complete(cmd->sk, hdev->id,
+                               MGMT_OP_READ_LOCAL_OOB_EXT_DATA, status,
+                               mgmt_rp, sizeof(*mgmt_rp) + eir_len);
+       if (err < 0 || status)
+               goto done;
+
+       hci_sock_set_flag(cmd->sk, HCI_MGMT_OOB_DATA_EVENTS);
+
+       err = mgmt_limited_event(MGMT_EV_LOCAL_OOB_DATA_UPDATED, hdev,
+                                mgmt_rp, sizeof(*mgmt_rp) + eir_len,
+                                HCI_MGMT_OOB_DATA_EVENTS, cmd->sk);
+done:
+       kfree(mgmt_rp);
+       mgmt_pending_remove(cmd);
+}
+
+static int read_local_ssp_oob_req(struct hci_dev *hdev, struct sock *sk,
+                                 struct mgmt_cp_read_local_oob_ext_data *cp)
+{
+       struct mgmt_pending_cmd *cmd;
+       struct hci_request req;
+       int err;
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, hdev,
+                              cp, sizeof(*cp));
+       if (!cmd)
+               return -ENOMEM;
+
+       hci_req_init(&req, hdev);
+
+       if (bredr_sc_enabled(hdev))
+               hci_req_add(&req, HCI_OP_READ_LOCAL_OOB_EXT_DATA, 0, NULL);
+       else
+               hci_req_add(&req, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
+
+       err = hci_req_run_skb(&req, read_local_oob_ext_data_complete);
+       if (err < 0) {
+               mgmt_pending_remove(cmd);
+               return err;
+       }
+
+       return 0;
+}
+
 static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev,
                                   void *data, u16 data_len)
 {
@@ -6517,8 +6656,19 @@ static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev,
        eir_len = 0;
        switch (cp->type) {
        case BIT(BDADDR_BREDR):
-               eir_len = eir_append_data(rp->eir, eir_len, EIR_CLASS_OF_DEV,
-                                         hdev->dev_class, 3);
+               if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) {
+                       err = read_local_ssp_oob_req(hdev, sk, cp);
+                       hci_dev_unlock(hdev);
+                       if (!err)
+                               goto done;
+
+                       status = MGMT_STATUS_FAILED;
+                       goto complete;
+               } else {
+                       eir_len = eir_append_data(rp->eir, eir_len,
+                                                 EIR_CLASS_OF_DEV,
+                                                 hdev->dev_class, 3);
+               }
                break;
        case (BIT(BDADDR_LE_PUBLIC) | BIT(BDADDR_LE_RANDOM)):
                if (hci_dev_test_flag(hdev, HCI_SC_ENABLED) &&
index 3304a544233174a3d1c7474cb19fffc05483be78..e97572b5d2ccfbce420009f60b30f5439fd1c571 100644 (file)
@@ -35,7 +35,7 @@ static inline int should_deliver(const struct net_bridge_port *p,
                p->state == BR_STATE_FORWARDING;
 }
 
-int br_dev_queue_push_xmit(struct sk_buff *skb)
+int br_dev_queue_push_xmit(struct sock *sk, struct sk_buff *skb)
 {
        if (!is_skb_forwardable(skb->dev, skb)) {
                kfree_skb(skb);
@@ -49,9 +49,10 @@ int br_dev_queue_push_xmit(struct sk_buff *skb)
 }
 EXPORT_SYMBOL_GPL(br_dev_queue_push_xmit);
 
-int br_forward_finish(struct sk_buff *skb)
+int br_forward_finish(struct sock *sk, struct sk_buff *skb)
 {
-       return NF_HOOK(NFPROTO_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+       return NF_HOOK(NFPROTO_BRIDGE, NF_BR_POST_ROUTING, sk, skb,
+                      NULL, skb->dev,
                       br_dev_queue_push_xmit);
 
 }
@@ -75,7 +76,8 @@ static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
                return;
        }
 
-       NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+       NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, NULL, skb,
+               NULL, skb->dev,
                br_forward_finish);
 }
 
@@ -96,7 +98,8 @@ static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
        skb->dev = to->dev;
        skb_forward_csum(skb);
 
-       NF_HOOK(NFPROTO_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+       NF_HOOK(NFPROTO_BRIDGE, NF_BR_FORWARD, NULL, skb,
+               indev, skb->dev,
                br_forward_finish);
 }
 
index 052c5ebbc9472c833df81e28a4895b96ba3f389c..f921a5dce22dadf465b79dd93be733707d5d5d0d 100644 (file)
@@ -55,8 +55,9 @@ static int br_pass_frame_up(struct sk_buff *skb)
        if (!skb)
                return NET_RX_DROP;
 
-       return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL,
-                      netif_receive_skb);
+       return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, NULL, skb,
+                      indev, NULL,
+                      netif_receive_skb_sk);
 }
 
 static void br_do_proxy_arp(struct sk_buff *skb, struct net_bridge *br,
@@ -119,7 +120,7 @@ static void br_do_proxy_arp(struct sk_buff *skb, struct net_bridge *br,
 }
 
 /* note: already called with rcu_read_lock */
-int br_handle_frame_finish(struct sk_buff *skb)
+int br_handle_frame_finish(struct sock *sk, struct sk_buff *skb)
 {
        const unsigned char *dest = eth_hdr(skb)->h_dest;
        struct net_bridge_port *p = br_port_get_rcu(skb->dev);
@@ -207,7 +208,7 @@ drop:
 EXPORT_SYMBOL_GPL(br_handle_frame_finish);
 
 /* note: already called with rcu_read_lock */
-static int br_handle_local_finish(struct sk_buff *skb)
+static int br_handle_local_finish(struct sock *sk, struct sk_buff *skb)
 {
        struct net_bridge_port *p = br_port_get_rcu(skb->dev);
        u16 vid = 0;
@@ -277,8 +278,8 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
                }
 
                /* Deliver packet to local host only */
-               if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,
-                           NULL, br_handle_local_finish)) {
+               if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, NULL, skb,
+                           skb->dev, NULL, br_handle_local_finish)) {
                        return RX_HANDLER_CONSUMED; /* consumed by filter */
                } else {
                        *pskb = skb;
@@ -302,7 +303,8 @@ forward:
                if (ether_addr_equal(p->br->dev->dev_addr, dest))
                        skb->pkt_type = PACKET_HOST;
 
-               NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+               NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, NULL, skb,
+                       skb->dev, NULL,
                        br_handle_frame_finish);
                break;
        default:
index c465876c7861814ba545cf83783c7ba11bbd91eb..4b6722f8f1790811d2ef4b9b1ae8839628b745c8 100644 (file)
@@ -814,7 +814,8 @@ static void __br_multicast_send_query(struct net_bridge *br,
 
        if (port) {
                skb->dev = port->dev;
-               NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+               NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, NULL, skb,
+                       NULL, skb->dev,
                        br_dev_queue_push_xmit);
        } else {
                br_multicast_select_own_querier(br, ip, skb);
index 7527e94dd5dce327064fe045506458d1a42f3c0c..ab55e2472beb0e44dece07e327f2e0eb8d3f502c 100644 (file)
@@ -111,6 +111,24 @@ static inline __be16 pppoe_proto(const struct sk_buff *skb)
         pppoe_proto(skb) == htons(PPP_IPV6) && \
         brnf_filter_pppoe_tagged)
 
+/* largest possible L2 header, see br_nf_dev_queue_xmit() */
+#define NF_BRIDGE_MAX_MAC_HEADER_LENGTH (PPPOE_SES_HLEN + ETH_HLEN)
+
+#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4)
+struct brnf_frag_data {
+       char mac[NF_BRIDGE_MAX_MAC_HEADER_LENGTH];
+       u8 encap_size;
+       u8 size;
+};
+
+static DEFINE_PER_CPU(struct brnf_frag_data, brnf_frag_data_storage);
+#endif
+
+static struct nf_bridge_info *nf_bridge_info_get(const struct sk_buff *skb)
+{
+       return skb->nf_bridge;
+}
+
 static inline struct rtable *bridge_parent_rtable(const struct net_device *dev)
 {
        struct net_bridge_port *port;
@@ -189,14 +207,6 @@ static inline void nf_bridge_pull_encap_header_rcsum(struct sk_buff *skb)
        skb->network_header += len;
 }
 
-static inline void nf_bridge_save_header(struct sk_buff *skb)
-{
-       int header_size = ETH_HLEN + nf_bridge_encap_header_len(skb);
-
-       skb_copy_from_linear_data_offset(skb, -header_size,
-                                        skb->nf_bridge->data, header_size);
-}
-
 /* When handing a packet over to the IP layer
  * check whether we have a skb that is in the
  * expected format
@@ -252,23 +262,29 @@ drop:
 
 static void nf_bridge_update_protocol(struct sk_buff *skb)
 {
-       if (skb->nf_bridge->mask & BRNF_8021Q)
+       switch (skb->nf_bridge->orig_proto) {
+       case BRNF_PROTO_8021Q:
                skb->protocol = htons(ETH_P_8021Q);
-       else if (skb->nf_bridge->mask & BRNF_PPPoE)
+               break;
+       case BRNF_PROTO_PPPOE:
                skb->protocol = htons(ETH_P_PPP_SES);
+               break;
+       case BRNF_PROTO_UNCHANGED:
+               break;
+       }
 }
 
 /* PF_BRIDGE/PRE_ROUTING *********************************************/
 /* Undo the changes made for ip6tables PREROUTING and continue the
  * bridge PRE_ROUTING hook. */
-static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb)
+static int br_nf_pre_routing_finish_ipv6(struct sock *sk, struct sk_buff *skb)
 {
-       struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+       struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
        struct rtable *rt;
 
-       if (nf_bridge->mask & BRNF_PKT_TYPE) {
+       if (nf_bridge->pkt_otherhost) {
                skb->pkt_type = PACKET_OTHERHOST;
-               nf_bridge->mask ^= BRNF_PKT_TYPE;
+               nf_bridge->pkt_otherhost = false;
        }
        nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
 
@@ -282,7 +298,8 @@ static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb)
        skb->dev = nf_bridge->physindev;
        nf_bridge_update_protocol(skb);
        nf_bridge_push_encap_header(skb);
-       NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+       NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, sk, skb,
+                      skb->dev, NULL,
                       br_handle_frame_finish, 1);
 
        return 0;
@@ -293,9 +310,8 @@ static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb)
  * don't, we use the neighbour framework to find out. In both cases, we make
  * sure that br_handle_frame_finish() is called afterwards.
  */
-static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
+static int br_nf_pre_routing_finish_bridge(struct sock *sk, struct sk_buff *skb)
 {
-       struct nf_bridge_info *nf_bridge = skb->nf_bridge;
        struct neighbour *neigh;
        struct dst_entry *dst;
 
@@ -305,12 +321,13 @@ static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
        dst = skb_dst(skb);
        neigh = dst_neigh_lookup_skb(dst, skb);
        if (neigh) {
+               struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
                int ret;
 
                if (neigh->hh.hh_len) {
                        neigh_hh_bridge(&neigh->hh, skb);
                        skb->dev = nf_bridge->physindev;
-                       ret = br_handle_frame_finish(skb);
+                       ret = br_handle_frame_finish(sk, skb);
                } else {
                        /* the neighbour function below overwrites the complete
                         * MAC header, so we save the Ethernet source address and
@@ -318,7 +335,7 @@ static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
                         */
                        skb_copy_from_linear_data_offset(skb,
                                                         -(ETH_HLEN-ETH_ALEN),
-                                                        skb->nf_bridge->data,
+                                                        nf_bridge->neigh_header,
                                                         ETH_HLEN-ETH_ALEN);
                        /* tell br_dev_xmit to continue with forwarding */
                        nf_bridge->mask |= BRNF_BRIDGED_DNAT;
@@ -387,11 +404,11 @@ static bool dnat_took_place(const struct sk_buff *skb)
  * device, we proceed as if ip_route_input() succeeded. If it differs from the
  * logical bridge port or if ip_route_output_key() fails we drop the packet.
  */
-static int br_nf_pre_routing_finish(struct sk_buff *skb)
+static int br_nf_pre_routing_finish(struct sock *sk, struct sk_buff *skb)
 {
        struct net_device *dev = skb->dev;
        struct iphdr *iph = ip_hdr(skb);
-       struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+       struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
        struct rtable *rt;
        int err;
        int frag_max_size;
@@ -399,9 +416,9 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb)
        frag_max_size = IPCB(skb)->frag_max_size;
        BR_INPUT_SKB_CB(skb)->frag_max_size = frag_max_size;
 
-       if (nf_bridge->mask & BRNF_PKT_TYPE) {
+       if (nf_bridge->pkt_otherhost) {
                skb->pkt_type = PACKET_OTHERHOST;
-               nf_bridge->mask ^= BRNF_PKT_TYPE;
+               nf_bridge->pkt_otherhost = false;
        }
        nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
        if (dnat_took_place(skb)) {
@@ -440,7 +457,7 @@ bridged_dnat:
                                nf_bridge_push_encap_header(skb);
                                NF_HOOK_THRESH(NFPROTO_BRIDGE,
                                               NF_BR_PRE_ROUTING,
-                                              skb, skb->dev, NULL,
+                                              sk, skb, skb->dev, NULL,
                                               br_nf_pre_routing_finish_bridge,
                                               1);
                                return 0;
@@ -460,7 +477,8 @@ bridged_dnat:
        skb->dev = nf_bridge->physindev;
        nf_bridge_update_protocol(skb);
        nf_bridge_push_encap_header(skb);
-       NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+       NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, sk, skb,
+                      skb->dev, NULL,
                       br_handle_frame_finish, 1);
 
        return 0;
@@ -483,20 +501,21 @@ static struct net_device *brnf_get_logical_dev(struct sk_buff *skb, const struct
 /* Some common code for IPv4/IPv6 */
 static struct net_device *setup_pre_routing(struct sk_buff *skb)
 {
-       struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+       struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
 
        if (skb->pkt_type == PACKET_OTHERHOST) {
                skb->pkt_type = PACKET_HOST;
-               nf_bridge->mask |= BRNF_PKT_TYPE;
+               nf_bridge->pkt_otherhost = true;
        }
 
        nf_bridge->mask |= BRNF_NF_BRIDGE_PREROUTING;
        nf_bridge->physindev = skb->dev;
        skb->dev = brnf_get_logical_dev(skb, skb->dev);
+
        if (skb->protocol == htons(ETH_P_8021Q))
-               nf_bridge->mask |= BRNF_8021Q;
+               nf_bridge->orig_proto = BRNF_PROTO_8021Q;
        else if (skb->protocol == htons(ETH_P_PPP_SES))
-               nf_bridge->mask |= BRNF_PPPoE;
+               nf_bridge->orig_proto = BRNF_PROTO_PPPOE;
 
        /* Must drop socket now because of tproxy. */
        skb_orphan(skb);
@@ -596,7 +615,8 @@ static unsigned int br_nf_pre_routing_ipv6(const struct nf_hook_ops *ops,
                return NF_DROP;
 
        skb->protocol = htons(ETH_P_IPV6);
-       NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, skb, skb->dev, NULL,
+       NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, state->sk, skb,
+               skb->dev, NULL,
                br_nf_pre_routing_finish_ipv6);
 
        return NF_STOLEN;
@@ -651,7 +671,8 @@ static unsigned int br_nf_pre_routing(const struct nf_hook_ops *ops,
 
        skb->protocol = htons(ETH_P_IP);
 
-       NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, skb->dev, NULL,
+       NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, state->sk, skb,
+               skb->dev, NULL,
                br_nf_pre_routing_finish);
 
        return NF_STOLEN;
@@ -674,16 +695,23 @@ static unsigned int br_nf_local_in(const struct nf_hook_ops *ops,
 }
 
 /* PF_BRIDGE/FORWARD *************************************************/
-static int br_nf_forward_finish(struct sk_buff *skb)
+static int br_nf_forward_finish(struct sock *sk, struct sk_buff *skb)
 {
-       struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+       struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
        struct net_device *in;
 
        if (!IS_ARP(skb) && !IS_VLAN_ARP(skb)) {
+               int frag_max_size;
+
+               if (skb->protocol == htons(ETH_P_IP)) {
+                       frag_max_size = IPCB(skb)->frag_max_size;
+                       BR_INPUT_SKB_CB(skb)->frag_max_size = frag_max_size;
+               }
+
                in = nf_bridge->physindev;
-               if (nf_bridge->mask & BRNF_PKT_TYPE) {
+               if (nf_bridge->pkt_otherhost) {
                        skb->pkt_type = PACKET_OTHERHOST;
-                       nf_bridge->mask ^= BRNF_PKT_TYPE;
+                       nf_bridge->pkt_otherhost = false;
                }
                nf_bridge_update_protocol(skb);
        } else {
@@ -691,8 +719,8 @@ static int br_nf_forward_finish(struct sk_buff *skb)
        }
        nf_bridge_push_encap_header(skb);
 
-       NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_FORWARD, skb, in,
-                      skb->dev, br_forward_finish, 1);
+       NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_FORWARD, sk, skb,
+                      in, skb->dev, br_forward_finish, 1);
        return 0;
 }
 
@@ -718,6 +746,10 @@ static unsigned int br_nf_forward_ip(const struct nf_hook_ops *ops,
        if (!nf_bridge_unshare(skb))
                return NF_DROP;
 
+       nf_bridge = nf_bridge_info_get(skb);
+       if (!nf_bridge)
+               return NF_DROP;
+
        parent = bridge_parent(state->out);
        if (!parent)
                return NF_DROP;
@@ -731,14 +763,19 @@ static unsigned int br_nf_forward_ip(const struct nf_hook_ops *ops,
 
        nf_bridge_pull_encap_header(skb);
 
-       nf_bridge = skb->nf_bridge;
        if (skb->pkt_type == PACKET_OTHERHOST) {
                skb->pkt_type = PACKET_HOST;
-               nf_bridge->mask |= BRNF_PKT_TYPE;
+               nf_bridge->pkt_otherhost = true;
        }
 
-       if (pf == NFPROTO_IPV4 && br_parse_ip_options(skb))
-               return NF_DROP;
+       if (pf == NFPROTO_IPV4) {
+               int frag_max = BR_INPUT_SKB_CB(skb)->frag_max_size;
+
+               if (br_parse_ip_options(skb))
+                       return NF_DROP;
+
+               IPCB(skb)->frag_max_size = frag_max;
+       }
 
        nf_bridge->physoutdev = skb->dev;
        if (pf == NFPROTO_IPV4)
@@ -746,7 +783,8 @@ static unsigned int br_nf_forward_ip(const struct nf_hook_ops *ops,
        else
                skb->protocol = htons(ETH_P_IPV6);
 
-       NF_HOOK(pf, NF_INET_FORWARD, skb, brnf_get_logical_dev(skb, state->in),
+       NF_HOOK(pf, NF_INET_FORWARD, NULL, skb,
+               brnf_get_logical_dev(skb, state->in),
                parent, br_nf_forward_finish);
 
        return NF_STOLEN;
@@ -780,69 +818,74 @@ static unsigned int br_nf_forward_arp(const struct nf_hook_ops *ops,
                return NF_ACCEPT;
        }
        *d = state->in;
-       NF_HOOK(NFPROTO_ARP, NF_ARP_FORWARD, skb, state->in,
-               state->out, br_nf_forward_finish);
+       NF_HOOK(NFPROTO_ARP, NF_ARP_FORWARD, state->sk, skb,
+               state->in, state->out, br_nf_forward_finish);
 
        return NF_STOLEN;
 }
 
 #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4)
-static bool nf_bridge_copy_header(struct sk_buff *skb)
+static int br_nf_push_frag_xmit(struct sock *sk, struct sk_buff *skb)
 {
+       struct brnf_frag_data *data;
        int err;
-       unsigned int header_size;
-
-       nf_bridge_update_protocol(skb);
-       header_size = ETH_HLEN + nf_bridge_encap_header_len(skb);
-       err = skb_cow_head(skb, header_size);
-       if (err)
-               return false;
 
-       skb_copy_to_linear_data_offset(skb, -header_size,
-                                      skb->nf_bridge->data, header_size);
-       __skb_push(skb, nf_bridge_encap_header_len(skb));
-       return true;
-}
+       data = this_cpu_ptr(&brnf_frag_data_storage);
+       err = skb_cow_head(skb, data->size);
 
-static int br_nf_push_frag_xmit(struct sk_buff *skb)
-{
-       if (!nf_bridge_copy_header(skb)) {
+       if (err) {
                kfree_skb(skb);
                return 0;
        }
 
-       return br_dev_queue_push_xmit(skb);
+       skb_copy_to_linear_data_offset(skb, -data->size, data->mac, data->size);
+       __skb_push(skb, data->encap_size);
+
+       return br_dev_queue_push_xmit(sk, skb);
 }
 
-static int br_nf_dev_queue_xmit(struct sk_buff *skb)
+static int br_nf_dev_queue_xmit(struct sock *sk, struct sk_buff *skb)
 {
        int ret;
        int frag_max_size;
        unsigned int mtu_reserved;
 
        if (skb_is_gso(skb) || skb->protocol != htons(ETH_P_IP))
-               return br_dev_queue_push_xmit(skb);
+               return br_dev_queue_push_xmit(sk, skb);
 
        mtu_reserved = nf_bridge_mtu_reduction(skb);
        /* This is wrong! We should preserve the original fragment
         * boundaries by preserving frag_list rather than refragmenting.
         */
        if (skb->len + mtu_reserved > skb->dev->mtu) {
+               struct brnf_frag_data *data;
+
                frag_max_size = BR_INPUT_SKB_CB(skb)->frag_max_size;
                if (br_parse_ip_options(skb))
                        /* Drop invalid packet */
                        return NF_DROP;
                IPCB(skb)->frag_max_size = frag_max_size;
-               ret = ip_fragment(skb, br_nf_push_frag_xmit);
-       } else
-               ret = br_dev_queue_push_xmit(skb);
+
+               nf_bridge_update_protocol(skb);
+
+               data = this_cpu_ptr(&brnf_frag_data_storage);
+               data->encap_size = nf_bridge_encap_header_len(skb);
+               data->size = ETH_HLEN + data->encap_size;
+
+               skb_copy_from_linear_data_offset(skb, -data->size, data->mac,
+                                                data->size);
+
+               ret = ip_fragment(sk, skb, br_nf_push_frag_xmit);
+       } else {
+               ret = br_dev_queue_push_xmit(sk, skb);
+       }
 
        return ret;
 }
 #else
-static int br_nf_dev_queue_xmit(struct sk_buff *skb)
+static int br_nf_dev_queue_xmit(struct sock *sk, struct sk_buff *skb)
 {
-        return br_dev_queue_push_xmit(skb);
+        return br_dev_queue_push_xmit(sk, skb);
 }
 #endif
 
@@ -851,7 +894,7 @@ static unsigned int br_nf_post_routing(const struct nf_hook_ops *ops,
                                       struct sk_buff *skb,
                                       const struct nf_hook_state *state)
 {
-       struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+       struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
        struct net_device *realoutdev = bridge_parent(skb->dev);
        u_int8_t pf;
 
@@ -877,17 +920,17 @@ static unsigned int br_nf_post_routing(const struct nf_hook_ops *ops,
         * about the value of skb->pkt_type. */
        if (skb->pkt_type == PACKET_OTHERHOST) {
                skb->pkt_type = PACKET_HOST;
-               nf_bridge->mask |= BRNF_PKT_TYPE;
+               nf_bridge->pkt_otherhost = true;
        }
 
        nf_bridge_pull_encap_header(skb);
-       nf_bridge_save_header(skb);
        if (pf == NFPROTO_IPV4)
                skb->protocol = htons(ETH_P_IP);
        else
                skb->protocol = htons(ETH_P_IPV6);
 
-       NF_HOOK(pf, NF_INET_POST_ROUTING, skb, NULL, realoutdev,
+       NF_HOOK(pf, NF_INET_POST_ROUTING, state->sk, skb,
+               NULL, realoutdev,
                br_nf_dev_queue_xmit);
 
        return NF_STOLEN;
@@ -919,15 +962,18 @@ static unsigned int ip_sabotage_in(const struct nf_hook_ops *ops,
  */
 static void br_nf_pre_routing_finish_bridge_slow(struct sk_buff *skb)
 {
-       struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+       struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
 
        skb_pull(skb, ETH_HLEN);
        nf_bridge->mask &= ~BRNF_BRIDGED_DNAT;
 
-       skb_copy_to_linear_data_offset(skb, -(ETH_HLEN-ETH_ALEN),
-                                      skb->nf_bridge->data, ETH_HLEN-ETH_ALEN);
+       BUILD_BUG_ON(sizeof(nf_bridge->neigh_header) != (ETH_HLEN - ETH_ALEN));
+
+       skb_copy_to_linear_data_offset(skb, -(ETH_HLEN - ETH_ALEN),
+                                      nf_bridge->neigh_header,
+                                      ETH_HLEN - ETH_ALEN);
        skb->dev = nf_bridge->physindev;
-       br_handle_frame_finish(skb);
+       br_handle_frame_finish(NULL, skb);
 }
 
 static int br_nf_dev_xmit(struct sk_buff *skb)
index b46fa0c5b8ece865017e23b29e18047f239edbf5..6ca0251cb478bf3147501b325a381a4081dd5149 100644 (file)
@@ -410,10 +410,10 @@ int br_fdb_external_learn_del(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);
-int br_dev_queue_push_xmit(struct sk_buff *skb);
+int br_dev_queue_push_xmit(struct sock *sk, struct sk_buff *skb);
 void br_forward(const struct net_bridge_port *to,
                struct sk_buff *skb, struct sk_buff *skb0);
-int br_forward_finish(struct sk_buff *skb);
+int br_forward_finish(struct sock *sk, struct sk_buff *skb);
 void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, bool unicast);
 void br_flood_forward(struct net_bridge *br, struct sk_buff *skb,
                      struct sk_buff *skb2, bool unicast);
@@ -431,7 +431,7 @@ 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);
+int br_handle_frame_finish(struct sock *sk, struct sk_buff *skb);
 rx_handler_result_t br_handle_frame(struct sk_buff **pskb);
 
 static inline bool br_rx_handler_check_rcu(const struct net_device *dev)
index bdb459d21ad8e2d5191023c5b096de39b6a78c90..534fc4cd263ef21dd517402efde9585e6f720f83 100644 (file)
@@ -54,8 +54,9 @@ static void br_send_bpdu(struct net_bridge_port *p,
 
        skb_reset_mac_header(skb);
 
-       NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
-               dev_queue_xmit);
+       NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, NULL, skb,
+               NULL, skb->dev,
+               dev_queue_xmit_sk);
 }
 
 static inline void br_set_ticks(unsigned char *dest, int j)
index 54a2fdf0f4574a4db6ba23193a511ffcf09293da..ae8141f409d9dc24d60d603133cc463e2564f895 100644 (file)
@@ -371,6 +371,8 @@ static int nft_reject_bridge_dump(struct sk_buff *skb,
                if (nla_put_u8(skb, NFTA_REJECT_ICMP_CODE, priv->icmp_code))
                        goto nla_put_failure;
                break;
+       default:
+               break;
        }
 
        return 0;
index 3b3965288f52764daf082dede116a851bff4fc9e..af4a1b0adc104f626c65507b19bd99c6dc3dd0eb 100644 (file)
@@ -1630,6 +1630,22 @@ int call_netdevice_notifiers(unsigned long val, struct net_device *dev)
 }
 EXPORT_SYMBOL(call_netdevice_notifiers);
 
+#ifdef CONFIG_NET_CLS_ACT
+static struct static_key ingress_needed __read_mostly;
+
+void net_inc_ingress_queue(void)
+{
+       static_key_slow_inc(&ingress_needed);
+}
+EXPORT_SYMBOL_GPL(net_inc_ingress_queue);
+
+void net_dec_ingress_queue(void)
+{
+       static_key_slow_dec(&ingress_needed);
+}
+EXPORT_SYMBOL_GPL(net_dec_ingress_queue);
+#endif
+
 static struct static_key netstamp_needed __read_mostly;
 #ifdef HAVE_JUMP_LABEL
 /* We are not allowed to call static_key_slow_dec() from irq context
@@ -2879,7 +2895,7 @@ EXPORT_SYMBOL(xmit_recursion);
  *     dev_loopback_xmit - loop back @skb
  *     @skb: buffer to transmit
  */
-int dev_loopback_xmit(struct sk_buff *skb)
+int dev_loopback_xmit(struct sock *sk, struct sk_buff *skb)
 {
        skb_reset_mac_header(skb);
        __skb_pull(skb, skb_network_offset(skb));
@@ -3017,11 +3033,11 @@ out:
        return rc;
 }
 
-int dev_queue_xmit(struct sk_buff *skb)
+int dev_queue_xmit_sk(struct sock *sk, struct sk_buff *skb)
 {
        return __dev_queue_xmit(skb, NULL);
 }
-EXPORT_SYMBOL(dev_queue_xmit);
+EXPORT_SYMBOL(dev_queue_xmit_sk);
 
 int dev_queue_xmit_accel(struct sk_buff *skb, void *accel_priv)
 {
@@ -3547,7 +3563,7 @@ static inline struct sk_buff *handle_ing(struct sk_buff *skb,
        struct netdev_queue *rxq = rcu_dereference(skb->dev->ingress_queue);
 
        if (!rxq || rcu_access_pointer(rxq->qdisc) == &noop_qdisc)
-               goto out;
+               return skb;
 
        if (*pt_prev) {
                *ret = deliver_skb(skb, *pt_prev, orig_dev);
@@ -3561,8 +3577,6 @@ static inline struct sk_buff *handle_ing(struct sk_buff *skb,
                return NULL;
        }
 
-out:
-       skb->tc_verd = 0;
        return skb;
 }
 #endif
@@ -3698,12 +3712,15 @@ another_round:
 
 skip_taps:
 #ifdef CONFIG_NET_CLS_ACT
-       skb = handle_ing(skb, &pt_prev, &ret, orig_dev);
-       if (!skb)
-               goto unlock;
+       if (static_key_false(&ingress_needed)) {
+               skb = handle_ing(skb, &pt_prev, &ret, orig_dev);
+               if (!skb)
+                       goto unlock;
+       }
+
+       skb->tc_verd = 0;
 ncls:
 #endif
-
        if (pfmemalloc && !skb_pfmemalloc_protocol(skb))
                goto drop;
 
@@ -3853,13 +3870,13 @@ static int netif_receive_skb_internal(struct sk_buff *skb)
  *     NET_RX_SUCCESS: no congestion
  *     NET_RX_DROP: packet was dropped
  */
-int netif_receive_skb(struct sk_buff *skb)
+int netif_receive_skb_sk(struct sock *sk, struct sk_buff *skb)
 {
        trace_netif_receive_skb_entry(skb);
 
        return netif_receive_skb_internal(skb);
 }
-EXPORT_SYMBOL(netif_receive_skb);
+EXPORT_SYMBOL(netif_receive_skb_sk);
 
 /* Network device is going away, flush any packets still pending
  * Called with irqs disabled.
index e7345d9031df18f1b52d87a8f2b501d0e76e1e4a..a3abb719221f690e2648a7a91126aef48f60219b 100644 (file)
@@ -148,9 +148,11 @@ static void ops_free_list(const struct pernet_operations *ops,
        }
 }
 
+static void rtnl_net_notifyid(struct net *net, struct net *peer, int cmd,
+                             int id);
 static int alloc_netid(struct net *net, struct net *peer, int reqid)
 {
-       int min = 0, max = 0;
+       int min = 0, max = 0, id;
 
        ASSERT_RTNL();
 
@@ -159,7 +161,11 @@ static int alloc_netid(struct net *net, struct net *peer, int reqid)
                max = reqid + 1;
        }
 
-       return idr_alloc(&net->netns_ids, peer, min, max, GFP_KERNEL);
+       id = idr_alloc(&net->netns_ids, peer, min, max, GFP_KERNEL);
+       if (id >= 0)
+               rtnl_net_notifyid(net, peer, RTM_NEWNSID, id);
+
+       return id;
 }
 
 /* This function is used by idr_for_each(). If net is equal to peer, the
@@ -359,8 +365,10 @@ static void cleanup_net(struct work_struct *work)
                for_each_net(tmp) {
                        int id = __peernet2id(tmp, net, false);
 
-                       if (id >= 0)
+                       if (id >= 0) {
+                               rtnl_net_notifyid(tmp, net, RTM_DELNSID, id);
                                idr_remove(&tmp->netns_ids, id);
+                       }
                }
                idr_destroy(&net->netns_ids);
 
@@ -531,7 +539,8 @@ static int rtnl_net_get_size(void)
 }
 
 static int rtnl_net_fill(struct sk_buff *skb, u32 portid, u32 seq, int flags,
-                        int cmd, struct net *net, struct net *peer)
+                        int cmd, struct net *net, struct net *peer,
+                        int nsid)
 {
        struct nlmsghdr *nlh;
        struct rtgenmsg *rth;
@@ -546,9 +555,13 @@ static int rtnl_net_fill(struct sk_buff *skb, u32 portid, u32 seq, int flags,
        rth = nlmsg_data(nlh);
        rth->rtgen_family = AF_UNSPEC;
 
-       id = __peernet2id(net, peer, false);
-       if  (id < 0)
-               id = NETNSA_NSID_NOT_ASSIGNED;
+       if (nsid >= 0) {
+               id = nsid;
+       } else {
+               id = __peernet2id(net, peer, false);
+               if  (id < 0)
+                       id = NETNSA_NSID_NOT_ASSIGNED;
+       }
        if (nla_put_s32(skb, NETNSA_NSID, id))
                goto nla_put_failure;
 
@@ -565,8 +578,8 @@ static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh)
        struct net *net = sock_net(skb->sk);
        struct nlattr *tb[NETNSA_MAX + 1];
        struct sk_buff *msg;
-       int err = -ENOBUFS;
        struct net *peer;
+       int err;
 
        err = nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX,
                          rtnl_net_policy);
@@ -589,7 +602,7 @@ static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh)
        }
 
        err = rtnl_net_fill(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq, 0,
-                           RTM_GETNSID, net, peer);
+                           RTM_GETNSID, net, peer, -1);
        if (err < 0)
                goto err_out;
 
@@ -603,6 +616,75 @@ out:
        return err;
 }
 
+struct rtnl_net_dump_cb {
+       struct net *net;
+       struct sk_buff *skb;
+       struct netlink_callback *cb;
+       int idx;
+       int s_idx;
+};
+
+static int rtnl_net_dumpid_one(int id, void *peer, void *data)
+{
+       struct rtnl_net_dump_cb *net_cb = (struct rtnl_net_dump_cb *)data;
+       int ret;
+
+       if (net_cb->idx < net_cb->s_idx)
+               goto cont;
+
+       ret = rtnl_net_fill(net_cb->skb, NETLINK_CB(net_cb->cb->skb).portid,
+                           net_cb->cb->nlh->nlmsg_seq, NLM_F_MULTI,
+                           RTM_NEWNSID, net_cb->net, peer, id);
+       if (ret < 0)
+               return ret;
+
+cont:
+       net_cb->idx++;
+       return 0;
+}
+
+static int rtnl_net_dumpid(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       struct net *net = sock_net(skb->sk);
+       struct rtnl_net_dump_cb net_cb = {
+               .net = net,
+               .skb = skb,
+               .cb = cb,
+               .idx = 0,
+               .s_idx = cb->args[0],
+       };
+
+       ASSERT_RTNL();
+
+       idr_for_each(&net->netns_ids, rtnl_net_dumpid_one, &net_cb);
+
+       cb->args[0] = net_cb.idx;
+       return skb->len;
+}
+
+static void rtnl_net_notifyid(struct net *net, struct net *peer, int cmd,
+                             int id)
+{
+       struct sk_buff *msg;
+       int err = -ENOMEM;
+
+       msg = nlmsg_new(rtnl_net_get_size(), GFP_KERNEL);
+       if (!msg)
+               goto out;
+
+       err = rtnl_net_fill(msg, 0, 0, 0, cmd, net, peer, id);
+       if (err < 0)
+               goto err_out;
+
+       rtnl_notify(msg, net, 0, RTNLGRP_NSID, NULL, 0);
+       return;
+
+err_out:
+       nlmsg_free(msg);
+out:
+       rtnl_set_sk_err(net, RTNLGRP_NSID, err);
+}
+
 static int __init net_ns_init(void)
 {
        struct net_generic *ng;
@@ -637,7 +719,8 @@ static int __init net_ns_init(void)
        register_pernet_subsys(&net_ns_ops);
 
        rtnl_register(PF_UNSPEC, RTM_NEWNSID, rtnl_net_newid, NULL, NULL);
-       rtnl_register(PF_UNSPEC, RTM_GETNSID, rtnl_net_getid, NULL, NULL);
+       rtnl_register(PF_UNSPEC, RTM_GETNSID, rtnl_net_getid, rtnl_net_dumpid,
+                     NULL);
 
        return 0;
 }
index 5e02260b087f60942a03b37d16b081c58c65badd..358d52a38533b90d8df212de6636ca8e2525730b 100644 (file)
@@ -818,7 +818,8 @@ static inline int rtnl_vfinfo_size(const struct net_device *dev,
                         nla_total_size(sizeof(struct ifla_vf_vlan)) +
                         nla_total_size(sizeof(struct ifla_vf_spoofchk)) +
                         nla_total_size(sizeof(struct ifla_vf_rate)) +
-                        nla_total_size(sizeof(struct ifla_vf_link_state)));
+                        nla_total_size(sizeof(struct ifla_vf_link_state)) +
+                        nla_total_size(sizeof(struct ifla_vf_rss_query_en)));
                return size;
        } else
                return 0;
@@ -1132,14 +1133,16 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                        struct ifla_vf_tx_rate vf_tx_rate;
                        struct ifla_vf_spoofchk vf_spoofchk;
                        struct ifla_vf_link_state vf_linkstate;
+                       struct ifla_vf_rss_query_en vf_rss_query_en;
 
                        /*
                         * Not all SR-IOV capable drivers support the
-                        * spoofcheck query.  Preset to -1 so the user
-                        * space tool can detect that the driver didn't
-                        * report anything.
+                        * spoofcheck and "RSS query enable" query.  Preset to
+                        * -1 so the user space tool can detect that the driver
+                        * didn't report anything.
                         */
                        ivi.spoofchk = -1;
+                       ivi.rss_query_en = -1;
                        memset(ivi.mac, 0, sizeof(ivi.mac));
                        /* The default value for VF link state is "auto"
                         * IFLA_VF_LINK_STATE_AUTO which equals zero
@@ -1152,7 +1155,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                                vf_rate.vf =
                                vf_tx_rate.vf =
                                vf_spoofchk.vf =
-                               vf_linkstate.vf = ivi.vf;
+                               vf_linkstate.vf =
+                               vf_rss_query_en.vf = ivi.vf;
 
                        memcpy(vf_mac.mac, ivi.mac, sizeof(ivi.mac));
                        vf_vlan.vlan = ivi.vlan;
@@ -1162,6 +1166,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                        vf_rate.max_tx_rate = ivi.max_tx_rate;
                        vf_spoofchk.setting = ivi.spoofchk;
                        vf_linkstate.link_state = ivi.linkstate;
+                       vf_rss_query_en.setting = ivi.rss_query_en;
                        vf = nla_nest_start(skb, IFLA_VF_INFO);
                        if (!vf) {
                                nla_nest_cancel(skb, vfinfo);
@@ -1176,7 +1181,10 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
                            nla_put(skb, IFLA_VF_SPOOFCHK, sizeof(vf_spoofchk),
                                    &vf_spoofchk) ||
                            nla_put(skb, IFLA_VF_LINK_STATE, sizeof(vf_linkstate),
-                                   &vf_linkstate))
+                                   &vf_linkstate) ||
+                           nla_put(skb, IFLA_VF_RSS_QUERY_EN,
+                                   sizeof(vf_rss_query_en),
+                                   &vf_rss_query_en))
                                goto nla_put_failure;
                        nla_nest_end(skb, vf);
                }
@@ -1290,6 +1298,7 @@ static const struct nla_policy ifla_vf_policy[IFLA_VF_MAX+1] = {
        [IFLA_VF_SPOOFCHK]      = { .len = sizeof(struct ifla_vf_spoofchk) },
        [IFLA_VF_RATE]          = { .len = sizeof(struct ifla_vf_rate) },
        [IFLA_VF_LINK_STATE]    = { .len = sizeof(struct ifla_vf_link_state) },
+       [IFLA_VF_RSS_QUERY_EN]  = { .len = sizeof(struct ifla_vf_rss_query_en) },
 };
 
 static const struct nla_policy ifla_port_policy[IFLA_PORT_MAX+1] = {
@@ -1500,6 +1509,17 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr *attr)
                                                                 ivl->link_state);
                        break;
                }
+               case IFLA_VF_RSS_QUERY_EN: {
+                       struct ifla_vf_rss_query_en *ivrssq_en;
+
+                       ivrssq_en = nla_data(vf);
+                       err = -EOPNOTSUPP;
+                       if (ops->ndo_set_vf_rss_query_en)
+                               err = ops->ndo_set_vf_rss_query_en(dev,
+                                                           ivrssq_en->vf,
+                                                           ivrssq_en->setting);
+                       break;
+               }
                default:
                        err = -EINVAL;
                        break;
@@ -1932,7 +1952,7 @@ int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm)
 EXPORT_SYMBOL(rtnl_configure_link);
 
 struct net_device *rtnl_create_link(struct net *net,
-       char *ifname, unsigned char name_assign_type,
+       const char *ifname, unsigned char name_assign_type,
        const struct rtnl_link_ops *ops, struct nlattr *tb[])
 {
        int err;
@@ -2404,7 +2424,7 @@ EXPORT_SYMBOL(rtmsg_ifinfo);
 
 static int nlmsg_populate_fdb_fill(struct sk_buff *skb,
                                   struct net_device *dev,
-                                  u8 *addr, u32 pid, u32 seq,
+                                  u8 *addr, u16 vid, u32 pid, u32 seq,
                                   int type, unsigned int flags,
                                   int nlflags)
 {
@@ -2426,6 +2446,9 @@ static int nlmsg_populate_fdb_fill(struct sk_buff *skb,
 
        if (nla_put(skb, NDA_LLADDR, ETH_ALEN, addr))
                goto nla_put_failure;
+       if (vid)
+               if (nla_put(skb, NDA_VLAN, sizeof(u16), &vid))
+                       goto nla_put_failure;
 
        nlmsg_end(skb, nlh);
        return 0;
@@ -2440,7 +2463,7 @@ static inline size_t rtnl_fdb_nlmsg_size(void)
        return NLMSG_ALIGN(sizeof(struct ndmsg)) + nla_total_size(ETH_ALEN);
 }
 
-static void rtnl_fdb_notify(struct net_device *dev, u8 *addr, int type)
+static void rtnl_fdb_notify(struct net_device *dev, u8 *addr, u16 vid, int type)
 {
        struct net *net = dev_net(dev);
        struct sk_buff *skb;
@@ -2450,7 +2473,8 @@ static void rtnl_fdb_notify(struct net_device *dev, u8 *addr, int type)
        if (!skb)
                goto errout;
 
-       err = nlmsg_populate_fdb_fill(skb, dev, addr, 0, 0, type, NTF_SELF, 0);
+       err = nlmsg_populate_fdb_fill(skb, dev, addr, vid,
+                                     0, 0, type, NTF_SELF, 0);
        if (err < 0) {
                kfree_skb(skb);
                goto errout;
@@ -2585,7 +2609,7 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
                                               nlh->nlmsg_flags);
 
                if (!err) {
-                       rtnl_fdb_notify(dev, addr, RTM_NEWNEIGH);
+                       rtnl_fdb_notify(dev, addr, vid, RTM_NEWNEIGH);
                        ndm->ndm_flags &= ~NTF_SELF;
                }
        }
@@ -2686,7 +2710,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh)
                        err = ndo_dflt_fdb_del(ndm, tb, dev, addr, vid);
 
                if (!err) {
-                       rtnl_fdb_notify(dev, addr, RTM_DELNEIGH);
+                       rtnl_fdb_notify(dev, addr, vid, RTM_DELNEIGH);
                        ndm->ndm_flags &= ~NTF_SELF;
                }
        }
@@ -2711,7 +2735,7 @@ static int nlmsg_populate_fdb(struct sk_buff *skb,
                if (*idx < cb->args[0])
                        goto skip;
 
-               err = nlmsg_populate_fdb_fill(skb, dev, ha->addr,
+               err = nlmsg_populate_fdb_fill(skb, dev, ha->addr, 0,
                                              portid, seq,
                                              RTM_NEWNEIGH, NTF_SELF,
                                              NLM_F_MULTI);
@@ -2754,7 +2778,6 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
 {
        struct net_device *dev;
        struct nlattr *tb[IFLA_MAX+1];
-       struct net_device *bdev = NULL;
        struct net_device *br_dev = NULL;
        const struct net_device_ops *ops = NULL;
        const struct net_device_ops *cops = NULL;
@@ -2778,7 +2801,6 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
                        return -ENODEV;
 
                ops = br_dev->netdev_ops;
-               bdev = br_dev;
        }
 
        for_each_netdev(net, dev) {
@@ -2791,7 +2813,6 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
                                cops = br_dev->netdev_ops;
                        }
 
-                       bdev = dev;
                } else {
                        if (dev != br_dev &&
                            !(dev->priv_flags & IFF_BRIDGE_PORT))
@@ -2801,7 +2822,6 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
                            !(dev->priv_flags & IFF_EBRIDGE))
                                continue;
 
-                       bdev = br_dev;
                        cops = ops;
                }
 
index cdb939b731aad72f039381d7b41183f1f6d94de5..3b6e5830256ec9e38399729cd352ee3735441198 100644 (file)
@@ -3752,7 +3752,6 @@ void skb_complete_wifi_ack(struct sk_buff *skb, bool acked)
 }
 EXPORT_SYMBOL_GPL(skb_complete_wifi_ack);
 
-
 /**
  * skb_partial_csum_set - set up and verify partial csum values for packet
  * @skb: the skb to set
index 654e38a9975948f981f35716e7b9eac8569f0f93..e891bcf325ca759c9b7498f29ec76aa946198d5e 100644 (file)
@@ -2799,8 +2799,7 @@ int proto_register(struct proto *prot, int alloc_slab)
                                kmem_cache_create(prot->twsk_prot->twsk_slab_name,
                                                  prot->twsk_prot->twsk_obj_size,
                                                  0,
-                                                 SLAB_HWCACHE_ALIGN |
-                                                       prot->slab_flags,
+                                                 prot->slab_flags,
                                                  NULL);
                        if (prot->twsk_prot->twsk_slab == NULL)
                                goto out_free_timewait_sock_slab_name;
index 332f7d6d994291c2cd8cded425c8e89965556acf..5f566663e47f3faa8025a998b2d8ae976db58860 100644 (file)
 
 struct inet_timewait_death_row dccp_death_row = {
        .sysctl_max_tw_buckets = NR_FILE * 2,
-       .period         = DCCP_TIMEWAIT_LEN / INET_TWDR_TWKILL_SLOTS,
-       .death_lock     = __SPIN_LOCK_UNLOCKED(dccp_death_row.death_lock),
        .hashinfo       = &dccp_hashinfo,
-       .tw_timer       = TIMER_INITIALIZER(inet_twdr_hangman, 0,
-                                           (unsigned long)&dccp_death_row),
-       .twkill_work    = __WORK_INITIALIZER(dccp_death_row.twkill_work,
-                                            inet_twdr_twkill_work),
-/* Short-time timewait calendar */
-
-       .twcal_hand     = -1,
-       .twcal_timer    = TIMER_INITIALIZER(inet_twdr_twcal_tick, 0,
-                                           (unsigned long)&dccp_death_row),
 };
 
 EXPORT_SYMBOL_GPL(dccp_death_row);
 
 void dccp_time_wait(struct sock *sk, int state, int timeo)
 {
-       struct inet_timewait_sock *tw = NULL;
+       struct inet_timewait_sock *tw;
 
-       if (dccp_death_row.tw_count < dccp_death_row.sysctl_max_tw_buckets)
-               tw = inet_twsk_alloc(sk, state);
+       tw = inet_twsk_alloc(sk, &dccp_death_row, state);
 
        if (tw != NULL) {
                const struct inet_connection_sock *icsk = inet_csk(sk);
@@ -71,8 +59,7 @@ void dccp_time_wait(struct sock *sk, int state, int timeo)
                if (state == DCCP_TIME_WAIT)
                        timeo = DCCP_TIMEWAIT_LEN;
 
-               inet_twsk_schedule(tw, &dccp_death_row, timeo,
-                                  DCCP_TIMEWAIT_LEN);
+               inet_twsk_schedule(tw, timeo);
                inet_twsk_put(tw);
        } else {
                /* Sorry, if we're out of memory, just CLOSE this
index be1f08cdad29135238c59c52f7c6bbc6548d39d2..4507b188fc5109c6dced018c7b2159e9b1a8c3b2 100644 (file)
@@ -194,7 +194,7 @@ static int dn_neigh_output(struct neighbour *neigh, struct sk_buff *skb)
        return err;
 }
 
-static int dn_neigh_output_packet(struct sk_buff *skb)
+static int dn_neigh_output_packet(struct sock *sk, struct sk_buff *skb)
 {
        struct dst_entry *dst = skb_dst(skb);
        struct dn_route *rt = (struct dn_route *)dst;
@@ -206,7 +206,8 @@ static int dn_neigh_output_packet(struct sk_buff *skb)
 /*
  * For talking to broadcast devices: Ethernet & PPP
  */
-static int dn_long_output(struct neighbour *neigh, struct sk_buff *skb)
+static int dn_long_output(struct neighbour *neigh, struct sock *sk,
+                         struct sk_buff *skb)
 {
        struct net_device *dev = neigh->dev;
        int headroom = dev->hard_header_len + sizeof(struct dn_long_packet) + 3;
@@ -245,14 +246,15 @@ static int dn_long_output(struct neighbour *neigh, struct sk_buff *skb)
 
        skb_reset_network_header(skb);
 
-       return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING, skb, NULL,
-                      neigh->dev, dn_neigh_output_packet);
+       return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING, sk, skb,
+                      NULL, neigh->dev, dn_neigh_output_packet);
 }
 
 /*
  * For talking to pointopoint and multidrop devices: DDCMP and X.25
  */
-static int dn_short_output(struct neighbour *neigh, struct sk_buff *skb)
+static int dn_short_output(struct neighbour *neigh, struct sock *sk,
+                          struct sk_buff *skb)
 {
        struct net_device *dev = neigh->dev;
        int headroom = dev->hard_header_len + sizeof(struct dn_short_packet) + 2;
@@ -284,8 +286,8 @@ static int dn_short_output(struct neighbour *neigh, struct sk_buff *skb)
 
        skb_reset_network_header(skb);
 
-       return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING, skb, NULL,
-                      neigh->dev, dn_neigh_output_packet);
+       return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING, sk, skb,
+                      NULL, neigh->dev, dn_neigh_output_packet);
 }
 
 /*
@@ -293,7 +295,8 @@ static int dn_short_output(struct neighbour *neigh, struct sk_buff *skb)
  * Phase 3 output is the same as short output, execpt that
  * it clears the area bits before transmission.
  */
-static int dn_phase3_output(struct neighbour *neigh, struct sk_buff *skb)
+static int dn_phase3_output(struct neighbour *neigh, struct sock *sk,
+                           struct sk_buff *skb)
 {
        struct net_device *dev = neigh->dev;
        int headroom = dev->hard_header_len + sizeof(struct dn_short_packet) + 2;
@@ -324,11 +327,11 @@ static int dn_phase3_output(struct neighbour *neigh, struct sk_buff *skb)
 
        skb_reset_network_header(skb);
 
-       return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING, skb, NULL,
-                      neigh->dev, dn_neigh_output_packet);
+       return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING, sk, skb,
+                      NULL, neigh->dev, dn_neigh_output_packet);
 }
 
-int dn_to_neigh_output(struct sk_buff *skb)
+int dn_to_neigh_output(struct sock *sk, struct sk_buff *skb)
 {
        struct dst_entry *dst = skb_dst(skb);
        struct dn_route *rt = (struct dn_route *) dst;
@@ -347,11 +350,11 @@ int dn_to_neigh_output(struct sk_buff *skb)
        rcu_read_unlock();
 
        if (dn->flags & DN_NDFLAG_P3)
-               return dn_phase3_output(neigh, skb);
+               return dn_phase3_output(neigh, sk, skb);
        if (use_long)
-               return dn_long_output(neigh, skb);
+               return dn_long_output(neigh, sk, skb);
        else
-               return dn_short_output(neigh, skb);
+               return dn_short_output(neigh, sk, skb);
 }
 
 /*
@@ -372,7 +375,7 @@ void dn_neigh_pointopoint_hello(struct sk_buff *skb)
 /*
  * Ethernet router hello message received
  */
-int dn_neigh_router_hello(struct sk_buff *skb)
+int dn_neigh_router_hello(struct sock *sk, struct sk_buff *skb)
 {
        struct rtnode_hello_message *msg = (struct rtnode_hello_message *)skb->data;
 
@@ -434,7 +437,7 @@ int dn_neigh_router_hello(struct sk_buff *skb)
 /*
  * Endnode hello message received
  */
-int dn_neigh_endnode_hello(struct sk_buff *skb)
+int dn_neigh_endnode_hello(struct sock *sk, struct sk_buff *skb)
 {
        struct endnode_hello_message *msg = (struct endnode_hello_message *)skb->data;
        struct neighbour *neigh;
index fe5f01485d3370ba4d645e4e640f073a1e26e41c..a321eac9fd0c5755f2d902b468137b413b092d46 100644 (file)
@@ -714,7 +714,7 @@ out:
        return ret;
 }
 
-static int dn_nsp_rx_packet(struct sk_buff *skb)
+static int dn_nsp_rx_packet(struct sock *sk2, struct sk_buff *skb)
 {
        struct dn_skb_cb *cb = DN_SKB_CB(skb);
        struct sock *sk = NULL;
@@ -814,7 +814,8 @@ free_out:
 
 int dn_nsp_rx(struct sk_buff *skb)
 {
-       return NF_HOOK(NFPROTO_DECNET, NF_DN_LOCAL_IN, skb, skb->dev, NULL,
+       return NF_HOOK(NFPROTO_DECNET, NF_DN_LOCAL_IN, NULL, skb,
+                      skb->dev, NULL,
                       dn_nsp_rx_packet);
 }
 
index 9ab0c4ba297f546ab4dd68b63894c14091b9ab3c..03227ffd19ce02c1a506ebd606813edb853a3e75 100644 (file)
@@ -512,7 +512,7 @@ static int dn_return_long(struct sk_buff *skb)
  *
  * Returns: result of input function if route is found, error code otherwise
  */
-static int dn_route_rx_packet(struct sk_buff *skb)
+static int dn_route_rx_packet(struct sock *sk, struct sk_buff *skb)
 {
        struct dn_skb_cb *cb;
        int err;
@@ -573,7 +573,8 @@ static int dn_route_rx_long(struct sk_buff *skb)
        ptr++;
        cb->hops = *ptr++; /* Visit Count */
 
-       return NF_HOOK(NFPROTO_DECNET, NF_DN_PRE_ROUTING, skb, skb->dev, NULL,
+       return NF_HOOK(NFPROTO_DECNET, NF_DN_PRE_ROUTING, NULL, skb,
+                      skb->dev, NULL,
                       dn_route_rx_packet);
 
 drop_it:
@@ -600,7 +601,8 @@ static int dn_route_rx_short(struct sk_buff *skb)
        ptr += 2;
        cb->hops = *ptr & 0x3f;
 
-       return NF_HOOK(NFPROTO_DECNET, NF_DN_PRE_ROUTING, skb, skb->dev, NULL,
+       return NF_HOOK(NFPROTO_DECNET, NF_DN_PRE_ROUTING, NULL, skb,
+                      skb->dev, NULL,
                       dn_route_rx_packet);
 
 drop_it:
@@ -608,7 +610,7 @@ drop_it:
        return NET_RX_DROP;
 }
 
-static int dn_route_discard(struct sk_buff *skb)
+static int dn_route_discard(struct sock *sk, struct sk_buff *skb)
 {
        /*
         * I know we drop the packet here, but thats considered success in
@@ -618,7 +620,7 @@ static int dn_route_discard(struct sk_buff *skb)
        return NET_RX_SUCCESS;
 }
 
-static int dn_route_ptp_hello(struct sk_buff *skb)
+static int dn_route_ptp_hello(struct sock *sk, struct sk_buff *skb)
 {
        dn_dev_hello(skb);
        dn_neigh_pointopoint_hello(skb);
@@ -704,22 +706,22 @@ int dn_route_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type
                switch (flags & DN_RT_CNTL_MSK) {
                case DN_RT_PKT_HELO:
                        return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO,
-                                      skb, skb->dev, NULL,
+                                      NULL, skb, skb->dev, NULL,
                                       dn_route_ptp_hello);
 
                case DN_RT_PKT_L1RT:
                case DN_RT_PKT_L2RT:
                        return NF_HOOK(NFPROTO_DECNET, NF_DN_ROUTE,
-                                      skb, skb->dev, NULL,
+                                      NULL, skb, skb->dev, NULL,
                                       dn_route_discard);
                case DN_RT_PKT_ERTH:
                        return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO,
-                                      skb, skb->dev, NULL,
+                                      NULL, skb, skb->dev, NULL,
                                       dn_neigh_router_hello);
 
                case DN_RT_PKT_EEDH:
                        return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO,
-                                      skb, skb->dev, NULL,
+                                      NULL, skb, skb->dev, NULL,
                                       dn_neigh_endnode_hello);
                }
        } else {
@@ -768,7 +770,8 @@ static int dn_output(struct sock *sk, struct sk_buff *skb)
        cb->rt_flags |= DN_RT_F_IE;
        cb->hops = 0;
 
-       return NF_HOOK(NFPROTO_DECNET, NF_DN_LOCAL_OUT, skb, NULL, dev,
+       return NF_HOOK(NFPROTO_DECNET, NF_DN_LOCAL_OUT, sk, skb,
+                      NULL, dev,
                       dn_to_neigh_output);
 
 error:
@@ -816,7 +819,8 @@ static int dn_forward(struct sk_buff *skb)
        if (rt->rt_flags & RTCF_DOREDIRECT)
                cb->rt_flags |= DN_RT_F_IE;
 
-       return NF_HOOK(NFPROTO_DECNET, NF_DN_FORWARD, skb, dev, skb->dev,
+       return NF_HOOK(NFPROTO_DECNET, NF_DN_FORWARD, NULL, skb,
+                      dev, skb->dev,
                       dn_to_neigh_output);
 
 drop:
index c6e67aa46c32aa78eb7fe9172d90628112e61739..933a92820d265e07b8c42300c7be6742565723b5 100644 (file)
@@ -591,7 +591,8 @@ EXPORT_SYMBOL(arp_create);
 void arp_xmit(struct sk_buff *skb)
 {
        /* Send it off, maybe filter it using firewalling first.  */
-       NF_HOOK(NFPROTO_ARP, NF_ARP_OUT, skb, NULL, skb->dev, dev_queue_xmit);
+       NF_HOOK(NFPROTO_ARP, NF_ARP_OUT, NULL, skb,
+               NULL, skb->dev, dev_queue_xmit_sk);
 }
 EXPORT_SYMBOL(arp_xmit);
 
@@ -625,7 +626,7 @@ EXPORT_SYMBOL(arp_send);
  *     Process an arp request.
  */
 
-static int arp_process(struct sk_buff *skb)
+static int arp_process(struct sock *sk, struct sk_buff *skb)
 {
        struct net_device *dev = skb->dev;
        struct in_device *in_dev = __in_dev_get_rcu(dev);
@@ -846,7 +847,7 @@ out:
 
 static void parp_redo(struct sk_buff *skb)
 {
-       arp_process(skb);
+       arp_process(NULL, skb);
 }
 
 
@@ -879,7 +880,8 @@ static int arp_rcv(struct sk_buff *skb, struct net_device *dev,
 
        memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
 
-       return NF_HOOK(NFPROTO_ARP, NF_ARP_IN, skb, dev, NULL, arp_process);
+       return NF_HOOK(NFPROTO_ARP, NF_ARP_IN, NULL, skb,
+                      dev, NULL, arp_process);
 
 consumeskb:
        consume_skb(skb);
index ff069f6597ace6302b46add285c787942645aac2..263710259774151e40fa67ba3aa9652d4a1e2955 100644 (file)
 #include <uapi/linux/fou.h>
 #include <uapi/linux/genetlink.h>
 
-static DEFINE_SPINLOCK(fou_lock);
-static LIST_HEAD(fou_list);
-
 struct fou {
        struct socket *sock;
        u8 protocol;
        u8 flags;
-       u16 port;
+       __be16 port;
+       u16 type;
        struct udp_offload udp_offloads;
        struct list_head list;
 };
@@ -37,6 +35,13 @@ struct fou_cfg {
        struct udp_port_cfg udp_config;
 };
 
+static unsigned int fou_net_id;
+
+struct fou_net {
+       struct list_head fou_list;
+       struct mutex fou_lock;
+};
+
 static inline struct fou *fou_from_sock(struct sock *sk)
 {
        return sk->sk_user_data;
@@ -387,20 +392,21 @@ out_unlock:
        return err;
 }
 
-static int fou_add_to_port_list(struct fou *fou)
+static int fou_add_to_port_list(struct net *net, struct fou *fou)
 {
+       struct fou_net *fn = net_generic(net, fou_net_id);
        struct fou *fout;
 
-       spin_lock(&fou_lock);
-       list_for_each_entry(fout, &fou_list, list) {
+       mutex_lock(&fn->fou_lock);
+       list_for_each_entry(fout, &fn->fou_list, list) {
                if (fou->port == fout->port) {
-                       spin_unlock(&fou_lock);
+                       mutex_unlock(&fn->fou_lock);
                        return -EALREADY;
                }
        }
 
-       list_add(&fou->list, &fou_list);
-       spin_unlock(&fou_lock);
+       list_add(&fou->list, &fn->fou_list);
+       mutex_unlock(&fn->fou_lock);
 
        return 0;
 }
@@ -410,14 +416,10 @@ static void fou_release(struct fou *fou)
        struct socket *sock = fou->sock;
        struct sock *sk = sock->sk;
 
-       udp_del_offload(&fou->udp_offloads);
-
+       if (sk->sk_family == AF_INET)
+               udp_del_offload(&fou->udp_offloads);
        list_del(&fou->list);
-
-       /* Remove hooks into tunnel socket */
-       sk->sk_user_data = NULL;
-
-       sock_release(sock);
+       udp_tunnel_sock_release(sock);
 
        kfree(fou);
 }
@@ -447,10 +449,10 @@ static int gue_encap_init(struct sock *sk, struct fou *fou, struct fou_cfg *cfg)
 static int fou_create(struct net *net, struct fou_cfg *cfg,
                      struct socket **sockp)
 {
-       struct fou *fou = NULL;
-       int err;
        struct socket *sock = NULL;
+       struct fou *fou = NULL;
        struct sock *sk;
+       int err;
 
        /* Open UDP socket */
        err = udp_sock_create(net, &cfg->udp_config, &sock);
@@ -486,6 +488,8 @@ static int fou_create(struct net *net, struct fou_cfg *cfg,
                goto error;
        }
 
+       fou->type = cfg->type;
+
        udp_sk(sk)->encap_type = 1;
        udp_encap_enable();
 
@@ -502,7 +506,7 @@ static int fou_create(struct net *net, struct fou_cfg *cfg,
                        goto error;
        }
 
-       err = fou_add_to_port_list(fou);
+       err = fou_add_to_port_list(net, fou);
        if (err)
                goto error;
 
@@ -514,27 +518,27 @@ static int fou_create(struct net *net, struct fou_cfg *cfg,
 error:
        kfree(fou);
        if (sock)
-               sock_release(sock);
+               udp_tunnel_sock_release(sock);
 
        return err;
 }
 
 static int fou_destroy(struct net *net, struct fou_cfg *cfg)
 {
-       struct fou *fou;
-       u16 port = cfg->udp_config.local_udp_port;
+       struct fou_net *fn = net_generic(net, fou_net_id);
+       __be16 port = cfg->udp_config.local_udp_port;
        int err = -EINVAL;
+       struct fou *fou;
 
-       spin_lock(&fou_lock);
-       list_for_each_entry(fou, &fou_list, list) {
+       mutex_lock(&fn->fou_lock);
+       list_for_each_entry(fou, &fn->fou_list, list) {
                if (fou->port == port) {
-                       udp_del_offload(&fou->udp_offloads);
                        fou_release(fou);
                        err = 0;
                        break;
                }
        }
-       spin_unlock(&fou_lock);
+       mutex_unlock(&fn->fou_lock);
 
        return err;
 }
@@ -573,7 +577,7 @@ static int parse_nl_config(struct genl_info *info,
        }
 
        if (info->attrs[FOU_ATTR_PORT]) {
-               u16 port = nla_get_u16(info->attrs[FOU_ATTR_PORT]);
+               __be16 port = nla_get_be16(info->attrs[FOU_ATTR_PORT]);
 
                cfg->udp_config.local_udp_port = port;
        }
@@ -592,6 +596,7 @@ static int parse_nl_config(struct genl_info *info,
 
 static int fou_nl_cmd_add_port(struct sk_buff *skb, struct genl_info *info)
 {
+       struct net *net = genl_info_net(info);
        struct fou_cfg cfg;
        int err;
 
@@ -599,16 +604,120 @@ static int fou_nl_cmd_add_port(struct sk_buff *skb, struct genl_info *info)
        if (err)
                return err;
 
-       return fou_create(&init_net, &cfg, NULL);
+       return fou_create(net, &cfg, NULL);
 }
 
 static int fou_nl_cmd_rm_port(struct sk_buff *skb, struct genl_info *info)
 {
+       struct net *net = genl_info_net(info);
+       struct fou_cfg cfg;
+       int err;
+
+       err = parse_nl_config(info, &cfg);
+       if (err)
+               return err;
+
+       return fou_destroy(net, &cfg);
+}
+
+static int fou_fill_info(struct fou *fou, struct sk_buff *msg)
+{
+       if (nla_put_u8(msg, FOU_ATTR_AF, fou->sock->sk->sk_family) ||
+           nla_put_be16(msg, FOU_ATTR_PORT, fou->port) ||
+           nla_put_u8(msg, FOU_ATTR_IPPROTO, fou->protocol) ||
+           nla_put_u8(msg, FOU_ATTR_TYPE, fou->type))
+               return -1;
+
+       if (fou->flags & FOU_F_REMCSUM_NOPARTIAL)
+               if (nla_put_flag(msg, FOU_ATTR_REMCSUM_NOPARTIAL))
+                       return -1;
+       return 0;
+}
+
+static int fou_dump_info(struct fou *fou, u32 portid, u32 seq,
+                        u32 flags, struct sk_buff *skb, u8 cmd)
+{
+       void *hdr;
+
+       hdr = genlmsg_put(skb, portid, seq, &fou_nl_family, flags, cmd);
+       if (!hdr)
+               return -ENOMEM;
+
+       if (fou_fill_info(fou, skb) < 0)
+               goto nla_put_failure;
+
+       genlmsg_end(skb, hdr);
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(skb, hdr);
+       return -EMSGSIZE;
+}
+
+static int fou_nl_cmd_get_port(struct sk_buff *skb, struct genl_info *info)
+{
+       struct net *net = genl_info_net(info);
+       struct fou_net *fn = net_generic(net, fou_net_id);
+       struct sk_buff *msg;
        struct fou_cfg cfg;
+       struct fou *fout;
+       __be16 port;
+       int ret;
+
+       ret = parse_nl_config(info, &cfg);
+       if (ret)
+               return ret;
+       port = cfg.udp_config.local_udp_port;
+       if (port == 0)
+               return -EINVAL;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       ret = -ESRCH;
+       mutex_lock(&fn->fou_lock);
+       list_for_each_entry(fout, &fn->fou_list, list) {
+               if (port == fout->port) {
+                       ret = fou_dump_info(fout, info->snd_portid,
+                                           info->snd_seq, 0, msg,
+                                           info->genlhdr->cmd);
+                       break;
+               }
+       }
+       mutex_unlock(&fn->fou_lock);
+       if (ret < 0)
+               goto out_free;
 
-       parse_nl_config(info, &cfg);
+       return genlmsg_reply(msg, info);
 
-       return fou_destroy(&init_net, &cfg);
+out_free:
+       nlmsg_free(msg);
+       return ret;
+}
+
+static int fou_nl_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       struct net *net = sock_net(skb->sk);
+       struct fou_net *fn = net_generic(net, fou_net_id);
+       struct fou *fout;
+       int idx = 0, ret;
+
+       mutex_lock(&fn->fou_lock);
+       list_for_each_entry(fout, &fn->fou_list, list) {
+               if (idx++ < cb->args[0])
+                       continue;
+               ret = fou_dump_info(fout, NETLINK_CB(cb->skb).portid,
+                                   cb->nlh->nlmsg_seq, NLM_F_MULTI,
+                                   skb, FOU_CMD_GET);
+               if (ret)
+                       goto done;
+       }
+       mutex_unlock(&fn->fou_lock);
+
+done:
+       cb->args[0] = idx;
+       return skb->len;
 }
 
 static const struct genl_ops fou_nl_ops[] = {
@@ -624,6 +733,12 @@ static const struct genl_ops fou_nl_ops[] = {
                .policy = fou_nl_policy,
                .flags = GENL_ADMIN_PERM,
        },
+       {
+               .cmd = FOU_CMD_GET,
+               .doit = fou_nl_cmd_get_port,
+               .dumpit = fou_nl_dump,
+               .policy = fou_nl_policy,
+       },
 };
 
 size_t fou_encap_hlen(struct ip_tunnel_encap *e)
@@ -820,38 +935,63 @@ static void ip_tunnel_encap_del_fou_ops(void)
 
 #endif
 
+static __net_init int fou_init_net(struct net *net)
+{
+       struct fou_net *fn = net_generic(net, fou_net_id);
+
+       INIT_LIST_HEAD(&fn->fou_list);
+       mutex_init(&fn->fou_lock);
+       return 0;
+}
+
+static __net_exit void fou_exit_net(struct net *net)
+{
+       struct fou_net *fn = net_generic(net, fou_net_id);
+       struct fou *fou, *next;
+
+       /* Close all the FOU sockets */
+       mutex_lock(&fn->fou_lock);
+       list_for_each_entry_safe(fou, next, &fn->fou_list, list)
+               fou_release(fou);
+       mutex_unlock(&fn->fou_lock);
+}
+
+static struct pernet_operations fou_net_ops = {
+       .init = fou_init_net,
+       .exit = fou_exit_net,
+       .id   = &fou_net_id,
+       .size = sizeof(struct fou_net),
+};
+
 static int __init fou_init(void)
 {
        int ret;
 
+       ret = register_pernet_device(&fou_net_ops);
+       if (ret)
+               goto exit;
+
        ret = genl_register_family_with_ops(&fou_nl_family,
                                            fou_nl_ops);
-
        if (ret < 0)
-               goto exit;
+               goto unregister;
 
        ret = ip_tunnel_encap_add_fou_ops();
-       if (ret < 0)
-               genl_unregister_family(&fou_nl_family);
+       if (ret == 0)
+               return 0;
 
+       genl_unregister_family(&fou_nl_family);
+unregister:
+       unregister_pernet_device(&fou_net_ops);
 exit:
        return ret;
 }
 
 static void __exit fou_fini(void)
 {
-       struct fou *fou, *next;
-
        ip_tunnel_encap_del_fou_ops();
-
        genl_unregister_family(&fou_nl_family);
-
-       /* Close all the FOU sockets */
-
-       spin_lock(&fou_lock);
-       list_for_each_entry_safe(fou, next, &fou_list, list)
-               fou_release(fou);
-       spin_unlock(&fou_lock);
+       unregister_pernet_device(&fou_net_ops);
 }
 
 module_init(fou_init);
index e64f8e9785d184cd033d23682691b397adc65f1e..b77f5e84c623f055fe277ea2178a29589fadaf1b 100644 (file)
@@ -136,7 +136,7 @@ int geneve_xmit_skb(struct geneve_sock *gs, struct rtable *rt,
 
        skb_set_inner_protocol(skb, htons(ETH_P_TEB));
 
-       return udp_tunnel_xmit_skb(rt, skb, src, dst,
+       return udp_tunnel_xmit_skb(rt, gs->sock->sk, skb, src, dst,
                                   tos, ttl, df, src_port, dst_port, xnet,
                                   !csum);
 }
index 76322c9867d5eb1ffe7808c908e42208046888a7..70e8b3c308ec3bd5c3069d87558b8b24d644ed52 100644 (file)
@@ -248,7 +248,7 @@ static int inet_twsk_diag_fill(struct sock *sk,
        struct inet_timewait_sock *tw = inet_twsk(sk);
        struct inet_diag_msg *r;
        struct nlmsghdr *nlh;
-       s32 tmo;
+       long tmo;
 
        nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r),
                        nlmsg_flags);
@@ -258,7 +258,7 @@ static int inet_twsk_diag_fill(struct sock *sk,
        r = nlmsg_data(nlh);
        BUG_ON(tw->tw_state != TCP_TIME_WAIT);
 
-       tmo = tw->tw_ttd - inet_tw_time_stamp();
+       tmo = tw->tw_timer.expires - jiffies;
        if (tmo < 0)
                tmo = 0;
 
index d4630bf2d9aad1fd9070a11323b1cd0f7c0b9949..c6fb80bd5826ea840eebd033fb87d01c595ab120 100644 (file)
@@ -388,7 +388,7 @@ static int __inet_check_established(struct inet_timewait_death_row *death_row,
                *twp = tw;
        } else if (tw) {
                /* Silly. Should hash-dance instead... */
-               inet_twsk_deschedule(tw, death_row);
+               inet_twsk_deschedule(tw);
 
                inet_twsk_put(tw);
        }
@@ -565,7 +565,7 @@ ok:
                spin_unlock(&head->lock);
 
                if (tw) {
-                       inet_twsk_deschedule(tw, death_row);
+                       inet_twsk_deschedule(tw);
                        while (twrefcnt) {
                                twrefcnt--;
                                inet_twsk_put(tw);
index 118f0f195820fa98554bafa5e1ddbd0da7c002c7..00ec8d5d7e7ee2f1c79dc7446127f13c7e23a331 100644 (file)
@@ -67,9 +67,9 @@ int inet_twsk_bind_unhash(struct inet_timewait_sock *tw,
 }
 
 /* Must be called with locally disabled BHs. */
-static void __inet_twsk_kill(struct inet_timewait_sock *tw,
-                            struct inet_hashinfo *hashinfo)
+static void inet_twsk_kill(struct inet_timewait_sock *tw)
 {
+       struct inet_hashinfo *hashinfo = tw->tw_dr->hashinfo;
        struct inet_bind_hashbucket *bhead;
        int refcnt;
        /* Unlink from established hashes. */
@@ -89,6 +89,8 @@ static void __inet_twsk_kill(struct inet_timewait_sock *tw,
 
        BUG_ON(refcnt >= atomic_read(&tw->tw_refcnt));
        atomic_sub(refcnt, &tw->tw_refcnt);
+       atomic_dec(&tw->tw_dr->tw_count);
+       inet_twsk_put(tw);
 }
 
 void inet_twsk_free(struct inet_timewait_sock *tw)
@@ -168,16 +170,34 @@ void __inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk,
 }
 EXPORT_SYMBOL_GPL(__inet_twsk_hashdance);
 
-struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk, const int state)
+void tw_timer_handler(unsigned long data)
 {
-       struct inet_timewait_sock *tw =
-               kmem_cache_alloc(sk->sk_prot_creator->twsk_prot->twsk_slab,
-                                GFP_ATOMIC);
+       struct inet_timewait_sock *tw = (struct inet_timewait_sock *)data;
+
+       if (tw->tw_kill)
+               NET_INC_STATS_BH(twsk_net(tw), LINUX_MIB_TIMEWAITKILLED);
+       else
+               NET_INC_STATS_BH(twsk_net(tw), LINUX_MIB_TIMEWAITED);
+       inet_twsk_kill(tw);
+}
+
+struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk,
+                                          struct inet_timewait_death_row *dr,
+                                          const int state)
+{
+       struct inet_timewait_sock *tw;
+
+       if (atomic_read(&dr->tw_count) >= dr->sysctl_max_tw_buckets)
+               return NULL;
+
+       tw = kmem_cache_alloc(sk->sk_prot_creator->twsk_prot->twsk_slab,
+                             GFP_ATOMIC);
        if (tw) {
                const struct inet_sock *inet = inet_sk(sk);
 
                kmemcheck_annotate_bitfield(tw, flags);
 
+               tw->tw_dr           = dr;
                /* Give us an identity. */
                tw->tw_daddr        = inet->inet_daddr;
                tw->tw_rcv_saddr    = inet->inet_rcv_saddr;
@@ -196,13 +216,14 @@ struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk, const int stat
                tw->tw_prot         = sk->sk_prot_creator;
                atomic64_set(&tw->tw_cookie, atomic64_read(&sk->sk_cookie));
                twsk_net_set(tw, sock_net(sk));
+               setup_timer(&tw->tw_timer, tw_timer_handler, (unsigned long)tw);
                /*
                 * Because we use RCU lookups, we should not set tw_refcnt
                 * to a non null value before everything is setup for this
                 * timewait socket.
                 */
                atomic_set(&tw->tw_refcnt, 0);
-               inet_twsk_dead_node_init(tw);
+
                __module_get(tw->tw_prot->owner);
        }
 
@@ -210,139 +231,20 @@ struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk, const int stat
 }
 EXPORT_SYMBOL_GPL(inet_twsk_alloc);
 
-/* Returns non-zero if quota exceeded.  */
-static int inet_twdr_do_twkill_work(struct inet_timewait_death_row *twdr,
-                                   const int slot)
-{
-       struct inet_timewait_sock *tw;
-       unsigned int killed;
-       int ret;
-
-       /* NOTE: compare this to previous version where lock
-        * was released after detaching chain. It was racy,
-        * because tw buckets are scheduled in not serialized context
-        * in 2.3 (with netfilter), and with softnet it is common, because
-        * soft irqs are not sequenced.
-        */
-       killed = 0;
-       ret = 0;
-rescan:
-       inet_twsk_for_each_inmate(tw, &twdr->cells[slot]) {
-               __inet_twsk_del_dead_node(tw);
-               spin_unlock(&twdr->death_lock);
-               __inet_twsk_kill(tw, twdr->hashinfo);
-#ifdef CONFIG_NET_NS
-               NET_INC_STATS_BH(twsk_net(tw), LINUX_MIB_TIMEWAITED);
-#endif
-               inet_twsk_put(tw);
-               killed++;
-               spin_lock(&twdr->death_lock);
-               if (killed > INET_TWDR_TWKILL_QUOTA) {
-                       ret = 1;
-                       break;
-               }
-
-               /* While we dropped twdr->death_lock, another cpu may have
-                * killed off the next TW bucket in the list, therefore
-                * do a fresh re-read of the hlist head node with the
-                * lock reacquired.  We still use the hlist traversal
-                * macro in order to get the prefetches.
-                */
-               goto rescan;
-       }
-
-       twdr->tw_count -= killed;
-#ifndef CONFIG_NET_NS
-       NET_ADD_STATS_BH(&init_net, LINUX_MIB_TIMEWAITED, killed);
-#endif
-       return ret;
-}
-
-void inet_twdr_hangman(unsigned long data)
-{
-       struct inet_timewait_death_row *twdr;
-       unsigned int need_timer;
-
-       twdr = (struct inet_timewait_death_row *)data;
-       spin_lock(&twdr->death_lock);
-
-       if (twdr->tw_count == 0)
-               goto out;
-
-       need_timer = 0;
-       if (inet_twdr_do_twkill_work(twdr, twdr->slot)) {
-               twdr->thread_slots |= (1 << twdr->slot);
-               schedule_work(&twdr->twkill_work);
-               need_timer = 1;
-       } else {
-               /* We purged the entire slot, anything left?  */
-               if (twdr->tw_count)
-                       need_timer = 1;
-               twdr->slot = ((twdr->slot + 1) & (INET_TWDR_TWKILL_SLOTS - 1));
-       }
-       if (need_timer)
-               mod_timer(&twdr->tw_timer, jiffies + twdr->period);
-out:
-       spin_unlock(&twdr->death_lock);
-}
-EXPORT_SYMBOL_GPL(inet_twdr_hangman);
-
-void inet_twdr_twkill_work(struct work_struct *work)
-{
-       struct inet_timewait_death_row *twdr =
-               container_of(work, struct inet_timewait_death_row, twkill_work);
-       int i;
-
-       BUILD_BUG_ON((INET_TWDR_TWKILL_SLOTS - 1) >
-                       (sizeof(twdr->thread_slots) * 8));
-
-       while (twdr->thread_slots) {
-               spin_lock_bh(&twdr->death_lock);
-               for (i = 0; i < INET_TWDR_TWKILL_SLOTS; i++) {
-                       if (!(twdr->thread_slots & (1 << i)))
-                               continue;
-
-                       while (inet_twdr_do_twkill_work(twdr, i) != 0) {
-                               if (need_resched()) {
-                                       spin_unlock_bh(&twdr->death_lock);
-                                       schedule();
-                                       spin_lock_bh(&twdr->death_lock);
-                               }
-                       }
-
-                       twdr->thread_slots &= ~(1 << i);
-               }
-               spin_unlock_bh(&twdr->death_lock);
-       }
-}
-EXPORT_SYMBOL_GPL(inet_twdr_twkill_work);
-
 /* These are always called from BH context.  See callers in
  * tcp_input.c to verify this.
  */
 
 /* This is for handling early-kills of TIME_WAIT sockets. */
-void inet_twsk_deschedule(struct inet_timewait_sock *tw,
-                         struct inet_timewait_death_row *twdr)
+void inet_twsk_deschedule(struct inet_timewait_sock *tw)
 {
-       spin_lock(&twdr->death_lock);
-       if (inet_twsk_del_dead_node(tw)) {
-               inet_twsk_put(tw);
-               if (--twdr->tw_count == 0)
-                       del_timer(&twdr->tw_timer);
-       }
-       spin_unlock(&twdr->death_lock);
-       __inet_twsk_kill(tw, twdr->hashinfo);
+       if (del_timer_sync(&tw->tw_timer))
+               inet_twsk_kill(tw);
 }
 EXPORT_SYMBOL(inet_twsk_deschedule);
 
-void inet_twsk_schedule(struct inet_timewait_sock *tw,
-                      struct inet_timewait_death_row *twdr,
-                      const int timeo, const int timewait_len)
+void inet_twsk_schedule(struct inet_timewait_sock *tw, const int timeo)
 {
-       struct hlist_head *list;
-       int slot;
-
        /* timeout := RTO * 3.5
         *
         * 3.5 = 1+2+0.5 to wait for two retransmits.
@@ -367,115 +269,15 @@ void inet_twsk_schedule(struct inet_timewait_sock *tw,
         * is greater than TS tick!) and detect old duplicates with help
         * of PAWS.
         */
-       slot = (timeo + (1 << INET_TWDR_RECYCLE_TICK) - 1) >> INET_TWDR_RECYCLE_TICK;
 
-       spin_lock(&twdr->death_lock);
-
-       /* Unlink it, if it was scheduled */
-       if (inet_twsk_del_dead_node(tw))
-               twdr->tw_count--;
-       else
+       tw->tw_kill = timeo <= 4*HZ;
+       if (!mod_timer_pinned(&tw->tw_timer, jiffies + timeo)) {
                atomic_inc(&tw->tw_refcnt);
-
-       if (slot >= INET_TWDR_RECYCLE_SLOTS) {
-               /* Schedule to slow timer */
-               if (timeo >= timewait_len) {
-                       slot = INET_TWDR_TWKILL_SLOTS - 1;
-               } else {
-                       slot = DIV_ROUND_UP(timeo, twdr->period);
-                       if (slot >= INET_TWDR_TWKILL_SLOTS)
-                               slot = INET_TWDR_TWKILL_SLOTS - 1;
-               }
-               tw->tw_ttd = inet_tw_time_stamp() + timeo;
-               slot = (twdr->slot + slot) & (INET_TWDR_TWKILL_SLOTS - 1);
-               list = &twdr->cells[slot];
-       } else {
-               tw->tw_ttd = inet_tw_time_stamp() + (slot << INET_TWDR_RECYCLE_TICK);
-
-               if (twdr->twcal_hand < 0) {
-                       twdr->twcal_hand = 0;
-                       twdr->twcal_jiffie = jiffies;
-                       twdr->twcal_timer.expires = twdr->twcal_jiffie +
-                                             (slot << INET_TWDR_RECYCLE_TICK);
-                       add_timer(&twdr->twcal_timer);
-               } else {
-                       if (time_after(twdr->twcal_timer.expires,
-                                      jiffies + (slot << INET_TWDR_RECYCLE_TICK)))
-                               mod_timer(&twdr->twcal_timer,
-                                         jiffies + (slot << INET_TWDR_RECYCLE_TICK));
-                       slot = (twdr->twcal_hand + slot) & (INET_TWDR_RECYCLE_SLOTS - 1);
-               }
-               list = &twdr->twcal_row[slot];
+               atomic_inc(&tw->tw_dr->tw_count);
        }
-
-       hlist_add_head(&tw->tw_death_node, list);
-
-       if (twdr->tw_count++ == 0)
-               mod_timer(&twdr->tw_timer, jiffies + twdr->period);
-       spin_unlock(&twdr->death_lock);
 }
 EXPORT_SYMBOL_GPL(inet_twsk_schedule);
 
-void inet_twdr_twcal_tick(unsigned long data)
-{
-       struct inet_timewait_death_row *twdr;
-       int n, slot;
-       unsigned long j;
-       unsigned long now = jiffies;
-       int killed = 0;
-       int adv = 0;
-
-       twdr = (struct inet_timewait_death_row *)data;
-
-       spin_lock(&twdr->death_lock);
-       if (twdr->twcal_hand < 0)
-               goto out;
-
-       slot = twdr->twcal_hand;
-       j = twdr->twcal_jiffie;
-
-       for (n = 0; n < INET_TWDR_RECYCLE_SLOTS; n++) {
-               if (time_before_eq(j, now)) {
-                       struct hlist_node *safe;
-                       struct inet_timewait_sock *tw;
-
-                       inet_twsk_for_each_inmate_safe(tw, safe,
-                                                      &twdr->twcal_row[slot]) {
-                               __inet_twsk_del_dead_node(tw);
-                               __inet_twsk_kill(tw, twdr->hashinfo);
-#ifdef CONFIG_NET_NS
-                               NET_INC_STATS_BH(twsk_net(tw), LINUX_MIB_TIMEWAITKILLED);
-#endif
-                               inet_twsk_put(tw);
-                               killed++;
-                       }
-               } else {
-                       if (!adv) {
-                               adv = 1;
-                               twdr->twcal_jiffie = j;
-                               twdr->twcal_hand = slot;
-                       }
-
-                       if (!hlist_empty(&twdr->twcal_row[slot])) {
-                               mod_timer(&twdr->twcal_timer, j);
-                               goto out;
-                       }
-               }
-               j += 1 << INET_TWDR_RECYCLE_TICK;
-               slot = (slot + 1) & (INET_TWDR_RECYCLE_SLOTS - 1);
-       }
-       twdr->twcal_hand = -1;
-
-out:
-       if ((twdr->tw_count -= killed) == 0)
-               del_timer(&twdr->tw_timer);
-#ifndef CONFIG_NET_NS
-       NET_ADD_STATS_BH(&init_net, LINUX_MIB_TIMEWAITKILLED, killed);
-#endif
-       spin_unlock(&twdr->death_lock);
-}
-EXPORT_SYMBOL_GPL(inet_twdr_twcal_tick);
-
 void inet_twsk_purge(struct inet_hashinfo *hashinfo,
                     struct inet_timewait_death_row *twdr, int family)
 {
@@ -509,7 +311,7 @@ restart:
 
                        rcu_read_unlock();
                        local_bh_disable();
-                       inet_twsk_deschedule(tw, twdr);
+                       inet_twsk_deschedule(tw);
                        local_bh_enable();
                        inet_twsk_put(tw);
                        goto restart_rcu;
index d9bc28ac5d1b97340e79aae1eefcbac3f463251a..939992c456f3bb0d1505c6d4e9af8cc5b4da1ecc 100644 (file)
@@ -57,7 +57,7 @@ static bool ip_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu)
 }
 
 
-static int ip_forward_finish(struct sk_buff *skb)
+static int ip_forward_finish(struct sock *sk, struct sk_buff *skb)
 {
        struct ip_options *opt  = &(IPCB(skb)->opt);
 
@@ -68,7 +68,7 @@ static int ip_forward_finish(struct sk_buff *skb)
                ip_forward_options(skb);
 
        skb_sender_cpu_clear(skb);
-       return dst_output(skb);
+       return dst_output_sk(sk, skb);
 }
 
 int ip_forward(struct sk_buff *skb)
@@ -136,8 +136,8 @@ int ip_forward(struct sk_buff *skb)
 
        skb->priority = rt_tos2priority(iph->tos);
 
-       return NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, skb, skb->dev,
-                      rt->dst.dev, ip_forward_finish);
+       return NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, NULL, skb,
+                      skb->dev, rt->dst.dev, ip_forward_finish);
 
 sr_failed:
        /*
index 2e0410ed8f16f0d41189a6846633a7598a04b504..2db4c8773c1b405da48758db66969060df2f0812 100644 (file)
@@ -187,7 +187,7 @@ bool ip_call_ra_chain(struct sk_buff *skb)
        return false;
 }
 
-static int ip_local_deliver_finish(struct sk_buff *skb)
+static int ip_local_deliver_finish(struct sock *sk, struct sk_buff *skb)
 {
        struct net *net = dev_net(skb->dev);
 
@@ -253,7 +253,8 @@ int ip_local_deliver(struct sk_buff *skb)
                        return 0;
        }
 
-       return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, skb, skb->dev, NULL,
+       return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, NULL, skb,
+                      skb->dev, NULL,
                       ip_local_deliver_finish);
 }
 
@@ -309,7 +310,7 @@ drop:
 int sysctl_ip_early_demux __read_mostly = 1;
 EXPORT_SYMBOL(sysctl_ip_early_demux);
 
-static int ip_rcv_finish(struct sk_buff *skb)
+static int ip_rcv_finish(struct sock *sk, struct sk_buff *skb)
 {
        const struct iphdr *iph = ip_hdr(skb);
        struct rtable *rt;
@@ -451,7 +452,8 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,
        /* Must drop socket now because of tproxy. */
        skb_orphan(skb);
 
-       return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, dev, NULL,
+       return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, NULL, skb,
+                      dev, NULL,
                       ip_rcv_finish);
 
 csum_error:
index 26f6f7956168a795e1c465a65e89939c5b7431d6..c65b93a7b7113660d9f946128c0a4acee810de0f 100644 (file)
@@ -91,14 +91,19 @@ void ip_send_check(struct iphdr *iph)
 }
 EXPORT_SYMBOL(ip_send_check);
 
-int __ip_local_out(struct sk_buff *skb)
+int __ip_local_out_sk(struct sock *sk, struct sk_buff *skb)
 {
        struct iphdr *iph = ip_hdr(skb);
 
        iph->tot_len = htons(skb->len);
        ip_send_check(iph);
-       return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, skb, NULL,
-                      skb_dst(skb)->dev, dst_output);
+       return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, sk, skb, NULL,
+                      skb_dst(skb)->dev, dst_output_sk);
+}
+
+int __ip_local_out(struct sk_buff *skb)
+{
+       return __ip_local_out_sk(skb->sk, skb);
 }
 
 int ip_local_out_sk(struct sock *sk, struct sk_buff *skb)
@@ -163,7 +168,7 @@ int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
 }
 EXPORT_SYMBOL_GPL(ip_build_and_send_pkt);
 
-static inline int ip_finish_output2(struct sk_buff *skb)
+static inline int ip_finish_output2(struct sock *sk, struct sk_buff *skb)
 {
        struct dst_entry *dst = skb_dst(skb);
        struct rtable *rt = (struct rtable *)dst;
@@ -211,7 +216,7 @@ static inline int ip_finish_output2(struct sk_buff *skb)
        return -EINVAL;
 }
 
-static int ip_finish_output_gso(struct sk_buff *skb)
+static int ip_finish_output_gso(struct sock *sk, struct sk_buff *skb)
 {
        netdev_features_t features;
        struct sk_buff *segs;
@@ -220,7 +225,7 @@ static int ip_finish_output_gso(struct sk_buff *skb)
        /* common case: locally created skb or seglen is <= mtu */
        if (((IPCB(skb)->flags & IPSKB_FORWARDED) == 0) ||
              skb_gso_network_seglen(skb) <= ip_skb_dst_mtu(skb))
-               return ip_finish_output2(skb);
+               return ip_finish_output2(sk, skb);
 
        /* Slowpath -  GSO segment length is exceeding the dst MTU.
         *
@@ -243,7 +248,7 @@ static int ip_finish_output_gso(struct sk_buff *skb)
                int err;
 
                segs->next = NULL;
-               err = ip_fragment(segs, ip_finish_output2);
+               err = ip_fragment(sk, segs, ip_finish_output2);
 
                if (err && ret == 0)
                        ret = err;
@@ -253,22 +258,22 @@ static int ip_finish_output_gso(struct sk_buff *skb)
        return ret;
 }
 
-static int ip_finish_output(struct sk_buff *skb)
+static int ip_finish_output(struct sock *sk, struct sk_buff *skb)
 {
 #if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
        /* Policy lookup after SNAT yielded a new policy */
        if (skb_dst(skb)->xfrm) {
                IPCB(skb)->flags |= IPSKB_REROUTED;
-               return dst_output(skb);
+               return dst_output_sk(sk, skb);
        }
 #endif
        if (skb_is_gso(skb))
-               return ip_finish_output_gso(skb);
+               return ip_finish_output_gso(sk, skb);
 
        if (skb->len > ip_skb_dst_mtu(skb))
-               return ip_fragment(skb, ip_finish_output2);
+               return ip_fragment(sk, skb, ip_finish_output2);
 
-       return ip_finish_output2(skb);
+       return ip_finish_output2(sk, skb);
 }
 
 int ip_mc_output(struct sock *sk, struct sk_buff *skb)
@@ -307,7 +312,7 @@ int ip_mc_output(struct sock *sk, struct sk_buff *skb)
                        struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
                        if (newskb)
                                NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING,
-                                       newskb, NULL, newskb->dev,
+                                       sk, newskb, NULL, newskb->dev,
                                        dev_loopback_xmit);
                }
 
@@ -322,11 +327,11 @@ int ip_mc_output(struct sock *sk, struct sk_buff *skb)
        if (rt->rt_flags&RTCF_BROADCAST) {
                struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
                if (newskb)
-                       NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, newskb,
+                       NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, sk, newskb,
                                NULL, newskb->dev, dev_loopback_xmit);
        }
 
-       return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb, NULL,
+       return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, sk, skb, NULL,
                            skb->dev, ip_finish_output,
                            !(IPCB(skb)->flags & IPSKB_REROUTED));
 }
@@ -340,7 +345,8 @@ int ip_output(struct sock *sk, struct sk_buff *skb)
        skb->dev = dev;
        skb->protocol = htons(ETH_P_IP);
 
-       return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb, NULL, dev,
+       return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, sk, skb,
+                           NULL, dev,
                            ip_finish_output,
                            !(IPCB(skb)->flags & IPSKB_REROUTED));
 }
@@ -449,7 +455,6 @@ no_route:
 }
 EXPORT_SYMBOL(ip_queue_xmit);
 
-
 static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from)
 {
        to->pkt_type = from->pkt_type;
@@ -480,7 +485,8 @@ static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from)
  *     single device frame, and queue such a frame for sending.
  */
 
-int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
+int ip_fragment(struct sock *sk, struct sk_buff *skb,
+               int (*output)(struct sock *, struct sk_buff *))
 {
        struct iphdr *iph;
        int ptr;
@@ -593,7 +599,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
                                ip_send_check(iph);
                        }
 
-                       err = output(skb);
+                       err = output(sk, skb);
 
                        if (!err)
                                IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGCREATES);
@@ -730,7 +736,7 @@ slow_path:
 
                ip_send_check(iph);
 
-               err = output(skb2);
+               err = output(sk, skb2);
                if (err)
                        goto fail;
 
@@ -813,7 +819,6 @@ static inline int ip_ufo_append_data(struct sock *sk,
 
                skb->csum = 0;
 
-
                __skb_queue_tail(queue, skb);
        } else if (skb_is_gso(skb)) {
                goto append;
@@ -1211,7 +1216,6 @@ ssize_t   ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page,
                skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
        }
 
-
        while (size > 0) {
                int i;
 
index 6d364ab8e14eec5ff24c49357fbea45b02b92b39..4c2c3ba4ba6595c788940e3ec2c2dadfe7abe2f0 100644 (file)
@@ -782,7 +782,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
                return;
        }
 
-       err = iptunnel_xmit(skb->sk, rt, skb, fl4.saddr, fl4.daddr, protocol,
+       err = iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr, protocol,
                            tos, ttl, df, !net_eq(tunnel->net, dev_net(dev)));
        iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
 
index 8c4dcc46acd2932e062faf546331c938b160b0d1..ce63ab21b6cda87f8caea29967a4651b7f78c909 100644 (file)
@@ -74,7 +74,8 @@ 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(sock_net(sk), iph, skb_shinfo(skb)->gso_segs ?: 1);
+       __ip_select_ident(dev_net(rt->dst.dev), iph,
+                         skb_shinfo(skb)->gso_segs ?: 1);
 
        err = ip_local_out_sk(sk, skb);
        if (unlikely(net_xmit_eval(err)))
index 5f17d0e780716eafc4df6fc66b71106016ce8a9b..3a2c0162c3badeed716599e538ed06426ddc7199 100644 (file)
@@ -1679,7 +1679,7 @@ static void ip_encap(struct net *net, struct sk_buff *skb,
        nf_reset(skb);
 }
 
-static inline int ipmr_forward_finish(struct sk_buff *skb)
+static inline int ipmr_forward_finish(struct sock *sk, struct sk_buff *skb)
 {
        struct ip_options *opt = &(IPCB(skb)->opt);
 
@@ -1689,7 +1689,7 @@ static inline int ipmr_forward_finish(struct sk_buff *skb)
        if (unlikely(opt->optlen))
                ip_forward_options(skb);
 
-       return dst_output(skb);
+       return dst_output_sk(sk, skb);
 }
 
 /*
@@ -1788,7 +1788,8 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
         * not mrouter) cannot join to more than one interface - it will
         * result in receiving multiple packets.
         */
-       NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, skb, skb->dev, dev,
+       NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, NULL, skb,
+               skb->dev, dev,
                ipmr_forward_finish);
        return;
 
index c5b794da51a91fd8b2236379b532910cafc0568f..3262e41ff76f38a89db3fd7da8c771a51b273abf 100644 (file)
@@ -13,6 +13,7 @@
 #include <net/dst.h>
 #include <net/netfilter/ipv4/nf_reject.h>
 #include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_bridge.h>
 #include <net/netfilter/ipv4/nf_reject.h>
 
 const struct tcphdr *nf_reject_ip_tcphdr_get(struct sk_buff *oldskb,
@@ -146,7 +147,8 @@ void nf_send_reset(struct sk_buff *oldskb, int hook)
         */
        if (oldskb->nf_bridge) {
                struct ethhdr *oeth = eth_hdr(oldskb);
-               nskb->dev = oldskb->nf_bridge->physindev;
+
+               nskb->dev = nf_bridge_get_physindev(oldskb);
                niph->tot_len = htons(nskb->len);
                ip_send_check(niph);
                if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol),
index 16a5d4d73d7565807490074d9f4df6f0ab90b297..a7621faa96783f31cf178a36a8ac147bedac5f32 100644 (file)
@@ -33,6 +33,8 @@ static void nft_reject_ipv4_eval(const struct nft_expr *expr,
        case NFT_REJECT_TCP_RST:
                nf_send_reset(pkt->skb, pkt->ops->hooknum);
                break;
+       default:
+               break;
        }
 
        data[NFT_REG_VERDICT].verdict = NF_DROP;
index d8953ef0770ca14bbb9c883a18354bcac321db7b..e1f3b911dd1e3739a63e38b63a1b9a7b29bfd7f0 100644 (file)
@@ -63,7 +63,7 @@ static int sockstat_seq_show(struct seq_file *seq, void *v)
        socket_seq_show(seq);
        seq_printf(seq, "TCP: inuse %d orphan %d tw %d alloc %d mem %ld\n",
                   sock_prot_inuse_get(net, &tcp_prot), orphans,
-                  tcp_death_row.tw_count, sockets,
+                  atomic_read(&tcp_death_row.tw_count), sockets,
                   proto_memory_allocated(&tcp_prot));
        seq_printf(seq, "UDP: inuse %d mem %ld\n",
                   sock_prot_inuse_get(net, &udp_prot),
index f2fc92a1241af90777c23f1bb6ded06c0f5868be..561cd4b8fc6e07b49222d788651e93c42ee3adcb 100644 (file)
@@ -411,8 +411,8 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
                icmp_out_count(net, ((struct icmphdr *)
                        skb_transport_header(skb))->type);
 
-       err = NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT, skb, NULL,
-                     rt->dst.dev, dst_output);
+       err = NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT, sk, skb,
+                     NULL, rt->dst.dev, dst_output_sk);
        if (err > 0)
                err = net_xmit_errno(err);
        if (err)
index 5da55e2b5cd22941b29abde6ef2993b79d97a848..e3d87aca6be8fafe02bec5a8f862a88a6fe79d50 100644 (file)
@@ -303,6 +303,7 @@ fastopen:
        } else if (foc->len > 0) /* Client presents an invalid cookie */
                NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENPASSIVEFAIL);
 
+       valid_foc.exp = foc->exp;
        *foc = valid_foc;
        return false;
 }
index c1ce304ba8d2b63930a8e70d11bd3b83370c134c..a7ef679dd3ea434e5b3bda48d391e1a872ed2478 100644 (file)
@@ -3099,17 +3099,15 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
                        if (sacked & TCPCB_SACKED_RETRANS)
                                tp->retrans_out -= acked_pcount;
                        flag |= FLAG_RETRANS_DATA_ACKED;
-               } else {
+               } else if (!(sacked & TCPCB_SACKED_ACKED)) {
                        last_ackt = skb->skb_mstamp;
                        WARN_ON_ONCE(last_ackt.v64 == 0);
                        if (!first_ackt.v64)
                                first_ackt = last_ackt;
 
-                       if (!(sacked & TCPCB_SACKED_ACKED)) {
-                               reord = min(pkts_acked, reord);
-                               if (!after(scb->end_seq, tp->high_seq))
-                                       flag |= FLAG_ORIG_SACK_ACKED;
-                       }
+                       reord = min(pkts_acked, reord);
+                       if (!after(scb->end_seq, tp->high_seq))
+                               flag |= FLAG_ORIG_SACK_ACKED;
                }
 
                if (sacked & TCPCB_SACKED_ACKED)
@@ -3603,6 +3601,23 @@ old_ack:
        return 0;
 }
 
+static void tcp_parse_fastopen_option(int len, const unsigned char *cookie,
+                                     bool syn, struct tcp_fastopen_cookie *foc,
+                                     bool exp_opt)
+{
+       /* Valid only in SYN or SYN-ACK with an even length.  */
+       if (!foc || !syn || len < 0 || (len & 1))
+               return;
+
+       if (len >= TCP_FASTOPEN_COOKIE_MIN &&
+           len <= TCP_FASTOPEN_COOKIE_MAX)
+               memcpy(foc->val, cookie, len);
+       else if (len != 0)
+               len = -1;
+       foc->len = len;
+       foc->exp = exp_opt;
+}
+
 /* Look for tcp options. Normally only called on SYN and SYNACK packets.
  * But, this can also be called on packets in the established flow when
  * the fast version below fails.
@@ -3692,21 +3707,22 @@ void tcp_parse_options(const struct sk_buff *skb,
                                 */
                                break;
 #endif
+                       case TCPOPT_FASTOPEN:
+                               tcp_parse_fastopen_option(
+                                       opsize - TCPOLEN_FASTOPEN_BASE,
+                                       ptr, th->syn, foc, false);
+                               break;
+
                        case TCPOPT_EXP:
                                /* Fast Open option shares code 254 using a
-                                * 16 bits magic number. It's valid only in
-                                * SYN or SYN-ACK with an even size.
+                                * 16 bits magic number.
                                 */
-                               if (opsize < TCPOLEN_EXP_FASTOPEN_BASE ||
-                                   get_unaligned_be16(ptr) != TCPOPT_FASTOPEN_MAGIC ||
-                                   !foc || !th->syn || (opsize & 1))
-                                       break;
-                               foc->len = opsize - TCPOLEN_EXP_FASTOPEN_BASE;
-                               if (foc->len >= TCP_FASTOPEN_COOKIE_MIN &&
-                                   foc->len <= TCP_FASTOPEN_COOKIE_MAX)
-                                       memcpy(foc->val, ptr + 2, foc->len);
-                               else if (foc->len != 0)
-                                       foc->len = -1;
+                               if (opsize >= TCPOLEN_EXP_FASTOPEN_BASE &&
+                                   get_unaligned_be16(ptr) ==
+                                   TCPOPT_FASTOPEN_MAGIC)
+                                       tcp_parse_fastopen_option(opsize -
+                                               TCPOLEN_EXP_FASTOPEN_BASE,
+                                               ptr + 2, th->syn, foc, true);
                                break;
 
                        }
@@ -5360,8 +5376,8 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack,
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct sk_buff *data = tp->syn_data ? tcp_write_queue_head(sk) : NULL;
-       u16 mss = tp->rx_opt.mss_clamp;
-       bool syn_drop;
+       u16 mss = tp->rx_opt.mss_clamp, try_exp = 0;
+       bool syn_drop = false;
 
        if (mss == tp->rx_opt.user_mss) {
                struct tcp_options_received opt;
@@ -5373,16 +5389,25 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack,
                mss = opt.mss_clamp;
        }
 
-       if (!tp->syn_fastopen)  /* Ignore an unsolicited cookie */
+       if (!tp->syn_fastopen) {
+               /* Ignore an unsolicited cookie */
                cookie->len = -1;
+       } else if (tp->total_retrans) {
+               /* SYN timed out and the SYN-ACK neither has a cookie nor
+                * acknowledges data. Presumably the remote received only
+                * the retransmitted (regular) SYNs: either the original
+                * SYN-data or the corresponding SYN-ACK was dropped.
+                */
+               syn_drop = (cookie->len < 0 && data);
+       } else if (cookie->len < 0 && !tp->syn_data) {
+               /* We requested a cookie but didn't get it. If we did not use
+                * the (old) exp opt format then try so next time (try_exp=1).
+                * Otherwise we go back to use the RFC7413 opt (try_exp=2).
+                */
+               try_exp = tp->syn_fastopen_exp ? 2 : 1;
+       }
 
-       /* The SYN-ACK neither has cookie nor acknowledges the data. Presumably
-        * the remote receives only the retransmitted (regular) SYNs: either
-        * the original SYN-data or the corresponding SYN-ACK is lost.
-        */
-       syn_drop = (cookie->len <= 0 && data && tp->total_retrans);
-
-       tcp_fastopen_cache_set(sk, mss, cookie, syn_drop);
+       tcp_fastopen_cache_set(sk, mss, cookie, syn_drop, try_exp);
 
        if (data) { /* Retransmit unacked data in SYN */
                tcp_for_write_queue_from(data, sk) {
index 560f9571f7c43957b5996957ebed5f093fff6d75..3571f2be4470749b9a7c8178fa43a6bf2bac33d8 100644 (file)
@@ -897,9 +897,9 @@ EXPORT_SYMBOL(tcp_md5_do_lookup);
 struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk,
                                         const struct sock *addr_sk)
 {
-       union tcp_md5_addr *addr;
+       const union tcp_md5_addr *addr;
 
-       addr = (union tcp_md5_addr *)&sk->sk_daddr;
+       addr = (const union tcp_md5_addr *)&addr_sk->sk_daddr;
        return tcp_md5_do_lookup(sk, addr, AF_INET);
 }
 EXPORT_SYMBOL(tcp_v4_md5_lookup);
@@ -1685,7 +1685,7 @@ do_time_wait:
                                                        iph->daddr, th->dest,
                                                        inet_iif(skb));
                if (sk2) {
-                       inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row);
+                       inet_twsk_deschedule(inet_twsk(sk));
                        inet_twsk_put(inet_twsk(sk));
                        sk = sk2;
                        goto process;
@@ -2242,9 +2242,9 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i)
 static void get_timewait4_sock(const struct inet_timewait_sock *tw,
                               struct seq_file *f, int i)
 {
+       long delta = tw->tw_timer.expires - jiffies;
        __be32 dest, src;
        __u16 destp, srcp;
-       s32 delta = tw->tw_ttd - inet_tw_time_stamp();
 
        dest  = tw->tw_daddr;
        src   = tw->tw_rcv_saddr;
index 78ecc4a017128adf389c33418c937134f111e39d..a51d63a43e33af5fc751e4f0f3369b9394776975 100644 (file)
@@ -28,7 +28,8 @@ static struct tcp_metrics_block *__tcp_get_metrics(const struct inetpeer_addr *s
 
 struct tcp_fastopen_metrics {
        u16     mss;
-       u16     syn_loss:10;            /* Recurring Fast Open SYN losses */
+       u16     syn_loss:10,            /* Recurring Fast Open SYN losses */
+               try_exp:2;              /* Request w/ exp. option (once) */
        unsigned long   last_syn_loss;  /* Last Fast Open SYN loss */
        struct  tcp_fastopen_cookie     cookie;
 };
@@ -131,6 +132,8 @@ static void tcpm_suck_dst(struct tcp_metrics_block *tm,
        if (fastopen_clear) {
                tm->tcpm_fastopen.mss = 0;
                tm->tcpm_fastopen.syn_loss = 0;
+               tm->tcpm_fastopen.try_exp = 0;
+               tm->tcpm_fastopen.cookie.exp = false;
                tm->tcpm_fastopen.cookie.len = 0;
        }
 }
@@ -713,6 +716,8 @@ void tcp_fastopen_cache_get(struct sock *sk, u16 *mss,
                        if (tfom->mss)
                                *mss = tfom->mss;
                        *cookie = tfom->cookie;
+                       if (cookie->len <= 0 && tfom->try_exp == 1)
+                               cookie->exp = true;
                        *syn_loss = tfom->syn_loss;
                        *last_syn_loss = *syn_loss ? tfom->last_syn_loss : 0;
                } while (read_seqretry(&fastopen_seqlock, seq));
@@ -721,7 +726,8 @@ void tcp_fastopen_cache_get(struct sock *sk, u16 *mss,
 }
 
 void tcp_fastopen_cache_set(struct sock *sk, u16 mss,
-                           struct tcp_fastopen_cookie *cookie, bool syn_lost)
+                           struct tcp_fastopen_cookie *cookie, bool syn_lost,
+                           u16 try_exp)
 {
        struct dst_entry *dst = __sk_dst_get(sk);
        struct tcp_metrics_block *tm;
@@ -738,6 +744,9 @@ void tcp_fastopen_cache_set(struct sock *sk, u16 mss,
                        tfom->mss = mss;
                if (cookie && cookie->len > 0)
                        tfom->cookie = *cookie;
+               else if (try_exp > tfom->try_exp &&
+                        tfom->cookie.len <= 0 && !tfom->cookie.exp)
+                       tfom->try_exp = try_exp;
                if (syn_lost) {
                        ++tfom->syn_loss;
                        tfom->last_syn_loss = jiffies;
index d7003911c894075c209756a0ce26950a6a31aba4..63d6311b5365944fae0bdaa0f7abfbc51f55a429 100644 (file)
@@ -34,18 +34,7 @@ int sysctl_tcp_abort_on_overflow __read_mostly;
 
 struct inet_timewait_death_row tcp_death_row = {
        .sysctl_max_tw_buckets = NR_FILE * 2,
-       .period         = TCP_TIMEWAIT_LEN / INET_TWDR_TWKILL_SLOTS,
-       .death_lock     = __SPIN_LOCK_UNLOCKED(tcp_death_row.death_lock),
        .hashinfo       = &tcp_hashinfo,
-       .tw_timer       = TIMER_INITIALIZER(inet_twdr_hangman, 0,
-                                           (unsigned long)&tcp_death_row),
-       .twkill_work    = __WORK_INITIALIZER(tcp_death_row.twkill_work,
-                                            inet_twdr_twkill_work),
-/* Short-time timewait calendar */
-
-       .twcal_hand     = -1,
-       .twcal_timer    = TIMER_INITIALIZER(inet_twdr_twcal_tick, 0,
-                                           (unsigned long)&tcp_death_row),
 };
 EXPORT_SYMBOL_GPL(tcp_death_row);
 
@@ -158,7 +147,7 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb,
                if (!th->fin ||
                    TCP_SKB_CB(skb)->end_seq != tcptw->tw_rcv_nxt + 1) {
 kill_with_rst:
-                       inet_twsk_deschedule(tw, &tcp_death_row);
+                       inet_twsk_deschedule(tw);
                        inet_twsk_put(tw);
                        return TCP_TW_RST;
                }
@@ -174,11 +163,9 @@ kill_with_rst:
                if (tcp_death_row.sysctl_tw_recycle &&
                    tcptw->tw_ts_recent_stamp &&
                    tcp_tw_remember_stamp(tw))
-                       inet_twsk_schedule(tw, &tcp_death_row, tw->tw_timeout,
-                                          TCP_TIMEWAIT_LEN);
+                       inet_twsk_schedule(tw, tw->tw_timeout);
                else
-                       inet_twsk_schedule(tw, &tcp_death_row, TCP_TIMEWAIT_LEN,
-                                          TCP_TIMEWAIT_LEN);
+                       inet_twsk_schedule(tw, TCP_TIMEWAIT_LEN);
                return TCP_TW_ACK;
        }
 
@@ -211,13 +198,12 @@ kill_with_rst:
                         */
                        if (sysctl_tcp_rfc1337 == 0) {
 kill:
-                               inet_twsk_deschedule(tw, &tcp_death_row);
+                               inet_twsk_deschedule(tw);
                                inet_twsk_put(tw);
                                return TCP_TW_SUCCESS;
                        }
                }
-               inet_twsk_schedule(tw, &tcp_death_row, TCP_TIMEWAIT_LEN,
-                                  TCP_TIMEWAIT_LEN);
+               inet_twsk_schedule(tw, TCP_TIMEWAIT_LEN);
 
                if (tmp_opt.saw_tstamp) {
                        tcptw->tw_ts_recent       = tmp_opt.rcv_tsval;
@@ -267,8 +253,7 @@ kill:
                 * Do not reschedule in the last case.
                 */
                if (paws_reject || th->ack)
-                       inet_twsk_schedule(tw, &tcp_death_row, TCP_TIMEWAIT_LEN,
-                                          TCP_TIMEWAIT_LEN);
+                       inet_twsk_schedule(tw, TCP_TIMEWAIT_LEN);
 
                return tcp_timewait_check_oow_rate_limit(
                        tw, skb, LINUX_MIB_TCPACKSKIPPEDTIMEWAIT);
@@ -283,16 +268,15 @@ EXPORT_SYMBOL(tcp_timewait_state_process);
  */
 void tcp_time_wait(struct sock *sk, int state, int timeo)
 {
-       struct inet_timewait_sock *tw = NULL;
        const struct inet_connection_sock *icsk = inet_csk(sk);
        const struct tcp_sock *tp = tcp_sk(sk);
+       struct inet_timewait_sock *tw;
        bool recycle_ok = false;
 
        if (tcp_death_row.sysctl_tw_recycle && tp->rx_opt.ts_recent_stamp)
                recycle_ok = tcp_remember_stamp(sk);
 
-       if (tcp_death_row.tw_count < tcp_death_row.sysctl_max_tw_buckets)
-               tw = inet_twsk_alloc(sk, state);
+       tw = inet_twsk_alloc(sk, &tcp_death_row, state);
 
        if (tw) {
                struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw);
@@ -355,8 +339,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo)
                                timeo = TCP_TIMEWAIT_LEN;
                }
 
-               inet_twsk_schedule(tw, &tcp_death_row, timeo,
-                                  TCP_TIMEWAIT_LEN);
+               inet_twsk_schedule(tw, timeo);
                inet_twsk_put(tw);
        } else {
                /* Sorry, if we're out of memory, just CLOSE this
@@ -628,10 +611,16 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
                                          LINUX_MIB_TCPACKSKIPPEDSYNRECV,
                                          &tcp_rsk(req)->last_oow_ack_time) &&
 
-                   !inet_rtx_syn_ack(sk, req))
-                       mod_timer_pending(&req->rsk_timer, jiffies +
-                               min(TCP_TIMEOUT_INIT << req->num_timeout,
-                                   TCP_RTO_MAX));
+                   !inet_rtx_syn_ack(sk, req)) {
+                       unsigned long expires = jiffies;
+
+                       expires += min(TCP_TIMEOUT_INIT << req->num_timeout,
+                                      TCP_RTO_MAX);
+                       if (!fastopen)
+                               mod_timer_pending(&req->rsk_timer, expires);
+                       else
+                               req->rsk_timer.expires = expires;
+               }
                return NULL;
        }
 
index 7404e5238e004395ce0a55a073c806076b2394c8..e662d85d1635d0269b669bb0f726760be3bae0d2 100644 (file)
@@ -518,17 +518,26 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp,
 
        if (unlikely(OPTION_FAST_OPEN_COOKIE & options)) {
                struct tcp_fastopen_cookie *foc = opts->fastopen_cookie;
+               u8 *p = (u8 *)ptr;
+               u32 len; /* Fast Open option length */
+
+               if (foc->exp) {
+                       len = TCPOLEN_EXP_FASTOPEN_BASE + foc->len;
+                       *ptr = htonl((TCPOPT_EXP << 24) | (len << 16) |
+                                    TCPOPT_FASTOPEN_MAGIC);
+                       p += TCPOLEN_EXP_FASTOPEN_BASE;
+               } else {
+                       len = TCPOLEN_FASTOPEN_BASE + foc->len;
+                       *p++ = TCPOPT_FASTOPEN;
+                       *p++ = len;
+               }
 
-               *ptr++ = htonl((TCPOPT_EXP << 24) |
-                              ((TCPOLEN_EXP_FASTOPEN_BASE + foc->len) << 16) |
-                              TCPOPT_FASTOPEN_MAGIC);
-
-               memcpy(ptr, foc->val, foc->len);
-               if ((foc->len & 3) == 2) {
-                       u8 *align = ((u8 *)ptr) + foc->len;
-                       align[0] = align[1] = TCPOPT_NOP;
+               memcpy(p, foc->val, foc->len);
+               if ((len & 3) == 2) {
+                       p[foc->len] = TCPOPT_NOP;
+                       p[foc->len + 1] = TCPOPT_NOP;
                }
-               ptr += (foc->len + 3) >> 2;
+               ptr += (len + 3) >> 2;
        }
 }
 
@@ -583,13 +592,17 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb,
        }
 
        if (fastopen && fastopen->cookie.len >= 0) {
-               u32 need = TCPOLEN_EXP_FASTOPEN_BASE + fastopen->cookie.len;
+               u32 need = fastopen->cookie.len;
+
+               need += fastopen->cookie.exp ? TCPOLEN_EXP_FASTOPEN_BASE :
+                                              TCPOLEN_FASTOPEN_BASE;
                need = (need + 3) & ~3U;  /* Align to 32 bits */
                if (remaining >= need) {
                        opts->options |= OPTION_FAST_OPEN_COOKIE;
                        opts->fastopen_cookie = &fastopen->cookie;
                        remaining -= need;
                        tp->syn_fastopen = 1;
+                       tp->syn_fastopen_exp = fastopen->cookie.exp ? 1 : 0;
                }
        }
 
@@ -641,8 +654,11 @@ static unsigned int tcp_synack_options(struct sock *sk,
                if (unlikely(!ireq->tstamp_ok))
                        remaining -= TCPOLEN_SACKPERM_ALIGNED;
        }
-       if (foc && foc->len >= 0) {
-               u32 need = TCPOLEN_EXP_FASTOPEN_BASE + foc->len;
+       if (foc != NULL && foc->len >= 0) {
+               u32 need = foc->len;
+
+               need += foc->exp ? TCPOLEN_EXP_FASTOPEN_BASE :
+                                  TCPOLEN_FASTOPEN_BASE;
                need = (need + 3) & ~3U;  /* Align to 32 bits */
                if (remaining >= need) {
                        opts->options |= OPTION_FAST_OPEN_COOKIE;
index 2568fd282873b7436ca2299e20c283e1affd8688..8c65dc147d8bcfb58e14c20b774711ffbcc30d5a 100644 (file)
@@ -167,7 +167,7 @@ static int tcp_write_timeout(struct sock *sk)
                if (icsk->icsk_retransmits) {
                        dst_negative_advice(sk);
                        if (tp->syn_fastopen || tp->syn_data)
-                               tcp_fastopen_cache_set(sk, 0, NULL, true);
+                               tcp_fastopen_cache_set(sk, 0, NULL, true, 0);
                        if (tp->syn_data)
                                NET_INC_STATS_BH(sock_net(sk),
                                                 LINUX_MIB_TCPFASTOPENACTIVEFAIL);
index 2162fc6ce1c1e779e0bbe4c63aa126ab1db08540..d10b7e0112ebdb8fa61c650725ae7fae68f7e669 100644 (file)
@@ -433,7 +433,6 @@ static u32 udp_ehashfn(const struct net *net, const __be32 laddr,
                              udp_ehash_secret + net_hash_mix(net));
 }
 
-
 /* called with read_rcu_lock() */
 static struct sock *udp4_lib_lookup2(struct net *net,
                __be32 saddr, __be16 sport,
@@ -1171,7 +1170,6 @@ out:
        return ret;
 }
 
-
 /**
  *     first_packet_length     - return length of first packet in receive queue
  *     @sk: socket
@@ -1355,7 +1353,6 @@ csum_copy_err:
        goto try_again;
 }
 
-
 int udp_disconnect(struct sock *sk, int flags)
 {
        struct inet_sock *inet = inet_sk(sk);
@@ -1579,7 +1576,6 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
            udp_lib_checksum_complete(skb))
                goto csum_error;
 
-
        if (sk_rcvqueues_full(sk, sk->sk_rcvbuf)) {
                UDP_INC_STATS_BH(sock_net(sk), UDP_MIB_RCVBUFERRORS,
                                 is_udplite);
@@ -1609,7 +1605,6 @@ drop:
        return -1;
 }
 
-
 static void flush_stack(struct sock **stack, unsigned int count,
                        struct sk_buff *skb, unsigned int final)
 {
index c83b354850563ebc268a349bc25bc8b668435732..6bb98cc193c9a1b532b668b34e9b401357ad96e1 100644 (file)
@@ -75,7 +75,7 @@ void setup_udp_tunnel_sock(struct net *net, struct socket *sock,
 }
 EXPORT_SYMBOL_GPL(setup_udp_tunnel_sock);
 
-int udp_tunnel_xmit_skb(struct rtable *rt, struct sk_buff *skb,
+int udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
                        __be32 src, __be32 dst, __u8 tos, __u8 ttl,
                        __be16 df, __be16 src_port, __be16 dst_port,
                        bool xnet, bool nocheck)
@@ -92,7 +92,7 @@ int udp_tunnel_xmit_skb(struct rtable *rt, struct sk_buff *skb,
 
        udp_set_csum(nocheck, skb, src, dst, skb->len);
 
-       return iptunnel_xmit(skb->sk, rt, skb, src, dst, IPPROTO_UDP,
+       return iptunnel_xmit(sk, rt, skb, src, dst, IPPROTO_UDP,
                             tos, ttl, df, xnet);
 }
 EXPORT_SYMBOL_GPL(udp_tunnel_xmit_skb);
index cac7468db0a1bfc74f9f302588c1e9875e84ada0..60b032f58ccc9ffca8617de21c72288b919889b7 100644 (file)
@@ -22,7 +22,7 @@ int xfrm4_extract_input(struct xfrm_state *x, struct sk_buff *skb)
        return xfrm4_extract_header(skb);
 }
 
-static inline int xfrm4_rcv_encap_finish(struct sk_buff *skb)
+static inline int xfrm4_rcv_encap_finish(struct sock *sk, struct sk_buff *skb)
 {
        if (!skb_dst(skb)) {
                const struct iphdr *iph = ip_hdr(skb);
@@ -52,7 +52,8 @@ int xfrm4_transport_finish(struct sk_buff *skb, int async)
        iph->tot_len = htons(skb->len);
        ip_send_check(iph);
 
-       NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, skb->dev, NULL,
+       NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, NULL, skb,
+               skb->dev, NULL,
                xfrm4_rcv_encap_finish);
        return 0;
 }
index dab73813cb9208dafaae1277e281c2255601a771..2878dbfffeb7e769a32079f1a6b80061136a7efc 100644 (file)
@@ -69,7 +69,7 @@ int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
 }
 EXPORT_SYMBOL(xfrm4_prepare_output);
 
-int xfrm4_output_finish(struct sk_buff *skb)
+int xfrm4_output_finish(struct sock *sk, struct sk_buff *skb)
 {
        memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
 
@@ -77,26 +77,26 @@ int xfrm4_output_finish(struct sk_buff *skb)
        IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED;
 #endif
 
-       return xfrm_output(skb);
+       return xfrm_output(sk, skb);
 }
 
-static int __xfrm4_output(struct sk_buff *skb)
+static int __xfrm4_output(struct sock *sk, struct sk_buff *skb)
 {
        struct xfrm_state *x = skb_dst(skb)->xfrm;
 
 #ifdef CONFIG_NETFILTER
        if (!x) {
                IPCB(skb)->flags |= IPSKB_REROUTED;
-               return dst_output(skb);
+               return dst_output_sk(sk, skb);
        }
 #endif
 
-       return x->outer_mode->afinfo->output_finish(skb);
+       return x->outer_mode->afinfo->output_finish(sk, skb);
 }
 
 int xfrm4_output(struct sock *sk, struct sk_buff *skb)
 {
-       return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb,
+       return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, sk, skb,
                            NULL, skb_dst(skb)->dev, __xfrm4_output,
                            !(IPCB(skb)->flags & IPSKB_REROUTED));
 }
index 033f17816ef4cf482d40eb496129a7d832c0a251..871641bc1ed4eb5b8f5f554c9efb75f2419fe5b6 100644 (file)
@@ -246,7 +246,7 @@ static int __inet6_check_established(struct inet_timewait_death_row *death_row,
                *twp = tw;
        } else if (tw) {
                /* Silly. Should hash-dance instead... */
-               inet_twsk_deschedule(tw, death_row);
+               inet_twsk_deschedule(tw);
 
                inet_twsk_put(tw);
        }
index f724329d7436430a532dd70c8c047bf0dd881642..b5e6cc1d4a7302f3288ba00ee9c75bb327410960 100644 (file)
@@ -760,7 +760,7 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb,
 
        skb_set_inner_protocol(skb, protocol);
 
-       ip6tunnel_xmit(skb, dev);
+       ip6tunnel_xmit(NULL, skb, dev);
        if (ndst)
                ip6_tnl_dst_store(tunnel, ndst);
        return 0;
index fb97f7f8d4ed11497e088d063fbe8d78e80d0ed0..f2e464eba5efdb7b2a8abe3c2cecedae473777e7 100644 (file)
@@ -46,8 +46,7 @@
 #include <net/xfrm.h>
 #include <net/inet_ecn.h>
 
-
-int ip6_rcv_finish(struct sk_buff *skb)
+int ip6_rcv_finish(struct sock *sk, struct sk_buff *skb)
 {
        if (sysctl_ip_early_demux && !skb_dst(skb) && skb->sk == NULL) {
                const struct inet6_protocol *ipprot;
@@ -183,7 +182,8 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt
        /* Must drop socket now because of tproxy. */
        skb_orphan(skb);
 
-       return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, skb, dev, NULL,
+       return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, NULL, skb,
+                      dev, NULL,
                       ip6_rcv_finish);
 err:
        IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INHDRERRORS);
@@ -198,7 +198,7 @@ drop:
  */
 
 
-static int ip6_input_finish(struct sk_buff *skb)
+static int ip6_input_finish(struct sock *sk, struct sk_buff *skb)
 {
        struct net *net = dev_net(skb_dst(skb)->dev);
        const struct inet6_protocol *ipprot;
@@ -277,7 +277,8 @@ discard:
 
 int ip6_input(struct sk_buff *skb)
 {
-       return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_IN, skb, skb->dev, NULL,
+       return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_IN, NULL, skb,
+                      skb->dev, NULL,
                       ip6_input_finish);
 }
 
index 654f245aa93051c03bfe69c7a9c1ed83c50c527d..7fde1f265c90e90f16291e6c861b6e242111c25b 100644 (file)
@@ -56,7 +56,7 @@
 #include <net/checksum.h>
 #include <linux/mroute6.h>
 
-static int ip6_finish_output2(struct sk_buff *skb)
+static int ip6_finish_output2(struct sock *sk, struct sk_buff *skb)
 {
        struct dst_entry *dst = skb_dst(skb);
        struct net_device *dev = dst->dev;
@@ -70,7 +70,7 @@ static int ip6_finish_output2(struct sk_buff *skb)
        if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) {
                struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb));
 
-               if (!(dev->flags & IFF_LOOPBACK) && sk_mc_loop(skb->sk) &&
+               if (!(dev->flags & IFF_LOOPBACK) && sk_mc_loop(sk) &&
                    ((mroute6_socket(dev_net(dev), skb) &&
                     !(IP6CB(skb)->flags & IP6SKB_FORWARDED)) ||
                     ipv6_chk_mcast_addr(dev, &ipv6_hdr(skb)->daddr,
@@ -82,7 +82,7 @@ static int ip6_finish_output2(struct sk_buff *skb)
                         */
                        if (newskb)
                                NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING,
-                                       newskb, NULL, newskb->dev,
+                                       sk, newskb, NULL, newskb->dev,
                                        dev_loopback_xmit);
 
                        if (ipv6_hdr(skb)->hop_limit == 0) {
@@ -122,14 +122,14 @@ static int ip6_finish_output2(struct sk_buff *skb)
        return -EINVAL;
 }
 
-static int ip6_finish_output(struct sk_buff *skb)
+static int ip6_finish_output(struct sock *sk, struct sk_buff *skb)
 {
        if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) ||
            dst_allfrag(skb_dst(skb)) ||
            (IP6CB(skb)->frag_max_size && skb->len > IP6CB(skb)->frag_max_size))
-               return ip6_fragment(skb, ip6_finish_output2);
+               return ip6_fragment(sk, skb, ip6_finish_output2);
        else
-               return ip6_finish_output2(skb);
+               return ip6_finish_output2(sk, skb);
 }
 
 int ip6_output(struct sock *sk, struct sk_buff *skb)
@@ -143,7 +143,8 @@ int ip6_output(struct sock *sk, struct sk_buff *skb)
                return 0;
        }
 
-       return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, skb, NULL, dev,
+       return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, sk, skb,
+                           NULL, dev,
                            ip6_finish_output,
                            !(IP6CB(skb)->flags & IP6SKB_REROUTED));
 }
@@ -223,8 +224,8 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
        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,
-                              dst->dev, dst_output);
+               return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb,
+                              NULL, dst->dev, dst_output_sk);
        }
 
        skb->dev = dst->dev;
@@ -316,10 +317,10 @@ static int ip6_forward_proxy_check(struct sk_buff *skb)
        return 0;
 }
 
-static inline int ip6_forward_finish(struct sk_buff *skb)
+static inline int ip6_forward_finish(struct sock *sk, struct sk_buff *skb)
 {
        skb_sender_cpu_clear(skb);
-       return dst_output(skb);
+       return dst_output_sk(sk, skb);
 }
 
 static unsigned int ip6_dst_mtu_forward(const struct dst_entry *dst)
@@ -511,7 +512,8 @@ int ip6_forward(struct sk_buff *skb)
 
        IP6_INC_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTFORWDATAGRAMS);
        IP6_ADD_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTOCTETS, skb->len);
-       return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD, skb, skb->dev, dst->dev,
+       return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD, NULL, skb,
+                      skb->dev, dst->dev,
                       ip6_forward_finish);
 
 error:
@@ -538,7 +540,8 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from)
        skb_copy_secmark(to, from);
 }
 
-int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
+int ip6_fragment(struct sock *sk, struct sk_buff *skb,
+                int (*output)(struct sock *, struct sk_buff *))
 {
        struct sk_buff *frag;
        struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
@@ -667,7 +670,7 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
                                ip6_copy_metadata(frag, skb);
                        }
 
-                       err = output(skb);
+                       err = output(sk, skb);
                        if (!err)
                                IP6_INC_STATS(net, ip6_dst_idev(&rt->dst),
                                              IPSTATS_MIB_FRAGCREATES);
@@ -800,7 +803,7 @@ slow_path:
                /*
                 *      Put this fragment into the sending queue.
                 */
-               err = output(frag);
+               err = output(sk, frag);
                if (err)
                        goto fail;
 
index b6a211a150b27e4b28eaa873e1edca204f0c03fd..5cafd92c231270703af5bf948d131d99c5e9c193 100644 (file)
@@ -1100,7 +1100,7 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
        ipv6h->nexthdr = proto;
        ipv6h->saddr = fl6->saddr;
        ipv6h->daddr = fl6->daddr;
-       ip6tunnel_xmit(skb, dev);
+       ip6tunnel_xmit(NULL, skb, dev);
        if (ndst)
                ip6_tnl_dst_store(t, ndst);
        return 0;
index 32d9b268e7d85faa6b33a3fa1a3f3fff9a36cad8..bba8903e871fabd73c217efb6250eb743f7d66d8 100644 (file)
@@ -62,7 +62,8 @@ error:
 }
 EXPORT_SYMBOL_GPL(udp_sock_create6);
 
-int udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sk_buff *skb,
+int udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sock *sk,
+                        struct sk_buff *skb,
                         struct net_device *dev, struct in6_addr *saddr,
                         struct in6_addr *daddr,
                         __u8 prio, __u8 ttl, __be16 src_port,
@@ -97,7 +98,7 @@ int udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sk_buff *skb,
        ip6h->daddr       = *daddr;
        ip6h->saddr       = *saddr;
 
-       ip6tunnel_xmit(skb, dev);
+       ip6tunnel_xmit(sk, skb, dev);
        return 0;
 }
 EXPORT_SYMBOL_GPL(udp_tunnel6_xmit_skb);
index 8493a22e74eb8a7f65b4b4177ece059d2f1c3060..74ceb73c1c9a042b0f8f9f65c264e8426d65f7f3 100644 (file)
@@ -1986,13 +1986,13 @@ int ip6mr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg)
 }
 #endif
 
-static inline int ip6mr_forward2_finish(struct sk_buff *skb)
+static inline int ip6mr_forward2_finish(struct sock *sk, struct sk_buff *skb)
 {
        IP6_INC_STATS_BH(dev_net(skb_dst(skb)->dev), ip6_dst_idev(skb_dst(skb)),
                         IPSTATS_MIB_OUTFORWDATAGRAMS);
        IP6_ADD_STATS_BH(dev_net(skb_dst(skb)->dev), ip6_dst_idev(skb_dst(skb)),
                         IPSTATS_MIB_OUTOCTETS, skb->len);
-       return dst_output(skb);
+       return dst_output_sk(sk, skb);
 }
 
 /*
@@ -2064,7 +2064,8 @@ static int ip6mr_forward2(struct net *net, struct mr6_table *mrt,
 
        IP6CB(skb)->flags |= IP6SKB_FORWARDED;
 
-       return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD, skb, skb->dev, dev,
+       return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD, NULL, skb,
+                      skb->dev, dev,
                       ip6mr_forward2_finish);
 
 out_free:
index fac1f27e428e26257a949b3d3d062ddecdda3e87..083b2927fc67aaa3939fff42a1c6ee9f1ca70afe 100644 (file)
@@ -1644,8 +1644,9 @@ static void mld_sendpack(struct sk_buff *skb)
 
        payload_len = skb->len;
 
-       err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, skb->dev,
-                     dst_output);
+       err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
+                     net->ipv6.igmp_sk, skb, NULL, skb->dev,
+                     dst_output_sk);
 out:
        if (!err) {
                ICMP6MSGOUT_INC_STATS(net, idev, ICMPV6_MLD2_REPORT);
@@ -2007,8 +2008,8 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
        }
 
        skb_dst_set(skb, dst);
-       err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, skb->dev,
-                     dst_output);
+       err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb,
+                     NULL, skb->dev, dst_output_sk);
 out:
        if (!err) {
                ICMP6MSGOUT_INC_STATS(net, idev, type);
index 71fde6cafb35d6e63da6bd18811a1b4322936892..96f153c0846b7abcd9b1af995c49067efbe7064d 100644 (file)
@@ -463,8 +463,9 @@ static void ndisc_send_skb(struct sk_buff *skb,
        idev = __in6_dev_get(dst->dev);
        IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
 
-       err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev,
-                     dst_output);
+       err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb,
+                     NULL, dst->dev,
+                     dst_output_sk);
        if (!err) {
                ICMP6MSGOUT_INC_STATS(net, idev, type);
                ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
index e2b88205675152fd229ca4a4c15f4d7212f14956..a45db0b4785c1e89f523ce28cb8e4231fbbc85b9 100644 (file)
@@ -75,7 +75,7 @@ static unsigned int ipv6_defrag(const struct nf_hook_ops *ops,
 
        nf_ct_frag6_consume_orig(reasm);
 
-       NF_HOOK_THRESH(NFPROTO_IPV6, ops->hooknum, reasm,
+       NF_HOOK_THRESH(NFPROTO_IPV6, ops->hooknum, state->sk, reasm,
                       state->in, state->out,
                       state->okfn, NF_IP6_PRI_CONNTRACK_DEFRAG + 1);
 
index 3afdce03d94e7c2dd27d19fabff9250c0f5aaddc..94b4c6dfb400c90b6c368acb7ecb83649309dce0 100644 (file)
@@ -13,6 +13,7 @@
 #include <net/ip6_checksum.h>
 #include <net/netfilter/ipv6/nf_reject.h>
 #include <linux/netfilter_ipv6.h>
+#include <linux/netfilter_bridge.h>
 #include <net/netfilter/ipv6/nf_reject.h>
 
 const struct tcphdr *nf_reject_ip6_tcphdr_get(struct sk_buff *oldskb,
@@ -195,7 +196,8 @@ void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook)
         */
        if (oldskb->nf_bridge) {
                struct ethhdr *oeth = eth_hdr(oldskb);
-               nskb->dev = oldskb->nf_bridge->physindev;
+
+               nskb->dev = nf_bridge_get_physindev(oldskb);
                nskb->protocol = htons(ETH_P_IPV6);
                ip6h->payload_len = htons(sizeof(struct tcphdr));
                if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol),
index f732859241444118d6cc357843482f70b432c786..71c7be5ee43a0e067e2d653cb37cee3a92e487bc 100644 (file)
@@ -34,6 +34,8 @@ static void nft_reject_ipv6_eval(const struct nft_expr *expr,
        case NFT_REJECT_TCP_RST:
                nf_send_reset6(net, pkt->skb, pkt->ops->hooknum);
                break;
+       default:
+               break;
        }
 
        data[NFT_REG_VERDICT].verdict = NF_DROP;
index 4016a6ef9d61479e9c6c418db608588b385314b6..85892af5736491fef3978bb8b195045e8e6d2e2c 100644 (file)
@@ -136,7 +136,7 @@ int ip6_dst_hoplimit(struct dst_entry *dst)
 EXPORT_SYMBOL(ip6_dst_hoplimit);
 #endif
 
-int __ip6_local_out(struct sk_buff *skb)
+static int __ip6_local_out_sk(struct sock *sk, struct sk_buff *skb)
 {
        int len;
 
@@ -146,19 +146,30 @@ int __ip6_local_out(struct sk_buff *skb)
        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);
+       return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb,
+                      NULL, skb_dst(skb)->dev, dst_output_sk);
+}
+
+int __ip6_local_out(struct sk_buff *skb)
+{
+       return __ip6_local_out_sk(skb->sk, skb);
 }
 EXPORT_SYMBOL_GPL(__ip6_local_out);
 
-int ip6_local_out(struct sk_buff *skb)
+int ip6_local_out_sk(struct sock *sk, struct sk_buff *skb)
 {
        int err;
 
-       err = __ip6_local_out(skb);
+       err = __ip6_local_out_sk(sk, skb);
        if (likely(err == 1))
-               err = dst_output(skb);
+               err = dst_output_sk(sk, skb);
 
        return err;
 }
+EXPORT_SYMBOL_GPL(ip6_local_out_sk);
+
+int ip6_local_out(struct sk_buff *skb)
+{
+       return ip6_local_out_sk(skb->sk, skb);
+}
 EXPORT_SYMBOL_GPL(ip6_local_out);
index 79ccdb4c1b336bca0f9ed72dfdd4ea02fbe6a01a..8072bd4139b7576a797bdebd6c3c5b75c8412582 100644 (file)
@@ -652,8 +652,8 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length,
                goto error_fault;
 
        IP6_UPD_PO_STATS(sock_net(sk), rt->rt6i_idev, IPSTATS_MIB_OUT, skb->len);
-       err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL,
-                     rt->dst.dev, dst_output);
+       err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb,
+                     NULL, rt->dst.dev, dst_output_sk);
        if (err > 0)
                err = net_xmit_errno(err);
        if (err)
index 6cf2026a9cea849c067d7388bc23f5d3a20cbeac..ac35a28599be557cac1114cad35e3c47c41436c1 100644 (file)
@@ -983,7 +983,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
 
        skb_set_inner_ipproto(skb, IPPROTO_IPV6);
 
-       err = iptunnel_xmit(skb->sk, rt, skb, fl4.saddr, fl4.daddr,
+       err = iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr,
                            protocol, tos, ttl, df,
                            !net_eq(tunnel->net, dev_net(dev)));
        iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
index f73a97f6e68ec8286972fadcf9328e29af123242..ad51df85aa00dda56b86dca24ed5fe9a3b5c791e 100644 (file)
@@ -1486,7 +1486,7 @@ do_time_wait:
                                            ntohs(th->dest), tcp_v6_iif(skb));
                if (sk2) {
                        struct inet_timewait_sock *tw = inet_twsk(sk);
-                       inet_twsk_deschedule(tw, &tcp_death_row);
+                       inet_twsk_deschedule(tw);
                        inet_twsk_put(tw);
                        sk = sk2;
                        tcp_v6_restore_cb(skb);
@@ -1728,9 +1728,9 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
 static void get_timewait6_sock(struct seq_file *seq,
                               struct inet_timewait_sock *tw, int i)
 {
+       long delta = tw->tw_timer.expires - jiffies;
        const struct in6_addr *dest, *src;
        __u16 destp, srcp;
-       s32 delta = tw->tw_ttd - inet_tw_time_stamp();
 
        dest = &tw->tw_v6_daddr;
        src  = &tw->tw_v6_rcv_saddr;
index 120aff9aa010f3e3b4d5b04b290324f16eecc5d9..3477c919fcc8eb534c3a438ab6d607a6215897f8 100644 (file)
@@ -120,7 +120,6 @@ static u32 udp6_portaddr_hash(const struct net *net,
        return hash ^ port;
 }
 
-
 int udp_v6_get_port(struct sock *sk, unsigned short snum)
 {
        unsigned int hash2_nulladdr =
@@ -385,7 +384,6 @@ struct sock *udp6_lib_lookup(struct net *net, const struct in6_addr *saddr, __be
 }
 EXPORT_SYMBOL_GPL(udp6_lib_lookup);
 
-
 /*
  *     This should be easy, if there is something there we
  *     return it, otherwise we block.
@@ -1555,7 +1553,6 @@ static struct inet_protosw udpv6_protosw = {
        .flags =     INET_PROTOSW_PERMANENT,
 };
 
-
 int __init udpv6_init(void)
 {
        int ret;
index f48fbe4d16f5f433c40cba8077663db77d2984d4..74bd17882a2fe5126012fae7625254a56f14e20d 100644 (file)
@@ -42,7 +42,8 @@ int xfrm6_transport_finish(struct sk_buff *skb, int async)
        ipv6_hdr(skb)->payload_len = htons(skb->len);
        __skb_push(skb, skb->data - skb_network_header(skb));
 
-       NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, skb, skb->dev, NULL,
+       NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, NULL, skb,
+               skb->dev, NULL,
                ip6_rcv_finish);
        return -1;
 }
index 010f8bd2d577f9767d7d44182b246e62f4a118f6..09c76a7b474dbcb12cae8aeba6fcba375d0d329a 100644 (file)
@@ -120,7 +120,7 @@ int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
 }
 EXPORT_SYMBOL(xfrm6_prepare_output);
 
-int xfrm6_output_finish(struct sk_buff *skb)
+int xfrm6_output_finish(struct sock *sk, struct sk_buff *skb)
 {
        memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
 
@@ -128,10 +128,10 @@ int xfrm6_output_finish(struct sk_buff *skb)
        IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED;
 #endif
 
-       return xfrm_output(skb);
+       return xfrm_output(sk, skb);
 }
 
-static int __xfrm6_output(struct sk_buff *skb)
+static int __xfrm6_output(struct sock *sk, struct sk_buff *skb)
 {
        struct dst_entry *dst = skb_dst(skb);
        struct xfrm_state *x = dst->xfrm;
@@ -140,7 +140,7 @@ static int __xfrm6_output(struct sk_buff *skb)
 #ifdef CONFIG_NETFILTER
        if (!x) {
                IP6CB(skb)->flags |= IP6SKB_REROUTED;
-               return dst_output(skb);
+               return dst_output_sk(sk, skb);
        }
 #endif
 
@@ -160,14 +160,15 @@ static int __xfrm6_output(struct sk_buff *skb)
        if (x->props.mode == XFRM_MODE_TUNNEL &&
            ((skb->len > mtu && !skb_is_gso(skb)) ||
                dst_allfrag(skb_dst(skb)))) {
-                       return ip6_fragment(skb, x->outer_mode->afinfo->output_finish);
+               return ip6_fragment(sk, skb,
+                                   x->outer_mode->afinfo->output_finish);
        }
-       return x->outer_mode->afinfo->output_finish(skb);
+       return x->outer_mode->afinfo->output_finish(sk, skb);
 }
 
 int xfrm6_output(struct sock *sk, struct sk_buff *skb)
 {
-       return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, skb,
+       return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, sk, skb,
                            NULL, skb_dst(skb)->dev, __xfrm6_output,
                            !(IP6CB(skb)->flags & IP6SKB_REROUTED));
 }
index 20522492d8cc80028b81dc772d32468951402ac6..cce9d425c71812f0f6184a0c953d2d39f6d7eb83 100644 (file)
@@ -188,6 +188,43 @@ ieee80211_wake_queue_agg(struct ieee80211_sub_if_data *sdata, int tid)
        __release(agg_queue);
 }
 
+static void
+ieee80211_agg_stop_txq(struct sta_info *sta, int tid)
+{
+       struct ieee80211_txq *txq = sta->sta.txq[tid];
+       struct txq_info *txqi;
+
+       if (!txq)
+               return;
+
+       txqi = to_txq_info(txq);
+
+       /* Lock here to protect against further seqno updates on dequeue */
+       spin_lock_bh(&txqi->queue.lock);
+       set_bit(IEEE80211_TXQ_STOP, &txqi->flags);
+       spin_unlock_bh(&txqi->queue.lock);
+}
+
+static void
+ieee80211_agg_start_txq(struct sta_info *sta, int tid, bool enable)
+{
+       struct ieee80211_txq *txq = sta->sta.txq[tid];
+       struct txq_info *txqi;
+
+       if (!txq)
+               return;
+
+       txqi = to_txq_info(txq);
+
+       if (enable)
+               set_bit(IEEE80211_TXQ_AMPDU, &txqi->flags);
+       else
+               clear_bit(IEEE80211_TXQ_AMPDU, &txqi->flags);
+
+       clear_bit(IEEE80211_TXQ_STOP, &txqi->flags);
+       drv_wake_tx_queue(sta->sdata->local, txqi);
+}
+
 /*
  * splice packets from the STA's pending to the local pending,
  * requires a call to ieee80211_agg_splice_finish later
@@ -247,6 +284,7 @@ static void ieee80211_remove_tid_tx(struct sta_info *sta, int tid)
        ieee80211_assign_tid_tx(sta, tid, NULL);
 
        ieee80211_agg_splice_finish(sta->sdata, tid);
+       ieee80211_agg_start_txq(sta, tid, false);
 
        kfree_rcu(tid_tx, rcu_head);
 }
@@ -418,6 +456,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
         */
        clear_bit(HT_AGG_STATE_WANT_START, &tid_tx->state);
 
+       ieee80211_agg_stop_txq(sta, tid);
+
        /*
         * Make sure no packets are being processed. This ensures that
         * we have a valid starting sequence number and that in-flight
@@ -440,6 +480,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
                ieee80211_agg_splice_finish(sdata, tid);
                spin_unlock_bh(&sta->lock);
 
+               ieee80211_agg_start_txq(sta, tid, false);
+
                kfree_rcu(tid_tx, rcu_head);
                return;
        }
@@ -669,6 +711,8 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local,
        ieee80211_agg_splice_finish(sta->sdata, tid);
 
        spin_unlock_bh(&sta->lock);
+
+       ieee80211_agg_start_txq(sta, tid, true);
 }
 
 void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid)
index 0a39d3db951a4a411448039c0d80190d50f299e2..26e1ca8a474af338685debf3d89b3cc3ad6d9986 100644 (file)
@@ -1367,4 +1367,16 @@ drv_tdls_recv_channel_switch(struct ieee80211_local *local,
        trace_drv_return_void(local);
 }
 
+static inline void drv_wake_tx_queue(struct ieee80211_local *local,
+                                    struct txq_info *txq)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->txq.vif);
+
+       if (!check_sdata_in_driver(sdata))
+               return;
+
+       trace_drv_wake_tx_queue(local, sdata, txq);
+       local->ops->wake_tx_queue(&local->hw, &txq->txq);
+}
+
 #endif /* __MAC80211_DRIVER_OPS */
index 487f5e2a9283bd1819adb0f61fead485f7fa6b0f..ab46ab4a72498fd04f1c12ac6bb44f867d86869b 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/etherdevice.h>
 #include <linux/leds.h>
 #include <linux/idr.h>
+#include <linux/rhashtable.h>
 #include <net/ieee80211_radiotap.h>
 #include <net/cfg80211.h>
 #include <net/mac80211.h>
@@ -810,6 +811,19 @@ struct mac80211_qos_map {
        struct rcu_head rcu_head;
 };
 
+enum txq_info_flags {
+       IEEE80211_TXQ_STOP,
+       IEEE80211_TXQ_AMPDU,
+};
+
+struct txq_info {
+       struct sk_buff_head queue;
+       unsigned long flags;
+
+       /* keep last! */
+       struct ieee80211_txq txq;
+};
+
 struct ieee80211_sub_if_data {
        struct list_head list;
 
@@ -852,6 +866,7 @@ struct ieee80211_sub_if_data {
        bool control_port_no_encrypt;
        int encrypt_headroom;
 
+       atomic_t txqs_len[IEEE80211_NUM_ACS];
        struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
        struct mac80211_qos_map __rcu *qos_map;
 
@@ -1187,7 +1202,7 @@ struct ieee80211_local {
        spinlock_t tim_lock;
        unsigned long num_sta;
        struct list_head sta_list;
-       struct sta_info __rcu *sta_hash[STA_HASH_SIZE];
+       struct rhashtable sta_hash;
        struct timer_list sta_cleanup;
        int sta_generation;
 
@@ -1449,6 +1464,10 @@ static inline struct ieee80211_local *hw_to_local(
        return container_of(hw, struct ieee80211_local, hw);
 }
 
+static inline struct txq_info *to_txq_info(struct ieee80211_txq *txq)
+{
+       return container_of(txq, struct txq_info, txq);
+}
 
 static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
 {
@@ -1905,6 +1924,9 @@ static inline bool ieee80211_can_run_worker(struct ieee80211_local *local)
        return true;
 }
 
+void ieee80211_init_tx_queue(struct ieee80211_sub_if_data *sdata,
+                            struct sta_info *sta,
+                            struct txq_info *txq, int tid);
 void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
                         u16 transaction, u16 auth_alg, u16 status,
                         const u8 *extra, size_t extra_len, const u8 *bssid,
@@ -1943,10 +1965,6 @@ int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
 void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata);
 void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata);
 
-size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
-                             const u8 *ids, int n_ids,
-                             const u8 *after_ric, int n_after_ric,
-                             size_t offset);
 size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset);
 u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
                              u16 cap);
index a0cd97fd0c49075d8682fadf734c18f3c018f13f..b4ac596a7cb76205cf39d935708d3383490c2b73 100644 (file)
@@ -969,6 +969,13 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
        }
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 
+       if (sdata->vif.txq) {
+               struct txq_info *txqi = to_txq_info(sdata->vif.txq);
+
+               ieee80211_purge_tx_queue(&local->hw, &txqi->queue);
+               atomic_set(&sdata->txqs_len[txqi->txq.ac], 0);
+       }
+
        if (local->open_count == 0)
                ieee80211_clear_tx_pending(local);
 
@@ -1654,6 +1661,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
 {
        struct net_device *ndev = NULL;
        struct ieee80211_sub_if_data *sdata = NULL;
+       struct txq_info *txqi;
        int ret, i;
        int txqs = 1;
 
@@ -1673,10 +1681,18 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
                ieee80211_assign_perm_addr(local, wdev->address, type);
                memcpy(sdata->vif.addr, wdev->address, ETH_ALEN);
        } else {
+               int size = ALIGN(sizeof(*sdata) + local->hw.vif_data_size,
+                                sizeof(void *));
+               int txq_size = 0;
+
+               if (local->ops->wake_tx_queue)
+                       txq_size += sizeof(struct txq_info) +
+                                   local->hw.txq_data_size;
+
                if (local->hw.queues >= IEEE80211_NUM_ACS)
                        txqs = IEEE80211_NUM_ACS;
 
-               ndev = alloc_netdev_mqs(sizeof(*sdata) + local->hw.vif_data_size,
+               ndev = alloc_netdev_mqs(size + txq_size,
                                        name, name_assign_type,
                                        ieee80211_if_setup, txqs, 1);
                if (!ndev)
@@ -1711,6 +1727,11 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
                memcpy(sdata->vif.addr, ndev->dev_addr, ETH_ALEN);
                memcpy(sdata->name, ndev->name, IFNAMSIZ);
 
+               if (txq_size) {
+                       txqi = netdev_priv(ndev) + size;
+                       ieee80211_init_tx_queue(sdata, NULL, txqi, 0);
+               }
+
                sdata->dev = ndev;
        }
 
index 4977967c8b0076a5960f3fc74e37271583d983fc..df3051d96afffbd1257442a16c3a2f198629fbcc 100644 (file)
@@ -557,6 +557,9 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
 
        local = wiphy_priv(wiphy);
 
+       if (sta_info_init(local))
+               goto err_free;
+
        local->hw.wiphy = wiphy;
 
        local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN);
@@ -629,8 +632,6 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
        spin_lock_init(&local->ack_status_lock);
        idr_init(&local->ack_status_frames);
 
-       sta_info_init(local);
-
        for (i = 0; i < IEEE80211_MAX_QUEUES; i++) {
                skb_queue_head_init(&local->pending[i]);
                atomic_set(&local->agg_queue_stop[i], 0);
@@ -650,6 +651,9 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
        ieee80211_roc_setup(local);
 
        return &local->hw;
+ err_free:
+       wiphy_free(wiphy);
+       return NULL;
 }
 EXPORT_SYMBOL(ieee80211_alloc_hw_nm);
 
@@ -1035,6 +1039,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 
        local->dynamic_ps_forced_timeout = -1;
 
+       if (!local->hw.txq_ac_max_pending)
+               local->hw.txq_ac_max_pending = 64;
+
        result = ieee80211_wep_init(local);
        if (result < 0)
                wiphy_debug(local->hw.wiphy, "Failed to initialize wep: %d\n",
@@ -1173,7 +1180,6 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
 
        destroy_workqueue(local->workqueue);
        wiphy_unregister(local->hw.wiphy);
-       sta_info_stop(local);
        ieee80211_wep_free(local);
        ieee80211_led_exit(local);
        kfree(local->int_scan_req);
index 00103f36dcbf4f5866c40892cd76882ce8022be8..26053bf2faa8ff441d0ff0c9905536bccf914281 100644 (file)
@@ -1348,15 +1348,15 @@ static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
         */
        if (has_80211h_pwr &&
            (!has_cisco_pwr || pwr_level_80211h <= pwr_level_cisco)) {
-               sdata_info(sdata,
-                          "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
-                          pwr_level_80211h, chan_pwr, pwr_reduction_80211h,
-                          sdata->u.mgd.bssid);
+               sdata_dbg(sdata,
+                         "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
+                         pwr_level_80211h, chan_pwr, pwr_reduction_80211h,
+                         sdata->u.mgd.bssid);
                new_ap_level = pwr_level_80211h;
        } else {  /* has_cisco_pwr is always true here. */
-               sdata_info(sdata,
-                          "Limiting TX power to %d dBm as advertised by %pM\n",
-                          pwr_level_cisco, sdata->u.mgd.bssid);
+               sdata_dbg(sdata,
+                         "Limiting TX power to %d dBm as advertised by %pM\n",
+                         pwr_level_cisco, sdata->u.mgd.bssid);
                new_ap_level = pwr_level_cisco;
        }
 
index ef6e8a6c4253c72f6f2398b733e8c427f5b59953..247552a7f6c2f23a1e4bc89b647d8d37680bf2c3 100644 (file)
@@ -69,14 +69,39 @@ rix_to_ndx(struct minstrel_sta_info *mi, int rix)
        return i;
 }
 
+/* return current EMWA throughput */
+int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_ewma)
+{
+       int usecs;
+
+       usecs = mr->perfect_tx_time;
+       if (!usecs)
+               usecs = 1000000;
+
+       /* reset thr. below 10% success */
+       if (mr->stats.prob_ewma < MINSTREL_FRAC(10, 100))
+               return 0;
+
+       if (prob_ewma > MINSTREL_FRAC(90, 100))
+               return MINSTREL_TRUNC(100000 * (MINSTREL_FRAC(90, 100) / usecs));
+       else
+               return MINSTREL_TRUNC(100000 * (prob_ewma / usecs));
+}
+
 /* find & sort topmost throughput rates */
 static inline void
 minstrel_sort_best_tp_rates(struct minstrel_sta_info *mi, int i, u8 *tp_list)
 {
        int j = MAX_THR_RATES;
+       struct minstrel_rate_stats *tmp_mrs = &mi->r[j - 1].stats;
+       struct minstrel_rate_stats *cur_mrs = &mi->r[i].stats;
 
-       while (j > 0 && mi->r[i].stats.cur_tp > mi->r[tp_list[j - 1]].stats.cur_tp)
+       while (j > 0 && (minstrel_get_tp_avg(&mi->r[i], cur_mrs->prob_ewma) >
+              minstrel_get_tp_avg(&mi->r[tp_list[j - 1]], tmp_mrs->prob_ewma))) {
                j--;
+               tmp_mrs = &mi->r[tp_list[j - 1]].stats;
+       }
+
        if (j < MAX_THR_RATES - 1)
                memmove(&tp_list[j + 1], &tp_list[j], MAX_THR_RATES - (j + 1));
        if (j < MAX_THR_RATES)
@@ -127,13 +152,47 @@ minstrel_update_rates(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
        rate_control_set_rates(mp->hw, mi->sta, ratetbl);
 }
 
+/*
+* Recalculate statistics and counters of a given rate
+*/
+void
+minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs)
+{
+       if (unlikely(mrs->attempts > 0)) {
+               mrs->sample_skipped = 0;
+               mrs->cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts);
+               if (unlikely(!mrs->att_hist)) {
+                       mrs->prob_ewma = mrs->cur_prob;
+               } else {
+                       /* update exponential weighted moving variance */
+                       mrs->prob_ewmsd = minstrel_ewmsd(mrs->prob_ewmsd,
+                                                        mrs->cur_prob,
+                                                        mrs->prob_ewma,
+                                                        EWMA_LEVEL);
+
+                       /*update exponential weighted moving avarage */
+                       mrs->prob_ewma = minstrel_ewma(mrs->prob_ewma,
+                                                      mrs->cur_prob,
+                                                      EWMA_LEVEL);
+               }
+               mrs->att_hist += mrs->attempts;
+               mrs->succ_hist += mrs->success;
+       } else {
+               mrs->sample_skipped++;
+       }
+
+       mrs->last_success = mrs->success;
+       mrs->last_attempts = mrs->attempts;
+       mrs->success = 0;
+       mrs->attempts = 0;
+}
+
 static void
 minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
 {
        u8 tmp_tp_rate[MAX_THR_RATES];
        u8 tmp_prob_rate = 0;
-       u32 usecs;
-       int i;
+       int i, tmp_cur_tp, tmp_prob_tp;
 
        for (i = 0; i < MAX_THR_RATES; i++)
            tmp_tp_rate[i] = 0;
@@ -141,38 +200,15 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
        for (i = 0; i < mi->n_rates; i++) {
                struct minstrel_rate *mr = &mi->r[i];
                struct minstrel_rate_stats *mrs = &mi->r[i].stats;
+               struct minstrel_rate_stats *tmp_mrs = &mi->r[tmp_prob_rate].stats;
 
-               usecs = mr->perfect_tx_time;
-               if (!usecs)
-                       usecs = 1000000;
-
-               if (unlikely(mrs->attempts > 0)) {
-                       mrs->sample_skipped = 0;
-                       mrs->cur_prob = MINSTREL_FRAC(mrs->success,
-                                                     mrs->attempts);
-                       mrs->succ_hist += mrs->success;
-                       mrs->att_hist += mrs->attempts;
-                       mrs->probability = minstrel_ewma(mrs->probability,
-                                                        mrs->cur_prob,
-                                                        EWMA_LEVEL);
-               } else
-                       mrs->sample_skipped++;
-
-               mrs->last_success = mrs->success;
-               mrs->last_attempts = mrs->attempts;
-               mrs->success = 0;
-               mrs->attempts = 0;
-
-               /* Update throughput per rate, reset thr. below 10% success */
-               if (mrs->probability < MINSTREL_FRAC(10, 100))
-                       mrs->cur_tp = 0;
-               else
-                       mrs->cur_tp = mrs->probability * (1000000 / usecs);
+               /* Update statistics of success probability per rate */
+               minstrel_calc_rate_stats(mrs);
 
                /* Sample less often below the 10% chance of success.
                 * Sample less often above the 95% chance of success. */
-               if (mrs->probability > MINSTREL_FRAC(95, 100) ||
-                   mrs->probability < MINSTREL_FRAC(10, 100)) {
+               if (mrs->prob_ewma > MINSTREL_FRAC(95, 100) ||
+                   mrs->prob_ewma < MINSTREL_FRAC(10, 100)) {
                        mr->adjusted_retry_count = mrs->retry_count >> 1;
                        if (mr->adjusted_retry_count > 2)
                                mr->adjusted_retry_count = 2;
@@ -192,11 +228,14 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
                 * choose the maximum throughput rate as max_prob_rate
                 * (2) if all success probabilities < 95%, the rate with
                 * highest success probability is chosen as max_prob_rate */
-               if (mrs->probability >= MINSTREL_FRAC(95, 100)) {
-                       if (mrs->cur_tp >= mi->r[tmp_prob_rate].stats.cur_tp)
+               if (mrs->prob_ewma >= MINSTREL_FRAC(95, 100)) {
+                       tmp_cur_tp = minstrel_get_tp_avg(mr, mrs->prob_ewma);
+                       tmp_prob_tp = minstrel_get_tp_avg(&mi->r[tmp_prob_rate],
+                                                         tmp_mrs->prob_ewma);
+                       if (tmp_cur_tp >= tmp_prob_tp)
                                tmp_prob_rate = i;
                } else {
-                       if (mrs->probability >= mi->r[tmp_prob_rate].stats.probability)
+                       if (mrs->prob_ewma >= tmp_mrs->prob_ewma)
                                tmp_prob_rate = i;
                }
        }
@@ -215,7 +254,7 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
 #endif
 
        /* Reset update timer */
-       mi->stats_update = jiffies;
+       mi->last_stats_update = jiffies;
 
        minstrel_update_rates(mp, mi);
 }
@@ -253,7 +292,7 @@ minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband,
        if (mi->sample_deferred > 0)
                mi->sample_deferred--;
 
-       if (time_after(jiffies, mi->stats_update +
+       if (time_after(jiffies, mi->last_stats_update +
                                (mp->update_interval * HZ) / 1000))
                minstrel_update_stats(mp, mi);
 }
@@ -385,7 +424,7 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
         * has a probability of >95%, we shouldn't be attempting
         * to use it, as this only wastes precious airtime */
        if (!mrr_capable &&
-          (mi->r[ndx].stats.probability > MINSTREL_FRAC(95, 100)))
+          (mi->r[ndx].stats.prob_ewma > MINSTREL_FRAC(95, 100)))
                return;
 
        mi->prev_sample = true;
@@ -519,7 +558,7 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
        }
 
        mi->n_rates = n;
-       mi->stats_update = jiffies;
+       mi->last_stats_update = jiffies;
 
        init_sample_table(mi);
        minstrel_update_rates(mp, mi);
@@ -553,7 +592,7 @@ minstrel_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp)
        if (!mi->sample_table)
                goto error1;
 
-       mi->stats_update = jiffies;
+       mi->last_stats_update = jiffies;
        return mi;
 
 error1:
@@ -663,12 +702,18 @@ minstrel_free(void *priv)
 static u32 minstrel_get_expected_throughput(void *priv_sta)
 {
        struct minstrel_sta_info *mi = priv_sta;
+       struct minstrel_rate_stats *tmp_mrs;
        int idx = mi->max_tp_rate[0];
+       int tmp_cur_tp;
 
        /* convert pkt per sec in kbps (1200 is the average pkt size used for
         * computing cur_tp
         */
-       return MINSTREL_TRUNC(mi->r[idx].stats.cur_tp) * 1200 * 8 / 1024;
+       tmp_mrs = &mi->r[idx].stats;
+       tmp_cur_tp = minstrel_get_tp_avg(&mi->r[idx], tmp_mrs->prob_ewma);
+       tmp_cur_tp = tmp_cur_tp * 1200 * 8 / 1024;
+
+       return tmp_cur_tp;
 }
 
 const struct rate_control_ops mac80211_minstrel = {
index 410efe620c579cb2d3eaa279384b7d0bf3907846..c230bbe93262b0affc476c01b8d1db9ab7dfe054 100644 (file)
@@ -13,7 +13,6 @@
 #define EWMA_DIV       128
 #define SAMPLE_COLUMNS 10      /* number of columns in sample table */
 
-
 /* scaled fraction values */
 #define MINSTREL_SCALE  16
 #define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div)
 
 /*
  * Perform EWMA (Exponentially Weighted Moving Average) calculation
 */
+ */
 static inline int
 minstrel_ewma(int old, int new, int weight)
 {
-       return (new * (EWMA_DIV - weight) + old * weight) / EWMA_DIV;
+       int diff, incr;
+
+       diff = new - old;
+       incr = (EWMA_DIV - weight) * diff / EWMA_DIV;
+
+       return old + incr;
+}
+
+/*
+ * Perform EWMSD (Exponentially Weighted Moving Standard Deviation) calculation
+ */
+static inline int
+minstrel_ewmsd(int old_ewmsd, int cur_prob, int prob_ewma, int weight)
+{
+       int diff, incr, tmp_var;
+
+       /* calculate exponential weighted moving variance */
+       diff = MINSTREL_TRUNC((cur_prob - prob_ewma) * 1000000);
+       incr = (EWMA_DIV - weight) * diff / EWMA_DIV;
+       tmp_var = old_ewmsd * old_ewmsd;
+       tmp_var = weight * (tmp_var + diff * incr / 1000000) / EWMA_DIV;
+
+       /* return standard deviation */
+       return (u16) int_sqrt(tmp_var);
 }
 
 struct minstrel_rate_stats {
@@ -39,11 +61,13 @@ struct minstrel_rate_stats {
        /* total attempts/success counters */
        u64 att_hist, succ_hist;
 
-       /* current throughput */
-       unsigned int cur_tp;
-
-       /* packet delivery probabilities */
-       unsigned int cur_prob, probability;
+       /* statistis of packet delivery probability
+        *  cur_prob  - current prob within last update intervall
+        *  prob_ewma - exponential weighted moving average of prob
+        *  prob_ewmsd - exp. weighted moving standard deviation of prob */
+       unsigned int cur_prob;
+       unsigned int prob_ewma;
+       u16 prob_ewmsd;
 
        /* maximum retry counts */
        u8 retry_count;
@@ -71,7 +95,7 @@ struct minstrel_rate {
 struct minstrel_sta_info {
        struct ieee80211_sta *sta;
 
-       unsigned long stats_update;
+       unsigned long last_stats_update;
        unsigned int sp_ack_dur;
        unsigned int rate_avg;
 
@@ -95,6 +119,7 @@ struct minstrel_sta_info {
 
 #ifdef CONFIG_MAC80211_DEBUGFS
        struct dentry *dbg_stats;
+       struct dentry *dbg_stats_csv;
 #endif
 };
 
@@ -121,7 +146,6 @@ struct minstrel_priv {
        u32 fixed_rate_idx;
        struct dentry *dbg_fixed_rate;
 #endif
-
 };
 
 struct minstrel_debugfs_info {
@@ -133,8 +157,13 @@ extern const struct rate_control_ops mac80211_minstrel;
 void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir);
 void minstrel_remove_sta_debugfs(void *priv, void *priv_sta);
 
+/* Recalculate success probabilities and counters for a given rate using EWMA */
+void minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs);
+int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_ewma);
+
 /* debugfs */
 int minstrel_stats_open(struct inode *inode, struct file *file);
+int minstrel_stats_csv_open(struct inode *inode, struct file *file);
 ssize_t minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos);
 int minstrel_stats_release(struct inode *inode, struct file *file);
 
index 2acab1bcaa4b377012a31c6354ceeb61fadbb171..1db5f7c3318ada6a1a1aede778dee49ccc99e824 100644 (file)
 #include <net/mac80211.h>
 #include "rc80211_minstrel.h"
 
+ssize_t
+minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos)
+{
+       struct minstrel_debugfs_info *ms;
+
+       ms = file->private_data;
+       return simple_read_from_buffer(buf, len, ppos, ms->buf, ms->len);
+}
+
+int
+minstrel_stats_release(struct inode *inode, struct file *file)
+{
+       kfree(file->private_data);
+       return 0;
+}
+
 int
 minstrel_stats_open(struct inode *inode, struct file *file)
 {
        struct minstrel_sta_info *mi = inode->i_private;
        struct minstrel_debugfs_info *ms;
-       unsigned int i, tp, prob, eprob;
+       unsigned int i, tp_max, tp_avg, prob, eprob;
        char *p;
 
        ms = kmalloc(2048, GFP_KERNEL);
@@ -68,8 +84,14 @@ minstrel_stats_open(struct inode *inode, struct file *file)
 
        file->private_data = ms;
        p = ms->buf;
-       p += sprintf(p, "rate          tpt eprob *prob"
-                       "  *ok(*cum)        ok(      cum)\n");
+       p += sprintf(p, "\n");
+       p += sprintf(p, "best   __________rate_________    ______"
+                       "statistics______    ________last_______    "
+                       "______sum-of________\n");
+       p += sprintf(p, "rate  [name idx airtime max_tp]  [ Ã¸(tp) Ã¸(prob) "
+                       "sd(prob)]  [prob.|retry|suc|att]  "
+                       "[#success | #attempts]\n");
+
        for (i = 0; i < mi->n_rates; i++) {
                struct minstrel_rate *mr = &mi->r[i];
                struct minstrel_rate_stats *mrs = &mi->r[i].stats;
@@ -79,18 +101,26 @@ minstrel_stats_open(struct inode *inode, struct file *file)
                *(p++) = (i == mi->max_tp_rate[2]) ? 'C' : ' ';
                *(p++) = (i == mi->max_tp_rate[3]) ? 'D' : ' ';
                *(p++) = (i == mi->max_prob_rate) ? 'P' : ' ';
-               p += sprintf(p, "%3u%s", mr->bitrate / 2,
+
+               p += sprintf(p, " %3u%s ", mr->bitrate / 2,
                                (mr->bitrate & 1 ? ".5" : "  "));
+               p += sprintf(p, "%3u  ", i);
+               p += sprintf(p, "%6u ", mr->perfect_tx_time);
 
-               tp = MINSTREL_TRUNC(mrs->cur_tp / 10);
+               tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100));
+               tp_avg = minstrel_get_tp_avg(mr, mrs->prob_ewma);
                prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
-               eprob = MINSTREL_TRUNC(mrs->probability * 1000);
+               eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
 
-               p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u"
-                               " %4u(%4u) %9llu(%9llu)\n",
-                               tp / 10, tp % 10,
+               p += sprintf(p, "%4u.%1u   %4u.%1u   %3u.%1u    %3u.%1u"
+                               "     %3u.%1u %3u   %3u %-3u   "
+                               "%9llu   %-9llu\n",
+                               tp_max / 10, tp_max % 10,
+                               tp_avg / 10, tp_avg % 10,
                                eprob / 10, eprob % 10,
+                               mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10,
                                prob / 10, prob % 10,
+                               mrs->retry_count,
                                mrs->last_success,
                                mrs->last_attempts,
                                (unsigned long long)mrs->succ_hist,
@@ -107,25 +137,75 @@ minstrel_stats_open(struct inode *inode, struct file *file)
        return 0;
 }
 
-ssize_t
-minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos)
+static const struct file_operations minstrel_stat_fops = {
+       .owner = THIS_MODULE,
+       .open = minstrel_stats_open,
+       .read = minstrel_stats_read,
+       .release = minstrel_stats_release,
+       .llseek = default_llseek,
+};
+
+int
+minstrel_stats_csv_open(struct inode *inode, struct file *file)
 {
+       struct minstrel_sta_info *mi = inode->i_private;
        struct minstrel_debugfs_info *ms;
+       unsigned int i, tp_max, tp_avg, prob, eprob;
+       char *p;
 
-       ms = file->private_data;
-       return simple_read_from_buffer(buf, len, ppos, ms->buf, ms->len);
-}
+       ms = kmalloc(2048, GFP_KERNEL);
+       if (!ms)
+               return -ENOMEM;
+
+       file->private_data = ms;
+       p = ms->buf;
+
+       for (i = 0; i < mi->n_rates; i++) {
+               struct minstrel_rate *mr = &mi->r[i];
+               struct minstrel_rate_stats *mrs = &mi->r[i].stats;
+
+               p += sprintf(p, "%s" ,((i == mi->max_tp_rate[0]) ? "A" : ""));
+               p += sprintf(p, "%s" ,((i == mi->max_tp_rate[1]) ? "B" : ""));
+               p += sprintf(p, "%s" ,((i == mi->max_tp_rate[2]) ? "C" : ""));
+               p += sprintf(p, "%s" ,((i == mi->max_tp_rate[3]) ? "D" : ""));
+               p += sprintf(p, "%s" ,((i == mi->max_prob_rate) ? "P" : ""));
+
+               p += sprintf(p, ",%u%s", mr->bitrate / 2,
+                               (mr->bitrate & 1 ? ".5," : ","));
+               p += sprintf(p, "%u,", i);
+               p += sprintf(p, "%u,",mr->perfect_tx_time);
+
+               tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100));
+               tp_avg = minstrel_get_tp_avg(mr, mrs->prob_ewma);
+               prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
+               eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
+
+               p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,%u,"
+                               "%llu,%llu,%d,%d\n",
+                               tp_max / 10, tp_max % 10,
+                               tp_avg / 10, tp_avg % 10,
+                               eprob / 10, eprob % 10,
+                               mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10,
+                               prob / 10, prob % 10,
+                               mrs->retry_count,
+                               mrs->last_success,
+                               mrs->last_attempts,
+                               (unsigned long long)mrs->succ_hist,
+                               (unsigned long long)mrs->att_hist,
+                               mi->total_packets - mi->sample_packets,
+                               mi->sample_packets);
+
+       }
+       ms->len = p - ms->buf;
+
+       WARN_ON(ms->len + sizeof(*ms) > 2048);
 
-int
-minstrel_stats_release(struct inode *inode, struct file *file)
-{
-       kfree(file->private_data);
        return 0;
 }
 
-static const struct file_operations minstrel_stat_fops = {
+static const struct file_operations minstrel_stat_csv_fops = {
        .owner = THIS_MODULE,
-       .open = minstrel_stats_open,
+       .open = minstrel_stats_csv_open,
        .read = minstrel_stats_read,
        .release = minstrel_stats_release,
        .llseek = default_llseek,
@@ -138,6 +218,9 @@ minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir)
 
        mi->dbg_stats = debugfs_create_file("rc_stats", S_IRUGO, dir, mi,
                        &minstrel_stat_fops);
+
+       mi->dbg_stats_csv = debugfs_create_file("rc_stats_csv", S_IRUGO, dir,
+                       mi, &minstrel_stat_csv_fops);
 }
 
 void
@@ -146,4 +229,6 @@ minstrel_remove_sta_debugfs(void *priv, void *priv_sta)
        struct minstrel_sta_info *mi = priv_sta;
 
        debugfs_remove(mi->dbg_stats);
+
+       debugfs_remove(mi->dbg_stats_csv);
 }
index 60698fc7042e5d0568f4d65e8d5a10bb05de9e21..7430a1df2ab13df2c1b227952a1315678aa20ce9 100644 (file)
@@ -313,67 +313,35 @@ minstrel_get_ratestats(struct minstrel_ht_sta *mi, int index)
        return &mi->groups[index / MCS_GROUP_RATES].rates[index % MCS_GROUP_RATES];
 }
 
-
 /*
- * Recalculate success probabilities and counters for a rate using EWMA
+ * Return current throughput based on the average A-MPDU length, taking into
+ * account the expected number of retransmissions and their expected length
  */
-static void
-minstrel_calc_rate_ewma(struct minstrel_rate_stats *mr)
+int
+minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate,
+                      int prob_ewma)
 {
-       if (unlikely(mr->attempts > 0)) {
-               mr->sample_skipped = 0;
-               mr->cur_prob = MINSTREL_FRAC(mr->success, mr->attempts);
-               if (!mr->att_hist)
-                       mr->probability = mr->cur_prob;
-               else
-                       mr->probability = minstrel_ewma(mr->probability,
-                               mr->cur_prob, EWMA_LEVEL);
-               mr->att_hist += mr->attempts;
-               mr->succ_hist += mr->success;
-       } else {
-               mr->sample_skipped++;
-       }
-       mr->last_success = mr->success;
-       mr->last_attempts = mr->attempts;
-       mr->success = 0;
-       mr->attempts = 0;
-}
-
-/*
- * Calculate throughput based on the average A-MPDU length, taking into account
- * the expected number of retransmissions and their expected length
- */
-static void
-minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate)
-{
-       struct minstrel_rate_stats *mr;
        unsigned int nsecs = 0;
-       unsigned int tp;
-       unsigned int prob;
 
-       mr = &mi->groups[group].rates[rate];
-       prob = mr->probability;
-
-       if (prob < MINSTREL_FRAC(1, 10)) {
-               mr->cur_tp = 0;
-               return;
-       }
-
-       /*
-        * For the throughput calculation, limit the probability value to 90% to
-        * account for collision related packet error rate fluctuation
-        */
-       if (prob > MINSTREL_FRAC(9, 10))
-               prob = MINSTREL_FRAC(9, 10);
+       /* do not account throughput if sucess prob is below 10% */
+       if (prob_ewma < MINSTREL_FRAC(10, 100))
+               return 0;
 
        if (group != MINSTREL_CCK_GROUP)
                nsecs = 1000 * mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len);
 
        nsecs += minstrel_mcs_groups[group].duration[rate];
 
-       /* prob is scaled - see MINSTREL_FRAC above */
-       tp = 1000000 * ((prob * 1000) / nsecs);
-       mr->cur_tp = MINSTREL_TRUNC(tp);
+       /*
+        * For the throughput calculation, limit the probability value to 90% to
+        * account for collision related packet error rate fluctuation
+        * (prob is scaled - see MINSTREL_FRAC above)
+        */
+       if (prob_ewma > MINSTREL_FRAC(90, 100))
+               return MINSTREL_TRUNC(100000 * ((MINSTREL_FRAC(90, 100) * 1000)
+                                                                     / nsecs));
+       else
+               return MINSTREL_TRUNC(100000 * ((prob_ewma * 1000) / nsecs));
 }
 
 /*
@@ -387,22 +355,23 @@ static void
 minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u16 index,
                               u16 *tp_list)
 {
-       int cur_group, cur_idx, cur_thr, cur_prob;
-       int tmp_group, tmp_idx, tmp_thr, tmp_prob;
+       int cur_group, cur_idx, cur_tp_avg, cur_prob;
+       int tmp_group, tmp_idx, tmp_tp_avg, tmp_prob;
        int j = MAX_THR_RATES;
 
        cur_group = index / MCS_GROUP_RATES;
        cur_idx = index  % MCS_GROUP_RATES;
-       cur_thr = mi->groups[cur_group].rates[cur_idx].cur_tp;
-       cur_prob = mi->groups[cur_group].rates[cur_idx].probability;
+       cur_prob = mi->groups[cur_group].rates[cur_idx].prob_ewma;
+       cur_tp_avg = minstrel_ht_get_tp_avg(mi, cur_group, cur_idx, cur_prob);
 
        do {
                tmp_group = tp_list[j - 1] / MCS_GROUP_RATES;
                tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES;
-               tmp_thr = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
-               tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability;
-               if (cur_thr < tmp_thr ||
-                   (cur_thr == tmp_thr && cur_prob <= tmp_prob))
+               tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma;
+               tmp_tp_avg = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx,
+                                                   tmp_prob);
+               if (cur_tp_avg < tmp_tp_avg ||
+                   (cur_tp_avg == tmp_tp_avg && cur_prob <= tmp_prob))
                        break;
                j--;
        } while (j > 0);
@@ -422,16 +391,21 @@ static void
 minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index)
 {
        struct minstrel_mcs_group_data *mg;
-       struct minstrel_rate_stats *mr;
-       int tmp_group, tmp_idx, tmp_tp, tmp_prob, max_tp_group;
+       struct minstrel_rate_stats *mrs;
+       int tmp_group, tmp_idx, tmp_tp_avg, tmp_prob;
+       int max_tp_group, cur_tp_avg, cur_group, cur_idx;
+       int max_gpr_group, max_gpr_idx;
+       int max_gpr_tp_avg, max_gpr_prob;
 
+       cur_group = index / MCS_GROUP_RATES;
+       cur_idx = index % MCS_GROUP_RATES;
        mg = &mi->groups[index / MCS_GROUP_RATES];
-       mr = &mg->rates[index % MCS_GROUP_RATES];
+       mrs = &mg->rates[index % MCS_GROUP_RATES];
 
        tmp_group = mi->max_prob_rate / MCS_GROUP_RATES;
        tmp_idx = mi->max_prob_rate % MCS_GROUP_RATES;
-       tmp_tp = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
-       tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability;
+       tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma;
+       tmp_tp_avg = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob);
 
        /* if max_tp_rate[0] is from MCS_GROUP max_prob_rate get selected from
         * MCS_GROUP as well as CCK_GROUP rates do not allow aggregation */
@@ -440,15 +414,24 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index)
            (max_tp_group != MINSTREL_CCK_GROUP))
                return;
 
-       if (mr->probability > MINSTREL_FRAC(75, 100)) {
-               if (mr->cur_tp > tmp_tp)
+       if (mrs->prob_ewma > MINSTREL_FRAC(75, 100)) {
+               cur_tp_avg = minstrel_ht_get_tp_avg(mi, cur_group, cur_idx,
+                                                   mrs->prob_ewma);
+               if (cur_tp_avg > tmp_tp_avg)
                        mi->max_prob_rate = index;
-               if (mr->cur_tp > mg->rates[mg->max_group_prob_rate].cur_tp)
+
+               max_gpr_group = mg->max_group_prob_rate / MCS_GROUP_RATES;
+               max_gpr_idx = mg->max_group_prob_rate % MCS_GROUP_RATES;
+               max_gpr_prob = mi->groups[max_gpr_group].rates[max_gpr_idx].prob_ewma;
+               max_gpr_tp_avg = minstrel_ht_get_tp_avg(mi, max_gpr_group,
+                                                       max_gpr_idx,
+                                                       max_gpr_prob);
+               if (cur_tp_avg > max_gpr_tp_avg)
                        mg->max_group_prob_rate = index;
        } else {
-               if (mr->probability > tmp_prob)
+               if (mrs->prob_ewma > tmp_prob)
                        mi->max_prob_rate = index;
-               if (mr->probability > mg->rates[mg->max_group_prob_rate].probability)
+               if (mrs->prob_ewma > mg->rates[mg->max_group_prob_rate].prob_ewma)
                        mg->max_group_prob_rate = index;
        }
 }
@@ -465,16 +448,18 @@ minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi,
                                 u16 tmp_mcs_tp_rate[MAX_THR_RATES],
                                 u16 tmp_cck_tp_rate[MAX_THR_RATES])
 {
-       unsigned int tmp_group, tmp_idx, tmp_cck_tp, tmp_mcs_tp;
+       unsigned int tmp_group, tmp_idx, tmp_cck_tp, tmp_mcs_tp, tmp_prob;
        int i;
 
        tmp_group = tmp_cck_tp_rate[0] / MCS_GROUP_RATES;
        tmp_idx = tmp_cck_tp_rate[0] % MCS_GROUP_RATES;
-       tmp_cck_tp = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
+       tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma;
+       tmp_cck_tp = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob);
 
        tmp_group = tmp_mcs_tp_rate[0] / MCS_GROUP_RATES;
        tmp_idx = tmp_mcs_tp_rate[0] % MCS_GROUP_RATES;
-       tmp_mcs_tp = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
+       tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma;
+       tmp_mcs_tp = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob);
 
        if (tmp_cck_tp > tmp_mcs_tp) {
                for(i = 0; i < MAX_THR_RATES; i++) {
@@ -493,8 +478,7 @@ static inline void
 minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi)
 {
        struct minstrel_mcs_group_data *mg;
-       struct minstrel_rate_stats *mr;
-       int tmp_max_streams, group;
+       int tmp_max_streams, group, tmp_idx, tmp_prob;
        int tmp_tp = 0;
 
        tmp_max_streams = minstrel_mcs_groups[mi->max_tp_rate[0] /
@@ -503,11 +487,16 @@ minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi)
                mg = &mi->groups[group];
                if (!mg->supported || group == MINSTREL_CCK_GROUP)
                        continue;
-               mr = minstrel_get_ratestats(mi, mg->max_group_prob_rate);
-               if (tmp_tp < mr->cur_tp &&
+
+               tmp_idx = mg->max_group_prob_rate % MCS_GROUP_RATES;
+               tmp_prob = mi->groups[group].rates[tmp_idx].prob_ewma;
+
+               if (tmp_tp < minstrel_ht_get_tp_avg(mi, group, tmp_idx, tmp_prob) &&
                   (minstrel_mcs_groups[group].streams < tmp_max_streams)) {
                                mi->max_prob_rate = mg->max_group_prob_rate;
-                               tmp_tp = mr->cur_tp;
+                               tmp_tp = minstrel_ht_get_tp_avg(mi, group,
+                                                               tmp_idx,
+                                                               tmp_prob);
                }
        }
 }
@@ -525,8 +514,8 @@ static void
 minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
 {
        struct minstrel_mcs_group_data *mg;
-       struct minstrel_rate_stats *mr;
-       int group, i, j;
+       struct minstrel_rate_stats *mrs;
+       int group, i, j, cur_prob;
        u16 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES];
        u16 tmp_cck_tp_rate[MAX_THR_RATES], index;
 
@@ -565,12 +554,12 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
 
                        index = MCS_GROUP_RATES * group + i;
 
-                       mr = &mg->rates[i];
-                       mr->retry_updated = false;
-                       minstrel_calc_rate_ewma(mr);
-                       minstrel_ht_calc_tp(mi, group, i);
+                       mrs = &mg->rates[i];
+                       mrs->retry_updated = false;
+                       minstrel_calc_rate_stats(mrs);
+                       cur_prob = mrs->prob_ewma;
 
-                       if (!mr->cur_tp)
+                       if (minstrel_ht_get_tp_avg(mi, group, i, cur_prob) == 0)
                                continue;
 
                        /* Find max throughput rate set */
@@ -614,7 +603,7 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
 #endif
 
        /* Reset update timer */
-       mi->stats_update = jiffies;
+       mi->last_stats_update = jiffies;
 }
 
 static bool
@@ -637,7 +626,7 @@ minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct ieee80211_tx_rate *rat
 }
 
 static void
-minstrel_next_sample_idx(struct minstrel_ht_sta *mi)
+minstrel_set_next_sample_idx(struct minstrel_ht_sta *mi)
 {
        struct minstrel_mcs_group_data *mg;
 
@@ -778,7 +767,8 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
                update = true;
        }
 
-       if (time_after(jiffies, mi->stats_update + (mp->update_interval / 2 * HZ) / 1000)) {
+       if (time_after(jiffies, mi->last_stats_update +
+                               (mp->update_interval / 2 * HZ) / 1000)) {
                update = true;
                minstrel_ht_update_stats(mp, mi);
        }
@@ -791,7 +781,7 @@ static void
 minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
                          int index)
 {
-       struct minstrel_rate_stats *mr;
+       struct minstrel_rate_stats *mrs;
        const struct mcs_group *group;
        unsigned int tx_time, tx_time_rtscts, tx_time_data;
        unsigned int cw = mp->cw_min;
@@ -800,16 +790,16 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
        unsigned int ampdu_len = MINSTREL_TRUNC(mi->avg_ampdu_len);
        unsigned int overhead = 0, overhead_rtscts = 0;
 
-       mr = minstrel_get_ratestats(mi, index);
-       if (mr->probability < MINSTREL_FRAC(1, 10)) {
-               mr->retry_count = 1;
-               mr->retry_count_rtscts = 1;
+       mrs = minstrel_get_ratestats(mi, index);
+       if (mrs->prob_ewma < MINSTREL_FRAC(1, 10)) {
+               mrs->retry_count = 1;
+               mrs->retry_count_rtscts = 1;
                return;
        }
 
-       mr->retry_count = 2;
-       mr->retry_count_rtscts = 2;
-       mr->retry_updated = true;
+       mrs->retry_count = 2;
+       mrs->retry_count_rtscts = 2;
+       mrs->retry_updated = true;
 
        group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
        tx_time_data = group->duration[index % MCS_GROUP_RATES] * ampdu_len / 1000;
@@ -840,9 +830,9 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
                tx_time_rtscts += ctime + overhead_rtscts + tx_time_data;
 
                if (tx_time_rtscts < mp->segment_size)
-                       mr->retry_count_rtscts++;
+                       mrs->retry_count_rtscts++;
        } while ((tx_time < mp->segment_size) &&
-                (++mr->retry_count < mp->max_retry));
+                (++mrs->retry_count < mp->max_retry));
 }
 
 
@@ -851,22 +841,22 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
                      struct ieee80211_sta_rates *ratetbl, int offset, int index)
 {
        const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
-       struct minstrel_rate_stats *mr;
+       struct minstrel_rate_stats *mrs;
        u8 idx;
        u16 flags = group->flags;
 
-       mr = minstrel_get_ratestats(mi, index);
-       if (!mr->retry_updated)
+       mrs = minstrel_get_ratestats(mi, index);
+       if (!mrs->retry_updated)
                minstrel_calc_retransmit(mp, mi, index);
 
-       if (mr->probability < MINSTREL_FRAC(20, 100) || !mr->retry_count) {
+       if (mrs->prob_ewma < MINSTREL_FRAC(20, 100) || !mrs->retry_count) {
                ratetbl->rate[offset].count = 2;
                ratetbl->rate[offset].count_rts = 2;
                ratetbl->rate[offset].count_cts = 2;
        } else {
-               ratetbl->rate[offset].count = mr->retry_count;
-               ratetbl->rate[offset].count_cts = mr->retry_count;
-               ratetbl->rate[offset].count_rts = mr->retry_count_rtscts;
+               ratetbl->rate[offset].count = mrs->retry_count;
+               ratetbl->rate[offset].count_cts = mrs->retry_count;
+               ratetbl->rate[offset].count_rts = mrs->retry_count_rtscts;
        }
 
        if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP)
@@ -924,7 +914,7 @@ minstrel_get_duration(int index)
 static int
 minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
 {
-       struct minstrel_rate_stats *mr;
+       struct minstrel_rate_stats *mrs;
        struct minstrel_mcs_group_data *mg;
        unsigned int sample_dur, sample_group, cur_max_tp_streams;
        int sample_idx = 0;
@@ -940,12 +930,12 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
        sample_group = mi->sample_group;
        mg = &mi->groups[sample_group];
        sample_idx = sample_table[mg->column][mg->index];
-       minstrel_next_sample_idx(mi);
+       minstrel_set_next_sample_idx(mi);
 
        if (!(mg->supported & BIT(sample_idx)))
                return -1;
 
-       mr = &mg->rates[sample_idx];
+       mrs = &mg->rates[sample_idx];
        sample_idx += sample_group * MCS_GROUP_RATES;
 
        /*
@@ -962,7 +952,7 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
         * Do not sample if the probability is already higher than 95%
         * to avoid wasting airtime.
         */
-       if (mr->probability > MINSTREL_FRAC(95, 100))
+       if (mrs->prob_ewma > MINSTREL_FRAC(95, 100))
                return -1;
 
        /*
@@ -977,7 +967,7 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
            (cur_max_tp_streams - 1 <
             minstrel_mcs_groups[sample_group].streams ||
             sample_dur >= minstrel_get_duration(mi->max_prob_rate))) {
-               if (mr->sample_skipped < 20)
+               if (mrs->sample_skipped < 20)
                        return -1;
 
                if (mi->sample_slow++ > 2)
@@ -1131,7 +1121,7 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
        memset(mi, 0, sizeof(*mi));
 
        mi->sta = sta;
-       mi->stats_update = jiffies;
+       mi->last_stats_update = jiffies;
 
        ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1, 0);
        mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1, 0);
@@ -1328,16 +1318,19 @@ 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;
+       int i, j, prob, tp_avg;
 
        if (!msp->is_ht)
                return mac80211_minstrel.get_expected_throughput(priv_sta);
 
        i = mi->max_tp_rate[0] / MCS_GROUP_RATES;
        j = mi->max_tp_rate[0] % MCS_GROUP_RATES;
+       prob = mi->groups[i].rates[j].prob_ewma;
+
+       /* convert tp_avg from pkt per second in kbps */
+       tp_avg = minstrel_ht_get_tp_avg(mi, i, j, prob) * AVG_PKT_SIZE * 8 / 1024;
 
-       /* convert cur_tp from pkt per second in kbps */
-       return mi->groups[i].rates[j].cur_tp * AVG_PKT_SIZE * 8 / 1024;
+       return tp_avg;
 }
 
 static const struct rate_control_ops mac80211_minstrel_ht = {
index f2217d6aa0c2ee28b158616711b99895b250df44..e8b52a94d24b64fd3e78b4d3a390c3dbdad9e328 100644 (file)
@@ -78,7 +78,7 @@ struct minstrel_ht_sta {
        u16 max_prob_rate;
 
        /* time of last status update */
-       unsigned long stats_update;
+       unsigned long last_stats_update;
 
        /* overhead time in usec for each frame */
        unsigned int overhead;
@@ -112,6 +112,7 @@ struct minstrel_ht_sta_priv {
        };
 #ifdef CONFIG_MAC80211_DEBUGFS
        struct dentry *dbg_stats;
+       struct dentry *dbg_stats_csv;
 #endif
        void *ratelist;
        void *sample_table;
@@ -120,5 +121,7 @@ struct minstrel_ht_sta_priv {
 
 void minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir);
 void minstrel_ht_remove_sta_debugfs(void *priv, void *priv_sta);
+int minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate,
+                          int prob_ewma);
 
 #endif
index 20c676b8e5b68fa366ed670640e9091c39ebfde3..6822ce0f95e580641f782fb2f59dcf9f09e5e639 100644 (file)
@@ -19,7 +19,7 @@ static char *
 minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
 {
        const struct mcs_group *mg;
-       unsigned int j, tp, prob, eprob;
+       unsigned int j, tp_max, tp_avg, prob, eprob, tx_time;
        char htmode = '2';
        char gimode = 'L';
        u32 gflags;
@@ -38,19 +38,26 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
                gimode = 'S';
 
        for (j = 0; j < MCS_GROUP_RATES; j++) {
-               struct minstrel_rate_stats *mr = &mi->groups[i].rates[j];
+               struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j];
                static const int bitrates[4] = { 10, 20, 55, 110 };
                int idx = i * MCS_GROUP_RATES + j;
 
                if (!(mi->groups[i].supported & BIT(j)))
                        continue;
 
-               if (gflags & IEEE80211_TX_RC_MCS)
-                       p += sprintf(p, " HT%c0/%cGI ", htmode, gimode);
-               else if (gflags & IEEE80211_TX_RC_VHT_MCS)
-                       p += sprintf(p, "VHT%c0/%cGI ", htmode, gimode);
-               else
-                       p += sprintf(p, " CCK/%cP   ", j < 4 ? 'L' : 'S');
+               if (gflags & IEEE80211_TX_RC_MCS) {
+                       p += sprintf(p, "HT%c0  ", htmode);
+                       p += sprintf(p, "%cGI  ", gimode);
+                       p += sprintf(p, "%d  ", mg->streams);
+               } else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
+                       p += sprintf(p, "VHT%c0 ", htmode);
+                       p += sprintf(p, "%cGI ", gimode);
+                       p += sprintf(p, "%d  ", mg->streams);
+               } else {
+                       p += sprintf(p, "CCK    ");
+                       p += sprintf(p, "%cP  ", j < 4 ? 'L' : 'S');
+                       p += sprintf(p, "1 ");
+               }
 
                *(p++) = (idx == mi->max_tp_rate[0]) ? 'A' : ' ';
                *(p++) = (idx == mi->max_tp_rate[1]) ? 'B' : ' ';
@@ -59,29 +66,39 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
                *(p++) = (idx == mi->max_prob_rate) ? 'P' : ' ';
 
                if (gflags & IEEE80211_TX_RC_MCS) {
-                       p += sprintf(p, " MCS%-2u ", (mg->streams - 1) * 8 + j);
+                       p += sprintf(p, "  MCS%-2u", (mg->streams - 1) * 8 + j);
                } else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
-                       p += sprintf(p, " MCS%-1u/%1u", j, mg->streams);
+                       p += sprintf(p, "  MCS%-1u/%1u", j, mg->streams);
                } else {
                        int r = bitrates[j % 4];
 
-                       p += sprintf(p, " %2u.%1uM ", r / 10, r % 10);
+                       p += sprintf(p, "   %2u.%1uM", r / 10, r % 10);
                }
 
-               tp = mr->cur_tp / 10;
-               prob = MINSTREL_TRUNC(mr->cur_prob * 1000);
-               eprob = MINSTREL_TRUNC(mr->probability * 1000);
+               p += sprintf(p, "  %3u  ", idx);
 
-               p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u "
-                               "%3u %4u(%4u) %9llu(%9llu)\n",
-                               tp / 10, tp % 10,
+               /* tx_time[rate(i)] in usec */
+               tx_time = DIV_ROUND_CLOSEST(mg->duration[j], 1000);
+               p += sprintf(p, "%6u  ", tx_time);
+
+               tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100));
+               tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_ewma);
+               prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
+               eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
+
+               p += sprintf(p, "%4u.%1u   %4u.%1u   %3u.%1u    %3u.%1u"
+                               "     %3u.%1u %3u   %3u %-3u   "
+                               "%9llu   %-9llu\n",
+                               tp_max / 10, tp_max % 10,
+                               tp_avg / 10, tp_avg % 10,
                                eprob / 10, eprob % 10,
+                               mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10,
                                prob / 10, prob % 10,
-                               mr->retry_count,
-                               mr->last_success,
-                               mr->last_attempts,
-                               (unsigned long long)mr->succ_hist,
-                               (unsigned long long)mr->att_hist);
+                               mrs->retry_count,
+                               mrs->last_success,
+                               mrs->last_attempts,
+                               (unsigned long long)mrs->succ_hist,
+                               (unsigned long long)mrs->att_hist);
        }
 
        return p;
@@ -94,8 +111,8 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)
        struct minstrel_ht_sta *mi = &msp->ht;
        struct minstrel_debugfs_info *ms;
        unsigned int i;
-       char *p;
        int ret;
+       char *p;
 
        if (!msp->is_ht) {
                inode->i_private = &msp->legacy;
@@ -110,8 +127,14 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)
 
        file->private_data = ms;
        p = ms->buf;
-       p += sprintf(p, " type           rate      tpt eprob *prob "
-                       "ret  *ok(*cum)        ok(      cum)\n");
+
+       p += sprintf(p, "\n");
+       p += sprintf(p, "              best   ____________rate__________    "
+                       "______statistics______    ________last_______    "
+                       "______sum-of________\n");
+       p += sprintf(p, "mode guard #  rate  [name   idx airtime  max_tp]  "
+                       "[ Ã¸(tp) Ã¸(prob) sd(prob)]  [prob.|retry|suc|att]  [#success | "
+                       "#attempts]\n");
 
        p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p);
        for (i = 0; i < MINSTREL_CCK_GROUP; i++)
@@ -123,11 +146,10 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)
                        "lookaround %d\n",
                        max(0, (int) mi->total_packets - (int) mi->sample_packets),
                        mi->sample_packets);
-       p += sprintf(p, "Average A-MPDU length: %d.%d\n",
+       p += sprintf(p, "Average # of aggregated frames per A-MPDU: %d.%d\n",
                MINSTREL_TRUNC(mi->avg_ampdu_len),
                MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10);
        ms->len = p - ms->buf;
-
        WARN_ON(ms->len + sizeof(*ms) > 32768);
 
        return nonseekable_open(inode, file);
@@ -141,6 +163,143 @@ static const struct file_operations minstrel_ht_stat_fops = {
        .llseek = no_llseek,
 };
 
+static char *
+minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p)
+{
+       const struct mcs_group *mg;
+       unsigned int j, tp_max, tp_avg, prob, eprob, tx_time;
+       char htmode = '2';
+       char gimode = 'L';
+       u32 gflags;
+
+       if (!mi->groups[i].supported)
+               return p;
+
+       mg = &minstrel_mcs_groups[i];
+       gflags = mg->flags;
+
+       if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+               htmode = '4';
+       else if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+               htmode = '8';
+       if (gflags & IEEE80211_TX_RC_SHORT_GI)
+               gimode = 'S';
+
+       for (j = 0; j < MCS_GROUP_RATES; j++) {
+               struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j];
+               static const int bitrates[4] = { 10, 20, 55, 110 };
+               int idx = i * MCS_GROUP_RATES + j;
+
+               if (!(mi->groups[i].supported & BIT(j)))
+                       continue;
+
+               if (gflags & IEEE80211_TX_RC_MCS) {
+                       p += sprintf(p, "HT%c0,", htmode);
+                       p += sprintf(p, "%cGI,", gimode);
+                       p += sprintf(p, "%d,", mg->streams);
+               } else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
+                       p += sprintf(p, "VHT%c0,", htmode);
+                       p += sprintf(p, "%cGI,", gimode);
+                       p += sprintf(p, "%d,", mg->streams);
+               } else {
+                       p += sprintf(p, "CCK,");
+                       p += sprintf(p, "%cP,", j < 4 ? 'L' : 'S');
+                       p += sprintf(p, "1,");
+               }
+
+               p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[0]) ? "A" : ""));
+               p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[1]) ? "B" : ""));
+               p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[2]) ? "C" : ""));
+               p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[3]) ? "D" : ""));
+               p += sprintf(p, "%s" ,((idx == mi->max_prob_rate) ? "P" : ""));
+
+               if (gflags & IEEE80211_TX_RC_MCS) {
+                       p += sprintf(p, ",MCS%-2u,", (mg->streams - 1) * 8 + j);
+               } else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
+                       p += sprintf(p, ",MCS%-1u/%1u,", j, mg->streams);
+               } else {
+                       int r = bitrates[j % 4];
+                       p += sprintf(p, ",%2u.%1uM,", r / 10, r % 10);
+               }
+
+               p += sprintf(p, "%u,", idx);
+               tx_time = DIV_ROUND_CLOSEST(mg->duration[j], 1000);
+               p += sprintf(p, "%u,", tx_time);
+
+               tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100));
+               tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_ewma);
+               prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
+               eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
+
+               p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,"
+                               "%u,%llu,%llu,",
+                               tp_max / 10, tp_max % 10,
+                               tp_avg / 10, tp_avg % 10,
+                               eprob / 10, eprob % 10,
+                               mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10,
+                               prob / 10, prob % 10,
+                               mrs->retry_count,
+                               mrs->last_success,
+                               mrs->last_attempts,
+                               (unsigned long long)mrs->succ_hist,
+                               (unsigned long long)mrs->att_hist);
+               p += sprintf(p, "%d,%d,%d.%d\n",
+                               max(0, (int) mi->total_packets -
+                               (int) mi->sample_packets),
+                               mi->sample_packets,
+                               MINSTREL_TRUNC(mi->avg_ampdu_len),
+                               MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10);
+       }
+
+       return p;
+}
+
+static int
+minstrel_ht_stats_csv_open(struct inode *inode, struct file *file)
+{
+       struct minstrel_ht_sta_priv *msp = inode->i_private;
+       struct minstrel_ht_sta *mi = &msp->ht;
+       struct minstrel_debugfs_info *ms;
+       unsigned int i;
+       int ret;
+       char *p;
+
+       if (!msp->is_ht) {
+               inode->i_private = &msp->legacy;
+               ret = minstrel_stats_csv_open(inode, file);
+               inode->i_private = msp;
+               return ret;
+       }
+
+       ms = kmalloc(32768, GFP_KERNEL);
+
+       if (!ms)
+               return -ENOMEM;
+
+       file->private_data = ms;
+
+       p = ms->buf;
+
+       p = minstrel_ht_stats_csv_dump(mi, MINSTREL_CCK_GROUP, p);
+       for (i = 0; i < MINSTREL_CCK_GROUP; i++)
+               p = minstrel_ht_stats_csv_dump(mi, i, p);
+       for (i++; i < ARRAY_SIZE(mi->groups); i++)
+               p = minstrel_ht_stats_csv_dump(mi, i, p);
+
+       ms->len = p - ms->buf;
+       WARN_ON(ms->len + sizeof(*ms) > 32768);
+
+       return nonseekable_open(inode, file);
+}
+
+static const struct file_operations minstrel_ht_stat_csv_fops = {
+       .owner = THIS_MODULE,
+       .open = minstrel_ht_stats_csv_open,
+       .read = minstrel_stats_read,
+       .release = minstrel_stats_release,
+       .llseek = no_llseek,
+};
+
 void
 minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir)
 {
@@ -148,6 +307,8 @@ minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir)
 
        msp->dbg_stats = debugfs_create_file("rc_stats", S_IRUGO, dir, msp,
                        &minstrel_ht_stat_fops);
+       msp->dbg_stats_csv = debugfs_create_file("rc_stats_csv", S_IRUGO,
+                            dir, msp, &minstrel_ht_stat_csv_fops);
 }
 
 void
@@ -156,4 +317,5 @@ minstrel_ht_remove_sta_debugfs(void *priv, void *priv_sta)
        struct minstrel_ht_sta_priv *msp = priv_sta;
 
        debugfs_remove(msp->dbg_stats);
+       debugfs_remove(msp->dbg_stats_csv);
 }
index 2cd02278d4d4076c580f9253460ce98f677edfe1..260eed45b6d2ff105052643169465c04d333c182 100644 (file)
@@ -1185,6 +1185,7 @@ static void sta_ps_start(struct sta_info *sta)
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        struct ieee80211_local *local = sdata->local;
        struct ps_data *ps;
+       int tid;
 
        if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
            sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
@@ -1198,6 +1199,18 @@ static void sta_ps_start(struct sta_info *sta)
                drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta);
        ps_dbg(sdata, "STA %pM aid %d enters power save mode\n",
               sta->sta.addr, sta->sta.aid);
+
+       if (!sta->sta.txq[0])
+               return;
+
+       for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) {
+               struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]);
+
+               if (!skb_queue_len(&txqi->queue))
+                       set_bit(tid, &sta->txq_buffered_tids);
+               else
+                       clear_bit(tid, &sta->txq_buffered_tids);
+       }
 }
 
 static void sta_ps_end(struct sta_info *sta)
@@ -3424,7 +3437,8 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
        __le16 fc;
        struct ieee80211_rx_data rx;
        struct ieee80211_sub_if_data *prev;
-       struct sta_info *sta, *tmp, *prev_sta;
+       struct sta_info *sta, *prev_sta;
+       struct rhash_head *tmp;
        int err = 0;
 
        fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
@@ -3459,9 +3473,13 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
                ieee80211_scan_rx(local, skb);
 
        if (ieee80211_is_data(fc)) {
+               const struct bucket_table *tbl;
+
                prev_sta = NULL;
 
-               for_each_sta_info(local, hdr->addr2, sta, tmp) {
+               tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash);
+
+               for_each_sta_info(local, tbl, hdr->addr2, sta, tmp) {
                        if (!prev_sta) {
                                prev_sta = sta;
                                continue;
index aacaa1a85e636c4b31df4540c680508be33cdb42..12971b71d0fa1ea8ce69dbdc09314c1c1641d7c8 100644 (file)
  * freed before they are done using it.
  */
 
+static const struct rhashtable_params sta_rht_params = {
+       .nelem_hint = 3, /* start small */
+       .head_offset = offsetof(struct sta_info, hash_node),
+       .key_offset = offsetof(struct sta_info, sta.addr),
+       .key_len = ETH_ALEN,
+       .hashfn = sta_addr_hash,
+};
+
 /* Caller must hold local->sta_mtx */
 static int sta_info_hash_del(struct ieee80211_local *local,
                             struct sta_info *sta)
 {
-       struct sta_info *s;
-
-       s = rcu_dereference_protected(local->sta_hash[STA_HASH(sta->sta.addr)],
-                                     lockdep_is_held(&local->sta_mtx));
-       if (!s)
-               return -ENOENT;
-       if (s == sta) {
-               rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)],
-                                  s->hnext);
-               return 0;
-       }
-
-       while (rcu_access_pointer(s->hnext) &&
-              rcu_access_pointer(s->hnext) != sta)
-               s = rcu_dereference_protected(s->hnext,
-                                       lockdep_is_held(&local->sta_mtx));
-       if (rcu_access_pointer(s->hnext)) {
-               rcu_assign_pointer(s->hnext, sta->hnext);
-               return 0;
-       }
-
-       return -ENOENT;
+       return rhashtable_remove_fast(&local->sta_hash, &sta->hash_node,
+                                     sta_rht_params);
 }
 
 static void __cleanup_single_sta(struct sta_info *sta)
@@ -118,6 +106,16 @@ static void __cleanup_single_sta(struct sta_info *sta)
                atomic_dec(&ps->num_sta_ps);
        }
 
+       if (sta->sta.txq[0]) {
+               for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
+                       struct txq_info *txqi = to_txq_info(sta->sta.txq[i]);
+                       int n = skb_queue_len(&txqi->queue);
+
+                       ieee80211_purge_tx_queue(&local->hw, &txqi->queue);
+                       atomic_sub(n, &sdata->txqs_len[txqi->txq.ac]);
+               }
+       }
+
        for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
                local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]);
                ieee80211_purge_tx_queue(&local->hw, &sta->ps_tx_buf[ac]);
@@ -159,18 +157,8 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
                              const u8 *addr)
 {
        struct ieee80211_local *local = sdata->local;
-       struct sta_info *sta;
 
-       sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)],
-                                   lockdep_is_held(&local->sta_mtx));
-       while (sta) {
-               if (sta->sdata == sdata &&
-                   ether_addr_equal(sta->sta.addr, addr))
-                       break;
-               sta = rcu_dereference_check(sta->hnext,
-                                           lockdep_is_held(&local->sta_mtx));
-       }
-       return sta;
+       return rhashtable_lookup_fast(&local->sta_hash, addr, sta_rht_params);
 }
 
 /*
@@ -182,18 +170,24 @@ struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_local *local = sdata->local;
        struct sta_info *sta;
+       struct rhash_head *tmp;
+       const struct bucket_table *tbl;
 
-       sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)],
-                                   lockdep_is_held(&local->sta_mtx));
-       while (sta) {
-               if ((sta->sdata == sdata ||
-                    (sta->sdata->bss && sta->sdata->bss == sdata->bss)) &&
-                   ether_addr_equal(sta->sta.addr, addr))
-                       break;
-               sta = rcu_dereference_check(sta->hnext,
-                                           lockdep_is_held(&local->sta_mtx));
+       rcu_read_lock();
+       tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash);
+
+       for_each_sta_info(local, tbl, addr, sta, tmp) {
+               if (sta->sdata == sdata ||
+                   (sta->sdata->bss && sta->sdata->bss == sdata->bss)) {
+                       rcu_read_unlock();
+                       /* this is safe as the caller must already hold
+                        * another rcu read section or the mutex
+                        */
+                       return sta;
+               }
        }
-       return sta;
+       rcu_read_unlock();
+       return NULL;
 }
 
 struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata,
@@ -234,6 +228,8 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
 
        sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr);
 
+       if (sta->sta.txq[0])
+               kfree(to_txq_info(sta->sta.txq[0]));
        kfree(rcu_dereference_raw(sta->sta.rates));
        kfree(sta);
 }
@@ -242,9 +238,8 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
 static void sta_info_hash_add(struct ieee80211_local *local,
                              struct sta_info *sta)
 {
-       lockdep_assert_held(&local->sta_mtx);
-       sta->hnext = local->sta_hash[STA_HASH(sta->sta.addr)];
-       rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta);
+       rhashtable_insert_fast(&local->sta_hash, &sta->hash_node,
+                              sta_rht_params);
 }
 
 static void sta_deliver_ps_frames(struct work_struct *wk)
@@ -285,11 +280,12 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
                                const u8 *addr, gfp_t gfp)
 {
        struct ieee80211_local *local = sdata->local;
+       struct ieee80211_hw *hw = &local->hw;
        struct sta_info *sta;
        struct timespec uptime;
        int i;
 
-       sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp);
+       sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp);
        if (!sta)
                return NULL;
 
@@ -321,11 +317,25 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
        for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++)
                ewma_init(&sta->chain_signal_avg[i], 1024, 8);
 
-       if (sta_prepare_rate_control(local, sta, gfp)) {
-               kfree(sta);
-               return NULL;
+       if (local->ops->wake_tx_queue) {
+               void *txq_data;
+               int size = sizeof(struct txq_info) +
+                          ALIGN(hw->txq_data_size, sizeof(void *));
+
+               txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp);
+               if (!txq_data)
+                       goto free;
+
+               for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
+                       struct txq_info *txq = txq_data + i * size;
+
+                       ieee80211_init_tx_queue(sdata, sta, txq, i);
+               }
        }
 
+       if (sta_prepare_rate_control(local, sta, gfp))
+               goto free_txq;
+
        for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
                /*
                 * timer_to_tid must be initialized with identity mapping
@@ -346,7 +356,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
        if (sdata->vif.type == NL80211_IFTYPE_AP ||
            sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
                struct ieee80211_supported_band *sband =
-                       local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)];
+                       hw->wiphy->bands[ieee80211_get_sdata_band(sdata)];
                u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >>
                                IEEE80211_HT_CAP_SM_PS_SHIFT;
                /*
@@ -371,6 +381,13 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
        sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
 
        return sta;
+
+free_txq:
+       if (sta->sta.txq[0])
+               kfree(to_txq_info(sta->sta.txq[0]));
+free:
+       kfree(sta);
+       return NULL;
 }
 
 static int sta_info_insert_check(struct sta_info *sta)
@@ -640,6 +657,8 @@ static void __sta_info_recalc_tim(struct sta_info *sta, bool ignore_pending)
 
                indicate_tim |=
                        sta->driver_buffered_tids & tids;
+               indicate_tim |=
+                       sta->txq_buffered_tids & tids;
        }
 
  done:
@@ -948,19 +967,32 @@ static void sta_info_cleanup(unsigned long data)
                  round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL));
 }
 
-void sta_info_init(struct ieee80211_local *local)
+u32 sta_addr_hash(const void *key, u32 length, u32 seed)
+{
+       return jhash(key, ETH_ALEN, seed);
+}
+
+int sta_info_init(struct ieee80211_local *local)
 {
+       int err;
+
+       err = rhashtable_init(&local->sta_hash, &sta_rht_params);
+       if (err)
+               return err;
+
        spin_lock_init(&local->tim_lock);
        mutex_init(&local->sta_mtx);
        INIT_LIST_HEAD(&local->sta_list);
 
        setup_timer(&local->sta_cleanup, sta_info_cleanup,
                    (unsigned long)local);
+       return 0;
 }
 
 void sta_info_stop(struct ieee80211_local *local)
 {
        del_timer_sync(&local->sta_cleanup);
+       rhashtable_destroy(&local->sta_hash);
 }
 
 
@@ -1024,16 +1056,21 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
 }
 
 struct ieee80211_sta *ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw,
-                                              const u8 *addr,
-                                              const u8 *localaddr)
+                                                  const u8 *addr,
+                                                  const u8 *localaddr)
 {
-       struct sta_info *sta, *nxt;
+       struct ieee80211_local *local = hw_to_local(hw);
+       struct sta_info *sta;
+       struct rhash_head *tmp;
+       const struct bucket_table *tbl;
+
+       tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash);
 
        /*
         * Just return a random station if localaddr is NULL
         * ... first in list.
         */
-       for_each_sta_info(hw_to_local(hw), addr, sta, nxt) {
+       for_each_sta_info(local, tbl, addr, sta, tmp) {
                if (localaddr &&
                    !ether_addr_equal(sta->sdata->vif.addr, localaddr))
                        continue;
@@ -1071,7 +1108,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        struct ieee80211_local *local = sdata->local;
        struct sk_buff_head pending;
-       int filtered = 0, buffered = 0, ac;
+       int filtered = 0, buffered = 0, ac, i;
        unsigned long flags;
        struct ps_data *ps;
 
@@ -1090,10 +1127,22 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
 
        BUILD_BUG_ON(BITS_TO_LONGS(IEEE80211_NUM_TIDS) > 1);
        sta->driver_buffered_tids = 0;
+       sta->txq_buffered_tids = 0;
 
        if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS))
                drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta);
 
+       if (sta->sta.txq[0]) {
+               for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
+                       struct txq_info *txqi = to_txq_info(sta->sta.txq[i]);
+
+                       if (!skb_queue_len(&txqi->queue))
+                               continue;
+
+                       drv_wake_tx_queue(local, txqi);
+               }
+       }
+
        skb_queue_head_init(&pending);
 
        /* sync with ieee80211_tx_h_unicast_ps_buf */
@@ -1275,8 +1324,10 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
                /* if we already have frames from software, then we can't also
                 * release from hardware queues
                 */
-               if (skb_queue_empty(&frames))
+               if (skb_queue_empty(&frames)) {
                        driver_release_tids |= sta->driver_buffered_tids & tids;
+                       driver_release_tids |= sta->txq_buffered_tids & tids;
+               }
 
                if (driver_release_tids) {
                        /* If the driver has data on more than one TID then
@@ -1447,6 +1498,9 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
 
                sta_info_recalc_tim(sta);
        } else {
+               unsigned long tids = sta->txq_buffered_tids & driver_release_tids;
+               int tid;
+
                /*
                 * We need to release a frame that is buffered somewhere in the
                 * driver ... it'll have to handle that.
@@ -1466,8 +1520,22 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
                 * that the TID(s) became empty before returning here from the
                 * release function.
                 * Either way, however, when the driver tells us that the TID(s)
-                * became empty we'll do the TIM recalculation.
+                * became empty or we find that a txq became empty, we'll do the
+                * TIM recalculation.
                 */
+
+               if (!sta->sta.txq[0])
+                       return;
+
+               for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) {
+                       struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]);
+
+                       if (!(tids & BIT(tid)) || skb_queue_len(&txqi->queue))
+                               continue;
+
+                       sta_info_recalc_tim(sta);
+                       break;
+               }
        }
 }
 
index 7e2fa4018d41331cc3845639eb7dfc497c13bc27..5c164fb3f6c5bd2d68b5daa3993ff369a2a8792f 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/workqueue.h>
 #include <linux/average.h>
 #include <linux/etherdevice.h>
+#include <linux/rhashtable.h>
 #include "key.h"
 
 /**
@@ -248,7 +249,7 @@ struct sta_ampdu_mlme {
  *
  * @list: global linked list entry
  * @free_list: list entry for keeping track of stations to free
- * @hnext: hash table linked list pointer
+ * @hash_node: hash node for rhashtable
  * @local: pointer to the global information
  * @sdata: virtual interface this station belongs to
  * @ptk: peer keys negotiated with this station, if any
@@ -276,6 +277,7 @@ struct sta_ampdu_mlme {
  *     entered power saving state, these are also delivered to
  *     the station when it leaves powersave or polls for frames
  * @driver_buffered_tids: bitmap of TIDs the driver has data buffered on
+ * @txq_buffered_tids: bitmap of TIDs that mac80211 has txq data buffered on
  * @rx_packets: Number of MSDUs received from this STA
  * @rx_bytes: Number of bytes received from this STA
  * @last_rx: time (in jiffies) when last frame was received from this STA
@@ -341,7 +343,7 @@ struct sta_info {
        /* General information, mostly static */
        struct list_head list, free_list;
        struct rcu_head rcu_head;
-       struct sta_info __rcu *hnext;
+       struct rhash_head hash_node;
        struct ieee80211_local *local;
        struct ieee80211_sub_if_data *sdata;
        struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
@@ -370,6 +372,7 @@ struct sta_info {
        struct sk_buff_head ps_tx_buf[IEEE80211_NUM_ACS];
        struct sk_buff_head tx_filtered[IEEE80211_NUM_ACS];
        unsigned long driver_buffered_tids;
+       unsigned long txq_buffered_tids;
 
        /* Updated from RX path only, no locking requirements */
        unsigned long rx_packets;
@@ -537,10 +540,6 @@ rcu_dereference_protected_tid_tx(struct sta_info *sta, int tid)
                                         lockdep_is_held(&sta->ampdu_mlme.mtx));
 }
 
-#define STA_HASH_SIZE 256
-#define STA_HASH(sta) (sta[5])
-
-
 /* Maximum number of frames to buffer per power saving station per AC */
 #define STA_MAX_TX_BUFFER      64
 
@@ -561,26 +560,15 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
 struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,
                                  const u8 *addr);
 
-static inline
-void for_each_sta_info_type_check(struct ieee80211_local *local,
-                                 const u8 *addr,
-                                 struct sta_info *sta,
-                                 struct sta_info *nxt)
-{
-}
+u32 sta_addr_hash(const void *key, u32 length, u32 seed);
+
+#define _sta_bucket_idx(_tbl, _a)                                      \
+       rht_bucket_index(_tbl, sta_addr_hash(_a, ETH_ALEN, (_tbl)->hash_rnd))
 
-#define for_each_sta_info(local, _addr, _sta, nxt)                     \
-       for (   /* initialise loop */                                   \
-               _sta = rcu_dereference(local->sta_hash[STA_HASH(_addr)]),\
-               nxt = _sta ? rcu_dereference(_sta->hnext) : NULL;       \
-               /* typecheck */                                         \
-               for_each_sta_info_type_check(local, (_addr), _sta, nxt),\
-               /* continue condition */                                \
-               _sta;                                                   \
-               /* advance loop */                                      \
-               _sta = nxt,                                             \
-               nxt = _sta ? rcu_dereference(_sta->hnext) : NULL        \
-            )                                                          \
+#define for_each_sta_info(local, tbl, _addr, _sta, _tmp)               \
+       rht_for_each_entry_rcu(_sta, _tmp, tbl,                         \
+                              _sta_bucket_idx(tbl, _addr),             \
+                              hash_node)                               \
        /* compare address and run code only if it matches */           \
        if (ether_addr_equal(_sta->sta.addr, (_addr)))
 
@@ -617,7 +605,7 @@ int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata,
 
 void sta_info_recalc_tim(struct sta_info *sta);
 
-void sta_info_init(struct ieee80211_local *local);
+int sta_info_init(struct ieee80211_local *local);
 void sta_info_stop(struct ieee80211_local *local);
 
 /**
index 2c51742428d59ebda73b2819b8bfc4391128d36c..005fdbe39a8b2f694b2deaa3d55e1a2baef031f3 100644 (file)
@@ -654,7 +654,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
        struct ieee80211_supported_band *sband;
        struct ieee80211_sub_if_data *sdata;
        struct net_device *prev_dev = NULL;
-       struct sta_info *sta, *tmp;
+       struct sta_info *sta;
+       struct rhash_head *tmp;
        int retry_count;
        int rates_idx;
        bool send_to_cooked;
@@ -663,6 +664,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
        int rtap_len;
        int shift = 0;
        int tid = IEEE80211_NUM_TIDS;
+       const struct bucket_table *tbl;
 
        rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
 
@@ -671,7 +673,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
        sband = local->hw.wiphy->bands[info->band];
        fc = hdr->frame_control;
 
-       for_each_sta_info(local, hdr->addr1, sta, tmp) {
+       tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash);
+
+       for_each_sta_info(local, tbl, hdr->addr1, sta, tmp) {
                /* skip wrong virtual interface */
                if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr))
                        continue;
index e9e462b349e5f828841398ec869c4ec2bf56e6eb..790bd45081c49f764b75330e2b3b0e6cde08d04d 100644 (file)
@@ -2312,6 +2312,37 @@ TRACE_EVENT(drv_tdls_recv_channel_switch,
        )
 );
 
+TRACE_EVENT(drv_wake_tx_queue,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata,
+                struct txq_info *txq),
+
+       TP_ARGS(local, sdata, txq),
+
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               VIF_ENTRY
+               STA_ENTRY
+               __field(u8, ac)
+               __field(u8, tid)
+       ),
+
+       TP_fast_assign(
+               struct ieee80211_sta *sta = txq->txq.sta;
+
+               LOCAL_ASSIGN;
+               VIF_ASSIGN;
+               STA_ASSIGN;
+               __entry->ac = txq->txq.ac;
+               __entry->tid = txq->txq.tid;
+       ),
+
+       TP_printk(
+               LOCAL_PR_FMT  VIF_PR_FMT  STA_PR_FMT " ac:%d tid:%d",
+               LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->ac, __entry->tid
+       )
+);
+
 #ifdef CONFIG_MAC80211_MESSAGE_TRACING
 #undef TRACE_SYSTEM
 #define TRACE_SYSTEM mac80211_msg
index 9f7fb4eec37bde2af4de010e474561513709ff29..667111ee6a20fc48493f88605e8ed45e36d6d55e 100644 (file)
@@ -767,12 +767,22 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
        return TX_CONTINUE;
 }
 
+static __le16 ieee80211_tx_next_seq(struct sta_info *sta, int tid)
+{
+       u16 *seq = &sta->tid_seq[tid];
+       __le16 ret = cpu_to_le16(*seq);
+
+       /* Increase the sequence number. */
+       *seq = (*seq + 0x10) & IEEE80211_SCTL_SEQ;
+
+       return ret;
+}
+
 static ieee80211_tx_result debug_noinline
 ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
 {
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
-       u16 *seq;
        u8 *qc;
        int tid;
 
@@ -823,13 +833,10 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
 
        qc = ieee80211_get_qos_ctl(hdr);
        tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
-       seq = &tx->sta->tid_seq[tid];
        tx->sta->tx_msdu[tid]++;
 
-       hdr->seq_ctrl = cpu_to_le16(*seq);
-
-       /* Increase the sequence number. */
-       *seq = (*seq + 0x10) & IEEE80211_SCTL_SEQ;
+       if (!tx->sta->sta.txq[0])
+               hdr->seq_ctrl = ieee80211_tx_next_seq(tx->sta, tid);
 
        return TX_CONTINUE;
 }
@@ -1070,7 +1077,7 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx,
                 * nothing -- this aggregation session is being started
                 * but that might still fail with the driver
                 */
-       } else {
+       } else if (!tx->sta->sta.txq[tid]) {
                spin_lock(&tx->sta->lock);
                /*
                 * Need to re-check now, because we may get here
@@ -1211,13 +1218,102 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
        return TX_CONTINUE;
 }
 
+static void ieee80211_drv_tx(struct ieee80211_local *local,
+                            struct ieee80211_vif *vif,
+                            struct ieee80211_sta *pubsta,
+                            struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_tx_control control = {
+               .sta = pubsta,
+       };
+       struct ieee80211_txq *txq = NULL;
+       struct txq_info *txqi;
+       u8 ac;
+
+       if (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE)
+               goto tx_normal;
+
+       if (!ieee80211_is_data(hdr->frame_control))
+               goto tx_normal;
+
+       if (pubsta) {
+               u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
+
+               txq = pubsta->txq[tid];
+       } else if (vif) {
+               txq = vif->txq;
+       }
+
+       if (!txq)
+               goto tx_normal;
+
+       ac = txq->ac;
+       txqi = to_txq_info(txq);
+       atomic_inc(&sdata->txqs_len[ac]);
+       if (atomic_read(&sdata->txqs_len[ac]) >= local->hw.txq_ac_max_pending)
+               netif_stop_subqueue(sdata->dev, ac);
+
+       skb_queue_tail(&txqi->queue, skb);
+       drv_wake_tx_queue(local, txqi);
+
+       return;
+
+tx_normal:
+       drv_tx(local, &control, skb);
+}
+
+struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
+                                    struct ieee80211_txq *txq)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif);
+       struct txq_info *txqi = container_of(txq, struct txq_info, txq);
+       struct ieee80211_hdr *hdr;
+       struct sk_buff *skb = NULL;
+       u8 ac = txq->ac;
+
+       spin_lock_bh(&txqi->queue.lock);
+
+       if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags))
+               goto out;
+
+       skb = __skb_dequeue(&txqi->queue);
+       if (!skb)
+               goto out;
+
+       atomic_dec(&sdata->txqs_len[ac]);
+       if (__netif_subqueue_stopped(sdata->dev, ac))
+               ieee80211_propagate_queue_wake(local, sdata->vif.hw_queue[ac]);
+
+       hdr = (struct ieee80211_hdr *)skb->data;
+       if (txq->sta && ieee80211_is_data_qos(hdr->frame_control)) {
+               struct sta_info *sta = container_of(txq->sta, struct sta_info,
+                                                   sta);
+               struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+               hdr->seq_ctrl = ieee80211_tx_next_seq(sta, txq->tid);
+               if (test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags))
+                       info->flags |= IEEE80211_TX_CTL_AMPDU;
+               else
+                       info->flags &= ~IEEE80211_TX_CTL_AMPDU;
+       }
+
+out:
+       spin_unlock_bh(&txqi->queue.lock);
+
+       return skb;
+}
+EXPORT_SYMBOL(ieee80211_tx_dequeue);
+
 static bool ieee80211_tx_frags(struct ieee80211_local *local,
                               struct ieee80211_vif *vif,
                               struct ieee80211_sta *sta,
                               struct sk_buff_head *skbs,
                               bool txpending)
 {
-       struct ieee80211_tx_control control;
        struct sk_buff *skb, *tmp;
        unsigned long flags;
 
@@ -1275,10 +1371,9 @@ static bool ieee80211_tx_frags(struct ieee80211_local *local,
                spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 
                info->control.vif = vif;
-               control.sta = sta;
 
                __skb_unlink(skb, skbs);
-               drv_tx(local, &control, skb);
+               ieee80211_drv_tx(local, vif, sta, skb);
        }
 
        return true;
index d1742a7d9ea497f248c4987d712507dd4d851ced..79412f16b61db9953a4a537db3bd5693d7c61cdb 100644 (file)
@@ -308,6 +308,11 @@ void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)
                for (ac = 0; ac < n_acs; ac++) {
                        int ac_queue = sdata->vif.hw_queue[ac];
 
+                       if (local->ops->wake_tx_queue &&
+                           (atomic_read(&sdata->txqs_len[ac]) >
+                            local->hw.txq_ac_max_pending))
+                               continue;
+
                        if (ac_queue == queue ||
                            (sdata->vif.cab_queue == queue &&
                             local->queue_stop_reasons[ac_queue] == 0 &&
@@ -2189,46 +2194,6 @@ void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata)
        mutex_unlock(&local->chanctx_mtx);
 }
 
-static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
-{
-       int i;
-
-       for (i = 0; i < n_ids; i++)
-               if (ids[i] == id)
-                       return true;
-       return false;
-}
-
-size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
-                             const u8 *ids, int n_ids,
-                             const u8 *after_ric, int n_after_ric,
-                             size_t offset)
-{
-       size_t pos = offset;
-
-       while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) {
-               if (ies[pos] == WLAN_EID_RIC_DATA && n_after_ric) {
-                       pos += 2 + ies[pos + 1];
-
-                       while (pos < ielen &&
-                              !ieee80211_id_in_list(after_ric, n_after_ric,
-                                                    ies[pos]))
-                               pos += 2 + ies[pos + 1];
-               } else {
-                       pos += 2 + ies[pos + 1];
-               }
-       }
-
-       return pos;
-}
-
-size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
-                         const u8 *ids, int n_ids, size_t offset)
-{
-       return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset);
-}
-EXPORT_SYMBOL(ieee80211_ie_split);
-
 size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset)
 {
        size_t pos = offset;
@@ -3352,3 +3317,20 @@ u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo)
 
        return buf;
 }
+
+void ieee80211_init_tx_queue(struct ieee80211_sub_if_data *sdata,
+                            struct sta_info *sta,
+                            struct txq_info *txqi, int tid)
+{
+       skb_queue_head_init(&txqi->queue);
+       txqi->txq.vif = &sdata->vif;
+
+       if (sta) {
+               txqi->txq.sta = &sta->sta;
+               sta->sta.txq[tid] = &txqi->txq;
+               txqi->txq.ac = ieee802_1d_to_ac[tid & 7];
+       } else {
+               sdata->vif.txq = &txqi->txq;
+               txqi->txq.ac = IEEE80211_AC_BE;
+       }
+}
index 89f73a9e98741702f2ea324e49b7b2f75bfb69fd..a87d8b8ec730421403930c69061a2c7167db0a6a 100644 (file)
@@ -70,7 +70,7 @@ obj-$(CONFIG_NETFILTER_SYNPROXY) += nf_synproxy_core.o
 
 # nf_tables
 nf_tables-objs += nf_tables_core.o nf_tables_api.o
-nf_tables-objs += nft_immediate.o nft_cmp.o nft_lookup.o
+nf_tables-objs += nft_immediate.o nft_cmp.o nft_lookup.o nft_dynset.o
 nf_tables-objs += nft_bitwise.o nft_byteorder.o nft_payload.o
 
 obj-$(CONFIG_NF_TABLES)                += nf_tables.o
index 758b002130d92f0e7fecf63627d426289cf03820..380ef5148ea11ef29b5e3ab8113b94905fbea389 100644 (file)
@@ -19,6 +19,7 @@
 #include <net/netlink.h>
 
 #include <linux/netfilter.h>
+#include <linux/netfilter_bridge.h>
 #include <linux/netfilter/ipset/pfxlen.h>
 #include <linux/netfilter/ipset/ip_set.h>
 #include <linux/netfilter/ipset/ip_set_hash.h>
@@ -211,6 +212,22 @@ hash_netiface4_data_next(struct hash_netiface4_elem *next,
 #define HKEY_DATALEN   sizeof(struct hash_netiface4_elem_hashed)
 #include "ip_set_hash_gen.h"
 
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
+static const char *get_physindev_name(const struct sk_buff *skb)
+{
+       struct net_device *dev = nf_bridge_get_physindev(skb);
+
+       return dev ? dev->name : NULL;
+}
+
+static const char *get_phyoutdev_name(const struct sk_buff *skb)
+{
+       struct net_device *dev = nf_bridge_get_physoutdev(skb);
+
+       return dev ? dev->name : NULL;
+}
+#endif
+
 static int
 hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb,
                    const struct xt_action_param *par,
@@ -234,16 +251,15 @@ hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb,
        e.ip &= ip_set_netmask(e.cidr);
 
 #define IFACE(dir)     (par->dir ? par->dir->name : NULL)
-#define PHYSDEV(dir)   (nf_bridge->dir ? nf_bridge->dir->name : NULL)
 #define SRCDIR         (opt->flags & IPSET_DIM_TWO_SRC)
 
        if (opt->cmdflags & IPSET_FLAG_PHYSDEV) {
 #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
-               const struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+               e.iface = SRCDIR ? get_physindev_name(skb) :
+                                  get_phyoutdev_name(skb);
 
-               if (!nf_bridge)
+               if (!e.iface)
                        return -EINVAL;
-               e.iface = SRCDIR ? PHYSDEV(physindev) : PHYSDEV(physoutdev);
                e.physdev = 1;
 #else
                e.iface = NULL;
@@ -476,11 +492,11 @@ hash_netiface6_kadt(struct ip_set *set, const struct sk_buff *skb,
 
        if (opt->cmdflags & IPSET_FLAG_PHYSDEV) {
 #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
-               const struct nf_bridge_info *nf_bridge = skb->nf_bridge;
-
-               if (!nf_bridge)
+               e.iface = SRCDIR ? get_physindev_name(skb) :
+                                  get_phyoutdev_name(skb);
+               if (!e.iface)
                        return -EINVAL;
-               e.iface = SRCDIR ? PHYSDEV(physindev) : PHYSDEV(physoutdev);
+
                e.physdev = 1;
 #else
                e.iface = NULL;
index bf02932b7188d71ea0c0f1ea438c1b33766b7979..19986ec5f21addd110ee6deadc72398641b61998 100644 (file)
@@ -536,8 +536,8 @@ static inline int ip_vs_nat_send_or_cont(int pf, struct sk_buff *skb,
                ip_vs_update_conntrack(skb, cp, 1);
        if (!local) {
                skb_forward_csum(skb);
-               NF_HOOK(pf, NF_INET_LOCAL_OUT, skb, NULL, skb_dst(skb)->dev,
-                       dst_output);
+               NF_HOOK(pf, NF_INET_LOCAL_OUT, NULL, skb,
+                       NULL, skb_dst(skb)->dev, dst_output_sk);
        } else
                ret = NF_ACCEPT;
        return ret;
@@ -554,8 +554,8 @@ static inline int ip_vs_send_or_cont(int pf, struct sk_buff *skb,
                ip_vs_notrack(skb);
        if (!local) {
                skb_forward_csum(skb);
-               NF_HOOK(pf, NF_INET_LOCAL_OUT, skb, NULL, skb_dst(skb)->dev,
-                       dst_output);
+               NF_HOOK(pf, NF_INET_LOCAL_OUT, NULL, skb,
+                       NULL, skb_dst(skb)->dev, dst_output_sk);
        } else
                ret = NF_ACCEPT;
        return ret;
index 2631876ac55be96aeec77ab1d15f5db8c5a80c49..a5aa5967b8e17b894a798dbf29d07bf12063fc16 100644 (file)
@@ -17,6 +17,7 @@
 #include <net/route.h>
 
 #include <linux/netfilter.h>
+#include <linux/netfilter_bridge.h>
 #include <linux/netfilter/xt_LOG.h>
 #include <net/netfilter/nf_log.h>
 
@@ -163,10 +164,10 @@ nf_log_dump_packet_common(struct nf_log_buf *m, u_int8_t pf,
                const struct net_device *physindev;
                const struct net_device *physoutdev;
 
-               physindev = skb->nf_bridge->physindev;
+               physindev = nf_bridge_get_physindev(skb);
                if (physindev && in != physindev)
                        nf_log_buf_add(m, "PHYSIN=%s ", physindev->name);
-               physoutdev = skb->nf_bridge->physoutdev;
+               physoutdev = nf_bridge_get_physoutdev(skb);
                if (physoutdev && out != physoutdev)
                        nf_log_buf_add(m, "PHYSOUT=%s ", physoutdev->name);
        }
index d3cd37edca182707fb587c89fd54610ca5c93a8a..2e88032cd5ad22fb1e910966167340a1ba8761f6 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/proc_fs.h>
 #include <linux/skbuff.h>
 #include <linux/netfilter.h>
+#include <linux/netfilter_bridge.h>
 #include <linux/seq_file.h>
 #include <linux/rcupdate.h>
 #include <net/protocol.h>
@@ -54,14 +55,18 @@ void nf_queue_entry_release_refs(struct nf_queue_entry *entry)
                dev_put(state->in);
        if (state->out)
                dev_put(state->out);
+       if (state->sk)
+               sock_put(state->sk);
 #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
        if (entry->skb->nf_bridge) {
-               struct nf_bridge_info *nf_bridge = entry->skb->nf_bridge;
+               struct net_device *physdev;
 
-               if (nf_bridge->physindev)
-                       dev_put(nf_bridge->physindev);
-               if (nf_bridge->physoutdev)
-                       dev_put(nf_bridge->physoutdev);
+               physdev = nf_bridge_get_physindev(entry->skb);
+               if (physdev)
+                       dev_put(physdev);
+               physdev = nf_bridge_get_physoutdev(entry->skb);
+               if (physdev)
+                       dev_put(physdev);
        }
 #endif
        /* Drop reference to owner of hook which queued us. */
@@ -81,15 +86,16 @@ bool nf_queue_entry_get_refs(struct nf_queue_entry *entry)
                dev_hold(state->in);
        if (state->out)
                dev_hold(state->out);
+       if (state->sk)
+               sock_hold(state->sk);
 #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
        if (entry->skb->nf_bridge) {
-               struct nf_bridge_info *nf_bridge = entry->skb->nf_bridge;
                struct net_device *physdev;
 
-               physdev = nf_bridge->physindev;
+               physdev = nf_bridge_get_physindev(entry->skb);
                if (physdev)
                        dev_hold(physdev);
-               physdev = nf_bridge->physoutdev;
+               physdev = nf_bridge_get_physoutdev(entry->skb);
                if (physdev)
                        dev_hold(physdev);
        }
@@ -198,7 +204,7 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
        case NF_ACCEPT:
        case NF_STOP:
                local_bh_disable();
-               entry->state.okfn(skb);
+               entry->state.okfn(entry->state.sk, skb);
                local_bh_enable();
                break;
        case NF_QUEUE:
index 5604c2df05d1a40f2e812d18d2a72fd895359100..0b96fa0d64b2f9bf536eed8a0778f36f8aa04508 100644 (file)
@@ -2159,7 +2159,7 @@ nft_select_set_ops(const struct nlattr * const nla[],
        features = 0;
        if (nla[NFTA_SET_FLAGS] != NULL) {
                features = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
-               features &= NFT_SET_INTERVAL | NFT_SET_MAP;
+               features &= NFT_SET_INTERVAL | NFT_SET_MAP | NFT_SET_TIMEOUT;
        }
 
        bops       = NULL;
@@ -2216,6 +2216,8 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = {
        [NFTA_SET_POLICY]               = { .type = NLA_U32 },
        [NFTA_SET_DESC]                 = { .type = NLA_NESTED },
        [NFTA_SET_ID]                   = { .type = NLA_U32 },
+       [NFTA_SET_TIMEOUT]              = { .type = NLA_U64 },
+       [NFTA_SET_GC_INTERVAL]          = { .type = NLA_U32 },
 };
 
 static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = {
@@ -2366,6 +2368,13 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
                        goto nla_put_failure;
        }
 
+       if (set->timeout &&
+           nla_put_be64(skb, NFTA_SET_TIMEOUT, cpu_to_be64(set->timeout)))
+               goto nla_put_failure;
+       if (set->gc_int &&
+           nla_put_be32(skb, NFTA_SET_GC_INTERVAL, htonl(set->gc_int)))
+               goto nla_put_failure;
+
        if (set->policy != NFT_SET_POL_PERFORMANCE) {
                if (nla_put_be32(skb, NFTA_SET_POLICY, htonl(set->policy)))
                        goto nla_put_failure;
@@ -2578,7 +2587,8 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
        char name[IFNAMSIZ];
        unsigned int size;
        bool create;
-       u32 ktype, dtype, flags, policy;
+       u64 timeout;
+       u32 ktype, dtype, flags, policy, gc_int;
        struct nft_set_desc desc;
        int err;
 
@@ -2605,7 +2615,8 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
        if (nla[NFTA_SET_FLAGS] != NULL) {
                flags = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
                if (flags & ~(NFT_SET_ANONYMOUS | NFT_SET_CONSTANT |
-                             NFT_SET_INTERVAL | NFT_SET_MAP))
+                             NFT_SET_INTERVAL | NFT_SET_MAP |
+                             NFT_SET_TIMEOUT))
                        return -EINVAL;
        }
 
@@ -2631,6 +2642,19 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
        } else if (flags & NFT_SET_MAP)
                return -EINVAL;
 
+       timeout = 0;
+       if (nla[NFTA_SET_TIMEOUT] != NULL) {
+               if (!(flags & NFT_SET_TIMEOUT))
+                       return -EINVAL;
+               timeout = be64_to_cpu(nla_get_be64(nla[NFTA_SET_TIMEOUT]));
+       }
+       gc_int = 0;
+       if (nla[NFTA_SET_GC_INTERVAL] != NULL) {
+               if (!(flags & NFT_SET_TIMEOUT))
+                       return -EINVAL;
+               gc_int = ntohl(nla_get_be32(nla[NFTA_SET_GC_INTERVAL]));
+       }
+
        policy = NFT_SET_POL_PERFORMANCE;
        if (nla[NFTA_SET_POLICY] != NULL)
                policy = ntohl(nla_get_be32(nla[NFTA_SET_POLICY]));
@@ -2699,6 +2723,8 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
        set->flags = flags;
        set->size  = desc.size;
        set->policy = policy;
+       set->timeout = timeout;
+       set->gc_int = gc_int;
 
        err = ops->init(set, &desc, nla);
        if (err < 0)
@@ -2785,12 +2811,13 @@ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
        if (!list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS)
                return -EBUSY;
 
-       if (set->flags & NFT_SET_MAP) {
+       if (binding->flags & NFT_SET_MAP) {
                /* If the set is already bound to the same chain all
                 * jumps are already validated for that chain.
                 */
                list_for_each_entry(i, &set->bindings, list) {
-                       if (i->chain == binding->chain)
+                       if (binding->flags & NFT_SET_MAP &&
+                           i->chain == binding->chain)
                                goto bind;
                }
 
@@ -2837,6 +2864,18 @@ const struct nft_set_ext_type nft_set_ext_types[] = {
                .len    = sizeof(u8),
                .align  = __alignof__(u8),
        },
+       [NFT_SET_EXT_TIMEOUT]           = {
+               .len    = sizeof(u64),
+               .align  = __alignof__(u64),
+       },
+       [NFT_SET_EXT_EXPIRATION]        = {
+               .len    = sizeof(unsigned long),
+               .align  = __alignof__(unsigned long),
+       },
+       [NFT_SET_EXT_USERDATA]          = {
+               .len    = sizeof(struct nft_userdata),
+               .align  = __alignof__(struct nft_userdata),
+       },
 };
 EXPORT_SYMBOL_GPL(nft_set_ext_types);
 
@@ -2848,6 +2887,9 @@ static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = {
        [NFTA_SET_ELEM_KEY]             = { .type = NLA_NESTED },
        [NFTA_SET_ELEM_DATA]            = { .type = NLA_NESTED },
        [NFTA_SET_ELEM_FLAGS]           = { .type = NLA_U32 },
+       [NFTA_SET_ELEM_TIMEOUT]         = { .type = NLA_U64 },
+       [NFTA_SET_ELEM_USERDATA]        = { .type = NLA_BINARY,
+                                           .len = NFT_USERDATA_MAXLEN },
 };
 
 static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = {
@@ -2909,6 +2951,34 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
                         htonl(*nft_set_ext_flags(ext))))
                goto nla_put_failure;
 
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT) &&
+           nla_put_be64(skb, NFTA_SET_ELEM_TIMEOUT,
+                        cpu_to_be64(*nft_set_ext_timeout(ext))))
+               goto nla_put_failure;
+
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
+               unsigned long expires, now = jiffies;
+
+               expires = *nft_set_ext_expiration(ext);
+               if (time_before(now, expires))
+                       expires -= now;
+               else
+                       expires = 0;
+
+               if (nla_put_be64(skb, NFTA_SET_ELEM_EXPIRATION,
+                                cpu_to_be64(jiffies_to_msecs(expires))))
+                       goto nla_put_failure;
+       }
+
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_USERDATA)) {
+               struct nft_userdata *udata;
+
+               udata = nft_set_ext_userdata(ext);
+               if (nla_put(skb, NFTA_SET_ELEM_USERDATA,
+                           udata->len + 1, udata->data))
+                       goto nla_put_failure;
+       }
+
        nla_nest_end(skb, nest);
        return 0;
 
@@ -3128,11 +3198,11 @@ static struct nft_trans *nft_trans_elem_alloc(struct nft_ctx *ctx,
        return trans;
 }
 
-static void *nft_set_elem_init(const struct nft_set *set,
-                              const struct nft_set_ext_tmpl *tmpl,
-                              const struct nft_data *key,
-                              const struct nft_data *data,
-                              gfp_t gfp)
+void *nft_set_elem_init(const struct nft_set *set,
+                       const struct nft_set_ext_tmpl *tmpl,
+                       const struct nft_data *key,
+                       const struct nft_data *data,
+                       u64 timeout, gfp_t gfp)
 {
        struct nft_set_ext *ext;
        void *elem;
@@ -3147,6 +3217,11 @@ static void *nft_set_elem_init(const struct nft_set *set,
        memcpy(nft_set_ext_key(ext), key, set->klen);
        if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
                memcpy(nft_set_ext_data(ext), data, set->dlen);
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION))
+               *nft_set_ext_expiration(ext) =
+                       jiffies + msecs_to_jiffies(timeout);
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT))
+               *nft_set_ext_timeout(ext) = timeout;
 
        return elem;
 }
@@ -3172,15 +3247,15 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
        struct nft_set_ext *ext;
        struct nft_set_elem elem;
        struct nft_set_binding *binding;
+       struct nft_userdata *udata;
        struct nft_data data;
        enum nft_registers dreg;
        struct nft_trans *trans;
+       u64 timeout;
        u32 flags;
+       u8 ulen;
        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)
@@ -3215,6 +3290,15 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                        return -EINVAL;
        }
 
+       timeout = 0;
+       if (nla[NFTA_SET_ELEM_TIMEOUT] != NULL) {
+               if (!(set->flags & NFT_SET_TIMEOUT))
+                       return -EINVAL;
+               timeout = be64_to_cpu(nla_get_be64(nla[NFTA_SET_ELEM_TIMEOUT]));
+       } else if (set->flags & NFT_SET_TIMEOUT) {
+               timeout = set->timeout;
+       }
+
        err = nft_data_init(ctx, &elem.key, &d1, nla[NFTA_SET_ELEM_KEY]);
        if (err < 0)
                goto err1;
@@ -3223,6 +3307,11 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                goto err2;
 
        nft_set_ext_add(&tmpl, NFT_SET_EXT_KEY);
+       if (timeout > 0) {
+               nft_set_ext_add(&tmpl, NFT_SET_EXT_EXPIRATION);
+               if (timeout != set->timeout)
+                       nft_set_ext_add(&tmpl, NFT_SET_EXT_TIMEOUT);
+       }
 
        if (nla[NFTA_SET_ELEM_DATA] != NULL) {
                err = nft_data_init(ctx, &data, &d2, nla[NFTA_SET_ELEM_DATA]);
@@ -3241,6 +3330,9 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                                .chain  = (struct nft_chain *)binding->chain,
                        };
 
+                       if (!(binding->flags & NFT_SET_MAP))
+                               continue;
+
                        err = nft_validate_data_load(&bind_ctx, dreg,
                                                     &data, d2.type);
                        if (err < 0)
@@ -3250,20 +3342,38 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                nft_set_ext_add(&tmpl, NFT_SET_EXT_DATA);
        }
 
+       /* The full maximum length of userdata can exceed the maximum
+        * offset value (U8_MAX) for following extensions, therefor it
+        * must be the last extension added.
+        */
+       ulen = 0;
+       if (nla[NFTA_SET_ELEM_USERDATA] != NULL) {
+               ulen = nla_len(nla[NFTA_SET_ELEM_USERDATA]);
+               if (ulen > 0)
+                       nft_set_ext_add_length(&tmpl, NFT_SET_EXT_USERDATA,
+                                              ulen);
+       }
+
        err = -ENOMEM;
-       elem.priv = nft_set_elem_init(set, &tmpl, &elem.key, &data, GFP_KERNEL);
+       elem.priv = nft_set_elem_init(set, &tmpl, &elem.key, &data,
+                                     timeout, GFP_KERNEL);
        if (elem.priv == NULL)
                goto err3;
 
        ext = nft_set_elem_ext(set, elem.priv);
        if (flags)
                *nft_set_ext_flags(ext) = flags;
+       if (ulen > 0) {
+               udata = nft_set_ext_userdata(ext);
+               udata->len = ulen - 1;
+               nla_memcpy(&udata->data, nla[NFTA_SET_ELEM_USERDATA], ulen);
+       }
 
        trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
        if (trans == NULL)
                goto err4;
 
-       ext->genmask = nft_genmask_cur(ctx->net);
+       ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK;
        err = set->ops->insert(set, &elem);
        if (err < 0)
                goto err5;
@@ -3316,11 +3426,15 @@ static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb,
                return -EBUSY;
 
        nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
+               if (set->size &&
+                   !atomic_add_unless(&set->nelems, 1, set->size + set->ndeact))
+                       return -ENFILE;
+
                err = nft_add_set_elem(&ctx, set, attr);
-               if (err < 0)
+               if (err < 0) {
+                       atomic_dec(&set->nelems);
                        break;
-
-               set->nelems++;
+               }
        }
        return err;
 }
@@ -3402,11 +3516,36 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
                if (err < 0)
                        break;
 
-               set->nelems--;
+               set->ndeact++;
        }
        return err;
 }
 
+void nft_set_gc_batch_release(struct rcu_head *rcu)
+{
+       struct nft_set_gc_batch *gcb;
+       unsigned int i;
+
+       gcb = container_of(rcu, struct nft_set_gc_batch, head.rcu);
+       for (i = 0; i < gcb->head.cnt; i++)
+               nft_set_elem_destroy(gcb->head.set, gcb->elems[i]);
+       kfree(gcb);
+}
+EXPORT_SYMBOL_GPL(nft_set_gc_batch_release);
+
+struct nft_set_gc_batch *nft_set_gc_batch_alloc(const struct nft_set *set,
+                                               gfp_t gfp)
+{
+       struct nft_set_gc_batch *gcb;
+
+       gcb = kzalloc(sizeof(*gcb), gfp);
+       if (gcb == NULL)
+               return gcb;
+       gcb->head.set = set;
+       return gcb;
+}
+EXPORT_SYMBOL_GPL(nft_set_gc_batch_alloc);
+
 static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net,
                                   u32 portid, u32 seq)
 {
@@ -3710,6 +3849,8 @@ static int nf_tables_commit(struct sk_buff *skb)
                                                 &te->elem,
                                                 NFT_MSG_DELSETELEM, 0);
                        te->set->ops->remove(te->set, &te->elem);
+                       atomic_dec(&te->set->nelems);
+                       te->set->ndeact--;
                        break;
                }
        }
@@ -3813,16 +3954,16 @@ static int nf_tables_abort(struct sk_buff *skb)
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_NEWSETELEM:
-                       nft_trans_elem_set(trans)->nelems--;
                        te = (struct nft_trans_elem *)trans->data;
 
                        te->set->ops->remove(te->set, &te->elem);
+                       atomic_dec(&te->set->nelems);
                        break;
                case NFT_MSG_DELSETELEM:
                        te = (struct nft_trans_elem *)trans->data;
 
-                       nft_trans_elem_set(trans)->nelems++;
                        te->set->ops->activate(te->set, &te->elem);
+                       te->set->ndeact--;
 
                        nft_trans_destroy(trans);
                        break;
@@ -3960,7 +4101,8 @@ static int nf_tables_check_loops(const struct nft_ctx *ctx,
                        continue;
 
                list_for_each_entry(binding, &set->bindings, list) {
-                       if (binding->chain != chain)
+                       if (!(binding->flags & NFT_SET_MAP) ||
+                           binding->chain != chain)
                                continue;
 
                        iter.skip       = 0;
index ef4dfcbaf149f4c207f0096ceb6b8a6c1aa3d924..7caf08a9225d29c3621896c881896ac03765827f 100644 (file)
@@ -239,8 +239,14 @@ int __init nf_tables_core_module_init(void)
        if (err < 0)
                goto err6;
 
+       err = nft_dynset_module_init();
+       if (err < 0)
+               goto err7;
+
        return 0;
 
+err7:
+       nft_payload_module_exit();
 err6:
        nft_byteorder_module_exit();
 err5:
@@ -257,6 +263,7 @@ err1:
 
 void nf_tables_core_module_exit(void)
 {
+       nft_dynset_module_exit();
        nft_payload_module_exit();
        nft_byteorder_module_exit();
        nft_bitwise_module_exit();
index 957b83a0223b8eef159b572a2b685095a2d3e0ab..3ad91266c821489500fbc8cbbcfc7bfd774b6f48 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/ipv6.h>
 #include <linux/netdevice.h>
 #include <linux/netfilter.h>
+#include <linux/netfilter_bridge.h>
 #include <net/netlink.h>
 #include <linux/netfilter/nfnetlink.h>
 #include <linux/netfilter/nfnetlink_log.h>
@@ -62,7 +63,7 @@ struct nfulnl_instance {
        struct timer_list timer;
        struct net *net;
        struct user_namespace *peer_user_ns;    /* User namespace of the peer process */
-       int peer_portid;                        /* PORTID of the peer process */
+       u32 peer_portid;                /* PORTID of the peer process */
 
        /* configurable parameters */
        unsigned int flushtimeout;      /* timeout until queue flush */
@@ -151,7 +152,7 @@ static void nfulnl_timer(unsigned long data);
 
 static struct nfulnl_instance *
 instance_create(struct net *net, u_int16_t group_num,
-               int portid, struct user_namespace *user_ns)
+               u32 portid, struct user_namespace *user_ns)
 {
        struct nfulnl_instance *inst;
        struct nfnl_log_net *log = nfnl_log_pernet(net);
@@ -448,14 +449,18 @@ __build_packet_message(struct nfnl_log_net *log,
                                         htonl(br_port_get_rcu(indev)->br->dev->ifindex)))
                                goto nla_put_failure;
                } else {
+                       struct net_device *physindev;
+
                        /* Case 2: indev is bridge group, we need to look for
                         * physical device (when called from ipv4) */
                        if (nla_put_be32(inst->skb, NFULA_IFINDEX_INDEV,
                                         htonl(indev->ifindex)))
                                goto nla_put_failure;
-                       if (skb->nf_bridge && skb->nf_bridge->physindev &&
+
+                       physindev = nf_bridge_get_physindev(skb);
+                       if (physindev &&
                            nla_put_be32(inst->skb, NFULA_IFINDEX_PHYSINDEV,
-                                        htonl(skb->nf_bridge->physindev->ifindex)))
+                                        htonl(physindev->ifindex)))
                                goto nla_put_failure;
                }
 #endif
@@ -479,14 +484,18 @@ __build_packet_message(struct nfnl_log_net *log,
                                         htonl(br_port_get_rcu(outdev)->br->dev->ifindex)))
                                goto nla_put_failure;
                } else {
+                       struct net_device *physoutdev;
+
                        /* Case 2: indev is a bridge group, we need to look
                         * for physical device (when called from ipv4) */
                        if (nla_put_be32(inst->skb, NFULA_IFINDEX_OUTDEV,
                                         htonl(outdev->ifindex)))
                                goto nla_put_failure;
-                       if (skb->nf_bridge && skb->nf_bridge->physoutdev &&
+
+                       physoutdev = nf_bridge_get_physoutdev(skb);
+                       if (physoutdev &&
                            nla_put_be32(inst->skb, NFULA_IFINDEX_PHYSOUTDEV,
-                                        htonl(skb->nf_bridge->physoutdev->ifindex)))
+                                        htonl(physoutdev->ifindex)))
                                goto nla_put_failure;
                }
 #endif
@@ -998,7 +1007,7 @@ static int seq_show(struct seq_file *s, void *v)
 {
        const struct nfulnl_instance *inst = v;
 
-       seq_printf(s, "%5d %6d %5d %1d %5d %6d %2d\n",
+       seq_printf(s, "%5u %6u %5u %1u %5u %6u %2u\n",
                   inst->group_num,
                   inst->peer_portid, inst->qlen,
                   inst->copy_mode, inst->copy_range,
index 6e74655a8d4f153818243e27750c2660e0e52380..0b98c74202390ae79598ceb955360f937bb9556d 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/proc_fs.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/netfilter_ipv6.h>
+#include <linux/netfilter_bridge.h>
 #include <linux/netfilter/nfnetlink.h>
 #include <linux/netfilter/nfnetlink_queue.h>
 #include <linux/list.h>
@@ -54,7 +55,7 @@ struct nfqnl_instance {
        struct hlist_node hlist;                /* global list of queues */
        struct rcu_head rcu;
 
-       int peer_portid;
+       u32 peer_portid;
        unsigned int queue_maxlen;
        unsigned int copy_range;
        unsigned int queue_dropped;
@@ -109,8 +110,7 @@ instance_lookup(struct nfnl_queue_net *q, u_int16_t queue_num)
 }
 
 static struct nfqnl_instance *
-instance_create(struct nfnl_queue_net *q, u_int16_t queue_num,
-               int portid)
+instance_create(struct nfnl_queue_net *q, u_int16_t queue_num, u32 portid)
 {
        struct nfqnl_instance *inst;
        unsigned int h;
@@ -396,14 +396,18 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
                                         htonl(br_port_get_rcu(indev)->br->dev->ifindex)))
                                goto nla_put_failure;
                } else {
+                       int physinif;
+
                        /* Case 2: indev is bridge group, we need to look for
                         * physical device (when called from ipv4) */
                        if (nla_put_be32(skb, NFQA_IFINDEX_INDEV,
                                         htonl(indev->ifindex)))
                                goto nla_put_failure;
-                       if (entskb->nf_bridge && entskb->nf_bridge->physindev &&
+
+                       physinif = nf_bridge_get_physinif(entskb);
+                       if (physinif &&
                            nla_put_be32(skb, NFQA_IFINDEX_PHYSINDEV,
-                                        htonl(entskb->nf_bridge->physindev->ifindex)))
+                                        htonl(physinif)))
                                goto nla_put_failure;
                }
 #endif
@@ -426,14 +430,18 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
                                         htonl(br_port_get_rcu(outdev)->br->dev->ifindex)))
                                goto nla_put_failure;
                } else {
+                       int physoutif;
+
                        /* Case 2: outdev is bridge group, we need to look for
                         * physical output device (when called from ipv4) */
                        if (nla_put_be32(skb, NFQA_IFINDEX_OUTDEV,
                                         htonl(outdev->ifindex)))
                                goto nla_put_failure;
-                       if (entskb->nf_bridge && entskb->nf_bridge->physoutdev &&
+
+                       physoutif = nf_bridge_get_physoutif(entskb);
+                       if (physoutif &&
                            nla_put_be32(skb, NFQA_IFINDEX_PHYSOUTDEV,
-                                        htonl(entskb->nf_bridge->physoutdev->ifindex)))
+                                        htonl(physoutif)))
                                goto nla_put_failure;
                }
 #endif
@@ -765,11 +773,12 @@ dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex)
                        return 1;
 #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
        if (entry->skb->nf_bridge) {
-               if (entry->skb->nf_bridge->physindev &&
-                   entry->skb->nf_bridge->physindev->ifindex == ifindex)
-                       return 1;
-               if (entry->skb->nf_bridge->physoutdev &&
-                   entry->skb->nf_bridge->physoutdev->ifindex == ifindex)
+               int physinif, physoutif;
+
+               physinif = nf_bridge_get_physinif(entry->skb);
+               physoutif = nf_bridge_get_physoutif(entry->skb);
+
+               if (physinif == ifindex || physoutif == ifindex)
                        return 1;
        }
 #endif
@@ -860,7 +869,7 @@ static const struct nla_policy nfqa_verdict_batch_policy[NFQA_MAX+1] = {
 };
 
 static struct nfqnl_instance *
-verdict_instance_lookup(struct nfnl_queue_net *q, u16 queue_num, int nlportid)
+verdict_instance_lookup(struct nfnl_queue_net *q, u16 queue_num, u32 nlportid)
 {
        struct nfqnl_instance *queue;
 
@@ -1242,7 +1251,7 @@ static int seq_show(struct seq_file *s, void *v)
 {
        const struct nfqnl_instance *inst = v;
 
-       seq_printf(s, "%5d %6d %5d %1d %5d %5d %5d %8d %2d\n",
+       seq_printf(s, "%5u %6u %5u %1u %5u %5u %5u %8u %2d\n",
                   inst->queue_num,
                   inst->peer_portid, inst->queue_total,
                   inst->copy_mode, inst->copy_range,
index 589b8487cd0840de2bafd01304242265b16ea11f..0d137c1ac889ea2cd8f74aaf71238773855198c0 100644 (file)
@@ -321,11 +321,11 @@ static void nft_match_eval(const struct nft_expr *expr,
                return;
        }
 
-       switch(ret) {
-       case true:
+       switch (ret ? 1 : 0) {
+       case 1:
                data[NFT_REG_VERDICT].verdict = NFT_CONTINUE;
                break;
-       case false:
+       case 0:
                data[NFT_REG_VERDICT].verdict = NFT_BREAK;
                break;
        }
index cc5603016242ea8e1f5cdce1d633e3a2687276ae..18d520e0ca0a73cadeb8cb8efa2565e1169e9980 100644 (file)
@@ -56,6 +56,8 @@ static void nft_ct_get_eval(const struct nft_expr *expr,
                        state = NF_CT_STATE_BIT(ctinfo);
                dest->data[0] = state;
                return;
+       default:
+               break;
        }
 
        if (ct == NULL)
@@ -117,6 +119,8 @@ static void nft_ct_get_eval(const struct nft_expr *expr,
                return;
        }
 #endif
+       default:
+               break;
        }
 
        tuple = &ct->tuplehash[priv->dir].tuple;
@@ -141,6 +145,8 @@ static void nft_ct_get_eval(const struct nft_expr *expr,
        case NFT_CT_PROTO_DST:
                dest->data[0] = (__force __u16)tuple->dst.u.all;
                return;
+       default:
+               break;
        }
        return;
 err:
@@ -172,6 +178,8 @@ static void nft_ct_set_eval(const struct nft_expr *expr,
                }
                break;
 #endif
+       default:
+               break;
        }
 }
 
diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c
new file mode 100644 (file)
index 0000000..eeb72de
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2015 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_core.h>
+
+struct nft_dynset {
+       struct nft_set                  *set;
+       struct nft_set_ext_tmpl         tmpl;
+       enum nft_dynset_ops             op:8;
+       enum nft_registers              sreg_key:8;
+       enum nft_registers              sreg_data:8;
+       u64                             timeout;
+       struct nft_set_binding          binding;
+};
+
+static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr,
+                           struct nft_data data[NFT_REG_MAX + 1])
+{
+       const struct nft_dynset *priv = nft_expr_priv(expr);
+       u64 timeout;
+       void *elem;
+
+       if (set->size && !atomic_add_unless(&set->nelems, 1, set->size))
+               return NULL;
+
+       timeout = priv->timeout ? : set->timeout;
+       elem = nft_set_elem_init(set, &priv->tmpl,
+                                &data[priv->sreg_key], &data[priv->sreg_data],
+                                timeout, GFP_ATOMIC);
+       if (elem == NULL) {
+               if (set->size)
+                       atomic_dec(&set->nelems);
+       }
+       return elem;
+}
+
+static void nft_dynset_eval(const struct nft_expr *expr,
+                           struct nft_data data[NFT_REG_MAX + 1],
+                           const struct nft_pktinfo *pkt)
+{
+       const struct nft_dynset *priv = nft_expr_priv(expr);
+       struct nft_set *set = priv->set;
+       const struct nft_set_ext *ext;
+       u64 timeout;
+
+       if (set->ops->update(set, &data[priv->sreg_key], nft_dynset_new,
+                            expr, data, &ext)) {
+               if (priv->op == NFT_DYNSET_OP_UPDATE &&
+                   nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
+                       timeout = priv->timeout ? : set->timeout;
+                       *nft_set_ext_expiration(ext) = jiffies + timeout;
+                       return;
+               }
+       }
+
+       data[NFT_REG_VERDICT].verdict = NFT_BREAK;
+}
+
+static const struct nla_policy nft_dynset_policy[NFTA_DYNSET_MAX + 1] = {
+       [NFTA_DYNSET_SET_NAME]  = { .type = NLA_STRING },
+       [NFTA_DYNSET_SET_ID]    = { .type = NLA_U32 },
+       [NFTA_DYNSET_OP]        = { .type = NLA_U32 },
+       [NFTA_DYNSET_SREG_KEY]  = { .type = NLA_U32 },
+       [NFTA_DYNSET_SREG_DATA] = { .type = NLA_U32 },
+       [NFTA_DYNSET_TIMEOUT]   = { .type = NLA_U64 },
+};
+
+static int nft_dynset_init(const struct nft_ctx *ctx,
+                          const struct nft_expr *expr,
+                          const struct nlattr * const tb[])
+{
+       struct nft_dynset *priv = nft_expr_priv(expr);
+       struct nft_set *set;
+       u64 timeout;
+       int err;
+
+       if (tb[NFTA_DYNSET_SET_NAME] == NULL ||
+           tb[NFTA_DYNSET_OP] == NULL ||
+           tb[NFTA_DYNSET_SREG_KEY] == NULL)
+               return -EINVAL;
+
+       set = nf_tables_set_lookup(ctx->table, tb[NFTA_DYNSET_SET_NAME]);
+       if (IS_ERR(set)) {
+               if (tb[NFTA_DYNSET_SET_ID])
+                       set = nf_tables_set_lookup_byid(ctx->net,
+                                                       tb[NFTA_DYNSET_SET_ID]);
+               if (IS_ERR(set))
+                       return PTR_ERR(set);
+       }
+
+       if (set->flags & NFT_SET_CONSTANT)
+               return -EBUSY;
+
+       priv->op = ntohl(nla_get_be32(tb[NFTA_DYNSET_OP]));
+       switch (priv->op) {
+       case NFT_DYNSET_OP_ADD:
+               break;
+       case NFT_DYNSET_OP_UPDATE:
+               if (!(set->flags & NFT_SET_TIMEOUT))
+                       return -EOPNOTSUPP;
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       timeout = 0;
+       if (tb[NFTA_DYNSET_TIMEOUT] != NULL) {
+               if (!(set->flags & NFT_SET_TIMEOUT))
+                       return -EINVAL;
+               timeout = be64_to_cpu(nla_get_be64(tb[NFTA_DYNSET_TIMEOUT]));
+       }
+
+       priv->sreg_key = ntohl(nla_get_be32(tb[NFTA_DYNSET_SREG_KEY]));
+       err = nft_validate_input_register(priv->sreg_key);
+       if (err < 0)
+               return err;
+
+       if (tb[NFTA_DYNSET_SREG_DATA] != NULL) {
+               if (!(set->flags & NFT_SET_MAP))
+                       return -EINVAL;
+               if (set->dtype == NFT_DATA_VERDICT)
+                       return -EOPNOTSUPP;
+
+               priv->sreg_data = ntohl(nla_get_be32(tb[NFTA_DYNSET_SREG_DATA]));
+               err = nft_validate_input_register(priv->sreg_data);
+               if (err < 0)
+                       return err;
+       } else if (set->flags & NFT_SET_MAP)
+               return -EINVAL;
+
+       nft_set_ext_prepare(&priv->tmpl);
+       nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_KEY, set->klen);
+       if (set->flags & NFT_SET_MAP)
+               nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_DATA, set->dlen);
+       if (set->flags & NFT_SET_TIMEOUT) {
+               if (timeout || set->timeout)
+                       nft_set_ext_add(&priv->tmpl, NFT_SET_EXT_EXPIRATION);
+       }
+
+       priv->timeout = timeout;
+
+       err = nf_tables_bind_set(ctx, set, &priv->binding);
+       if (err < 0)
+               return err;
+
+       priv->set = set;
+       return 0;
+}
+
+static void nft_dynset_destroy(const struct nft_ctx *ctx,
+                              const struct nft_expr *expr)
+{
+       struct nft_dynset *priv = nft_expr_priv(expr);
+
+       nf_tables_unbind_set(ctx, priv->set, &priv->binding);
+}
+
+static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+       const struct nft_dynset *priv = nft_expr_priv(expr);
+
+       if (nla_put_be32(skb, NFTA_DYNSET_SREG_KEY, htonl(priv->sreg_key)))
+               goto nla_put_failure;
+       if (priv->set->flags & NFT_SET_MAP &&
+           nla_put_be32(skb, NFTA_DYNSET_SREG_DATA, htonl(priv->sreg_data)))
+               goto nla_put_failure;
+       if (nla_put_be32(skb, NFTA_DYNSET_OP, htonl(priv->op)))
+               goto nla_put_failure;
+       if (nla_put_string(skb, NFTA_DYNSET_SET_NAME, priv->set->name))
+               goto nla_put_failure;
+       if (nla_put_be64(skb, NFTA_DYNSET_TIMEOUT, cpu_to_be64(priv->timeout)))
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return -1;
+}
+
+static struct nft_expr_type nft_dynset_type;
+static const struct nft_expr_ops nft_dynset_ops = {
+       .type           = &nft_dynset_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_dynset)),
+       .eval           = nft_dynset_eval,
+       .init           = nft_dynset_init,
+       .destroy        = nft_dynset_destroy,
+       .dump           = nft_dynset_dump,
+};
+
+static struct nft_expr_type nft_dynset_type __read_mostly = {
+       .name           = "dynset",
+       .ops            = &nft_dynset_ops,
+       .policy         = nft_dynset_policy,
+       .maxattr        = NFTA_DYNSET_MAX,
+       .owner          = THIS_MODULE,
+};
+
+int __init nft_dynset_module_init(void)
+{
+       return nft_register_expr(&nft_dynset_type);
+}
+
+void nft_dynset_module_exit(void)
+{
+       nft_unregister_expr(&nft_dynset_type);
+}
index c7e1a9d7d46f515c9ef80f67d8fffe630ddafb01..bc23806b7fbef29005dbb9d4adc35ae4d76ff16e 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/log2.h>
 #include <linux/jhash.h>
 #include <linux/netlink.h>
+#include <linux/workqueue.h>
 #include <linux/rhashtable.h>
 #include <linux/netfilter.h>
 #include <linux/netfilter/nf_tables.h>
@@ -25,6 +26,7 @@
 
 struct nft_hash {
        struct rhashtable               ht;
+       struct delayed_work             gc_work;
 };
 
 struct nft_hash_elem {
@@ -62,6 +64,8 @@ static inline int nft_hash_cmp(struct rhashtable_compare_arg *arg,
 
        if (nft_data_cmp(nft_set_ext_key(&he->ext), x->key, x->set->klen))
                return 1;
+       if (nft_set_elem_expired(&he->ext))
+               return 1;
        if (!nft_set_elem_active(&he->ext, x->genmask))
                return 1;
        return 0;
@@ -86,6 +90,42 @@ static bool nft_hash_lookup(const struct nft_set *set,
        return !!he;
 }
 
+static bool nft_hash_update(struct nft_set *set, const struct nft_data *key,
+                           void *(*new)(struct nft_set *,
+                                        const struct nft_expr *,
+                                        struct nft_data []),
+                           const struct nft_expr *expr,
+                           struct nft_data data[],
+                           const struct nft_set_ext **ext)
+{
+       struct nft_hash *priv = nft_set_priv(set);
+       struct nft_hash_elem *he;
+       struct nft_hash_cmp_arg arg = {
+               .genmask = NFT_GENMASK_ANY,
+               .set     = set,
+               .key     = key,
+       };
+
+       he = rhashtable_lookup_fast(&priv->ht, &arg, nft_hash_params);
+       if (he != NULL)
+               goto out;
+
+       he = new(set, expr, data);
+       if (he == NULL)
+               goto err1;
+       if (rhashtable_lookup_insert_key(&priv->ht, &arg, &he->node,
+                                        nft_hash_params))
+               goto err2;
+out:
+       *ext = &he->ext;
+       return true;
+
+err2:
+       nft_set_elem_destroy(set, he);
+err1:
+       return false;
+}
+
 static int nft_hash_insert(const struct nft_set *set,
                           const struct nft_set_elem *elem)
 {
@@ -107,6 +147,7 @@ static void nft_hash_activate(const struct nft_set *set,
        struct nft_hash_elem *he = elem->priv;
 
        nft_set_elem_change_active(set, &he->ext);
+       nft_set_elem_clear_busy(&he->ext);
 }
 
 static void *nft_hash_deactivate(const struct nft_set *set,
@@ -120,9 +161,15 @@ static void *nft_hash_deactivate(const struct nft_set *set,
                .key     = &elem->key,
        };
 
+       rcu_read_lock();
        he = rhashtable_lookup_fast(&priv->ht, &arg, nft_hash_params);
-       if (he != NULL)
-               nft_set_elem_change_active(set, &he->ext);
+       if (he != NULL) {
+               if (!nft_set_elem_mark_busy(&he->ext))
+                       nft_set_elem_change_active(set, &he->ext);
+               else
+                       he = NULL;
+       }
+       rcu_read_unlock();
 
        return he;
 }
@@ -170,6 +217,8 @@ static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set,
 
                if (iter->count < iter->skip)
                        goto cont;
+               if (nft_set_elem_expired(&he->ext))
+                       goto cont;
                if (!nft_set_elem_active(&he->ext, genmask))
                        goto cont;
 
@@ -188,6 +237,55 @@ out:
        rhashtable_walk_exit(&hti);
 }
 
+static void nft_hash_gc(struct work_struct *work)
+{
+       struct nft_set *set;
+       struct nft_hash_elem *he;
+       struct nft_hash *priv;
+       struct nft_set_gc_batch *gcb = NULL;
+       struct rhashtable_iter hti;
+       int err;
+
+       priv = container_of(work, struct nft_hash, gc_work.work);
+       set  = nft_set_container_of(priv);
+
+       err = rhashtable_walk_init(&priv->ht, &hti);
+       if (err)
+               goto schedule;
+
+       err = rhashtable_walk_start(&hti);
+       if (err && err != -EAGAIN)
+               goto out;
+
+       while ((he = rhashtable_walk_next(&hti))) {
+               if (IS_ERR(he)) {
+                       if (PTR_ERR(he) != -EAGAIN)
+                               goto out;
+                       continue;
+               }
+
+               if (!nft_set_elem_expired(&he->ext))
+                       continue;
+               if (nft_set_elem_mark_busy(&he->ext))
+                       continue;
+
+               gcb = nft_set_gc_batch_check(set, gcb, GFP_ATOMIC);
+               if (gcb == NULL)
+                       goto out;
+               rhashtable_remove_fast(&priv->ht, &he->node, nft_hash_params);
+               atomic_dec(&set->nelems);
+               nft_set_gc_batch_add(gcb, he);
+       }
+out:
+       rhashtable_walk_stop(&hti);
+       rhashtable_walk_exit(&hti);
+
+       nft_set_gc_batch_complete(gcb);
+schedule:
+       queue_delayed_work(system_power_efficient_wq, &priv->gc_work,
+                          nft_set_gc_interval(set));
+}
+
 static unsigned int nft_hash_privsize(const struct nlattr * const nla[])
 {
        return sizeof(struct nft_hash);
@@ -207,11 +305,20 @@ static int nft_hash_init(const struct nft_set *set,
 {
        struct nft_hash *priv = nft_set_priv(set);
        struct rhashtable_params params = nft_hash_params;
+       int err;
 
        params.nelem_hint = desc->size ?: NFT_HASH_ELEMENT_HINT;
        params.key_len    = set->klen;
 
-       return rhashtable_init(&priv->ht, &params);
+       err = rhashtable_init(&priv->ht, &params);
+       if (err < 0)
+               return err;
+
+       INIT_DEFERRABLE_WORK(&priv->gc_work, nft_hash_gc);
+       if (set->flags & NFT_SET_TIMEOUT)
+               queue_delayed_work(system_power_efficient_wq, &priv->gc_work,
+                                  nft_set_gc_interval(set));
+       return 0;
 }
 
 static void nft_hash_elem_destroy(void *ptr, void *arg)
@@ -223,6 +330,7 @@ static void nft_hash_destroy(const struct nft_set *set)
 {
        struct nft_hash *priv = nft_set_priv(set);
 
+       cancel_delayed_work_sync(&priv->gc_work);
        rhashtable_free_and_destroy(&priv->ht, nft_hash_elem_destroy,
                                    (void *)set);
 }
@@ -263,8 +371,9 @@ static struct nft_set_ops nft_hash_ops __read_mostly = {
        .deactivate     = nft_hash_deactivate,
        .remove         = nft_hash_remove,
        .lookup         = nft_hash_lookup,
+       .update         = nft_hash_update,
        .walk           = nft_hash_walk,
-       .features       = NFT_SET_MAP,
+       .features       = NFT_SET_MAP | NFT_SET_TIMEOUT,
        .owner          = THIS_MODULE,
 };
 
index a5f30b8760eab5aa476f0afc13fe0e8686c9ff47..d8cf86fb30fc33fdf657a03da1320f331bc38c9a 100644 (file)
@@ -92,6 +92,8 @@ static int nft_lookup_init(const struct nft_ctx *ctx,
        } else if (set->flags & NFT_SET_MAP)
                return -EINVAL;
 
+       priv->binding.flags = set->flags & NFT_SET_MAP;
+
        err = nf_tables_bind_set(ctx, set, &priv->binding);
        if (err < 0)
                return err;
index 5197874372ec4a2055a3f9251f3a3ec248f53fbb..d79ce88be77f3568aa9e6409f7de20242d9c343f 100644 (file)
@@ -166,9 +166,8 @@ void nft_meta_get_eval(const struct nft_expr *expr,
                dest->data[0] = out->group;
                break;
        case NFT_META_CGROUP:
-               if (skb->sk == NULL)
-                       break;
-
+               if (skb->sk == NULL || !sk_fullsock(skb->sk))
+                       goto err;
                dest->data[0] = skb->sk->sk_classid;
                break;
        default:
index c205b26a2beea67d4eec9a0d22d18f97c73edd8d..cca96cec1b689fcd104e273a64db6eda44171beb 100644 (file)
@@ -272,7 +272,7 @@ tproxy_handle_time_wait4(struct sk_buff *skb, __be32 laddr, __be16 lport,
                                            hp->source, lport ? lport : hp->dest,
                                            skb->dev, NFT_LOOKUP_LISTENER);
                if (sk2) {
-                       inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row);
+                       inet_twsk_deschedule(inet_twsk(sk));
                        inet_twsk_put(inet_twsk(sk));
                        sk = sk2;
                }
@@ -437,7 +437,7 @@ tproxy_handle_time_wait6(struct sk_buff *skb, int tproto, int thoff,
                                            tgi->lport ? tgi->lport : hp->dest,
                                            skb->dev, NFT_LOOKUP_LISTENER);
                if (sk2) {
-                       inet_twsk_deschedule(inet_twsk(sk), &tcp_death_row);
+                       inet_twsk_deschedule(inet_twsk(sk));
                        inet_twsk_put(inet_twsk(sk));
                        sk = sk2;
                }
index 7198d660b4dea1e9e79c6f9a13f4e6669bca569d..a1d126f2946305a10ccc04ce92e469b1255f60f9 100644 (file)
@@ -39,7 +39,7 @@ cgroup_mt(const struct sk_buff *skb, struct xt_action_param *par)
 {
        const struct xt_cgroup_info *info = par->matchinfo;
 
-       if (skb->sk == NULL)
+       if (skb->sk == NULL || !sk_fullsock(skb->sk))
                return false;
 
        return (info->id == skb->sk->sk_classid) ^ info->invert;
index 50a52043650fd95989eb6618a36cbb8dba0f6b18..1caaccbc306c7751f717aa1e223cf2ef30f00bec 100644 (file)
@@ -25,16 +25,15 @@ MODULE_ALIAS("ip6t_physdev");
 static bool
 physdev_mt(const struct sk_buff *skb, struct xt_action_param *par)
 {
-       static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
        const struct xt_physdev_info *info = par->matchinfo;
+       const struct net_device *physdev;
        unsigned long ret;
        const char *indev, *outdev;
-       const struct nf_bridge_info *nf_bridge;
 
        /* Not a bridged IP packet or no info available yet:
         * LOCAL_OUT/mangle and LOCAL_OUT/nat don't know if
         * the destination device will be a bridge. */
-       if (!(nf_bridge = skb->nf_bridge)) {
+       if (!skb->nf_bridge) {
                /* Return MATCH if the invert flags of the used options are on */
                if ((info->bitmask & XT_PHYSDEV_OP_BRIDGED) &&
                    !(info->invert & XT_PHYSDEV_OP_BRIDGED))
@@ -54,30 +53,41 @@ physdev_mt(const struct sk_buff *skb, struct xt_action_param *par)
                return true;
        }
 
+       physdev = nf_bridge_get_physoutdev(skb);
+       outdev = physdev ? physdev->name : NULL;
+
        /* This only makes sense in the FORWARD and POSTROUTING chains */
        if ((info->bitmask & XT_PHYSDEV_OP_BRIDGED) &&
-           (!!nf_bridge->physoutdev ^ !(info->invert & XT_PHYSDEV_OP_BRIDGED)))
+           (!!outdev ^ !(info->invert & XT_PHYSDEV_OP_BRIDGED)))
                return false;
 
+       physdev = nf_bridge_get_physindev(skb);
+       indev = physdev ? physdev->name : NULL;
+
        if ((info->bitmask & XT_PHYSDEV_OP_ISIN &&
-           (!nf_bridge->physindev ^ !!(info->invert & XT_PHYSDEV_OP_ISIN))) ||
+           (!indev ^ !!(info->invert & XT_PHYSDEV_OP_ISIN))) ||
            (info->bitmask & XT_PHYSDEV_OP_ISOUT &&
-           (!nf_bridge->physoutdev ^ !!(info->invert & XT_PHYSDEV_OP_ISOUT))))
+           (!outdev ^ !!(info->invert & XT_PHYSDEV_OP_ISOUT))))
                return false;
 
        if (!(info->bitmask & XT_PHYSDEV_OP_IN))
                goto match_outdev;
-       indev = nf_bridge->physindev ? nf_bridge->physindev->name : nulldevname;
-       ret = ifname_compare_aligned(indev, info->physindev, info->in_mask);
 
-       if (!ret ^ !(info->invert & XT_PHYSDEV_OP_IN))
-               return false;
+       if (indev) {
+               ret = ifname_compare_aligned(indev, info->physindev,
+                                            info->in_mask);
+
+               if (!ret ^ !(info->invert & XT_PHYSDEV_OP_IN))
+                       return false;
+       }
 
 match_outdev:
        if (!(info->bitmask & XT_PHYSDEV_OP_OUT))
                return true;
-       outdev = nf_bridge->physoutdev ?
-                nf_bridge->physoutdev->name : nulldevname;
+
+       if (!outdev)
+               return false;
+
        ret = ifname_compare_aligned(outdev, info->physoutdev, info->out_mask);
 
        return (!!ret ^ !(info->invert & XT_PHYSDEV_OP_OUT));
index 895534e87a47a5bb3c4452f93a019831c43754ca..e092cb04632607f21f253d84a8692681a1fefe89 100644 (file)
@@ -143,13 +143,10 @@ static bool xt_socket_sk_is_transparent(struct sock *sk)
        }
 }
 
-static bool
-socket_match(const struct sk_buff *skb, struct xt_action_param *par,
-            const struct xt_socket_mtinfo1 *info)
+static struct sock *xt_socket_lookup_slow_v4(const struct sk_buff *skb,
+                                            const struct net_device *indev)
 {
        const struct iphdr *iph = ip_hdr(skb);
-       struct udphdr _hdr, *hp = NULL;
-       struct sock *sk = skb->sk;
        __be32 uninitialized_var(daddr), uninitialized_var(saddr);
        __be16 uninitialized_var(dport), uninitialized_var(sport);
        u8 uninitialized_var(protocol);
@@ -159,10 +156,12 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par,
 #endif
 
        if (iph->protocol == IPPROTO_UDP || iph->protocol == IPPROTO_TCP) {
+               struct udphdr _hdr, *hp;
+
                hp = skb_header_pointer(skb, ip_hdrlen(skb),
                                        sizeof(_hdr), &_hdr);
                if (hp == NULL)
-                       return false;
+                       return NULL;
 
                protocol = iph->protocol;
                saddr = iph->saddr;
@@ -172,16 +171,17 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par,
 
        } else if (iph->protocol == IPPROTO_ICMP) {
                if (extract_icmp4_fields(skb, &protocol, &saddr, &daddr,
-                                       &sport, &dport))
-                       return false;
+                                        &sport, &dport))
+                       return NULL;
        } else {
-               return false;
+               return NULL;
        }
 
 #ifdef XT_SOCKET_HAVE_CONNTRACK
-       /* Do the lookup with the original socket address in case this is a
-        * reply packet of an established SNAT-ted connection. */
-
+       /* Do the lookup with the original socket address in
+        * case this is a reply packet of an established
+        * SNAT-ted connection.
+        */
        ct = nf_ct_get(skb, &ctinfo);
        if (ct && !nf_ct_is_untracked(ct) &&
            ((iph->protocol != IPPROTO_ICMP &&
@@ -197,10 +197,18 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par,
        }
 #endif
 
+       return xt_socket_get_sock_v4(dev_net(skb->dev), protocol, saddr, daddr,
+                                    sport, dport, indev);
+}
+
+static bool
+socket_match(const struct sk_buff *skb, struct xt_action_param *par,
+            const struct xt_socket_mtinfo1 *info)
+{
+       struct sock *sk = skb->sk;
+
        if (!sk)
-               sk = xt_socket_get_sock_v4(dev_net(skb->dev), protocol,
-                                          saddr, daddr, sport, dport,
-                                          par->in);
+               sk = xt_socket_lookup_slow_v4(skb, par->in);
        if (sk) {
                bool wildcard;
                bool transparent = true;
@@ -225,12 +233,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par,
                        sk = NULL;
        }
 
-       pr_debug("proto %hhu %pI4:%hu -> %pI4:%hu (orig %pI4:%hu) sock %p\n",
-                protocol, &saddr, ntohs(sport),
-                &daddr, ntohs(dport),
-                &iph->daddr, hp ? ntohs(hp->dest) : 0, sk);
-
-       return (sk != NULL);
+       return sk != NULL;
 }
 
 static bool
@@ -327,28 +330,26 @@ xt_socket_get_sock_v6(struct net *net, const u8 protocol,
        return NULL;
 }
 
-static bool
-socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)
+static struct sock *xt_socket_lookup_slow_v6(const struct sk_buff *skb,
+                                            const struct net_device *indev)
 {
-       struct ipv6hdr ipv6_var, *iph = ipv6_hdr(skb);
-       struct udphdr _hdr, *hp = NULL;
-       struct sock *sk = skb->sk;
-       const struct in6_addr *daddr = NULL, *saddr = NULL;
        __be16 uninitialized_var(dport), uninitialized_var(sport);
-       int thoff = 0, uninitialized_var(tproto);
-       const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo;
+       const struct in6_addr *daddr = NULL, *saddr = NULL;
+       struct ipv6hdr *iph = ipv6_hdr(skb);
+       int thoff = 0, tproto;
 
        tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL);
        if (tproto < 0) {
                pr_debug("unable to find transport header in IPv6 packet, dropping\n");
-               return NF_DROP;
+               return NULL;
        }
 
        if (tproto == IPPROTO_UDP || tproto == IPPROTO_TCP) {
-               hp = skb_header_pointer(skb, thoff,
-                                       sizeof(_hdr), &_hdr);
+               struct udphdr _hdr, *hp;
+
+               hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr);
                if (hp == NULL)
-                       return false;
+                       return NULL;
 
                saddr = &iph->saddr;
                sport = hp->source;
@@ -356,17 +357,27 @@ socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)
                dport = hp->dest;
 
        } else if (tproto == IPPROTO_ICMPV6) {
+               struct ipv6hdr ipv6_var;
+
                if (extract_icmp6_fields(skb, thoff, &tproto, &saddr, &daddr,
                                         &sport, &dport, &ipv6_var))
-                       return false;
+                       return NULL;
        } else {
-               return false;
+               return NULL;
        }
 
+       return xt_socket_get_sock_v6(dev_net(skb->dev), tproto, saddr, daddr,
+                                    sport, dport, indev);
+}
+
+static bool
+socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)
+{
+       const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo;
+       struct sock *sk = skb->sk;
+
        if (!sk)
-               sk = xt_socket_get_sock_v6(dev_net(skb->dev), tproto,
-                                          saddr, daddr, sport, dport,
-                                          par->in);
+               sk = xt_socket_lookup_slow_v6(skb, par->in);
        if (sk) {
                bool wildcard;
                bool transparent = true;
@@ -391,13 +402,7 @@ socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)
                        sk = NULL;
        }
 
-       pr_debug("proto %hhd %pI6:%hu -> %pI6:%hu "
-                "(orig %pI6:%hu) sock %p\n",
-                tproto, saddr, ntohs(sport),
-                daddr, ntohs(dport),
-                &iph->daddr, hp ? ntohs(hp->dest) : 0, sk);
-
-       return (sk != NULL);
+       return sk != NULL;
 }
 #endif
 
index 9575a1892607c5dbf18f5bc04f531ed826dfe812..49ff321060809a320966d5efc24ea6aaaab1add8 100644 (file)
@@ -907,6 +907,16 @@ static int nci_se_io(struct nfc_dev *nfc_dev, u32 se_idx,
        return 0;
 }
 
+static int nci_fw_download(struct nfc_dev *nfc_dev, const char *firmware_name)
+{
+       struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+
+       if (!ndev->ops->fw_download)
+               return -ENOTSUPP;
+
+       return ndev->ops->fw_download(ndev, firmware_name);
+}
+
 static struct nfc_ops nci_nfc_ops = {
        .dev_up = nci_dev_up,
        .dev_down = nci_dev_down,
@@ -922,6 +932,7 @@ static struct nfc_ops nci_nfc_ops = {
        .disable_se = nci_disable_se,
        .discover_se = nci_discover_se,
        .se_io = nci_se_io,
+       .fw_download = nci_fw_download,
 };
 
 /* ---- Interface to NCI drivers ---- */
index 14a2d11581da7ededf0eff4ca09293a277853b2d..3763036710aedfc38768793c44388b70375ae71b 100644 (file)
@@ -1584,7 +1584,7 @@ static const struct genl_ops nfc_genl_ops[] = {
 
 struct urelease_work {
        struct  work_struct w;
-       int     portid;
+       u32     portid;
 };
 
 static void nfc_urelease_event_work(struct work_struct *work)
index 3277a7520e31320479e0d40bbd66b6dd6f89de59..6d39766e7828c4351a004fa8ce7d839348c3681d 100644 (file)
@@ -222,7 +222,8 @@ static int vxlan_tnl_send(struct vport *vport, struct sk_buff *skb)
 {
        struct net *net = ovs_dp_get_net(vport->dp);
        struct vxlan_port *vxlan_port = vxlan_vport(vport);
-       __be16 dst_port = inet_sk(vxlan_port->vs->sock->sk)->inet_sport;
+       struct sock *sk = vxlan_port->vs->sock->sk;
+       __be16 dst_port = inet_sk(sk)->inet_sport;
        const struct ovs_key_ipv4_tunnel *tun_key;
        struct vxlan_metadata md = {0};
        struct rtable *rt;
@@ -255,7 +256,7 @@ static int vxlan_tnl_send(struct vport *vport, struct sk_buff *skb)
        vxflags = vxlan_port->exts |
                      (tun_key->tun_flags & TUNNEL_CSUM ? VXLAN_F_UDP_CSUM : 0);
 
-       err = vxlan_xmit_skb(rt, skb, fl.saddr, tun_key->ipv4_dst,
+       err = vxlan_xmit_skb(rt, sk, skb, fl.saddr, tun_key->ipv4_dst,
                             tun_key->ipv4_tos, tun_key->ipv4_ttl, df,
                             src_port, dst_port,
                             &md, false, vxflags);
index eb5b8445fef989c1f331fa1485fa39c649181a82..4cdbfb85686a7ee55d71d0a7c1ea5cdd7e789a22 100644 (file)
@@ -88,11 +88,19 @@ static int ingress_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 
 /* ------------------------------------------------------------- */
 
+static int ingress_init(struct Qdisc *sch, struct nlattr *opt)
+{
+       net_inc_ingress_queue();
+
+       return 0;
+}
+
 static void ingress_destroy(struct Qdisc *sch)
 {
        struct ingress_qdisc_data *p = qdisc_priv(sch);
 
        tcf_destroy_chain(&p->filter_list);
+       net_dec_ingress_queue();
 }
 
 static int ingress_dump(struct Qdisc *sch, struct sk_buff *skb)
@@ -124,6 +132,7 @@ static struct Qdisc_ops ingress_qdisc_ops __read_mostly = {
        .id             =       "ingress",
        .priv_size      =       sizeof(struct ingress_qdisc_data),
        .enqueue        =       ingress_enqueue,
+       .init           =       ingress_init,
        .destroy        =       ingress_destroy,
        .dump           =       ingress_dump,
        .owner          =       THIS_MODULE,
index ef3d7aa2854aabdf9b866ecd445f19c21435730e..66deebc66aa10820880bd51839bd2ae379eaeb86 100644 (file)
@@ -176,7 +176,8 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb,
                        goto tx_error;
                }
                ttl = ip4_dst_hoplimit(&rt->dst);
-               err = udp_tunnel_xmit_skb(rt, clone, src->ipv4.s_addr,
+               err = udp_tunnel_xmit_skb(rt, ub->ubsock->sk, clone,
+                                         src->ipv4.s_addr,
                                          dst->ipv4.s_addr, 0, ttl, 0,
                                          src->udp_port, dst->udp_port,
                                          false, true);
@@ -197,7 +198,8 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb,
                if (err)
                        goto tx_error;
                ttl = ip6_dst_hoplimit(ndst);
-               err = udp_tunnel6_xmit_skb(ndst, clone, ndst->dev, &src->ipv6,
+               err = udp_tunnel6_xmit_skb(ndst, ub->ubsock->sk, clone,
+                                          ndst->dev, &src->ipv6,
                                           &dst->ipv6, 0, ttl, src->udp_port,
                                           dst->udp_port, false);
 #endif
index b13dfb4ff001908160ba087db5d25377d43c5491..4f5543dd25243e0c959dacb00327155cd4e27e2b 100644 (file)
@@ -175,7 +175,7 @@ config CFG80211_INTERNAL_REGDB
          Most distributions have a CRDA package.  So if unsure, say N.
 
 config CFG80211_WEXT
-       bool "cfg80211 wireless extensions compatibility"
+       bool "cfg80211 wireless extensions compatibility" if !CFG80211_WEXT_EXPORT
        depends on CFG80211
        select WEXT_CORE
        default y if CFG80211_WEXT_EXPORT
index 6dd1ab3b10ea25c76e20a73998bb5f1ebc41b6b8..dd78445c7d50630524b7d33b96b73a2e416c662c 100644 (file)
@@ -5664,7 +5664,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
                }
        }
 
-       r = set_regdom(rd);
+       r = set_regdom(rd, REGD_SOURCE_CRDA);
        /* set_regdom took ownership */
        rd = NULL;
 
index be5f81caa488bf24a0bfc79c6ffb9ba534d1e4aa..0e347f888fe910d07e7a094058755f37bee9fcd9 100644 (file)
@@ -135,6 +135,11 @@ static spinlock_t reg_indoor_lock;
 /* Used to track the userspace process controlling the indoor setting */
 static u32 reg_is_indoor_portid;
 
+/* Max number of consecutive attempts to communicate with CRDA  */
+#define REG_MAX_CRDA_TIMEOUTS 10
+
+static u32 reg_crda_timeouts;
+
 static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
 {
        return rtnl_dereference(cfg80211_regdomain);
@@ -485,7 +490,7 @@ static void reg_regdb_search(struct work_struct *work)
        mutex_unlock(&reg_regdb_search_mutex);
 
        if (!IS_ERR_OR_NULL(regdom))
-               set_regdom(regdom);
+               set_regdom(regdom, REGD_SOURCE_INTERNAL_DB);
 
        rtnl_unlock();
 }
@@ -535,15 +540,20 @@ static int call_crda(const char *alpha2)
        snprintf(country, sizeof(country), "COUNTRY=%c%c",
                 alpha2[0], alpha2[1]);
 
+       /* query internal regulatory database (if it exists) */
+       reg_regdb_query(alpha2);
+
+       if (reg_crda_timeouts > REG_MAX_CRDA_TIMEOUTS) {
+               pr_info("Exceeded CRDA call max attempts. Not calling CRDA\n");
+               return -EINVAL;
+       }
+
        if (!is_world_regdom((char *) alpha2))
                pr_info("Calling CRDA for country: %c%c\n",
                        alpha2[0], alpha2[1]);
        else
                pr_info("Calling CRDA to update world regulatory domain\n");
 
-       /* query internal regulatory database (if it exists) */
-       reg_regdb_query(alpha2);
-
        return kobject_uevent_env(&reg_pdev->dev.kobj, KOBJ_CHANGE, env);
 }
 
@@ -2293,6 +2303,9 @@ int regulatory_hint_user(const char *alpha2,
        request->initiator = NL80211_REGDOM_SET_BY_USER;
        request->user_reg_hint_type = user_reg_hint_type;
 
+       /* Allow calling CRDA again */
+       reg_crda_timeouts = 0;
+
        queue_regulatory_request(request);
 
        return 0;
@@ -2362,6 +2375,9 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
        request->alpha2[1] = alpha2[1];
        request->initiator = NL80211_REGDOM_SET_BY_DRIVER;
 
+       /* Allow calling CRDA again */
+       reg_crda_timeouts = 0;
+
        queue_regulatory_request(request);
 
        return 0;
@@ -2415,6 +2431,9 @@ void regulatory_hint_country_ie(struct wiphy *wiphy, enum ieee80211_band band,
        request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE;
        request->country_ie_env = env;
 
+       /* Allow calling CRDA again */
+       reg_crda_timeouts = 0;
+
        queue_regulatory_request(request);
        request = NULL;
 out:
@@ -2893,7 +2912,8 @@ static int reg_set_rd_country_ie(const struct ieee80211_regdomain *rd,
  * multiple drivers can be ironed out later. Caller must've already
  * kmalloc'd the rd structure.
  */
-int set_regdom(const struct ieee80211_regdomain *rd)
+int set_regdom(const struct ieee80211_regdomain *rd,
+              enum ieee80211_regd_source regd_src)
 {
        struct regulatory_request *lr;
        bool user_reset = false;
@@ -2904,6 +2924,9 @@ int set_regdom(const struct ieee80211_regdomain *rd)
                return -EINVAL;
        }
 
+       if (regd_src == REGD_SOURCE_CRDA)
+               reg_crda_timeouts = 0;
+
        lr = get_last_request();
 
        /* Note that this doesn't update the wiphys, this is done below */
@@ -3063,6 +3086,7 @@ static void reg_timeout_work(struct work_struct *work)
 {
        REG_DBG_PRINT("Timeout while waiting for CRDA to reply, restoring regulatory settings\n");
        rtnl_lock();
+       reg_crda_timeouts++;
        restore_regulatory_settings(true);
        rtnl_unlock();
 }
index a2c4e16459da06a3b6ebad974aaab665eda717f0..9f495d76eca075d6f779078196f9adddd98a34aa 100644 (file)
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+enum ieee80211_regd_source {
+       REGD_SOURCE_INTERNAL_DB,
+       REGD_SOURCE_CRDA,
+};
+
 extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain;
 
 bool reg_is_valid_request(const char *alpha2);
@@ -46,7 +51,9 @@ void wiphy_regulatory_deregister(struct wiphy *wiphy);
 int __init regulatory_init(void);
 void regulatory_exit(void);
 
-int set_regdom(const struct ieee80211_regdomain *rd);
+int set_regdom(const struct ieee80211_regdomain *rd,
+              enum ieee80211_regd_source regd_src);
+
 unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
                                   const struct ieee80211_reg_rule *rule);
 
index ea1da6621ff051028970f50154af56d6f2e01b46..d11454f87bacf9396241bd5bbc3ba643ef0b3302 100644 (file)
@@ -42,7 +42,7 @@ struct cfg80211_conn {
                CFG80211_CONN_CONNECTED,
        } state;
        u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
-       u8 *ie;
+       const u8 *ie;
        size_t ie_len;
        bool auto_auth, prev_bssid_valid;
 };
@@ -423,6 +423,62 @@ void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev)
        schedule_work(&rdev->conn_work);
 }
 
+static int cfg80211_sme_get_conn_ies(struct wireless_dev *wdev,
+                                    const u8 *ies, size_t ies_len,
+                                    const u8 **out_ies, size_t *out_ies_len)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+       u8 *buf;
+       size_t offs;
+
+       if (!rdev->wiphy.extended_capabilities_len ||
+           (ies && cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, ies, ies_len))) {
+               *out_ies = kmemdup(ies, ies_len, GFP_KERNEL);
+               if (!*out_ies)
+                       return -ENOMEM;
+               *out_ies_len = ies_len;
+               return 0;
+       }
+
+       buf = kmalloc(ies_len + rdev->wiphy.extended_capabilities_len + 2,
+                     GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       if (ies_len) {
+               static const u8 before_extcapa[] = {
+                       /* not listing IEs expected to be created by driver */
+                       WLAN_EID_RSN,
+                       WLAN_EID_QOS_CAPA,
+                       WLAN_EID_RRM_ENABLED_CAPABILITIES,
+                       WLAN_EID_MOBILITY_DOMAIN,
+                       WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
+                       WLAN_EID_BSS_COEX_2040,
+               };
+
+               offs = ieee80211_ie_split(ies, ies_len, before_extcapa,
+                                         ARRAY_SIZE(before_extcapa), 0);
+               memcpy(buf, ies, offs);
+               /* leave a whole for extended capabilities IE */
+               memcpy(buf + offs + rdev->wiphy.extended_capabilities_len + 2,
+                      ies + offs, ies_len - offs);
+       } else {
+               offs = 0;
+       }
+
+       /* place extended capabilities IE (with only driver capabilities) */
+       buf[offs] = WLAN_EID_EXT_CAPABILITY;
+       buf[offs + 1] = rdev->wiphy.extended_capabilities_len;
+       memcpy(buf + offs + 2,
+              rdev->wiphy.extended_capabilities,
+              rdev->wiphy.extended_capabilities_len);
+
+       *out_ies = buf;
+       *out_ies_len = ies_len + rdev->wiphy.extended_capabilities_len + 2;
+
+       return 0;
+}
+
 static int cfg80211_sme_connect(struct wireless_dev *wdev,
                                struct cfg80211_connect_params *connect,
                                const u8 *prev_bssid)
@@ -453,16 +509,14 @@ static int cfg80211_sme_connect(struct wireless_dev *wdev,
                memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN);
        }
 
-       if (connect->ie) {
-               wdev->conn->ie = kmemdup(connect->ie, connect->ie_len,
-                                       GFP_KERNEL);
-               wdev->conn->params.ie = wdev->conn->ie;
-               if (!wdev->conn->ie) {
-                       kfree(wdev->conn);
-                       wdev->conn = NULL;
-                       return -ENOMEM;
-               }
+       if (cfg80211_sme_get_conn_ies(wdev, connect->ie, connect->ie_len,
+                                     &wdev->conn->ie,
+                                     &wdev->conn->params.ie_len)) {
+               kfree(wdev->conn);
+               wdev->conn = NULL;
+               return -ENOMEM;
        }
+       wdev->conn->params.ie = wdev->conn->ie;
 
        if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
                wdev->conn->auto_auth = true;
index f218b151530a915c106376bde1643700750e5007..70051ab52f4f34d2817e76210aafa70bbda62e60 100644 (file)
@@ -1290,6 +1290,47 @@ int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len,
 }
 EXPORT_SYMBOL(cfg80211_get_p2p_attr);
 
+static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
+{
+       int i;
+
+       for (i = 0; i < n_ids; i++)
+               if (ids[i] == id)
+                       return true;
+       return false;
+}
+
+size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
+                             const u8 *ids, int n_ids,
+                             const u8 *after_ric, int n_after_ric,
+                             size_t offset)
+{
+       size_t pos = offset;
+
+       while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) {
+               if (ies[pos] == WLAN_EID_RIC_DATA && n_after_ric) {
+                       pos += 2 + ies[pos + 1];
+
+                       while (pos < ielen &&
+                              !ieee80211_id_in_list(after_ric, n_after_ric,
+                                                    ies[pos]))
+                               pos += 2 + ies[pos + 1];
+               } else {
+                       pos += 2 + ies[pos + 1];
+               }
+       }
+
+       return pos;
+}
+EXPORT_SYMBOL(ieee80211_ie_split_ric);
+
+size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
+                         const u8 *ids, int n_ids, size_t offset)
+{
+       return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset);
+}
+EXPORT_SYMBOL(ieee80211_ie_split);
+
 bool ieee80211_operating_class_to_band(u8 operating_class,
                                       enum ieee80211_band *band)
 {
index 7c532856b39829f0cddf50baecc6d8c73c3422e7..fbcedbe33190346a40fc148369757a6ef64a2106 100644 (file)
@@ -19,7 +19,7 @@
 #include <net/dst.h>
 #include <net/xfrm.h>
 
-static int xfrm_output2(struct sk_buff *skb);
+static int xfrm_output2(struct sock *sk, struct sk_buff *skb);
 
 static int xfrm_skb_check_space(struct sk_buff *skb)
 {
@@ -130,7 +130,7 @@ int xfrm_output_resume(struct sk_buff *skb, int err)
                        return dst_output(skb);
 
                err = nf_hook(skb_dst(skb)->ops->family,
-                             NF_INET_POST_ROUTING, skb,
+                             NF_INET_POST_ROUTING, skb->sk, skb,
                              NULL, skb_dst(skb)->dev, xfrm_output2);
                if (unlikely(err != 1))
                        goto out;
@@ -144,12 +144,12 @@ out:
 }
 EXPORT_SYMBOL_GPL(xfrm_output_resume);
 
-static int xfrm_output2(struct sk_buff *skb)
+static int xfrm_output2(struct sock *sk, struct sk_buff *skb)
 {
        return xfrm_output_resume(skb, 1);
 }
 
-static int xfrm_output_gso(struct sk_buff *skb)
+static int xfrm_output_gso(struct sock *sk, struct sk_buff *skb)
 {
        struct sk_buff *segs;
 
@@ -165,7 +165,7 @@ static int xfrm_output_gso(struct sk_buff *skb)
                int err;
 
                segs->next = NULL;
-               err = xfrm_output2(segs);
+               err = xfrm_output2(sk, segs);
 
                if (unlikely(err)) {
                        kfree_skb_list(nskb);
@@ -178,13 +178,13 @@ static int xfrm_output_gso(struct sk_buff *skb)
        return 0;
 }
 
-int xfrm_output(struct sk_buff *skb)
+int xfrm_output(struct sock *sk, struct sk_buff *skb)
 {
        struct net *net = dev_net(skb_dst(skb)->dev);
        int err;
 
        if (skb_is_gso(skb))
-               return xfrm_output_gso(skb);
+               return xfrm_output_gso(sk, skb);
 
        if (skb->ip_summed == CHECKSUM_PARTIAL) {
                err = skb_checksum_help(skb);
@@ -195,7 +195,7 @@ int xfrm_output(struct sk_buff *skb)
                }
        }
 
-       return xfrm_output2(skb);
+       return xfrm_output2(sk, skb);
 }
 EXPORT_SYMBOL_GPL(xfrm_output);
 
index 7de2ed9ec46ddd003ef3db00554335a12f95cb89..2091664295bae1a3a4725287e5d0bf5cfb28357c 100644 (file)
@@ -2423,6 +2423,11 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
        const struct xfrm_link *link;
        int type, err;
 
+#ifdef CONFIG_COMPAT
+       if (is_compat_task())
+               return -ENOTSUPP;
+#endif
+
        type = nlh->nlmsg_type;
        if (type > XFRM_MSG_MAX)
                return -EINVAL;
index 2df7b900e25965828ed91e3bd376340a672d9c0f..2bbb41822d8ec8882f8dacbbb4c5f8a1feac59ca 100644 (file)
@@ -73,6 +73,9 @@ static struct nlmsg_perm nlmsg_route_perms[] =
        { RTM_NEWMDB,           NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
        { RTM_DELMDB,           NETLINK_ROUTE_SOCKET__NLMSG_WRITE  },
        { RTM_GETMDB,           NETLINK_ROUTE_SOCKET__NLMSG_READ  },
+       { RTM_NEWNSID,          NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+       { RTM_DELNSID,          NETLINK_ROUTE_SOCKET__NLMSG_READ  },
+       { RTM_GETNSID,          NETLINK_ROUTE_SOCKET__NLMSG_READ  },
 };
 
 static struct nlmsg_perm nlmsg_tcpdiag_perms[] =
@@ -100,6 +103,13 @@ static struct nlmsg_perm nlmsg_xfrm_perms[] =
        { XFRM_MSG_FLUSHPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
        { XFRM_MSG_NEWAE,       NETLINK_XFRM_SOCKET__NLMSG_WRITE },
        { XFRM_MSG_GETAE,       NETLINK_XFRM_SOCKET__NLMSG_READ  },
+       { XFRM_MSG_REPORT,      NETLINK_XFRM_SOCKET__NLMSG_READ  },
+       { XFRM_MSG_MIGRATE,     NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+       { XFRM_MSG_NEWSADINFO,  NETLINK_XFRM_SOCKET__NLMSG_READ  },
+       { XFRM_MSG_GETSADINFO,  NETLINK_XFRM_SOCKET__NLMSG_READ  },
+       { XFRM_MSG_NEWSPDINFO,  NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+       { XFRM_MSG_GETSPDINFO,  NETLINK_XFRM_SOCKET__NLMSG_READ  },
+       { XFRM_MSG_MAPPING,     NETLINK_XFRM_SOCKET__NLMSG_READ  },
 };
 
 static struct nlmsg_perm nlmsg_audit_perms[] =
@@ -143,6 +153,8 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
 
        switch (sclass) {
        case SECCLASS_NETLINK_ROUTE_SOCKET:
+               /* RTM_MAX always point to RTM_SETxxxx, ie RTM_NEWxxx + 3 */
+               BUILD_BUG_ON(RTM_MAX != (RTM_NEWNSID + 3));
                err = nlmsg_perm(nlmsg_type, perm, nlmsg_route_perms,
                                 sizeof(nlmsg_route_perms));
                break;
@@ -153,6 +165,7 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
                break;
 
        case SECCLASS_NETLINK_XFRM_SOCKET:
+               BUILD_BUG_ON(XFRM_MSG_MAX != XFRM_MSG_MAPPING);
                err = nlmsg_perm(nlmsg_type, perm, nlmsg_xfrm_perms,
                                 sizeof(nlmsg_xfrm_perms));
                break;